All checks were successful
## 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`).
- [ ] 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.
Reviewed-on: #87
65 lines
2.2 KiB
HTML
65 lines
2.2 KiB
HTML
<main utrecht-document class="utrecht-theme">
|
|
<utrecht-article>
|
|
<utrecht-heading-1>Werkbak</utrecht-heading-1>
|
|
<p utrecht-paragraph>
|
|
Registraties die wachten op beoordeling. Keur elke registratie goed of wijs deze af.
|
|
</p>
|
|
|
|
@if (loading()) {
|
|
<p utrecht-paragraph role="status">Bezig met laden…</p>
|
|
} @else if (failed()) {
|
|
<p utrecht-paragraph role="alert">
|
|
Kon de werkbak niet laden. Controleer of je als behandelaar bent ingelogd en probeer het
|
|
opnieuw.
|
|
</p>
|
|
} @else if (loaded() && items().length === 0) {
|
|
<p utrecht-paragraph role="status">De werkbak is leeg.</p>
|
|
} @else if (items().length > 0) {
|
|
<table utrecht-table>
|
|
<caption>
|
|
Registraties in behandeling
|
|
</caption>
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Referentie</th>
|
|
<th scope="col">BSN</th>
|
|
<th scope="col">Status</th>
|
|
<th scope="col">Actie</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for (item of items(); track item.registrationId) {
|
|
<tr>
|
|
<td>{{ item.registrationId }}</td>
|
|
<td>{{ item.bsn }}</td>
|
|
<td>{{ item.status }}</td>
|
|
<td>
|
|
<button
|
|
utrecht-button
|
|
appearance="primary-action-button"
|
|
type="button"
|
|
[attr.aria-label]="'Goedkeuren ' + item.registrationId"
|
|
[disabled]="deciding() === item.registrationId"
|
|
(click)="decide(item.registrationId, 'goedkeuren')"
|
|
>
|
|
Goedkeuren
|
|
</button>
|
|
<button
|
|
utrecht-button
|
|
appearance="secondary-action-button"
|
|
type="button"
|
|
[attr.aria-label]="'Afwijzen ' + item.registrationId"
|
|
[disabled]="deciding() === item.registrationId"
|
|
(click)="decide(item.registrationId, 'afwijzen')"
|
|
>
|
|
Afwijzen
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
</table>
|
|
}
|
|
</utrecht-article>
|
|
</main>
|