Files
no-copy-frontend/src/app/components/StackedBarChart.tsx
T

229 lines
5.7 KiB
TypeScript
Raw Normal View History

'use client'
2025-12-16 15:03:32 +07:00
import { useTranslations } from 'next-intl';
2026-01-05 15:07:10 +07:00
import { useMemo } from 'react';
2025-12-16 15:03:32 +07:00
import { BarChart, Bar, Cell, XAxis, Tooltip } from 'recharts';
2026-01-09 11:08:31 +07:00
import { pluralize } from '@/app/lib/pluralize';
2026-01-09 11:41:26 +07:00
import { useStoreWithDevtools } from '@/app/stores/useStoreWithDevtools';
2025-12-16 15:03:32 +07:00
type Files = {
data: {
name: string,
checks: number,
violations: number
}[]
}
interface BarShapeProps {
x: number;
y: number;
width: number;
height: number;
fill: string;
radius?: number[];
}
export const StackedBarChart = ({ data }: Files) => {
const t = useTranslations('Global');
2026-01-05 15:07:10 +07:00
const CHECKS_COLOR = '#6366f1';
const VIOLATIONS_COLOR = '#f08c00'
const BORDER_RADIUS = 5;
const STROKE_COLOR = "#dfdede24";
const STROKE_WIDTH = 1;
2026-01-09 11:41:26 +07:00
/* const { totalChecks, totalViolations } = useMemo(() => ({
totalChecks: data.reduce((sum, item) => sum + (item.checks || 0), 0),
totalViolations: data.reduce((sum, item) => sum + (item.violations || 0), 0)
}), [data]); */
const totalChecks = useStoreWithDevtools(s => s.value1);
const totalViolations = useStoreWithDevtools(s => s.value2);
2025-12-16 15:03:32 +07:00
const renderBarWithPartialBorder = (props: BarShapeProps) => {
const { x, y, width, height, fill } = props;
return (
<>
2025-12-17 12:50:01 +07:00
{/* Основной прямоугольник с закругленными углами */}
2025-12-16 15:03:32 +07:00
<rect
x={x}
y={y}
width={width}
height={height}
fill={fill}
2026-01-05 15:07:10 +07:00
rx={BORDER_RADIUS}
ry={BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
/>
2025-12-17 12:50:01 +07:00
{/* Левая граница (центральная часть, без закруглений) */}
2025-12-16 15:03:32 +07:00
<line
x1={x}
2026-01-05 15:07:10 +07:00
y1={y + BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
x2={x}
2026-01-05 15:07:10 +07:00
y2={y + height - BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
/>
2025-12-17 12:50:01 +07:00
{/* Правая граница (центральная часть, без закруглений) */}
2025-12-16 15:03:32 +07:00
<line
x1={x + width}
2026-01-05 15:07:10 +07:00
y1={y + BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
x2={x + width}
2026-01-05 15:07:10 +07:00
y2={y + height - BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
/>
2025-12-17 12:50:01 +07:00
{/* Нижняя граница (прямая часть между закруглениями) */}
2025-12-16 15:03:32 +07:00
<line
2026-01-05 15:07:10 +07:00
x1={x + BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
y1={y + height}
2026-01-05 15:07:10 +07:00
x2={x + width - BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
y2={y + height}
2025-12-17 12:50:01 +07:00
/>
{/* Верхняя граница (прямая часть между закруглениями) */}
<line
2026-01-05 15:07:10 +07:00
x1={x + BORDER_RADIUS}
2025-12-17 12:50:01 +07:00
y1={y}
2026-01-05 15:07:10 +07:00
x2={x + width - BORDER_RADIUS}
2025-12-17 12:50:01 +07:00
y2={y}
2025-12-16 15:03:32 +07:00
/>
</>
);
};
const renderBarWithTopBorder = (props: BarShapeProps) => {
const { x, y, width, height, fill } = props;
if (height <= 0 || width <= 0) return null;
return (
<>
2025-12-17 12:50:01 +07:00
{/* Основной прямоугольник с закругленными углами */}
<rect
x={x}
y={y}
width={width}
height={height}
2025-12-16 15:03:32 +07:00
fill={fill}
2026-01-05 15:07:10 +07:00
rx={BORDER_RADIUS}
ry={BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
/>
2025-12-17 12:50:01 +07:00
{/* Левая граница (центральная часть, без закруглений) */}
2025-12-16 15:03:32 +07:00
<line
x1={x}
2026-01-05 15:07:10 +07:00
y1={y + BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
x2={x}
2026-01-05 15:07:10 +07:00
y2={y + height - BORDER_RADIUS}
stroke={STROKE_COLOR}
strokeWidth={STROKE_WIDTH}
2025-12-16 15:03:32 +07:00
/>
2025-12-17 12:50:01 +07:00
{/* Правая граница (центральная часть, без закруглений) */}
2025-12-16 15:03:32 +07:00
<line
x1={x + width}
2026-01-05 15:07:10 +07:00
y1={y + BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
x2={x + width}
2026-01-05 15:07:10 +07:00
y2={y + height - BORDER_RADIUS}
stroke={STROKE_COLOR}
strokeWidth={STROKE_WIDTH}
2025-12-17 12:50:01 +07:00
/>
{/* Нижняя граница (прямая часть между закруглениями) */}
<line
2026-01-05 15:07:10 +07:00
x1={x + BORDER_RADIUS}
2025-12-17 12:50:01 +07:00
y1={y + height}
2026-01-05 15:07:10 +07:00
x2={x + width - BORDER_RADIUS}
2025-12-16 15:03:32 +07:00
y2={y + height}
2026-01-05 15:07:10 +07:00
stroke={STROKE_COLOR}
strokeWidth={STROKE_WIDTH}
2025-12-16 15:03:32 +07:00
/>
2025-12-17 12:50:01 +07:00
{/* Верхняя граница (прямая часть между закруглениями) */}
<line
2026-01-05 15:07:10 +07:00
x1={x + BORDER_RADIUS}
2025-12-17 12:50:01 +07:00
y1={y}
2026-01-05 15:07:10 +07:00
x2={x + width - BORDER_RADIUS}
2025-12-17 12:50:01 +07:00
y2={y}
2026-01-05 15:07:10 +07:00
stroke={STROKE_COLOR}
strokeWidth={STROKE_WIDTH}
2025-12-16 15:03:32 +07:00
/>
</>
2025-12-17 12:50:01 +07:00
);
2025-12-16 15:03:32 +07:00
};
2026-01-09 11:08:31 +07:00
const pluralizeCheks = (number: number) => {
const translate = [t('check'), t('checks-few'), t('checks')];
return pluralize(number, translate[0], translate[1], translate[2]);
};
const pluralizeViolations = (number: number) => {
const translate = [t('violation'), t('violations-few'), t('violations')];
return pluralize(number, translate[0], translate[1], translate[2]);
};
2025-12-16 15:03:32 +07:00
return (
<>
2026-01-05 16:15:07 +07:00
<div className="stacked-bar-chart-wrapper">
<div className="stacked-bar-chart">
<BarChart
style={{ width: '100%', maxWidth: '600px', maxHeight: '210px', aspectRatio: 1.618 }}
responsive
data={data}
barCategoryGap={15}
margin={{
top: 20,
right: 0,
left: 0,
bottom: 5,
}}
>
{/* <Tooltip /> */}
<XAxis
dataKey="name"
2026-01-05 15:07:10 +07:00
stroke={CHECKS_COLOR}
fontSize={12}
axisLine={false}
/>
<Bar
dataKey="checks"
stackId="a"
2026-01-05 15:07:10 +07:00
label={{ position: 'top', fill: CHECKS_COLOR, fontSize: 12 }}
radius={5}
shape={renderBarWithPartialBorder as any}
>
{data.map((_entry, index) => (
2026-01-05 15:07:10 +07:00
<Cell key={`cell-${index}`} fill={CHECKS_COLOR} />
))}
</Bar>
<Bar
dataKey="violations"
stackId="b"
2026-01-05 15:07:10 +07:00
label={{ position: 'top', fill: CHECKS_COLOR, fontSize: 12 }}
radius={5}
shape={renderBarWithTopBorder as any}
>
{data.map((_entry, index) => (
2026-01-05 15:07:10 +07:00
<Cell key={`cell-${index}`} fill={VIOLATIONS_COLOR} />
))}
</Bar>
</BarChart>
</div>
<div className="protection-stat-total-info">
<div
2026-01-09 11:08:31 +07:00
className={`font-bold`}
2026-01-09 11:41:26 +07:00
style={{ color: CHECKS_COLOR }}
>
2026-01-09 11:08:31 +07:00
{totalChecks} {pluralizeCheks(totalChecks)}
</div>
<div
2026-01-09 11:08:31 +07:00
className={`font-bold`}
2026-01-09 11:41:26 +07:00
style={{ color: VIOLATIONS_COLOR }}
>
2026-01-09 11:08:31 +07:00
{totalViolations} {pluralizeViolations(totalViolations)}
</div>
</div>
</div>
</>
2025-12-16 15:03:32 +07:00
);
};