Files
solelog/docs/sessions/2026-06-17-pause-reorder-loginfix.md
Bas van Rossem 1807f2b6d6 docs: pause-accounting + reorder session log
Finalize the pause-accounting + reorderable-handelingen + login-tab-fix
feature: session log (goal/work/verification/outcome), a one-line roadmap
status note, and an oxfmt pass over the changed files that strips a stray
trailing comma after the last call argument in the worker Stopwatch (es5
trailing-comma style) — pure formatting, tests stay green.
2026-06-17 21:24:16 +02:00

100 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Session: 2026-06-17 — Pause accounting + reorderable handelingen + login-tab fix
## Goal
Ship four maintainer-reported items grouped around one root change (server-authoritative
pause):
1. **Admin shows paused sessions as running** — pause was client-only, so the admin live
view (filters `status='active'`) could not tell paused from running.
2. **Reorderable handelingen** — admin sets the order with ↑/↓ arrows; the worker picker
follows.
3. **Saved duration ignored pause** — stop stored wall-clock `(end start)`, but the
stopwatch displayed *worked* time. Now save worked time and store paused time too.
4. **Wrong default tab after re-login** — logout on the Account tab left the URL at
`/account`, so the next login re-mounted there instead of the Stopwatch.
Spec: `docs/superpowers/specs/2026-06-17-pause-reorder-loginfix-design.md`;
plan: `docs/superpowers/plans/2026-06-17-pause-reorder-loginfix.md`.
## Work done
Implemented task-by-task per the plan (TDD throughout), one commit per task:
- **Task 1 — Contracts + schema + migration + mappers** (`0d82b6e`). `@solelog/shared`:
`WorkSession` gains `paused_seconds: number` + `paused_at: string | null`; `Activity`
gains `sort_order: number`; new `ReorderActivitiesInput`. Schema: `work_sessions`
`paused_seconds` (int NOT NULL default 0) + `paused_at` (timestamp_ms nullable);
`activities.sort_order` (int NOT NULL default 0). Migration `0003` generated via
`db:generate` and applied. `toWorkSession`/`toActivity` map the new fields.
- **Task 2 — Pause/resume endpoints + stop math + CSV column** (`974ecb1`).
`POST /api/sessions/:id/pause` (active + not paused → set `paused_at`, else 409),
`POST /api/sessions/:id/resume` (paused → accumulate `paused_seconds`, clear `paused_at`,
else 409). `stop` folds any open pause span into `paused_seconds`, then
`duration_seconds = max(0, wall paused_seconds)`. `/api/export` gains a
`Paused Duration` column (after `Total Duration`).
- **Task 3 — Orderable activities + reorder endpoint** (`56e0162`). `GET /api/activities`
orders by `(sort_order, name)`; `POST` appends with `max(sort_order)+1`;
`PUT /api/activities/reorder` (admin-gated, registered *before* `:id` routes) assigns
`sort_order = index`, validates the id set (unknown/missing → 400), and returns the
reordered list.
- **Task 4 — Worker Stopwatch server pause + recovery** (`ce396ec`). `usePauseSession` /
`useResumeSession` hooks (invalidate `['sessions']`); tapping the display calls
pause/resume; the local clock stays for snappy feel but the server is source of truth.
Recovery-on-load seeds `isPaused`/`pausedMs`/`pauseStartedMs` from `paused_at` /
`paused_seconds`.
- **Task 5 — Worker History paused line + login-tab fix** (`1765f40`). History card shows
a grey "Pauze H:MM:SS" pill when `paused_seconds > 0`. `AuthContext.signOut` does
`window.history.replaceState(null, '', '/')` before clearing auth so the next login lands
on the Stopwatch.
- **Task 6 — Admin Live paused state + login-tab fix** (`0b0a6bd`). A "Gepauzeerd" badge
when `paused_at` is set; the elapsed timer freezes (worked =
`(paused_at start) paused_seconds`) and a paused total is shown. Admin
`AuthContext.signOut` gets the same path reset (lands on Live).
- **Task 7 — Admin Activities ↑/↓ reorder** (`e48df48`). `useReorderActivities()`
`PUT /api/activities/reorder`. Each non-editing row gets ↑/↓ buttons (aria-labels
"Verplaats <naam> omhoog/omlaag"), disabled at the ends, swapping with the neighbour and
firing the mutation.
- **Task 8 — Docs, lint, verification** (this task). Lint/format on the feature files, full
green matrix, an in-process live smoke, and this session log + roadmap note.
## Verification (Task 8)
- `npx oxlint` — clean (exit 0).
- `npx oxfmt` on the feature-changed files only — reformatted two Task 4 files
(`apps/worker/src/screens/Stopwatch.tsx` + `.test.tsx`) that carried a stray trailing
comma after the last call argument (es5 trailing-comma style strips it); pure formatting,
worker tests + typecheck stay green afterward. All other feature files were already clean.
- `yarn workspace @solelog/api typecheck` — pass; `test`**60 passed** (12 files).
- `yarn workspace @solelog/worker typecheck` — pass; `test`**28 passed** (8 files);
`build` — pass (vite, 91 modules).
- `yarn workspace @solelog/admin typecheck` — pass; `test`**21 passed** (5 files);
`build` — pass (vite, 89 modules).
- **Live smoke** — driven **in-process** (`createApp()` + `app.request`) against a real
on-disk SQLite file freshly migrated to `0003`; no server started, so port 3000 was never
bound (avoids the Windows libsql lock trap). Worker: start → pause (`paused_at` set, still
active) → resume (`paused_seconds = 2`, `paused_at` cleared) → stop
(`duration_seconds = 2`, `paused_seconds = 2`, wall ≈ 4; `duration + paused ≈ wall` and
`duration < wall`). Admin: `PUT /api/activities/reorder``GET /api/activities` reflects
the new order; a worker reorder → 403. The smoke script + temp DB were deleted afterward;
port 3000 confirmed free.
## Outcome
The feature is implemented and green across all three workspaces. Pause is now
server-authoritative: the admin live view shows a "Gepauzeerd" badge with a frozen timer,
the stored `duration_seconds` is worked time (paused time stored separately and surfaced in
the worker History and the CSV `Paused Duration` column), admins reorder handelingen with
↑/↓ arrows (the worker picker inherits the order), and logging out resets the route to `/`
in both clients so the next login lands on Stopwatch (worker) / Live (admin).
The two unrelated working-tree edits to `.env.prod.example` / `docker-compose.prod.yml`
(deploy SQLite bind-mount config) were left untouched — out of this feature's scope.
## Next
- Phase 3b inherits the paused fields through `/api/admin/sessions` (via `toWorkSession`):
the all-sessions list / reports view should show worked + paused per the design.
- Admin pause/resume/stop of *another worker's* session remains Phase 3b (manual-entry /
admin-control work); pause stayed a worker action here.