From 56e002337255286198502af3e98ab2fe81810327 Mon Sep 17 00:00:00 2001 From: Egor Date: Tue, 20 Jan 2026 06:33:20 +0300 Subject: [PATCH] Update Subscription.tsx --- src/pages/Subscription.tsx | 203 +++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) diff --git a/src/pages/Subscription.tsx b/src/pages/Subscription.tsx index 2af8144..1f1426f 100644 --- a/src/pages/Subscription.tsx +++ b/src/pages/Subscription.tsx @@ -97,6 +97,8 @@ export default function Subscription() { const [devicesToAdd, setDevicesToAdd] = useState(1) const [showTrafficTopup, setShowTrafficTopup] = useState(false) const [selectedTrafficPackage, setSelectedTrafficPackage] = useState(null) + const [showServerManagement, setShowServerManagement] = useState(false) + const [selectedServersToUpdate, setSelectedServersToUpdate] = useState([]) const { data: subscription, isLoading } = useQuery({ queryKey: ['subscription'], @@ -299,6 +301,31 @@ export default function Subscription() { }, }) + // Countries/servers query + const { data: countriesData, isLoading: countriesLoading } = useQuery({ + queryKey: ['countries'], + queryFn: subscriptionApi.getCountries, + enabled: showServerManagement && !!subscription && !subscription.is_trial, + }) + + // Initialize selected servers when data loads + useEffect(() => { + if (countriesData && showServerManagement) { + const connected = countriesData.countries.filter(c => c.is_connected).map(c => c.uuid) + setSelectedServersToUpdate(connected) + } + }, [countriesData, showServerManagement]) + + // Countries update mutation + const updateCountriesMutation = useMutation({ + mutationFn: (countries: string[]) => subscriptionApi.updateCountries(countries), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['subscription'] }) + queryClient.invalidateQueries({ queryKey: ['countries'] }) + setShowServerManagement(false) + }, + }) + // Auto-scroll to switch tariff modal when it appears useEffect(() => { if (switchTariffId && switchModalRef.current) { @@ -912,6 +939,182 @@ export default function Subscription() { )} )} + + {/* Server Management */} +
+ {!showServerManagement ? ( + + ) : ( +
+
+

Управление серверами

+ +
+ + {countriesLoading ? ( +
+
+
+ ) : countriesData && countriesData.countries.length > 0 ? ( +
+
+ ✅ — подключено • ➕ — будет добавлено (платно) • ➖ — будет отключено +
+ +
+ {countriesData.countries.map((country) => { + const isCurrentlyConnected = country.is_connected + const isSelected = selectedServersToUpdate.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 = selectedServersToUpdate.filter(u => !currentConnected.includes(u)) + const removed = currentConnected.filter(u => !selectedServersToUpdate.includes(u)) + const hasChanges = added.length > 0 || removed.length > 0 + + // Calculate cost for added servers + 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 && ( +
+ Добавить:{' '} + + {addedServers.map(s => s.name).join(', ')} + +
+ )} + {removed.length > 0 && ( +
+ Отключить:{' '} + + {countriesData.countries.filter(c => removed.includes(c.uuid)).map(s => s.name).join(', ')} + +
+ )} + {totalCost > 0 && ( +
+
К оплате (пропорционально оставшимся дням):
+
{formatPrice(totalCost)}
+
+ )} + + {totalCost > 0 && !hasEnoughBalance && missingAmount > 0 && ( + + )} + + +
+ ) : ( +
+ Выберите серверы для подключения или отключения +
+ ) + })()} + + {updateCountriesMutation.isError && ( +
+ {getErrorMessage(updateCountriesMutation.error)} +
+ )} +
+ ) : ( +
+ Нет доступных серверов для управления +
+ )} +
+ )} +
)}