Files
krafttrainer/frontend/src/components/history/ExerciseChart.tsx
Christoph K. dfd66e43c6 Initial commit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:03:55 +01:00

114 lines
3.5 KiB
TypeScript
Executable File

import { useState, useEffect } from 'react';
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from 'recharts';
import { api } from '../../api/client';
import { useExerciseStore } from '../../stores/exerciseStore';
import type { SessionLog } from '../../types';
export function ExerciseChart() {
const { exercises, fetchExercises } = useExerciseStore();
const [selectedId, setSelectedId] = useState<number | null>(null);
const [chartData, setChartData] = useState<{ date: string; weight: number }[]>([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
fetchExercises();
}, [fetchExercises]);
useEffect(() => {
if (!selectedId) {
setChartData([]);
return;
}
setLoading(true);
api.exercises
.history(selectedId, 50)
.then((logs: SessionLog[]) => {
// Gruppiere nach Datum, nehme max Gewicht pro Tag
const byDate = new Map<string, number>();
for (const log of logs) {
const date = new Date(log.logged_at).toLocaleDateString('de-DE', {
day: '2-digit',
month: '2-digit',
});
const current = byDate.get(date) || 0;
if (log.weight_kg > current) {
byDate.set(date, log.weight_kg);
}
}
const data = Array.from(byDate.entries())
.map(([date, weight]) => ({ date, weight }))
.reverse();
setChartData(data);
})
.catch(() => setChartData([]))
.finally(() => setLoading(false));
}, [selectedId]);
return (
<div className="bg-gray-900 border border-gray-800 rounded-xl p-4">
<h3 className="text-lg font-semibold text-gray-100 mb-3">Gewichtsverlauf</h3>
<select
value={selectedId ?? ''}
onChange={(e) =>
setSelectedId(e.target.value ? parseInt(e.target.value) : null)
}
className="w-full bg-gray-800 border border-gray-700 rounded-lg px-3 py-2 text-gray-100 focus:outline-none focus:border-blue-500 min-h-[44px] mb-4"
>
<option value="">Übung auswählen...</option>
{exercises.map((ex) => (
<option key={ex.id} value={ex.id}>
{ex.name}
</option>
))}
</select>
{loading ? (
<div className="text-center text-gray-500 py-8">Laden...</div>
) : chartData.length === 0 ? (
<div className="text-center text-gray-500 py-8">
{selectedId
? 'Keine Daten vorhanden'
: 'Wähle eine Übung aus'}
</div>
) : (
<div className="h-64">
<ResponsiveContainer width="100%" height="100%">
<LineChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" stroke="#374151" />
<XAxis dataKey="date" stroke="#9CA3AF" fontSize={12} />
<YAxis stroke="#9CA3AF" fontSize={12} unit=" kg" />
<Tooltip
contentStyle={{
backgroundColor: '#1F2937',
border: '1px solid #374151',
borderRadius: '8px',
color: '#F3F4F6',
}}
formatter={(value) => [`${value} kg`, 'Max. Gewicht']}
/>
<Line
type="monotone"
dataKey="weight"
stroke="#3B82F6"
strokeWidth={2}
dot={{ fill: '#3B82F6', r: 4 }}
activeDot={{ r: 6 }}
/>
</LineChart>
</ResponsiveContainer>
</div>
)}
</div>
);
}