diff --git a/App.tsx b/App.tsx index 05fbca2..8b0e1ce 100644 --- a/App.tsx +++ b/App.tsx @@ -134,6 +134,18 @@ function App() { } }; + const handleUpdateSetInActive = (updatedSet: WorkoutSet) => { + if (activeSession) { + setActiveSession(prev => { + if (!prev) return null; + return { + ...prev, + sets: prev.sets.map(s => s.id === updatedSet.id ? updatedSet : s) + }; + }); + } + }; + const handleUpdateSession = (updatedSession: WorkoutSession) => { if (!currentUser) return; saveSession(currentUser.id, updatedSession); @@ -169,6 +181,7 @@ function App() { onSessionEnd={handleEndSession} onSetAdded={handleAddSet} onRemoveSet={handleRemoveSetFromActive} + onUpdateSet={handleUpdateSetInActive} lang={language} /> )} diff --git a/components/Tracker.tsx b/components/Tracker.tsx index 81de3a2..b142e18 100644 --- a/components/Tracker.tsx +++ b/components/Tracker.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { Plus, Activity, ChevronDown, ChevronUp, Dumbbell, PlayCircle, CheckCircle, User, Scale, X, Flame, Timer as TimerIcon, ArrowUp, ArrowRight, Footprints, Ruler, CheckSquare, Trash2, Percent, MoreVertical } from 'lucide-react'; +import { Plus, Activity, ChevronDown, ChevronUp, Dumbbell, PlayCircle, CheckCircle, User, Scale, X, Flame, Timer as TimerIcon, ArrowUp, ArrowRight, Footprints, Ruler, CheckSquare, Trash2, Percent, MoreVertical, Edit } from 'lucide-react'; import { WorkoutSession, WorkoutSet, ExerciseDef, ExerciseType, WorkoutPlan, Language } from '../types'; import { getExercises, getLastSetForExercise, saveExercise, getPlans } from '../services/storage'; import { getCurrentUserProfile } from '../services/auth'; @@ -15,13 +15,14 @@ interface TrackerProps { onSessionEnd: () => void; onSetAdded: (set: WorkoutSet) => void; onRemoveSet: (setId: string) => void; + onUpdateSet: (set: WorkoutSet) => void; lang: Language; } import FilledInput from './FilledInput'; import ExerciseModal from './ExerciseModal'; -const Tracker: React.FC = ({ userId, userWeight, activeSession, activePlan, onSessionStart, onSessionEnd, onSetAdded, onRemoveSet, lang }) => { +const Tracker: React.FC = ({ userId, userWeight, activeSession, activePlan, onSessionStart, onSessionEnd, onSetAdded, onRemoveSet, onUpdateSet, lang }) => { const [exercises, setExercises] = useState([]); const [plans, setPlans] = useState([]); const [selectedExercise, setSelectedExercise] = useState(null); @@ -54,6 +55,14 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac const [showQuitConfirm, setShowQuitConfirm] = useState(false); const [showMenu, setShowMenu] = useState(false); + // Edit Set State + const [editingSetId, setEditingSetId] = useState(null); + const [editWeight, setEditWeight] = useState(''); + const [editReps, setEditReps] = useState(''); + const [editDuration, setEditDuration] = useState(''); + const [editDistance, setEditDistance] = useState(''); + const [editHeight, setEditHeight] = useState(''); + useEffect(() => { const loadData = async () => { const exList = await getExercises(userId); @@ -174,6 +183,32 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac setIsCreating(false); }; + const handleEditSet = (set: WorkoutSet) => { + setEditingSetId(set.id); + setEditWeight(set.weight?.toString() || ''); + setEditReps(set.reps?.toString() || ''); + setEditDuration(set.durationSeconds?.toString() || ''); + setEditDistance(set.distanceMeters?.toString() || ''); + setEditHeight(set.height?.toString() || ''); + }; + + const handleSaveEdit = (set: WorkoutSet) => { + const updatedSet: WorkoutSet = { + ...set, + ...(editWeight && { weight: parseFloat(editWeight) }), + ...(editReps && { reps: parseInt(editReps) }), + ...(editDuration && { durationSeconds: parseInt(editDuration) }), + ...(editDistance && { distanceMeters: parseFloat(editDistance) }), + ...(editHeight && { height: parseFloat(editHeight) }) + }; + onUpdateSet(updatedSet); + setEditingSetId(null); + }; + + const handleCancelEdit = () => { + setEditingSetId(null); + }; + const jumpToStep = (index: number) => { if (!activePlan) return; setCurrentStepIndex(index); @@ -446,22 +481,6 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac )} - {(selectedExercise.type === ExerciseType.BODYWEIGHT || selectedExercise.type === ExerciseType.STATIC) && ( -
-
- - {t('body_weight_percent', lang)} -
- setBwPercentage(e.target.value)} - /> - % -
- )} - - -
-
- {t('prev', lang)}: - {lastSet ? ( - <> - {lastSet?.weight ? `${lastSet?.weight}kg × ` : ''} - {lastSet?.reps ? `${lastSet?.reps}` : ''} - {lastSet?.distanceMeters ? `${lastSet?.distanceMeters}m` : ''} - {lastSet?.height ? `${lastSet?.height}cm` : ''} - {lastSet?.durationSeconds ? `${lastSet?.durationSeconds}s` : ''} - - ) : '—'} - -
-
)} @@ -494,29 +497,113 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac
{[...activeSession.sets].reverse().map((set, idx) => { const setNumber = activeSession.sets.length - idx; + const isEditing = editingSetId === set.id; return (
-
+
{setNumber}
-
-
{set.exerciseName}
-
- {set.weight !== undefined && `${set.weight}kg `} - {set.reps !== undefined && `x ${set.reps}`} - {set.distanceMeters !== undefined && `${set.distanceMeters}m`} - {set.durationSeconds !== undefined && `${set.durationSeconds}s`} - {set.height !== undefined && `${set.height}cm`} + {isEditing ? ( +
+
{set.exerciseName}
+
+ {set.weight !== undefined && ( + setEditWeight(e.target.value)} + className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none" + placeholder="Weight (kg)" + /> + )} + {set.reps !== undefined && ( + setEditReps(e.target.value)} + className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none" + placeholder="Reps" + /> + )} + {set.durationSeconds !== undefined && ( + setEditDuration(e.target.value)} + className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none" + placeholder="Duration (s)" + /> + )} + {set.distanceMeters !== undefined && ( + setEditDistance(e.target.value)} + className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none" + placeholder="Distance (m)" + /> + )} + {set.height !== undefined && ( + setEditHeight(e.target.value)} + className="px-2 py-1 bg-surface-container-high rounded text-sm text-on-surface border border-outline-variant focus:border-primary focus:outline-none" + placeholder="Height (cm)" + /> + )} +
-
+ ) : ( +
+
{set.exerciseName}
+
+ {set.weight !== undefined && `${set.weight}kg `} + {set.reps !== undefined && `x ${set.reps}`} + {set.distanceMeters !== undefined && `${set.distanceMeters}m`} + {set.durationSeconds !== undefined && `${set.durationSeconds}s`} + {set.height !== undefined && `${set.height}cm`} +
+
+ )} +
+
+ {isEditing ? ( + <> + + + + ) : ( + <> + + + + )}
-
); })} @@ -552,7 +639,7 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac setShowFinishConfirm(false); onSessionEnd(); }} - className="px-6 py-2.5 rounded-full bg-primary text-on-primary font-medium" + className="px-6 py-2.5 rounded-full bg-green-600 text-white font-medium hover:bg-green-700" > {t('confirm', lang)} @@ -580,7 +667,7 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac // Quit without saving - just navigate away or reset window.location.reload(); }} - className="px-6 py-2.5 rounded-full bg-error text-on-error font-medium" + className="px-6 py-2.5 rounded-full bg-green-600 text-white font-medium hover:bg-green-700" > {t('confirm', lang)} diff --git a/server/prisma/dev.db b/server/prisma/dev.db index 59d7a26..983d7ac 100644 Binary files a/server/prisma/dev.db and b/server/prisma/dev.db differ