# Architecture: Custom Workouts & Flexible Sets **Project:** Gravl — PPL Workout Tracker **Researched:** 2026-02-15 ## Current State Fixed program structure: ``` Users → Programs (hardcoded id=1) → Program Days (6) → Program Exercises (fixed sets) → Workout Logs ``` ## Proposed: Dual Data Paths Two parallel workout sources: 1. **Program Workouts** (existing): Fixed PPL structure, unchanged 2. **Custom Workouts** (new): User-built workouts with flexible sets ### Schema Additions ```sql -- User-created workout templates CREATE TABLE custom_workouts ( id SERIAL PRIMARY KEY, user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT NOW() ); -- Exercises in custom workouts CREATE TABLE custom_workout_exercises ( id SERIAL PRIMARY KEY, custom_workout_id INTEGER REFERENCES custom_workouts(id) ON DELETE CASCADE, exercise_id INTEGER REFERENCES exercises(id), sets INTEGER DEFAULT 3, sort_order INTEGER DEFAULT 0 ); ``` Enhanced `workout_logs`: - Add `source_type` column ('program' | 'custom') — defaults to 'program' for backward compat - Add `custom_workout_exercise_id` column (nullable FK) ### Frontend Components **New pages:** - `CustomWorkoutBuilder.jsx` — search exercises, build workout, save template - `ModifyWorkoutPage.jsx` — fork a program workout into custom **New components:** - `ExerciseSearchInput.jsx` — searchable exercise list - `SetCountEditor.jsx` — +/- controls for set count - `StepperInput.jsx` — number input with +/- buttons and unit label **Enhanced:** - `WorkoutSelectPage.jsx` — show both program and custom workouts - `WorkoutPage.jsx` — flexible set count, stepper inputs ### Backend Endpoints New: - `POST /api/custom-workouts` — create custom workout - `GET /api/custom-workouts` — list user's custom workouts - `GET /api/custom-workouts/:id` — get custom workout with exercises - `PUT /api/custom-workouts/:id` — update custom workout - `DELETE /api/custom-workouts/:id` — delete custom workout - `GET /api/exercises` — list all exercises (for search/selection) Enhanced: - `POST /api/logs` — accept both program_exercise_id and custom_workout_exercise_id ### Data Flow 1. **Program Workout** (unchanged): Dashboard → WorkoutSelectPage → WorkoutPage → logs 2. **Custom Workout Build**: Dashboard → CustomWorkoutBuilder → search exercises → save → POST /api/custom-workouts 3. **Modify Program Workout**: WorkoutPage → "Modify" → fork to custom workout → edit exercises/sets 4. **Flexible Sets**: User clicks "+Set" → local state adds entry → logs all sets on save ## Build Order 1. **Input UX fixes** — stepper inputs, validation, units (no backend changes) 2. **Flexible sets** — local state for set count, backend accepts variable sets 3. **Exercise list endpoint** — GET /api/exercises for search 4. **Custom workout CRUD** — new tables + endpoints 5. **Custom workout builder UI** — frontend page + components 6. **Modify program workout** — fork program workout to custom Each phase builds on the previous. Phase 1 can ship independently.