import { useState } from 'react'; import { useTranslation } from 'react-i18next'; 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, 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 limited (traffic exhausted) state const isLimited = subscription.is_limited; // Detect daily subscription (disabled or expired) const isDaily = subscription.is_daily; const isDisabledDaily = subscription.status === 'disabled' && isDaily; // For daily subs, check if balance covers daily price; otherwise 100 kopeks minimum const dailyPrice = subscription.daily_price_kopeks ?? 0; const hasBalance = isDaily ? 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(subscription.id); } else if (isDaily && subscription.tariff_id) { // Expired daily tariff — purchase for 1 day. Pass subscription.id // so the backend resolves the EXACT row instead of doing a // (user_id, tariff_id) re-lookup that races with concurrent // panel webhooks (would surface as "Тариф уже активен" + refund). await subscriptionApi.purchaseTariff(subscription.tariff_id, 1, undefined, subscription.id); } else { await subscriptionApi.renewSubscription(30, subscription.id); } haptic.success(); queryClient.invalidateQueries({ predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === 'subscription', }); queryClient.invalidateQueries({ queryKey: ['subscriptions-list'] }); 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()}`); }; // Color scheme: amber for limited, red for expired/disabled const accent = isLimited ? { r: 255, g: 184, b: 0, hex: 'rgb(var(--color-urgent-400))', gradient: 'linear-gradient(135deg, #FFB800, #FF8C00)', } : { r: 255, g: 59, b: 92, hex: 'rgb(var(--color-critical-500))', gradient: 'linear-gradient(135deg, #FF3B5C, #FF6B35)', }; return (
{/* Glow */}