diff --git a/src/components/WebSocketNotifications.tsx b/src/components/WebSocketNotifications.tsx index f8b38ea..31e0ab1 100644 --- a/src/components/WebSocketNotifications.tsx +++ b/src/components/WebSocketNotifications.tsx @@ -7,7 +7,7 @@ import { useCallback } from 'react'; import { useNavigate } from 'react-router'; import { useTranslation } from 'react-i18next'; import { useQueryClient } from '@tanstack/react-query'; -import { useWebSocket, WSMessage } from '../hooks/useWebSocket'; +import { useWebSocket, type WSMessage } from '../hooks/useWebSocket'; import { useToast } from './Toast'; import { useAuthStore } from '../store/auth'; import { useCurrency } from '../hooks/useCurrency'; @@ -296,6 +296,131 @@ export default function WebSocketNotifications() { return; } + // SBP recurring events + if (type === 'sbp_recurring.confirmed') { + const amount = message.amount_rubles ?? (message.amount_kopeks ?? 0) / 100; + showToast({ + type: 'success', + title: t('wsNotifications.sbpRecurring.confirmedTitle', 'SBP payment confirmed'), + message: t( + 'wsNotifications.sbpRecurring.confirmedMessage', + 'Payment of {{amount}} {{currency}} was charged successfully', + { + amount: formatAmount(amount), + currency: currencySymbol, + }, + ), + icon: 🔁, + onClick: () => navigate('/subscriptions'), + duration: 8000, + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'subscription', + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'sbp-recurring', + }); + queryClient.invalidateQueries({ queryKey: ['subscriptions-list'] }); + refreshUser(); + return; + } + + if (type === 'sbp_recurring.activated') { + showToast({ + type: 'success', + title: t('wsNotifications.sbpRecurring.activatedTitle', 'SBP auto-payment connected'), + message: t( + 'wsNotifications.sbpRecurring.activatedMessage', + 'SBP auto-payment has been successfully connected', + ), + icon: 🔁, + onClick: () => navigate('/subscriptions'), + duration: 8000, + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'subscription', + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'sbp-recurring', + }); + queryClient.invalidateQueries({ queryKey: ['subscriptions-list'] }); + refreshUser(); + return; + } + + if (type === 'sbp_recurring.failed') { + showToast({ + type: 'error', + title: t('wsNotifications.sbpRecurring.failedTitle', 'SBP payment failed'), + message: t( + 'wsNotifications.sbpRecurring.failedMessage', + 'Failed to charge your SBP auto-payment', + ), + icon: , + onClick: () => navigate('/subscriptions'), + duration: 10000, + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'subscription', + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'sbp-recurring', + }); + return; + } + + if (type === 'sbp_recurring.past_due') { + showToast({ + type: 'warning', + title: t('wsNotifications.sbpRecurring.pastDueTitle', 'SBP payment overdue'), + message: t( + 'wsNotifications.sbpRecurring.pastDueMessage', + 'Your SBP auto-payment is overdue. Please check your banking app.', + ), + icon: ⚠️, + onClick: () => navigate('/subscriptions'), + duration: 10000, + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'subscription', + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'sbp-recurring', + }); + return; + } + + if (type === 'sbp_recurring.cancelled') { + showToast({ + type: 'warning', + title: t('wsNotifications.sbpRecurring.cancelledTitle', 'SBP auto-payment disabled'), + message: t( + 'wsNotifications.sbpRecurring.cancelledMessage', + 'SBP auto-payment has been disabled', + ), + icon: 🔕, + onClick: () => navigate('/subscriptions'), + duration: 8000, + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'subscription', + }); + queryClient.invalidateQueries({ + predicate: (query) => + Array.isArray(query.queryKey) && query.queryKey[0] === 'sbp-recurring', + }); + return; + } + // Account events if (type === 'account.banned') { showToast({ diff --git a/src/providers/WebSocketContext.ts b/src/providers/WebSocketContext.ts index 6c4cbb1..8a23823 100644 --- a/src/providers/WebSocketContext.ts +++ b/src/providers/WebSocketContext.ts @@ -21,6 +21,9 @@ export interface WSMessage { new_expires_at?: string; tariff_name?: string; days_left?: number; + // SBP recurring events + status?: string; + next_charge_at?: string | null; // Device purchase events devices_added?: number; new_device_limit?: number;