From 87f639e3205c4b3dfc7ad2680b913ac1906ad57d Mon Sep 17 00:00:00 2001 From: AG Date: Fri, 12 Dec 2025 00:12:18 +0200 Subject: [PATCH] Edit modals for Sets are complete --- server/src/services/session.service.ts | 15 +- server/test.db | Bin 925696 -> 946176 bytes src/components/EditSetModal.tsx | 217 +++++++++++++++++++ src/components/History.tsx | 182 ++++++---------- src/components/Tracker/ActiveSessionView.tsx | 174 ++++----------- src/components/Tracker/SporadicView.tsx | 163 +++----------- src/components/Tracker/useTracker.ts | 1 + src/services/sessions.ts | 12 +- 8 files changed, 379 insertions(+), 385 deletions(-) create mode 100644 src/components/EditSetModal.tsx diff --git a/server/src/services/session.service.ts b/server/src/services/session.service.ts index 3a974cd..ca12400 100644 --- a/server/src/services/session.service.ts +++ b/server/src/services/session.service.ts @@ -55,6 +55,9 @@ export class SessionService { reps: s.reps, distanceMeters: s.distanceMeters, durationSeconds: s.durationSeconds, + height: s.height, + bodyWeightPercentage: s.bodyWeightPercentage, + side: s.side, completed: s.completed !== undefined ? s.completed : true })) } @@ -95,6 +98,9 @@ export class SessionService { reps: s.reps, distanceMeters: s.distanceMeters, durationSeconds: s.durationSeconds, + height: s.height, + bodyWeightPercentage: s.bodyWeightPercentage, + side: s.side, completed: s.completed !== undefined ? s.completed : true })) } @@ -160,6 +166,9 @@ export class SessionService { reps: s.reps, distanceMeters: s.distanceMeters, durationSeconds: s.durationSeconds, + height: s.height, + bodyWeightPercentage: s.bodyWeightPercentage, + side: s.side, completed: s.completed !== undefined ? s.completed : true })) } @@ -248,6 +257,8 @@ export class SessionService { reps: reps ? parseInt(reps) : null, distanceMeters: distanceMeters ? parseFloat(distanceMeters) : null, durationSeconds: durationSeconds ? parseInt(durationSeconds) : null, + height: height ? parseFloat(height) : null, + bodyWeightPercentage: bodyWeightPercentage ? parseFloat(bodyWeightPercentage) : null, side: side || null }, include: { exercise: true } @@ -261,7 +272,7 @@ export class SessionService { } static async logSetToActiveSession(userId: string, data: any) { - const { exerciseId, reps, weight, distanceMeters, durationSeconds, side } = data; + const { exerciseId, reps, weight, distanceMeters, durationSeconds, side, height, bodyWeightPercentage } = data; const activeSession = await prisma.workoutSession.findFirst({ where: { userId, endTime: null, type: 'STANDARD' }, @@ -283,6 +294,8 @@ export class SessionService { weight: weight ? parseFloat(weight) : null, distanceMeters: distanceMeters ? parseFloat(distanceMeters) : null, durationSeconds: durationSeconds ? parseInt(durationSeconds) : null, + height: height ? parseFloat(height) : null, + bodyWeightPercentage: bodyWeightPercentage ? parseFloat(bodyWeightPercentage) : null, side: side || null, completed: true }, diff --git a/server/test.db b/server/test.db index 110acd151552f55256216b8dbfb7b70f01478533..3b02c90ad176de2737817b88ebb4ce033a4bdd0c 100644 GIT binary patch delta 21316 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 ka*mny+HZ|#TbYvaSg void; + set: WorkoutSet; + exerciseDef?: ExerciseDef; + onSave: (updatedSet: WorkoutSet) => Promise | void; + lang: Language; +} + +const EditSetModal: React.FC = ({ + isOpen, + onClose, + set: initialSet, + exerciseDef, + onSave, + lang +}) => { + const [set, setSet] = useState(initialSet); + + // Reset state when modal opens with a new set + useEffect(() => { + setSet(initialSet); + }, [initialSet, isOpen]); + + const handleUpdate = (field: keyof WorkoutSet, value: any) => { + setSet(prev => ({ ...prev, [field]: value })); + }; + + const handleSave = async () => { + await onSave(set); + onClose(); + }; + + // Date/Time handling + const setDate = new Date(set.timestamp); + const dateStr = dateStrFromDate(setDate); + const timeStr = timeStrFromDate(setDate); + + function dateStrFromDate(d: Date) { + // YYYY-MM-DD + const pad = (n: number) => n < 10 ? '0' + n : n; + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; + } + + function timeStrFromDate(d: Date) { + // HH:mm + const pad = (n: number) => n < 10 ? '0' + n : n; + return `${pad(d.getHours())}:${pad(d.getMinutes())}`; + } + + const handleDateChange = (newDateStr: string) => { + // Keep time, change date + const current = new Date(set.timestamp); + const [y, m, d] = newDateStr.split('-').map(Number); + const newDate = new Date(y, m - 1, d, current.getHours(), current.getMinutes(), current.getSeconds()); + handleUpdate('timestamp', newDate.getTime()); + }; + + const handleTimeChange = (newTimeStr: string) => { + // Keep date, change time + const current = new Date(set.timestamp); + const [h, min] = newTimeStr.split(':').map(Number); + const newDate = new Date(current.getFullYear(), current.getMonth(), current.getDate(), h, min, current.getSeconds()); + handleUpdate('timestamp', newDate.getTime()); + }; + + // Determine functionality based on resolved type + // Fallback: Check feature availability based on Type String or Prop existence + const type = (exerciseDef?.type || set.type) as string; + + const hasWeight = ['STRENGTH', 'BODYWEIGHT', 'STATIC', 'Strength', 'Bodyweight', 'Static'].includes(type) || set.weight !== null; + const hasReps = ['STRENGTH', 'BODYWEIGHT', 'PLYOMETRIC', 'Strength', 'Bodyweight', 'Plyometric'].includes(type) || set.reps !== null; + const hasBwPercent = ['BODYWEIGHT', 'STATIC', 'Bodyweight', 'Static'].includes(type) || set.bodyWeightPercentage !== null; + const hasTime = ['CARDIO', 'STATIC', 'Cardio', 'Static'].includes(type) || set.durationSeconds !== null; + const hasDist = ['CARDIO', 'LONG_JUMP', 'Cardio', 'Long Jump'].includes(type) || set.distanceMeters !== null; + const hasHeight = ['HIGH_JUMP', 'High Jump'].includes(type) || set.height !== null; + + return ( + +
+
+

+ {set.exerciseName} +

+

+ {exerciseDef?.type || set.type} + {set.side && ` • ${t(set.side.toLowerCase() as any, lang)}`} +

+
+ + {/* Date & Time */} +
+
+ + handleDateChange(e.target.value)} + className="w-full bg-transparent text-sm text-on-surface focus:outline-none" + /> +
+
+ + handleTimeChange(e.target.value)} + className="w-full bg-transparent text-sm text-on-surface focus:outline-none" + /> +
+
+ + {/* Metrics */} +
+ {hasWeight && ( +
+ + handleUpdate('weight', parseFloat(e.target.value))} + placeholder="0" + /> +
+ )} + {hasReps && ( +
+ + handleUpdate('reps', parseInt(e.target.value))} + placeholder="0" + /> +
+ )} + {hasBwPercent && ( +
+ + handleUpdate('bodyWeightPercentage', parseFloat(e.target.value))} + placeholder="100" + /> +
+ )} + {hasTime && ( +
+ + handleUpdate('durationSeconds', parseFloat(e.target.value))} + placeholder="0" + /> +
+ )} + {hasDist && ( +
+ + handleUpdate('distanceMeters', parseFloat(e.target.value))} + placeholder="0" + /> +
+ )} + {hasHeight && ( +
+ + handleUpdate('height', parseFloat(e.target.value))} + placeholder="0" + /> +
+ )} +
+ +
+ +
+
+
+ ); +}; + +export default EditSetModal; diff --git a/src/components/History.tsx b/src/components/History.tsx index b4a572e..c06f65c 100644 --- a/src/components/History.tsx +++ b/src/components/History.tsx @@ -9,6 +9,7 @@ import { getExercises } from '../services/storage'; import { Button } from './ui/Button'; import { Card } from './ui/Card'; import { Modal } from './ui/Modal'; +import EditSetModal from './EditSetModal'; import FilledInput from './FilledInput'; interface HistoryProps { @@ -25,6 +26,38 @@ const History: React.FC = ({ lang }) => { const [deletingId, setDeletingId] = useState(null); const [deletingSetInfo, setDeletingSetInfo] = useState<{ sessionId: string, setId: string } | null>(null); + const [editingSetInfo, setEditingSetInfo] = useState<{ sessionId: string, set: WorkoutSet } | null>(null); + + const handleSaveSingleSet = async (updatedSet: WorkoutSet) => { + if (!editingSetInfo) return; + const session = sessions.find(s => s.id === editingSetInfo.sessionId); + if (session) { + const updatedSets = session.sets.map(s => s.id === updatedSet.id ? updatedSet : s); + const updatedSession = { ...session, sets: updatedSets }; + try { + await updateSession(updatedSession); + setEditingSetInfo(null); + } catch (e) { + console.error("Failed to update set", e); + } + } + }; + + const handleSaveSetFromModal = async (updatedSet: WorkoutSet) => { + if (!editingSetInfo) return; + + // If editing within the session edit modal, update local state only + if (editingSession && editingSession.id === editingSetInfo.sessionId) { + const updatedSets = editingSession.sets.map(s => + s.id === updatedSet.id ? updatedSet : s + ); + setEditingSession({ ...editingSession, sets: updatedSets }); + setEditingSetInfo(null); + } else { + // Otherwise save to backend (Quick Log flow) + await handleSaveSingleSet(updatedSet); + } + }; React.useEffect(() => { if (!userId) return; @@ -87,13 +120,7 @@ const History: React.FC = ({ lang }) => { } }; - const handleUpdateSet = (setId: string, field: keyof WorkoutSet, value: number | string) => { - if (!editingSession) return; - const updatedSets = editingSession.sets.map(s => - s.id === setId ? { ...s, [field]: value } : s - ); - setEditingSession({ ...editingSession, sets: updatedSets }); - }; + const handleDeleteSet = (setId: string) => { if (!editingSession) return; @@ -267,7 +294,7 @@ const History: React.FC = ({ lang }) => { // Find the session this set belongs to and open edit mode const parentSession = daySessions.find(s => s.sets.some(st => st.id === set.id)); if (parentSession) { - setEditingSession(JSON.parse(JSON.stringify(parentSession))); + setEditingSetInfo({ sessionId: parentSession.id, set: set }); } }} variant="ghost" @@ -380,122 +407,37 @@ const History: React.FC = ({ lang }) => {

{t('sets_count', lang)} ({editingSession.sets.length})

{editingSession.sets.map((set, idx) => ( -
-
-
- {idx + 1} - {set.exerciseName}{set.side && {t(set.side.toLowerCase() as any, lang)}} +
+
+
+ {idx + 1} + {set.exerciseName} + {set.side && {t(set.side.toLowerCase() as any, lang)}}
+
+ {formatSetMetrics(set, lang)} +
+
+
+
- -
- {(set.type === ExerciseType.STRENGTH || set.type === ExerciseType.BODYWEIGHT || set.type === ExerciseType.STATIC) && ( -
- - handleUpdateSet(set.id, 'weight', parseFloat(e.target.value))} - /> -
- )} - {(set.type === ExerciseType.STRENGTH || set.type === ExerciseType.BODYWEIGHT || set.type === ExerciseType.PLYOMETRIC) && ( -
- - handleUpdateSet(set.id, 'reps', parseInt(e.target.value))} - /> -
- )} - {(set.type === ExerciseType.BODYWEIGHT || set.type === ExerciseType.STATIC) && ( -
- - handleUpdateSet(set.id, 'bodyWeightPercentage', parseFloat(e.target.value))} - /> -
- )} - {(set.type === ExerciseType.CARDIO || set.type === ExerciseType.STATIC) && ( -
- - handleUpdateSet(set.id, 'durationSeconds', parseFloat(e.target.value))} - /> -
- )} - {(set.type === ExerciseType.CARDIO || set.type === ExerciseType.LONG_JUMP) && ( -
- - handleUpdateSet(set.id, 'distanceMeters', parseFloat(e.target.value))} - /> -
- )} - {(set.type === ExerciseType.HIGH_JUMP) && ( -
- - handleUpdateSet(set.id, 'height', parseFloat(e.target.value))} - /> -
- )} -
- {/* Side Selector - Full width on mobile, 1 col on desktop if space */} - {(() => { - const exDef = exercises.find(e => e.id === set.exerciseId); - const showSide = set.side || exDef?.isUnilateral; - - if (!showSide) return null; - - return ( -
- -
- {(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((sideOption) => { - const labelMap: Record = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' }; - return ( - - ); - })} -
-
- ); - })()}
))}
@@ -510,6 +452,16 @@ const History: React.FC = ({ lang }) => { ) } + {editingSetInfo && ( + setEditingSetInfo(null)} + set={editingSetInfo.set} + exerciseDef={exercises.find(e => e.id === editingSetInfo.set.exerciseId)} + onSave={handleSaveSetFromModal} + lang={lang} + /> + )}
); }; diff --git a/src/components/Tracker/ActiveSessionView.tsx b/src/components/Tracker/ActiveSessionView.tsx index de206b7..c40cd28 100644 --- a/src/components/Tracker/ActiveSessionView.tsx +++ b/src/components/Tracker/ActiveSessionView.tsx @@ -10,6 +10,8 @@ import { formatSetMetrics } from '../../utils/setFormatting'; import { useAuth } from '../../context/AuthContext'; import { api } from '../../services/api'; import RestTimerFAB from '../ui/RestTimerFAB'; +import EditSetModal from '../EditSetModal'; + interface ActiveSessionViewProps { tracker: ReturnType; @@ -79,6 +81,15 @@ const ActiveSessionView: React.FC = ({ tracker, activeSe // Timer Logic is now managed in useTracker to persist across re-renders/step changes const { timer } = tracker; + const [editingSet, setEditingSet] = React.useState(null); + + const handleSaveSetFromModal = async (updatedSet: WorkoutSet) => { + if (tracker.updateSet) { + tracker.updateSet(updatedSet); + } + setEditingSet(null); + }; + const handleLogSet = async () => { await handleAddSet(); @@ -244,145 +255,34 @@ const ActiveSessionView: React.FC = ({ tracker, activeSe
{[...activeSession.sets].reverse().map((set: WorkoutSet, idx: number) => { const setNumber = activeSession.sets.length - idx; - const isEditing = editingSetId === set.id; return (
{setNumber}
- {isEditing ? ( -
-
{set.exerciseName}{set.side && {t(set.side.toLowerCase() as any, lang)}}
-
- {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)" - /> - )} - {(() => { - const exDef = exercises.find(e => e.name === set.exerciseName); // Best effort matching by name since set might not have exerciseId deeply populated in some contexts, but id is safer. - // Actually set has exerciseId usually. Let's try to match by ID if possible, else name. - // But wait, ActiveSession sets might not have exerciseId if created ad-hoc? No, they should. - // Let's assume we can look up by name if id missing, or just check set.side presence. - // Detailed look: The session object has sets. - // Ideally check exDef.isUnilateral. - const isUnilateral = set.side || (exercises.find(e => e.name === set.exerciseName)?.isUnilateral); - - if (isUnilateral) { - return ( -
- {(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((side) => { - const labelMap: Record = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' }; - return ( - - ); - })} -
- ) - } - return null; - })()} -
+
+
{set.exerciseName}{set.side && {t(set.side.toLowerCase() as any, lang)}}
+
+ {formatSetMetrics(set, lang)}
- ) : ( -
-
{set.exerciseName}{set.side && {t(set.side.toLowerCase() as any, lang)}}
-
- {formatSetMetrics(set, lang)} -
-
- )} +
-
- {isEditing ? ( - <> - - - - ) : ( - <> - - - - )} +
+ +
); @@ -456,6 +356,18 @@ const ActiveSessionView: React.FC = ({ tracker, activeSe
)} + {/* Edit Set Modal */} + {editingSet && ( + setEditingSet(null)} + set={editingSet} + exerciseDef={tracker.exercises.find(e => e.id === editingSet.exerciseId) || tracker.exercises.find(e => e.name === editingSet.exerciseName)} + onSave={handleSaveSetFromModal} + lang={lang} + /> + )} +
); diff --git a/src/components/Tracker/SporadicView.tsx b/src/components/Tracker/SporadicView.tsx index 1310af3..6cc544d 100644 --- a/src/components/Tracker/SporadicView.tsx +++ b/src/components/Tracker/SporadicView.tsx @@ -3,6 +3,7 @@ import { CheckCircle, Plus, Pencil, Trash2, X, Save } from 'lucide-react'; import { Language, WorkoutSet } from '../../types'; import { t } from '../../services/i18n'; import ExerciseModal from '../ExerciseModal'; +import EditSetModal from '../EditSetModal'; import { useTracker } from './useTracker'; import SetLogger from './SetLogger'; import { formatSetMetrics } from '../../utils/setFormatting'; @@ -157,139 +158,35 @@ const SporadicView: React.FC = ({ tracker, lang }) => { {/* Edit Set Modal */} {editingSetId && editingSet && ( -
-
-
-

{t('edit', lang)}

- -
-
- {/* Side Selector */} - {(() => { - const exDef = exercises.find(e => e.name === editingSet.exerciseName); - const isUnilateral = editingSet.side || exDef?.isUnilateral; - - if (isUnilateral) { - return ( -
- -
- {(['LEFT', 'ALTERNATELY', 'RIGHT'] as const).map((side) => { - const labelMap: Record = { LEFT: 'L', RIGHT: 'R', ALTERNATELY: 'A' }; - return ( - - ); - })} -
-
- ) - } - return null; - })()} - - {(editingSet.type === 'STRENGTH' || editingSet.type === 'BODYWEIGHT') && ( - <> -
- - setEditingSet({ ...editingSet, weight: parseFloat(e.target.value) || 0 })} - className="w-full mt-1 px-4 py-2 bg-surface-container-high text-on-surface rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" - /> -
-
- - setEditingSet({ ...editingSet, reps: parseInt(e.target.value) || 0 })} - className="w-full mt-1 px-4 py-2 bg-surface-container-high text-on-surface rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" - /> -
- - )} - {(editingSet.type === 'CARDIO' || editingSet.type === 'STATIC') && ( -
- - setEditingSet({ ...editingSet, durationSeconds: parseInt(e.target.value) || 0 })} - className="w-full mt-1 px-4 py-2 bg-surface-container-high text-on-surface rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" - /> -
- )} - {editingSet.type === 'CARDIO' && ( -
- - setEditingSet({ ...editingSet, distanceMeters: parseFloat(e.target.value) || 0 })} - className="w-full mt-1 px-4 py-2 bg-surface-container-high text-on-surface rounded-lg focus:outline-none focus:ring-2 focus:ring-primary" - /> -
- )} -
-
- - -
-
-
+ { + setEditingSetId(null); + setEditingSet(null); + }} + set={editingSet} + exerciseDef={exercises.find(e => e.id === editingSet.exerciseId || e.name === editingSet.exerciseName)} + onSave={async (updatedSet) => { + try { + const response = await fetch(`/api/sessions/active/set/${editingSetId}`, { + method: 'PATCH', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('token')}` + }, + body: JSON.stringify(updatedSet) + }); + if (response.ok) { + await loadQuickLogSession(); + setEditingSetId(null); + setEditingSet(null); + } + } catch (error) { + console.error('Failed to update set:', error); + } + }} + lang={lang} + /> )} {/* Delete Confirmation Modal */} diff --git a/src/components/Tracker/useTracker.ts b/src/components/Tracker/useTracker.ts index 95ab525..9decf39 100644 --- a/src/components/Tracker/useTracker.ts +++ b/src/components/Tracker/useTracker.ts @@ -263,6 +263,7 @@ export const useTracker = (props: any) => { // Props ignored/removed onSessionEnd: endSession, onSessionQuit: quitSession, onRemoveSet: removeSet, + updateSet: handleUpdateSetWrapper, activeSession, // Need this in view timer // Expose timer to views }; diff --git a/src/services/sessions.ts b/src/services/sessions.ts index 9b556b6..876acb2 100644 --- a/src/services/sessions.ts +++ b/src/services/sessions.ts @@ -9,7 +9,9 @@ interface ApiSession extends Omit => endTime: session.endTime ? new Date(session.endTime).getTime() : undefined, sets: session.sets.map((set) => ({ ...set, - exerciseName: set.exercise?.name || 'Unknown', - type: set.exercise?.type || ExerciseType.STRENGTH + exerciseName: set.exerciseName || set.exercise?.name || 'Unknown', + type: set.type || set.exercise?.type || ExerciseType.STRENGTH })) as WorkoutSet[] })); } catch { @@ -58,8 +60,8 @@ export const getActiveSession = async (userId: string): Promise ({ ...set, - exerciseName: set.exercise?.name || 'Unknown', - type: set.exercise?.type || ExerciseType.STRENGTH + exerciseName: set.exerciseName || set.exercise?.name || 'Unknown', + type: set.type || set.exercise?.type || ExerciseType.STRENGTH })) as WorkoutSet[] }; } catch {