2026-07-11 00:34:38 +03:30
|
|
|
'use client';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
Bar,
|
|
|
|
|
BarChart,
|
|
|
|
|
CartesianGrid,
|
|
|
|
|
Cell,
|
|
|
|
|
ResponsiveContainer,
|
|
|
|
|
Tooltip,
|
|
|
|
|
XAxis,
|
|
|
|
|
YAxis,
|
|
|
|
|
} from 'recharts';
|
|
|
|
|
import type { TodayChartBucket } from '@/types/today';
|
2026-07-11 22:32:37 +03:30
|
|
|
import { TodayChartFrame } from '@/components/today/TodayChartFrame';
|
2026-07-11 00:34:38 +03:30
|
|
|
import {
|
|
|
|
|
TODAY_CHART_AXIS_COLOR,
|
|
|
|
|
TODAY_CHART_COLORS,
|
|
|
|
|
TODAY_CHART_GRID_COLOR,
|
|
|
|
|
TODAY_CHART_TOOLTIP_BG,
|
|
|
|
|
TODAY_CHART_TOOLTIP_BORDER,
|
|
|
|
|
} from '@/components/today/chart-theme';
|
|
|
|
|
|
|
|
|
|
interface TodayBarChartProps {
|
|
|
|
|
data: TodayChartBucket[];
|
2026-07-11 03:12:56 +03:30
|
|
|
colorForCode?: (code: string, index: number) => string;
|
2026-07-11 00:34:38 +03:30
|
|
|
}
|
|
|
|
|
|
2026-07-11 03:12:56 +03:30
|
|
|
export function TodayBarChart({ data, colorForCode }: TodayBarChartProps) {
|
2026-07-11 00:34:38 +03:30
|
|
|
const chartData = data.map((item) => ({
|
|
|
|
|
...item,
|
|
|
|
|
shortLabel: truncateLabel(item.label),
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
return (
|
2026-07-11 22:32:37 +03:30
|
|
|
<TodayChartFrame>
|
|
|
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
|
|
|
<BarChart
|
|
|
|
|
data={chartData}
|
|
|
|
|
margin={{ top: 8, right: 8, left: -12, bottom: 0 }}
|
|
|
|
|
>
|
2026-07-11 00:34:38 +03:30
|
|
|
<CartesianGrid stroke={TODAY_CHART_GRID_COLOR} vertical={false} />
|
|
|
|
|
<XAxis
|
|
|
|
|
dataKey="shortLabel"
|
2026-07-11 03:12:56 +03:30
|
|
|
tick={
|
|
|
|
|
colorForCode
|
|
|
|
|
? (props) => {
|
|
|
|
|
const { x, y, payload } = props as {
|
|
|
|
|
x: number;
|
|
|
|
|
y: number;
|
|
|
|
|
payload: { value: string };
|
|
|
|
|
};
|
|
|
|
|
const index = chartData.findIndex((row) => row.shortLabel === payload.value);
|
|
|
|
|
const entry = chartData[index];
|
|
|
|
|
const fill =
|
|
|
|
|
entry != null
|
|
|
|
|
? colorForCode(entry.code, index >= 0 ? index : 0)
|
|
|
|
|
: TODAY_CHART_AXIS_COLOR;
|
|
|
|
|
return (
|
|
|
|
|
<text
|
|
|
|
|
x={x}
|
|
|
|
|
y={y}
|
|
|
|
|
dy={16}
|
|
|
|
|
textAnchor="middle"
|
|
|
|
|
fill={fill}
|
|
|
|
|
fontSize={11}
|
|
|
|
|
>
|
|
|
|
|
{payload.value}
|
|
|
|
|
</text>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
: { fill: TODAY_CHART_AXIS_COLOR, fontSize: 11 }
|
|
|
|
|
}
|
2026-07-11 00:34:38 +03:30
|
|
|
axisLine={{ stroke: TODAY_CHART_GRID_COLOR }}
|
|
|
|
|
tickLine={false}
|
|
|
|
|
interval={0}
|
|
|
|
|
/>
|
|
|
|
|
<YAxis
|
|
|
|
|
allowDecimals={false}
|
|
|
|
|
tick={{ fill: TODAY_CHART_AXIS_COLOR, fontSize: 11 }}
|
|
|
|
|
axisLine={false}
|
|
|
|
|
tickLine={false}
|
|
|
|
|
width={32}
|
|
|
|
|
/>
|
|
|
|
|
<Tooltip
|
|
|
|
|
cursor={{ fill: 'rgba(0, 188, 255, 0.08)' }}
|
|
|
|
|
contentStyle={{
|
|
|
|
|
backgroundColor: TODAY_CHART_TOOLTIP_BG,
|
|
|
|
|
border: `1px solid ${TODAY_CHART_TOOLTIP_BORDER}`,
|
|
|
|
|
borderRadius: '6px',
|
|
|
|
|
color: '#f5f9ff',
|
|
|
|
|
fontSize: '12px',
|
|
|
|
|
}}
|
|
|
|
|
labelFormatter={(_, payload) => {
|
|
|
|
|
const row = payload?.[0]?.payload as TodayChartBucket | undefined;
|
|
|
|
|
return row?.label ?? '';
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
<Bar dataKey="count" radius={[4, 4, 0, 0]} maxBarSize={48}>
|
|
|
|
|
{chartData.map((entry, index) => (
|
|
|
|
|
<Cell
|
|
|
|
|
key={entry.code}
|
2026-07-11 03:12:56 +03:30
|
|
|
fill={
|
|
|
|
|
colorForCode?.(entry.code, index) ??
|
|
|
|
|
TODAY_CHART_COLORS[index % TODAY_CHART_COLORS.length]
|
|
|
|
|
}
|
2026-07-11 00:34:38 +03:30
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</Bar>
|
|
|
|
|
</BarChart>
|
2026-07-11 22:32:37 +03:30
|
|
|
</ResponsiveContainer>
|
|
|
|
|
</TodayChartFrame>
|
2026-07-11 00:34:38 +03:30
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function truncateLabel(label: string, max = 12): string {
|
|
|
|
|
if (label.length <= max) return label;
|
|
|
|
|
return `${label.slice(0, max - 1)}…`;
|
|
|
|
|
}
|