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

212 lines
5.0 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';
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;
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]);
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
};
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-05 15:07:10 +07:00
className={`font-bold text-[${CHECKS_COLOR}]`}
>
2026-01-08 17:21:09 +07:00
{t('checks')}: {totalChecks}
</div>
<div
2026-01-05 15:07:10 +07:00
className={`font-bold text-[${VIOLATIONS_COLOR}]`}
>
2026-01-05 15:07:10 +07:00
{t('violations')}: {totalViolations}
</div>
</div>
</div>
</>
2025-12-16 15:03:32 +07:00
);
};