Commit Graph

17 Commits

Author SHA1 Message Date
7463efdc2d Add branching intake wizard (derived steps + radio-group atom)
A second wizard demonstrating a BRANCHING flow: the visible steps are derived
from the answers by a pure `visibleSteps` function rather than stored, so
answering "buiten Nederland gewerkt? -> ja" or reporting few hours adds steps
and the progress denominator changes live. Same Elm-style store + RemoteData
patterns as the fixed wizard; answers persist to localStorage.

- intake.machine.ts: IntakeState union + Answers + visibleSteps + pure reduce (+spec)
- intake-wizard organism, intake.page, submit-intake command
- new radio-group atom (ControlValueAccessor) in shared/ui
- /intake route + dashboard link + concepts showcase section
- tighten Aantekening.type to a 'Specialisme' | 'Aantekening' union
- README + ARCHITECTURE updated

Verified live end-to-end (branches add steps 4->5->6, review, submit) with no
console errors; build, unit tests, and Storybook all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 09:38:26 +02:00
164d20a10d Add second question (jaren werkzaam) to herregistratie wizard step 1
Step 1 was a single field, making the wizard feel thin. Add "Aantal jaren
werkzaam" beside "Gewerkte uren" on the same step (no new step): Draft/Valid
gain `jaren`, `next` validates both step-1 fields before advancing, and
`validate` parses it for the submitted payload. Verified live: an empty jaren
blocks advancing with an inline error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 09:38:11 +02:00
6086729563 Fix login output name collision and herregistratie demo eligibility
Walking through the running app surfaced two issues:

- login-form's `submit` output collided with the native DOM `submit` event
  bubbling to <app-login-form>, so login() also fired with an Event (not the
  BSN string) — "bsn.trim is not a function". Renamed the output to `submitted`
  (matching the other forms).
- The static mock herregistratie deadline (2027-09-01) sat outside the 12-month
  eligibility window, so the wizard was correctly hidden. Moved it to 2027-03-01
  so "verloopt binnenkort" is true and the flow is demoable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 07:38:06 +02:00
8b590a50d9 Regenerate compodoc documentation.json
Reflects the bounded-context restructuring and new state-management modules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 07:20:23 +02:00
8eeffc3d4a Add architecture guide for developers new to FP
Plain-language walkthrough of the bounded-context layering and the state
management (RemoteData, the Elm-style store, combining services, optimistic
updates, value objects), with a glossary — aimed at a junior with no functional
programming background.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 07:20:23 +02:00
2114514ad7 Restructure into DDD bounded contexts + functional state management
Reorganise from atomic-design-only folders into bounded contexts
(auth / registratie / herregistratie) over a shared kernel, each split into
domain / application / infrastructure / ui layers. Dependencies point inward;
the domain layer is framework-free. Path aliases (@shared/@auth/@registratie/
@herregistratie) make import direction explicit.

State management (Elm-style, native TS, no new deps):
- shared/application/store.ts — createStore(init, update): pure reducer + signal
- shared/application/remote-data.ts — add map/map2/map3/andThen combinators so
  several services fold into one RemoteData; <app-async> gains an [rd] input
- registratie/application/big-profile.store.ts — root singleton combining the
  BIG-register and BRP services via map2 into one state; holds the optimistic
  herregistratie flag shared with the dashboard
- herregistratie: machine gains a WizardMsg union + pure reduce; submit is a
  command that calls infra and dispatches the result, with optimistic update +
  rollback against the shared store
- auth: SessionStore + DigiD adapter + functional route guard; login establishes
  the session, protected routes use canActivate

Rich domain: registration.policy.ts (statusColor/label, herregistratie
eligibility, invariants); BigNummer/Postcode/Uren value objects with smart
constructors. status-badge is now domain-free (colour/label inputs).

Specs for the reducer, RemoteData combinators, and eligibility policy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 07:20:13 +02:00
6bd6e854c7 Regenerate compodoc documentation.json
Reflects the new types, components, and pages from the impossible-states work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:56:17 +02:00
727253e5f5 Add /concepts showcase page
A teaching page pairing each pattern's impossible-state-permitting "before"
with the "after" the type system enforces: discriminated unions, the
RemoteData fold, parse-don't-validate (live), and the wizard state machine.
Composition-only — no new atoms. Linked from the dashboard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:56:11 +02:00
80c1b627d0 Drive herregistratie as a state-machine wizard
Model the multi-step form as one tagged union: step/errors exist only while
Editing, and Submitting/Submitted/Failed carry a parsed Valid payload. So
"submitting while a field is invalid" and "success screen with errors set"
are unrepresentable by construction.

Pure transitions (next/back/submit/resolve) with a spec covering the key
invariants; illegal events are no-ops. The page becomes pure composition.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:56:04 +02:00
ad50b3fa8f Parse, don't validate: branded types for form input
Add smart constructors parsePostcode/parseUren returning Result<string, Brand>.
The constructor is the only way to mint a Postcode/Uren, so a validated value
is a distinct type from a raw string.

change-request-form now emits a ChangeRequest carrying a parsed Postcode, and
its field errors come straight from the parser's Result — no parallel "is it
valid" flag that can drift out of sync with the value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:55:56 +02:00
57b9f3f804 Model Registration status as a discriminated union
Each status variant now owns its own data: only Geregistreerd carries a
herregistratieDatum; Geschorst/Doorgehaald carry their own dates + reason.
A struck-off registration can no longer hold a future herregistratie date —
that impossible combination is gone from the type.

- status-badge keys color off the tag via a switch + assertNever
- registration-summary renders only the rows a variant's data supports
- registration.json nests the status; stories cover all three variants

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:55:49 +02:00
0920063553 Back AsyncComponent with a RemoteData tagged union
Introduce RemoteData<E,T> (Loading | Empty | Failure | Success) plus
fromResource and an exhaustive foldRemote. The data lives ON the state,
so "loaded without value" or "error with stale value" are unrepresentable.

AsyncComponent now derives a single rd() and pulls value/error out via the
fold instead of a loose State string. Public API (resource/isEmpty inputs,
the four slot directives, the ASYNC array) is unchanged, so the dashboard,
detail page, and async stories need no edits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:55:40 +02:00
43b2f83485 Add native-TS functional toolkit (assertNever, Result, Brand)
The shared foundation for the "make impossible states impossible" work:
- assertNever for compile-time exhaustiveness in union switches
- Result<E,T> + ok/err constructors (plain objects, no classes)
- Brand<T,B> for nominal types

No runtime dependency — this is the whole "library".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 16:55:30 +02:00
Claude
4e14152758 Content-only page transitions + dependency security overrides
- Persistent ShellComponent hosts the router-outlet so header/footer mount once
  (no re-mount flash); pages nest under a shell route. page-shell is now
  content-only; page-layout removed.
- Native withViewTransitions() cross-fades only the routed content (chrome gets
  stable view-transition-names); respects prefers-reduced-motion.
- package.json overrides pin patched transitive dev/build deps: npm audit 16
  (3 high/9 mod/4 low) -> 5 low; shipped app stays at 0 (npm audit --omit=dev).
  No Angular downgrade, no breaking Babel 8 bump. jsdom 28 -> 29.
- README: page-transitions section + honest dependency-security note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 15:46:42 +02:00
Claude
f1f4f982a6 Full-width layout, page-shell template, httpResource async states, README
- Fix full-bleed header/footer (align-items:stretch + block hosts; centered
  content column via shared --app-content-max).
- New templates/page-shell (back-link + heading + intro + content, narrow mode);
  all pages refactored to compose it.
- Async state management with native httpResource + <app-async> wrapper that
  renders exactly one of loading/empty/error/loaded (impossible states
  unrepresentable); delayed spinner + skeleton atoms for slow/fast connections.
- Scenario interceptor (?scenario=slow|loading|empty|error) to demo every state.
- Storybook: spinner/skeleton/page-shell/async-states stories.
- README rewritten as a guide (atomic design, reuse benefits, state handling).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:53:28 +02:00
Claude
9b85309002 Add herregistratie page composed entirely from existing components
Demonstrates the atomic-design payoff: a whole new flow (route + page + nav
link) reuses page-layout, heading, alert, form-field, text-input, button and
link with no new component files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:27:18 +02:00
Claude
033d6f0317 Atomic-design POC: BIG-register self-service portal (Angular + Rijkshuisstijl)
Atoms/molecules/organisms/templates/pages composing the NL Design System
(Utrecht) CSS themed Rijkshuisstijl via @rijkshuisstijl-community tokens.
Login -> dashboard -> registration detail, mock JSON over HttpClient, Storybook
organized by atomic layer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 14:23:11 +02:00