feat(showcase): WP-39 — linked code snippets + teaching animations
Anti-drift snippets: `?raw` isn't supported by Angular's esbuild build, so real code is exposed via // #region showcase:<name> markers in source (registration/remote-data/ postcode/change-request.machine/intake.machine) → scripts/gen-snippets.mjs → committed snippets.generated.ts, with a CI drift gate so the shown code is the shipped code. The /concepts page renders the 5 real snippets (union/fold/parse/machine/steps) with a source caption + a tiny dependency-free highlightTs (+spec); deliberately-wrong illustrations stay authored. Teaching motion: active state node pops on transition + parse result animates in (existing reduced-motion-safe toolkit). Generated file prettier-ignored (like api-client). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -24,11 +24,13 @@ export type Errors = Partial<Record<keyof Draft, string>>;
|
||||
* Submitting/Submitted/Failed carry the parsed `Valid`. Illegal states (submitting
|
||||
* an invalid draft, a success screen with errors) are unrepresentable.
|
||||
*/
|
||||
// #region showcase:machine
|
||||
export type ChangeRequestState =
|
||||
| { tag: 'Editing'; draft: Draft; errors: Errors }
|
||||
| { tag: 'Submitting'; data: Valid }
|
||||
| { tag: 'Editing'; draft: Draft; errors: Errors } // draft/errors exist ONLY while editing
|
||||
| { tag: 'Submitting'; data: Valid } // carries the parsed value, no errors
|
||||
| { tag: 'Submitted'; data: Valid; referentie: string }
|
||||
| { tag: 'Failed'; data: Valid; error: string };
|
||||
// #endregion showcase:machine
|
||||
|
||||
export const initial: ChangeRequestState = {
|
||||
tag: 'Editing',
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
* old flat interface allowed that impossible combination — this makes it
|
||||
* unrepresentable.
|
||||
*/
|
||||
// #region showcase:union
|
||||
export type RegistrationStatus =
|
||||
| { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date
|
||||
| { tag: 'Geregistreerd'; herregistratieDatum: string } // only this variant carries the date
|
||||
| { tag: 'Geschorst'; geschorstTot: string; reden: string }
|
||||
| { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };
|
||||
// #endregion showcase:union
|
||||
|
||||
/** Just the discriminant — for atoms that only need the label/color. */
|
||||
export type StatusTag = RegistrationStatus['tag'];
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Brand, Result, ok, err } from '@shared/kernel/fp';
|
||||
*/
|
||||
export type Postcode = Brand<string, 'Postcode'>;
|
||||
|
||||
// #region showcase:parse
|
||||
export function parsePostcode(raw: string): Result<string, Postcode> {
|
||||
const t = raw.trim().toUpperCase();
|
||||
if (!/^[1-9]\d{3}\s?[A-Z]{2}$/.test(t)) {
|
||||
@@ -15,3 +16,4 @@ export function parsePostcode(raw: string): Result<string, Postcode> {
|
||||
// Normalise to "1234 AB" — the parser also cleans up.
|
||||
return ok(t.replace(/^(\d{4})\s?([A-Z]{2})$/, '$1 $2') as Postcode);
|
||||
}
|
||||
// #endregion showcase:parse
|
||||
|
||||
Reference in New Issue
Block a user