8.3 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-flexible-sets | 02 | execute | 2 |
|
|
true |
|
Purpose: Without this, deleted set rows leave orphan rows in workout_logs, causing ghost sets to reappear on next load. The frontend dynamic state from plan 01 is correct per-session, but only persists with proper backend deletion.
Output: Backend DELETE endpoint + App.jsx deleteLog function + WorkoutPage onDeleteSet prop wired to ExerciseCard.
<execution_context> @/home/intense/.claude/get-shit-done/workflows/execute-plan.md @/home/intense/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/phases/02-flexible-sets/02-CONTEXT.md @.planning/phases/02-flexible-sets/02-01-SUMMARY.md @backend/src/index.js @frontend/src/App.jsx @frontend/src/pages/WorkoutPage.jsx Task 1: Add DELETE /api/logs endpoint to backend backend/src/index.js Add a DELETE endpoint that removes a specific set log row. Place it directly after the existing POST /api/logs route (around line 329).// Delete a specific set log
app.delete('/api/logs', async (req, res) => {
try {
const { user_id, program_exercise_id, date, set_number } = req.body;
const result = await pool.query(
'DELETE FROM workout_logs WHERE user_id = $1 AND program_exercise_id = $2 AND date = $3 AND set_number = $4 RETURNING id',
[user_id, program_exercise_id, date, set_number]
);
if (result.rows.length === 0) {
return res.status(404).json({ error: 'Log not found' });
}
res.json({ deleted: result.rows[0].id });
} catch (err) {
console.error('Error deleting log:', err);
res.status(500).json({ error: 'Database error' });
}
});
No auth middleware on this endpoint — consistent with the existing POST /api/logs which also has no auth middleware (user_id comes from the request body). Do not add authMiddleware unless the existing POST /api/logs has it (it does not).
Start backend (npm start in backend/) and run:
curl -X DELETE http://localhost:3001/api/logs \ -H "Content-Type: application/json" \ -d '{"user_id":1,"program_exercise_id":1,"date":"2026-02-21","set_number":1}'
Returns {"deleted": N} if row existed, or {"error":"Log not found"} with 404 if not. Server does not crash on missing body fields (returns 404 or 500 gracefully).
DELETE /api/logs endpoint exists, deletes the matching workout_logs row by composite key (user_id, program_exercise_id, date, set_number), returns 200+id on success, 404 if not found.
Add a deleteLog function alongside the existing logSet function:
const deleteLog = async (programExerciseId, setNumber) => {
try {
await fetch(`${API_URL}/logs`, {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
user_id: userId,
program_exercise_id: programExerciseId,
date: today,
set_number: setNumber
})
})
// Remove from local logs state
setLogs(prev => ({
...prev,
[programExerciseId]: (prev[programExerciseId] || []).filter(l => l.set_number !== setNumber)
}))
} catch (err) {
console.error('Failed to delete log:', err)
}
}
Pass deleteLog to WorkoutPage as onDeleteSet:
<WorkoutPage
day={selectedDay}
week={currentWeek}
logs={logs}
onLogSet={logSet}
onDeleteSet={deleteLog}
onBack={() => setView('dashboard')}
fetchProgression={fetchProgression}
/>
In WorkoutPage.jsx:
Update the WorkoutPage function signature to accept onDeleteSet:
function WorkoutPage({ day, week, logs, onLogSet, onDeleteSet, onBack, fetchProgression }) {
Pass onDeleteSet through to each ExerciseCard:
<ExerciseCard
key={exercise.id || idx}
exercise={exercise}
logs={logs[exercise.id] || []}
progression={progressions[exercise.id]}
expanded={expandedExercise === exercise.id}
onToggle={() => setExpandedExercise(
expandedExercise === exercise.id ? null : exercise.id
)}
onLogSet={onLogSet}
onDeleteSet={onDeleteSet}
/>
The ExerciseCard already has onDeleteSet in its prop signature and calls it in handleDeleteSet from plan 01. No changes needed inside ExerciseCard itself — just confirm onDeleteSet is being passed through correctly.
Behavior when delete is called:
- If the set being deleted has
completed: truein setList (was logged to DB),deleteLogfires and removes the DB row - If the set is new and not yet logged (e.g. user added a set but didn't complete it),
onDeleteSetis still called but the DELETE request will return 404 — the catch block silently ignores this (the row didn't exist; no harm done)
This means the handleDeleteSet in ExerciseCard should always call onDeleteSet regardless of whether the set was completed — the backend handles the "not found" case gracefully.
In the dev server:
1. Start a workout, complete set 1 of an exercise (logs it to DB)
2. Verify it's logged: curl "http://localhost:3001/api/logs?user_id=1&program_exercise_id=1&date=TODAY"
3. Delete set 1 row using the trash icon
4. Verify it's gone from DB: repeat the curl — set_number=1 should no longer appear
5. Add a new set (Vanligt set), complete it — verify it appears in DB as the next set_number
6. Reload the workout — no ghost sets, count matches what was logged
deleteLog in App.jsx calls DELETE /api/logs. WorkoutPage passes onDeleteSet to ExerciseCard. Deleting a completed set removes it from the database and from local logs state. Non-logged sets delete silently without error.
Full flow test:
- Open a workout
- Add 2 extra sets to the first exercise (Vanligt set)
- Complete all sets — verify they all persist in DB
- Delete the middle set — verify DB row removed, UI renumbers
- Save workout (navigate back to dashboard)
- Re-open same workout — set count matches what was logged, no ghost rows
<success_criteria> DELETE /api/logs endpoint exists in backend. App.jsx has deleteLog function. WorkoutPage passes onDeleteSet to ExerciseCard. Deleting a logged set removes it from DB. Adding and completing new sets saves beyond original program set count. Frontend build passes. </success_criteria>
After completion, create `.planning/phases/02-flexible-sets/02-02-SUMMARY.md`