Commit Graph
6 Commits
Author SHA1 Message Date
ehoandClaude Opus 4.8 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
ehoandClaude Opus 4.8 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
ehoandClaude Opus 4.8 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
ehoandClaude Opus 4.8 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
ehoandClaude Opus 4.8 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
ehoandClaude Opus 4.8 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