feat(web): master-data pages for jobs, vehicles, sites, crews and catalogue (P05-P08, P11) #83

Merged
bart merged 6 commits from feature/web-master-data into main 2026-09-11 13:28:54 +00:00
Collaborator

Description

This is the frontend half of T-A: planners and admins can now manage jobs, vehicles, sites, crews/people/shifts and the catalogue directly against the live API instead of seeing "not built yet" placeholders.

  • Jobs: a filterable, paginated table with bulk cancel and bulk priority change, a new/edit form, and a detail page showing candidate opportunities (with confidence), plus cancel/hold/release/mark-urgent/add-manual-window actions.
  • Vehicles: a list (external id, label, active, sites count, last feed, stale flag, open jobs) and a detail page with a map of the vehicle's sites and its reliability score per site/weekday. The presence heatmap panel is intentionally left out here — it needs data from the ingest track, which hasn't landed yet.
  • Sites: a map + table of sites, a suitability editor per site (service type x allowed/blocked/unknown), a bulk suitability rule, and an exclude-site toggle.
  • Crews / People / Shifts: crews with a click-to-place home-base map and a member roster, a people directory, and a per-crew weekly shift calendar with new/cancel shift and an apply-template action.
  • Catalogue: admin CRUD for service types, skills and reason codes.

Every string is translated (nl/en), and each page talks to the API surface the backend half of this ticket implements.

Contributes to #16 (T-A, docs/12-implementation-plan.md). No PR has closed #16 yet, so this one doesn't either — the backend half (feature/api-master-data) is the other contributor.

Also filed #79 (seam request, see Additional Context).

Type of Change

  • New feature
  • Bug fix
  • Refactor / cleanup (no behaviour change)
  • Documentation
  • Chore / build / CI
  • Breaking change

Breaking Changes

None.

Test Plan

ng build (dev and production configurations), ng test --watch=false (67 tests green) and the i18n key-parity check all pass. frontend/src/app/features/** lints clean on its own (see Additional Context for the one pre-existing gap in ng lint overall). No live backend/compose stack was available in this environment to click through the pages end to end; verification here is build/type/test/lint green plus a careful read of what each endpoint actually returns against the committed openapi.json.

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)
  • No new dependency without a reason in the commit body

Additional Context

Test coverage: this PR does not add new unit/component tests for the new pages/stores — given the size of the ticket, the time budget went into covering all five page areas against the real API surface rather than test scaffolding. The existing 67 tests (shared/core, from earlier tickets) stay green. Happy to follow up with tests if that's wanted before merge.

Known gaps, all called out in code comments and flagged for follow-up:

  • GET /jobs has no filter query parameters in the currently committed openapi.json (PH1.7 stub); the jobs list sends them anyway using the names docs/09-api.md documents, so filtering starts working as soon as the backend half implements it — no frontend change needed then.
  • SiteSuitabilityBulkRequest has no site-selector field yet (the "for all Thuis sites" example in docs/06-ux.md implies one); the bulk-rule dialog sends a site_type filter as an extra property, inert until the backend adds support.
  • Catalogue service types and crews have no skills field/endpoint in the current API surface, so skill requirements aren't editable from either page yet.
  • Vehicle ingest history and the presence heatmap are out of scope per the ticket brief (need the ingest/projector track).
  • Shift cancel always shows the "triggers an event re-plan" warning rather than only when the shift has confirmed assignments — no endpoint currently exposes that state.

Seam request filed: #79frontend/eslint.config.js has no ignore for the git-ignored generated API client, so npm run api:gen && ng lint reports ~210 errors inside the generated file itself once backend/openapi.json has a real surface (as it now does). Verified this is 100% confined to the generated file, not application code, by linting frontend/src/app/features/** on its own (clean). Not fixed in this PR since eslint.config.js predates this session and isn't in this ticket's owned paths.

Backend availability: the backend half of this ticket (feature/api-master-data) had not merged as of this PR — built against the already-committed, full PH1.7 backend/openapi.json on main (issue #74), regenerating the typed client with npm run api:gen. Once the backend half fills in the real domain logic behind the currently-stubbed routers, no frontend regeneration should be needed unless a DTO shape changes.


🤖 Generated with Claude Code

https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM

## Description This is the frontend half of T-A: planners and admins can now manage jobs, vehicles, sites, crews/people/shifts and the catalogue directly against the live API instead of seeing "not built yet" placeholders. - **Jobs**: a filterable, paginated table with bulk cancel and bulk priority change, a new/edit form, and a detail page showing candidate opportunities (with confidence), plus cancel/hold/release/mark-urgent/add-manual-window actions. - **Vehicles**: a list (external id, label, active, sites count, last feed, stale flag, open jobs) and a detail page with a map of the vehicle's sites and its reliability score per site/weekday. The presence heatmap panel is intentionally left out here — it needs data from the ingest track, which hasn't landed yet. - **Sites**: a map + table of sites, a suitability editor per site (service type x allowed/blocked/unknown), a bulk suitability rule, and an exclude-site toggle. - **Crews / People / Shifts**: crews with a click-to-place home-base map and a member roster, a people directory, and a per-crew weekly shift calendar with new/cancel shift and an apply-template action. - **Catalogue**: admin CRUD for service types, skills and reason codes. Every string is translated (nl/en), and each page talks to the API surface the backend half of this ticket implements. ## Related Issues Contributes to #16 (T-A, docs/12-implementation-plan.md). No PR has closed #16 yet, so this one doesn't either — the backend half (`feature/api-master-data`) is the other contributor. Also filed #79 (seam request, see Additional Context). ## Type of Change - [x] New feature - [ ] Bug fix - [ ] Refactor / cleanup (no behaviour change) - [ ] Documentation - [ ] Chore / build / CI - [ ] Breaking change ## Breaking Changes None. ## Test Plan `ng build` (dev and production configurations), `ng test --watch=false` (67 tests green) and the i18n key-parity check all pass. `frontend/src/app/features/**` lints clean on its own (see Additional Context for the one pre-existing gap in `ng lint` overall). No live backend/compose stack was available in this environment to click through the pages end to end; verification here is build/type/test/lint green plus a careful read of what each endpoint actually returns against the committed `openapi.json`. ## Checklist - [x] Conventional commit(s), one logical change per commit - [ ] 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) - [x] No new dependency without a reason in the commit body ## Additional Context **Test coverage**: this PR does not add new unit/component tests for the new pages/stores — given the size of the ticket, the time budget went into covering all five page areas against the real API surface rather than test scaffolding. The existing 67 tests (shared/core, from earlier tickets) stay green. Happy to follow up with tests if that's wanted before merge. **Known gaps, all called out in code comments and flagged for follow-up**: - `GET /jobs` has no filter query parameters in the currently committed `openapi.json` (PH1.7 stub); the jobs list sends them anyway using the names docs/09-api.md documents, so filtering starts working as soon as the backend half implements it — no frontend change needed then. - `SiteSuitabilityBulkRequest` has no site-selector field yet (the "for all Thuis sites" example in docs/06-ux.md implies one); the bulk-rule dialog sends a `site_type` filter as an extra property, inert until the backend adds support. - Catalogue service types and crews have no `skills` field/endpoint in the current API surface, so skill requirements aren't editable from either page yet. - Vehicle ingest history and the presence heatmap are out of scope per the ticket brief (need the ingest/projector track). - Shift cancel always shows the "triggers an event re-plan" warning rather than only when the shift has confirmed assignments — no endpoint currently exposes that state. **Seam request filed**: #79 — `frontend/eslint.config.js` has no ignore for the git-ignored generated API client, so `npm run api:gen && ng lint` reports ~210 errors inside the generated file itself once `backend/openapi.json` has a real surface (as it now does). Verified this is 100% confined to the generated file, not application code, by linting `frontend/src/app/features/**` on its own (clean). Not fixed in this PR since `eslint.config.js` predates this session and isn't in this ticket's owned paths. **Backend availability**: the backend half of this ticket (`feature/api-master-data`) had not merged as of this PR — built against the already-committed, full PH1.7 `backend/openapi.json` on `main` (issue #74), regenerating the typed client with `npm run api:gen`. Once the backend half fills in the real domain logic behind the currently-stubbed routers, no frontend regeneration should be needed unless a DTO shape changes. --- 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
Service types, skills and reason codes now manage via the live API using
the generic admin CRUD table+form (shared/admin-crud, PH1.5), one
signalStoreFeature slice per entity composed into catalogue.store.ts.

Live counts of jobs/crews affected by an edit are not implemented: the
jobs/crews list endpoints don't yet support filtering by catalogue value,
so there's no cheap way to compute them from the frontend. A static impact
warning stands in for now; flagged on issue #16 for a follow-up once those
filters exist.

Refs #16 (T-A, docs/12-implementation-plan.md)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
Table with status/vehicle/service-type filters, bulk cancel and bulk
priority change; new/edit form; detail page with candidate opportunities
(confidence via the shared confidence pipe/tag), cancel/hold/release/mark
urgent/add-manual-window actions. Store composed from four slices
(list, detail, mutations, lookups).

Assignment history and feedback history render as an explicit
"not available yet" panel: the current API surface has no per-job history
endpoint (docs/09-api.md lists none), so no shape is fabricated for it.
GET /jobs also has no query parameters in the committed openapi.json yet
(PH1.7 stub) — filters are sent anyway against the documented names from
docs/09-api.md, forward-compatible with the sibling backend session's
implementation.

Refs #16 (T-A, docs/12-implementation-plan.md)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
List (external id, label, active, sites count, last feed, stale flag, open
jobs), detail (MapLibre map of site polygons/centroids, reliability score
per site/weekday, admin create/deactivate). Sites/open-jobs counts are
fetched per row after the page loads (no aggregate endpoint exists yet) --
fine at a page size of 20 for an internal admin table, flagged as a
follow-up if VehicleRead grows those columns server-side.

Presence heatmap panel intentionally skipped per the ticket brief (needs
ingest/projector data from T-B, not built yet); ingest history likewise
not rendered (same T-B dependency, no endpoint scoped to a vehicle in the
current API). Both show as explicit "not built yet" panels rather than
being silently missing.

Site geometry (polygon/centroid) is typed as a plain string in the current
openapi.json even though docs/09-api.md says geometry is GeoJSON in/out --
the map parses either GeoJSON or WKT defensively until that's settled.

Refs #16 (T-A, docs/12-implementation-plan.md)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
Map (MapLibre polygons) above a server-paginated table (vehicle, type,
area, valid_from/to, reliability score averaged across weekdays); per-site
suitability editor (service type x allowed/blocked/unknown) and a bulk
rule dialog; exclude-site toggle with a required reason.

SiteSuitabilityBulkRequest in the committed openapi.json has no site
selector field yet (docs/06-ux.md's "for all Thuis sites" example implies
one) -- the bulk dialog collects a site-type filter and sends it as an
extra property alongside the documented body; harmless against Pydantic's
default extra-field handling today, starts working once the backend adds
real support. Flagged on issue #16.

Refs #16 (T-A, docs/12-implementation-plan.md)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
Crews tab: table, create/edit form with a click-to-pick home base map,
member roster panel (add/end membership with validity dates). People tab
reuses shared/admin-crud (PersonRead is plain, soft-deletable master data).
Shifts tab: crew selector, week navigation, day columns of shift cards,
new/cancel shift, apply-template action.

Crew-level skills have no endpoint in the current API surface (no
crew_skill DTO, CrewRead carries no skills field) -- same class of gap as
service-type required-skills in the catalogue feature -- so the form does
not render them; flagged on issue #16.

Shift cancel always shows the "triggers an event re-plan" warning
(docs/06-ux.md) rather than only when the shift has confirmed assignments:
no endpoint currently exposes that state to the frontend, so this is the
conservative default until one does.

Refs #16 (T-A, docs/12-implementation-plan.md)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
- Replace literal placeholder strings with Transloco keys, associate
  labels with their controls, and avoid the click-handler-on-a-plain-span
  a11y warning in jobs bulk selection -- all polaris/no-template-literal-text
  and @angular-eslint/template accessibility rule violations.
- Fix an unused-destructure lint error in job-detail.page.ts by building
  the JobUpdate payload from an explicit allow-list instead of
  discarding vehicle_id/service_type_id via rest-spread.
- Drop the full maplibre-gl.css import (~80KB) from the three map
  components: it exceeded the Angular CLI's per-component style budget on
  a production build. Replaced with the handful of hand-copied
  positioning rules (.maplibregl-map/-canvas/-marker) these
  controls-free, popup-free maps actually need, applied unscoped via
  ViewEncapsulation.None since MapLibre's canvas/marker DOM is created
  outside Angular's template and never gets the emulated-encapsulation
  attribute.

Confirmed frontend/src/app/features/** lints clean on its own; the
remaining `ng lint` failures are entirely inside the git-ignored
generated OpenAPI client (eslint.config.js has no ignore for it), a
pre-existing gap unrelated to this ticket -- filed as issue #79.

Refs #16 (T-A, docs/12-implementation-plan.md)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013YioTVKBPoE6thZqbnTtnM
bart merged commit da2f94cb9d into main 2026-09-11 13:28:54 +00:00
bart deleted branch feature/web-master-data 2026-09-11 13:28:55 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
patrick/Polaris!83
No description provided.