diff --git a/.planning/research/ARCHITECTURE.md b/.planning/research/ARCHITECTURE.md new file mode 100644 index 0000000..7be6c25 --- /dev/null +++ b/.planning/research/ARCHITECTURE.md @@ -0,0 +1,88 @@ +# 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. diff --git a/.planning/research/FEATURES.md b/.planning/research/FEATURES.md new file mode 100644 index 0000000..082ff9e --- /dev/null +++ b/.planning/research/FEATURES.md @@ -0,0 +1,42 @@ +# Features: Workout Logging UX Improvements + +**Project:** Gravl — PPL Workout Tracker +**Researched:** 2026-02-15 + +## Table Stakes (Must-Have) + +| Feature | Complexity | Dependencies | Notes | +|---------|-----------|--------------|-------| +| Input validation (no negative reps, weight min 0) | Low | None | Currently broken — allows any value | +| Weight unit display (kg suffix) | Low | None | Missing — only placeholder text | +| Mobile input layout (44px min touch targets) | Low | None | Currently compressed inputs | +| Add/remove sets per exercise | Medium | Backend log changes | Fixed set count is rigid | +| Pre-fill last workout's weight/reps | Medium | Progression API | Users need reference for what to lift | +| Exercise search/filter | Medium | Exercise list API | Needed for custom workout builder | + +## Differentiators (Competitive Advantage) + +| Feature | Complexity | Dependencies | Notes | +|---------|-----------|--------------|-------| +| Custom workout builder | High | New DB tables, new endpoints | Build workouts from exercise list | +| Modify program workouts | Medium | Custom workout infra | Swap/add exercises mid-workout | +| Rapid-fire set logging | Medium | Stepper inputs | Auto-advance, minimal taps per set | +| Progressive overload visualization | Medium | History data | Show trend vs last workout clearly | +| Rest timer with notifications | Low | Browser APIs | setInterval + Notification API | +| Superset/circuit support | High | Schema changes | Group exercises for alternating sets | + +## Anti-Features (Deliberately Avoid) + +| Feature | Why to Avoid | +|---------|-------------| +| Social features | Users hate mandatory social in workout apps | +| Complex periodization | Overcomplicates a personal PPL tracker | +| Video exercise demos | Storage/bandwidth cost, not core value | +| Gamification (badges, streaks) | Distracts from simple logging | +| AI workout generation | Scope creep — user knows their program | + +## Priority for This Milestone + +1. **Input fixes** — validation, units, layout (table stakes, low effort) +2. **Flexible sets** — add/remove sets (table stakes, medium effort) +3. **Custom workouts** — build from scratch + modify program (differentiator, high effort) diff --git a/.planning/research/PITFALLS.md b/.planning/research/PITFALLS.md new file mode 100644 index 0000000..78c65ec --- /dev/null +++ b/.planning/research/PITFALLS.md @@ -0,0 +1,72 @@ +# Pitfalls: Workout App UX Improvements + +**Project:** Gravl — PPL Workout Tracker +**Researched:** 2026-02-15 + +## Critical Pitfalls (Address in This Milestone) + +### 1. Breaking Existing Logging Flow +- **Risk:** Custom exercises don't integrate with `program_exercise_id` FK; progression and history break +- **Warning signs:** Existing workout logs return empty after schema changes; progression graph gaps +- **Prevention:** Add `source_type` column with default 'program'; never modify existing FK relationships; custom workouts use separate `custom_workout_exercise_id` +- **Phase:** Database schema changes (early) + +### 2. Competing State on Shared Program +- **Risk:** If users modify `program_exercises` directly, it affects ALL users sharing program_id=1 +- **Warning signs:** One user's set count change appears for another user +- **Prevention:** Never modify program_exercises table for per-user changes. Custom modifications create a new custom_workout that forks from the program. Program data stays read-only +- **Phase:** Custom workout architecture + +### 3. Backward Compatibility with Existing Logs +- **Risk:** Existing logs assume fixed sets; schema changes break progression graphs and workout history +- **Warning signs:** Historical workout data disappears or shows incorrectly +- **Prevention:** `source_type` defaults to 'program'; all existing queries continue unchanged; new queries handle both source types +- **Phase:** Database migration + +### 4. Input Validation Gaps +- **Risk:** No validation on negative reps, extreme weights, or invalid set numbers +- **Warning signs:** Corrupted data in database; nonsensical progression suggestions +- **Prevention:** Frontend: `min=0` on inputs, stepper controls with bounds. Backend: validate before insert +- **Phase:** Input UX fixes (Phase 1) + +### 5. Mobile Layout Breakage +- **Risk:** Extra buttons (add set, remove set, modify workout) break 600px layout; unusable on small phones +- **Warning signs:** Horizontal scroll appears; buttons overlap; touch targets too small +- **Prevention:** Design all new controls within existing 600px constraint first; test on 320px width; maintain 44px minimum touch targets +- **Phase:** All UI changes + +### 6. Scope Creep from "Add Set" to Full Program Builder +- **Risk:** "Flexible sets" requirement grows into full periodization, program editor, template system +- **Warning signs:** Conversations about "what if users want to plan a whole week" or "program templates" +- **Prevention:** Strict scope: add/remove sets during a workout session. Custom workouts are simple exercise lists, not programs. No scheduling, no periodization +- **Phase:** Scope discipline throughout + +### 7. Unclear Completion State +- **Risk:** Flexible sets make "workout complete" ambiguous — did they skip a set or just not add one? +- **Warning signs:** Users feel guilty about "incomplete" workouts; confusion about what counts as done +- **Prevention:** No "complete workout" enforcement. Each logged set is saved independently. Summary shows what was actually done, not what was "expected" +- **Phase:** Workout flow UI + +## Gravl-Specific Risks + +### Hardcoded program_id=1 +Dashboard directly fetches `programs/1`. Custom workouts that aren't program-linked will need their own navigation path in WorkoutSelectPage. + +### Upsert-Only Logging +Current `/api/logs` only updates/inserts. If user removes a set, there's no delete mechanism. Need DELETE endpoint for individual log entries. + +### Component-Level State Loss +Logs stored in React useState, not localStorage. If app closes mid-workout, all unlogged progress is lost. Consider auto-saving to localStorage as draft. + +### Single-File Backend +Adding new endpoints to `backend/src/index.js` (already 425 lines) increases risk of accidentally breaking existing routes. Test existing endpoints after each backend change. + +## Pre-Shipping Checklist + +- [ ] Existing workout logging still works identically +- [ ] Historical workout data displays correctly +- [ ] Progression suggestions unchanged for program workouts +- [ ] All inputs validate (no negative reps, no negative weight) +- [ ] Layout works on 320px-600px width range +- [ ] Custom workouts don't affect other users' program data +- [ ] Set add/remove persists correctly in database diff --git a/.planning/research/STACK.md b/.planning/research/STACK.md new file mode 100644 index 0000000..9c21df9 --- /dev/null +++ b/.planning/research/STACK.md @@ -0,0 +1,64 @@ +# Technology Stack: Workout Logging UX Improvements + +**Project:** Gravl — PPL Workout Tracker (UX Milestone) +**Researched:** 2026-02-15 +**Scope:** UX improvements to existing React 18 + Vite + Express + PostgreSQL app + +## What the Codebase Already Has + +| Layer | Technology | Version | Notes | +|-------|------------|---------|-------| +| Frontend framework | React | 18.2.0 | JSX, hooks-based | +| Build tool | Vite | 5.0.8 | Already fast | +| Routing | react-router-dom | 6.21.0 | Mostly unused — App.jsx uses manual `view` state | +| Styling | Plain CSS + CSS custom properties | — | Dark fitness theme, `--accent`, `--bg-*` vars defined | +| Backend | Express | — | REST API, `/api/*` endpoints | +| Database | PostgreSQL | — | workout_logs, programs, exercises tables | +| State | React useState | — | Local component state, no global store | +| HTTP | Native fetch | — | Direct fetch() calls in App.jsx | + +## Recommended Stack Additions + +### Form Validation: React Hook Form + Zod + +| Technology | Version | Purpose | Why | +|------------|---------|---------|-----| +| react-hook-form | 7.x | Input registration, validation | Zero re-renders on keystroke. Integrates with native `` without wrapping. | +| zod | 3.x | Schema for weight/reps | `z.number().min(0).max(500)` reads as documentation. | +| @hookform/resolvers | 3.x | Bridge RHF <-> Zod | Required; maintained by RHF team. | + +**Why not Formik:** Higher re-render cost. Context-based, creates overhead for per-set inline inputs. + +### Number Input Stepper: Custom Component (No Library) + +Build a custom `StepperInput` component with existing CSS variables. The requirement — +/- buttons flanking a number field with a unit label — is ~30 lines of React. Weight step: 2.5 kg. Reps step: 1. + +### Set Count Management: React State Only + +Add a `localSets` state initialized from `exercise.sets`. +/- controls add/remove entries. Copy last set's weight as default for added sets. + +### Custom Workout Creation: Existing Stack + +Use existing React + fetch + PostgreSQL. Add a `WorkoutBuilderPage.jsx`. No new global state needed initially. + +### Touch Target Sizing: CSS Only + +Critical rule: `font-size: 1rem` (minimum 16px) on all inputs prevents iOS Safari auto-zoom. Minimum 44px height on all interactive elements per iOS HIG. + +## What NOT to Add + +| Library | Why to Avoid | +|---------|-------------| +| Formik | Higher re-render cost; worse DX for inline per-row forms | +| Material UI / Chakra UI | Conflicts with custom dark CSS; adds 200KB+ | +| TanStack Query | Simple fetch pattern doesn't warrant it yet | +| Framer Motion | Minimal animation intent; complex on budget phones | +| Redux Toolkit | Overkill for 5-page single-user app | + +## Installation Summary + +```bash +npm install react-hook-form zod @hookform/resolvers +``` + +**Bundle impact:** ~38KB gzipped total addition. diff --git a/.planning/research/SUMMARY.md b/.planning/research/SUMMARY.md new file mode 100644 index 0000000..d18b30c --- /dev/null +++ b/.planning/research/SUMMARY.md @@ -0,0 +1,59 @@ +# Research Summary: Workout UX Improvements + +**Project:** Gravl — PPL Workout Tracker +**Synthesized:** 2026-02-15 + +## Key Findings + +### Stack +- Keep existing React 18 + Vite + Express + PostgreSQL stack +- Add only: `react-hook-form` + `zod` + `@hookform/resolvers` (~38KB gzipped) +- Build custom stepper input component (no library needed) +- Do NOT add: UI frameworks, Redux, TanStack Query, Framer Motion +- CSS-only fix for touch targets: min 44px height, 16px font-size prevents iOS zoom + +### Table Stakes (Must Ship) +- Input validation: no negative reps/weights, proper number constraints +- Weight unit display (kg suffix visible in input) +- Mobile-optimized input layout (larger touch targets) +- Add/remove sets per exercise +- Pre-fill last workout's values for reference + +### Differentiators (Should Ship) +- Custom workout builder (pick exercises, save as template) +- Modify program workouts (fork to custom) +- Stepper inputs for rapid logging (+/- buttons) + +### Watch Out For +1. **Don't break existing flow** — program workout logging must stay identical +2. **Don't modify shared program data** — fork to custom_workout for per-user changes +3. **Don't let scope creep** — "add set" ≠ "full program builder" +4. **Don't break mobile layout** — all new controls must fit 600px width +5. **Backward compat** — existing workout_logs must keep working with new schema + +## Architecture Decision + +**Dual data path:** +- Program workouts (existing, read-only) — unchanged +- Custom workouts (new) — user-created, flexible sets, stored in new tables + +**New tables:** `custom_workouts`, `custom_workout_exercises` +**Enhanced:** `workout_logs` gets `source_type` column (default 'program') + +## Suggested Build Order + +1. Input UX fixes (validation, units, stepper, layout) — no backend changes +2. Flexible sets (local state + backend accepts variable set count) +3. Exercise list endpoint (GET /api/exercises for search) +4. Custom workout CRUD (new tables + endpoints) +5. Custom workout builder UI (frontend page) +6. Modify program workout (fork program → custom) + +Each phase is independently shippable. Phase 1 delivers immediate UX value with zero risk. + +## Research Files + +- `STACK.md` — Technology recommendations and what to avoid +- `FEATURES.md` — Feature categorization (table stakes vs differentiators) +- `ARCHITECTURE.md` — Schema design, component structure, data flow +- `PITFALLS.md` — 7 critical pitfalls with prevention strategies