208 lines
8.2 KiB
TypeScript
208 lines
8.2 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { getReferralProfile, getReferralStats, getMyReferrals, type MyReferralItem } from '@/api/referrals';
|
||
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
|
||
import { useToast } from '@/contexts/ToastContext';
|
||
|
||
const formatCurrency = (v: number) =>
|
||
new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB', maximumFractionDigits: 0 }).format(v);
|
||
|
||
function formatDate(s: string) {
|
||
try {
|
||
return new Date(s).toLocaleDateString('ru-RU', { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||
} catch {
|
||
return s;
|
||
}
|
||
}
|
||
|
||
export function ReferralsPageContent() {
|
||
const { showToast } = useToast();
|
||
const [profile, setProfile] = useState<any>(null);
|
||
const [stats, setStats] = useState<any>(null);
|
||
const [referralsList, setReferralsList] = useState<{ direct: MyReferralItem[]; indirect: MyReferralItem[] } | null>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [copied, setCopied] = useState(false);
|
||
|
||
useEffect(() => {
|
||
Promise.all([
|
||
getReferralProfile().then(setProfile),
|
||
getReferralStats().then(setStats),
|
||
getMyReferrals().then(setReferralsList).catch(() => setReferralsList({ direct: [], indirect: [] })),
|
||
])
|
||
.finally(() => setLoading(false));
|
||
}, []);
|
||
|
||
const copyLink = () => {
|
||
if (profile?.referral_link) {
|
||
navigator.clipboard.writeText(profile.referral_link);
|
||
setCopied(true);
|
||
showToast('Реферальная ссылка скопирована', 'success');
|
||
setTimeout(() => setCopied(false), 2000);
|
||
}
|
||
};
|
||
|
||
if (loading) {
|
||
return <LoadingSpinner size="medium" />;
|
||
}
|
||
|
||
if (!profile) {
|
||
return (
|
||
<p style={{ color: 'var(--md-sys-color-on-surface-variant)', fontSize: 14 }}>
|
||
Реферальная программа недоступна
|
||
</p>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
letterSpacing: '0.05em',
|
||
color: 'var(--md-sys-color-on-surface-variant)',
|
||
marginBottom: 8,
|
||
}}
|
||
>
|
||
РЕФЕРАЛЬНАЯ ССЫЛКА
|
||
</div>
|
||
<div className="referral-link-row" style={{ display: 'flex', gap: 8 }}>
|
||
<input
|
||
type="text"
|
||
readOnly
|
||
value={profile.referral_link || ''}
|
||
style={{
|
||
flex: 1,
|
||
minWidth: 0,
|
||
padding: '12px 16px',
|
||
borderRadius: 12,
|
||
border: '1px solid var(--md-sys-color-outline)',
|
||
background: 'var(--md-sys-color-surface-container-low)',
|
||
color: 'var(--md-sys-color-on-surface)',
|
||
fontSize: 14,
|
||
}}
|
||
/>
|
||
<button
|
||
type="button"
|
||
onClick={copyLink}
|
||
style={{
|
||
padding: '12px 20px',
|
||
borderRadius: 12,
|
||
border: 'none',
|
||
background: 'var(--md-sys-color-primary)',
|
||
color: 'var(--md-sys-color-on-primary)',
|
||
fontSize: 14,
|
||
fontWeight: 500,
|
||
cursor: 'pointer',
|
||
}}
|
||
>
|
||
{copied ? 'Скопировано' : 'Копировать'}
|
||
</button>
|
||
</div>
|
||
{profile.referral_code && (
|
||
<p style={{ fontSize: 12, color: 'var(--md-sys-color-on-surface-variant)', marginTop: 8 }}>
|
||
Код: <strong>{profile.referral_code}</strong>
|
||
</p>
|
||
)}
|
||
</div>
|
||
{stats && (
|
||
<div
|
||
className="referral-stats-grid"
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(auto-fit, minmax(120px, 1fr))',
|
||
gap: 12,
|
||
}}
|
||
>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Уровень
|
||
</div>
|
||
<div style={{ fontSize: 16, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{stats.current_level?.name || '-'}
|
||
</div>
|
||
</div>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Баллы
|
||
</div>
|
||
<div style={{ fontSize: 16, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{stats.total_points ?? 0}
|
||
</div>
|
||
</div>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Рефералов
|
||
</div>
|
||
<div style={{ fontSize: 16, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{stats.referrals?.total ?? 0}
|
||
</div>
|
||
</div>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 11, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Заработано
|
||
</div>
|
||
<div style={{ fontSize: 16, fontWeight: 600, color: 'var(--md-sys-color-primary)' }}>
|
||
{formatCurrency(stats.earnings?.total ?? stats.bonus_account?.balance ?? 0)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Список приглашённых рефералов */}
|
||
{referralsList && (referralsList.direct.length > 0 || referralsList.indirect.length > 0) && (
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
letterSpacing: '0.05em',
|
||
color: 'var(--md-sys-color-on-surface-variant)',
|
||
marginBottom: 8,
|
||
}}
|
||
>
|
||
ПРИГЛАШЁННЫЕ
|
||
</div>
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||
{referralsList.direct.length > 0 && (
|
||
<div>
|
||
<div style={{ fontSize: 12, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 6 }}>
|
||
Прямые рефералы ({referralsList.direct.length})
|
||
</div>
|
||
<ul className="referral-list" style={{ margin: 0, paddingLeft: 20, listStyle: 'disc' }}>
|
||
{referralsList.direct.map((r: MyReferralItem, i: number) => (
|
||
<li key={i} style={{ marginBottom: 4, fontSize: 14, color: 'var(--md-sys-color-on-surface)', wordBreak: 'break-word' }}>
|
||
{r.email} — {r.level}, {r.total_points} баллов, зарегистрирован {formatDate(r.created_at)}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
{referralsList.indirect.length > 0 && (
|
||
<div>
|
||
<div style={{ fontSize: 12, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 6 }}>
|
||
Рефералы ваших рефералов ({referralsList.indirect.length})
|
||
</div>
|
||
<ul className="referral-list" style={{ margin: 0, paddingLeft: 20, listStyle: 'disc' }}>
|
||
{referralsList.indirect.map((r: MyReferralItem, i: number) => (
|
||
<li key={i} style={{ marginBottom: 4, fontSize: 14, color: 'var(--md-sys-color-on-surface)', wordBreak: 'break-word' }}>
|
||
{r.email} — {r.level}, {r.total_points} баллов, зарегистрирован {formatDate(r.created_at)}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
{referralsList && referralsList.direct.length === 0 && referralsList.indirect.length === 0 && (
|
||
<p style={{ fontSize: 14, color: 'var(--md-sys-color-on-surface-variant)' }}>
|
||
Пока никого нет. Поделитесь реферальной ссылкой — когда кто-то зарегистрируется по ней, он появится здесь и вы получите уведомление.
|
||
</p>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|