Behandelportal: monorepo merge + WP-64..67 backoffice arc (OpenZaak write closes it out) #1

Merged
eho merged 8 commits from monorepo-behandelportal into main 2026-08-04 12:56:34 +00:00
Owner

Summary

This PR now covers the full Phase 11 (Behandelportal) arc, not just the original monorepo merge — it closes out the entire showcase-hardening backlog (WP-01 through WP-67, all done).

  • WP-67: merge the behandelportal sibling repo into this repo as a monorepo (apps/ssp + apps/behandelportal sharing libs/shared + libs/beheer + one CI/backend).
  • WP-64: behandelportal's first real screen — the werkvoorraad (queue) of aanvragen needing treatment, gated by the medewerker aanvraag:beoordelen capability (not the admin role).
  • WP-65a: beoordeling detail (read) screen + a fix for an unreachable medewerker login path.
  • WP-65b: beoordeling besluit — the behandelaar's decision write (Goedkeuren/Afwijzen/MeerInfoOpvragen), advancing the WP-63 status lifecycle locally.
  • WP-66: wires that decision into OpenZaak — IZaakSource.RecordBesluit (mirroring WP-50's CreateZaak write pattern) POSTs a new Statussen entry to the zaak when Zgw:Enabled=true; a failure is flagged via RecordZgwDivergence, the same way submit's create-zaak/document writes are (closing WP-60's "second write pair" gap). LocalZaakSource stays a no-op, so the behandelportal is unchanged when ZGW is off.
  • Two housekeeping commits: a CI fix (test-storybook config-dir + missing behandelportal a11y coverage) and a docs fix (stale WP-65 status in the backlog README).

Test plan

  • npm run ci green (lint, dep:check ×2, both apps' localized builds, both Storybook builds, tests, backend tests, api-client drift)
  • cd backend && dotnet test — 204/204 passing
  • New unit tests for OpenZaakZaakSource.RecordBesluit against a stub HttpMessageHandler (posts the last statustype with besluit+toelichting; no-ops when the aanvraag has no zaak; throws on an unconfigured zaaktype)
  • Manual: npm start (ssp :4200) and npm run start:behandelportal (:4201) against the shared backend, logged in on both
  • Residual risk (documented in WP-66): no live-harness (OpenZaakIntegrationTests) round-trip added for the besluit write yet — only stub-based unit tests, same gap WP-50 originally shipped with before WP-54's harness caught a real bug (Content-Crs header). Worth closing before relying on this in a real deployment.
## Summary This PR now covers the full Phase 11 (Behandelportal) arc, not just the original monorepo merge — it closes out the entire showcase-hardening backlog (WP-01 through WP-67, all `done`). - **WP-67**: merge the behandelportal sibling repo into this repo as a monorepo (`apps/ssp` + `apps/behandelportal` sharing `libs/shared` + `libs/beheer` + one CI/backend). - **WP-64**: behandelportal's first real screen — the werkvoorraad (queue) of aanvragen needing treatment, gated by the medewerker `aanvraag:beoordelen` capability (not the admin role). - **WP-65a**: beoordeling detail (read) screen + a fix for an unreachable medewerker login path. - **WP-65b**: beoordeling besluit — the behandelaar's decision write (Goedkeuren/Afwijzen/MeerInfoOpvragen), advancing the WP-63 status lifecycle locally. - **WP-66**: wires that decision into OpenZaak — `IZaakSource.RecordBesluit` (mirroring WP-50's `CreateZaak` write pattern) POSTs a new Statussen entry to the zaak when `Zgw:Enabled=true`; a failure is flagged via `RecordZgwDivergence`, the same way submit's create-zaak/document writes are (closing WP-60's "second write pair" gap). `LocalZaakSource` stays a no-op, so the behandelportal is unchanged when ZGW is off. - Two housekeeping commits: a CI fix (test-storybook config-dir + missing behandelportal a11y coverage) and a docs fix (stale WP-65 status in the backlog README). ## Test plan - [x] `npm run ci` green (lint, dep:check ×2, both apps' localized builds, both Storybook builds, tests, backend tests, api-client drift) - [x] `cd backend && dotnet test` — 204/204 passing - [x] New unit tests for `OpenZaakZaakSource.RecordBesluit` against a stub `HttpMessageHandler` (posts the last statustype with besluit+toelichting; no-ops when the aanvraag has no zaak; throws on an unconfigured zaaktype) - [x] Manual: `npm start` (ssp :4200) and `npm run start:behandelportal` (:4201) against the shared backend, logged in on both - [ ] Residual risk (documented in WP-66): no live-harness (`OpenZaakIntegrationTests`) round-trip added for the besluit write yet — only stub-based unit tests, same gap WP-50 originally shipped with before WP-54's harness caught a real bug (`Content-Crs` header). Worth closing before relying on this in a real deployment.
eho added 2 commits 2026-08-02 20:04:16 +00:00
Restructures into apps/ssp + apps/behandelportal (two Angular projects)
plus libs/shared + libs/beheer (cross-app libraries), replacing WP-61's
separate sibling repo. That split had already produced real drift: a
hand-vendored copy of the backend's OpenAPI doc, a shared/ui+layout tree
forked and silently diverging (7 files), and beheer + the styles.scss
token bridge duplicated byte-for-byte across both repos.

- git mv the SSP's src/app/* into apps/ssp/; fold shared/, beheer/,
  environments/, the Storybook docs/*.mdx, and styles.scss into
  libs/shared + libs/beheer (all confirmed identical between the two
  repos before merging). auth stays deliberately duplicated per
  ADR-0002 (actor-specific, expected to diverge) - amended there.
- One generated API client (libs/shared), no more vendored swagger.json.
- .dependency-cruiser split into a base factory + one config per app,
  and Storybook into .storybook-ssp/.storybook-behandelportal - both
  forced by the @auth/* alias resolving to different directories per app.
- SiteHeaderComponent/ShellComponent gained HEADER_NAV_ITEMS/
  HEADER_ADMIN_LINKS/DEBUG_PANEL injection tokens so each app supplies
  its own nav/admin-links/dev-panel instead of one being hardcoded.
- CLAUDE.md, ARCHITECTURE.md, dependencies.md, and ADR-0002 updated;
  WP-67 backlog entry documents the full decision trail.

npm run ci green (lint, dep:check x2, 360 tests across ssp/
behandelportal/shared/beheer, both localized builds, backend tests,
snippet + api-client drift); both dev servers, both Storybook
instances, and docker compose verified working.

The old sibling repo (/home/eho/repos/behandelportal) is left
untouched, not deleted.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat(behandelportal): WP-64 werkvoorraad (queue) screen
CI / changes (pull_request) Successful in 15s
CI / lint (pull_request) Successful in 57s
CI / frontend (pull_request) Successful in 2m36s
CI / storybook-a11y (pull_request) Failing after 3m14s
CI / backend (pull_request) Successful in 2m1s
CI / semgrep (pull_request) Successful in 1m10s
CI / e2e (pull_request) Successful in 3m3s
CI / api-client-drift (pull_request) Successful in 2m1s
fe69caee63
New GET /werkvoorraad endpoint lists aanvragen still open (Ingediend/InBehandeling),
gated by the medewerker capability (CanBeoordelen) rather than the admin role — reuses
the existing ApplicationSummaryDto, no new DTO. GET /me now surfaces aanvraag:beoordelen
for a behandelaar so the FE can gate with the same AccessStore/capabilityGuard idiom
every other page uses.

FE: a behandeling domain type deliberately narrower than ssp's full AanvraagStatus
union (only the two open tags — illegal states unrepresentable), composed into a
werkvoorraad-list organism from existing shared/ui molecules. Replaces WP-61's
scaffold placeholder as the app's real landing page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
eho added 1 commit 2026-08-03 07:01:39 +00:00
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
4133b30e5d
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>
eho added 1 commit 2026-08-03 07:47:21 +00:00
feat(behandelportal): WP-65b beoordeling besluit (decision write)
CI / changes (pull_request) Successful in 17s
CI / lint (pull_request) Failing after 56s
CI / frontend (pull_request) Successful in 2m36s
CI / storybook-a11y (pull_request) Failing after 3m19s
CI / backend (pull_request) Failing after 1m55s
CI / api-client-drift (pull_request) Canceled after 0s
CI / e2e (pull_request) Canceled after 40s
CI / semgrep (pull_request) Canceled after 24s
af8a011819
Adds POST /beoordeling/{id}/besluit: a Besluit enum (Goedkeuren/Afwijzen/
MeerInfoOpvragen) backed by new Aanvraag.BesluitStatus/BesluitToelichting
columns, gated by the same BeoordelingRules.CanDecide the read side's
canBesluiten flag already uses (409 on an illegal transition, 400 on a
missing required toelichting). Mappers.ToStatusDto gains the "a recorded
decision wins" branch. FE: besluit.machine.ts + besluit-form organism
(same form idiom as change-request-form), wired into the beoordeling page
behind the server's canBesluiten flag.

Completes WP-65 (65a + 65b) — verified end-to-end against a running
backend (werkvoorraad -> beoordeling -> besluit -> status reflected back).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
eho added 1 commit 2026-08-03 07:52:36 +00:00
docs: fix stale WP-65 status in backlog README, closing the format:check gap
CI / changes (pull_request) Successful in 16s
CI / lint (pull_request) Successful in 54s
CI / frontend (pull_request) Successful in 2m27s
CI / storybook-a11y (pull_request) Failing after 3m16s
CI / backend (pull_request) Successful in 1m53s
CI / semgrep (pull_request) Successful in 1m7s
CI / e2e (pull_request) Successful in 2m45s
CI / api-client-drift (pull_request) Successful in 1m57s
6c6bef45f3
WP-65's summary-table row still said "in progress (65a done)" from before
65b shipped, and that one oversized cell was forcing prettier to want to
re-pad the entire ~65-row table. Correcting it to "done" (its actual
status, per the WP-65 file's own outcome notes) removes the outlier —
npm run format:check is green again with zero other rows touched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
eho added 1 commit 2026-08-03 08:24:52 +00:00
ci: fix test-storybook config-dir + add missing behandelportal a11y coverage
CI / changes (pull_request) Successful in 17s
CI / lint (pull_request) Successful in 56s
CI / frontend (pull_request) Successful in 2m31s
CI / backend (pull_request) Successful in 1m57s
CI / e2e (pull_request) Successful in 3m32s
CI / semgrep (pull_request) Successful in 1m13s
CI / api-client-drift (pull_request) Successful in 2m2s
CI / storybook-a11y (pull_request) Successful in 11m42s
39409bdf76
WP-67's monorepo split renamed .storybook to .storybook-ssp/
.storybook-behandelportal, but test-storybook still defaulted to the
(now nonexistent) plain .storybook dir -- it loads <config-dir>/main.js
for test-runner hooks even in --url mode, so every invocation failed
with "Could not load main.js in .storybook".

Also: test-storybook:ci only ever built+served+tested the ssp instance.
Since the split, behandelportal's stories (werkvoorraad, beoordeling,
besluit-form, ...) were never axe-tested in CI at all. Added the
:behandelportal siblings (mirroring the existing storybook/
build-storybook naming) and wired them into ci-local.sh's --full step
and the storybook-a11y GitHub Actions job.

Verified directly: ssp 62/180 stories green, behandelportal 45/112
green (including the new besluit-form story), full `ci-local.sh --full`
green end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
eho added 2 commits 2026-08-04 07:50:09 +00:00
Extends IZaakSource with RecordBesluit, mirroring WP-50's CreateZaak write
pattern: OpenZaakZaakSource POSTs a new Statussen entry (highest-volgnummer
statustype, since the harness catalogus has no per-outcome besluittype),
carrying the besluit + toelichting in statustoelichting; LocalZaakSource
no-ops. The beoordeling endpoint calls it after the local decision commits,
flagging a failure via RecordZgwDivergence the same way submit's
create-zaak/document writes do — closing WP-60's "second write pair" gap.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
docs: record WP-66 commit hash
CI / changes (pull_request) Successful in 18s
CI / lint (pull_request) Successful in 1m9s
CI / frontend (pull_request) Successful in 3m28s
CI / backend (pull_request) Successful in 2m59s
CI / e2e (pull_request) Successful in 4m33s
CI / semgrep (pull_request) Successful in 1m23s
CI / api-client-drift (pull_request) Successful in 2m25s
CI / storybook-a11y (pull_request) Successful in 14m48s
a2e301060e
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
eho changed title from Monorepo: merge behandelportal + WP-64 werkvoorraad screen to Behandelportal: monorepo merge + WP-64..67 backoffice arc (OpenZaak write closes it out) 2026-08-04 12:55:20 +00:00
eho merged commit d2c2cffc1f into main 2026-08-04 12:56:34 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: eho/atomic-design-poc#1