import { useEffect, useMemo, 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 { usePlatform } from '../platform'; 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'; import { StatCard } from '@/components/stats'; import { ArrowDownIcon, ArrowUpIcon, BanknotesIcon, CardIcon, CheckIcon, ClockIcon, CopyIcon, ExclamationIcon, GiftIcon, LinkIcon, PartnerIcon, PercentIcon, ShareIcon, TelegramIcon, UserPlusIcon, UsersIcon, WalletIcon, } from '@/components/icons'; 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 [copiedLink, setCopiedLink] = useState<'cabinet' | 'bot' | null>(null); 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 botReferralLink = info?.bot_referral_link || ''; 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 programTerms = useMemo(() => { if (!terms) return null; 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 gridColsMap: Record = { 2: 'md:grid-cols-2', 3: 'md:grid-cols-3', 4: 'md:grid-cols-4', }; const gridCols = gridColsMap[cardCount] ?? 'md:grid-cols-4'; return (

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

} tone="neutral" /> } tone="neutral" /> {showNewUserBonus && ( } tone="success" /> )} {showInviterBonus && ( } tone="success" /> )}
); }, [terms, t, formatAmount, formatPositive, currencySymbol]); const copyLink = async (link: string, type: 'cabinet' | 'bot') => { if (!link) return; try { await copyToClipboard(link); setCopiedLink(type); if (copyTimerRef.current) clearTimeout(copyTimerRef.current); copyTimerRef.current = setTimeout(() => setCopiedLink(null), 2000); } catch { // clipboard write failed silently } }; const { openTelegramLink } = usePlatform(); 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(() => {}); return; } const telegramUrl = `https://t.me/share/url?url=${encodeURIComponent( referralLink, )}&text=${encodeURIComponent(shareText)}`; openTelegramLink(telegramUrl); }; 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 */}
} tone="neutral" subValue={`${info?.active_referrals || 0} ${t('referral.stats.activeReferrals').toLowerCase()}`} />
} tone="success" /> } tone="accent" />
{/* Referral Links */}

{t('referral.yourLink')}

{/* Bot link */} {botReferralLink && (
{t('referral.botLink')}
)} {/* Cabinet link */}
{t('referral.cabinetLink')}

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

{/* Program Terms */} {programTerms} {/* 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')}

} tone="success" />
} tone="neutral" /> } tone="neutral" /> } tone="neutral" /> } tone="warning" />
{!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')}
)}
)}
); }