Unilateral exercises logging
This commit is contained in:
@@ -77,6 +77,36 @@ export const useTracker = ({
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
const exList = await getExercises(userId);
|
||||
@@ -216,100 +246,289 @@ export const useTracker = ({
|
||||
const handleAddSet = async () => {
|
||||
if (!activeSession || !selectedExercise) return;
|
||||
|
||||
const setData: Partial<WorkoutSet> = {
|
||||
exerciseId: selectedExercise.id,
|
||||
};
|
||||
// For unilateral exercises, create two sets (LEFT and RIGHT)
|
||||
if (selectedExercise.isUnilateral) {
|
||||
const setsToCreate: Array<Partial<WorkoutSet> & { side: 'LEFT' | 'RIGHT' }> = [];
|
||||
|
||||
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;
|
||||
}
|
||||
if (sameValuesBothSides) {
|
||||
// Create two identical sets with LEFT and RIGHT sides
|
||||
const setData: Partial<WorkoutSet> = {
|
||||
exerciseId: selectedExercise.id,
|
||||
};
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
const set: any = {
|
||||
exerciseId: selectedExercise.id,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
// For unilateral exercises, create two sets (LEFT and RIGHT)
|
||||
if (selectedExercise.isUnilateral) {
|
||||
const setsToCreate: any[] = [];
|
||||
|
||||
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;
|
||||
}
|
||||
if (sameValuesBothSides) {
|
||||
// Create two identical sets with LEFT and RIGHT sides
|
||||
const set: any = {
|
||||
exerciseId: selectedExercise.id,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await logSporadicSet(set);
|
||||
if (result) {
|
||||
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);
|
||||
}
|
||||
setSporadicSuccess(true);
|
||||
setTimeout(() => setSporadicSuccess(false), 2000);
|
||||
// Reset form
|
||||
@@ -318,10 +537,72 @@ export const useTracker = ({
|
||||
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 sporadic set:", error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -440,5 +721,29 @@ 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,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user