init
This commit is contained in:
105
components/volume-trend-chart.tsx
Normal file
105
components/volume-trend-chart.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
|
||||
type WorkoutDataPoint = {
|
||||
label: string;
|
||||
date: string;
|
||||
name: string;
|
||||
volumeKg: number;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
data: WorkoutDataPoint[];
|
||||
};
|
||||
|
||||
const COLORS: Record<string, string> = {};
|
||||
const PALETTE = ["var(--color-accent)", "#5b9bd5", "#70ad47", "#ffc000", "#ed7d31"];
|
||||
|
||||
function getColor(name: string): string {
|
||||
if (!COLORS[name]) {
|
||||
COLORS[name] = PALETTE[Object.keys(COLORS).length % PALETTE.length];
|
||||
}
|
||||
return COLORS[name];
|
||||
}
|
||||
|
||||
export function VolumeTrendChart({ data }: Props) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
useEffect(() => setMounted(true), []);
|
||||
|
||||
if (!mounted) {
|
||||
return (
|
||||
<div className="w-full rounded-lg border border-muted/40 bg-surface p-4">
|
||||
<div className="mb-2 h-4 w-56 animate-pulse rounded bg-muted/30" />
|
||||
<div className="h-[220px] animate-pulse rounded bg-muted/20" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const workoutNames = [...new Set(data.map((d) => d.name))];
|
||||
|
||||
const chartData: Record<string, string | number>[] = [];
|
||||
const grouped = new Map<string, Record<string, string | number>>();
|
||||
|
||||
for (const point of data) {
|
||||
const key = point.date;
|
||||
if (!grouped.has(key)) {
|
||||
grouped.set(key, { label: point.label });
|
||||
}
|
||||
const entry = grouped.get(key)!;
|
||||
entry[point.name] = ((entry[point.name] as number) || 0) + point.volumeKg;
|
||||
}
|
||||
|
||||
for (const entry of grouped.values()) {
|
||||
chartData.push(entry);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full rounded-lg border border-muted/40 bg-surface p-4">
|
||||
<div className="mb-2 text-sm text-fg/60">
|
||||
Trend wolumenu treningowego wg typu
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={220}>
|
||||
<BarChart data={chartData} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>
|
||||
<XAxis dataKey="label" stroke="var(--color-fg)" opacity={0.5} fontSize={11} />
|
||||
<YAxis stroke="var(--color-fg)" opacity={0.5} fontSize={11} width={48} />
|
||||
<Tooltip
|
||||
cursor={{ fill: "var(--color-bg)" }}
|
||||
contentStyle={{
|
||||
background: "var(--color-bg)",
|
||||
border: "1px solid var(--color-muted)",
|
||||
borderRadius: 8,
|
||||
fontSize: 12,
|
||||
color: "var(--color-fg)",
|
||||
}}
|
||||
formatter={(value) => [
|
||||
`${Math.round(Number(value)).toLocaleString("pl-PL")} kg`,
|
||||
"Wolumen",
|
||||
]}
|
||||
/>
|
||||
<Legend
|
||||
wrapperStyle={{ fontSize: 12, color: "var(--color-fg)" }}
|
||||
/>
|
||||
{workoutNames.map((name) => (
|
||||
<Bar
|
||||
key={name}
|
||||
dataKey={name}
|
||||
fill={getColor(name)}
|
||||
radius={[4, 4, 0, 0]}
|
||||
stackId="volume"
|
||||
/>
|
||||
))}
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user