Drove src/lib/csv.ts to 100% stmts/branch/funcs/lines (was 97.95%/55.55%):
- added a null-fallback row test covering every ?? default (activityName,
userName, insoleType→Kurk, pairCount→2, duration/paused→0) and the null
end-time branch (line 65).
- added CSV-escaping edge tests: commas, embedded quotes, and newlines inside
a field stay inside the quoted cell.
- added formatDuration edge (hours >99 → 100:00:00).
- added a /api/export ordering-by-start-time behavior test and adopted
resetDb per-test isolation in the export suite.
No bugs found; CSV code behaves correctly. sessions.ts /api/export route
already at 100%. Full api suite green (150 tests).
Drives src/routes/sessions.ts and src/lib/work-session.ts to 100%
stmts/branch/funcs/lines (branch was 84.12% / 83.33%).
- sessions.test.ts: added error/edge branch tests — malformed-JSON start
(400), non-numeric id on pause/resume/stop/discard (404), pause/resume of a
no-longer-active session (409), discard missing (404) + already-closed (409),
stop missing (404), and discard ownership scoping (user B → 404).
- work-session.test.ts: mapper unit tests — null insoleType, nullish
pausedSeconds → 0, null durationSeconds/notes, full-field shape with
activity/user opts, and opts defaulting to undefined.
- test/integration/worker-session-lifecycle.test.ts: the anchor lifecycle test
(start→pause→resume→stop→history→export).
No bugs found — the code was correct. The two remaining uncovered branches were
the defensive `row.pausedSeconds ?? 0` guards in resume/stop; pausedSeconds is
NOT NULL DEFAULT 0 in the schema, so the null branch is unreachable via any
route. Annotated both with `/* c8 ignore next -- reason */` (comment only, no
behavior change).
Drives all infra src files to 100% (env.ts, db/client.ts, db/ensure-dir.ts,
db/migrate.ts, db/seed.ts, routes/health.ts).
- env.test.ts: dev defaults when unset, overrides when set (PORT coerced),
CORS_ORIGINS parsing (trim + drop empties) and empty-fallback.
- db.test.ts: runMigrations idempotency; ensureDbDir no-op for non-file URLs
and bare filenames (branch coverage).
- seed.test.ts: production guard — no dev users seeded when NODE_ENV=production.
- c8-ignore the migrate.ts/seed.ts CLI direct-run blocks (unreachable under vitest).
No lifecycle test (infra domain, per plan). No bugs found — infra code correct;
only coverage gaps. All 112 API tests green; typecheck + oxlint clean.
Add the shared SessionForm component (create/edit) used by the Sessies screen.
- Create mode: worker picker (useAdminUsers), activity (useActivities), zooltype,
pair count, start/end datetime-local, pauze, notitie; posts CreateManualSessionInput.
- Edit mode: hides the worker picker, prefills from the row session, adds a status
select; PUTs AdminUpdateSessionInput (end_time nulled when status=active).
- Builds ISO start_time/end_time from the datetime-local values; live "gewerkt"
preview = end - start - paused; inline error on a 400.
- Wires the form into Sessions.tsx ("+ Nieuwe registratie" -> create, pencil -> edit).
Products affected: SoleLog admin client
Sub
Phase 3b.1 needs admin-side write paths so an admin can hand-create a
session for a worker, correct any session, and stop/cancel a worker's
stuck active session — duration always derived server-side.
Adds under the existing /api/admin/* admin guard:
- GET /api/admin/users — {id,name,email} roster ordered by name (worker picker)
- POST /api/admin/sessions — manual create -> completed, source=manual,
duration = max(0, round((end-start)/1000) - paused)
- PUT /api/admin/sessions/:id — edit any session (no user reassignment);
recomputes duration when end_time present, else duration null
- POST /api/admin/sessions/:id/stop — fold open pause, end=now, completed
- POST /api/admin/sessions/:id/discard — status=discarded, end=now
Validates end>=start, pair_count>=1, paused<=span, activity/user exist
(400/404); stop/discard on an already-closed session -> 409. Responses
reuse toWorkSession.
Products affected: SoleLog backend (apps/api)
Add CreateManualSessionInput and AdminUpdateSessionInput zod schemas plus
inferred types. packages/shared has no test runner, so these are exercised
via Task 2's API tests rather than a standalone parse test.
The repo was authored prettier-style (trailing-comma 'all') but .oxfmtrc.json
was set to 'es5', so every formatted file diverged. Switch the config to 'all'
to match the existing code, ignore docs/** and **/drizzle/** (prose + generated
snapshots the formatter should not own), and reformat the source tree once for
consistency. No behavioural change; all suites green (api 60, worker 28, admin 21).
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.
- Live cards freeze the worked timer at paused_at and show an amber
"Gepauzeerd" badge plus a "Pauze H:MM:SS" total when paused.
- AuthContext.signOut resets the path to / so the next admin login lands
on Live rather than the tab it logged out from.
Add user-scoped POST /api/sessions/:id/pause and /resume endpoints,
mirroring the stop handler's ownership/lookup and 401/404/409 guards.
Pause sets paused_at (status stays active); resume folds the open span
into paused_seconds and clears paused_at.
Change stop to fold any open pause span into paused_seconds, then set
duration_seconds = max(0, round((end-start)/1000) - paused_seconds) so
saved duration is worked time, and clear paused_at.
Add a "Paused Duration" column to /api/export (after "Total Duration")
using formatDuration(paused_seconds).
Products affected: SoleLog backend (apps/api)