import { useCallback, useEffect, useRef, useState } from 'react'; import { useParams, useNavigate, Link } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { campaignsApi, CampaignBonusType } from '../api/campaigns'; import type { AdminCampaignChartData } from '../api/campaigns'; import { AdminBackButton } from '../components/admin'; import { DailyChart, PeriodComparison, StatCard } from '../components/stats'; import { PARTNER_STATS } from '../constants/partner'; import { useCurrency } from '../hooks/useCurrency'; import { copyToClipboard } from '../utils/clipboard'; import { useHaptic } from '../platform'; import { ChartIcon, CopyIcon, LinkIcon, UsersIcon } from '@/components/icons'; // Bonus type config const bonusTypeConfig: Record< CampaignBonusType, { labelKey: string; color: string; bgColor: string } > = { balance: { labelKey: 'admin.campaigns.bonusType.balance', color: 'text-success-400', bgColor: 'bg-success-500/20', }, subscription: { labelKey: 'admin.campaigns.bonusType.subscription', color: 'text-accent-400', bgColor: 'bg-accent-500/20', }, tariff: { labelKey: 'admin.campaigns.bonusType.tariff', color: 'text-purple-400', bgColor: 'bg-purple-500/20', }, none: { labelKey: 'admin.campaigns.bonusType.none', color: 'text-dark-400', bgColor: 'bg-dark-600', }, }; export default function AdminCampaignStats() { const { t, i18n } = useTranslation(); const { id } = useParams<{ id: string }>(); const numericId = id ? Number(id) : null; const isValidId = numericId !== null && !isNaN(numericId); const navigate = useNavigate(); const haptic = useHaptic(); const { formatWithCurrency } = useCurrency(); const [copiedBot, setCopiedBot] = useState(false); const [copiedWeb, setCopiedWeb] = useState(false); const [showUsers, setShowUsers] = useState(false); const copyBotTimer = useRef>(undefined); const copyWebTimer = useRef>(undefined); useEffect(() => { return () => { clearTimeout(copyBotTimer.current); clearTimeout(copyWebTimer.current); }; }, []); // Fetch stats const { data: stats, isLoading, error, } = useQuery({ queryKey: ['campaign-stats', id], queryFn: () => campaignsApi.getCampaignStats(numericId!), enabled: isValidId, staleTime: PARTNER_STATS.STATS_STALE_TIME, }); // Fetch registrations when users section is open const { data: registrationsData, isLoading: usersLoading } = useQuery({ queryKey: ['campaign-registrations', id], queryFn: () => campaignsApi.getCampaignRegistrations(numericId!, 1, 50), enabled: isValidId && showUsers, }); // Fetch chart data const { data: chartData, isLoading: chartLoading } = useQuery({ queryKey: ['campaign-chart-data', id], queryFn: () => campaignsApi.getChartData(numericId!), enabled: isValidId, staleTime: PARTNER_STATS.STATS_STALE_TIME, }); const handleCopy = useCallback( async (url: string, type: 'bot' | 'web') => { try { await copyToClipboard(url); haptic.notification('success'); if (type === 'bot') { setCopiedBot(true); clearTimeout(copyBotTimer.current); copyBotTimer.current = setTimeout( () => setCopiedBot(false), PARTNER_STATS.COPY_FEEDBACK_MS, ); } else { setCopiedWeb(true); clearTimeout(copyWebTimer.current); copyWebTimer.current = setTimeout( () => setCopiedWeb(false), PARTNER_STATS.COPY_FEEDBACK_MS, ); } } catch { haptic.notification('error'); } }, [haptic], ); const formatDate = useCallback( (date: string | null): string => { if (!date) return '-'; return new Date(date).toLocaleDateString(i18n.language, { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', }); }, [i18n.language], ); if (isLoading) { return (
); } if (error || !stats) { return (

{t('admin.campaigns.stats.title')}

{t('admin.campaigns.stats.loadError')}

); } return (
{/* Header */}

{stats.name}

{t(bonusTypeConfig[stats.bonus_type].labelKey)} {stats.is_active ? ( {t('admin.campaigns.stats.active')} ) : ( {t('admin.campaigns.stats.inactive')} )}
{/* Links */} {(stats.deep_link || stats.web_link) && (
{stats.deep_link && (
{t('admin.campaigns.stats.botLink')}
{stats.deep_link}
)} {stats.web_link && (
{t('admin.campaigns.stats.webLink')}
{stats.web_link}
)}
)} {/* Main Stats */}
{stats.registrations}
{t('admin.campaigns.stats.registrations')}
{formatWithCurrency(stats.total_revenue_kopeks / PARTNER_STATS.KOPEKS_DIVISOR)}
{t('admin.campaigns.stats.revenue')}
{stats.paid_users_count}
{t('admin.campaigns.stats.paidUsers')}
{stats.conversion_rate}%
{t('admin.campaigns.stats.conversion')}
{/* Detailed Stats */}

{t('admin.campaigns.stats.detailedStats')}

{t('admin.campaigns.stats.bonusesIssued')}
{stats.bonus_type === 'balance' && (
{formatWithCurrency(stats.balance_issued_kopeks / PARTNER_STATS.KOPEKS_DIVISOR)}
)} {stats.bonus_type === 'subscription' && (
{t('admin.campaigns.stats.subscriptionsIssued', { count: stats.subscription_issued, })}
)} {stats.bonus_type === 'tariff' && (
{t('admin.campaigns.stats.tariffsIssued', { count: stats.subscription_issued })}
)} {stats.bonus_type === 'none' && (
-
)}
{t('admin.campaigns.stats.avgRevenuePerUser')}
{formatWithCurrency( stats.avg_revenue_per_user_kopeks / PARTNER_STATS.KOPEKS_DIVISOR, )}
{t('admin.campaigns.stats.avgFirstPayment')}
{formatWithCurrency(stats.avg_first_payment_kopeks / PARTNER_STATS.KOPEKS_DIVISOR)}
{t('admin.campaigns.stats.trialSubscriptions')}
{t('admin.campaigns.stats.trialCount', { total: stats.trial_users_count, active: stats.active_trials_count, })}
{t('admin.campaigns.stats.trialConversion')}
{stats.trial_conversion_rate}%
{t('admin.campaigns.stats.lastRegistration')}
{formatDate(stats.last_registration)}
{/* Analytics Charts */}
{chartLoading ? (
) : chartData ? ( <> {/* Deposits vs Spending */}
{/* Top Registrations */} {chartData.top_registrations.length > 0 && (

{t('admin.campaigns.stats.topRegistrations')}

{chartData.top_registrations.map((reg) => (
{reg.full_name} {reg.is_active && ( {t('admin.campaigns.stats.active')} )} {reg.has_paid && !reg.is_active && ( {t('admin.campaigns.stats.paid')} )}
{new Date(reg.created_at).toLocaleDateString(i18n.language)}
{formatWithCurrency( reg.total_earnings_kopeks / PARTNER_STATS.KOPEKS_DIVISOR, )}
))}
)} ) : null}
{/* Users Section */}
{showUsers && (
{usersLoading ? (
) : registrationsData?.registrations.length === 0 ? (
{t('admin.campaigns.stats.noUsers')}
) : (
{registrationsData?.registrations.map((reg) => (
{reg.first_name || reg.username || `${t('admin.campaigns.stats.users')} #${reg.user_id}`}
{reg.telegram_id}
{reg.has_paid && ( {t('admin.campaigns.stats.paid')} )} {reg.has_subscription && ( {t('admin.campaigns.stats.hasSub')} )} {formatDate(reg.created_at)}
))}
)}
)}
); }