2026-06-16 09:43:48 +02:00
|
|
|
"use client";
|
|
|
|
|
|
2026-06-16 11:51:10 +02:00
|
|
|
import { useEffect, useState } from "react";
|
2026-06-16 09:43:48 +02:00
|
|
|
import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
|
|
|
|
|
|
|
|
|
|
type ExerciseProgressChartProps = {
|
|
|
|
|
name: string;
|
2026-06-16 11:11:19 +02:00
|
|
|
data: { label: string; volumeKg: number; topWeightKg?: number; e1rmKg?: number }[];
|
2026-06-16 09:43:48 +02:00
|
|
|
};
|
|
|
|
|
|
2026-06-16 11:11:19 +02:00
|
|
|
function E1rmDelta({ data }: { data: ExerciseProgressChartProps["data"] }) {
|
|
|
|
|
const points = data.filter((d) => d.e1rmKg != null).slice(-5);
|
|
|
|
|
if (points.length < 2) return null;
|
|
|
|
|
|
|
|
|
|
const first = points[0].e1rmKg!;
|
|
|
|
|
const last = points[points.length - 1].e1rmKg!;
|
|
|
|
|
const diffKg = Math.round((last - first) * 10) / 10;
|
|
|
|
|
const diffPct = Math.round(((last - first) / first) * 100);
|
|
|
|
|
const sign = diffKg >= 0 ? "+" : "";
|
|
|
|
|
const color = diffKg > 0 ? "text-emerald-400" : diffKg < 0 ? "text-rose-400" : "text-fg/40";
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<span className={`text-xs font-medium tabular-nums ${color}`}>
|
|
|
|
|
E1RM {sign}{diffKg} kg ({sign}{diffPct}%)
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-16 09:43:48 +02:00
|
|
|
export function ExerciseProgressChart({ name, data }: ExerciseProgressChartProps) {
|
2026-06-16 11:51:10 +02:00
|
|
|
const [mounted, setMounted] = useState(false);
|
|
|
|
|
useEffect(() => setMounted(true), []);
|
|
|
|
|
|
|
|
|
|
if (data.length < 2) return null;
|
|
|
|
|
|
|
|
|
|
if (!mounted) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="w-full rounded-lg border border-muted/40 bg-surface p-4">
|
|
|
|
|
<div className="mb-2 h-4 w-32 animate-pulse rounded bg-muted/30" />
|
|
|
|
|
<div className="h-[150px] animate-pulse rounded bg-muted/20" />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-06-16 09:43:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="w-full rounded-lg border border-muted/40 bg-surface p-4">
|
2026-06-16 11:11:19 +02:00
|
|
|
<div className="mb-2 flex items-baseline justify-between gap-2">
|
|
|
|
|
<span className="text-sm text-fg/60">{name}</span>
|
|
|
|
|
<E1rmDelta data={data} />
|
|
|
|
|
</div>
|
2026-06-16 09:43:48 +02:00
|
|
|
<ResponsiveContainer width="100%" height={150}>
|
|
|
|
|
<LineChart data={data} margin={{ top: 8, right: 8, left: 0, bottom: 0 }}>
|
|
|
|
|
<CartesianGrid stroke="var(--color-muted)" opacity={0.3} vertical={false} />
|
|
|
|
|
<XAxis dataKey="label" stroke="var(--color-fg)" opacity={0.5} fontSize={12} />
|
|
|
|
|
<YAxis yAxisId="volume" stroke="var(--color-accent)" opacity={0.7} fontSize={12} width={48} />
|
|
|
|
|
<YAxis
|
|
|
|
|
yAxisId="weight"
|
|
|
|
|
orientation="right"
|
|
|
|
|
stroke="var(--color-sand)"
|
|
|
|
|
opacity={0.7}
|
|
|
|
|
fontSize={12}
|
|
|
|
|
width={48}
|
|
|
|
|
/>
|
|
|
|
|
<Tooltip
|
|
|
|
|
contentStyle={{
|
|
|
|
|
background: "var(--color-bg)",
|
|
|
|
|
border: "1px solid var(--color-muted)",
|
|
|
|
|
borderRadius: 8,
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
color: "var(--color-fg)",
|
|
|
|
|
}}
|
2026-06-16 11:11:19 +02:00
|
|
|
formatter={(value, key) => {
|
|
|
|
|
if (key === "volumeKg") return [`${Math.round(Number(value)).toLocaleString("pl-PL")} kg`, "Wolumen"];
|
|
|
|
|
if (key === "topWeightKg") return [`${value} kg`, "Maks. ciężar"];
|
|
|
|
|
if (key === "e1rmKg") return [`${value} kg`, "E1RM (Epley)"];
|
|
|
|
|
return [`${value}`, String(key)];
|
|
|
|
|
}}
|
2026-06-16 09:43:48 +02:00
|
|
|
/>
|
|
|
|
|
<Line
|
|
|
|
|
yAxisId="volume"
|
|
|
|
|
type="monotone"
|
|
|
|
|
dataKey="volumeKg"
|
|
|
|
|
stroke="var(--color-accent)"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
dot={{ r: 3 }}
|
|
|
|
|
/>
|
|
|
|
|
<Line
|
|
|
|
|
yAxisId="weight"
|
|
|
|
|
type="monotone"
|
|
|
|
|
dataKey="topWeightKg"
|
|
|
|
|
stroke="var(--color-sand)"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
dot={{ r: 3 }}
|
|
|
|
|
/>
|
2026-06-16 11:11:19 +02:00
|
|
|
<Line
|
|
|
|
|
yAxisId="weight"
|
|
|
|
|
type="monotone"
|
|
|
|
|
dataKey="e1rmKg"
|
|
|
|
|
stroke="#60a5fa"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
strokeDasharray="4 2"
|
|
|
|
|
dot={{ r: 3 }}
|
|
|
|
|
/>
|
2026-06-16 09:43:48 +02:00
|
|
|
</LineChart>
|
|
|
|
|
</ResponsiveContainer>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|