docs: complete domain research

This commit is contained in:
2026-02-15 22:48:57 +01:00
parent 7d97b2c830
commit 887ab56edc
5 changed files with 325 additions and 0 deletions
+88
View File
@@ -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.
+42
View File
@@ -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)
+72
View File
@@ -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
+64
View File
@@ -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 `<input>` 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.
+59
View File
@@ -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