import { useEffect, useRef, useState } from 'react'; import { useNavigate } from 'react-router'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { referralApi } from '../api/referral'; import { copyToClipboard } from '../utils/clipboard'; import { brandingApi } from '../api/branding'; import { partnerApi } from '../api/partners'; import { withdrawalApi } from '../api/withdrawals'; import { CampaignCard } from '../components/partner/CampaignCard'; import { useCurrency } from '../hooks/useCurrency'; const LinkIcon = () => ( ); const CopyIcon = () => ( ); const CheckIcon = () => ( ); const ShareIcon = () => ( ); const PartnerIcon = () => ( ); const ClockIcon = () => ( ); const WalletIcon = () => ( ); function getWithdrawalStatusBadge(status: string): string { switch (status) { case 'completed': return 'badge-success'; case 'approved': return 'badge-info'; case 'pending': return 'badge-warning'; case 'rejected': case 'cancelled': return 'badge-error'; default: return 'badge-neutral'; } } export default function Referral() { const { t, i18n } = useTranslation(); const navigate = useNavigate(); const { formatAmount, currencySymbol, formatPositive, formatWithCurrency } = useCurrency(); const queryClient = useQueryClient(); const [copied, setCopied] = useState(false); const copyTimerRef = useRef | null>(null); useEffect(() => { return () => { if (copyTimerRef.current) clearTimeout(copyTimerRef.current); }; }, []); const { data: info, isLoading } = useQuery({ queryKey: ['referral-info'], queryFn: referralApi.getReferralInfo, }); // Build referral link for cabinet registration const referralLink = info?.referral_code ? `${window.location.origin}/login?ref=${info.referral_code}` : ''; const { data: terms } = useQuery({ queryKey: ['referral-terms'], queryFn: referralApi.getReferralTerms, }); const { data: referralList } = useQuery({ queryKey: ['referral-list'], queryFn: () => referralApi.getReferralList({ per_page: 10 }), }); const { data: earnings } = useQuery({ queryKey: ['referral-earnings'], queryFn: () => referralApi.getReferralEarnings({ per_page: 10 }), }); const { data: branding } = useQuery({ queryKey: ['branding'], queryFn: brandingApi.getBranding, staleTime: 60000, }); // Partner status query const { data: partnerStatus } = useQuery({ queryKey: ['partner-status'], queryFn: partnerApi.getStatus, }); const isPartner = partnerStatus?.partner_status === 'approved'; // Withdrawal queries (only when partner is approved) const { data: withdrawalBalance } = useQuery({ queryKey: ['withdrawal-balance'], queryFn: withdrawalApi.getBalance, enabled: isPartner, }); const { data: withdrawalHistory } = useQuery({ queryKey: ['withdrawal-history'], queryFn: withdrawalApi.getHistory, enabled: isPartner, }); // Withdrawal cancel mutation const cancelWithdrawalMutation = useMutation({ mutationFn: withdrawalApi.cancel, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['withdrawal-balance'] }); queryClient.invalidateQueries({ queryKey: ['withdrawal-history'] }); }, }); const copyLink = async () => { if (!referralLink) return; try { await copyToClipboard(referralLink); setCopied(true); if (copyTimerRef.current) clearTimeout(copyTimerRef.current); copyTimerRef.current = setTimeout(() => setCopied(false), 2000); } catch { // clipboard write failed silently } }; const shareLink = () => { if (!referralLink) return; const shareText = t('referral.shareMessage', { percent: info?.commission_percent || 0, botName: branding?.name || import.meta.env.VITE_APP_NAME || 'Cabinet', }); if (navigator.share) { navigator .share({ title: t('referral.title'), text: shareText, url: referralLink, }) .catch(() => { // ignore cancellation errors }); return; } const telegramUrl = `https://t.me/share/url?url=${encodeURIComponent( referralLink, )}&text=${encodeURIComponent(shareText)}`; window.open(telegramUrl, '_blank', 'noopener,noreferrer'); }; if (isLoading) { return (
); } // Show disabled state if referral program is disabled if (terms && !terms.is_enabled) { return (

{t('referral.title')}

{t('referral.disabled')}

); } const partnerStatusValue = partnerStatus?.partner_status ?? 'none'; const showApplySection = partnerStatusValue === 'none'; const showPendingSection = partnerStatusValue === 'pending'; const showApprovedSection = partnerStatusValue === 'approved'; const showRejectedSection = partnerStatusValue === 'rejected'; return (

{t('referral.title')}

{/* Stats Cards */}
{t('referral.stats.totalReferrals')}
{info?.total_referrals || 0}
{info?.active_referrals || 0} {t('referral.stats.activeReferrals').toLowerCase()}
{t('referral.stats.totalEarnings')}
{formatPositive(info?.total_earnings_rubles || 0)}
{t('referral.stats.commissionRate')}
{info?.commission_percent || 0}%
{/* Referral Link */}

{t('referral.yourLink')}

{t('referral.shareHint', { percent: info?.commission_percent || 0 })}

{/* Program Terms */} {terms && (() => { const showNewUserBonus = terms.first_topup_bonus_kopeks > 0; const showInviterBonus = terms.inviter_bonus_kopeks > 0; const cardCount = 2 + (showNewUserBonus ? 1 : 0) + (showInviterBonus ? 1 : 0); const gridCols = cardCount <= 2 ? 'md:grid-cols-2' : cardCount === 3 ? 'md:grid-cols-3' : 'md:grid-cols-4'; return (

{t('referral.terms.title')}

{t('referral.terms.commission')}
{terms.commission_percent}%
{t('referral.terms.minTopup')}
{formatAmount(terms.minimum_topup_rubles)} {currencySymbol}
{showNewUserBonus && (
{t('referral.terms.newUserBonus')}
{formatPositive(terms.first_topup_bonus_rubles)}
)} {showInviterBonus && (
{t('referral.terms.inviterBonus')}
{formatPositive(terms.inviter_bonus_rubles)}
)}
); })()} {/* Referrals List */}

{t('referral.yourReferrals')}

{referralList?.items && referralList.items.length > 0 ? (
{referralList.items.map((ref) => (
{ref.first_name || ref.username || t('referral.anonymousUser', { id: ref.id })}
{new Date(ref.created_at).toLocaleDateString(i18n.language)}
{ref.has_paid ? ( {t('referral.status.paid')} ) : ( {t('referral.status.pending')} )}
))}
) : (
{t('referral.noReferrals')}
)}
{/* Earnings History */} {earnings?.items && earnings.items.length > 0 && (

{t('referral.earningsHistory')}

{earnings.items.map((earning) => (
{earning.referral_first_name || earning.referral_username || t('referral.anonymousReferral')}
{t(`referral.reasons.${earning.reason}`, earning.reason)} •{' '} {new Date(earning.created_at).toLocaleDateString(i18n.language)}
{formatPositive(earning.amount_rubles)}
))}
)} {/* ==================== Partner Application Section ==================== */} {/* Status: none — Become a Partner CTA */} {terms?.partner_section_visible !== false && showApplySection && (

{t('referral.partner.becomePartner')}

{t('referral.partner.becomePartnerDesc')}

)} {/* Status: pending — Application Under Review */} {terms?.partner_section_visible !== false && showPendingSection && (

{t('referral.partner.underReview')}

{t('referral.partner.underReviewDesc')}

{partnerStatus?.latest_application?.created_at && (

{t('referral.partner.submittedAt', { date: new Date(partnerStatus.latest_application.created_at).toLocaleDateString( i18n.language, ), })}

)}
)} {/* Status: approved — Partner Badge */} {terms?.partner_section_visible !== false && showApprovedSection && (

{t('referral.partner.partnerStatus')}

{t('referral.partner.active')}

{t('referral.partner.commissionInfo', { percent: partnerStatus?.commission_percent ?? 0, })}

{t('referral.withdrawal.goToWithdrawal')}
)} {/* Status: rejected — Rejection Notice */} {terms?.partner_section_visible !== false && showRejectedSection && (

{t('referral.partner.rejected')}

{partnerStatus?.latest_application?.admin_comment && (

{partnerStatus.latest_application.admin_comment}

)}
)} {/* ==================== Partner Campaigns Section ==================== */} {terms?.partner_section_visible !== false && isPartner && partnerStatus?.campaigns && partnerStatus.campaigns.length > 0 && (

{t('referral.partner.yourCampaigns')}

{partnerStatus.campaigns.map((campaign) => ( ))}
)} {/* ==================== Withdrawal Section (approved partners only) ==================== */} {terms?.partner_section_visible !== false && isPartner && (
{/* Withdrawal Balance Card */} {withdrawalBalance && (

{t('referral.withdrawal.title')}

{t('referral.withdrawal.available')}
{formatWithCurrency(withdrawalBalance.available_total / 100)}
{t('referral.withdrawal.totalEarned')}
{formatWithCurrency(withdrawalBalance.total_earned / 100)}
{t('referral.withdrawal.withdrawn')}
{formatWithCurrency(withdrawalBalance.withdrawn / 100)}
{t('referral.withdrawal.spent')}
{formatWithCurrency(withdrawalBalance.referral_spent / 100)}
{t('referral.withdrawal.pending')}
{formatWithCurrency(withdrawalBalance.pending / 100)}
{!withdrawalBalance.can_request && withdrawalBalance.cannot_request_reason ? (

{withdrawalBalance.cannot_request_reason}

) : ( withdrawalBalance.min_amount_kopeks > 0 && (

{t('referral.withdrawal.minAmount', { amount: formatWithCurrency(withdrawalBalance.min_amount_kopeks / 100), })}

) )}
)} {/* Withdrawal History */}

{t('referral.withdrawal.history')}

{withdrawalHistory?.items && withdrawalHistory.items.length > 0 ? (
{withdrawalHistory.items.map((item) => (
{formatWithCurrency(item.amount_rubles)} {t(`referral.withdrawal.status.${item.status}`, item.status)}
{new Date(item.created_at).toLocaleDateString(i18n.language)} {item.payment_details && ( •{' '} {item.payment_details.length > 40 ? `${item.payment_details.slice(0, 40)}...` : item.payment_details} )}
{item.admin_comment && (
{item.admin_comment}
)}
{item.status === 'pending' && ( )}
))}
) : (
{t('referral.withdrawal.noHistory')}
)}
)}
); }