f09eb66fb4
- 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>
124 lines
6.0 KiB
Markdown
124 lines
6.0 KiB
Markdown
---
|
|
phase: 02-flexible-sets
|
|
plan: "01"
|
|
subsystem: ui
|
|
tags: [react, workout, setlist, modal, dynamic-sets, dropset]
|
|
|
|
# Dependency graph
|
|
requires:
|
|
- phase: 01-input-ux
|
|
provides: WeightInput, RepsInput, StepperInput components integrated into ExerciseCard set rows
|
|
provides:
|
|
- 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
|
|
affects: [02-02-flexible-sets, backend-logging]
|
|
|
|
# Tech tracking
|
|
tech-stack:
|
|
added: []
|
|
patterns: [setList array replaces keyed object for ordered set state, idx+1 as set_number derivation, last-set guard pattern]
|
|
|
|
key-files:
|
|
created: []
|
|
modified:
|
|
- frontend/src/pages/WorkoutPage.jsx
|
|
- frontend/src/components/Icons.jsx
|
|
- frontend/src/App.css
|
|
|
|
key-decisions:
|
|
- "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"
|
|
|
|
patterns-established:
|
|
- "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"
|
|
|
|
# Metrics
|
|
duration: 8min
|
|
completed: 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*
|