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')}
)}
);
}