From ada10d52e88f688fabfd220fd120a8ebece1647f Mon Sep 17 00:00:00 2001 From: AG Date: Thu, 20 Nov 2025 23:57:00 +0200 Subject: [PATCH] Minor History fix --- App.tsx | 10 +- components/History.tsx | 561 +++++++++++++++++++++-------------------- components/Tracker.tsx | 46 ++-- 3 files changed, 312 insertions(+), 305 deletions(-) diff --git a/App.tsx b/App.tsx index b022053..05fbca2 100644 --- a/App.tsx +++ b/App.tsx @@ -94,13 +94,19 @@ function App() { setCurrentTab('TRACK'); }; - const handleEndSession = () => { + const handleEndSession = async () => { if (activeSession && currentUser) { const finishedSession = { ...activeSession, endTime: Date.now() }; - saveSession(currentUser.id, finishedSession); + await saveSession(currentUser.id, finishedSession); setSessions(prev => [finishedSession, ...prev]); setActiveSession(null); setActivePlan(null); + + // Refetch user to get updated weight + const res = await getMe(); + if (res.success && res.user) { + setCurrentUser(res.user); + } } }; diff --git a/components/History.tsx b/components/History.tsx index ae97f85..112ffca 100644 --- a/components/History.tsx +++ b/components/History.tsx @@ -5,300 +5,301 @@ import { WorkoutSession, ExerciseType, WorkoutSet, Language } from '../types'; import { t } from '../services/i18n'; interface HistoryProps { - sessions: WorkoutSession[]; - onUpdateSession?: (session: WorkoutSession) => void; - onDeleteSession?: (sessionId: string) => void; - lang: Language; + sessions: WorkoutSession[]; + onUpdateSession?: (session: WorkoutSession) => void; + onDeleteSession?: (sessionId: string) => void; + lang: Language; } const History: React.FC = ({ sessions, onUpdateSession, onDeleteSession, lang }) => { - const [editingSession, setEditingSession] = useState(null); - const [deletingId, setDeletingId] = useState(null); + const [editingSession, setEditingSession] = useState(null); + const [deletingId, setDeletingId] = useState(null); - const calculateSessionWork = (session: WorkoutSession) => { - const bw = session.userBodyWeight || 70; - return session.sets.reduce((acc, set) => { - let w = 0; - if (set.type === ExerciseType.STRENGTH) { - w = (set.weight || 0) * (set.reps || 0); - } - if (set.type === ExerciseType.BODYWEIGHT) { - const percent = set.bodyWeightPercentage || 100; - const effectiveBw = bw * (percent / 100); - w = (effectiveBw + (set.weight || 0)) * (set.reps || 0); - } - return acc + Math.max(0, w); - }, 0); - }; + const calculateSessionWork = (session: WorkoutSession) => { + const bw = session.userBodyWeight || 70; + return session.sets.reduce((acc, set) => { + let w = 0; + if (set.type === ExerciseType.STRENGTH) { + w = (set.weight || 0) * (set.reps || 0); + } + if (set.type === ExerciseType.BODYWEIGHT) { + const percent = set.bodyWeightPercentage || 100; + const effectiveBw = bw * (percent / 100); + w = (effectiveBw + (set.weight || 0)) * (set.reps || 0); + } + return acc + Math.max(0, w); + }, 0); + }; - const formatDateForInput = (timestamp: number) => { - const d = new Date(timestamp); - const pad = (n: number) => n < 10 ? '0' + n : n; - return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; - }; + const formatDateForInput = (timestamp: number) => { + const d = new Date(timestamp); + const pad = (n: number) => n < 10 ? '0' + n : n; + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; + }; - const parseDateFromInput = (value: string) => { - return new Date(value).getTime(); - }; + const parseDateFromInput = (value: string) => { + return new Date(value).getTime(); + }; - const handleSaveEdit = () => { - if (editingSession && onUpdateSession) { - onUpdateSession(editingSession); - setEditingSession(null); - } - }; + const handleSaveEdit = () => { + if (editingSession && onUpdateSession) { + onUpdateSession(editingSession); + setEditingSession(null); + } + }; - const handleUpdateSet = (setId: string, field: keyof WorkoutSet, value: number) => { - if (!editingSession) return; - const updatedSets = editingSession.sets.map(s => - s.id === setId ? { ...s, [field]: value } : s - ); - setEditingSession({ ...editingSession, sets: updatedSets }); - }; + const handleUpdateSet = (setId: string, field: keyof WorkoutSet, value: number) => { + 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; - setEditingSession({ - ...editingSession, - sets: editingSession.sets.filter(s => s.id !== setId) - }); - }; + const handleDeleteSet = (setId: string) => { + if (!editingSession) return; + setEditingSession({ + ...editingSession, + sets: editingSession.sets.filter(s => s.id !== setId) + }); + }; - const handleConfirmDelete = () => { - if (deletingId && onDeleteSession) { - onDeleteSession(deletingId); - setDeletingId(null); - } - } + const handleConfirmDelete = () => { + if (deletingId && onDeleteSession) { + onDeleteSession(deletingId); + setDeletingId(null); + } + } + + if (sessions.length === 0) { + return ( +
+ +

{t('history_empty', lang)}

+
+ ); + } - if (sessions.length === 0) { return ( -
- -

{t('history_empty', lang)}

+
+
+

{t('tab_history', lang)}

+
+ +
+ {sessions.map((session) => { + const totalWork = calculateSessionWork(session); + + return ( +
+
+
+
+ +
+
+
+ {new Date(session.startTime).toLocaleDateString(lang === 'ru' ? 'ru-RU' : 'en-US', { weekday: 'long', day: 'numeric', month: 'long' })} +
+
+ {new Date(session.startTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} + {session.userBodyWeight && {session.userBodyWeight}kg} +
+
+
+ +
+ + +
+
+ +
+ {Array.from(new Set(session.sets.map(s => s.exerciseName))).slice(0, 4).map(exName => { + const sets = session.sets.filter(s => s.exerciseName === exName); + const count = sets.length; + const bestSet = sets[0]; + let detail = ""; + if (bestSet.type === ExerciseType.HIGH_JUMP) detail = `${t('max', lang)}: ${Math.max(...sets.map(s => s.height || 0))}cm`; + else if (bestSet.type === ExerciseType.LONG_JUMP) detail = `${t('max', lang)}: ${Math.max(...sets.map(s => s.distanceMeters || 0))}m`; + else if (bestSet.type === ExerciseType.STRENGTH) detail = `${t('upto', lang)} ${Math.max(...sets.map(s => s.weight || 0))}kg`; + + return ( +
+ {exName} + + {detail && {detail}} + {count} + +
+ ); + })} + {new Set(session.sets.map(s => s.exerciseName)).size > 4 && ( +
+ + ... +
+ )} +
+ +
+
+ {t('sets_count', lang)}: {session.sets.length} + {totalWork > 0 && ( + + + {(totalWork / 1000).toFixed(1)}t + + )} +
+
+ + {t('finished', lang)} +
+
+
+ ) + })} +
+ + {/* DELETE CONFIRMATION DIALOG (MD3) */} + {deletingId && ( +
+
+

{t('delete_workout', lang)}

+

{t('delete_confirm', lang)}

+
+ + +
+
+
+ )} + + {/* EDIT SESSION FULLSCREEN DIALOG */} + {editingSession && ( +
+
+ +

{t('edit', lang)}

+ +
+ +
+ {/* Meta Info */} +
+
+
+ + setEditingSession({ ...editingSession, startTime: parseDateFromInput(e.target.value) })} + className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1" + /> +
+
+ + setEditingSession({ ...editingSession, endTime: parseDateFromInput(e.target.value) })} + className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1" + /> +
+
+
+ + setEditingSession({ ...editingSession, userBodyWeight: parseFloat(e.target.value) })} + className="w-full bg-transparent text-on-surface focus:outline-none text-lg mt-1" + /> +
+
+ +
+

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

+ {editingSession.sets.map((set, idx) => ( +
+
+
+ {idx + 1} + {set.exerciseName} +
+ +
+ +
+ {(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', parseFloat(e.target.value))} + /> +
+ )} + {(set.type === ExerciseType.BODYWEIGHT || set.type === ExerciseType.STATIC) && ( +
+ + handleUpdateSet(set.id, 'bodyWeightPercentage', parseFloat(e.target.value))} + /> +
+ )} + {/* Add other fields similarly styled if needed */} +
+
+ ))} +
+
+
+ )}
); - } - - return ( -
-
-

{t('tab_history', lang)}

-
- -
- {sessions.map((session) => { - const totalWork = calculateSessionWork(session); - - return ( -
-
-
-
- -
-
-
- {new Date(session.startTime).toLocaleDateString(lang === 'ru' ? 'ru-RU' : 'en-US', { weekday: 'long', day: 'numeric', month: 'long' })} -
-
- {new Date(session.startTime).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} - {session.userBodyWeight && {session.userBodyWeight}kg} -
-
-
- -
- - -
-
- -
- {Array.from(new Set(session.sets.map(s => s.exerciseName))).slice(0, 4).map(exName => { - const sets = session.sets.filter(s => s.exerciseName === exName); - const count = sets.length; - const bestSet = sets[0]; - let detail = ""; - if (bestSet.type === ExerciseType.HIGH_JUMP) detail = `${t('max', lang)}: ${Math.max(...sets.map(s => s.height || 0))}cm`; - else if (bestSet.type === ExerciseType.LONG_JUMP) detail = `${t('max', lang)}: ${Math.max(...sets.map(s => s.distanceMeters || 0))}m`; - else if (bestSet.type === ExerciseType.STRENGTH) detail = `${t('upto', lang)} ${Math.max(...sets.map(s => s.weight || 0))}kg`; - - return ( -
- {exName} - - {detail && {detail}} - {count} - -
- ); - })} - {new Set(session.sets.map(s => s.exerciseName)).size > 4 && ( -
- + ... -
- )} -
- -
-
- {t('sets_count', lang)}: {session.sets.length} - {totalWork > 0 && ( - - - {(totalWork / 1000).toFixed(1)}t - - )} -
-
- - {t('finished', lang)} -
-
-
- )})} -
- - {/* DELETE CONFIRMATION DIALOG (MD3) */} - {deletingId && ( -
-
-

{t('delete_workout', lang)}

-

{t('delete_confirm', lang)}

-
- - -
-
-
- )} - - {/* EDIT SESSION FULLSCREEN DIALOG */} - {editingSession && ( -
-
- -

{t('edit', lang)}

- -
- -
- {/* Meta Info */} -
-
-
- - setEditingSession({...editingSession, startTime: parseDateFromInput(e.target.value)})} - className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1" - /> -
-
- - setEditingSession({...editingSession, endTime: parseDateFromInput(e.target.value)})} - className="w-full bg-transparent text-on-surface focus:outline-none text-sm mt-1" - /> -
-
-
- - setEditingSession({...editingSession, userBodyWeight: parseFloat(e.target.value)})} - className="w-full bg-transparent text-on-surface focus:outline-none text-lg mt-1" - /> -
-
- -
-

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

- {editingSession.sets.map((set, idx) => ( -
-
-
- {idx + 1} - {set.exerciseName} -
- -
- -
- {(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', parseFloat(e.target.value))} - /> -
- )} - {(set.type === ExerciseType.BODYWEIGHT || set.type === ExerciseType.STATIC) && ( -
- - handleUpdateSet(set.id, 'bodyWeightPercentage', parseFloat(e.target.value))} - /> -
- )} - {/* Add other fields similarly styled if needed */} -
-
- ))} -
-
-
- )} -
- ); }; export default History; diff --git a/components/Tracker.tsx b/components/Tracker.tsx index 37b6194..37296f6 100644 --- a/components/Tracker.tsx +++ b/components/Tracker.tsx @@ -18,6 +18,24 @@ interface TrackerProps { lang: Language; } +const FilledInput = ({ label, value, onChange, type = "number", icon, autoFocus, step }: any) => ( +
+ + +
+); + const Tracker: React.FC = ({ userId, userWeight, activeSession, activePlan, onSessionStart, onSessionEnd, onSetAdded, onRemoveSet, lang }) => { const [exercises, setExercises] = useState([]); const [plans, setPlans] = useState([]); @@ -107,11 +125,11 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac setLastSet(set); if (set) { - if (set.weight !== undefined) setWeight(set.weight.toString()); - if (set.reps !== undefined) setReps(set.reps.toString()); - if (set.durationSeconds !== undefined) setDuration(set.durationSeconds.toString()); - if (set.distanceMeters !== undefined) setDistance(set.distanceMeters.toString()); - if (set.height !== undefined) setHeight(set.height.toString()); + if (set.weight != null) setWeight(set.weight.toString()); + if (set.reps != null) setReps(set.reps.toString()); + if (set.durationSeconds != null) setDuration(set.durationSeconds.toString()); + if (set.distanceMeters != null) setDistance(set.distanceMeters.toString()); + if (set.height != null) setHeight(set.height.toString()); } else { setWeight(''); setReps(''); setDuration(''); setDistance(''); setHeight(''); } @@ -189,24 +207,6 @@ const Tracker: React.FC = ({ userId, userWeight, activeSession, ac const isPlanFinished = activePlan && currentStepIndex >= activePlan.steps.length; - const FilledInput = ({ label, value, onChange, type = "number", icon, autoFocus, step }: any) => ( -
- - -
- ); - const exerciseTypeLabels: Record = { [ExerciseType.STRENGTH]: t('type_strength', lang), [ExerciseType.BODYWEIGHT]: t('type_bodyweight', lang),