diff --git a/src/components/dashboard/SubscriptionCardExpired.tsx b/src/components/dashboard/SubscriptionCardExpired.tsx index 38ef1a1..5812312 100644 --- a/src/components/dashboard/SubscriptionCardExpired.tsx +++ b/src/components/dashboard/SubscriptionCardExpired.tsx @@ -1,23 +1,98 @@ +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router'; +import { Link, useNavigate, useLocation } from 'react-router'; +import { useQueryClient } from '@tanstack/react-query'; +import { AxiosError } from 'axios'; import type { Subscription } from '../../types'; +import { subscriptionApi } from '../../api/subscription'; import { useTheme } from '../../hooks/useTheme'; +import { useCurrency } from '../../hooks/useCurrency'; +import { useHapticFeedback } from '../../platform/hooks/useHaptic'; import { getGlassColors } from '../../utils/glassTheme'; +import { getInsufficientBalanceError } from '../../utils/subscriptionHelpers'; interface SubscriptionCardExpiredProps { subscription: Subscription; + balanceKopeks?: number; + balanceRubles?: number; + className?: string; } -export default function SubscriptionCardExpired({ subscription }: SubscriptionCardExpiredProps) { +export default function SubscriptionCardExpired({ + subscription, + balanceKopeks = 0, + balanceRubles = 0, + className, +}: SubscriptionCardExpiredProps) { const { t } = useTranslation(); const { isDark } = useTheme(); const g = getGlassColors(isDark); + const queryClient = useQueryClient(); + const navigate = useNavigate(); + const location = useLocation(); + const { formatAmount, currencySymbol } = useCurrency(); + const haptic = useHapticFeedback(); + + const [isRenewing, setIsRenewing] = useState(false); + const [renewError, setRenewError] = useState(null); const formattedDate = new Date(subscription.end_date).toLocaleDateString(); + // Detect DISABLED daily subscription (suspended by system due to insufficient balance) + const isDisabledDaily = subscription.status === 'disabled' && subscription.is_daily; + + // For disabled daily subs, check if balance covers daily price + const dailyPrice = subscription.daily_price_kopeks ?? 0; + const hasBalance = isDisabledDaily + ? balanceKopeks >= dailyPrice && dailyPrice > 0 + : balanceKopeks >= 100; + + const handleQuickRenew = async () => { + setIsRenewing(true); + setRenewError(null); + haptic.buttonPressHeavy(); + + try { + if (isDisabledDaily) { + // Resume daily subscription via toggle pause endpoint + await subscriptionApi.togglePause(); + } else { + await subscriptionApi.renewSubscription(30); + } + haptic.success(); + queryClient.invalidateQueries({ queryKey: ['subscription'] }); + queryClient.invalidateQueries({ queryKey: ['balance'] }); + queryClient.invalidateQueries({ queryKey: ['purchase-options'] }); + } catch (err: unknown) { + haptic.error(); + const insufficientData = getInsufficientBalanceError(err); + if (insufficientData) { + setRenewError(t('dashboard.expired.insufficientFunds')); + } else if (err instanceof AxiosError) { + const detail = err.response?.data?.detail; + if (typeof detail === 'string') { + setRenewError(detail); + } else { + setRenewError(t('dashboard.expired.renewError')); + } + } else { + setRenewError(t('dashboard.expired.renewError')); + } + } finally { + setIsRenewing(false); + } + }; + + const handleTopUp = () => { + haptic.buttonPress(); + const params = new URLSearchParams(); + params.set('returnTo', location.pathname); + navigate(`/balance/top-up?${params.toString()}`); + }; + return (

- {subscription.is_trial ? t('dashboard.expired.trialTitle') : t('dashboard.expired.title')} + {isDisabledDaily + ? t('dashboard.suspended.title') + : subscription.is_trial + ? t('dashboard.expired.trialTitle') + : t('dashboard.expired.title')}

- {/* Expired date */} + {/* Expired date + Balance row */}
-
- {t('dashboard.expired.expiredDate')} +
+
+ {t('dashboard.expired.expiredDate')} +
+
+ {formattedDate} +
-
- {formattedDate} +
+ + {t('dashboard.expired.balance')} + + + {formatAmount(balanceRubles)} {currencySymbol} +
+ {/* Renew error */} + {renewError && ( +
+ {renewError} +
+ )} + {/* Action buttons */}
- - {t('dashboard.expired.renew')} - + {/* Quick Renew or Top Up button */} + {hasBalance ? ( + + ) : ( + + )} + + {/* Renew (go to purchase page) */}
- ) : subscription?.is_expired ? ( - + ) : subscription?.is_expired || subscription?.status === 'disabled' ? ( + ) : subscription ? ( subscriptionApi.togglePause(), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['subscription'] }); + queryClient.invalidateQueries({ queryKey: ['balance'] }); }, }); @@ -525,7 +526,9 @@ export default function Subscription() { ? subscription.is_trial ? t('subscription.trialStatus') : t('subscription.active') - : t('subscription.expired')} + : subscription.status === 'disabled' + ? t('subscription.pause.suspended') + : t('subscription.expired')}
@@ -982,9 +985,11 @@ export default function Subscription() { {t('subscription.pause.title')}
- {subscription.is_daily_paused - ? t('subscription.pause.paused') - : t('subscription.pause.active')} + {subscription.status === 'disabled' + ? t('subscription.pause.suspended') + : subscription.is_daily_paused + ? t('subscription.pause.paused') + : t('subscription.pause.active')}