style: format frontend, docs and skills with prettier; add .prettierignore
One-time prettier --write so the new format:check CI gate starts green. .prettierignore excludes generated (api-client.ts, documentation.json), vendored (public/cibg-huisstijl), and backend (dotnet format owns it). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,13 @@ import { parsePostcode } from '@registratie/domain/value-objects/postcode';
|
||||
|
||||
/** Minimal fake Resource so <app-async> can be driven through every state without HTTP. */
|
||||
function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T> {
|
||||
return { value: () => value as T, status: () => status, error: () => error, hasValue: () => value !== undefined, reload: () => {} } as unknown as Resource<T>;
|
||||
return {
|
||||
value: () => value as T,
|
||||
status: () => status,
|
||||
error: () => error,
|
||||
hasValue: () => value !== undefined,
|
||||
reload: () => {},
|
||||
} as unknown as Resource<T>;
|
||||
}
|
||||
|
||||
/** Teaching showcase: each section pairs the impossible-state-permitting"before"
|
||||
@@ -22,38 +28,151 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
@Component({
|
||||
selector: 'app-concepts-page',
|
||||
imports: [
|
||||
FormsModule, PageShellComponent, HeadingComponent, TextInputComponent,
|
||||
...ASYNC, SkeletonComponent, RegistrationSummaryComponent, HerregistratieWizardComponent, IntakeWizardComponent,
|
||||
FormsModule,
|
||||
PageShellComponent,
|
||||
HeadingComponent,
|
||||
TextInputComponent,
|
||||
...ASYNC,
|
||||
SkeletonComponent,
|
||||
RegistrationSummaryComponent,
|
||||
HerregistratieWizardComponent,
|
||||
IntakeWizardComponent,
|
||||
],
|
||||
styles: [
|
||||
`
|
||||
.section {
|
||||
margin: 0 0 3rem;
|
||||
}
|
||||
.lead {
|
||||
color: var(--rhc-color-grijs-700);
|
||||
max-width: 46rem;
|
||||
margin: 0.25rem 0 1.25rem;
|
||||
}
|
||||
.cols {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
|
||||
gap: 1.5rem;
|
||||
align-items: start;
|
||||
}
|
||||
.card {
|
||||
border: 1px solid var(--rhc-color-grijs-200, #e5e5e5);
|
||||
border-radius: 10px;
|
||||
padding: 1.25rem;
|
||||
background: #fff;
|
||||
}
|
||||
.card--bad {
|
||||
border-color: var(--rhc-color-rood-300, #f0b4b4);
|
||||
}
|
||||
.card--good {
|
||||
border-color: var(--rhc-color-groen-300, #b4e0b4);
|
||||
}
|
||||
.tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
font-weight: 700;
|
||||
font-size: 0.72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin: 0 0 0.75rem;
|
||||
}
|
||||
.tag::before {
|
||||
content: '';
|
||||
width: 0.6rem;
|
||||
height: 0.6rem;
|
||||
border-radius: 50%;
|
||||
}
|
||||
.tag.bad {
|
||||
color: var(--rhc-color-rood-600, #a30000);
|
||||
}
|
||||
.tag.bad::before {
|
||||
background: var(--rhc-color-rood-500, #d52b1e);
|
||||
}
|
||||
.tag.good {
|
||||
color: var(--rhc-color-groen-700, #277337);
|
||||
}
|
||||
.tag.good::before {
|
||||
background: var(--rhc-color-groen-500, #39870c);
|
||||
}
|
||||
.tag.plain {
|
||||
color: var(--rhc-color-grijs-700);
|
||||
}
|
||||
.tag.plain::before {
|
||||
display: none;
|
||||
}
|
||||
pre {
|
||||
background: #1e2430;
|
||||
color: #e6e9ef;
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
overflow: auto;
|
||||
font-size: 0.82rem;
|
||||
line-height: 1.55;
|
||||
margin: 0;
|
||||
}
|
||||
pre .k {
|
||||
color: #c792ea;
|
||||
}
|
||||
pre .s {
|
||||
color: #c3e88d;
|
||||
}
|
||||
pre .c {
|
||||
color: #7e8aa0;
|
||||
font-style: italic;
|
||||
}
|
||||
.note {
|
||||
font-size: 0.9rem;
|
||||
color: var(--rhc-color-grijs-700);
|
||||
margin: 0.75rem 0 0;
|
||||
}
|
||||
/* live state diagram */
|
||||
.machine {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
.node {
|
||||
padding: 0.4rem 0.8rem;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--rhc-color-grijs-300, #ccc);
|
||||
font-size: 0.82rem;
|
||||
color: var(--rhc-color-grijs-700);
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.node.on {
|
||||
background: var(--rhc-color-hemelblauw-100, #e5f1fb);
|
||||
border-color: var(--rhc-color-hemelblauw-500, #007bc7);
|
||||
color: var(--rhc-color-hemelblauw-700, #00567d);
|
||||
font-weight: 700;
|
||||
}
|
||||
.steplist {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
.pill {
|
||||
padding: 0.3rem 0.7rem;
|
||||
border-radius: 8px;
|
||||
background: var(--rhc-color-grijs-100, #f3f3f3);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.pill.extra {
|
||||
background: var(--rhc-color-geel-100, #fff6d6);
|
||||
border: 1px dashed var(--rhc-color-geel-600, #c79a00);
|
||||
}
|
||||
.arrow {
|
||||
color: var(--rhc-color-grijs-400, #999);
|
||||
}
|
||||
`,
|
||||
],
|
||||
styles: [`
|
||||
.section { margin: 0 0 3rem }
|
||||
.lead { color: var(--rhc-color-grijs-700); max-width: 46rem; margin: 0.25rem 0 1.25rem }
|
||||
.cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1.5rem; align-items: start }
|
||||
.card { border: 1px solid var(--rhc-color-grijs-200, #e5e5e5); border-radius: 10px; padding: 1.25rem; background: #fff }
|
||||
.card--bad { border-color: var(--rhc-color-rood-300, #f0b4b4) }
|
||||
.card--good { border-color: var(--rhc-color-groen-300, #b4e0b4) }
|
||||
.tag { display: inline-flex; align-items: center; gap: 0.4rem; font-weight: 700; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; margin: 0 0 0.75rem }
|
||||
.tag::before { content: ''; width: 0.6rem; height: 0.6rem; border-radius: 50% }
|
||||
.tag.bad { color: var(--rhc-color-rood-600, #a30000) } .tag.bad::before { background: var(--rhc-color-rood-500, #d52b1e) }
|
||||
.tag.good { color: var(--rhc-color-groen-700, #277337) } .tag.good::before { background: var(--rhc-color-groen-500, #39870c) }
|
||||
.tag.plain { color: var(--rhc-color-grijs-700) } .tag.plain::before { display: none }
|
||||
pre { background: #1e2430; color: #e6e9ef; padding: 1rem; border-radius: 8px; overflow: auto; font-size: 0.82rem; line-height: 1.55; margin: 0 }
|
||||
pre .k { color: #c792ea } pre .s { color: #c3e88d } pre .c { color: #7e8aa0; font-style: italic }
|
||||
.note { font-size: 0.9rem; color: var(--rhc-color-grijs-700); margin: 0.75rem 0 0 }
|
||||
/* live state diagram */
|
||||
.machine { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem }
|
||||
.node { padding: 0.4rem 0.8rem; border-radius: 999px; border: 1px solid var(--rhc-color-grijs-300, #ccc); font-size: 0.82rem; color: var(--rhc-color-grijs-700); transition: all .15s }
|
||||
.node.on { background: var(--rhc-color-hemelblauw-100, #e5f1fb); border-color: var(--rhc-color-hemelblauw-500, #007bc7); color: var(--rhc-color-hemelblauw-700, #00567d); font-weight: 700 }
|
||||
.steplist { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin: 0 0 1rem }
|
||||
.pill { padding: 0.3rem 0.7rem; border-radius: 8px; background: var(--rhc-color-grijs-100, #f3f3f3); font-size: 0.8rem }
|
||||
.pill.extra { background: var(--rhc-color-geel-100, #fff6d6); border: 1px dashed var(--rhc-color-geel-600, #c79a00) }
|
||||
.arrow { color: var(--rhc-color-grijs-400, #999) }
|
||||
`],
|
||||
template: `
|
||||
<app-page-shell heading="Onmogelijke toestanden onmogelijk maken" backLink="/dashboard">
|
||||
<p class="lead">
|
||||
Vijf functionele patronen die atomic design makkelijker maakt om te tonen — telkens
|
||||
"fout" (de oude vorm liet het toe) naast"goed" (het type maakt het onmogelijk).
|
||||
Vijf functionele patronen die atomic design makkelijker maakt om te tonen — telkens "fout"
|
||||
(de oude vorm liet het toe) naast"goed" (het type maakt het onmogelijk).
|
||||
</p>
|
||||
|
||||
<!-- 1. Discriminated unions -->
|
||||
@@ -64,12 +183,17 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
<div class="card card--bad">
|
||||
<p class="tag bad">Fout — vlakke interface</p>
|
||||
<pre [innerHTML]="unionBad"></pre>
|
||||
<p class="note">Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.</p>
|
||||
<p class="note">
|
||||
Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card card--good">
|
||||
<p class="tag good">Goed — sum type</p>
|
||||
<app-registration-summary [reg]="doorgehaald" />
|
||||
<p class="note">De variant <code>Doorgehaald</code> kent geen herregistratiedatum, dus de rij bestaat simpelweg niet.</p>
|
||||
<p class="note">
|
||||
De variant <code>Doorgehaald</code> kent geen herregistratiedatum, dus de rij bestaat
|
||||
simpelweg niet.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -77,23 +201,44 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
<!-- 2. RemoteData fold -->
|
||||
<section class="section">
|
||||
<app-heading [level]="2">2 · RemoteData fold</app-heading>
|
||||
<p class="lead">Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.</p>
|
||||
<p class="lead">
|
||||
Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.
|
||||
</p>
|
||||
<div class="cols">
|
||||
<div class="card card--good">
|
||||
<p class="tag good">Vier toestanden, één molecuul</p>
|
||||
<p class="tag plain">Loading</p>
|
||||
<app-async [resource]="loadingRes"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template><ng-template appAsyncLoading><app-skeleton [count]="2" height="1.2rem" [delay]="0" /></ng-template></app-async>
|
||||
<app-async [resource]="loadingRes"
|
||||
><ng-template appAsyncLoaded let-v>{{ v }}</ng-template
|
||||
><ng-template appAsyncLoading
|
||||
><app-skeleton [count]="2" height="1.2rem" [delay]="0" /></ng-template
|
||||
></app-async>
|
||||
<p class="tag plain">Empty</p>
|
||||
<app-async [resource]="emptyRes" [isEmpty]="isEmpty"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async>
|
||||
<app-async [resource]="emptyRes" [isEmpty]="isEmpty"
|
||||
><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async
|
||||
>
|
||||
<p class="tag plain">Failure</p>
|
||||
<app-async [resource]="errorRes"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async>
|
||||
<app-async [resource]="errorRes"
|
||||
><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async
|
||||
>
|
||||
<p class="tag plain">Success</p>
|
||||
<app-async [resource]="successRes" [isEmpty]="isEmpty"><ng-template appAsyncLoaded let-v><ul>@for (i of v; track i) {<li>{{ i }}</li>}</ul></ng-template></app-async>
|
||||
<app-async [resource]="successRes" [isEmpty]="isEmpty"
|
||||
><ng-template appAsyncLoaded let-v
|
||||
><ul>
|
||||
@for (i of v; track i) {
|
||||
<li>{{ i }}</li>
|
||||
}
|
||||
</ul></ng-template
|
||||
></app-async
|
||||
>
|
||||
</div>
|
||||
<div class="card card--good">
|
||||
<p class="tag good">De exhaustieve fold</p>
|
||||
<pre [innerHTML]="foldCode"></pre>
|
||||
<p class="note">Een nieuwe variant toevoegen breekt de compile via <code>assertNever</code> tot je hem afhandelt.</p>
|
||||
<p class="note">
|
||||
Een nieuwe variant toevoegen breekt de compile via <code>assertNever</code> tot je hem
|
||||
afhandelt.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -105,13 +250,21 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
<div class="cols">
|
||||
<div class="card">
|
||||
<p class="tag good">Smart constructor → Result</p>
|
||||
<app-text-input inputId="pc" [ngModel]="raw()" (ngModelChange)="raw.set($event)" name="pc" placeholder="Typ een postcode, bijv. 1234 AB" />
|
||||
<app-text-input
|
||||
inputId="pc"
|
||||
[ngModel]="raw()"
|
||||
(ngModelChange)="raw.set($event)"
|
||||
name="pc"
|
||||
placeholder="Typ een postcode, bijv. 1234 AB"
|
||||
/>
|
||||
</div>
|
||||
<div class="card" [class.card--good]="parsed().ok" [class.card--bad]="!parsed().ok">
|
||||
@if (parsed().ok) {
|
||||
<p class="tag good">ok</p>
|
||||
<pre>Postcode ="{{ $any(parsed()).value }}"</pre>
|
||||
<p class="note">Een gevalideerde <code>Postcode</code> is een ander type dan een ruwe string.</p>
|
||||
<p class="note">
|
||||
Een gevalideerde <code>Postcode</code> is een ander type dan een ruwe string.
|
||||
</p>
|
||||
} @else {
|
||||
<p class="tag bad">err</p>
|
||||
<pre>{{ $any(parsed()).error }}</pre>
|
||||
@@ -123,17 +276,22 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
<!-- 4. State machine / wizard (live state diagram) -->
|
||||
<section class="section">
|
||||
<app-heading [level]="2">4 · Form als state machine</app-heading>
|
||||
<p class="lead">Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de huidige.</p>
|
||||
<p class="lead">
|
||||
Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de
|
||||
huidige.
|
||||
</p>
|
||||
<div class="cols">
|
||||
<div class="card card--bad">
|
||||
<p class="tag bad">Fout — losse booleans</p>
|
||||
<pre [innerHTML]="machineBad"></pre>
|
||||
<p class="note">Niets verhindert"submitting" mét validatiefouten of een successcherm met errors.</p>
|
||||
<p class="note">
|
||||
Niets verhindert"submitting" mét validatiefouten of een successcherm met errors.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card card--good">
|
||||
<p class="tag good">Goed — één tagged union</p>
|
||||
<div class="machine">
|
||||
@for (n of ['Editing','Submitting','Submitted','Failed']; track n) {
|
||||
@for (n of ['Editing', 'Submitting', 'Submitted', 'Failed']; track n) {
|
||||
<span class="node" [class.on]="w.state().tag === n">{{ n }}</span>
|
||||
}
|
||||
</div>
|
||||
@@ -144,11 +302,14 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
|
||||
<!-- 5. Fixed steps, questions revealed inline -->
|
||||
<section class="section">
|
||||
<app-heading [level]="2">5 · Vragenlijst met vaste stappen —"vragen tonen, niet stappen toevoegen"</app-heading>
|
||||
<app-heading [level]="2"
|
||||
>5 · Vragenlijst met vaste stappen —"vragen tonen, niet stappen toevoegen"</app-heading
|
||||
>
|
||||
<p class="lead">
|
||||
Het aantal stappen ligt vast (<code>STEPS</code>); vervolgvragen verschijnen <em>binnen</em> een stap
|
||||
op basis van eerdere antwoorden. Antwoord"ja" op buitenland of vul weinig uren in, en er komt een
|
||||
extra vraag bij in dezelfde stap — de voortgang"van N" blijft gelijk.
|
||||
Het aantal stappen ligt vast (<code>STEPS</code>); vervolgvragen verschijnen
|
||||
<em>binnen</em> een stap op basis van eerdere antwoorden. Antwoord"ja" op buitenland of
|
||||
vul weinig uren in, en er komt een extra vraag bij in dezelfde stap — de voortgang"van N"
|
||||
blijft gelijk.
|
||||
</p>
|
||||
<div class="cols">
|
||||
<div class="card card--good">
|
||||
@@ -156,10 +317,15 @@ function fakeResource<T>(status: string, value?: T, error?: Error): Resource<T>
|
||||
<div class="steplist">
|
||||
@for (s of iw.steps; track s; let last = $last) {
|
||||
<span class="pill">{{ s }}</span>
|
||||
@if (!last) { <span class="arrow">→</span> }
|
||||
@if (!last) {
|
||||
<span class="arrow">→</span>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<p class="note">De stappen zijn altijd dezelfde; alleen de vragen <em>binnen</em> een stap verschijnen of verdwijnen.</p>
|
||||
<p class="note">
|
||||
De stappen zijn altijd dezelfde; alleen de vragen <em>binnen</em> een stap verschijnen
|
||||
of verdwijnen.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card card--good">
|
||||
<p class="tag good">De wizard</p>
|
||||
@@ -174,8 +340,11 @@ export class ConceptsPage {
|
||||
isEmpty = (v: string[]) => !v || v.length === 0;
|
||||
|
||||
doorgehaald: Registration = {
|
||||
bigNummer: '19012345601', naam: 'Dr. A. (Anna) de Vries', beroep: 'Arts',
|
||||
registratiedatum: '2012-09-01', geboortedatum: '1985-03-14',
|
||||
bigNummer: '19012345601',
|
||||
naam: 'Dr. A. (Anna) de Vries',
|
||||
beroep: 'Arts',
|
||||
registratiedatum: '2012-09-01',
|
||||
geboortedatum: '1985-03-14',
|
||||
status: { tag: 'Doorgehaald', doorgehaaldOp: '2024-05-01', reden: 'Op eigen verzoek' },
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user