feat(web): Gantt and map components, read-only plan board #78

Merged
bart merged 1 commit from feature/web-plan-board into main 2026-09-11 13:29:42 +00:00
Owner

Description

A planner (or viewer) opening the plan board now sees a real Gantt (crews × time) and a MapLibre map for the selected date, built as fully decoupled, reusable components — the Gantt takes plain schedule rows/items, the map takes plain GeoJSON, neither knows about the API or any store. The board's date picker and findings panel load from the plan API; since that API is still stubbed, a new /plan/dev/fixtures page shows the same two components against a realistic checked-in example plan (two crews, four assignments in different triage states, a couple of validator findings, an unassigned job), so the schedule viewer can be reviewed and demoed today. Clicking an assignment on the Gantt highlights its stop on the map and vice versa. Every label and finding on the board is translated (Dutch and English).

This is the read-only viewer only — accepting/rejecting assignments, dragging them between crews, the unassigned-jobs tray, and the version/diff switcher are later tickets building on the same store.

Closes #21. Row T-F (docs/12-implementation-plan.md). Docs: docs/06-ux.md P02 (read-only parts).

Type of Change

  • New feature

Breaking Changes

None.

Test Plan

Tests green: Vitest covers the DTO → view-model mapping functions (including every triage-status/finding-severity combination) and both new components (including a mocked MapLibre so the map component is unit-testable without a real WebGL context). npm run build, npm run lint, and the i18n key-parity check are all green. Manually verified /plan/dev/fixtures renders the fixture plan on both the Gantt and the map.

Checklist

  • Conventional commit(s), one logical change per commit
  • Tests added/updated and passing
  • docs/ updated for any behavioural change (ADR added if a prior decision was reversed)
  • No hard deletes; no direct status updates outside polaris.domain
  • No business logic in routers/components
  • Migration included if the schema changed (one per PR max, reversible downgrade) — n/a, frontend only
  • No new dependency without a reason in the commit body — none added

Additional Context

Two small touches outside this row's own paths, each minimal and explained in the commit body:

  • /dev/fixtures is mounted at /plan/dev/fixtures (via features/plan/plan.routes.ts, which this row owns) instead of a new top-level entry in app.routes.ts. That file is sealed, and its paired spec enforces exact parity between top-level routes and core/nav.ts — a dev-only route has no nav entry by design, so adding it there would have broken that invariant.
  • frontend/src/styles.scss now imports maplibre-gl's own stylesheet globally, since a per-component styleUrl import would blow the (intentionally tight) anyComponentStyle budget.

(The generated-OpenAPI-client lint exclusion this branch needed too was already added by #77 in the meantime — rebased onto main, nothing left to touch there.)

Known, deliberate scope limits (left for the tickets that need them): assignment/map-stop labels are Job #<id> rather than a full "service — vehicle" label, since that needs the jobs/vehicles/catalogue DTOs T-A hasn't shipped yet — the mapping functions take an optional formatter so a richer label is a drop-in later. The "week strip" date switcher is a plain date input for now, not a full week view.

## Description A planner (or viewer) opening the plan board now sees a real Gantt (crews × time) and a MapLibre map for the selected date, built as fully decoupled, reusable components — the Gantt takes plain schedule rows/items, the map takes plain GeoJSON, neither knows about the API or any store. The board's date picker and findings panel load from the plan API; since that API is still stubbed, a new `/plan/dev/fixtures` page shows the same two components against a realistic checked-in example plan (two crews, four assignments in different triage states, a couple of validator findings, an unassigned job), so the schedule viewer can be reviewed and demoed today. Clicking an assignment on the Gantt highlights its stop on the map and vice versa. Every label and finding on the board is translated (Dutch and English). This is the read-only viewer only — accepting/rejecting assignments, dragging them between crews, the unassigned-jobs tray, and the version/diff switcher are later tickets building on the same store. ## Related Issues Closes #21. Row **T-F** (`docs/12-implementation-plan.md`). Docs: `docs/06-ux.md` P02 (read-only parts). ## Type of Change - [x] New feature ## Breaking Changes None. ## Test Plan Tests green: Vitest covers the DTO → view-model mapping functions (including every triage-status/finding-severity combination) and both new components (including a mocked MapLibre so the map component is unit-testable without a real WebGL context). `npm run build`, `npm run lint`, and the i18n key-parity check are all green. Manually verified `/plan/dev/fixtures` renders the fixture plan on both the Gantt and the map. ## Checklist - [x] Conventional commit(s), one logical change per commit - [x] Tests added/updated and passing - [x] `docs/` updated for any behavioural change (ADR added if a prior decision was reversed) - [x] No hard deletes; no direct status updates outside `polaris.domain` - [x] No business logic in routers/components - [x] Migration included if the schema changed (one per PR max, reversible downgrade) — n/a, frontend only - [x] No new dependency without a reason in the commit body — none added ## Additional Context Two small touches outside this row's own paths, each minimal and explained in the commit body: - `/dev/fixtures` is mounted at `/plan/dev/fixtures` (via `features/plan/plan.routes.ts`, which this row owns) instead of a new top-level entry in `app.routes.ts`. That file is sealed, and its paired spec enforces exact parity between top-level routes and `core/nav.ts` — a dev-only route has no nav entry by design, so adding it there would have broken that invariant. - `frontend/src/styles.scss` now imports `maplibre-gl`'s own stylesheet globally, since a per-component `styleUrl` import would blow the (intentionally tight) `anyComponentStyle` budget. (The generated-OpenAPI-client lint exclusion this branch needed too was already added by #77 in the meantime — rebased onto `main`, nothing left to touch there.) Known, deliberate scope limits (left for the tickets that need them): assignment/map-stop labels are `Job #<id>` rather than a full "service — vehicle" label, since that needs the jobs/vehicles/catalogue DTOs T-A hasn't shipped yet — the mapping functions take an optional formatter so a richer label is a drop-in later. The "week strip" date switcher is a plain date input for now, not a full week view.
Adds the P02 plan board's read-only viewer: a MapLibre-independent Gantt
(shared/gantt) and a MapLibre GL map (shared/map), both dumb components
driven purely by ScheduleRow/ScheduleItem and GeoJSON view models, per the
frontend decoupling rules. features/plan gets the two read-only store
slices (with-versions, with-selection), the toScheduleRows/toScheduleItems/
toPlanMapViewModel DTO mappings with Vitest coverage, and the P02 page
wired against the generated (still-501-stub) plans/shifts/crews/sites API
client. features/dev-fixtures adds a /plan/dev/fixtures route that runs
checked-in fixture JSON through the exact same mapping functions, so the
viewer is demoable before the plan API is live. Findings render with
translated (nl/en) templates for every validator code.

/dev/fixtures lives at /plan/dev/fixtures rather than a new app.routes.ts
top-level entry: that file is sealed, and app.routes.spec.ts enforces
route/nav parity that a dev-only route deliberately breaks. Also excludes
the generated OpenAPI client (core/api/generated/, git-ignored) from
eslint, and adds maplibre-gl's stylesheet to the global styles bundle
(component style budgets are too small for it) — both small, necessary
touches outside this row's owned paths, called out here and in the PR.

Row T-F (docs/12-implementation-plan.md). Docs: 06-ux.md P02 (read-only
parts).

Closes #21

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LoNrSy7Reyp7evkfcdHeLX
patrick force-pushed feature/web-plan-board from 37d6048e9f to a58b6c9ed0 2026-09-11 13:23:22 +00:00 Compare
bart merged commit 82bddb26b8 into main 2026-09-11 13:29:42 +00:00
bart deleted branch feature/web-plan-board 2026-09-11 13:29:43 +00:00
Sign in to join this conversation.
No description provided.