uchill/front_material/components/dashboard/RevenueChart.tsx

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>
);
};