Files
gravl/.planning/phases/02-flexible-sets/02-01-SUMMARY.md
T
clawd 616451af20 docs(02-01): complete flexible sets plan 01 — summary and state update
- 02-01-SUMMARY.md: dynamic setList refactor, add-set modal, delete-set with last-set guard
- STATE.md: advance to phase 2 plan 2, record decisions, update metrics and session

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 18:43:18 +01:00

6.0 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established duration completed
02-flexible-sets 01 ui
react
workout
setlist
modal
dynamic-sets
dropset
phase provides
01-input-ux WeightInput, RepsInput, StepperInput components integrated into ExerciseCard set rows
ExerciseCard with dynamic setList array (replaces fixed exercise.sets count)
Add-set modal with Vanligt set and Dropset choices
Delete-set button per row with last-set guard
Trash icon added to Icons.jsx library
CSS
.add-set-btn, .delete-set-btn, .set-type-modal-overlay, .set-type-modal, .set-type-option
02-02-flexible-sets
backend-logging
added patterns
setList array replaces keyed object for ordered set state
idx+1 as set_number derivation
last-set guard pattern
created modified
frontend/src/pages/WorkoutPage.jsx
frontend/src/components/Icons.jsx
frontend/src/App.css
setList uses array index (not set_number key) — set_number derived as idx+1 when calling onLogSet
Dropset weight drops: 80% then 60% of base weight, each rounded to nearest 2.5kg per app progression convention
Last-set guard: handleDeleteSet returns early if setList.length <= 1, delete button also gets disabled attribute
progress-badge and all-done class now reference setList.length instead of exercise.sets — badge reflects actual set count
CSS --surface variable not present in app; used --bg-card for modal background to match existing dark theme
onDeleteSet prop is optional (stub) — backend wiring deferred to plan 02
setList pattern: dynamic ordered array of {weight, reps, completed} objects as single source of truth for set count
Modal bottom sheet: fixed overlay + border-radius top only on card, safe-area-inset-bottom padding for iOS
last-set guard: both UI (disabled attribute + .disabled class) and logic (early return) prevent deleting last set
8min 2026-02-21

Phase 2 Plan 01: Flexible Sets — Dynamic setList, Add-Set Modal, Delete-Set Summary

ExerciseCard refactored to dynamic setList array with add-set bottom-sheet modal (Vanligt set / Dropset) and inline delete button with last-set guard

Performance

  • Duration: ~8 min
  • Started: 2026-02-21T00:00:00Z
  • Completed: 2026-02-21T00:08:00Z
  • Tasks: 2
  • Files modified: 3

Accomplishments

  • ExerciseCard state migrated from keyed setInputs object to ordered setList array — enables variable-length set lists
  • Add-set bottom-sheet modal with two choices: Vanligt set (copies last row's weight/reps) and Dropset (3 sets at 100%/80%/60% weight rounded to 2.5kg, 10 reps)
  • Per-row delete button with dual guard (disabled attribute + early return) prevents deleting the last remaining set
  • Trash icon SVG added to Icons.jsx (outline style, consistent with existing library)
  • All new interactive elements meet 44px minimum touch target requirement
  • Build passes with no errors after both changes

Task Commits

Each task was committed atomically:

  1. Task 1: Refactor ExerciseCard to dynamic setList + add-set modal + delete-set button - af80f16 (feat)
  2. Task 2: Add CSS for modal overlay, add-set button, and delete-set button - 3d8a29c (feat)

Files Created/Modified

  • frontend/src/pages/WorkoutPage.jsx - ExerciseCard fully refactored: setList state, handleAddNormal, handleAddDropset, handleDeleteSet, updated render with modal JSX
  • frontend/src/components/Icons.jsx - Added trash SVG icon to Icons object
  • frontend/src/App.css - Added 128 lines: .add-set-btn, .delete-set-btn (with disabled/hover states), .set-type-modal-overlay, .set-type-modal, .set-type-option, .set-type-option.dropset, .set-type-cancel

Decisions Made

  • setList as array not object: Array index (idx) is the position; set_number is derived as idx+1 when calling onLogSet. Simpler than maintaining a keyed object when order matters for renumbering.
  • Dropset percentages: 80% and 60% of base weight (20% drop per step), rounded to nearest 2.5kg — matches app's progression convention and research confirming 20% drops.
  • CSS --bg-card over --surface: Plan used --surface which doesn't exist in the theme; --bg-card is the correct variable for card backgrounds.
  • onDeleteSet as optional stub: Backend wiring (deleting orphaned set_number rows) is deferred to plan 02. The prop is accepted but only called if provided.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Used --bg-card instead of nonexistent --surface CSS variable

  • Found during: Task 2 (CSS addition)
  • Issue: Plan specified var(--surface) and var(--surface-2) for modal background, but these variables do not exist in App.css; the app uses --bg-card and --bg-secondary
  • Fix: Replaced var(--surface) with var(--bg-card) and var(--surface-2, rgba(255,255,255,0.05)) with var(--bg-secondary) in the modal CSS
  • Files modified: frontend/src/App.css
  • Verification: Build passes, variables resolve correctly in dark theme context
  • Committed in: 3d8a29c (Task 2 commit)

Total deviations: 1 auto-fixed (Rule 1 - variable name correction) Impact on plan: Minor correction required for CSS to work correctly. No scope change.

Issues Encountered

None — build passed cleanly after each task. The CSS variable substitution was caught during Task 2 before committing.

User Setup Required

None - no external service configuration required.

Next Phase Readiness

  • ExerciseCard now supports variable-length set lists entirely in frontend state
  • Backend already persists sets by (exercise_id, set_number) via upsert — adding sets on frontend means next save includes correct sequence
  • Plan 02 can wire onDeleteSet to call a DELETE /api/logs/:id endpoint to remove orphaned set_number rows from workout_logs when a set is deleted mid-workout

Phase: 02-flexible-sets Completed: 2026-02-21