import { useState } from 'react'; import { useParams } from 'react-router'; import { useQuery, useMutation } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import axios from 'axios'; import { couponsApi, CouponRedeemResponse } from '../api/coupons'; import { useAuthStore } from '../store/auth'; import { formatShortDate } from '../utils/format'; import { Spinner } from '@/components/ui/Spinner'; import { AnimatedCheckmark } from '@/components/ui/AnimatedCheckmark'; import { TicketIcon } from '@/components/icons'; function Shell({ children }: { children: React.ReactNode }) { return (
{children}
); } // The redeem endpoint answers 400 with a structured {detail: {code, message}} โ€” // map the stable machine code to a localized message. const redeemErrorText = (err: unknown, t: (key: string) => string): string => { if (axios.isAxiosError(err)) { const detail = err.response?.data?.detail as { code?: string } | string | undefined; if (detail && typeof detail === 'object' && detail.code) { const known = ['invalid', 'expired', 'already_redeemed_by_you', 'internal']; if (known.includes(detail.code)) return t(`coupon.errors.${detail.code}`); } } return t('coupon.errors.generic'); }; export default function CouponStatus() { const { token } = useParams<{ token: string }>(); const { t } = useTranslation(); const isAuthenticated = useAuthStore((state) => state.isAuthenticated); const [redeemed, setRedeemed] = useState(null); const [redeemError, setRedeemError] = useState(null); const { data: coupon, isLoading, error, } = useQuery({ queryKey: ['coupon-status', token], queryFn: () => couponsApi.getCouponStatus(token!), enabled: !!token, // 404 is the expected answer for an invalid/consumed/expired token โ€” the // common case for a shared link โ€” so retrying only wastes a request against // the rate-limited public endpoint. retry: false, }); const redeemMutation = useMutation({ mutationFn: () => couponsApi.redeemCoupon(token!), onSuccess: (result) => { setRedeemError(null); setRedeemed(result); }, onError: (err) => { setRedeemError(redeemErrorText(err, t)); }, }); if (isLoading) { return (
); } if (error || !coupon) { return (

{t('coupon.notFound.title')}

{t('coupon.notFound.text')}

); } if (redeemed) { return (

{redeemed.renewed ? t('coupon.success.renewed') : t('coupon.success.activated')}

{redeemed.tariff_name} ยท {redeemed.period_days} {t('coupon.daysSuffix')} {redeemed.end_date && ( <>
{t('coupon.success.until')} {formatShortDate(redeemed.end_date)} )}

); } return (

{t('coupon.title')}

{t('coupon.subtitle')}

{t('coupon.tariff')} {coupon.tariff_name}
{t('coupon.period')} {coupon.period_days} {t('coupon.daysSuffix')}
{coupon.valid_until && (
{t('coupon.validUntil')} {formatShortDate(coupon.valid_until)}
)}
{redeemError && (
{redeemError}
)}
{coupon.bot_link && ( {t('coupon.openBot')} )} {isAuthenticated ? ( ) : (

{t('coupon.needAuth')}

)}
); }