import { useState, useEffect } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import { FaArrowLeft, FaCheck, FaSave, FaPlay, FaStop, FaStar, FaRegStar, FaForward } from 'react-icons/fa'; import type { Routine, Exercise, RecordRoutine, RecordExercise, RecordSet, Set, RecordRoutineItem } from '../types/models'; import { RoutineService, WorkoutService } from '../services/api'; interface SetForWorkout { id?: number; setId: number; actualReps: number; actualWeight: number; actualDuration: number; originalSet: Set; completed: boolean; } interface ExerciseForWorkout { id?: number; exerciseId: number; exercise: Exercise; sets: SetForWorkout[]; startedAt?: string; endedAt?: string; actualRestTime: number; // in seconds notes: string; } const NewWorkoutPage = () => { const navigate = useNavigate(); const location = useLocation(); // Routines state const [routines, setRoutines] = useState([]); const [selectedRoutine, setSelectedRoutine] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Workout tracking state const [workoutStarted, setWorkoutStarted] = useState(false); const [workoutCompleted, setWorkoutCompleted] = useState(false); const [startTime, setStartTime] = useState(''); const [endTime, setEndTime] = useState(null); const [elapsedSeconds, setElapsedSeconds] = useState(0); const [intervalId, setIntervalId] = useState(null); // Exercise tracking state const [currentExerciseIndex, setCurrentExerciseIndex] = useState(0); const [workoutExercises, setWorkoutExercises] = useState([]); // Workout notes and rating const [workoutNotes, setWorkoutNotes] = useState(''); const [feelingRating, setFeelingRating] = useState(3); // Success message state const [successMessage, setSuccessMessage] = useState(null); // Load routines and check for pre-selected routine useEffect(() => { const fetchRoutines = async () => { try { setIsLoading(true); const data = await RoutineService.getAll(); setRoutines(data); // Check if a routine was pre-selected (from workouts page) if (location.state && location.state.routineId) { const routineId = location.state.routineId; const routine = data.find(r => r.id === routineId); if (routine) { handleSelectRoutine(routine); } } setError(null); } catch (err) { console.error('Failed to fetch routines:', err); setError('Could not load workout routines. Please try again later.'); } finally { setIsLoading(false); } }; fetchRoutines(); }, [location]); // Setup the workout when a routine is selected const handleSelectRoutine = (routine: Routine) => { setSelectedRoutine(routine); // Initialize workout exercises from routine items const exercises: ExerciseForWorkout[] = []; // Process routine items into exercises for the workout routine.routineItems.forEach(item => { if (item.exercise && item.exerciseId) { // This is a regular exercise item const exercise = item.exercise; // Get the sets from the exercise or create default ones const exerciseSets = exercise.sets || []; const setsForWorkout: SetForWorkout[] = exerciseSets.map(set => ({ setId: set.id || 0, originalSet: set, actualReps: set.reps, actualWeight: set.weight, actualDuration: set.duration, completed: false })); // If there are no sets defined, create a default set if (setsForWorkout.length === 0) { setsForWorkout.push({ setId: 0, originalSet: { id: 0, exerciseId: exercise.id || 0, reps: 10, weight: 0, duration: 0, orderIndex: 0 }, actualReps: 10, actualWeight: 0, actualDuration: 0, completed: false }); } exercises.push({ exerciseId: exercise.id || 0, exercise: exercise, sets: setsForWorkout, actualRestTime: item.restTime, notes: '' }); } // We could handle supersets here if needed }); setWorkoutExercises(exercises); setCurrentExerciseIndex(0); }; // Start the workout const startWorkout = () => { if (!selectedRoutine) return; const now = new Date().toISOString(); setStartTime(now); setWorkoutStarted(true); // Mark first exercise as started if (workoutExercises.length > 0) { const updatedExercises = [...workoutExercises]; updatedExercises[0].startedAt = now; setWorkoutExercises(updatedExercises); } // Start the timer const id = window.setInterval(() => { setElapsedSeconds(prev => prev + 1); }, 1000); setIntervalId(id); }; // Complete the workout const completeWorkout = () => { if (intervalId) { clearInterval(intervalId); setIntervalId(null); } const now = new Date().toISOString(); setEndTime(now); // Mark current exercise as completed if not already if (workoutExercises.length > 0 && currentExerciseIndex < workoutExercises.length) { const updatedExercises = [...workoutExercises]; const currentExercise = updatedExercises[currentExerciseIndex]; if (currentExercise.startedAt && !currentExercise.endedAt) { currentExercise.endedAt = now; } setWorkoutExercises(updatedExercises); } setWorkoutCompleted(true); }; // Format timer display const formatTime = (seconds: number) => { const hrs = Math.floor(seconds / 3600); const mins = Math.floor((seconds % 3600) / 60); const secs = seconds % 60; return `${hrs > 0 ? hrs + ':' : ''}${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`; }; // Handle set completion toggle const toggleSetCompleted = (exerciseIndex: number, setIndex: number) => { const updatedExercises = [...workoutExercises]; const currentSet = updatedExercises[exerciseIndex].sets[setIndex]; currentSet.completed = !currentSet.completed; setWorkoutExercises(updatedExercises); }; // Handle weight, reps or duration change const handleSetDataChange = ( exerciseIndex: number, setIndex: number, field: 'actualReps' | 'actualWeight' | 'actualDuration', value: number ) => { const updatedExercises = [...workoutExercises]; const currentSet = updatedExercises[exerciseIndex].sets[setIndex]; currentSet[field] = value; setWorkoutExercises(updatedExercises); }; // Move to next exercise const nextExercise = () => { if (currentExerciseIndex >= workoutExercises.length - 1) return; const now = new Date().toISOString(); const updatedExercises = [...workoutExercises]; // Complete current exercise const currentExercise = updatedExercises[currentExerciseIndex]; if (currentExercise.startedAt && !currentExercise.endedAt) { currentExercise.endedAt = now; } // Start next exercise const nextIndex = currentExerciseIndex + 1; const nextExercise = updatedExercises[nextIndex]; nextExercise.startedAt = now; setWorkoutExercises(updatedExercises); setCurrentExerciseIndex(nextIndex); }; // Handle notes for an exercise const handleExerciseNotes = (exerciseIndex: number, notes: string) => { const updatedExercises = [...workoutExercises]; updatedExercises[exerciseIndex].notes = notes; setWorkoutExercises(updatedExercises); }; // Create RecordSets from workout exercise sets const createRecordSets = (exercise: ExerciseForWorkout): RecordSet[] => { return exercise.sets.map((set, index) => ({ recordExerciseId: 0, // Will be filled in by backend setId: set.setId, actualReps: set.actualReps, actualWeight: set.actualWeight, actualDuration: set.actualDuration, completedAt: exercise.endedAt || new Date().toISOString(), orderIndex: index, set: set.originalSet })); }; // Save workout record const saveWorkout = async () => { if (!selectedRoutine || !startTime) return; try { const now = new Date().toISOString(); // Ensure all exercises have start/end times const completedExercises = workoutExercises.map((ex) => { if (!ex.startedAt) { ex.startedAt = startTime; } if (!ex.endedAt) { ex.endedAt = endTime || now; } return ex; }); // Create RecordExercises from completed exercises const recordExercises: RecordExercise[] = completedExercises.map((ex, index) => ({ id: undefined, recordRoutineId: 0, // Will be filled in by backend exerciseId: ex.exerciseId, startedAt: ex.startedAt || startTime, endedAt: ex.endedAt || now, actualRestTime: ex.actualRestTime, orderIndex: index, recordSets: createRecordSets(ex), exercise: ex.exercise })); // Create RecordRoutineItems from recordExercises const recordRoutineItems: RecordRoutineItem[] = recordExercises.map((ex, index) => ({ recordRoutineId: 0, // Will be filled in by backend recordExerciseId: undefined, // Will be filled in after recordExercise is created recordSuperSetId: null, actualRestTime: workoutExercises[index].actualRestTime, orderIndex: index, recordExercise: ex, recordSuperSet: null })); const workoutRecord: RecordRoutine = { routineId: selectedRoutine.id!, startedAt: startTime, endedAt: endTime || now, routine: selectedRoutine, recordRoutineItems: recordRoutineItems }; await WorkoutService.create(workoutRecord); setSuccessMessage('Workout saved successfully!'); // Redirect after a brief delay setTimeout(() => { navigate('/home'); }, 1500); } catch (err) { console.error('Failed to save workout:', err); setError('Failed to save your workout. Please try again.'); } }; // Check if all sets in current exercise are completed const isCurrentExerciseComplete = () => { if (currentExerciseIndex >= workoutExercises.length) return false; const currentExercise = workoutExercises[currentExerciseIndex]; return currentExercise.sets.every(set => set.completed); }; // Progress status percentage const calculateProgress = () => { if (workoutExercises.length === 0) return 0; const totalSets = workoutExercises.reduce((total, ex) => total + ex.sets.length, 0); const completedSets = workoutExercises.reduce((total, ex) => { return total + ex.sets.filter(set => set.completed).length; }, 0); return Math.round((completedSets / totalSets) * 100); }; return (

New Workout

{error &&
{error}
} {successMessage &&
{successMessage}
} {!selectedRoutine ? ( // Routine selection view

Select a Routine

{isLoading ? (
Loading routines...
) : routines.length === 0 ? (

No routines found.

Create a routine to start working out.

) : (
{routines.map(routine => (
handleSelectRoutine(routine)}>

{routine.name}

{routine.description &&

{routine.description}

}
{routine.routineItems.length} exercises
))}
)}
) : !workoutStarted ? ( // Workout ready view

{selectedRoutine.name}

{selectedRoutine.description &&

{selectedRoutine.description}

}
Exercises: {workoutExercises.length}
Sets: {workoutExercises.reduce((total, ex) => total + ex.sets.length, 0)}

Exercises

    {workoutExercises.map((exercise, index) => (
  • {exercise.exercise.name}
    {exercise.sets.length} sets
  • ))}
) : workoutCompleted ? ( // Workout complete view

Workout Complete!

Duration {formatTime(elapsedSeconds)}
Completed {calculateProgress()}%

How was your workout?

{[1, 2, 3, 4, 5].map(rating => ( ))}