Set logging is now a united. Sporadic set table removed.

This commit is contained in:
AG
2025-12-05 08:55:59 +02:00
parent a632de65ea
commit 41d1d0f16a
19 changed files with 1129 additions and 1232 deletions

View File

@@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { WorkoutSession, WorkoutSet, ExerciseDef, ExerciseType, WorkoutPlan, Language } from '../../types';
import { getExercises, getLastSetForExercise, saveExercise, getPlans } from '../../services/storage';
import { api } from '../../services/api';
import { logSporadicSet } from '../../services/sporadicSets';
interface UseTrackerProps {
userId: string;
@@ -73,39 +73,13 @@ export const useTracker = ({
const [editDistance, setEditDistance] = useState<string>('');
const [editHeight, setEditHeight] = useState<string>('');
// Sporadic Set State
// Quick Log State
const [quickLogSession, setQuickLogSession] = useState<WorkoutSession | null>(null);
const [isSporadicMode, setIsSporadicMode] = useState(false);
const [sporadicSuccess, setSporadicSuccess] = useState(false);
// Unilateral Exercise State
const [sameValuesBothSides, setSameValuesBothSides] = useState(true);
const [weightLeft, setWeightLeft] = useState<string>('');
const [weightRight, setWeightRight] = useState<string>('');
const [repsLeft, setRepsLeft] = useState<string>('');
const [repsRight, setRepsRight] = useState<string>('');
const [durationLeft, setDurationLeft] = useState<string>('');
const [durationRight, setDurationRight] = useState<string>('');
const [distanceLeft, setDistanceLeft] = useState<string>('');
const [distanceRight, setDistanceRight] = useState<string>('');
const [heightLeft, setHeightLeft] = useState<string>('');
const [heightRight, setHeightRight] = useState<string>('');
const handleToggleSameValues = (checked: boolean) => {
setSameValuesBothSides(checked);
if (!checked) {
// Propagate values from single fields to left/right fields
setWeightLeft(weight);
setWeightRight(weight);
setRepsLeft(reps);
setRepsRight(reps);
setDurationLeft(duration);
setDurationRight(duration);
setDistanceLeft(distance);
setDistanceRight(distance);
setHeightLeft(height);
setHeightRight(height);
}
};
const [unilateralSide, setUnilateralSide] = useState<'LEFT' | 'RIGHT'>('LEFT');
useEffect(() => {
const loadData = async () => {
@@ -120,10 +94,34 @@ export const useTracker = ({
} else if (userWeight) {
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);
}
};
loadData();
}, [activeSession, userId, userWeight, activePlan]);
// Function to reload Quick Log session
const loadQuickLogSession = async () => {
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);
}
};
// Timer Logic
useEffect(() => {
let interval: number;
@@ -167,7 +165,6 @@ export const useTracker = ({
}
}, [activeSession, activePlan]);
useEffect(() => {
if (activeSession && activePlan && exercises.length > 0 && activePlan.steps.length > 0) {
if (currentStepIndex < activePlan.steps.length) {
@@ -222,6 +219,7 @@ export const useTracker = ({
updateSelection();
}, [selectedExercise, userId]);
const filteredExercises = searchQuery === ''
? exercises
: exercises.filter(ex =>
@@ -246,363 +244,126 @@ export const useTracker = ({
const handleAddSet = async () => {
if (!activeSession || !selectedExercise) return;
// For unilateral exercises, create two sets (LEFT and RIGHT)
const setData: Partial<WorkoutSet> = {
exerciseId: selectedExercise.id,
};
if (selectedExercise.isUnilateral) {
const setsToCreate: Array<Partial<WorkoutSet> & { side: 'LEFT' | 'RIGHT' }> = [];
setData.side = unilateralSide;
}
if (sameValuesBothSides) {
// Create two identical sets with LEFT and RIGHT sides
const setData: Partial<WorkoutSet> = {
exerciseId: selectedExercise.id,
};
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;
}
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;
}
try {
const response = await api.post('/sessions/active/log-set', setData);
if (response.success) {
const { newSet, activeExerciseId } = response;
onSetAdded(newSet);
setsToCreate.push({ ...setData, side: 'LEFT' });
setsToCreate.push({ ...setData, side: 'RIGHT' });
} else {
// Create separate sets for LEFT and RIGHT with different values
const leftSetData: Partial<WorkoutSet> = {
exerciseId: selectedExercise.id,
};
const rightSetData: Partial<WorkoutSet> = {
exerciseId: selectedExercise.id,
};
switch (selectedExercise.type) {
case ExerciseType.STRENGTH:
if (weightLeft) leftSetData.weight = parseFloat(weightLeft);
if (repsLeft) leftSetData.reps = parseInt(repsLeft);
if (weightRight) rightSetData.weight = parseFloat(weightRight);
if (repsRight) rightSetData.reps = parseInt(repsRight);
break;
case ExerciseType.BODYWEIGHT:
if (weightLeft) leftSetData.weight = parseFloat(weightLeft);
if (repsLeft) leftSetData.reps = parseInt(repsLeft);
leftSetData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
if (weightRight) rightSetData.weight = parseFloat(weightRight);
if (repsRight) rightSetData.reps = parseInt(repsRight);
rightSetData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.CARDIO:
if (durationLeft) leftSetData.durationSeconds = parseInt(durationLeft);
if (distanceLeft) leftSetData.distanceMeters = parseFloat(distanceLeft);
if (durationRight) rightSetData.durationSeconds = parseInt(durationRight);
if (distanceRight) rightSetData.distanceMeters = parseFloat(distanceRight);
break;
case ExerciseType.STATIC:
if (durationLeft) leftSetData.durationSeconds = parseInt(durationLeft);
leftSetData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
if (durationRight) rightSetData.durationSeconds = parseInt(durationRight);
rightSetData.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.HIGH_JUMP:
if (heightLeft) leftSetData.height = parseFloat(heightLeft);
if (heightRight) rightSetData.height = parseFloat(heightRight);
break;
case ExerciseType.LONG_JUMP:
if (distanceLeft) leftSetData.distanceMeters = parseFloat(distanceLeft);
if (distanceRight) rightSetData.distanceMeters = parseFloat(distanceRight);
break;
case ExerciseType.PLYOMETRIC:
if (repsLeft) leftSetData.reps = parseInt(repsLeft);
if (repsRight) rightSetData.reps = parseInt(repsRight);
break;
}
setsToCreate.push({ ...leftSetData, side: 'LEFT' });
setsToCreate.push({ ...rightSetData, side: 'RIGHT' });
}
// Log both sets
try {
for (const setData of setsToCreate) {
const response = await api.post('/sessions/active/log-set', setData);
if (response.success) {
const { newSet } = response;
onSetAdded(newSet);
if (activePlan && activeExerciseId) {
const nextStepIndex = activePlan.steps.findIndex(step => step.exerciseId === activeExerciseId);
if (nextStepIndex !== -1) {
setCurrentStepIndex(nextStepIndex);
}
} else if (activePlan && !activeExerciseId) {
// Plan is finished
setCurrentStepIndex(activePlan.steps.length);
}
// Update plan progress after logging both sets
if (activePlan) {
const response = await api.post('/sessions/active/log-set', { exerciseId: selectedExercise.id });
if (response.success && response.activeExerciseId) {
const nextStepIndex = activePlan.steps.findIndex(step => step.exerciseId === response.activeExerciseId);
if (nextStepIndex !== -1) {
setCurrentStepIndex(nextStepIndex);
}
} else if (response.success && !response.activeExerciseId) {
setCurrentStepIndex(activePlan.steps.length);
}
}
} catch (error) {
console.error("Failed to log unilateral sets:", error);
}
} else {
// Regular bilateral exercise - single set
const setData: Partial<WorkoutSet> = {
exerciseId: selectedExercise.id,
};
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;
}
try {
const response = await api.post('/sessions/active/log-set', setData);
if (response.success) {
const { newSet, activeExerciseId } = response;
onSetAdded(newSet);
if (activePlan && activeExerciseId) {
const nextStepIndex = activePlan.steps.findIndex(step => step.exerciseId === activeExerciseId);
if (nextStepIndex !== -1) {
setCurrentStepIndex(nextStepIndex);
}
} else if (activePlan && !activeExerciseId) {
// Plan is finished
setCurrentStepIndex(activePlan.steps.length);
}
}
} catch (error) {
console.error("Failed to log set:", error);
}
} catch (error) {
console.error("Failed to log set:", error);
}
};
const handleLogSporadicSet = async () => {
if (!selectedExercise) return;
// For unilateral exercises, create two sets (LEFT and RIGHT)
const setData: any = {
exerciseId: selectedExercise.id,
};
if (selectedExercise.isUnilateral) {
const setsToCreate: any[] = [];
setData.side = unilateralSide;
}
if (sameValuesBothSides) {
// Create two identical sets with LEFT and RIGHT sides
const set: any = {
exerciseId: selectedExercise.id,
timestamp: Date.now(),
};
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;
}
switch (selectedExercise.type) {
case ExerciseType.STRENGTH:
if (weight) set.weight = parseFloat(weight);
if (reps) set.reps = parseInt(reps);
break;
case ExerciseType.BODYWEIGHT:
if (weight) set.weight = parseFloat(weight);
if (reps) set.reps = parseInt(reps);
set.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.CARDIO:
if (duration) set.durationSeconds = parseInt(duration);
if (distance) set.distanceMeters = parseFloat(distance);
break;
case ExerciseType.STATIC:
if (duration) set.durationSeconds = parseInt(duration);
set.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.HIGH_JUMP:
if (height) set.height = parseFloat(height);
break;
case ExerciseType.LONG_JUMP:
if (distance) set.distanceMeters = parseFloat(distance);
break;
case ExerciseType.PLYOMETRIC:
if (reps) set.reps = parseInt(reps);
break;
}
setsToCreate.push({ ...set, side: 'LEFT' });
setsToCreate.push({ ...set, side: 'RIGHT' });
} else {
// Create separate sets for LEFT and RIGHT with different values
const leftSet: any = {
exerciseId: selectedExercise.id,
timestamp: Date.now(),
};
const rightSet: any = {
exerciseId: selectedExercise.id,
timestamp: Date.now(),
};
switch (selectedExercise.type) {
case ExerciseType.STRENGTH:
if (weightLeft) leftSet.weight = parseFloat(weightLeft);
if (repsLeft) leftSet.reps = parseInt(repsLeft);
if (weightRight) rightSet.weight = parseFloat(weightRight);
if (repsRight) rightSet.reps = parseInt(repsRight);
break;
case ExerciseType.BODYWEIGHT:
if (weightLeft) leftSet.weight = parseFloat(weightLeft);
if (repsLeft) leftSet.reps = parseInt(repsLeft);
leftSet.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
if (weightRight) rightSet.weight = parseFloat(weightRight);
if (repsRight) rightSet.reps = parseInt(repsRight);
rightSet.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.CARDIO:
if (durationLeft) leftSet.durationSeconds = parseInt(durationLeft);
if (distanceLeft) leftSet.distanceMeters = parseFloat(distanceLeft);
if (durationRight) rightSet.durationSeconds = parseInt(durationRight);
if (distanceRight) rightSet.distanceMeters = parseFloat(distanceRight);
break;
case ExerciseType.STATIC:
if (durationLeft) leftSet.durationSeconds = parseInt(durationLeft);
leftSet.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
if (durationRight) rightSet.durationSeconds = parseInt(durationRight);
rightSet.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.HIGH_JUMP:
if (heightLeft) leftSet.height = parseFloat(heightLeft);
if (heightRight) rightSet.height = parseFloat(heightRight);
break;
case ExerciseType.LONG_JUMP:
if (distanceLeft) leftSet.distanceMeters = parseFloat(distanceLeft);
if (distanceRight) rightSet.distanceMeters = parseFloat(distanceRight);
break;
case ExerciseType.PLYOMETRIC:
if (repsLeft) leftSet.reps = parseInt(repsLeft);
if (repsRight) rightSet.reps = parseInt(repsRight);
break;
}
setsToCreate.push({ ...leftSet, side: 'LEFT' });
setsToCreate.push({ ...rightSet, side: 'RIGHT' });
}
// Log both sets
try {
for (const set of setsToCreate) {
await logSporadicSet(set);
}
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('');
setWeightLeft('');
setWeightRight('');
setRepsLeft('');
setRepsRight('');
setDurationLeft('');
setDurationRight('');
setDistanceLeft('');
setDistanceRight('');
setHeightLeft('');
setHeightRight('');
if (onSporadicSetAdded) onSporadicSetAdded();
} catch (error) {
console.error("Failed to log unilateral sporadic sets:", error);
}
} else {
// Regular bilateral exercise - single set
const set: any = {
exerciseId: selectedExercise.id,
timestamp: Date.now(),
};
switch (selectedExercise.type) {
case ExerciseType.STRENGTH:
if (weight) set.weight = parseFloat(weight);
if (reps) set.reps = parseInt(reps);
break;
case ExerciseType.BODYWEIGHT:
if (weight) set.weight = parseFloat(weight);
if (reps) set.reps = parseInt(reps);
set.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.CARDIO:
if (duration) set.durationSeconds = parseInt(duration);
if (distance) set.distanceMeters = parseFloat(distance);
break;
case ExerciseType.STATIC:
if (duration) set.durationSeconds = parseInt(duration);
set.bodyWeightPercentage = parseFloat(bwPercentage) || 100;
break;
case ExerciseType.HIGH_JUMP:
if (height) set.height = parseFloat(height);
break;
case ExerciseType.LONG_JUMP:
if (distance) set.distanceMeters = parseFloat(distance);
break;
case ExerciseType.PLYOMETRIC:
if (reps) set.reps = parseInt(reps);
break;
}
try {
const result = await logSporadicSet(set);
if (result) {
setSporadicSuccess(true);
setTimeout(() => setSporadicSuccess(false), 2000);
// Reset form
setWeight('');
setReps('');
setDuration('');
setDistance('');
setHeight('');
if (onSporadicSetAdded) onSporadicSetAdded();
}
} catch (error) {
console.error("Failed to log sporadic set:", error);
}
} catch (error) {
console.error("Failed to log quick log set:", error);
}
};
@@ -721,29 +482,9 @@ export const useTracker = ({
handleCancelEdit,
jumpToStep,
resetForm,
// Unilateral exercise state
sameValuesBothSides,
setSameValuesBothSides,
weightLeft,
setWeightLeft,
weightRight,
setWeightRight,
repsLeft,
setRepsLeft,
repsRight,
setRepsRight,
durationLeft,
setDurationLeft,
durationRight,
setDurationRight,
distanceLeft,
setDistanceLeft,
distanceRight,
setDistanceRight,
heightLeft,
setHeightLeft,
heightRight,
setHeightRight,
handleToggleSameValues,
unilateralSide,
setUnilateralSide,
quickLogSession, // Export this
loadQuickLogSession, // Export reload function
};
};