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
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
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -248,3 +248,30 @@ curl -fsS "http://localhost:8140/openbaar/register?q=$ref" | jq
|
|||||||
|
|
||||||
> The openbaar register's "Referentie" column and its search now use this reference — the exact value
|
> The openbaar register's "Referentie" column and its search now use this reference — the exact value
|
||||||
> the citizen saw on submit. Asserted end-to-end by the Playwright happy path.
|
> the citizen saw on submit. Asserted end-to-end by the Playwright happy path.
|
||||||
|
|
||||||
|
## S-12 — Behandel portal: werkbak + beoordeling (#13, ADR-0013)
|
||||||
|
|
||||||
|
A behandelaar now works submitted registrations in a real portal instead of the temporary admin
|
||||||
|
endpoint. After a citizen submits (as above), the workflow parks the registration at the Flowable
|
||||||
|
`Beoordelen` user task, and it shows up in the **werkbak**. The behandelaar logs in against the
|
||||||
|
Keycloak `medewerker` realm and decides — **goedkeuren** (→ INGESCHREVEN via the ACL, per ADR-0011)
|
||||||
|
or **afwijzen** — which also completes the Beoordelen task so the process advances.
|
||||||
|
|
||||||
|
```text
|
||||||
|
# 1. Open the behandel portal and log in as a behandelaar (medewerker realm):
|
||||||
|
# http://localhost:8142/ → merel-behandelaar / test123
|
||||||
|
#
|
||||||
|
# 2. The werkbak lists the registrations awaiting beoordeling (referentie / bsn / status).
|
||||||
|
# Find the reference from the submit confirmation and click "Goedkeuren" on that row.
|
||||||
|
#
|
||||||
|
# 3. The row drops off the werkbak (its Beoordelen task is completed) and the openbaar register
|
||||||
|
# (http://localhost:8141/) now shows that reference as INGESCHREVEN.
|
||||||
|
```
|
||||||
|
|
||||||
|
**The path:** behandel portal → BFF `POST /behandel/registrations/{id}/decide` (behandelaar policy,
|
||||||
|
`medewerker` realm) → domain applies the decision + completes the Flowable `Beoordelen` task →
|
||||||
|
ACL → NRC → event-subscriber → projection → openbaar register shows INGESCHREVEN.
|
||||||
|
|
||||||
|
> The full round-trip — DigiD submit → public INGEDIEND → behandelaar goedkeurt in the werkbak →
|
||||||
|
> public INGESCHREVEN — is the Playwright happy path (`tests/e2e/registration.spec.ts`), which now
|
||||||
|
> drives the behandel portal in place of the old admin endpoint.
|
||||||
|
|||||||
@@ -117,3 +117,37 @@ with the submit form (S-08c, #67); any deviation from NL DS will be recorded her
|
|||||||
(id + status); `bsn`/`naam` never leave the BFF. The e2e asserts the bsn never renders.
|
(id + status); `bsn`/`naam` never leave the BFF. The e2e asserts the bsn never renders.
|
||||||
- **Loads on open, filters on search.** `RegisterPage` fetches the full register on construction and
|
- **Loads on open, filters on search.** `RegisterPage` fetches the full register on construction and
|
||||||
re-queries `/openbaar/register?q=` on search — no client-side filtering, the BFF owns the query.
|
re-queries `/openbaar/register?q=` on search — no client-side filtering, the BFF owns the query.
|
||||||
|
|
||||||
|
## Behandel portal (S-12, #13)
|
||||||
|
|
||||||
|
The staff portal where a behandelaar works the **werkbak** (registrations awaiting beoordeling) and
|
||||||
|
decides each — goedkeuren or afwijzen. `apps/behandel` mirrors `apps/self-service`; the net-new
|
||||||
|
frontend work is the medewerker realm auth and the werkbak/decide page. Wiring rationale is in
|
||||||
|
**ADR-0013**; this entry records the frontend-specific choices.
|
||||||
|
|
||||||
|
- **Medewerker realm auth, reusing `libs/auth`.** Staff authenticate against the Keycloak
|
||||||
|
`medewerker` realm (public client `big-portal`), not `digid`. Rather than fork the auth lib, the
|
||||||
|
abstract `AuthService` grew a **`roles`/`hasRole` surface** (empty for realms without roles, e.g.
|
||||||
|
`digid`), and a parallel **`MedewerkerAuthService` + `provideMedewerkerAuth`** were added — same
|
||||||
|
auth-code + PKCE config, bound to the medewerker realm, reading the nested `realm_access.roles`
|
||||||
|
claim. The library's own `authInterceptor` attaches the token to the relative `/behandel/` calls
|
||||||
|
(secure route), exactly as self-service does for `/self-service/`.
|
||||||
|
- **Roles reach the frontend via a realm mapper.** Keycloak emits realm roles in the access token by
|
||||||
|
default but not the ID token/userinfo the SPA reads, so the medewerker `big-portal` client gets a
|
||||||
|
**realm-roles protocol mapper** (`realm_access.roles`, added to id + userinfo tokens). The
|
||||||
|
**BFF remains the security boundary** (`behandelaar` policy, 401/403 on `/behandel/*`, ADR-0013);
|
||||||
|
the frontend role signal is for display/UX, and the werkbak page surfaces a load failure (e.g. a
|
||||||
|
403 for a non-behandelaar) rather than swallowing it.
|
||||||
|
- **Same-origin via nginx, like the other portals.** The compose `behandel` image serves the built
|
||||||
|
app and reverse-proxies `/behandel` to the BFF (relative calls, no CORS). Served on `:8142`,
|
||||||
|
health-checked over IPv4 (`127.0.0.1`), depends on Keycloak for the medewerker realm.
|
||||||
|
- **Werkbak = decide-and-refresh.** `WerkbakPage` loads `GET /behandel/werkbak` on open and renders a
|
||||||
|
row per registration (referentie/bsn/status). Goedkeuren/afwijzen `POST /behandel/registrations/
|
||||||
|
{id}/decide` and then reload the werkbak, so the handled item drops off (its Flowable `Beoordelen`
|
||||||
|
task is completed). Per-row decide buttons carry an `aria-label` including the reference, so the
|
||||||
|
e2e (and screen readers) can target a specific registration in a shared werkbak.
|
||||||
|
- **Testing.** Component tests use `@testing-library/angular` with `BffApiV1Service`/`AuthService`
|
||||||
|
mocked and the axe WCAG 2.1 AA check; an `app.config.spec` drives the real interceptor + api-client
|
||||||
|
to assert the medewerker token attaches to `/behandel/*` (and not to the anonymous openbaar call).
|
||||||
|
The full DigiD-submit → behandel-decide → public INGESCHREVEN round-trip is the Playwright happy
|
||||||
|
path.
|
||||||
|
|||||||
Reference in New Issue
Block a user