docs: phase 3b.2 reports/export session log + roadmap note
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m17s
All checks were successful
Build and Push Docker Image / build (push) Successful in 1m17s
This commit is contained in:
@@ -187,8 +187,13 @@ Each phase keeps the system working and is its own spec → plan → build cycle
|
||||
`GET /api/admin/users` roster), an admin **Sessies** screen (list + status filter +
|
||||
create/edit form + row actions), Stop/Annuleer on the Live cards, and a worker stopwatch
|
||||
that polls (15 s) and reconciles to server truth (converges within ~15 s when an admin
|
||||
stops/cancels/edits a session — no stuck state). **3b remaining:** reports/export
|
||||
(all-users filtered CSV — current `/api/export` is self-scoped) and user management
|
||||
stops/cancels/edits a session — no stuck state).
|
||||
**3b·2 implemented** (plan `docs/superpowers/plans/2026-06-24-phase-3b2-reports-export.md`):
|
||||
the admin **Rapporten** screen (period presets Deze week / Deze maand / Alles +
|
||||
worker/type/activity filters, headline totals and breakdowns per worker/activity/type) backed by
|
||||
`GET /api/admin/report` (completed-only, JS aggregation), plus an all-users filtered CSV via
|
||||
`GET /api/admin/export` — a shared `buildSessionsCsv(rows, {includeWorker})` now serves both the
|
||||
worker self-export and the admin export. **3b remaining:** user management
|
||||
(better-auth `/api/auth/admin/*`).
|
||||
Activity management (add/edit/delete handelingen + their `insole_types`) was removed from the
|
||||
worker client in the Phase 2 follow-up because it is admin-only; it must be **ported here**. The
|
||||
|
||||
95
docs/sessions/2026-06-24-phase-3b2-reports-export.md
Normal file
95
docs/sessions/2026-06-24-phase-3b2-reports-export.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Session: 2026-06-24 — Phase 3b·2 (Reports + all-users export)
|
||||
|
||||
## Goal
|
||||
|
||||
Give the admin a **Rapporten** screen: pick a period (Deze week / Deze maand / Alles + manual
|
||||
from/to) and optionally narrow by worker / insole type / activity, see headline production totals
|
||||
plus three breakdowns (per medewerker / per handeling / per type), and export the underlying
|
||||
detail rows (all workers) to CSV. Today's `/api/export` is **self-scoped** to the logged-in user;
|
||||
this cycle adds the cross-user, filterable reporting + export. Second of three Phase 3b cycles
|
||||
(after manual-sessions 3b·1; user management remains).
|
||||
Spec: `docs/superpowers/specs/2026-06-24-phase-3b2-reports-export-design.md`;
|
||||
plan: `docs/superpowers/plans/2026-06-24-phase-3b2-reports-export.md`.
|
||||
Tracked as Plane epic **SL-48** with tasks **SL-49…SL-53**.
|
||||
|
||||
## Scope decisions (brainstorming)
|
||||
|
||||
- **Both lenses on one screen** (totals + breakdowns per worker AND activity AND type).
|
||||
- **Filters:** date range (spine) + optional worker + type + activity; default **this week**.
|
||||
- **Completed-only** counts toward totals/export (active/discarded excluded).
|
||||
- **All four metrics:** gewerkte tijd, aantal zolen, aantal sessies, pauzetijd.
|
||||
- **CSV = detail rows, all workers** (a leading Worker column), not the aggregated summary.
|
||||
- **Tables only** — no chart library, no CSS bars (dependency-light).
|
||||
|
||||
## Work done
|
||||
|
||||
Built via a 5-task TDD **Workflow** (`w7ut902zo`), one commit per task, run sequentially because the
|
||||
tasks share one working tree:
|
||||
|
||||
- **Task 1 — DRY CSV builder** (`01fa18f`). Extracted the inline header/row builder from the
|
||||
self-scoped `/api/export` into `buildSessionsCsv(rows, { includeWorker })` + a `SessionCsvRow`
|
||||
interface in `apps/api/src/lib/csv.ts`. `sessions.ts` now maps its query rows into `SessionCsvRow`
|
||||
and calls the shared builder. `includeWorker:false` keeps the worker export **byte-identical**
|
||||
(the existing `export.test.ts` is the regression guard); `includeWorker:true` prepends a Worker
|
||||
column. New `csv.test.ts` covers both header variants.
|
||||
- **Task 2 — Report contracts + `GET /api/admin/report`** (`4b213e2`). Added `ReportTotals`,
|
||||
`ReportWorkerRow`, `ReportActivityRow`, `ReportTypeRow`, `ReportResponse` to `@solelog/shared`.
|
||||
In `admin.ts`: `parseReportQuery` (validates `from`/`to` range, `insole_type`, `activity_id`),
|
||||
`buildSessionFilters` (completed-only + date range + optional worker/type/activity), and the
|
||||
report route — fetches the filtered joined rows and aggregates `totals` + `by_worker`/
|
||||
`by_activity`/`by_type` in JS (sorted by `worked_seconds` desc; empty range → zeros + empty
|
||||
arrays; null type bucketed under "Onbekend"). `report.test.ts` (5 cases).
|
||||
- **Task 3 — `GET /api/admin/export`** (`d33fcb7`). All-users filtered CSV reusing
|
||||
`parseReportQuery` + `buildSessionFilters` (Task 2) and `buildSessionsCsv(includeWorker:true)`
|
||||
(Task 1); ordered by `start_time` asc; filename `solelog-report_<from>_<to>.csv`. 401/403 inherited
|
||||
from the `/api/admin/*` guard. Export tests appended to `report.test.ts`.
|
||||
- **Task 4 — Admin API client** (`8ad2e69`). `apps/admin/src/api/reports.ts`: `ReportFilters`,
|
||||
`filtersToQuery` (omits empty optionals), `useReport` (React Query), and `downloadExport` — a raw
|
||||
`fetch` with the bearer token → `Blob` → object-URL download (a plain `<a href>` can't carry the
|
||||
token). `reports.test.ts` covers serialization + the bearer-fetch download + non-ok throw.
|
||||
- **Task 5 — Rapporten screen + nav** (`8d75be0`). Pure `lib/date-range.ts` helpers (`thisWeek`,
|
||||
`thisMonth`, `allTime`, `dayStartISO`/`dayEndISO` — local-day → ISO instants so there's no server
|
||||
tz ambiguity) with unit tests. `screens/Reports.tsx`: filter bar (presets + date inputs + worker/
|
||||
type/handeling dropdowns), a headline totals line, and three breakdown tables, plus the
|
||||
**Exporteer CSV** button wired to `downloadExport`. Sidebar moves **Rapporten** into `navItems`
|
||||
(soon = just `Gebruikers`); `App.tsx` gets the `/rapporten` route; report styles appended to
|
||||
`styles.css`.
|
||||
|
||||
## Deviations (fix-forward, test-only)
|
||||
|
||||
- **Task 3:** the API test DB is reset once per file (`test/setup.ts`), so the WIDE-window export
|
||||
tests initially picked up sessions seeded by the earlier report tests. Fixed by scoping the two
|
||||
non-gating export tests to their own freshly-seeded activity via `&activity_id=…` (a real filter
|
||||
the route already supports). The route code is exactly as planned.
|
||||
- **Task 5:** the plan's `Reports.test.tsx` had a few assertions that collided with the screen's own
|
||||
markup (headline `36` vs a table cell; `Jan`/`Kurk` matching dropdown `<option>`s). Tightened three
|
||||
assertions (scope to `.reports-totals`; target `getByRole('cell', …)`) and loosened the apiFetch
|
||||
mock signature for a teardown refetch. Screen behavior/markup unchanged.
|
||||
|
||||
## Verification (independent of the workflow's self-reports)
|
||||
|
||||
- `git log` — five task commits `01fa18f → 8d75be0`; **clean tree**.
|
||||
- `yarn workspace @solelog/api test` — **85 passed** (14 files), incl. `csv`, `report`, and the
|
||||
unchanged regression `export.test.ts`; `typecheck` clean.
|
||||
- `yarn workspace @solelog/admin test` — **46 passed** (10 files), incl. `reports`, `date-range`,
|
||||
`Reports`; `typecheck` clean; `build` succeeds (vite, 94 modules).
|
||||
- `npx oxlint` — clean (exit 0).
|
||||
|
||||
## Outcome
|
||||
|
||||
Phase 3b·2 is implemented and green across both touched workspaces. An admin opens **Rapporten**,
|
||||
picks a period + optional filters, and sees correct headline totals (worked / zolen / sessies /
|
||||
pauze) with three breakdown tables, then exports every matching completed session (all workers, with
|
||||
a Worker column) to a range-named CSV. One CSV format now serves both the worker self-export and the
|
||||
admin export. No DB migration (reuses existing `work_sessions` columns). Plane SL-48 + SL-49…SL-53
|
||||
all marked Done. Roadmap Phase 3 note updated: **3b·2 done; user management is the last 3b cycle.**
|
||||
|
||||
The maintainer's deploy WIP (`.env.prod.example` / `docker-compose.prod.yml`) was left untouched.
|
||||
`origin/main` is **behind** (the pre-compaction push never landed); after this cycle `main` is ahead
|
||||
**~16** commits — a single `git push origin main` from the maintainer's terminal carries the batch
|
||||
(and triggers Gitea CI).
|
||||
|
||||
## Next (Phase 3b remainder)
|
||||
|
||||
- **User management** — create / set role / deactivate via better-auth `/api/auth/admin/*` (the
|
||||
`Gebruikers` screen). Final 3b cycle.
|
||||
Reference in New Issue
Block a user