# 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.
