206 lines
5.5 KiB
TypeScript
206 lines
5.5 KiB
TypeScript
/**
|
|
* График доходов для Dashboard ментора
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import React, { useMemo } from 'react';
|
|
import dynamic from 'next/dynamic';
|
|
import { IncomeChartData } from '@/api/dashboard';
|
|
|
|
const ApexChart = dynamic(() => import('react-apexcharts'), { ssr: false });
|
|
|
|
interface RevenueChartProps {
|
|
data: IncomeChartData[];
|
|
loading?: boolean;
|
|
period?: 'day' | 'week' | 'month';
|
|
/** Показывать серию «Занятий» (по умолчанию true для дашборда) */
|
|
showLessons?: boolean;
|
|
/** Высота графика в px (по умолчанию 250) */
|
|
height?: number;
|
|
}
|
|
|
|
export const RevenueChart: React.FC<RevenueChartProps> = ({
|
|
data,
|
|
loading,
|
|
period = 'month',
|
|
showLessons = true,
|
|
height = 250,
|
|
}) => {
|
|
if (!data || data.length === 0) {
|
|
return (
|
|
<div
|
|
style={{
|
|
height: `${height}px`,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
color: 'var(--md-sys-color-on-surface-variant)',
|
|
fontSize: '14px',
|
|
}}
|
|
>
|
|
{loading ? 'Загрузка...' : 'Нет данных'}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const { categories, incomeSeries, lessonsSeries } = useMemo(() => {
|
|
const items = Array.isArray(data) ? data : [];
|
|
return {
|
|
categories: items.map((item) => item.date),
|
|
incomeSeries: items.map((item) => item.income),
|
|
lessonsSeries: items.map((item) => item.lessons),
|
|
};
|
|
}, [data]);
|
|
|
|
const options: any = useMemo(
|
|
() => ({
|
|
chart: {
|
|
id: 'mentor-income-chart',
|
|
toolbar: { show: false },
|
|
zoom: { enabled: false },
|
|
animations: {
|
|
enabled: true,
|
|
easing: 'easeinout',
|
|
speed: 400,
|
|
},
|
|
},
|
|
stroke: {
|
|
curve: 'smooth',
|
|
width: showLessons ? [3, 2] : 3,
|
|
},
|
|
colors: showLessons ? ['#6750A4', '#7D5260'] : ['#6750A4'],
|
|
dataLabels: {
|
|
enabled: false,
|
|
},
|
|
legend: {
|
|
show: showLessons,
|
|
position: 'top',
|
|
horizontalAlign: 'left',
|
|
markers: {
|
|
width: 10,
|
|
height: 10,
|
|
radius: 12,
|
|
},
|
|
labels: {
|
|
colors: 'var(--md-sys-color-on-surface)',
|
|
style: {
|
|
fontSize: '15px',
|
|
},
|
|
},
|
|
},
|
|
xaxis: {
|
|
categories,
|
|
axisBorder: { show: false },
|
|
axisTicks: { show: false },
|
|
labels: {
|
|
formatter: (val: string) => {
|
|
if (typeof val === 'string' && /^\d{2}\.\d{2}$/.test(val)) {
|
|
return val.split('.')[0];
|
|
}
|
|
return val;
|
|
},
|
|
style: {
|
|
colors: 'var(--md-sys-color-on-surface-variant)',
|
|
fontSize: '18px',
|
|
},
|
|
},
|
|
},
|
|
yaxis: showLessons
|
|
? [
|
|
{
|
|
seriesName: 'Доход',
|
|
labels: {
|
|
formatter: (val: number) => `${Math.round(val)} ₽`,
|
|
style: {
|
|
colors: 'var(--md-sys-color-on-surface-variant)',
|
|
fontSize: '18px',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
opposite: true,
|
|
seriesName: 'Занятий',
|
|
labels: {
|
|
style: {
|
|
colors: 'var(--md-sys-color-on-surface-variant)',
|
|
fontSize: '18px',
|
|
},
|
|
},
|
|
},
|
|
]
|
|
: [
|
|
{
|
|
labels: {
|
|
formatter: (val: number) => `${Math.round(val)} ₽`,
|
|
style: {
|
|
colors: 'var(--md-sys-color-on-surface-variant)',
|
|
fontSize: '18px',
|
|
},
|
|
},
|
|
},
|
|
],
|
|
fill: {
|
|
type: showLessons ? ['gradient', 'solid'] : 'gradient',
|
|
gradient: {
|
|
shade: 'dark',
|
|
type: 'vertical',
|
|
shadeIntensity: 0.6,
|
|
gradientToColors: ['rgba(103,80,164,0)'],
|
|
inverseColors: false,
|
|
opacityFrom: 0.7,
|
|
opacityTo: 0.15,
|
|
stops: [0, 25, 50, 75, 100],
|
|
},
|
|
},
|
|
tooltip: {
|
|
shared: showLessons,
|
|
intersect: false,
|
|
style: {
|
|
fontSize: '15px',
|
|
},
|
|
x: {
|
|
show: true,
|
|
},
|
|
y: {
|
|
formatter: (val: number, { seriesIndex }: any) =>
|
|
showLessons && seriesIndex === 1
|
|
? `${val} занятий`
|
|
: `${Math.round(Number(val)).toLocaleString('ru-RU')} ₽`,
|
|
},
|
|
},
|
|
grid: {
|
|
borderColor: 'var(--ios26-list-divider)',
|
|
strokeDashArray: 4,
|
|
yaxis: {
|
|
lines: { show: true },
|
|
},
|
|
},
|
|
markers: {
|
|
size: 0,
|
|
hover: {
|
|
size: 4,
|
|
},
|
|
},
|
|
}),
|
|
[categories, showLessons],
|
|
);
|
|
|
|
const series = useMemo(
|
|
() =>
|
|
showLessons
|
|
? [
|
|
{ name: 'Доход', type: 'area' as const, data: incomeSeries },
|
|
{ name: 'Занятий', type: 'line' as const, data: lessonsSeries },
|
|
]
|
|
: [{ name: 'Доход', type: 'area' as const, data: incomeSeries }],
|
|
[incomeSeries, lessonsSeries, showLessons],
|
|
);
|
|
|
|
return (
|
|
<div style={{ width: '100%', height: `${height}px` }}>
|
|
<ApexChart options={options} series={series} type="line" height={height} />
|
|
</div>
|
|
);
|
|
};
|