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

6.1 KiB
Raw Blame History

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 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; test60 passed (12 files).
  • yarn workspace @solelog/worker typecheck — pass; test28 passed (8 files); build — pass (vite, 91 modules).
  • yarn workspace @solelog/admin typecheck — pass; test21 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/reorderGET /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.