feat(behandelportal): WP-65a beoordeling detail (read) + fix unreachable medewerker login
CI / changes (pull_request) Successful in 17s
CI / lint (pull_request) Failing after 54s
CI / frontend (pull_request) Successful in 2m38s
CI / storybook-a11y (pull_request) Failing after 3m28s
CI / backend (pull_request) Successful in 2m1s
CI / semgrep (pull_request) Successful in 1m9s
CI / e2e (pull_request) Successful in 2m55s
CI / api-client-drift (pull_request) Successful in 2m1s
CI / changes (pull_request) Successful in 17s
CI / lint (pull_request) Failing after 54s
CI / frontend (pull_request) Successful in 2m38s
CI / storybook-a11y (pull_request) Failing after 3m28s
CI / backend (pull_request) Successful in 2m1s
CI / semgrep (pull_request) Successful in 1m9s
CI / e2e (pull_request) Successful in 2m55s
CI / api-client-drift (pull_request) Successful in 2m1s
New GET /beoordeling/{id} shows one aanvraag's status, linked documents, and a
canBesluiten decision flag, gated by the same CanBeoordelen capability as the
werkvoorraad list. Reads through IZaakSource.ListCases rather than a new seam
method (WP-66 needs one anyway for the real write); owner BSN is masked.
Fixes a real gap found while wiring this up: the behandelportal's login was still
WP-61's copied citizen/BSN DigiD flow, so nothing ever sent X-Medewerker and the
werkvoorraad screen (WP-64) always denied in a real browser. A dev-only
medewerkerInterceptor (mirrors the existing ?role= stand-in as ?rollen=) fixes that.
WP-65's own Risks note authorized splitting read from write across sessions given
its size; this is the read half. The decision-recording mutation is next (65b).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -114,8 +114,8 @@ for its existing violations, so every WP ends green.
|
||||
| [WP-61](WP-61-behandelportal-bootstrap.md) | Bootstrap the behandelportal app | 11 · Behandelportal | done |
|
||||
| [WP-62](WP-62-medewerker-identity-authz.md) | Backend: medewerker caller identity + authz seam | 11 · Behandelportal | done |
|
||||
| [WP-63](WP-63-aanvraag-status-lifecycle.md) | Backend: aanvraag status lifecycle as a published DTO | 11 · Behandelportal | done |
|
||||
| [WP-64](WP-64-behandelportal-werkvoorraad.md) | Behandelportal: werkvoorraad (queue) screen | 11 · Behandelportal | todo |
|
||||
| [WP-65](WP-65-behandelportal-beoordeling.md) | Behandelportal: zaak detail + beoordeling (decision) screen | 11 · Behandelportal | todo |
|
||||
| [WP-64](WP-64-behandelportal-werkvoorraad.md) | Behandelportal: werkvoorraad (queue) screen | 11 · Behandelportal | done |
|
||||
| [WP-65](WP-65-behandelportal-beoordeling.md) | Behandelportal: zaak detail + beoordeling (decision) screen | 11 · Behandelportal | in progress (65a done) |
|
||||
| [WP-66](WP-66-behandelportal-openzaak-write.md) | Wire the decision into OpenZaak | 11 · Behandelportal | todo |
|
||||
| [WP-67](WP-67-monorepo-behandelportal.md) | Merge behandelportal into this repo as a monorepo | 11 · Behandelportal | done |
|
||||
|
||||
|
||||
@@ -75,6 +75,13 @@ atom. The stopgap `behandeling.page.ts`/`BehandelingPage` (WP-61's scaffold plac
|
||||
its own TODO said to replace it) is gone; `/dashboard` now loads `WerkvoorraadPage`
|
||||
directly, and the redundant `/behandeling` route (same placeholder, two paths) was dropped.
|
||||
|
||||
**Correction (found during WP-65):** this WP's Verification line ("manual: log in as a stub
|
||||
medewerker, see the queue populated") could not actually have passed — the behandelportal's
|
||||
login was still WP-61's copy-pasted citizen/BSN DigiD flow, nothing sent `X-Medewerker`, so
|
||||
`WerkvoorraadPage` always rendered its denial alert in a real browser. CI stayed green
|
||||
regardless (none of this WP's tests exercise the browser gate). Fixed in WP-65 with a
|
||||
dev-only `medewerkerInterceptor` — see that WP's Progress notes.
|
||||
|
||||
## Verification
|
||||
|
||||
`npm run ci` in the behandelportal app; `cd backend && dotnet test`; manual: log in as a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# WP-65 — Behandelportal: zaak detail + beoordeling (decision) screen
|
||||
|
||||
Status: todo
|
||||
Status: in progress (65a — detail read — done; 65b — decision write — not started)
|
||||
Phase: 11 — Behandelportal
|
||||
|
||||
## Why
|
||||
@@ -44,6 +44,72 @@ New mutation endpoint + command in `BigRegister.Api`; `behandeling/ui` detail pa
|
||||
3. Wire the werkvoorraad list (WP-64) to link into this detail page.
|
||||
4. Storybook stories + a11y for the new detail/decision UI.
|
||||
|
||||
## Progress notes (65a — done)
|
||||
|
||||
This WP's own Risks note offered an explicit escape hatch ("split detail-view (read) from
|
||||
decision-recording (write) into two sessions if it feels too big"). Taken: 65a below is done
|
||||
and committed; 65b (the mutation + decision machine + form) is the remaining half — see its
|
||||
own separate session notes once started. Don't relitigate the split; do relitigate nothing
|
||||
else pre-made here.
|
||||
|
||||
**Blocking gap found and fixed, not in this WP's original scope:** the behandelportal's
|
||||
login was still WP-61's copy-pasted citizen/BSN DigiD flow — nothing sent `X-Medewerker`, so
|
||||
`StubIdentityProvider` always resolved a zorgverlener, `GET /me` never contained
|
||||
`aanvraag:beoordelen`, and WP-64's `WerkvoorraadPage` always rendered its denial alert in a
|
||||
real browser. WP-64's own Verification line ("manual: log in as a stub medewerker, see the
|
||||
queue populated") could not have passed as written — CI stayed green because none of its
|
||||
tests exercise the browser gate. Fixed with a dev-only `medewerkerInterceptor`
|
||||
(`apps/behandelportal/src/app/auth/infrastructure/medewerker.interceptor.ts` +
|
||||
`medewerker.ts`), mirroring `@shared/infrastructure/role.ts`'s `?role=` idiom but app-local
|
||||
(`?rollen=`, sticky per tab, default `behandelaar`) — real employee-SSO login stays out of
|
||||
scope (ADR-0002 §3: the two apps' login flows are expected to diverge). Documented in
|
||||
`docs/reference/roles-and-access.md`.
|
||||
|
||||
**Backend (`GET /beoordeling/{id}`):** reads through `IZaakSource.ListCases` (no new seam
|
||||
method — one would force an `OpenZaakZaakSource` get-by-id + mapper, which is WP-66's
|
||||
surface), filters to the requested id, 404s a Concept or unknown id. Documents come from
|
||||
`DocumentStore.ByIds` (new method, mirrors `ByLocalIds`) — `DocumentStore` directly, not
|
||||
`IDocumentSource`, since that seam has no read method and its own header comment already
|
||||
says `DocumentStore` stays the record of truth for preview/download/audit regardless of
|
||||
config. The owner BSN is masked (`MaskTail`, same helper `/brief`'s BIG-nummer masking
|
||||
uses) — WP-64's queue row leaks the full BSN via the same `ApplicationSummaryDto.Owner`
|
||||
field; flagging as a follow-up, not fixed here or ssp's `/admin/cases` page moves too.
|
||||
The gate that was `Werkvoorraad(ctx, action)` is now `Beoordelen(ctx, resource, action)` —
|
||||
one gate for every behandelaar endpoint, `resource` feeding the audit row; the one existing
|
||||
`/werkvoorraad` call site was updated to pass `"werkvoorraad"`.
|
||||
|
||||
**Decision-readiness (`BeoordelingDecisionsDto.canBesluiten`) ships now, not deferred to
|
||||
65b:** `BeoordelingRules.CanDecide(AanvraagStatusTag)` only inspects the aanvraag's current
|
||||
*computed* status tag (`Ingediend`/`InBehandeling`/`MeerInfoGevraagd` → decidable;
|
||||
`Goedgekeurd`/`Afgewezen` → not) — no persisted "was a decision recorded" field exists yet,
|
||||
so this pure rule needed nothing from 65b's eventual migration to be correct today. 65b adds
|
||||
the mutation, the `Besluit` enum, and the transition-legality check that reuses this same
|
||||
function.
|
||||
|
||||
**FE:** `BeoordelingStatus` (`domain/beoordeling.ts`) is the five-tag union (all of ssp's
|
||||
`AanvraagStatus` minus `Concept`, which 404s) — wider than WP-64's `WerkvoorraadStatus` (two
|
||||
tags). `TYPE_LABELS`/`statusLabel` moved from `werkvoorraad-item-view.ts` into a new
|
||||
`domain/beoordeling-view.ts` (the file owning the wider union) and are re-exported from the
|
||||
old location so no consumer or existing spec needed to change. Detail page composes
|
||||
`<app-data-block>`/`<app-data-row>` (mirrors ssp's `aanvraag-detail.page.ts`) plus one new
|
||||
organism, `beoordeling-documenten` (plain links to the existing, pre-existing-unauthenticated
|
||||
`/uploads/{id}/content` endpoint — not `ApplicationLinkComponent`, whose `to` is a
|
||||
`routerLink`, not an external href). The werkvoorraad list's rows now link to
|
||||
`/aanvraag/:id`, gated by the same `aanvraag:beoordelen` capability. No `MaskedValueComponent`
|
||||
reveal affordance — this screen never reveals the owner, so pulling in that atom's unused
|
||||
reveal machinery would be speculative.
|
||||
|
||||
**Deferred to 65b, deliberately:** the mutation endpoint, the `Aanvraag.BesluitStatus`
|
||||
column + EF migration, `Mappers.ToStatusDto`'s "a recorded decision wins" branch, the
|
||||
`besluit.machine.ts` + `besluit-form` UI, and re-running the werkvoorraad/ssp-dashboard
|
||||
end-to-end smoke this WP's acceptance criteria actually asks for (a decision advancing
|
||||
status, illegal transitions rejected). None of WP-65's acceptance criteria are checked off
|
||||
yet — 65a is infrastructure the decision screen needs, not a slice of the AC itself.
|
||||
|
||||
`npm run ci` green (lint, dep:check ×2, both apps' localized builds, both Storybook builds,
|
||||
test, backend test — 197/197 including this WP's 9 new tests). Only the api-client-drift
|
||||
step shows the expected pre-commit diff (this WP's own uncommitted endpoint).
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] A medewerker can view one aanvraag's detail and record a decision that advances its
|
||||
|
||||
Reference in New Issue
Block a user