130 lines
4.7 KiB
TypeScript
130 lines
4.7 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import { getIncomeStats } from '@/api/income';
|
||
import { LoadingSpinner } from '@/components/common/LoadingSpinner';
|
||
|
||
const formatCurrency = (v: number) =>
|
||
new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB', maximumFractionDigits: 0 }).format(v);
|
||
|
||
export function ProfileAnalyticsTab() {
|
||
const [data, setData] = useState<any>(null);
|
||
const [loading, setLoading] = useState(true);
|
||
const [period, setPeriod] = useState<'day' | 'week' | 'month'>('month');
|
||
|
||
useEffect(() => {
|
||
setLoading(true);
|
||
getIncomeStats({ period })
|
||
.then(setData)
|
||
.catch(() => setData(null))
|
||
.finally(() => setLoading(false));
|
||
}, [period]);
|
||
|
||
if (loading && !data) {
|
||
return <LoadingSpinner size="medium" />;
|
||
}
|
||
|
||
if (!data) {
|
||
return (
|
||
<p style={{ color: 'var(--md-sys-color-on-surface-variant)', fontSize: 14 }}>
|
||
Нет данных о доходах
|
||
</p>
|
||
);
|
||
}
|
||
|
||
const s = data.summary || {};
|
||
return (
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||
{(['day', 'week', 'month'] as const).map((p) => (
|
||
<button
|
||
key={p}
|
||
type="button"
|
||
onClick={() => setPeriod(p)}
|
||
style={{
|
||
padding: '8px 16px',
|
||
borderRadius: 8,
|
||
border: period === p ? 'none' : '1px solid var(--md-sys-color-outline)',
|
||
background: period === p ? 'var(--md-sys-color-primary)' : 'transparent',
|
||
color: period === p ? 'var(--md-sys-color-on-primary)' : 'var(--md-sys-color-on-surface)',
|
||
fontSize: 14,
|
||
cursor: 'pointer',
|
||
}}
|
||
>
|
||
{p === 'day' ? 'День' : p === 'week' ? 'Неделя' : 'Месяц'}
|
||
</button>
|
||
))}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: 'grid',
|
||
gridTemplateColumns: 'repeat(auto-fit, minmax(140px, 1fr))',
|
||
gap: 16,
|
||
}}
|
||
>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 12, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Общий доход
|
||
</div>
|
||
<div style={{ fontSize: 20, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{formatCurrency(s.total_income || 0)}
|
||
</div>
|
||
</div>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 12, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Занятий
|
||
</div>
|
||
<div style={{ fontSize: 20, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{s.total_lessons || 0}
|
||
</div>
|
||
</div>
|
||
<div className="ios26-panel" style={{ padding: 16 }}>
|
||
<div style={{ fontSize: 12, color: 'var(--md-sys-color-on-surface-variant)', marginBottom: 4 }}>
|
||
Средняя цена
|
||
</div>
|
||
<div style={{ fontSize: 20, fontWeight: 600, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{formatCurrency(s.average_lesson_price || 0)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{data.top_lessons && data.top_lessons.length > 0 && (
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
letterSpacing: '0.05em',
|
||
color: 'var(--md-sys-color-on-surface-variant)',
|
||
marginBottom: 12,
|
||
}}
|
||
>
|
||
ТОП ЗАНЯТИЙ ПО ДОХОДАМ
|
||
</div>
|
||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||
{data.top_lessons.slice(0, 5).map((item: any, i: number) => (
|
||
<div
|
||
key={i}
|
||
style={{
|
||
display: 'flex',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
padding: 12,
|
||
borderRadius: 12,
|
||
background: 'var(--md-sys-color-surface-container-low)',
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 14, color: 'var(--md-sys-color-on-surface)' }}>
|
||
{item.lesson_title || item.target_name}
|
||
</span>
|
||
<span style={{ fontSize: 14, fontWeight: 600, color: 'var(--md-sys-color-primary)' }}>
|
||
{formatCurrency(item.total_income || 0)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|