import { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { subscriptionApi } from '../../../api/subscription'; import { getErrorMessage, getFlagEmoji } from '../../../utils/subscriptionHelpers'; import InsufficientBalancePrompt from '../../InsufficientBalancePrompt'; import { ChevronRightIcon } from '../../icons'; import type { PurchaseOptions, Subscription } from '../../../types'; // ────────────────────────────────────────────────────────────────── // Manage-servers sheet (classic-mode only — caller decides whether to // render). Self-owns the countries query + update mutation; parent // holds the selected-uuid set so the global "close all modals" can // reset it. // // Extracted from Subscription.tsx — ~280 lines off the god page. // ────────────────────────────────────────────────────────────────── export interface ServerManagementSheetProps { open: boolean; onOpen: () => void; onClose: () => void; subscription: Subscription; subscriptionId: number | undefined; selectedServers: string[]; onSelectedServersChange: (uuids: string[] | ((prev: string[]) => string[])) => void; purchaseOptions: PurchaseOptions | undefined; isDark: boolean; } export function ServerManagementSheet({ open, onOpen, onClose, subscription, subscriptionId, selectedServers, onSelectedServersChange, purchaseOptions, isDark, }: ServerManagementSheetProps) { const { t } = useTranslation(); const queryClient = useQueryClient(); const formatPrice = (kopeks: number) => { const rubles = kopeks / 100; return rubles % 1 === 0 ? `${rubles} ₽` : `${rubles.toFixed(2)} ₽`; }; const { data: countriesData, isLoading: countriesLoading } = useQuery({ queryKey: ['countries', subscriptionId], queryFn: () => subscriptionApi.getCountries(subscriptionId), enabled: open && !!subscription && !subscription.is_trial, }); // Seed selected = currently connected once the data loads. useEffect(() => { if (countriesData && open) { const connected = countriesData.countries.filter((c) => c.is_connected).map((c) => c.uuid); onSelectedServersChange(connected); } // Intentionally narrow — the setter is stable and we don't want to // re-seed every time the user toggles a server. // eslint-disable-next-line react-hooks/exhaustive-deps }, [countriesData, open]); const updateMutation = useMutation({ mutationFn: (countries: string[]) => subscriptionApi.updateCountries(countries, subscriptionId), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['subscription', subscriptionId] }); queryClient.invalidateQueries({ queryKey: ['subscriptions-list'] }); queryClient.invalidateQueries({ queryKey: ['countries', subscriptionId] }); onClose(); }, }); if (!open) { return ( ); } return (

{t('subscription.additionalOptions.manageServersTitle')}

{countriesLoading ? (
) : countriesData && countriesData.countries.length > 0 ? (
{t('subscription.serverManagement.statusLegend')}
{countriesData.discount_percent > 0 && (
🎁{' '} {t('subscription.serverManagement.discountBanner', { percent: countriesData.discount_percent, })}
)}
{countriesData.countries .filter((country) => country.is_available || country.is_connected) .map((country) => { const isCurrentlyConnected = country.is_connected; const isSelected = selectedServers.includes(country.uuid); const willBeAdded = !isCurrentlyConnected && isSelected; const willBeRemoved = isCurrentlyConnected && !isSelected; return ( ); })}
{(() => { const currentConnected = countriesData.countries .filter((c) => c.is_connected) .map((c) => c.uuid); const added = selectedServers.filter((u) => !currentConnected.includes(u)); const removed = currentConnected.filter((u) => !selectedServers.includes(u)); const hasChanges = added.length > 0 || removed.length > 0; const addedServers = countriesData.countries.filter((c) => added.includes(c.uuid)); const totalCost = addedServers.reduce((sum, s) => sum + s.price_kopeks, 0); const hasEnoughBalance = !purchaseOptions || totalCost <= purchaseOptions.balance_kopeks; const missingAmount = purchaseOptions ? totalCost - purchaseOptions.balance_kopeks : 0; return hasChanges ? (
{added.length > 0 && (
{t('subscription.serverManagement.toAdd')} {' '} {addedServers.map((s) => s.name).join(', ')}
)} {removed.length > 0 && (
{t('subscription.serverManagement.toDisconnect')} {' '} {countriesData.countries .filter((c) => removed.includes(c.uuid)) .map((s) => s.name) .join(', ')}
)} {totalCost > 0 && (
{t('subscription.serverManagement.paymentProrated')}
{formatPrice(totalCost)}
)} {totalCost > 0 && !hasEnoughBalance && missingAmount > 0 && ( )}
) : (
{t('subscription.serverManagement.selectServersHint')}
); })()} {updateMutation.isError && (
{getErrorMessage(updateMutation.error)}
)}
) : (
{t('subscription.serverManagement.noServersAvailable')}
)}
); }