This commit is contained in:
Dominik Klarkowski
2026-06-16 11:11:19 +02:00
parent 36407f534b
commit 21e5db3409
4 changed files with 158 additions and 19 deletions

View File

@@ -4,9 +4,27 @@ import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YA
type ExerciseProgressChartProps = {
name: string;
data: { label: string; volumeKg: number; topWeightKg?: number }[];
data: { label: string; volumeKg: number; topWeightKg?: number; e1rmKg?: number }[];
};
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>
);
}
export function ExerciseProgressChart({ name, data }: ExerciseProgressChartProps) {
if (data.length < 2) {
return null;
@@ -14,7 +32,10 @@ export function ExerciseProgressChart({ name, data }: ExerciseProgressChartProps
return (
<div className="w-full rounded-lg border border-muted/40 bg-surface p-4">
<div className="mb-2 text-sm text-fg/60">{name}</div>
<div className="mb-2 flex items-baseline justify-between gap-2">
<span className="text-sm text-fg/60">{name}</span>
<E1rmDelta data={data} />
</div>
<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} />
@@ -36,10 +57,12 @@ export function ExerciseProgressChart({ name, data }: ExerciseProgressChartProps
fontSize: 12,
color: "var(--color-fg)",
}}
formatter={(value, key) => [
key === "topWeightKg" ? `${value} kg` : `${Math.round(Number(value)).toLocaleString("pl-PL")} kg`,
key === "topWeightKg" ? "Maks. ciężar" : "Wolumen",
]}
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)];
}}
/>
<Line
yAxisId="volume"
@@ -57,6 +80,15 @@ export function ExerciseProgressChart({ name, data }: ExerciseProgressChartProps
strokeWidth={2}
dot={{ r: 3 }}
/>
<Line
yAxisId="weight"
type="monotone"
dataKey="e1rmKg"
stroke="#60a5fa"
strokeWidth={2}
strokeDasharray="4 2"
dot={{ r: 3 }}
/>
</LineChart>
</ResponsiveContainer>
</div>