Sporadic set logging added

This commit is contained in:
AG
2025-11-29 19:03:42 +02:00
parent d86abd6b1b
commit b5c8e8ac43
15 changed files with 1491 additions and 396 deletions

View File

@@ -1,19 +1,24 @@
import React, { useState } from 'react';
import { Calendar, Clock, TrendingUp, Gauge, Pencil, Trash2, X, Save, ArrowRight, ArrowUp, Timer, Activity, Dumbbell, Percent } from 'lucide-react';
import { WorkoutSession, ExerciseType, WorkoutSet, Language } from '../types';
import { WorkoutSession, ExerciseType, WorkoutSet, Language, SporadicSet } from '../types';
import { t } from '../services/i18n';
interface HistoryProps {
sessions: WorkoutSession[];
sporadicSets?: SporadicSet[];
onUpdateSession?: (session: WorkoutSession) => void;
onDeleteSession?: (sessionId: string) => void;
onUpdateSporadicSet?: (set: SporadicSet) => void;
onDeleteSporadicSet?: (setId: string) => void;
lang: Language;
}
const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSession, lang }) => {
const History: React.FC<HistoryProps> = ({ sessions, sporadicSets, onUpdateSession, onDeleteSession, onUpdateSporadicSet, onDeleteSporadicSet, lang }) => {
const [editingSession, setEditingSession] = useState<WorkoutSession | null>(null);
const [deletingId, setDeletingId] = useState<string | null>(null);
const [editingSporadicSet, setEditingSporadicSet] = useState<SporadicSet | null>(null);
const [deletingSporadicId, setDeletingSporadicId] = useState<string | null>(null);
const calculateSessionWork = (session: WorkoutSession) => {
const bw = session.userBodyWeight || 70;
@@ -88,6 +93,25 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
}
}
const handleSaveSporadicEdit = () => {
if (editingSporadicSet && onUpdateSporadicSet) {
onUpdateSporadicSet(editingSporadicSet);
setEditingSporadicSet(null);
}
};
const handleUpdateSporadicField = (field: keyof SporadicSet, value: number) => {
if (!editingSporadicSet) return;
setEditingSporadicSet({ ...editingSporadicSet, [field]: value });
};
const handleConfirmDeleteSporadic = () => {
if (deletingSporadicId && onDeleteSporadicSet) {
onDeleteSporadicSet(deletingSporadicId);
setDeletingSporadicId(null);
}
};
if (sessions.length === 0) {
return (
<div className="flex flex-col items-center justify-center h-full text-on-surface-variant p-8 text-center">
@@ -175,6 +199,65 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
})}
</div>
{/* Sporadic Sets Section */}
{sporadicSets && sporadicSets.length > 0 && (
<div className="mt-8">
<h3 className="text-xl font-medium text-on-surface mb-4 px-2">{t('sporadic_sets_title', lang)}</h3>
{Object.entries(
sporadicSets.reduce((groups: Record<string, SporadicSet[]>, set) => {
const date = new Date(set.timestamp).toISOString().split('T')[0];
if (!groups[date]) groups[date] = [];
groups[date].push(set);
return groups;
}, {})
)
.sort(([a], [b]) => b.localeCompare(a))
.map(([date, sets]) => (
<div key={date} className="mb-4">
<div className="text-sm text-on-surface-variant px-2 mb-2 font-medium">{date}</div>
<div className="space-y-2">
{sets.map(set => (
<div
key={set.id}
className="bg-surface-container-low rounded-xl p-4 border border-outline-variant/10 flex justify-between items-center"
>
<div className="flex-1">
<div className="font-medium text-on-surface">{set.exerciseName}</div>
<div className="text-sm text-on-surface-variant mt-1">
{set.type === ExerciseType.STRENGTH && `${set.weight || 0}kg x ${set.reps || 0}`}
{set.type === ExerciseType.BODYWEIGHT && `${set.weight ? `+${set.weight}kg` : 'BW'} x ${set.reps || 0}`}
{set.type === ExerciseType.CARDIO && `${set.durationSeconds || 0}s ${set.distanceMeters ? `/ ${set.distanceMeters}m` : ''}`}
{set.type === ExerciseType.STATIC && `${set.durationSeconds || 0}s`}
{set.type === ExerciseType.HIGH_JUMP && `${set.height || 0}cm`}
{set.type === ExerciseType.LONG_JUMP && `${set.distanceMeters || 0}m`}
{set.type === ExerciseType.PLYOMETRIC && `x ${set.reps || 0}`}
</div>
<div className="text-xs text-on-surface-variant mt-1">
{new Date(set.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</div>
</div>
<div className="flex gap-1">
<button
onClick={() => setEditingSporadicSet(JSON.parse(JSON.stringify(set)))}
className="p-2 text-on-surface-variant hover:text-primary hover:bg-surface-container-high rounded-full transition-colors"
>
<Pencil size={18} />
</button>
<button
onClick={() => setDeletingSporadicId(set.id)}
className="p-2 text-on-surface-variant hover:text-error hover:bg-surface-container-high rounded-full transition-colors"
>
<Trash2 size={18} />
</button>
</div>
</div>
))}
</div>
</div>
))}
</div>
)}
{/* DELETE CONFIRMATION DIALOG (MD3) */}
{deletingId && (
<div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
@@ -338,6 +421,30 @@ const History: React.FC<HistoryProps> = ({ sessions, onUpdateSession, onDeleteSe
</div>
</div>
)}
{/* Sporadic Set Delete Confirmation */}
{deletingSporadicId && (
<div className="fixed inset-0 z-[60] flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
<div className="bg-surface-container w-full max-w-xs rounded-[28px] p-6 shadow-elevation-3">
<h3 className="text-xl font-normal text-on-surface mb-2">{t('delete', lang)}</h3>
<p className="text-sm text-on-surface-variant mb-8">{t('delete_confirm', lang)}</p>
<div className="flex justify-end gap-2">
<button
onClick={() => setDeletingSporadicId(null)}
className="px-4 py-2 rounded-full text-primary font-medium hover:bg-white/5"
>
{t('cancel', lang)}
</button>
<button
onClick={handleConfirmDeleteSporadic}
className="px-4 py-2 rounded-full bg-error-container text-on-error-container font-medium"
>
{t('delete', lang)}
</button>
</div>
</div>
</div>
)}
</div>
);
};