feat(behandel): behandel-portal — werkbak + beoordeling (closes #13) #87

Merged
not merged 8 commits from feat/13-behandel-portal into main 2026-07-16 08:31:58 +00:00
Contributor

What & why

Finishes S-12 · Behandel-portal — werkbak + beoordeling. The backend sub-slices (S-12a/b/c-1/c-2) were merged, but the slice's stated outcome — a behandel portal with medewerker login, a werkbak, and decide — had no frontend. This adds it.

  • libs/auth: MedewerkerAuthService + provideMedewerkerAuth (Keycloak medewerker realm), a roles/hasRole surface on the shared AuthService, and a realm-roles protocol mapper so the SPA can read behandelaar/teamlead from the token. The BFF remains the security boundary (ADR-0013).
  • apps/behandel: a new Nx Angular app mirroring self-service — medewerker OIDC login and a werkbak page listing registrations awaiting beoordeling (GET /behandel/werkbak) with per-row Goedkeuren/Afwijzen actions (POST /behandel/registrations/{id}/decide) that refresh the list. NL DS/Utrecht, standalone + signals.
  • e2e: the walking-skeleton happy path now approves through the real portal (behandelaar logs in, finds the row by reference, clicks Goedkeuren) instead of the temporary admin endpoint.
  • infra/docs: behandel service in compose (:8142, depends on Keycloak); added to the smoke WAIT_SVCS + CI log dump; frontend-decisions.md and demo-script.md updated.

Closes #13

Definition of Done

  • Linked Gitea issue (above).
  • Failing test committed before the implementation.
  • Implementation makes the test pass; refactor commit if structure improved.
  • Conventional Commits referencing the issue (refs #13).
  • CI green — all Gitea Actions jobs.
  • docker compose up from a fresh clone reaches green health checks within 3 minutes. (behandel image + container verified locally; full stack gated in CI.)
  • Docs updated if behaviour, contracts, or operations changed.
  • ADR added — ADR-0013 (merged with the backend sub-slices) already covers the wiring; no new decision here.
  • Demo note in docs/demo-script.md.

Notes for reviewers

  • Verified locally: auth + behandel + all frontend projects pass lint & unit tests (incl. axe WCAG 2.1 AA); production build green; the behandel Docker image builds and serves with the correct baked medewerker config + SPA fallback.
  • The full compose-up smoke, e2e, and mutation are CI-gated (known local full-stack verify limits).
  • Follow-ups (not in scope): the WerkbakItem contract has no citizen name (werkbak shows the BSN) — adding one is a BFF+domain contract change; and the domain's temporary admin approve endpoint is now unused by the e2e and could be removed.
## What & why Finishes **S-12 · Behandel-portal — werkbak + beoordeling**. The backend sub-slices (S-12a/b/c-1/c-2) were merged, but the slice's stated outcome — a behandel *portal* with medewerker login, a werkbak, and decide — had no frontend. This adds it. - **`libs/auth`**: `MedewerkerAuthService` + `provideMedewerkerAuth` (Keycloak `medewerker` realm), a `roles`/`hasRole` surface on the shared `AuthService`, and a realm-roles protocol mapper so the SPA can read `behandelaar`/`teamlead` from the token. The BFF remains the security boundary (ADR-0013). - **`apps/behandel`**: a new Nx Angular app mirroring self-service — medewerker OIDC login and a **werkbak** page listing registrations awaiting beoordeling (`GET /behandel/werkbak`) with per-row **Goedkeuren/Afwijzen** actions (`POST /behandel/registrations/{id}/decide`) that refresh the list. NL DS/Utrecht, standalone + signals. - **e2e**: the walking-skeleton happy path now approves through the real portal (behandelaar logs in, finds the row by reference, clicks Goedkeuren) instead of the temporary admin endpoint. - **infra/docs**: behandel service in compose (`:8142`, depends on Keycloak); added to the smoke `WAIT_SVCS` + CI log dump; `frontend-decisions.md` and `demo-script.md` updated. Closes #13 ## Definition of Done - [x] Linked Gitea issue (above). - [x] Failing test committed before the implementation. - [x] Implementation makes the test pass; refactor commit if structure improved. - [x] Conventional Commits referencing the issue (`refs #13`). - [x] CI green — all Gitea Actions jobs. - [x] `docker compose up` from a fresh clone reaches green health checks within 3 minutes. *(behandel image + container verified locally; full stack gated in CI.)* - [x] Docs updated if behaviour, contracts, or operations changed. - [x] ADR added — ADR-0013 (merged with the backend sub-slices) already covers the wiring; no new decision here. - [x] Demo note in `docs/demo-script.md`. ## Notes for reviewers - Verified locally: auth + behandel + all frontend projects pass lint & unit tests (incl. axe WCAG 2.1 AA); production build green; the behandel Docker image builds and serves with the correct baked `medewerker` config + SPA fallback. - The full compose-up smoke, e2e, and mutation are CI-gated (known local full-stack verify limits). - **Follow-ups (not in scope):** the `WerkbakItem` contract has no citizen name (werkbak shows the BSN) — adding one is a BFF+domain contract change; and the domain's temporary admin `approve` endpoint is now unused by the e2e and could be removed.
not added 7 commits 2026-07-16 07:24:22 +00:00
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The behandel-portal authenticates staff against the Keycloak `medewerker` realm. Add

MedewerkerAuthService (reads nested realm_access.roles), provideMedewerkerAuth, a roles/hasRole

surface on the shared AuthService, and a realm-roles protocol mapper so the frontend can read

the behandelaar/teamlead roles from the token. Per ADR-0013 the BFF remains the security boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drives the behandel-portal frontend: the werkbak lists registrations awaiting beoordeling, a

decision (goedkeuren/afwijzen) posts to the BFF and refreshes, an empty werkbak and a load

failure are surfaced, the page is WCAG 2.1 AA clean, and the medewerker token attaches to the

relative /behandel/ calls (and not to the anonymous openbaar register).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Scaffold apps/behandel (mirrors self-service): medewerker-realm OIDC login, the werkbak page

listing registrations awaiting beoordeling (GET /behandel/werkbak) with goedkeuren/afwijzen

actions (POST /behandel/registrations/{id}/decide) that refresh the list, plus Dockerfile and

same-origin nginx reverse proxy for /behandel/ (ADR-0013; S-12).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The walking-skeleton happy path now drives the real S-12 flow: after submit, a behandelaar logs

in to the behandel portal (medewerker realm), finds the registration in the werkbak by its

reference, and clicks Goedkeuren — replacing the temporary domain admin endpoint. Treat the

behandel origin as secure too, for its PKCE login.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the behandel service (:8142, nginx → BFF, depends on Keycloak for the medewerker realm) to

docker-compose, add it to the smoke's WAIT_SVCS and the CI failure-log dump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
docs(behandel): frontend decision log + demo-script for the behandel portal (refs #13)
Some checks failed
CI / lint (pull_request) Successful in 1m20s
CI / build (pull_request) Successful in 59s
CI / unit (pull_request) Successful in 1m12s
CI / frontend (pull_request) Successful in 6m36s
CI / mutation (pull_request) Successful in 5m10s
CI / verify-stack (pull_request) Failing after 5m27s
2c88cb0db0
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not added the type:slicearea:portal-behandelarea:workflowarea:bff labels 2026-07-16 07:24:36 +00:00
not added 1 commit 2026-07-16 08:12:53 +00:00
fix(e2e): await the decide POST before leaving the werkbak page (refs #13)
All checks were successful
CI / lint (pull_request) Successful in 1m13s
CI / build (pull_request) Successful in 54s
CI / unit (pull_request) Successful in 1m4s
CI / frontend (pull_request) Successful in 2m31s
CI / mutation (pull_request) Successful in 4m57s
CI / verify-stack (pull_request) Successful in 6m55s
52ba2fc242
The happy path clicked Goedkeuren and immediately navigated to the openbaar register, which

cancelled the in-flight POST /behandel/registrations/{id}/decide (nginx 499) — the decision

never reached the domain, so the registration stayed INGEDIEND and the final INGESCHREVEN

assertion failed. Wait for the decide response (204) before navigating away.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
not merged commit 3abf8f7ccf into main 2026-07-16 08:31:58 +00:00
Sign in to join this conversation.