import React, { useState, useEffect, useCallback } from 'react'; import { Link } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { useAuth } from '../context/AuthContext'; import { getUserUsage, redeemCode, getAffCode, transferAffQuota, getAffEarnings, Q, quotaToDollar } from '../api'; import { useCurrency } from '../context/SiteContext'; import CountUp from '../components/bits/CountUp'; import toast from 'react-hot-toast'; export default function Dashboard() { const { t } = useTranslation(); const { user, refreshUser } = useAuth(); const { symbol, rate } = useCurrency(); const [usage, setUsage] = useState(null); const [redeemInput, setRedeemInput] = useState(''); const [redeeming, setRedeeming] = useState(false); // Invitation / Aff const [affLink, setAffLink] = useState(''); const [affEarnings, setAffEarnings] = useState([]); const [showAffEarnings, setShowAffEarnings] = useState(false); const [affEarningsLoading, setAffEarningsLoading] = useState(false); const [transferAmount, setTransferAmount] = useState(''); const [transferring, setTransferring] = useState(false); const loadData = useCallback(async () => { try { const [usageRes, affRes] = await Promise.all([ getUserUsage(), getAffCode().catch(() => null), ]); if (usageRes.data.success) setUsage(usageRes.data.data); if (affRes?.data?.success && affRes.data.data) { setAffLink(`${window.location.origin}/register?aff=${affRes.data.data}`); } } catch (e) { /* interceptor */ } }, []); useEffect(() => { loadData(); }, [loadData]); const handleRedeem = async (e) => { e.preventDefault(); if (!redeemInput.trim()) return; setRedeeming(true); try { const res = await redeemCode(redeemInput.trim()); if (res.data.success) { toast.success(t('dashboard.redeemSuccess')); setRedeemInput(''); await Promise.all([loadData(), refreshUser()]); } } catch (err) { /* interceptor */ } setRedeeming(false); }; const loadAffEarnings = async () => { setAffEarningsLoading(true); try { const res = await getAffEarnings({ page: 1, page_size: 20 }); if (res.data.success && res.data.data) { setAffEarnings(res.data.data); } } catch (e) { /* interceptor */ } setAffEarningsLoading(false); }; const handleCopyAffLink = () => { if (!affLink) return; navigator.clipboard.writeText(affLink).then(() => { toast.success(t('topup.copied')); }).catch(() => { toast.error('Copy failed'); }); }; const handleTransfer = async () => { const val = parseInt(transferAmount); if (!val || val <= 0) { toast.error(t('topup.enterAmount')); return; } setTransferring(true); try { const res = await transferAffQuota({ quota: val }); if (res.data.success) { toast.success(res.data.message || t('topup.transferSuccess')); setTransferAmount(''); await Promise.all([loadData(), refreshUser()]); } } catch (e) { /* interceptor */ } setTransferring(false); }; const quota = usage?.quota ?? user?.quota ?? 0; const usedQuota = usage?.used_quota ?? user?.used_quota ?? 0; const requestCount = usage?.request_count ?? user?.request_count ?? 0; const balanceDollars = quota / Q * rate; return (
{t('dashboard.manageDesc')}
{t('dashboard.balance')}
{t('dashboard.quotaUnits', { count: quota.toLocaleString() })}
{t('dashboard.used')}
{t('dashboard.quotaUnits', { count: usedQuota.toLocaleString() })}
{t('dashboard.totalRequests')}
{t('dashboard.apiKeys')}
{t('dashboard.manageKeys')}
{t('nav.topup')}
{t('dashboard.topupDesc')}
{t('dashboard.packages')}
{t('dashboard.viewPlans')}
{t('dashboard.pricing')}
{t('dashboard.modelPrices')}
{t('dashboard.logs')}
{t('dashboard.viewLogs')}
{t('topup.inviteSubtitle')}
{/* Stats */}{t('topup.affAvailable')}
{symbol}{((user?.aff_quota || 0) / Q * rate).toFixed(2)}
{t('topup.affTotal')}
{symbol}{((user?.aff_history_quota || 0) / Q * rate).toFixed(2)}
{t('topup.affCount')}
{user?.aff_count || 0}
{t('topup.noEarnings')}
) : ({item.model_name}
{new Date(item.created_time * 1000).toLocaleString()} ยท {(item.commission_rate * 100).toFixed(1)}%