Code maintainability fixes

This commit is contained in:
AG
2025-12-06 11:32:40 +02:00
parent a13ef9f479
commit 4106f3b783
23 changed files with 1775 additions and 796 deletions

View File

@@ -1,8 +1,10 @@
import { useState, useEffect } from 'react';
import { WorkoutSession, WorkoutSet, ExerciseDef, ExerciseType, WorkoutPlan, Language } from '../../types';
import { getExercises, getLastSetForExercise, saveExercise, getPlans } from '../../services/storage';
import { WorkoutSession, WorkoutSet, ExerciseDef, ExerciseType, WorkoutPlan } from '../../types';
import { getExercises, saveExercise, getPlans } from '../../services/storage';
import { api } from '../../services/api';
import { useSessionTimer } from '../../hooks/useSessionTimer';
import { useWorkoutForm } from '../../hooks/useWorkoutForm';
import { usePlanExecution } from '../../hooks/usePlanExecution';
interface UseTrackerProps {
userId: string;
@@ -25,9 +27,7 @@ export const useTracker = ({
activePlan,
onSessionStart,
onSessionEnd,
onSessionQuit,
onSetAdded,
onRemoveSet,
onUpdateSet,
onSporadicSetAdded
}: UseTrackerProps) => {
@@ -38,49 +38,28 @@ export const useTracker = ({
const [searchQuery, setSearchQuery] = useState<string>('');
const [showSuggestions, setShowSuggestions] = useState(false);
// Timer State
const [elapsedTime, setElapsedTime] = useState<string>('00:00:00');
// Form State
const [weight, setWeight] = useState<string>('');
const [reps, setReps] = useState<string>('');
const [duration, setDuration] = useState<string>('');
const [distance, setDistance] = useState<string>('');
const [height, setHeight] = useState<string>('');
const [bwPercentage, setBwPercentage] = useState<string>('100');
// User Weight State
const [userBodyWeight, setUserBodyWeight] = useState<string>(userWeight ? userWeight.toString() : '70');
// Create Exercise State
const [isCreating, setIsCreating] = useState(false);
// Plan Execution State
const [currentStepIndex, setCurrentStepIndex] = useState(0);
const [showPlanPrep, setShowPlanPrep] = useState<WorkoutPlan | null>(null);
const [showPlanList, setShowPlanList] = useState(false);
// Confirmation State
const [showFinishConfirm, setShowFinishConfirm] = useState(false);
const [showQuitConfirm, setShowQuitConfirm] = useState(false);
const [showMenu, setShowMenu] = useState(false);
// Edit Set State
const [editingSetId, setEditingSetId] = useState<string | null>(null);
const [editWeight, setEditWeight] = useState<string>('');
const [editReps, setEditReps] = useState<string>('');
const [editDuration, setEditDuration] = useState<string>('');
const [editDistance, setEditDistance] = useState<string>('');
const [editHeight, setEditHeight] = useState<string>('');
// Quick Log State
const [quickLogSession, setQuickLogSession] = useState<WorkoutSession | null>(null);
const [isSporadicMode, setIsSporadicMode] = useState(false);
const [sporadicSuccess, setSporadicSuccess] = useState(false);
// Unilateral Exercise State
const [unilateralSide, setUnilateralSide] = useState<'LEFT' | 'RIGHT'>('LEFT');
// Hooks
const elapsedTime = useSessionTimer(activeSession);
const form = useWorkoutForm({ userId, onUpdateSet });
const planExec = usePlanExecution({ activeSession, activePlan, exercises });
// Initial Data Load
useEffect(() => {
const loadData = async () => {
const exList = await getExercises(userId);
@@ -95,15 +74,7 @@ export const useTracker = ({
setUserBodyWeight(userWeight.toString());
}
// Load Quick Log Session
try {
const response = await api.get('/sessions/quick-log');
if (response.success && response.session) {
setQuickLogSession(response.session);
}
} catch (error) {
console.error("Failed to load quick log session:", error);
}
loadQuickLogSession();
};
loadData();
}, [activeSession, userId, userWeight, activePlan]);
@@ -120,107 +91,30 @@ export const useTracker = ({
}
};
// Timer Logic
// Auto-select exercise from plan step
useEffect(() => {
let interval: number;
if (activeSession) {
const updateTimer = () => {
const diff = Math.floor((Date.now() - activeSession.startTime) / 1000);
const h = Math.floor(diff / 3600);
const m = Math.floor((diff % 3600) / 60);
const s = diff % 60;
setElapsedTime(`${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`);
};
updateTimer();
interval = window.setInterval(updateTimer, 1000);
}
return () => clearInterval(interval);
}, [activeSession]);
// Recalculate current step when sets change
useEffect(() => {
if (activeSession && activePlan) {
const performedCounts = new Map<string, number>();
for (const set of activeSession.sets) {
performedCounts.set(set.exerciseId, (performedCounts.get(set.exerciseId) || 0) + 1);
}
let nextStepIndex = activePlan.steps.length; // Default to finished
const plannedCounts = new Map<string, number>();
for (let i = 0; i < activePlan.steps.length; i++) {
const step = activePlan.steps[i];
const exerciseId = step.exerciseId;
plannedCounts.set(exerciseId, (plannedCounts.get(exerciseId) || 0) + 1);
const performedCount = performedCounts.get(exerciseId) || 0;
if (performedCount < plannedCounts.get(exerciseId)!) {
nextStepIndex = i;
break;
}
}
setCurrentStepIndex(nextStepIndex);
}
}, [activeSession, activePlan]);
useEffect(() => {
if (activeSession && activePlan && exercises.length > 0 && activePlan.steps.length > 0) {
if (currentStepIndex < activePlan.steps.length) {
const step = activePlan.steps[currentStepIndex];
if (step) {
const exDef = exercises.find(e => e.id === step.exerciseId);
if (exDef) {
setSelectedExercise(exDef);
}
}
const step = planExec.getCurrentStep();
if (step) {
const exDef = exercises.find(e => e.id === step.exerciseId);
if (exDef) {
setSelectedExercise(exDef);
}
}
}, [currentStepIndex, activePlan, exercises]);
}, [planExec.currentStepIndex, activePlan, exercises]);
// Update form when exercise changes
useEffect(() => {
const updateSelection = async () => {
if (selectedExercise) {
setBwPercentage(selectedExercise.bodyWeightPercentage ? selectedExercise.bodyWeightPercentage.toString() : '100');
setSearchQuery(selectedExercise.name);
const set = await getLastSetForExercise(userId, selectedExercise.id);
setLastSet(set);
if (set) {
setWeight(set.weight?.toString() || '');
setReps(set.reps?.toString() || '');
setDuration(set.durationSeconds?.toString() || '');
setDistance(set.distanceMeters?.toString() || '');
setHeight(set.height?.toString() || '');
} else {
setWeight(''); setReps(''); setDuration(''); setDistance(''); setHeight('');
}
// Clear fields not relevant to the selected exercise type
if (selectedExercise.type !== ExerciseType.STRENGTH && selectedExercise.type !== ExerciseType.BODYWEIGHT) {
setWeight('');
}
if (selectedExercise.type !== ExerciseType.STRENGTH && selectedExercise.type !== ExerciseType.BODYWEIGHT && selectedExercise.type !== ExerciseType.PLYOMETRIC) {
setReps('');
}
if (selectedExercise.type !== ExerciseType.CARDIO && selectedExercise.type !== ExerciseType.STATIC) {
setDuration('');
}
if (selectedExercise.type !== ExerciseType.CARDIO && selectedExercise.type !== ExerciseType.LONG_JUMP) {
setDistance('');
}
if (selectedExercise.type !== ExerciseType.HIGH_JUMP) {
setHeight('');
}
await form.updateFormFromLastSet(selectedExercise.id, selectedExercise.type, selectedExercise.bodyWeightPercentage);
} else {
setSearchQuery(''); // Clear search query if no exercise is selected
setSearchQuery('');
}
};
updateSelection();
}, [selectedExercise, userId]);
const filteredExercises = searchQuery === ''
? exercises
: exercises.filter(ex =>
@@ -229,58 +123,23 @@ export const useTracker = ({
const handleStart = (plan?: WorkoutPlan) => {
if (plan && plan.description) {
setShowPlanPrep(plan);
planExec.setShowPlanPrep(plan);
} else {
onSessionStart(plan, parseFloat(userBodyWeight));
}
};
const confirmPlanStart = () => {
if (showPlanPrep) {
onSessionStart(showPlanPrep, parseFloat(userBodyWeight));
setShowPlanPrep(null);
if (planExec.showPlanPrep) {
onSessionStart(planExec.showPlanPrep, parseFloat(userBodyWeight));
planExec.setShowPlanPrep(null);
}
}
const handleAddSet = async () => {
if (!activeSession || !selectedExercise) return;
const setData: Partial<WorkoutSet> = {
exerciseId: selectedExercise.id,
};
if (selectedExercise.isUnilateral) {
setData.side = unilateralSide;
}
switch (selectedExercise.type) {
case ExerciseType.STRENGTH:
if (weight) setData.weight = parseFloat(weight);
if (reps) setData.reps = parseInt(reps);
break;
case ExerciseType.BODYWEIGHT:
if (weight) setData.weight = parseFloat(weight);
if (reps) setData.reps = parseInt(reps);
setData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.CARDIO:
if (duration) setData.durationSeconds = parseInt(duration);
if (distance) setData.distanceMeters = parseFloat(distance);
break;
case ExerciseType.STATIC:
if (duration) setData.durationSeconds = parseInt(duration);
setData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.HIGH_JUMP:
if (height) setData.height = parseFloat(height);
break;
case ExerciseType.LONG_JUMP:
if (distance) setData.distanceMeters = parseFloat(distance);
break;
case ExerciseType.PLYOMETRIC:
if (reps) setData.reps = parseInt(reps);
break;
}
const setData = form.prepareSetData(selectedExercise);
try {
const response = await api.post('/sessions/active/log-set', setData);
@@ -291,11 +150,10 @@ export const useTracker = ({
if (activePlan && activeExerciseId) {
const nextStepIndex = activePlan.steps.findIndex(step => step.exerciseId === activeExerciseId);
if (nextStepIndex !== -1) {
setCurrentStepIndex(nextStepIndex);
planExec.setCurrentStepIndex(nextStepIndex);
}
} else if (activePlan && !activeExerciseId) {
// Plan is finished
setCurrentStepIndex(activePlan.steps.length);
planExec.setCurrentStepIndex(activePlan.steps.length);
}
}
} catch (error) {
@@ -305,62 +163,15 @@ export const useTracker = ({
const handleLogSporadicSet = async () => {
if (!selectedExercise) return;
const setData: any = {
exerciseId: selectedExercise.id,
};
if (selectedExercise.isUnilateral) {
setData.side = unilateralSide;
}
switch (selectedExercise.type) {
case ExerciseType.STRENGTH:
if (weight) setData.weight = parseFloat(weight);
if (reps) setData.reps = parseInt(reps);
break;
case ExerciseType.BODYWEIGHT:
if (weight) setData.weight = parseFloat(weight);
if (reps) setData.reps = parseInt(reps);
setData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.CARDIO:
if (duration) setData.durationSeconds = parseInt(duration);
if (distance) setData.distanceMeters = parseFloat(distance);
break;
case ExerciseType.STATIC:
if (duration) setData.durationSeconds = parseInt(duration);
setData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.HIGH_JUMP:
if (height) setData.height = parseFloat(height);
break;
case ExerciseType.LONG_JUMP:
if (distance) setData.distanceMeters = parseFloat(distance);
break;
case ExerciseType.PLYOMETRIC:
if (reps) setData.reps = parseInt(reps);
break;
}
const setData = form.prepareSetData(selectedExercise);
try {
const response = await api.post('/sessions/quick-log/set', setData);
if (response.success) {
setSporadicSuccess(true);
setTimeout(() => setSporadicSuccess(false), 2000);
// Refresh quick log session
const sessionRes = await api.get('/sessions/quick-log');
if (sessionRes.success && sessionRes.session) {
setQuickLogSession(sessionRes.session);
}
// Reset form
setWeight('');
setReps('');
setDuration('');
setDistance('');
setHeight('');
loadQuickLogSession();
form.resetForm();
if (onSporadicSetAdded) onSporadicSetAdded();
}
} catch (error) {
@@ -376,44 +187,14 @@ export const useTracker = ({
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);
setShowPlanList(false);
};
// Forwarding form handlers from hook
const handleEditSet = form.startEditing;
const handleSaveEdit = form.saveEdit;
const handleCancelEdit = form.cancelEdit;
// Reset override
const resetForm = () => {
setWeight('');
setReps('');
setDuration('');
setDistance('');
setHeight('');
form.resetForm();
setSelectedExercise(null);
setSearchQuery('');
setSporadicSuccess(false);
@@ -431,46 +212,37 @@ export const useTracker = ({
showSuggestions,
setShowSuggestions,
elapsedTime,
weight,
setWeight,
reps,
setReps,
duration,
setDuration,
distance,
setDistance,
height,
setHeight,
bwPercentage,
setBwPercentage,
userBodyWeight,
setUserBodyWeight,
isCreating,
setIsCreating,
currentStepIndex,
showPlanPrep,
setShowPlanPrep,
showPlanList,
setShowPlanList,
showFinishConfirm,
setShowFinishConfirm,
showQuitConfirm,
setShowQuitConfirm,
showMenu,
setShowMenu,
editingSetId,
editWeight,
setEditWeight,
editReps,
setEditReps,
editDuration,
setEditDuration,
editDistance,
setEditDistance,
editHeight,
setEditHeight,
isSporadicMode,
setIsSporadicMode,
// Form Props
weight: form.weight, setWeight: form.setWeight,
reps: form.reps, setReps: form.setReps,
duration: form.duration, setDuration: form.setDuration,
distance: form.distance, setDistance: form.setDistance,
height: form.height, setHeight: form.setHeight,
bwPercentage: form.bwPercentage, setBwPercentage: form.setBwPercentage,
unilateralSide: form.unilateralSide, setUnilateralSide: form.setUnilateralSide,
userBodyWeight, setUserBodyWeight,
isCreating, setIsCreating,
// Plan Execution Props
currentStepIndex: planExec.currentStepIndex,
showPlanPrep: planExec.showPlanPrep, setShowPlanPrep: planExec.setShowPlanPrep,
showPlanList: planExec.showPlanList, setShowPlanList: planExec.setShowPlanList,
jumpToStep: planExec.jumpToStep,
showFinishConfirm, setShowFinishConfirm,
showQuitConfirm, setShowQuitConfirm,
showMenu, setShowMenu,
// Editing
editingSetId: form.editingSetId,
editWeight: form.editWeight, setEditWeight: form.setEditWeight,
editReps: form.editReps, setEditReps: form.setEditReps,
editDuration: form.editDuration, setEditDuration: form.setEditDuration,
editDistance: form.editDistance, setEditDistance: form.setEditDistance,
editHeight: form.editHeight, setEditHeight: form.setEditHeight,
isSporadicMode, setIsSporadicMode,
sporadicSuccess,
filteredExercises,
handleStart,
@@ -481,11 +253,8 @@ export const useTracker = ({
handleEditSet,
handleSaveEdit,
handleCancelEdit,
jumpToStep,
resetForm,
unilateralSide,
setUnilateralSide,
quickLogSession, // Export this
loadQuickLogSession, // Export reload function
quickLogSession,
loadQuickLogSession
};
};