Files
atomic-design-poc/documentation.json
Edwin van den Houdt 64385999eb Add registratie wizard, BFF dashboard-view, contracts/value-objects, and architecture docs
Checkpoint of in-progress work: the registration wizard (address prefill,
DUO diploma lookup, policy questions), decision-DTO contracts, parse-don't-
validate value objects, infrastructure adapters, plus CLAUDE.md and the
architecture/ADR docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 17:23:52 +02:00

14311 lines
834 KiB
JSON

{
"pipes": [],
"interfaces": [
{
"name": "Aantekening",
"id": "interface-Aantekening-8e28c6bf4ec154e4db100e2ec1ee19fe9c2d50bc429733f8288f32277ce91963f67ec0363028632e71cfb7e05ce25001908c8dd8ee66d732632d67b993c0645b",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export type RegistrationStatus =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\n/** Just the discriminant — for atoms that only need the label/color. */\nexport type StatusTag = RegistrationStatus['tag'];\n\nexport interface Registration {\n bigNummer: string;\n naam: string;\n beroep: string; // arts, verpleegkundige, apotheker, ...\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatus;\n}\n\n/** A note is either a recognised specialism or a plain annotation — a closed set,\n not an open string, so a typo can't slip through. */\nexport type AantekeningType = 'Specialisme' | 'Aantekening';\n\nexport interface Aantekening {\n type: AantekeningType;\n omschrijving: string;\n datum: string;\n}\n",
"properties": [
{
"name": "datum",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 32
},
{
"name": "omschrijving",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 31
},
{
"name": "type",
"deprecated": false,
"deprecationMessage": "",
"type": "AantekeningType",
"indexKey": "",
"optional": false,
"description": "",
"line": 30
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "Adres",
"id": "interface-Adres-07cec46d80a41919e40d0bcb002981627d1a30edb363d64a0a140a8af2c38ad85be5603d88030d0704870800694c5bed9c709ae6891fe83e646e90c1a67cf548",
"file": "src/app/registratie/domain/person.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface Adres {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface Person {\n naam: string;\n geboortedatum: string; // ISO date\n adres: Adres;\n}\n",
"properties": [
{
"name": "postcode",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 4
},
{
"name": "straat",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 3
},
{
"name": "woonplaats",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 5
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>Person identity as supplied by the BRP (Basisregistratie Personen).</p>\n",
"rawdescription": "\nPerson identity as supplied by the BRP (Basisregistratie Personen).",
"methods": [],
"extends": []
},
{
"name": "Answers",
"id": "interface-Answers-13704a2a04ab3cffa46cbc109ee84699b236928e1c2649f459e18e4daf323047e8b21cdd5a21440c5fd696a605a09feb673b4020394ba7954ae35162f1bd2732",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, ok, err, assertNever } from '@shared/kernel/fp';\nimport { Uren, parseUren } from '@registratie/domain/value-objects/uren';\n\n/**\n * A FIXED 3-step wizard with progressive disclosure. The steps never change in\n * number (always `STEPS`); instead, follow-up questions appear *inline within a\n * step* depending on earlier answers — answer \"buiten Nederland gewerkt? → ja\"\n * and the country/hours questions reveal in the same step; report few hours and\n * the scholing-question reveals inside the 'werk' step. \"Is this field required\n * right now\" is a pure function (`validateStep`/`lageUren`), so it's trivial to\n * test and impossible to get out of sync with the data.\n */\n\nexport type JaNee = 'ja' | 'nee';\n\n/** The three fixed steps. Each step groups one or more questions. */\nexport type StepId = 'buitenland' | 'werk' | 'review';\n\n/** One record carried across every step (and persisted). All optional: the user\n fills it in gradually, and branches may never ask some fields. */\nexport interface Answers {\n buitenlandGewerkt?: JaNee; // Q1\n land?: string; // Q1a — only when buitenlandGewerkt === 'ja'\n buitenlandseUren?: string; // Q1b — only when buitenlandGewerkt === 'ja'\n uren?: string; // Q2 — uren in NL\n scholingGevolgd?: JaNee; // Q3 — only when total hours are below the threshold\n punten?: string; // Q4\n}\n\n/** What we have after the review step parses — guaranteed valid/typed. */\nexport interface ValidIntake {\n werktBuitenland: boolean;\n land?: string;\n buitenlandseUren?: Uren;\n uren: Uren;\n aanvullendeScholing?: boolean;\n punten?: Uren; // only collected when aanvullende scholing is gevolgd (scholingGevolgd === 'ja')\n}\n\n/** Demo fallback only — the real threshold is a SERVER-OWNED policy value fetched\n at runtime (see IntakePolicyDto / SetPolicy). ponytail: default is the offline\n fallback; the server value wins. */\nexport const SCHOLING_THRESHOLD_DEFAULT = 1000;\n\n/** True when NL-hours are low enough that the scholing question must be answered.\n The threshold is passed in (server-owned), not hardcoded. */\nexport function lageUren(a: Answers, scholingThreshold = SCHOLING_THRESHOLD_DEFAULT): boolean {\n const r = parseUren(a.uren ?? '');\n return r.ok && r.value < scholingThreshold;\n}\n\n/** The fixed step list. Number of steps never changes; questions reveal inline. */\nexport const STEPS: StepId[] = ['buitenland', 'werk', 'review'];\n\n/** Per-field error map: one message per question, since a step holds several. */\ntype Errors = Partial<Record<keyof Answers, string>>;\n\nexport type IntakeState =\n | { tag: 'Answering'; answers: Answers; cursor: number; errors: Errors; scholingThreshold: number }\n | { tag: 'Submitting'; data: ValidIntake }\n | { tag: 'Submitted'; data: ValidIntake }\n | { tag: 'Failed'; data: ValidIntake; error: string };\n\nexport const initial: IntakeState = { tag: 'Answering', answers: {}, cursor: 0, errors: {}, scholingThreshold: SCHOLING_THRESHOLD_DEFAULT };\n\n/** Which step the cursor currently points at (clamped to the fixed list). */\nexport function currentStep(s: Extract<IntakeState, { tag: 'Answering' }>): StepId {\n return STEPS[Math.min(s.cursor, STEPS.length - 1)];\n}\n\n/** Validate every question currently visible in ONE step. Errors keyed per field. */\nfunction validateStep(step: StepId, a: Answers, scholingThreshold: number): Result<Errors, void> {\n const errors: Errors = {};\n switch (step) {\n case 'buitenland':\n if (!a.buitenlandGewerkt) errors.buitenlandGewerkt = 'Maak een keuze.';\n else if (a.buitenlandGewerkt === 'ja') {\n if (!a.land || a.land.trim() === '') errors.land = 'Vul een land in.';\n const u = parseUren(a.buitenlandseUren ?? '');\n if (!u.ok) errors.buitenlandseUren = u.error;\n }\n break;\n case 'werk': {\n const u = parseUren(a.uren ?? '');\n if (!u.ok) errors.uren = u.error;\n if (lageUren(a, scholingThreshold) && !a.scholingGevolgd) errors.scholingGevolgd = 'Maak een keuze.';\n // Nascholingspunten are only asked (and required) when scholing was followed.\n if (a.scholingGevolgd === 'ja') {\n const p = parseUren(a.punten ?? '');\n if (!p.ok) errors.punten = p.error;\n }\n break;\n }\n case 'review':\n break; // review shows a summary; no own fields\n default:\n return assertNever(step);\n }\n return Object.keys(errors).length > 0 ? err(errors) : ok(undefined);\n}\n\n/** Parse the whole questionnaire into a ValidIntake (called on submit). */\nfunction validateAll(a: Answers, scholingThreshold: number): Result<Errors, ValidIntake> {\n const errors: Errors = {};\n for (const step of STEPS) {\n const r = validateStep(step, a, scholingThreshold);\n if (!r.ok) Object.assign(errors, r.error);\n }\n if (Object.keys(errors).length > 0) return err(errors);\n\n const uren = parseUren(a.uren ?? '');\n // validateStep guaranteed uren parses, but keep the compiler happy.\n if (!uren.ok) return err(errors);\n\n const werktBuitenland = a.buitenlandGewerkt === 'ja';\n const buitenland = parseUren(a.buitenlandseUren ?? '');\n // Punten are only collected when aanvullende scholing was gevolgd.\n const punten = a.scholingGevolgd === 'ja' ? parseUren(a.punten ?? '') : undefined;\n return ok({\n werktBuitenland,\n land: werktBuitenland ? a.land : undefined,\n buitenlandseUren: werktBuitenland && buitenland.ok ? buitenland.value : undefined,\n uren: uren.value,\n aanvullendeScholing: lageUren(a, scholingThreshold) ? a.scholingGevolgd === 'ja' : undefined,\n punten: punten?.ok ? punten.value : undefined,\n });\n}\n\nexport function setAnswer(s: IntakeState, key: keyof Answers, value: string): IntakeState {\n if (s.tag !== 'Answering') return s;\n // Steps are fixed, so editing an answer never moves the cursor — it only\n // reveals/hides inline questions within the current step.\n return { ...s, answers: { ...s.answers, [key]: value } };\n}\n\nexport function next(s: IntakeState): IntakeState {\n if (s.tag !== 'Answering') return s;\n const r = validateStep(currentStep(s), s.answers, s.scholingThreshold);\n if (!r.ok) return { ...s, errors: r.error };\n return { ...s, cursor: Math.min(s.cursor + 1, STEPS.length - 1), errors: {} };\n}\n\n/** Apply a server-owned policy value (e.g. the scholing threshold). */\nexport function setPolicy(s: IntakeState, scholingThreshold: number): IntakeState {\n return s.tag === 'Answering' ? { ...s, scholingThreshold } : s;\n}\n\nexport function back(s: IntakeState): IntakeState {\n if (s.tag !== 'Answering' || s.cursor === 0) return s;\n return { ...s, cursor: s.cursor - 1, errors: {} };\n}\n\nexport function submit(s: IntakeState): IntakeState {\n if (s.tag !== 'Answering') return s;\n const r = validateAll(s.answers, s.scholingThreshold);\n return r.ok ? { tag: 'Submitting', data: r.value } : { ...s, errors: r.error };\n}\n\nexport function resolve(s: IntakeState, r: Result<string, void>): IntakeState {\n if (s.tag !== 'Submitting') return s;\n return r.ok ? { tag: 'Submitted', data: s.data } : { tag: 'Failed', data: s.data, error: r.error };\n}\n\nexport type IntakeMsg =\n | { tag: 'SetAnswer'; key: keyof Answers; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed' }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'SetPolicy'; scholingThreshold: number }\n | { tag: 'Seed'; state: IntakeState };\n\nexport function reduce(s: IntakeState, m: IntakeMsg): IntakeState {\n switch (m.tag) {\n case 'SetAnswer':\n return setAnswer(s, m.key, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Failed' ? { tag: 'Submitting', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Submitting' ? { tag: 'Submitted', data: s.data } : s;\n case 'SubmitFailed':\n return s.tag === 'Submitting' ? { tag: 'Failed', data: s.data, error: m.error } : s;\n case 'SetPolicy':\n return setPolicy(s, m.scholingThreshold);\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "buitenlandGewerkt",
"deprecated": false,
"deprecationMessage": "",
"type": "JaNee",
"indexKey": "",
"optional": true,
"description": "",
"line": 22
},
{
"name": "buitenlandseUren",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 24
},
{
"name": "land",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 23
},
{
"name": "punten",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 27
},
{
"name": "scholingGevolgd",
"deprecated": false,
"deprecationMessage": "",
"type": "JaNee",
"indexKey": "",
"optional": true,
"description": "",
"line": 26
},
{
"name": "uren",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 25
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>One record carried across every step (and persisted). All optional: the user\nfills it in gradually, and branches may never ask some fields.</p>\n",
"rawdescription": "\nOne record carried across every step (and persisted). All optional: the user\nfills it in gradually, and branches may never ask some fields.",
"methods": [],
"extends": []
},
{
"name": "BigProfile",
"id": "interface-BigProfile-c363e317899ff3cd424b0033c2f57ea14e9617f52401171543b5a82d67046a894a2806afc198812808d0080b3093471e081f7e79b12f2c8c37eef68785e47a43",
"file": "src/app/registratie/domain/big-profile.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Registration } from './registration';\nimport { Person } from './person';\n\n/**\n * The view the dashboard/detail render: a registration (from the BIG-register)\n * enriched with person data (from the BRP). It only exists when BOTH sources\n * have loaded — see BigProfileStore, which builds it with map2.\n */\nexport interface BigProfile {\n registration: Registration;\n person: Person;\n}\n",
"properties": [
{
"name": "person",
"deprecated": false,
"deprecationMessage": "",
"type": "Person",
"indexKey": "",
"optional": false,
"description": "",
"line": 11
},
{
"name": "registration",
"deprecated": false,
"deprecationMessage": "",
"type": "Registration",
"indexKey": "",
"optional": false,
"description": "",
"line": 10
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>The view the dashboard/detail render: a registration (from the BIG-register)\nenriched with person data (from the BRP). It only exists when BOTH sources\nhave loaded — see BigProfileStore, which builds it with map2.</p>\n",
"rawdescription": "\n\nThe view the dashboard/detail render: a registration (from the BIG-register)\nenriched with person data (from the BRP). It only exists when BOTH sources\nhave loaded — see BigProfileStore, which builds it with map2.\n",
"methods": [],
"extends": []
},
{
"name": "BreadcrumbItem",
"id": "interface-BreadcrumbItem-4dedba5c509fabd51900b3062e33290365b01e1fdc64a11c5ce7fe12f20201b340d7fcfbe61254b0d0b7a386ab40a29795bd467542fb1a353f4d9bda46e65688",
"file": "src/app/shared/layout/breadcrumb/breadcrumb.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nexport interface BreadcrumbItem {\n label: string;\n link?: string; // omit on the current (last) page\n}\n\n/** Chrome: breadcrumb navigation. Renders the RHC/Utrecht breadcrumb pattern; the\n last item is the current page (no link, aria-current). Domain-free — the caller\n supplies the trail. */\n@Component({\n selector: 'app-breadcrumb',\n imports: [RouterLink],\n styles: [`\n :host{display:block}\n .list{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;list-style:none;margin:0;padding:0}\n .sep{color:var(--rhc-color-foreground-subtle)}\n `],\n template: `\n <nav class=\"rhc-breadcrumb-nav utrecht-breadcrumb-nav\" aria-label=\"Kruimelpad\">\n <ol class=\"utrecht-breadcrumb-nav__list list\">\n @for (item of items(); track item.label; let last = $last) {\n <li class=\"utrecht-breadcrumb-nav__item\">\n @if (item.link && !last) {\n <a class=\"utrecht-breadcrumb-nav__link rhc-link nl-link\" [routerLink]=\"item.link\">{{ item.label }}</a>\n <span class=\"sep\" aria-hidden=\"true\">/</span>\n } @else {\n <span class=\"utrecht-breadcrumb-nav__link rhc-breadcrumb-nav__link--current\" aria-current=\"page\">{{ item.label }}</span>\n }\n </li>\n }\n </ol>\n </nav>\n `,\n})\nexport class BreadcrumbComponent {\n items = input.required<BreadcrumbItem[]>();\n}\n",
"properties": [
{
"name": "label",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 5
},
{
"name": "link",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 6
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "BrpAddressDto",
"id": "interface-BrpAddressDto-029e98a059f908b1aab86b91327ec0c2a409efb766692f042935997cd36e685859ecf375e579b912496b2dac205dfd2c751f3a301b7486f0da9cb04cda7c704f",
"file": "src/app/registratie/contracts/brp-address.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface BrpAddressDto {\n gevonden: boolean;\n adres?: { straat: string; postcode: string; woonplaats: string };\n}\n",
"properties": [
{
"name": "adres",
"deprecated": false,
"deprecationMessage": "",
"type": "literal type",
"indexKey": "",
"optional": true,
"description": "",
"line": 14
},
{
"name": "gevonden",
"deprecated": false,
"deprecationMessage": "",
"type": "boolean",
"indexKey": "",
"optional": false,
"description": "",
"line": 13
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>WIRE CONTRACT for the BRP address lookup (&quot;BFF-lite&quot; — one screen-shaped call).</p>\n<p>In production this is GENERATED from the OpenAPI/TypeSpec spec and served by our\nown backend, which talks to the BRP behind an adapter. The frontend never sees\nthe BRP&#39;s own wire format. See docs/architecture/0001-bff-lite-decision-dtos.md.</p>\n<p>&quot;Geen adres bekend&quot; is a first-class outcome (<code>gevonden: false</code>), not an error —\nthe wizard falls back to manual entry (PRD §7). Slice 1 ships only the happy\npath (gevonden: true).</p>\n",
"rawdescription": "\n\nWIRE CONTRACT for the BRP address lookup (\"BFF-lite\" — one screen-shaped call).\n\nIn production this is GENERATED from the OpenAPI/TypeSpec spec and served by our\nown backend, which talks to the BRP behind an adapter. The frontend never sees\nthe BRP's own wire format. See docs/architecture/0001-bff-lite-decision-dtos.md.\n\n\"Geen adres bekend\" is a first-class outcome (`gevonden: false`), not an error —\nthe wizard falls back to manual entry (PRD §7). Slice 1 ships only the happy\npath (gevonden: true).\n",
"methods": [],
"extends": []
},
{
"name": "ChangeRequest",
"id": "interface-ChangeRequest-8d79b6a9ce75f62f4e04f9d3ed0867e00d005c9c05cf267503536c4eec9813372aff51a3c8686927fd07f46503b60ddb4107edcc5f57fdfa1b6b52fe2a98026c",
"file": "src/app/registratie/ui/change-request-form/change-request-form.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Component, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\n\n/** A submitted change request carries a *parsed* postcode (branded Postcode),\n not a raw string — downstream code can't receive an unvalidated one. */\nexport interface ChangeRequest {\n street: string;\n zip: Postcode;\n city: string;\n}\n\n/** Organism: change-request (adreswijziging) form. Reuses the same form-field\n molecule + text-input/button atoms as the login form. Field errors come\n straight from the parser's Result — no parallel \"is it valid\" flag to drift. */\n@Component({\n selector: 'app-change-request-form',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent, HeadingComponent],\n template: `\n <app-heading [level]=\"2\">Adreswijziging doorgeven</app-heading>\n <form (ngSubmit)=\"onSubmit()\" style=\"max-width:28rem\">\n <app-form-field label=\"Straat en huisnummer\" fieldId=\"street\" [error]=\"streetError()\">\n <app-text-input inputId=\"street\" [(ngModel)]=\"street\" name=\"street\" [invalid]=\"!!streetError()\" />\n </app-form-field>\n <app-form-field label=\"Postcode\" fieldId=\"zip\" [error]=\"zipError()\">\n <app-text-input inputId=\"zip\" [(ngModel)]=\"zip\" name=\"zip\" placeholder=\"1234 AB\" [invalid]=\"!!zipError()\" />\n </app-form-field>\n <app-form-field label=\"Woonplaats\" fieldId=\"city\">\n <app-text-input inputId=\"city\" [(ngModel)]=\"city\" name=\"city\" />\n </app-form-field>\n <div style=\"margin-top:1rem\">\n <app-button type=\"submit\" variant=\"primary\">Wijziging indienen</app-button>\n </div>\n </form>\n `,\n})\nexport class ChangeRequestFormComponent {\n street = '';\n zip = '';\n city = '';\n streetError = signal('');\n zipError = signal('');\n submitted = output<ChangeRequest>();\n\n onSubmit() {\n const street = this.street.trim();\n this.streetError.set(street ? '' : 'Vul straat en huisnummer in.');\n\n const postcode = parsePostcode(this.zip);\n this.zipError.set(postcode.ok ? '' : postcode.error);\n\n if (!street || !postcode.ok) return;\n this.submitted.emit({ street, zip: postcode.value, city: this.city.trim() });\n }\n}\n",
"properties": [
{
"name": "city",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 14
},
{
"name": "street",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 12
},
{
"name": "zip",
"deprecated": false,
"deprecationMessage": "",
"type": "Postcode",
"indexKey": "",
"optional": false,
"description": "",
"line": 13
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>A submitted change request carries a <em>parsed</em> postcode (branded Postcode),\nnot a raw string — downstream code can&#39;t receive an unvalidated one.</p>\n",
"rawdescription": "\nA submitted change request carries a *parsed* postcode (branded Postcode),\nnot a raw string — downstream code can't receive an unvalidated one.",
"methods": [],
"extends": []
},
{
"name": "DashboardView",
"id": "interface-DashboardView-b39e266ec8fabdaddad87f1e15a5b1b86e45d84ac2c95028b4e676c2eb425fd675c77383c3d4f8f88f4a072f3eb88a954eb0f8755597224ebef59da18d24fda0",
"file": "src/app/registratie/contracts/dashboard-view.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Registration } from '@registratie/domain/registration';\nimport { Person } from '@registratie/domain/person';\nimport { BigProfile } from '@registratie/domain/big-profile';\n\n/**\n * WIRE CONTRACT for the dashboard screen — the \"BFF-lite\" response.\n *\n * In production this type is GENERATED from the OpenAPI/TypeSpec spec (one source\n * of truth for both sides), and the `decisions` block is computed BY THE BACKEND\n * — never recomputed on the client. The frontend renders decisions; it does not\n * own the rules. See docs/architecture/0001-bff-lite-decision-dtos.md.\n *\n * One screen-shaped call replaces the previous three (BIG-register + BRP + …),\n * so the page always sees one consistent snapshot instead of three independently\n * loading/erroring resources.\n */\nexport interface DashboardViewDto {\n registration: Registration;\n person: Person;\n decisions: HerregistratieDecisions;\n}\n\n/** Server-computed decisions. The eligibility rule lives on the backend; the\n optional reason lets the UI explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\n/** The parsed, frontend-side view (DTO mapped onto our own domain model). Keeping\n this distinct from DashboardViewDto is the decoupling seam: the wire shape can\n change without the FE domain following, and vice-versa. */\nexport interface DashboardView {\n profile: BigProfile;\n decisions: HerregistratieDecisions;\n}\n",
"properties": [
{
"name": "decisions",
"deprecated": false,
"deprecationMessage": "",
"type": "HerregistratieDecisions",
"indexKey": "",
"optional": false,
"description": "",
"line": 35
},
{
"name": "profile",
"deprecated": false,
"deprecationMessage": "",
"type": "BigProfile",
"indexKey": "",
"optional": false,
"description": "",
"line": 34
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>The parsed, frontend-side view (DTO mapped onto our own domain model). Keeping\nthis distinct from DashboardViewDto is the decoupling seam: the wire shape can\nchange without the FE domain following, and vice-versa.</p>\n",
"rawdescription": "\nThe parsed, frontend-side view (DTO mapped onto our own domain model). Keeping\nthis distinct from DashboardViewDto is the decoupling seam: the wire shape can\nchange without the FE domain following, and vice-versa.",
"methods": [],
"extends": []
},
{
"name": "DashboardViewDto",
"id": "interface-DashboardViewDto-b39e266ec8fabdaddad87f1e15a5b1b86e45d84ac2c95028b4e676c2eb425fd675c77383c3d4f8f88f4a072f3eb88a954eb0f8755597224ebef59da18d24fda0",
"file": "src/app/registratie/contracts/dashboard-view.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Registration } from '@registratie/domain/registration';\nimport { Person } from '@registratie/domain/person';\nimport { BigProfile } from '@registratie/domain/big-profile';\n\n/**\n * WIRE CONTRACT for the dashboard screen — the \"BFF-lite\" response.\n *\n * In production this type is GENERATED from the OpenAPI/TypeSpec spec (one source\n * of truth for both sides), and the `decisions` block is computed BY THE BACKEND\n * — never recomputed on the client. The frontend renders decisions; it does not\n * own the rules. See docs/architecture/0001-bff-lite-decision-dtos.md.\n *\n * One screen-shaped call replaces the previous three (BIG-register + BRP + …),\n * so the page always sees one consistent snapshot instead of three independently\n * loading/erroring resources.\n */\nexport interface DashboardViewDto {\n registration: Registration;\n person: Person;\n decisions: HerregistratieDecisions;\n}\n\n/** Server-computed decisions. The eligibility rule lives on the backend; the\n optional reason lets the UI explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\n/** The parsed, frontend-side view (DTO mapped onto our own domain model). Keeping\n this distinct from DashboardViewDto is the decoupling seam: the wire shape can\n change without the FE domain following, and vice-versa. */\nexport interface DashboardView {\n profile: BigProfile;\n decisions: HerregistratieDecisions;\n}\n",
"properties": [
{
"name": "decisions",
"deprecated": false,
"deprecationMessage": "",
"type": "HerregistratieDecisions",
"indexKey": "",
"optional": false,
"description": "",
"line": 20
},
{
"name": "person",
"deprecated": false,
"deprecationMessage": "",
"type": "Person",
"indexKey": "",
"optional": false,
"description": "",
"line": 19
},
{
"name": "registration",
"deprecated": false,
"deprecationMessage": "",
"type": "Registration",
"indexKey": "",
"optional": false,
"description": "",
"line": 18
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>WIRE CONTRACT for the dashboard screen — the &quot;BFF-lite&quot; response.</p>\n<p>In production this type is GENERATED from the OpenAPI/TypeSpec spec (one source\nof truth for both sides), and the <code>decisions</code> block is computed BY THE BACKEND\n— never recomputed on the client. The frontend renders decisions; it does not\nown the rules. See docs/architecture/0001-bff-lite-decision-dtos.md.</p>\n<p>One screen-shaped call replaces the previous three (BIG-register + BRP + …),\nso the page always sees one consistent snapshot instead of three independently\nloading/erroring resources.</p>\n",
"rawdescription": "\n\nWIRE CONTRACT for the dashboard screen — the \"BFF-lite\" response.\n\nIn production this type is GENERATED from the OpenAPI/TypeSpec spec (one source\nof truth for both sides), and the `decisions` block is computed BY THE BACKEND\n— never recomputed on the client. The frontend renders decisions; it does not\nown the rules. See docs/architecture/0001-bff-lite-decision-dtos.md.\n\nOne screen-shaped call replaces the previous three (BIG-register + BRP + …),\nso the page always sees one consistent snapshot instead of three independently\nloading/erroring resources.\n",
"methods": [],
"extends": []
},
{
"name": "Draft",
"id": "interface-Draft-097a742aa05bc7bbfee387dd3ea5b2ac382815db8ca18107e699d11e6da110cdeae2eb377bcea284f415cfd263937c3b0f6e7ed2ee9090d660d898dddefe3a4f",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, assertNever } from '@shared/kernel/fp';\nimport { Uren, parseUren } from '@registratie/domain/value-objects/uren';\n\n/** What the user is typing (raw, possibly invalid). */\nexport interface Draft {\n uren: string;\n jaren: string;\n punten: string;\n}\n\n/** What we have AFTER parsing — branded/typed, guaranteed valid. */\nexport interface Valid {\n uren: Uren;\n jaren: number;\n punten: number;\n}\n\n/**\n * The whole wizard as one tagged union. `step` and `errors` exist ONLY while\n * Editing; Submitting/Submitted/Failed carry a `Valid` payload and nothing else.\n * So \"submitting while a field is invalid\" or \"showing the success screen with\n * errors set\" are unrepresentable — the bug class is gone by construction.\n */\nexport type WizardState =\n | { tag: 'Editing'; step: 1 | 2; draft: Draft; errors: Partial<Record<keyof Draft, string>> }\n | { tag: 'Submitting'; data: Valid }\n | { tag: 'Submitted'; data: Valid }\n | { tag: 'Failed'; data: Valid; error: string };\n\nexport const initial: WizardState = { tag: 'Editing', step: 1, draft: { uren: '', jaren: '', punten: '' }, errors: {} };\n\n/** Parse every field; on success hand back a Valid, else the per-field errors. */\nfunction validate(draft: Draft): Result<Partial<Record<keyof Draft, string>>, Valid> {\n const uren = parseUren(draft.uren);\n const jaren = parseUren(draft.jaren);\n const punten = parseUren(draft.punten);\n const errors: Partial<Record<keyof Draft, string>> = {};\n if (!uren.ok) errors.uren = uren.error;\n if (!jaren.ok) errors.jaren = jaren.error;\n if (!punten.ok) errors.punten = punten.error;\n if (uren.ok && jaren.ok && punten.ok) {\n return { ok: true, value: { uren: uren.value, jaren: jaren.value, punten: punten.value } };\n }\n return { ok: false, error: errors };\n}\n\n/** Step 1 → 2: advance only when BOTH step-1 fields parse. Illegal elsewhere = no-op. */\nexport function next(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 1) return s;\n const uren = parseUren(s.draft.uren);\n const jaren = parseUren(s.draft.jaren);\n const errors: Partial<Record<keyof Draft, string>> = {};\n if (!uren.ok) errors.uren = uren.error;\n if (!jaren.ok) errors.jaren = jaren.error;\n return Object.keys(errors).length === 0 ? { ...s, step: 2, errors: {} } : { ...s, errors };\n}\n\nexport function back(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 2) return s;\n return { ...s, step: 1, errors: {} };\n}\n\n/** Step 2 submit: parse everything; move to Submitting only with Valid data. */\nexport function submit(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 2) return s;\n const result = validate(s.draft);\n return result.ok ? { tag: 'Submitting', data: result.value } : { ...s, errors: result.error };\n}\n\n/** Resolve the async submit. Only meaningful while Submitting. */\nexport function resolve(s: WizardState, r: Result<string, void>): WizardState {\n if (s.tag !== 'Submitting') return s;\n return r.ok ? { tag: 'Submitted', data: s.data } : { tag: 'Failed', data: s.data, error: r.error };\n}\n\n/** Update one draft field while editing; ignored in any other state. */\nexport function setField(s: WizardState, key: keyof Draft, value: string): WizardState {\n if (s.tag !== 'Editing') return s;\n return { ...s, draft: { ...s.draft, [key]: value } };\n}\n\n/**\n * Every event that can happen to the wizard, as one message type. The component\n * sends a WizardMsg; `reduce` decides the next state. This is the Elm\n * Model+Msg+update pattern: ONE pure function describes all state changes.\n */\nexport type WizardMsg =\n | { tag: 'SetField'; key: keyof Draft; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed' }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Seed'; state: WizardState }; // mount a specific state (stories/showcase)\n\nexport function reduce(s: WizardState, m: WizardMsg): WizardState {\n switch (m.tag) {\n case 'SetField':\n return setField(s, m.key, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Failed' ? { tag: 'Submitting', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Submitting' ? { tag: 'Submitted', data: s.data } : s;\n case 'SubmitFailed':\n return s.tag === 'Submitting' ? { tag: 'Failed', data: s.data, error: m.error } : s;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "jaren",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 7
},
{
"name": "punten",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 8
},
{
"name": "uren",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 6
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>What the user is typing (raw, possibly invalid).</p>\n",
"rawdescription": "\nWhat the user is typing (raw, possibly invalid).",
"methods": [],
"extends": []
},
{
"name": "Draft",
"id": "interface-Draft-6ed41f6a28b6c00dc907f54219be9c1d7955aa11bc22f128620d2206b1ea12e6730533ac33f5f5df7b8d7c8d7f977584b3a0a3e98a6c2c3b91d1afe6411278e2-1",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, ok, err, assertNever } from '@shared/kernel/fp';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\nimport { Email, parseEmail } from '@registratie/domain/value-objects/email';\n\n/**\n * A FIXED 3-step registration wizard. The steps never change in number (always\n * `STEPS`): (1) adres + correspondentievoorkeur, (2) beroep o.b.v. diploma,\n * (3) controle & indienen. Follow-up questions appear *inline within a step*\n * (e.g. choosing 'email' reveals the e-mail field). \"Is this field required\n * right now\" is a pure function (`validateStep`), so it is trivial to test and\n * impossible to get out of sync with the data. Invariants live here, not in the\n * UI: the wizard reaches `Indienen` only when a complete `ValidRegistratie` parses.\n */\n\nexport type StepId = 'adres' | 'beroep' | 'controle';\n\n/** The fixed step list. Number of steps never changes; questions reveal inline. */\nexport const STEPS: StepId[] = ['adres', 'beroep', 'controle'];\n\n/** Where a piece of data came from — recorded on the aggregate (PRD §5). */\nexport type AdresHerkomst = 'brp' | 'handmatig';\nexport type DiplomaHerkomst = 'duo' | 'handmatig';\nexport type Correspondentie = 'email' | 'post';\n\n/** One record carried across every step (and persisted). All optional: the user\n fills it in gradually. Adres fields are kept flat so one `SetField` message\n serves them all (mirrors the intake machine). */\nexport interface Draft {\n straat?: string;\n postcode?: string;\n woonplaats?: string;\n adresHerkomst?: AdresHerkomst;\n correspondentie?: Correspondentie;\n email?: string;\n diplomaId?: string;\n diplomaHerkomst?: DiplomaHerkomst;\n beroep?: string; // DERIVED from the chosen DUO diploma (or declared for a manual one)\n vraagIds?: string[]; // ids of the policy questions that apply to the chosen diploma\n antwoorden: Record<string, string>; // geldigheidsantwoorden, keyed by question id\n}\n\n/** What we have after the controle step parses — guaranteed valid/typed. */\nexport interface ValidRegistratie {\n adres: { straat: string; postcode: Postcode; woonplaats: string };\n adresHerkomst: AdresHerkomst;\n correspondentie: Correspondentie;\n email?: Email; // only when correspondentie === 'email'\n diplomaId: string;\n diplomaHerkomst: DiplomaHerkomst;\n beroep: string;\n antwoorden: Record<string, string>;\n}\n\n/** Text fields settable via SetField. */\nexport type DraftField = 'straat' | 'postcode' | 'woonplaats' | 'email';\n\n/** Per-field error map. `antwoorden` holds per-policy-question errors, keyed by\n question id (a step can show several questions). */\nexport interface Errors {\n straat?: string;\n postcode?: string;\n woonplaats?: string;\n email?: string;\n correspondentie?: string;\n diploma?: string;\n antwoorden?: Record<string, string>;\n}\n\nexport type RegistratieState =\n | { tag: 'Invullen'; draft: Draft; cursor: number; errors: Errors }\n | { tag: 'Indienen'; data: ValidRegistratie }\n | { tag: 'Ingediend'; data: ValidRegistratie; referentie: string }\n | { tag: 'Mislukt'; data: ValidRegistratie; error: string };\n\nconst emptyDraft: Draft = { antwoorden: {} };\nexport const initial: RegistratieState = { tag: 'Invullen', draft: emptyDraft, cursor: 0, errors: {} };\n\n/** Which step the cursor currently points at (clamped to the fixed list). */\nexport function currentStep(s: Extract<RegistratieState, { tag: 'Invullen' }>): StepId {\n return STEPS[Math.min(s.cursor, STEPS.length - 1)];\n}\n\n/** Validate every question currently visible in ONE step. Errors keyed per field. */\nfunction validateStep(step: StepId, d: Draft): Result<Errors, void> {\n const errors: Errors = {};\n switch (step) {\n case 'adres': {\n if (!d.straat || d.straat.trim() === '') errors.straat = 'Vul een straat en huisnummer in.';\n const pc = parsePostcode(d.postcode ?? '');\n if (!pc.ok) errors.postcode = pc.error;\n if (!d.woonplaats || d.woonplaats.trim() === '') errors.woonplaats = 'Vul een woonplaats in.';\n if (!d.correspondentie) errors.correspondentie = 'Maak een keuze.';\n // E-mail is only required when 'email' is the chosen channel.\n if (d.correspondentie === 'email') {\n const e = parseEmail(d.email ?? '');\n if (!e.ok) errors.email = e.error;\n }\n break;\n }\n case 'beroep': {\n // A diploma must be chosen (or declared manually); its beroep is then known.\n if (!d.diplomaId || !d.beroep) {\n errors.diploma = 'Kies het diploma waarmee u zich wilt registreren, of voer het handmatig in.';\n break;\n }\n // Every policy question the chosen diploma raised must be answered. Which\n // questions apply is server-decided (carried in `vraagIds`); we only check\n // they're answered.\n const open: Record<string, string> = {};\n for (const id of d.vraagIds ?? []) {\n if (!(d.antwoorden[id] ?? '').trim()) open[id] = 'Beantwoord deze vraag.';\n }\n if (Object.keys(open).length > 0) errors.antwoorden = open;\n break;\n }\n case 'controle':\n break; // controle shows a summary; no own fields\n default:\n return assertNever(step);\n }\n return Object.keys(errors).length > 0 ? err(errors) : ok(undefined);\n}\n\n/** Parse the whole wizard into a ValidRegistratie (called on submit). */\nfunction validateAll(d: Draft): Result<Errors, ValidRegistratie> {\n const errors: Errors = {};\n for (const step of STEPS) {\n const r = validateStep(step, d);\n if (!r.ok) Object.assign(errors, r.error);\n }\n if (Object.keys(errors).length > 0) return err(errors);\n\n const pc = parsePostcode(d.postcode ?? '');\n // validateStep guaranteed these parse, but keep the compiler happy.\n if (!pc.ok || !d.diplomaId || !d.beroep || !d.correspondentie) return err(errors);\n const email = d.correspondentie === 'email' ? parseEmail(d.email ?? '') : undefined;\n // Keep only the answers to the questions that actually applied.\n const vraagIds = d.vraagIds ?? [];\n const antwoorden = Object.fromEntries(vraagIds.map((id) => [id, d.antwoorden[id] ?? '']));\n\n return ok({\n adres: { straat: d.straat!, postcode: pc.value, woonplaats: d.woonplaats! },\n adresHerkomst: d.adresHerkomst ?? 'handmatig',\n correspondentie: d.correspondentie,\n email: email?.ok ? email.value : undefined,\n diplomaId: d.diplomaId,\n diplomaHerkomst: d.diplomaHerkomst ?? 'handmatig',\n beroep: d.beroep,\n antwoorden,\n });\n}\n\nexport function setField(s: RegistratieState, key: DraftField, value: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const draft: Draft = { ...s.draft, [key]: value };\n // Editing an address field means the user owns it now — not the BRP copy.\n if (key === 'straat' || key === 'postcode' || key === 'woonplaats') draft.adresHerkomst = 'handmatig';\n return { ...s, draft };\n}\n\nexport function setCorrespondentie(s: RegistratieState, value: Correspondentie): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, correspondentie: value } };\n}\n\n/** Prefill the address from a BRP lookup and flag its origin (PRD §7). */\nexport function prefillAdres(s: RegistratieState, straat: string, postcode: string, woonplaats: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, straat, postcode, woonplaats, adresHerkomst: 'brp' } };\n}\n\n/** Pick a DUO diploma; the beroep is derived from it and the applicable policy\n questions (`vraagIds`) come with it (both server-computed, passed in). */\nexport function kiesDiploma(s: RegistratieState, diplomaId: string, beroep: string, vraagIds: string[]): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, diplomaId, beroep, vraagIds, diplomaHerkomst: 'duo' }, errors: {} };\n}\n\n/** Switch to manual diploma entry: the diploma isn't in DUO, so the MAXIMAL\n policy-question set applies and the entry is flagged handmatig/unverified. The\n beroep is declared separately (declareerBeroep). */\nexport function kiesHandmatig(s: RegistratieState, vraagIds: string[]): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, diplomaId: 'handmatig', beroep: undefined, vraagIds, diplomaHerkomst: 'handmatig' }, errors: {} };\n}\n\n/** Declare the beroep for a manually-entered diploma (chosen from a fixed list). */\nexport function declareerBeroep(s: RegistratieState, beroep: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, beroep } };\n}\n\nexport function setAntwoord(s: RegistratieState, vraagId: string, value: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, antwoorden: { ...s.draft.antwoorden, [vraagId]: value } } };\n}\n\nexport function next(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const r = validateStep(currentStep(s), s.draft);\n if (!r.ok) return { ...s, errors: r.error };\n return { ...s, cursor: Math.min(s.cursor + 1, STEPS.length - 1), errors: {} };\n}\n\nexport function back(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen' || s.cursor === 0) return s;\n return { ...s, cursor: s.cursor - 1, errors: {} };\n}\n\n/** Jump back to an earlier step to correct data (controle → step N). Forward\n jumps are not allowed (would skip validation). Preserves the draft. */\nexport function gaNaarStap(s: RegistratieState, cursor: number): RegistratieState {\n if (s.tag !== 'Invullen' || cursor < 0 || cursor >= s.cursor) return s;\n return { ...s, cursor, errors: {} };\n}\n\nexport function submit(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const r = validateAll(s.draft);\n return r.ok ? { tag: 'Indienen', data: r.value } : { ...s, errors: r.error };\n}\n\nexport function resolve(s: RegistratieState, r: Result<string, string>): RegistratieState {\n if (s.tag !== 'Indienen') return s;\n return r.ok ? { tag: 'Ingediend', data: s.data, referentie: r.value } : { tag: 'Mislukt', data: s.data, error: r.error };\n}\n\nexport type RegistratieMsg =\n | { tag: 'SetField'; key: DraftField; value: string }\n | { tag: 'SetCorrespondentie'; value: Correspondentie }\n | { tag: 'PrefillAdres'; straat: string; postcode: string; woonplaats: string }\n | { tag: 'KiesDiploma'; diplomaId: string; beroep: string; vraagIds: string[] }\n | { tag: 'KiesHandmatig'; vraagIds: string[] }\n | { tag: 'DeclareerBeroep'; beroep: string }\n | { tag: 'SetAntwoord'; vraagId: string; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'GaNaarStap'; cursor: number }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed'; referentie: string }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Seed'; state: RegistratieState };\n\nexport function reduce(s: RegistratieState, m: RegistratieMsg): RegistratieState {\n switch (m.tag) {\n case 'SetField':\n return setField(s, m.key, m.value);\n case 'SetCorrespondentie':\n return setCorrespondentie(s, m.value);\n case 'PrefillAdres':\n return prefillAdres(s, m.straat, m.postcode, m.woonplaats);\n case 'KiesDiploma':\n return kiesDiploma(s, m.diplomaId, m.beroep, m.vraagIds);\n case 'KiesHandmatig':\n return kiesHandmatig(s, m.vraagIds);\n case 'DeclareerBeroep':\n return declareerBeroep(s, m.beroep);\n case 'SetAntwoord':\n return setAntwoord(s, m.vraagId, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'GaNaarStap':\n return gaNaarStap(s, m.cursor);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Mislukt' ? { tag: 'Indienen', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Indienen' ? { tag: 'Ingediend', data: s.data, referentie: m.referentie } : s;\n case 'SubmitFailed':\n return s.tag === 'Indienen' ? { tag: 'Mislukt', data: s.data, error: m.error } : s;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "adresHerkomst",
"deprecated": false,
"deprecationMessage": "",
"type": "AdresHerkomst",
"indexKey": "",
"optional": true,
"description": "",
"line": 32
},
{
"name": "antwoorden",
"deprecated": false,
"deprecationMessage": "",
"type": "Record<string | string>",
"indexKey": "",
"optional": false,
"description": "",
"line": 39
},
{
"name": "beroep",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 37
},
{
"name": "correspondentie",
"deprecated": false,
"deprecationMessage": "",
"type": "Correspondentie",
"indexKey": "",
"optional": true,
"description": "",
"line": 33
},
{
"name": "diplomaHerkomst",
"deprecated": false,
"deprecationMessage": "",
"type": "DiplomaHerkomst",
"indexKey": "",
"optional": true,
"description": "",
"line": 36
},
{
"name": "diplomaId",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 35
},
{
"name": "email",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 34
},
{
"name": "postcode",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 30
},
{
"name": "straat",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 29
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": "",
"type": "string[]",
"indexKey": "",
"optional": true,
"description": "",
"line": 38
},
{
"name": "woonplaats",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 31
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>One record carried across every step (and persisted). All optional: the user\nfills it in gradually. Adres fields are kept flat so one <code>SetField</code> message\nserves them all (mirrors the intake machine).</p>\n",
"rawdescription": "\nOne record carried across every step (and persisted). All optional: the user\nfills it in gradually. Adres fields are kept flat so one `SetField` message\nserves them all (mirrors the intake machine).",
"methods": [],
"extends": [],
"isDuplicate": true,
"duplicateId": 1,
"duplicateName": "Draft-1"
},
{
"name": "DuoDiplomaDto",
"id": "interface-DuoDiplomaDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1",
"file": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface DuoLookupDto {\n diplomas: DuoDiplomaDto[];\n handmatig: ManualDiplomaPolicyDto;\n}\n\nexport interface DuoDiplomaDto {\n id: string;\n naam: string;\n instelling: string;\n jaar: number;\n beroep: string; // server-derived profession\n policyQuestions: PolicyQuestionDto[]; // server-decided geldigheidsvragen\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen: string[]; // professions the user may declare for a manual diploma\n policyQuestions: PolicyQuestionDto[]; // maximal set applied to a manual diploma\n}\n\nexport interface PolicyQuestionDto {\n id: string;\n vraag: string;\n type: 'ja-nee' | 'tekst';\n}\n",
"properties": [
{
"name": "beroep",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 25
},
{
"name": "id",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 21
},
{
"name": "instelling",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 23
},
{
"name": "jaar",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"indexKey": "",
"optional": false,
"description": "",
"line": 24
},
{
"name": "naam",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 22
},
{
"name": "policyQuestions",
"deprecated": false,
"deprecationMessage": "",
"type": "PolicyQuestionDto[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 26
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "DuoLookupDto",
"id": "interface-DuoLookupDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1",
"file": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface DuoLookupDto {\n diplomas: DuoDiplomaDto[];\n handmatig: ManualDiplomaPolicyDto;\n}\n\nexport interface DuoDiplomaDto {\n id: string;\n naam: string;\n instelling: string;\n jaar: number;\n beroep: string; // server-derived profession\n policyQuestions: PolicyQuestionDto[]; // server-decided geldigheidsvragen\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen: string[]; // professions the user may declare for a manual diploma\n policyQuestions: PolicyQuestionDto[]; // maximal set applied to a manual diploma\n}\n\nexport interface PolicyQuestionDto {\n id: string;\n vraag: string;\n type: 'ja-nee' | 'tekst';\n}\n",
"properties": [
{
"name": "diplomas",
"deprecated": false,
"deprecationMessage": "",
"type": "DuoDiplomaDto[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 16
},
{
"name": "handmatig",
"deprecated": false,
"deprecationMessage": "",
"type": "ManualDiplomaPolicyDto",
"indexKey": "",
"optional": false,
"description": "",
"line": 17
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>WIRE CONTRACT for the DUO diploma lookup (&quot;BFF-lite&quot; — one screen-shaped call\nreturning everything the beroep step needs).</p>\n<p>Each diploma carries its server-computed <code>beroep</code> (the profession it maps to)\nand the <code>policyQuestions</code> (geldigheidsvragen) that apply to it. These are\nDECISIONS computed by the backend from the diploma&#39;s attributes — the frontend\nrenders them, it does not derive them (decision-DTO pattern, ADR-0001). E.g. an\nEnglish-language diploma carries the Dutch-proficiency question.</p>\n<p><code>handmatig</code> is the fallback when the diploma is not in the DUO list: the\nprofessions the user may declare and the MAXIMAL policy-question set that then\napplies (a manual diploma is unverified, so the strictest set is used).</p>\n",
"rawdescription": "\n\nWIRE CONTRACT for the DUO diploma lookup (\"BFF-lite\" — one screen-shaped call\nreturning everything the beroep step needs).\n\nEach diploma carries its server-computed `beroep` (the profession it maps to)\nand the `policyQuestions` (geldigheidsvragen) that apply to it. These are\nDECISIONS computed by the backend from the diploma's attributes — the frontend\nrenders them, it does not derive them (decision-DTO pattern, ADR-0001). E.g. an\nEnglish-language diploma carries the Dutch-proficiency question.\n\n`handmatig` is the fallback when the diploma is not in the DUO list: the\nprofessions the user may declare and the MAXIMAL policy-question set that then\napplies (a manual diploma is unverified, so the strictest set is used).\n",
"methods": [],
"extends": []
},
{
"name": "Errors",
"id": "interface-Errors-6ed41f6a28b6c00dc907f54219be9c1d7955aa11bc22f128620d2206b1ea12e6730533ac33f5f5df7b8d7c8d7f977584b3a0a3e98a6c2c3b91d1afe6411278e2",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, ok, err, assertNever } from '@shared/kernel/fp';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\nimport { Email, parseEmail } from '@registratie/domain/value-objects/email';\n\n/**\n * A FIXED 3-step registration wizard. The steps never change in number (always\n * `STEPS`): (1) adres + correspondentievoorkeur, (2) beroep o.b.v. diploma,\n * (3) controle & indienen. Follow-up questions appear *inline within a step*\n * (e.g. choosing 'email' reveals the e-mail field). \"Is this field required\n * right now\" is a pure function (`validateStep`), so it is trivial to test and\n * impossible to get out of sync with the data. Invariants live here, not in the\n * UI: the wizard reaches `Indienen` only when a complete `ValidRegistratie` parses.\n */\n\nexport type StepId = 'adres' | 'beroep' | 'controle';\n\n/** The fixed step list. Number of steps never changes; questions reveal inline. */\nexport const STEPS: StepId[] = ['adres', 'beroep', 'controle'];\n\n/** Where a piece of data came from — recorded on the aggregate (PRD §5). */\nexport type AdresHerkomst = 'brp' | 'handmatig';\nexport type DiplomaHerkomst = 'duo' | 'handmatig';\nexport type Correspondentie = 'email' | 'post';\n\n/** One record carried across every step (and persisted). All optional: the user\n fills it in gradually. Adres fields are kept flat so one `SetField` message\n serves them all (mirrors the intake machine). */\nexport interface Draft {\n straat?: string;\n postcode?: string;\n woonplaats?: string;\n adresHerkomst?: AdresHerkomst;\n correspondentie?: Correspondentie;\n email?: string;\n diplomaId?: string;\n diplomaHerkomst?: DiplomaHerkomst;\n beroep?: string; // DERIVED from the chosen DUO diploma (or declared for a manual one)\n vraagIds?: string[]; // ids of the policy questions that apply to the chosen diploma\n antwoorden: Record<string, string>; // geldigheidsantwoorden, keyed by question id\n}\n\n/** What we have after the controle step parses — guaranteed valid/typed. */\nexport interface ValidRegistratie {\n adres: { straat: string; postcode: Postcode; woonplaats: string };\n adresHerkomst: AdresHerkomst;\n correspondentie: Correspondentie;\n email?: Email; // only when correspondentie === 'email'\n diplomaId: string;\n diplomaHerkomst: DiplomaHerkomst;\n beroep: string;\n antwoorden: Record<string, string>;\n}\n\n/** Text fields settable via SetField. */\nexport type DraftField = 'straat' | 'postcode' | 'woonplaats' | 'email';\n\n/** Per-field error map. `antwoorden` holds per-policy-question errors, keyed by\n question id (a step can show several questions). */\nexport interface Errors {\n straat?: string;\n postcode?: string;\n woonplaats?: string;\n email?: string;\n correspondentie?: string;\n diploma?: string;\n antwoorden?: Record<string, string>;\n}\n\nexport type RegistratieState =\n | { tag: 'Invullen'; draft: Draft; cursor: number; errors: Errors }\n | { tag: 'Indienen'; data: ValidRegistratie }\n | { tag: 'Ingediend'; data: ValidRegistratie; referentie: string }\n | { tag: 'Mislukt'; data: ValidRegistratie; error: string };\n\nconst emptyDraft: Draft = { antwoorden: {} };\nexport const initial: RegistratieState = { tag: 'Invullen', draft: emptyDraft, cursor: 0, errors: {} };\n\n/** Which step the cursor currently points at (clamped to the fixed list). */\nexport function currentStep(s: Extract<RegistratieState, { tag: 'Invullen' }>): StepId {\n return STEPS[Math.min(s.cursor, STEPS.length - 1)];\n}\n\n/** Validate every question currently visible in ONE step. Errors keyed per field. */\nfunction validateStep(step: StepId, d: Draft): Result<Errors, void> {\n const errors: Errors = {};\n switch (step) {\n case 'adres': {\n if (!d.straat || d.straat.trim() === '') errors.straat = 'Vul een straat en huisnummer in.';\n const pc = parsePostcode(d.postcode ?? '');\n if (!pc.ok) errors.postcode = pc.error;\n if (!d.woonplaats || d.woonplaats.trim() === '') errors.woonplaats = 'Vul een woonplaats in.';\n if (!d.correspondentie) errors.correspondentie = 'Maak een keuze.';\n // E-mail is only required when 'email' is the chosen channel.\n if (d.correspondentie === 'email') {\n const e = parseEmail(d.email ?? '');\n if (!e.ok) errors.email = e.error;\n }\n break;\n }\n case 'beroep': {\n // A diploma must be chosen (or declared manually); its beroep is then known.\n if (!d.diplomaId || !d.beroep) {\n errors.diploma = 'Kies het diploma waarmee u zich wilt registreren, of voer het handmatig in.';\n break;\n }\n // Every policy question the chosen diploma raised must be answered. Which\n // questions apply is server-decided (carried in `vraagIds`); we only check\n // they're answered.\n const open: Record<string, string> = {};\n for (const id of d.vraagIds ?? []) {\n if (!(d.antwoorden[id] ?? '').trim()) open[id] = 'Beantwoord deze vraag.';\n }\n if (Object.keys(open).length > 0) errors.antwoorden = open;\n break;\n }\n case 'controle':\n break; // controle shows a summary; no own fields\n default:\n return assertNever(step);\n }\n return Object.keys(errors).length > 0 ? err(errors) : ok(undefined);\n}\n\n/** Parse the whole wizard into a ValidRegistratie (called on submit). */\nfunction validateAll(d: Draft): Result<Errors, ValidRegistratie> {\n const errors: Errors = {};\n for (const step of STEPS) {\n const r = validateStep(step, d);\n if (!r.ok) Object.assign(errors, r.error);\n }\n if (Object.keys(errors).length > 0) return err(errors);\n\n const pc = parsePostcode(d.postcode ?? '');\n // validateStep guaranteed these parse, but keep the compiler happy.\n if (!pc.ok || !d.diplomaId || !d.beroep || !d.correspondentie) return err(errors);\n const email = d.correspondentie === 'email' ? parseEmail(d.email ?? '') : undefined;\n // Keep only the answers to the questions that actually applied.\n const vraagIds = d.vraagIds ?? [];\n const antwoorden = Object.fromEntries(vraagIds.map((id) => [id, d.antwoorden[id] ?? '']));\n\n return ok({\n adres: { straat: d.straat!, postcode: pc.value, woonplaats: d.woonplaats! },\n adresHerkomst: d.adresHerkomst ?? 'handmatig',\n correspondentie: d.correspondentie,\n email: email?.ok ? email.value : undefined,\n diplomaId: d.diplomaId,\n diplomaHerkomst: d.diplomaHerkomst ?? 'handmatig',\n beroep: d.beroep,\n antwoorden,\n });\n}\n\nexport function setField(s: RegistratieState, key: DraftField, value: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const draft: Draft = { ...s.draft, [key]: value };\n // Editing an address field means the user owns it now — not the BRP copy.\n if (key === 'straat' || key === 'postcode' || key === 'woonplaats') draft.adresHerkomst = 'handmatig';\n return { ...s, draft };\n}\n\nexport function setCorrespondentie(s: RegistratieState, value: Correspondentie): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, correspondentie: value } };\n}\n\n/** Prefill the address from a BRP lookup and flag its origin (PRD §7). */\nexport function prefillAdres(s: RegistratieState, straat: string, postcode: string, woonplaats: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, straat, postcode, woonplaats, adresHerkomst: 'brp' } };\n}\n\n/** Pick a DUO diploma; the beroep is derived from it and the applicable policy\n questions (`vraagIds`) come with it (both server-computed, passed in). */\nexport function kiesDiploma(s: RegistratieState, diplomaId: string, beroep: string, vraagIds: string[]): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, diplomaId, beroep, vraagIds, diplomaHerkomst: 'duo' }, errors: {} };\n}\n\n/** Switch to manual diploma entry: the diploma isn't in DUO, so the MAXIMAL\n policy-question set applies and the entry is flagged handmatig/unverified. The\n beroep is declared separately (declareerBeroep). */\nexport function kiesHandmatig(s: RegistratieState, vraagIds: string[]): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, diplomaId: 'handmatig', beroep: undefined, vraagIds, diplomaHerkomst: 'handmatig' }, errors: {} };\n}\n\n/** Declare the beroep for a manually-entered diploma (chosen from a fixed list). */\nexport function declareerBeroep(s: RegistratieState, beroep: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, beroep } };\n}\n\nexport function setAntwoord(s: RegistratieState, vraagId: string, value: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, antwoorden: { ...s.draft.antwoorden, [vraagId]: value } } };\n}\n\nexport function next(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const r = validateStep(currentStep(s), s.draft);\n if (!r.ok) return { ...s, errors: r.error };\n return { ...s, cursor: Math.min(s.cursor + 1, STEPS.length - 1), errors: {} };\n}\n\nexport function back(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen' || s.cursor === 0) return s;\n return { ...s, cursor: s.cursor - 1, errors: {} };\n}\n\n/** Jump back to an earlier step to correct data (controle → step N). Forward\n jumps are not allowed (would skip validation). Preserves the draft. */\nexport function gaNaarStap(s: RegistratieState, cursor: number): RegistratieState {\n if (s.tag !== 'Invullen' || cursor < 0 || cursor >= s.cursor) return s;\n return { ...s, cursor, errors: {} };\n}\n\nexport function submit(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const r = validateAll(s.draft);\n return r.ok ? { tag: 'Indienen', data: r.value } : { ...s, errors: r.error };\n}\n\nexport function resolve(s: RegistratieState, r: Result<string, string>): RegistratieState {\n if (s.tag !== 'Indienen') return s;\n return r.ok ? { tag: 'Ingediend', data: s.data, referentie: r.value } : { tag: 'Mislukt', data: s.data, error: r.error };\n}\n\nexport type RegistratieMsg =\n | { tag: 'SetField'; key: DraftField; value: string }\n | { tag: 'SetCorrespondentie'; value: Correspondentie }\n | { tag: 'PrefillAdres'; straat: string; postcode: string; woonplaats: string }\n | { tag: 'KiesDiploma'; diplomaId: string; beroep: string; vraagIds: string[] }\n | { tag: 'KiesHandmatig'; vraagIds: string[] }\n | { tag: 'DeclareerBeroep'; beroep: string }\n | { tag: 'SetAntwoord'; vraagId: string; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'GaNaarStap'; cursor: number }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed'; referentie: string }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Seed'; state: RegistratieState };\n\nexport function reduce(s: RegistratieState, m: RegistratieMsg): RegistratieState {\n switch (m.tag) {\n case 'SetField':\n return setField(s, m.key, m.value);\n case 'SetCorrespondentie':\n return setCorrespondentie(s, m.value);\n case 'PrefillAdres':\n return prefillAdres(s, m.straat, m.postcode, m.woonplaats);\n case 'KiesDiploma':\n return kiesDiploma(s, m.diplomaId, m.beroep, m.vraagIds);\n case 'KiesHandmatig':\n return kiesHandmatig(s, m.vraagIds);\n case 'DeclareerBeroep':\n return declareerBeroep(s, m.beroep);\n case 'SetAntwoord':\n return setAntwoord(s, m.vraagId, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'GaNaarStap':\n return gaNaarStap(s, m.cursor);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Mislukt' ? { tag: 'Indienen', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Indienen' ? { tag: 'Ingediend', data: s.data, referentie: m.referentie } : s;\n case 'SubmitFailed':\n return s.tag === 'Indienen' ? { tag: 'Mislukt', data: s.data, error: m.error } : s;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "antwoorden",
"deprecated": false,
"deprecationMessage": "",
"type": "Record<string | string>",
"indexKey": "",
"optional": true,
"description": "",
"line": 66
},
{
"name": "correspondentie",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 64
},
{
"name": "diploma",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 65
},
{
"name": "email",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 63
},
{
"name": "postcode",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 61
},
{
"name": "straat",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 60
},
{
"name": "woonplaats",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 62
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>Per-field error map. <code>antwoorden</code> holds per-policy-question errors, keyed by\nquestion id (a step can show several questions).</p>\n",
"rawdescription": "\nPer-field error map. `antwoorden` holds per-policy-question errors, keyed by\nquestion id (a step can show several questions).",
"methods": [],
"extends": []
},
{
"name": "HerregistratieDecisions",
"id": "interface-HerregistratieDecisions-b39e266ec8fabdaddad87f1e15a5b1b86e45d84ac2c95028b4e676c2eb425fd675c77383c3d4f8f88f4a072f3eb88a954eb0f8755597224ebef59da18d24fda0",
"file": "src/app/registratie/contracts/dashboard-view.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Registration } from '@registratie/domain/registration';\nimport { Person } from '@registratie/domain/person';\nimport { BigProfile } from '@registratie/domain/big-profile';\n\n/**\n * WIRE CONTRACT for the dashboard screen — the \"BFF-lite\" response.\n *\n * In production this type is GENERATED from the OpenAPI/TypeSpec spec (one source\n * of truth for both sides), and the `decisions` block is computed BY THE BACKEND\n * — never recomputed on the client. The frontend renders decisions; it does not\n * own the rules. See docs/architecture/0001-bff-lite-decision-dtos.md.\n *\n * One screen-shaped call replaces the previous three (BIG-register + BRP + …),\n * so the page always sees one consistent snapshot instead of three independently\n * loading/erroring resources.\n */\nexport interface DashboardViewDto {\n registration: Registration;\n person: Person;\n decisions: HerregistratieDecisions;\n}\n\n/** Server-computed decisions. The eligibility rule lives on the backend; the\n optional reason lets the UI explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\n/** The parsed, frontend-side view (DTO mapped onto our own domain model). Keeping\n this distinct from DashboardViewDto is the decoupling seam: the wire shape can\n change without the FE domain following, and vice-versa. */\nexport interface DashboardView {\n profile: BigProfile;\n decisions: HerregistratieDecisions;\n}\n",
"properties": [
{
"name": "eligibleForHerregistratie",
"deprecated": false,
"deprecationMessage": "",
"type": "boolean",
"indexKey": "",
"optional": false,
"description": "",
"line": 26
},
{
"name": "herregistratieReason",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 27
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>Server-computed decisions. The eligibility rule lives on the backend; the\noptional reason lets the UI explain itself without knowing the rule.</p>\n",
"rawdescription": "\nServer-computed decisions. The eligibility rule lives on the backend; the\noptional reason lets the UI explain itself without knowing the rule.",
"methods": [],
"extends": []
},
{
"name": "IntakePolicyDto",
"id": "interface-IntakePolicyDto-1c35bbad790b2fa78117a69dd0537aa3737a0ede256f3d6834bedb78c85cb056d6f571ed0667abc30f5f6c9eff3e6561723917f21ba9f67b0712fd109594b27a",
"file": "src/app/herregistratie/contracts/intake-policy.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface IntakePolicyDto {\n /** Below this many NL-hours the scholing question is required. */\n scholingThreshold: number;\n}\n",
"properties": [
{
"name": "scholingThreshold",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"indexKey": "",
"optional": false,
"description": "<p>Below this many NL-hours the scholing question is required.</p>\n",
"line": 11,
"rawdescription": "\nBelow this many NL-hours the scholing question is required."
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>WIRE CONTRACT for intake policy values owned by the backend.</p>\n<p>This is the &quot;config value&quot; shape of moving policy off the client: instead of\nhardcoding the scholing threshold in the frontend, the backend ships the value\nand the UI applies it for instant feedback. The backend remains the AUTHORITY\n— it re-validates on submit. See docs/architecture/0001-bff-lite-decision-dtos.md.</p>\n",
"rawdescription": "\n\nWIRE CONTRACT for intake policy values owned by the backend.\n\nThis is the \"config value\" shape of moving policy off the client: instead of\nhardcoding the scholing threshold in the frontend, the backend ships the value\nand the UI applies it for instant feedback. The backend remains the AUTHORITY\n— it re-validates on submit. See docs/architecture/0001-bff-lite-decision-dtos.md.\n",
"methods": [],
"extends": []
},
{
"name": "ManualDiplomaPolicyDto",
"id": "interface-ManualDiplomaPolicyDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1",
"file": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface DuoLookupDto {\n diplomas: DuoDiplomaDto[];\n handmatig: ManualDiplomaPolicyDto;\n}\n\nexport interface DuoDiplomaDto {\n id: string;\n naam: string;\n instelling: string;\n jaar: number;\n beroep: string; // server-derived profession\n policyQuestions: PolicyQuestionDto[]; // server-decided geldigheidsvragen\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen: string[]; // professions the user may declare for a manual diploma\n policyQuestions: PolicyQuestionDto[]; // maximal set applied to a manual diploma\n}\n\nexport interface PolicyQuestionDto {\n id: string;\n vraag: string;\n type: 'ja-nee' | 'tekst';\n}\n",
"properties": [
{
"name": "beroepen",
"deprecated": false,
"deprecationMessage": "",
"type": "string[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 30
},
{
"name": "policyQuestions",
"deprecated": false,
"deprecationMessage": "",
"type": "PolicyQuestionDto[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 31
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "Person",
"id": "interface-Person-07cec46d80a41919e40d0bcb002981627d1a30edb363d64a0a140a8af2c38ad85be5603d88030d0704870800694c5bed9c709ae6891fe83e646e90c1a67cf548",
"file": "src/app/registratie/domain/person.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface Adres {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface Person {\n naam: string;\n geboortedatum: string; // ISO date\n adres: Adres;\n}\n",
"properties": [
{
"name": "adres",
"deprecated": false,
"deprecationMessage": "",
"type": "Adres",
"indexKey": "",
"optional": false,
"description": "",
"line": 11
},
{
"name": "geboortedatum",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 10
},
{
"name": "naam",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 9
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "PolicyQuestionDto",
"id": "interface-PolicyQuestionDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1",
"file": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface DuoLookupDto {\n diplomas: DuoDiplomaDto[];\n handmatig: ManualDiplomaPolicyDto;\n}\n\nexport interface DuoDiplomaDto {\n id: string;\n naam: string;\n instelling: string;\n jaar: number;\n beroep: string; // server-derived profession\n policyQuestions: PolicyQuestionDto[]; // server-decided geldigheidsvragen\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen: string[]; // professions the user may declare for a manual diploma\n policyQuestions: PolicyQuestionDto[]; // maximal set applied to a manual diploma\n}\n\nexport interface PolicyQuestionDto {\n id: string;\n vraag: string;\n type: 'ja-nee' | 'tekst';\n}\n",
"properties": [
{
"name": "id",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 35
},
{
"name": "type",
"deprecated": false,
"deprecationMessage": "",
"type": "\"ja-nee\" | \"tekst\"",
"indexKey": "",
"optional": false,
"description": "",
"line": 37
},
{
"name": "vraag",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 36
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "RadioOption",
"id": "interface-RadioOption-a4c75390d3b62730cdd6f53a2ffcb3badde60f23c9104d57d8f8221d038f1dc96a229916ee28bbec4a54dd41e0e88c838ea46b35facfab0ddb5fea79fed37cc0",
"file": "src/app/shared/ui/radio-group/radio-group.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Component, forwardRef, input } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nexport interface RadioOption {\n value: string;\n label: string;\n}\n\n/** Atom: a radio group. Thin wrapper over the Utrecht/RHC radio CSS, wired as a\n form control so it works with ngModel just like the text-input atom. */\n@Component({\n selector: 'app-radio-group',\n styles: [`\n .rhc-radio-option {\n display: flex;\n align-items: center;\n gap: var(--rhc-space-max-md);\n padding-block: var(--rhc-space-max-sm);\n }\n `],\n template: `\n <div\n class=\"utrecht-form-field-radio-group\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"name() + '-label'\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() ? name() + '-error' : null\">\n @for (opt of options(); track opt.value) {\n <label class=\"utrecht-form-label utrecht-form-label--radio-button rhc-radio-option\">\n <input\n class=\"utrecht-radio-button\"\n [class.utrecht-radio-button--checked]=\"value === opt.value\"\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"opt.value\"\n [checked]=\"value === opt.value\"\n [disabled]=\"disabled\"\n (change)=\"select(opt.value)\"\n (blur)=\"onTouched()\" />\n {{ opt.label }}\n </label>\n }\n </div>\n `,\n providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true }],\n})\nexport class RadioGroupComponent implements ControlValueAccessor {\n options = input.required<RadioOption[]>();\n name = input.required<string>();\n invalid = input(false);\n\n value = '';\n disabled = false;\n onChange: (v: string) => void = () => {};\n onTouched: () => void = () => {};\n\n select(v: string) {\n this.value = v;\n this.onChange(v);\n }\n writeValue(v: string) { this.value = v ?? ''; }\n registerOnChange(fn: (v: string) => void) { this.onChange = fn; }\n registerOnTouched(fn: () => void) { this.onTouched = fn; }\n setDisabledState(d: boolean) { this.disabled = d; }\n}\n",
"properties": [
{
"name": "label",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 6
},
{
"name": "value",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 5
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "Registration",
"id": "interface-Registration-8e28c6bf4ec154e4db100e2ec1ee19fe9c2d50bc429733f8288f32277ce91963f67ec0363028632e71cfb7e05ce25001908c8dd8ee66d732632d67b993c0645b",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export type RegistrationStatus =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\n/** Just the discriminant — for atoms that only need the label/color. */\nexport type StatusTag = RegistrationStatus['tag'];\n\nexport interface Registration {\n bigNummer: string;\n naam: string;\n beroep: string; // arts, verpleegkundige, apotheker, ...\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatus;\n}\n\n/** A note is either a recognised specialism or a plain annotation — a closed set,\n not an open string, so a typo can't slip through. */\nexport type AantekeningType = 'Specialisme' | 'Aantekening';\n\nexport interface Aantekening {\n type: AantekeningType;\n omschrijving: string;\n datum: string;\n}\n",
"properties": [
{
"name": "beroep",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 19
},
{
"name": "bigNummer",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 17
},
{
"name": "geboortedatum",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 21
},
{
"name": "naam",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 18
},
{
"name": "registratiedatum",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 20
},
{
"name": "status",
"deprecated": false,
"deprecationMessage": "",
"type": "RegistrationStatus",
"indexKey": "",
"optional": false,
"description": "",
"line": 22
}
],
"indexSignatures": [],
"kind": 172,
"methods": [],
"extends": []
},
{
"name": "Session",
"id": "interface-Session-4f483129ecb1f3747a600d06904b954a4950b10c4da782d9635896d2c39feaa7777d0e9ed018efa5d4252fd44a29035a431e7dcc15ec19c903f710b163e162f2",
"file": "src/app/auth/domain/session.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "export interface Session {\n readonly bsn: string;\n readonly naam: string;\n}\n\nexport function isAuthenticated(s: Session | null): s is Session {\n return s !== null;\n}\n",
"properties": [
{
"name": "bsn",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 3,
"modifierKind": [
148
]
},
{
"name": "naam",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 4,
"modifierKind": [
148
]
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>Who is logged in. Framework-free domain type.</p>\n",
"rawdescription": "\nWho is logged in. Framework-free domain type.",
"methods": [],
"extends": []
},
{
"name": "Store",
"id": "interface-Store-de6d0ec7e0704c5d9490e9a33e624477e924daff53384c527eb4aaa76178bd1716ada071d7e46af3e0a859a7cbec63cdc114f61db5d36b61e49effcd5ac744f9",
"file": "src/app/shared/application/store.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Signal, signal } from '@angular/core';\n\n/**\n * A tiny \"Elm-style\" store. The whole idea: all state lives in ONE value\n * (the Model). The only way to change it is to send a message (Msg) to a PURE\n * function `update(model, msg)` that returns the next Model. Nothing else\n * mutates state, so to understand the app you only read the update function.\n *\n * Side effects (HTTP, timers) do NOT go in `update` — that stays pure and easy\n * to test. Instead, effectful \"command\" functions call the network and then\n * `dispatch` a message describing what happened (e.g. Loaded / Failed).\n */\nexport interface Store<Model, Msg> {\n /** The current state, as a read-only Angular signal. */\n readonly model: Signal<Model>;\n /** Send a message; the model becomes update(model, msg). */\n dispatch(msg: Msg): void;\n}\n\nexport function createStore<Model, Msg>(\n init: Model,\n update: (model: Model, msg: Msg) => Model,\n): Store<Model, Msg> {\n const model = signal(init);\n return {\n model: model.asReadonly(),\n dispatch: (msg) => model.set(update(model(), msg)),\n };\n}\n",
"properties": [
{
"name": "model",
"deprecated": false,
"deprecationMessage": "",
"type": "Signal<Model>",
"indexKey": "",
"optional": false,
"description": "<p>The current state, as a read-only Angular signal.</p>\n",
"line": 15,
"rawdescription": "\nThe current state, as a read-only Angular signal.",
"modifierKind": [
148
]
}
],
"indexSignatures": [],
"kind": 174,
"description": "<p>A tiny &quot;Elm-style&quot; store. The whole idea: all state lives in ONE value\n(the Model). The only way to change it is to send a message (Msg) to a PURE\nfunction <code>update(model, msg)</code> that returns the next Model. Nothing else\nmutates state, so to understand the app you only read the update function.</p>\n<p>Side effects (HTTP, timers) do NOT go in <code>update</code> — that stays pure and easy\nto test. Instead, effectful &quot;command&quot; functions call the network and then\n<code>dispatch</code> a message describing what happened (e.g. Loaded / Failed).</p>\n",
"rawdescription": "\n\nA tiny \"Elm-style\" store. The whole idea: all state lives in ONE value\n(the Model). The only way to change it is to send a message (Msg) to a PURE\nfunction `update(model, msg)` that returns the next Model. Nothing else\nmutates state, so to understand the app you only read the update function.\n\nSide effects (HTTP, timers) do NOT go in `update` — that stays pure and easy\nto test. Instead, effectful \"command\" functions call the network and then\n`dispatch` a message describing what happened (e.g. Loaded / Failed).\n",
"methods": [
{
"name": "dispatch",
"args": [
{
"name": "msg",
"type": "Msg",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 17,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nSend a message; the model becomes update(model, msg).",
"description": "<p>Send a message; the model becomes update(model, msg).</p>\n",
"jsdoctags": [
{
"name": "msg",
"type": "Msg",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"extends": []
},
{
"name": "Valid",
"id": "interface-Valid-097a742aa05bc7bbfee387dd3ea5b2ac382815db8ca18107e699d11e6da110cdeae2eb377bcea284f415cfd263937c3b0f6e7ed2ee9090d660d898dddefe3a4f",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, assertNever } from '@shared/kernel/fp';\nimport { Uren, parseUren } from '@registratie/domain/value-objects/uren';\n\n/** What the user is typing (raw, possibly invalid). */\nexport interface Draft {\n uren: string;\n jaren: string;\n punten: string;\n}\n\n/** What we have AFTER parsing — branded/typed, guaranteed valid. */\nexport interface Valid {\n uren: Uren;\n jaren: number;\n punten: number;\n}\n\n/**\n * The whole wizard as one tagged union. `step` and `errors` exist ONLY while\n * Editing; Submitting/Submitted/Failed carry a `Valid` payload and nothing else.\n * So \"submitting while a field is invalid\" or \"showing the success screen with\n * errors set\" are unrepresentable — the bug class is gone by construction.\n */\nexport type WizardState =\n | { tag: 'Editing'; step: 1 | 2; draft: Draft; errors: Partial<Record<keyof Draft, string>> }\n | { tag: 'Submitting'; data: Valid }\n | { tag: 'Submitted'; data: Valid }\n | { tag: 'Failed'; data: Valid; error: string };\n\nexport const initial: WizardState = { tag: 'Editing', step: 1, draft: { uren: '', jaren: '', punten: '' }, errors: {} };\n\n/** Parse every field; on success hand back a Valid, else the per-field errors. */\nfunction validate(draft: Draft): Result<Partial<Record<keyof Draft, string>>, Valid> {\n const uren = parseUren(draft.uren);\n const jaren = parseUren(draft.jaren);\n const punten = parseUren(draft.punten);\n const errors: Partial<Record<keyof Draft, string>> = {};\n if (!uren.ok) errors.uren = uren.error;\n if (!jaren.ok) errors.jaren = jaren.error;\n if (!punten.ok) errors.punten = punten.error;\n if (uren.ok && jaren.ok && punten.ok) {\n return { ok: true, value: { uren: uren.value, jaren: jaren.value, punten: punten.value } };\n }\n return { ok: false, error: errors };\n}\n\n/** Step 1 → 2: advance only when BOTH step-1 fields parse. Illegal elsewhere = no-op. */\nexport function next(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 1) return s;\n const uren = parseUren(s.draft.uren);\n const jaren = parseUren(s.draft.jaren);\n const errors: Partial<Record<keyof Draft, string>> = {};\n if (!uren.ok) errors.uren = uren.error;\n if (!jaren.ok) errors.jaren = jaren.error;\n return Object.keys(errors).length === 0 ? { ...s, step: 2, errors: {} } : { ...s, errors };\n}\n\nexport function back(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 2) return s;\n return { ...s, step: 1, errors: {} };\n}\n\n/** Step 2 submit: parse everything; move to Submitting only with Valid data. */\nexport function submit(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 2) return s;\n const result = validate(s.draft);\n return result.ok ? { tag: 'Submitting', data: result.value } : { ...s, errors: result.error };\n}\n\n/** Resolve the async submit. Only meaningful while Submitting. */\nexport function resolve(s: WizardState, r: Result<string, void>): WizardState {\n if (s.tag !== 'Submitting') return s;\n return r.ok ? { tag: 'Submitted', data: s.data } : { tag: 'Failed', data: s.data, error: r.error };\n}\n\n/** Update one draft field while editing; ignored in any other state. */\nexport function setField(s: WizardState, key: keyof Draft, value: string): WizardState {\n if (s.tag !== 'Editing') return s;\n return { ...s, draft: { ...s.draft, [key]: value } };\n}\n\n/**\n * Every event that can happen to the wizard, as one message type. The component\n * sends a WizardMsg; `reduce` decides the next state. This is the Elm\n * Model+Msg+update pattern: ONE pure function describes all state changes.\n */\nexport type WizardMsg =\n | { tag: 'SetField'; key: keyof Draft; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed' }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Seed'; state: WizardState }; // mount a specific state (stories/showcase)\n\nexport function reduce(s: WizardState, m: WizardMsg): WizardState {\n switch (m.tag) {\n case 'SetField':\n return setField(s, m.key, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Failed' ? { tag: 'Submitting', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Submitting' ? { tag: 'Submitted', data: s.data } : s;\n case 'SubmitFailed':\n return s.tag === 'Submitting' ? { tag: 'Failed', data: s.data, error: m.error } : s;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "jaren",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"indexKey": "",
"optional": false,
"description": "",
"line": 14
},
{
"name": "punten",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"indexKey": "",
"optional": false,
"description": "",
"line": 15
},
{
"name": "uren",
"deprecated": false,
"deprecationMessage": "",
"type": "Uren",
"indexKey": "",
"optional": false,
"description": "",
"line": 13
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>What we have AFTER parsing — branded/typed, guaranteed valid.</p>\n",
"rawdescription": "\nWhat we have AFTER parsing — branded/typed, guaranteed valid.",
"methods": [],
"extends": []
},
{
"name": "ValidIntake",
"id": "interface-ValidIntake-13704a2a04ab3cffa46cbc109ee84699b236928e1c2649f459e18e4daf323047e8b21cdd5a21440c5fd696a605a09feb673b4020394ba7954ae35162f1bd2732",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, ok, err, assertNever } from '@shared/kernel/fp';\nimport { Uren, parseUren } from '@registratie/domain/value-objects/uren';\n\n/**\n * A FIXED 3-step wizard with progressive disclosure. The steps never change in\n * number (always `STEPS`); instead, follow-up questions appear *inline within a\n * step* depending on earlier answers — answer \"buiten Nederland gewerkt? → ja\"\n * and the country/hours questions reveal in the same step; report few hours and\n * the scholing-question reveals inside the 'werk' step. \"Is this field required\n * right now\" is a pure function (`validateStep`/`lageUren`), so it's trivial to\n * test and impossible to get out of sync with the data.\n */\n\nexport type JaNee = 'ja' | 'nee';\n\n/** The three fixed steps. Each step groups one or more questions. */\nexport type StepId = 'buitenland' | 'werk' | 'review';\n\n/** One record carried across every step (and persisted). All optional: the user\n fills it in gradually, and branches may never ask some fields. */\nexport interface Answers {\n buitenlandGewerkt?: JaNee; // Q1\n land?: string; // Q1a — only when buitenlandGewerkt === 'ja'\n buitenlandseUren?: string; // Q1b — only when buitenlandGewerkt === 'ja'\n uren?: string; // Q2 — uren in NL\n scholingGevolgd?: JaNee; // Q3 — only when total hours are below the threshold\n punten?: string; // Q4\n}\n\n/** What we have after the review step parses — guaranteed valid/typed. */\nexport interface ValidIntake {\n werktBuitenland: boolean;\n land?: string;\n buitenlandseUren?: Uren;\n uren: Uren;\n aanvullendeScholing?: boolean;\n punten?: Uren; // only collected when aanvullende scholing is gevolgd (scholingGevolgd === 'ja')\n}\n\n/** Demo fallback only — the real threshold is a SERVER-OWNED policy value fetched\n at runtime (see IntakePolicyDto / SetPolicy). ponytail: default is the offline\n fallback; the server value wins. */\nexport const SCHOLING_THRESHOLD_DEFAULT = 1000;\n\n/** True when NL-hours are low enough that the scholing question must be answered.\n The threshold is passed in (server-owned), not hardcoded. */\nexport function lageUren(a: Answers, scholingThreshold = SCHOLING_THRESHOLD_DEFAULT): boolean {\n const r = parseUren(a.uren ?? '');\n return r.ok && r.value < scholingThreshold;\n}\n\n/** The fixed step list. Number of steps never changes; questions reveal inline. */\nexport const STEPS: StepId[] = ['buitenland', 'werk', 'review'];\n\n/** Per-field error map: one message per question, since a step holds several. */\ntype Errors = Partial<Record<keyof Answers, string>>;\n\nexport type IntakeState =\n | { tag: 'Answering'; answers: Answers; cursor: number; errors: Errors; scholingThreshold: number }\n | { tag: 'Submitting'; data: ValidIntake }\n | { tag: 'Submitted'; data: ValidIntake }\n | { tag: 'Failed'; data: ValidIntake; error: string };\n\nexport const initial: IntakeState = { tag: 'Answering', answers: {}, cursor: 0, errors: {}, scholingThreshold: SCHOLING_THRESHOLD_DEFAULT };\n\n/** Which step the cursor currently points at (clamped to the fixed list). */\nexport function currentStep(s: Extract<IntakeState, { tag: 'Answering' }>): StepId {\n return STEPS[Math.min(s.cursor, STEPS.length - 1)];\n}\n\n/** Validate every question currently visible in ONE step. Errors keyed per field. */\nfunction validateStep(step: StepId, a: Answers, scholingThreshold: number): Result<Errors, void> {\n const errors: Errors = {};\n switch (step) {\n case 'buitenland':\n if (!a.buitenlandGewerkt) errors.buitenlandGewerkt = 'Maak een keuze.';\n else if (a.buitenlandGewerkt === 'ja') {\n if (!a.land || a.land.trim() === '') errors.land = 'Vul een land in.';\n const u = parseUren(a.buitenlandseUren ?? '');\n if (!u.ok) errors.buitenlandseUren = u.error;\n }\n break;\n case 'werk': {\n const u = parseUren(a.uren ?? '');\n if (!u.ok) errors.uren = u.error;\n if (lageUren(a, scholingThreshold) && !a.scholingGevolgd) errors.scholingGevolgd = 'Maak een keuze.';\n // Nascholingspunten are only asked (and required) when scholing was followed.\n if (a.scholingGevolgd === 'ja') {\n const p = parseUren(a.punten ?? '');\n if (!p.ok) errors.punten = p.error;\n }\n break;\n }\n case 'review':\n break; // review shows a summary; no own fields\n default:\n return assertNever(step);\n }\n return Object.keys(errors).length > 0 ? err(errors) : ok(undefined);\n}\n\n/** Parse the whole questionnaire into a ValidIntake (called on submit). */\nfunction validateAll(a: Answers, scholingThreshold: number): Result<Errors, ValidIntake> {\n const errors: Errors = {};\n for (const step of STEPS) {\n const r = validateStep(step, a, scholingThreshold);\n if (!r.ok) Object.assign(errors, r.error);\n }\n if (Object.keys(errors).length > 0) return err(errors);\n\n const uren = parseUren(a.uren ?? '');\n // validateStep guaranteed uren parses, but keep the compiler happy.\n if (!uren.ok) return err(errors);\n\n const werktBuitenland = a.buitenlandGewerkt === 'ja';\n const buitenland = parseUren(a.buitenlandseUren ?? '');\n // Punten are only collected when aanvullende scholing was gevolgd.\n const punten = a.scholingGevolgd === 'ja' ? parseUren(a.punten ?? '') : undefined;\n return ok({\n werktBuitenland,\n land: werktBuitenland ? a.land : undefined,\n buitenlandseUren: werktBuitenland && buitenland.ok ? buitenland.value : undefined,\n uren: uren.value,\n aanvullendeScholing: lageUren(a, scholingThreshold) ? a.scholingGevolgd === 'ja' : undefined,\n punten: punten?.ok ? punten.value : undefined,\n });\n}\n\nexport function setAnswer(s: IntakeState, key: keyof Answers, value: string): IntakeState {\n if (s.tag !== 'Answering') return s;\n // Steps are fixed, so editing an answer never moves the cursor — it only\n // reveals/hides inline questions within the current step.\n return { ...s, answers: { ...s.answers, [key]: value } };\n}\n\nexport function next(s: IntakeState): IntakeState {\n if (s.tag !== 'Answering') return s;\n const r = validateStep(currentStep(s), s.answers, s.scholingThreshold);\n if (!r.ok) return { ...s, errors: r.error };\n return { ...s, cursor: Math.min(s.cursor + 1, STEPS.length - 1), errors: {} };\n}\n\n/** Apply a server-owned policy value (e.g. the scholing threshold). */\nexport function setPolicy(s: IntakeState, scholingThreshold: number): IntakeState {\n return s.tag === 'Answering' ? { ...s, scholingThreshold } : s;\n}\n\nexport function back(s: IntakeState): IntakeState {\n if (s.tag !== 'Answering' || s.cursor === 0) return s;\n return { ...s, cursor: s.cursor - 1, errors: {} };\n}\n\nexport function submit(s: IntakeState): IntakeState {\n if (s.tag !== 'Answering') return s;\n const r = validateAll(s.answers, s.scholingThreshold);\n return r.ok ? { tag: 'Submitting', data: r.value } : { ...s, errors: r.error };\n}\n\nexport function resolve(s: IntakeState, r: Result<string, void>): IntakeState {\n if (s.tag !== 'Submitting') return s;\n return r.ok ? { tag: 'Submitted', data: s.data } : { tag: 'Failed', data: s.data, error: r.error };\n}\n\nexport type IntakeMsg =\n | { tag: 'SetAnswer'; key: keyof Answers; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed' }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'SetPolicy'; scholingThreshold: number }\n | { tag: 'Seed'; state: IntakeState };\n\nexport function reduce(s: IntakeState, m: IntakeMsg): IntakeState {\n switch (m.tag) {\n case 'SetAnswer':\n return setAnswer(s, m.key, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Failed' ? { tag: 'Submitting', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Submitting' ? { tag: 'Submitted', data: s.data } : s;\n case 'SubmitFailed':\n return s.tag === 'Submitting' ? { tag: 'Failed', data: s.data, error: m.error } : s;\n case 'SetPolicy':\n return setPolicy(s, m.scholingThreshold);\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "aanvullendeScholing",
"deprecated": false,
"deprecationMessage": "",
"type": "boolean",
"indexKey": "",
"optional": true,
"description": "",
"line": 36
},
{
"name": "buitenlandseUren",
"deprecated": false,
"deprecationMessage": "",
"type": "Uren",
"indexKey": "",
"optional": true,
"description": "",
"line": 34
},
{
"name": "land",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": true,
"description": "",
"line": 33
},
{
"name": "punten",
"deprecated": false,
"deprecationMessage": "",
"type": "Uren",
"indexKey": "",
"optional": true,
"description": "",
"line": 37
},
{
"name": "uren",
"deprecated": false,
"deprecationMessage": "",
"type": "Uren",
"indexKey": "",
"optional": false,
"description": "",
"line": 35
},
{
"name": "werktBuitenland",
"deprecated": false,
"deprecationMessage": "",
"type": "boolean",
"indexKey": "",
"optional": false,
"description": "",
"line": 32
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>What we have after the review step parses — guaranteed valid/typed.</p>\n",
"rawdescription": "\nWhat we have after the review step parses — guaranteed valid/typed.",
"methods": [],
"extends": []
},
{
"name": "ValidRegistratie",
"id": "interface-ValidRegistratie-6ed41f6a28b6c00dc907f54219be9c1d7955aa11bc22f128620d2206b1ea12e6730533ac33f5f5df7b8d7c8d7f977584b3a0a3e98a6c2c3b91d1afe6411278e2",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "interface",
"sourceCode": "import { Result, ok, err, assertNever } from '@shared/kernel/fp';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\nimport { Email, parseEmail } from '@registratie/domain/value-objects/email';\n\n/**\n * A FIXED 3-step registration wizard. The steps never change in number (always\n * `STEPS`): (1) adres + correspondentievoorkeur, (2) beroep o.b.v. diploma,\n * (3) controle & indienen. Follow-up questions appear *inline within a step*\n * (e.g. choosing 'email' reveals the e-mail field). \"Is this field required\n * right now\" is a pure function (`validateStep`), so it is trivial to test and\n * impossible to get out of sync with the data. Invariants live here, not in the\n * UI: the wizard reaches `Indienen` only when a complete `ValidRegistratie` parses.\n */\n\nexport type StepId = 'adres' | 'beroep' | 'controle';\n\n/** The fixed step list. Number of steps never changes; questions reveal inline. */\nexport const STEPS: StepId[] = ['adres', 'beroep', 'controle'];\n\n/** Where a piece of data came from — recorded on the aggregate (PRD §5). */\nexport type AdresHerkomst = 'brp' | 'handmatig';\nexport type DiplomaHerkomst = 'duo' | 'handmatig';\nexport type Correspondentie = 'email' | 'post';\n\n/** One record carried across every step (and persisted). All optional: the user\n fills it in gradually. Adres fields are kept flat so one `SetField` message\n serves them all (mirrors the intake machine). */\nexport interface Draft {\n straat?: string;\n postcode?: string;\n woonplaats?: string;\n adresHerkomst?: AdresHerkomst;\n correspondentie?: Correspondentie;\n email?: string;\n diplomaId?: string;\n diplomaHerkomst?: DiplomaHerkomst;\n beroep?: string; // DERIVED from the chosen DUO diploma (or declared for a manual one)\n vraagIds?: string[]; // ids of the policy questions that apply to the chosen diploma\n antwoorden: Record<string, string>; // geldigheidsantwoorden, keyed by question id\n}\n\n/** What we have after the controle step parses — guaranteed valid/typed. */\nexport interface ValidRegistratie {\n adres: { straat: string; postcode: Postcode; woonplaats: string };\n adresHerkomst: AdresHerkomst;\n correspondentie: Correspondentie;\n email?: Email; // only when correspondentie === 'email'\n diplomaId: string;\n diplomaHerkomst: DiplomaHerkomst;\n beroep: string;\n antwoorden: Record<string, string>;\n}\n\n/** Text fields settable via SetField. */\nexport type DraftField = 'straat' | 'postcode' | 'woonplaats' | 'email';\n\n/** Per-field error map. `antwoorden` holds per-policy-question errors, keyed by\n question id (a step can show several questions). */\nexport interface Errors {\n straat?: string;\n postcode?: string;\n woonplaats?: string;\n email?: string;\n correspondentie?: string;\n diploma?: string;\n antwoorden?: Record<string, string>;\n}\n\nexport type RegistratieState =\n | { tag: 'Invullen'; draft: Draft; cursor: number; errors: Errors }\n | { tag: 'Indienen'; data: ValidRegistratie }\n | { tag: 'Ingediend'; data: ValidRegistratie; referentie: string }\n | { tag: 'Mislukt'; data: ValidRegistratie; error: string };\n\nconst emptyDraft: Draft = { antwoorden: {} };\nexport const initial: RegistratieState = { tag: 'Invullen', draft: emptyDraft, cursor: 0, errors: {} };\n\n/** Which step the cursor currently points at (clamped to the fixed list). */\nexport function currentStep(s: Extract<RegistratieState, { tag: 'Invullen' }>): StepId {\n return STEPS[Math.min(s.cursor, STEPS.length - 1)];\n}\n\n/** Validate every question currently visible in ONE step. Errors keyed per field. */\nfunction validateStep(step: StepId, d: Draft): Result<Errors, void> {\n const errors: Errors = {};\n switch (step) {\n case 'adres': {\n if (!d.straat || d.straat.trim() === '') errors.straat = 'Vul een straat en huisnummer in.';\n const pc = parsePostcode(d.postcode ?? '');\n if (!pc.ok) errors.postcode = pc.error;\n if (!d.woonplaats || d.woonplaats.trim() === '') errors.woonplaats = 'Vul een woonplaats in.';\n if (!d.correspondentie) errors.correspondentie = 'Maak een keuze.';\n // E-mail is only required when 'email' is the chosen channel.\n if (d.correspondentie === 'email') {\n const e = parseEmail(d.email ?? '');\n if (!e.ok) errors.email = e.error;\n }\n break;\n }\n case 'beroep': {\n // A diploma must be chosen (or declared manually); its beroep is then known.\n if (!d.diplomaId || !d.beroep) {\n errors.diploma = 'Kies het diploma waarmee u zich wilt registreren, of voer het handmatig in.';\n break;\n }\n // Every policy question the chosen diploma raised must be answered. Which\n // questions apply is server-decided (carried in `vraagIds`); we only check\n // they're answered.\n const open: Record<string, string> = {};\n for (const id of d.vraagIds ?? []) {\n if (!(d.antwoorden[id] ?? '').trim()) open[id] = 'Beantwoord deze vraag.';\n }\n if (Object.keys(open).length > 0) errors.antwoorden = open;\n break;\n }\n case 'controle':\n break; // controle shows a summary; no own fields\n default:\n return assertNever(step);\n }\n return Object.keys(errors).length > 0 ? err(errors) : ok(undefined);\n}\n\n/** Parse the whole wizard into a ValidRegistratie (called on submit). */\nfunction validateAll(d: Draft): Result<Errors, ValidRegistratie> {\n const errors: Errors = {};\n for (const step of STEPS) {\n const r = validateStep(step, d);\n if (!r.ok) Object.assign(errors, r.error);\n }\n if (Object.keys(errors).length > 0) return err(errors);\n\n const pc = parsePostcode(d.postcode ?? '');\n // validateStep guaranteed these parse, but keep the compiler happy.\n if (!pc.ok || !d.diplomaId || !d.beroep || !d.correspondentie) return err(errors);\n const email = d.correspondentie === 'email' ? parseEmail(d.email ?? '') : undefined;\n // Keep only the answers to the questions that actually applied.\n const vraagIds = d.vraagIds ?? [];\n const antwoorden = Object.fromEntries(vraagIds.map((id) => [id, d.antwoorden[id] ?? '']));\n\n return ok({\n adres: { straat: d.straat!, postcode: pc.value, woonplaats: d.woonplaats! },\n adresHerkomst: d.adresHerkomst ?? 'handmatig',\n correspondentie: d.correspondentie,\n email: email?.ok ? email.value : undefined,\n diplomaId: d.diplomaId,\n diplomaHerkomst: d.diplomaHerkomst ?? 'handmatig',\n beroep: d.beroep,\n antwoorden,\n });\n}\n\nexport function setField(s: RegistratieState, key: DraftField, value: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const draft: Draft = { ...s.draft, [key]: value };\n // Editing an address field means the user owns it now — not the BRP copy.\n if (key === 'straat' || key === 'postcode' || key === 'woonplaats') draft.adresHerkomst = 'handmatig';\n return { ...s, draft };\n}\n\nexport function setCorrespondentie(s: RegistratieState, value: Correspondentie): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, correspondentie: value } };\n}\n\n/** Prefill the address from a BRP lookup and flag its origin (PRD §7). */\nexport function prefillAdres(s: RegistratieState, straat: string, postcode: string, woonplaats: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, straat, postcode, woonplaats, adresHerkomst: 'brp' } };\n}\n\n/** Pick a DUO diploma; the beroep is derived from it and the applicable policy\n questions (`vraagIds`) come with it (both server-computed, passed in). */\nexport function kiesDiploma(s: RegistratieState, diplomaId: string, beroep: string, vraagIds: string[]): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, diplomaId, beroep, vraagIds, diplomaHerkomst: 'duo' }, errors: {} };\n}\n\n/** Switch to manual diploma entry: the diploma isn't in DUO, so the MAXIMAL\n policy-question set applies and the entry is flagged handmatig/unverified. The\n beroep is declared separately (declareerBeroep). */\nexport function kiesHandmatig(s: RegistratieState, vraagIds: string[]): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, diplomaId: 'handmatig', beroep: undefined, vraagIds, diplomaHerkomst: 'handmatig' }, errors: {} };\n}\n\n/** Declare the beroep for a manually-entered diploma (chosen from a fixed list). */\nexport function declareerBeroep(s: RegistratieState, beroep: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, beroep } };\n}\n\nexport function setAntwoord(s: RegistratieState, vraagId: string, value: string): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n return { ...s, draft: { ...s.draft, antwoorden: { ...s.draft.antwoorden, [vraagId]: value } } };\n}\n\nexport function next(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const r = validateStep(currentStep(s), s.draft);\n if (!r.ok) return { ...s, errors: r.error };\n return { ...s, cursor: Math.min(s.cursor + 1, STEPS.length - 1), errors: {} };\n}\n\nexport function back(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen' || s.cursor === 0) return s;\n return { ...s, cursor: s.cursor - 1, errors: {} };\n}\n\n/** Jump back to an earlier step to correct data (controle → step N). Forward\n jumps are not allowed (would skip validation). Preserves the draft. */\nexport function gaNaarStap(s: RegistratieState, cursor: number): RegistratieState {\n if (s.tag !== 'Invullen' || cursor < 0 || cursor >= s.cursor) return s;\n return { ...s, cursor, errors: {} };\n}\n\nexport function submit(s: RegistratieState): RegistratieState {\n if (s.tag !== 'Invullen') return s;\n const r = validateAll(s.draft);\n return r.ok ? { tag: 'Indienen', data: r.value } : { ...s, errors: r.error };\n}\n\nexport function resolve(s: RegistratieState, r: Result<string, string>): RegistratieState {\n if (s.tag !== 'Indienen') return s;\n return r.ok ? { tag: 'Ingediend', data: s.data, referentie: r.value } : { tag: 'Mislukt', data: s.data, error: r.error };\n}\n\nexport type RegistratieMsg =\n | { tag: 'SetField'; key: DraftField; value: string }\n | { tag: 'SetCorrespondentie'; value: Correspondentie }\n | { tag: 'PrefillAdres'; straat: string; postcode: string; woonplaats: string }\n | { tag: 'KiesDiploma'; diplomaId: string; beroep: string; vraagIds: string[] }\n | { tag: 'KiesHandmatig'; vraagIds: string[] }\n | { tag: 'DeclareerBeroep'; beroep: string }\n | { tag: 'SetAntwoord'; vraagId: string; value: string }\n | { tag: 'Next' }\n | { tag: 'Back' }\n | { tag: 'GaNaarStap'; cursor: number }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed'; referentie: string }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Seed'; state: RegistratieState };\n\nexport function reduce(s: RegistratieState, m: RegistratieMsg): RegistratieState {\n switch (m.tag) {\n case 'SetField':\n return setField(s, m.key, m.value);\n case 'SetCorrespondentie':\n return setCorrespondentie(s, m.value);\n case 'PrefillAdres':\n return prefillAdres(s, m.straat, m.postcode, m.woonplaats);\n case 'KiesDiploma':\n return kiesDiploma(s, m.diplomaId, m.beroep, m.vraagIds);\n case 'KiesHandmatig':\n return kiesHandmatig(s, m.vraagIds);\n case 'DeclareerBeroep':\n return declareerBeroep(s, m.beroep);\n case 'SetAntwoord':\n return setAntwoord(s, m.vraagId, m.value);\n case 'Next':\n return next(s);\n case 'Back':\n return back(s);\n case 'GaNaarStap':\n return gaNaarStap(s, m.cursor);\n case 'Submit':\n return submit(s);\n case 'Retry':\n return s.tag === 'Mislukt' ? { tag: 'Indienen', data: s.data } : s;\n case 'SubmitConfirmed':\n return s.tag === 'Indienen' ? { tag: 'Ingediend', data: s.data, referentie: m.referentie } : s;\n case 'SubmitFailed':\n return s.tag === 'Indienen' ? { tag: 'Mislukt', data: s.data, error: m.error } : s;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n",
"properties": [
{
"name": "adres",
"deprecated": false,
"deprecationMessage": "",
"type": "literal type",
"indexKey": "",
"optional": false,
"description": "",
"line": 44
},
{
"name": "adresHerkomst",
"deprecated": false,
"deprecationMessage": "",
"type": "AdresHerkomst",
"indexKey": "",
"optional": false,
"description": "",
"line": 45
},
{
"name": "antwoorden",
"deprecated": false,
"deprecationMessage": "",
"type": "Record<string | string>",
"indexKey": "",
"optional": false,
"description": "",
"line": 51
},
{
"name": "beroep",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 50
},
{
"name": "correspondentie",
"deprecated": false,
"deprecationMessage": "",
"type": "Correspondentie",
"indexKey": "",
"optional": false,
"description": "",
"line": 46
},
{
"name": "diplomaHerkomst",
"deprecated": false,
"deprecationMessage": "",
"type": "DiplomaHerkomst",
"indexKey": "",
"optional": false,
"description": "",
"line": 49
},
{
"name": "diplomaId",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 48
},
{
"name": "email",
"deprecated": false,
"deprecationMessage": "",
"type": "Email",
"indexKey": "",
"optional": true,
"description": "",
"line": 47
}
],
"indexSignatures": [],
"kind": 172,
"description": "<p>What we have after the controle step parses — guaranteed valid/typed.</p>\n",
"rawdescription": "\nWhat we have after the controle step parses — guaranteed valid/typed.",
"methods": [],
"extends": []
}
],
"injectables": [
{
"name": "BigProfileStore",
"id": "injectable-BigProfileStore-69aed5b05b57a19cafee014d2f45ac87468f795733cf7c00ad07d0283d30edd836e2d4b8a45cad42641fa5c84cad96f314aa4d287ca1c55d3fdd8feceefcd642",
"file": "src/app/registratie/application/big-profile.store.ts",
"properties": [
{
"name": "aantekeningen",
"defaultValue": "computed<RemoteData<Err, Aantekening[]>>(() =>\n fromResource(this.aantekeningenRes, (v) => v.length === 0),\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Specialisms/notes stay a separate stream (they have their own empty state).</p>\n",
"line": 45,
"rawdescription": "\nSpecialisms/notes stay a separate stream (they have their own empty state).",
"modifierKind": [
148
]
},
{
"name": "aantekeningenRes",
"defaultValue": "this.big.aantekeningenResource()",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 28,
"modifierKind": [
123
]
},
{
"name": "big",
"defaultValue": "inject(BigRegisterAdapter)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 24,
"modifierKind": [
123
]
},
{
"name": "decisions",
"defaultValue": "computed<RemoteData<Err, HerregistratieDecisions>>(() => map(this.view(), (v) => v.decisions))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Server-computed decisions (e.g. herregistratie eligibility) — rendered, not recomputed.</p>\n",
"line": 42,
"rawdescription": "\nServer-computed decisions (e.g. herregistratie eligibility) — rendered, not recomputed.",
"modifierKind": [
148
]
},
{
"name": "pending",
"defaultValue": "signal(false)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 50,
"modifierKind": [
123
]
},
{
"name": "pendingHerregistratie",
"defaultValue": "this.pending.asReadonly()",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>True while a herregistratie submission is in flight or just submitted.</p>\n",
"line": 52,
"rawdescription": "\nTrue while a herregistratie submission is in flight or just submitted.",
"modifierKind": [
148
]
},
{
"name": "profile",
"defaultValue": "computed<RemoteData<Err, BigProfile>>(() => map(this.view(), (v) => v.profile))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Registration + person, from the single aggregated call.</p>\n",
"line": 39,
"rawdescription": "\nRegistration + person, from the single aggregated call.",
"modifierKind": [
148
]
},
{
"name": "view",
"defaultValue": "computed<RemoteData<Err, DashboardView>>(() => {\n const rd = fromResource(this.viewRes);\n if (rd.tag !== 'Success') return rd;\n const parsed = parseDashboardView(rd.value);\n return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) };\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>The aggregated view, validated at the trust boundary (DTO → domain).</p>\n",
"line": 31,
"rawdescription": "\nThe aggregated view, validated at the trust boundary (DTO → domain).",
"modifierKind": [
123
]
},
{
"name": "viewAdapter",
"defaultValue": "inject(DashboardViewAdapter)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 25,
"modifierKind": [
123
]
},
{
"name": "viewRes",
"defaultValue": "this.viewAdapter.dashboardViewResource()",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 27,
"modifierKind": [
123
]
}
],
"methods": [
{
"name": "beginHerregistratie",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 54,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "confirmHerregistratie",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 57,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "rollbackHerregistratie",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 61,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The single source of truth for the logged-in professional&#39;s profile, shared\nacross pages (providedIn:&#39;root&#39; = one instance). It owns the httpResources\n(created here, in the required injection context) and exposes them as\nRemoteData signals.</p>\n<p>The dashboard data now comes from ONE screen-shaped (&quot;BFF-lite&quot;) call that\nreturns registration + person + server-computed <code>decisions</code>. One request → one\nconsistent snapshot, instead of stitching three independently loading/erroring\nresources together client-side. See docs/architecture/0001-bff-lite-decision-dtos.md.</p>\n",
"rawdescription": "\n\nThe single source of truth for the logged-in professional's profile, shared\nacross pages (providedIn:'root' = one instance). It owns the httpResources\n(created here, in the required injection context) and exposes them as\nRemoteData signals.\n\nThe dashboard data now comes from ONE screen-shaped (\"BFF-lite\") call that\nreturns registration + person + server-computed `decisions`. One request → one\nconsistent snapshot, instead of stitching three independently loading/erroring\nresources together client-side. See docs/architecture/0001-bff-lite-decision-dtos.md.\n",
"sourceCode": "import { Injectable, computed, inject, signal } from '@angular/core';\nimport { RemoteData, fromResource, map } from '@shared/application/remote-data';\nimport { Aantekening } from '../domain/registration';\nimport { BigProfile } from '../domain/big-profile';\nimport { HerregistratieDecisions, DashboardView } from '../contracts/dashboard-view.dto';\nimport { BigRegisterAdapter } from '../infrastructure/big-register.adapter';\nimport { DashboardViewAdapter, parseDashboardView } from '../infrastructure/dashboard-view.adapter';\n\ntype Err = Error | undefined;\n\n/**\n * The single source of truth for the logged-in professional's profile, shared\n * across pages (providedIn:'root' = one instance). It owns the httpResources\n * (created here, in the required injection context) and exposes them as\n * RemoteData signals.\n *\n * The dashboard data now comes from ONE screen-shaped (\"BFF-lite\") call that\n * returns registration + person + server-computed `decisions`. One request → one\n * consistent snapshot, instead of stitching three independently loading/erroring\n * resources together client-side. See docs/architecture/0001-bff-lite-decision-dtos.md.\n */\n@Injectable({ providedIn: 'root' })\nexport class BigProfileStore {\n private big = inject(BigRegisterAdapter);\n private viewAdapter = inject(DashboardViewAdapter);\n\n private viewRes = this.viewAdapter.dashboardViewResource();\n private aantekeningenRes = this.big.aantekeningenResource();\n\n /** The aggregated view, validated at the trust boundary (DTO → domain). */\n private view = computed<RemoteData<Err, DashboardView>>(() => {\n const rd = fromResource(this.viewRes);\n if (rd.tag !== 'Success') return rd;\n const parsed = parseDashboardView(rd.value);\n return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) };\n });\n\n /** Registration + person, from the single aggregated call. */\n readonly profile = computed<RemoteData<Err, BigProfile>>(() => map(this.view(), (v) => v.profile));\n\n /** Server-computed decisions (e.g. herregistratie eligibility) — rendered, not recomputed. */\n readonly decisions = computed<RemoteData<Err, HerregistratieDecisions>>(() => map(this.view(), (v) => v.decisions));\n\n /** Specialisms/notes stay a separate stream (they have their own empty state). */\n readonly aantekeningen = computed<RemoteData<Err, Aantekening[]>>(() =>\n fromResource(this.aantekeningenRes, (v) => v.length === 0),\n );\n\n // --- Optimistic herregistratie state, shared with the dashboard -----------\n private pending = signal(false);\n /** True while a herregistratie submission is in flight or just submitted. */\n readonly pendingHerregistratie = this.pending.asReadonly();\n\n beginHerregistratie() {\n this.pending.set(true); // optimistic: show it immediately on the dashboard\n }\n confirmHerregistratie() {\n this.pending.set(false);\n this.viewRes.reload(); // invalidate: re-fetch the now-updated view (registration + decisions)\n }\n rollbackHerregistratie() {\n this.pending.set(false); // submission failed — undo the optimistic flag\n }\n}\n",
"extends": [],
"type": "injectable"
},
{
"name": "BigRegisterAdapter",
"id": "injectable-BigRegisterAdapter-a97ebf3c62a06bc0fe10b80bf7c03978bd380fe2f177627c268298d8cb92c8ac99f4977a95602bc5b4038f0d32aef28a0075d314b56d6c3b1edeeb66327cf358",
"file": "src/app/registratie/infrastructure/big-register.adapter.ts",
"properties": [],
"methods": [
{
"name": "aantekeningenResource",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 16,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Infrastructure adapter for the BIG-register source. Exposes signal-based\nresources (Angular&#39;s httpResource); each returns a Resource with\nstatus()/value()/error()/reload(). Call from an injection context\n(a field initializer in the store).</p>\n<p>Note: registration + person are now served via the aggregated dashboard-view\nendpoint (see DashboardViewAdapter). Only the notes stream remains separate.</p>\n",
"rawdescription": "\n\nInfrastructure adapter for the BIG-register source. Exposes signal-based\nresources (Angular's httpResource); each returns a Resource with\nstatus()/value()/error()/reload(). Call from an injection context\n(a field initializer in the store).\n\nNote: registration + person are now served via the aggregated dashboard-view\nendpoint (see DashboardViewAdapter). Only the notes stream remains separate.\n",
"sourceCode": "import { Injectable } from '@angular/core';\nimport { httpResource } from '@angular/common/http';\nimport { Aantekening } from '../domain/registration';\n\n/**\n * Infrastructure adapter for the BIG-register source. Exposes signal-based\n * resources (Angular's httpResource); each returns a Resource with\n * status()/value()/error()/reload(). Call from an injection context\n * (a field initializer in the store).\n *\n * Note: registration + person are now served via the aggregated dashboard-view\n * endpoint (see DashboardViewAdapter). Only the notes stream remains separate.\n */\n@Injectable({ providedIn: 'root' })\nexport class BigRegisterAdapter {\n aantekeningenResource() {\n return httpResource<Aantekening[]>(() => 'mock/notes.json', { defaultValue: [] });\n }\n}\n",
"extends": [],
"type": "injectable"
},
{
"name": "BrpAdapter",
"id": "injectable-BrpAdapter-a344bff4ee9ecd8353a0f1cac582f6ade0f15a250cd704bceb4f884101b9063dcba26b6db8242dbcd28fc6a7b8c259b0ed02732215fe9fec1dbd5dbba6b6c336",
"file": "src/app/registratie/infrastructure/brp.adapter.ts",
"properties": [],
"methods": [
{
"name": "adresResource",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 15,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Infrastructure adapter for the BRP address lookup, reached only through our own\n(&quot;BFF-lite&quot;) endpoint — the anti-corruption boundary. In this POC the endpoint\nis a static mock; pointing at a real backend touches only this file + the DTO.</p>\n",
"rawdescription": "\n\nInfrastructure adapter for the BRP address lookup, reached only through our own\n(\"BFF-lite\") endpoint — the anti-corruption boundary. In this POC the endpoint\nis a static mock; pointing at a real backend touches only this file + the DTO.\n",
"sourceCode": "import { Injectable } from '@angular/core';\nimport { httpResource } from '@angular/common/http';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { BrpAddressDto } from '@registratie/contracts/brp-address.dto';\n\n/**\n * Infrastructure adapter for the BRP address lookup, reached only through our own\n * (\"BFF-lite\") endpoint — the anti-corruption boundary. In this POC the endpoint\n * is a static mock; pointing at a real backend touches only this file + the DTO.\n */\n@Injectable({ providedIn: 'root' })\nexport class BrpAdapter {\n // Typed as the DTO for ergonomics, but the value is untrusted JSON until\n // parseBrpAddress validates it.\n adresResource() {\n return httpResource<BrpAddressDto>(() => 'mock/brp-address.json');\n }\n}\n\n/** Trust-boundary parse: validate the untrusted response shape. \"Geen adres\" is a\n valid outcome (gevonden: false), not a malformed response. ponytail: hand-written;\n reach for a schema lib once the contract count grows. */\nexport function parseBrpAddress(json: unknown): Result<string, BrpAddressDto> {\n if (typeof json !== 'object' || json === null) return err('brp-address: not an object');\n const dto = json as Partial<BrpAddressDto>;\n if (typeof dto.gevonden !== 'boolean') return err('brp-address: missing/invalid gevonden');\n if (dto.gevonden) {\n const a = dto.adres;\n if (!a || typeof a.straat !== 'string' || typeof a.postcode !== 'string' || typeof a.woonplaats !== 'string') {\n return err('brp-address: missing/invalid adres');\n }\n }\n return ok({ gevonden: dto.gevonden, adres: dto.adres });\n}\n",
"extends": [],
"type": "injectable"
},
{
"name": "DashboardViewAdapter",
"id": "injectable-DashboardViewAdapter-f9f802caa4aa1f8e0892c550edb337b98c92f6fc1b268ed0f6c5f4e28416042eab089262dcfac88d0d16f9bbbec112731a09f9329be75c7030133e8827a8eb47",
"file": "src/app/registratie/infrastructure/dashboard-view.adapter.ts",
"properties": [],
"methods": [
{
"name": "dashboardViewResource",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 16,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Infrastructure adapter for the screen-shaped (&quot;BFF-lite&quot;) dashboard endpoint.\nONE call returns registration + person + server-computed decisions.\n(In this POC the endpoint is a static mock; the decisions are precomputed to\nstand in for what the backend would compute.)</p>\n",
"rawdescription": "\n\nInfrastructure adapter for the screen-shaped (\"BFF-lite\") dashboard endpoint.\nONE call returns registration + person + server-computed decisions.\n(In this POC the endpoint is a static mock; the decisions are precomputed to\nstand in for what the backend would compute.)\n",
"sourceCode": "import { Injectable } from '@angular/core';\nimport { httpResource } from '@angular/common/http';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { DashboardViewDto, DashboardView } from '@registratie/contracts/dashboard-view.dto';\n\n/**\n * Infrastructure adapter for the screen-shaped (\"BFF-lite\") dashboard endpoint.\n * ONE call returns registration + person + server-computed decisions.\n * (In this POC the endpoint is a static mock; the decisions are precomputed to\n * stand in for what the backend would compute.)\n */\n@Injectable({ providedIn: 'root' })\nexport class DashboardViewAdapter {\n // Typed as the DTO for ergonomics, but the value is still untrusted JSON —\n // parseDashboardView validates it at the boundary before the app uses it.\n dashboardViewResource() {\n return httpResource<DashboardViewDto>(() => 'mock/dashboard-view.json');\n }\n}\n\n/**\n * Trust-boundary parse: validate the untrusted response shape and map the DTO\n * onto our own domain model. Hand-written on purpose — no Zod for a single\n * contract. ponytail: reach for a schema lib once the contract count grows.\n */\nexport function parseDashboardView(json: unknown): Result<string, DashboardView> {\n if (typeof json !== 'object' || json === null) return err('dashboard-view: not an object');\n const dto = json as Partial<DashboardViewDto>;\n\n const reg = dto.registration;\n if (!reg || typeof reg.bigNummer !== 'string' || !reg.status || typeof reg.status.tag !== 'string') {\n return err('dashboard-view: missing/invalid registration');\n }\n const person = dto.person;\n if (!person || !person.adres || typeof person.adres.postcode !== 'string') {\n return err('dashboard-view: missing/invalid person');\n }\n const d = dto.decisions;\n if (!d || typeof d.eligibleForHerregistratie !== 'boolean') {\n return err('dashboard-view: missing/invalid decisions');\n }\n\n return ok({\n profile: { registration: reg, person },\n decisions: { eligibleForHerregistratie: d.eligibleForHerregistratie, herregistratieReason: d.herregistratieReason },\n });\n}\n",
"extends": [],
"type": "injectable"
},
{
"name": "DigidAdapter",
"id": "injectable-DigidAdapter-6b0a6d77a6f7411602d1c257ac45d4d45631556b1a468f7f38abfbbe375a3ac5e566fa6024ba56cb59f8418174ed2dbc6ea937b7b7999e58655acf01c7d7ecc9",
"file": "src/app/auth/infrastructure/digid.adapter.ts",
"properties": [],
"methods": [
{
"name": "authenticate",
"args": [
{
"name": "bsn",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "Promise<Result<string, Session>>",
"typeParameters": [],
"line": 10,
"deprecated": false,
"deprecationMessage": "",
"modifierKind": [
134
],
"jsdoctags": [
{
"name": "bsn",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Infrastructure: talks to the (mock) DigiD identity provider.</p>\n",
"rawdescription": "\nInfrastructure: talks to the (mock) DigiD identity provider.",
"sourceCode": "import { Injectable } from '@angular/core';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { Session } from '../domain/session';\n\n/** Infrastructure: talks to the (mock) DigiD identity provider. */\n@Injectable({ providedIn: 'root' })\nexport class DigidAdapter {\n // ponytail: fake DigiD — any 9-digit BSN authenticates to a fixed identity.\n // Swap for a real OIDC redirect flow when there's a backend.\n async authenticate(bsn: string): Promise<Result<string, Session>> {\n const t = bsn.trim();\n if (!/^\\d{9}$/.test(t)) return err('Voer een geldig BSN van 9 cijfers in.');\n return ok({ bsn: t, naam: 'Dr. A. (Anna) de Vries' });\n }\n}\n",
"extends": [],
"type": "injectable"
},
{
"name": "DuoAdapter",
"id": "injectable-DuoAdapter-616feea0d64b7da051ee732696d22103481dabb2238ec6ec39b7fdba3b8d67af05b0d4eaf45c3d1c0bddfbadd70cb5b603a56d5a231bd35be56366357866cdb3",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"properties": [],
"methods": [
{
"name": "diplomasResource",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 17,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Infrastructure adapter for the DUO diploma lookup, reached only through our own\n(&quot;BFF-lite&quot;) endpoint — the anti-corruption boundary. The response carries the\nuser&#39;s diplomas (each with its server-computed beroep + policy questions) and\nthe manual-entry fallback policy. The frontend renders; it does not derive. In\nthis POC the endpoint is a static mock.</p>\n",
"rawdescription": "\n\nInfrastructure adapter for the DUO diploma lookup, reached only through our own\n(\"BFF-lite\") endpoint — the anti-corruption boundary. The response carries the\nuser's diplomas (each with its server-computed beroep + policy questions) and\nthe manual-entry fallback policy. The frontend renders; it does not derive. In\nthis POC the endpoint is a static mock.\n",
"sourceCode": "import { Injectable } from '@angular/core';\nimport { httpResource } from '@angular/common/http';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { DuoLookupDto, DuoDiplomaDto, PolicyQuestionDto, ManualDiplomaPolicyDto } from '@registratie/contracts/duo-diplomas.dto';\n\nconst EMPTY: DuoLookupDto = { diplomas: [], handmatig: { beroepen: [], policyQuestions: [] } };\n\n/**\n * Infrastructure adapter for the DUO diploma lookup, reached only through our own\n * (\"BFF-lite\") endpoint — the anti-corruption boundary. The response carries the\n * user's diplomas (each with its server-computed beroep + policy questions) and\n * the manual-entry fallback policy. The frontend renders; it does not derive. In\n * this POC the endpoint is a static mock.\n */\n@Injectable({ providedIn: 'root' })\nexport class DuoAdapter {\n diplomasResource() {\n return httpResource<DuoLookupDto>(() => 'mock/duo-diplomas.json', { defaultValue: EMPTY });\n }\n}\n\nfunction parseQuestions(json: unknown): PolicyQuestionDto[] | null {\n if (!Array.isArray(json)) return null;\n const out: PolicyQuestionDto[] = [];\n for (const q of json) {\n if (typeof q !== 'object' || q === null) return null;\n const p = q as Partial<PolicyQuestionDto>;\n if (typeof p.id !== 'string' || typeof p.vraag !== 'string' || (p.type !== 'ja-nee' && p.type !== 'tekst')) return null;\n out.push({ id: p.id, vraag: p.vraag, type: p.type });\n }\n return out;\n}\n\n/** Trust-boundary parse: validate the untrusted response shape (diplomas, each\n with derived beroep + policy questions, plus the manual-entry fallback). */\nexport function parseDuoLookup(json: unknown): Result<string, DuoLookupDto> {\n if (typeof json !== 'object' || json === null) return err('duo-lookup: not an object');\n const dto = json as Partial<DuoLookupDto>;\n if (!Array.isArray(dto.diplomas)) return err('duo-lookup: missing diplomas');\n\n const diplomas: DuoDiplomaDto[] = [];\n for (const item of dto.diplomas) {\n if (typeof item !== 'object' || item === null) return err('duo-lookup: invalid diploma');\n const d = item as Partial<DuoDiplomaDto>;\n const vragen = parseQuestions(d.policyQuestions);\n if (typeof d.id !== 'string' || typeof d.naam !== 'string' || typeof d.beroep !== 'string' || vragen === null) {\n return err('duo-lookup: missing/invalid diploma fields');\n }\n diplomas.push({ id: d.id, naam: d.naam, instelling: d.instelling ?? '', jaar: typeof d.jaar === 'number' ? d.jaar : 0, beroep: d.beroep, policyQuestions: vragen });\n }\n\n const hm = dto.handmatig;\n const hmVragen = hm ? parseQuestions(hm.policyQuestions) : null;\n if (!hm || !Array.isArray(hm.beroepen) || hmVragen === null) {\n return err('duo-lookup: missing/invalid handmatig fallback');\n }\n const handmatig: ManualDiplomaPolicyDto = { beroepen: hm.beroepen, policyQuestions: hmVragen };\n\n return ok({ diplomas, handmatig });\n}\n",
"extends": [],
"type": "injectable"
},
{
"name": "SessionStore",
"id": "injectable-SessionStore-9e6ea772dc35cac2224969c550dc4edbeabbc2782278eaab988c670b860fd6232fe010ab80c4cfe06d81e3acf2ed437c648bed3e6e60d5c18f2a8c9ba3a83201",
"file": "src/app/auth/application/session.store.ts",
"properties": [
{
"name": "_session",
"defaultValue": "signal<Session | null>(restore())",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 30,
"modifierKind": [
123
]
},
{
"name": "digid",
"defaultValue": "inject(DigidAdapter)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 29,
"modifierKind": [
123
]
},
{
"name": "isAuthenticated",
"defaultValue": "computed(() => this._session() !== null)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 33,
"modifierKind": [
148
]
},
{
"name": "session",
"defaultValue": "this._session.asReadonly()",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 32,
"modifierKind": [
148
]
}
],
"methods": [
{
"name": "login",
"args": [
{
"name": "bsn",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "Promise<Result<string, Session>>",
"typeParameters": [],
"line": 44,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nEffectful command: authenticate, then store the session on success.",
"description": "<p>Effectful command: authenticate, then store the session on success.</p>\n",
"modifierKind": [
134
],
"jsdoctags": [
{
"name": "bsn",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "logout",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 50,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Holds the current session for the whole app. Because it is providedIn:&#39;root&#39;\nthere is exactly one instance — every component that injects it sees the same\nsession signal, so logging in is instantly visible everywhere (the guard, the\nheader, etc.). The session is mirrored to sessionStorage so a refresh or a\ndeep-link to a protected route keeps you logged in; it clears when the tab\ncloses. ponytail: sessionStorage, not localStorage — no cross-tab sync, which\nmatches a single-session portal.</p>\n",
"rawdescription": "\n\nHolds the current session for the whole app. Because it is providedIn:'root'\nthere is exactly one instance — every component that injects it sees the same\nsession signal, so logging in is instantly visible everywhere (the guard, the\nheader, etc.). The session is mirrored to sessionStorage so a refresh or a\ndeep-link to a protected route keeps you logged in; it clears when the tab\ncloses. ponytail: sessionStorage, not localStorage — no cross-tab sync, which\nmatches a single-session portal.\n",
"sourceCode": "import { Injectable, computed, effect, inject, signal } from '@angular/core';\nimport { Result } from '@shared/kernel/fp';\nimport { Session } from '../domain/session';\nimport { DigidAdapter } from '../infrastructure/digid.adapter';\n\nconst STORAGE_KEY = 'session-v1';\n\n/** Restore a persisted session (best-effort; corrupt entry → logged out). */\nfunction restore(): Session | null {\n try {\n const raw = sessionStorage.getItem(STORAGE_KEY);\n return raw ? (JSON.parse(raw) as Session) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Holds the current session for the whole app. Because it is providedIn:'root'\n * there is exactly one instance — every component that injects it sees the same\n * session signal, so logging in is instantly visible everywhere (the guard, the\n * header, etc.). The session is mirrored to sessionStorage so a refresh or a\n * deep-link to a protected route keeps you logged in; it clears when the tab\n * closes. ponytail: sessionStorage, not localStorage — no cross-tab sync, which\n * matches a single-session portal.\n */\n@Injectable({ providedIn: 'root' })\nexport class SessionStore {\n private digid = inject(DigidAdapter);\n private _session = signal<Session | null>(restore());\n\n readonly session = this._session.asReadonly();\n readonly isAuthenticated = computed(() => this._session() !== null);\n\n constructor() {\n effect(() => {\n const s = this._session();\n if (s) sessionStorage.setItem(STORAGE_KEY, JSON.stringify(s));\n else sessionStorage.removeItem(STORAGE_KEY);\n });\n }\n\n /** Effectful command: authenticate, then store the session on success. */\n async login(bsn: string): Promise<Result<string, Session>> {\n const r = await this.digid.authenticate(bsn);\n if (r.ok) this._session.set(r.value);\n return r;\n }\n\n logout() {\n this._session.set(null);\n }\n}\n",
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [],
"line": 33
},
"extends": [],
"type": "injectable"
}
],
"guards": [],
"interceptors": [],
"classes": [],
"directives": [
{
"name": "AsyncEmptyDirective",
"id": "directive-AsyncEmptyDirective-0d66d917cda6e95349b21544c6fed09ee792cd86cf353cab9a71395a29342dfc9115ba1a9c915bb1851c09330826035b08479c514c9e47f9a464e03c3f71675c",
"file": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"description": "",
"rawdescription": "\n",
"sourceCode": "import { Component, Directive, TemplateRef, computed, contentChild, input } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport type { Resource } from '@angular/core';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { RemoteData, fromResource, foldRemote } from '@shared/application/remote-data';\n\n/* Slot markers. Put on <ng-template> children of <app-async>. */\n@Directive({ selector: '[appAsyncLoaded]' })\nexport class AsyncLoadedDirective {\n constructor(public tpl: TemplateRef<{ $implicit: unknown }>) {}\n}\n@Directive({ selector: '[appAsyncLoading]' })\nexport class AsyncLoadingDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncError]' })\nexport class AsyncErrorDirective {\n constructor(public tpl: TemplateRef<{ $implicit: Error | undefined; retry: () => void }>) {}\n}\n\n/**\n * Renders exactly ONE of loading / empty / error / loaded for a signal-based\n * resource (e.g. httpResource). Built on a RemoteData tagged union (see\n * core/remote-data.ts), so the states are mutually exclusive by construction —\n * the UI can never show two at once (\"impossible states\"). Unprovided slots\n * fall back to sensible defaults.\n */\n@Component({\n selector: 'app-async',\n imports: [NgTemplateOutlet, SpinnerComponent, AlertComponent, ButtonComponent],\n template: `\n <div aria-live=\"polite\" [attr.aria-busy]=\"rd().tag === 'Loading' ? 'true' : null\">\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { <ng-container [ngTemplateOutlet]=\"loadingTpl()!.tpl\" /> }\n @else { <app-spinner /> }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n <ng-container [ngTemplateOutlet]=\"errorTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: error(), retry: retry }\" />\n } @else {\n <app-alert type=\"error\">Er ging iets mis bij het laden van de gegevens.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"retry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { <ng-container [ngTemplateOutlet]=\"emptyTpl()!.tpl\" /> }\n @else { <p class=\"rhc-paragraph\">Geen gegevens gevonden.</p> }\n }\n @case ('Success') {\n <ng-container [ngTemplateOutlet]=\"loadedTpl().tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value() }\" />\n }\n }\n </div>\n `,\n})\nexport class AsyncComponent<T> {\n // Two ways to feed this component:\n // [resource] — a raw httpResource (the common case), or\n // [data] — an already-combined RemoteData (e.g. from a store via map2).\n resource = input<Resource<T>>();\n data = input<RemoteData<Error | undefined, T>>();\n isEmpty = input<(v: T) => boolean>(() => false);\n\n loadedTpl = contentChild.required(AsyncLoadedDirective);\n loadingTpl = contentChild(AsyncLoadingDirective);\n emptyTpl = contentChild(AsyncEmptyDirective);\n errorTpl = contentChild(AsyncErrorDirective);\n\n // Single source of truth: the supplied RemoteData, or the resource projected into one.\n protected rd = computed<RemoteData<Error | undefined, T>>(() => {\n const data = this.data();\n if (data) return data;\n const r = this.resource();\n return r ? fromResource(r, this.isEmpty()) : { tag: 'Loading' };\n });\n\n // value/error are pulled out via the exhaustive fold — only Success carries a\n // value, only Failure carries an error, so these can't lie.\n protected value = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: () => undefined, success: (v) => v }),\n );\n protected error = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: (e) => e, success: () => undefined }),\n );\n\n retry = () => {\n const r = this.resource();\n if (r && 'reload' in r && typeof (r as { reload?: unknown }).reload === 'function') {\n (r as { reload: () => void }).reload();\n }\n };\n}\n\n/** Convenience: import this array to get the wrapper + all slot directives. */\nexport const ASYNC = [\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const;\n",
"selector": "[appAsyncEmpty]",
"providers": [],
"hostDirectives": [],
"standalone": false,
"inputsClass": [],
"outputsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"propertiesClass": [
{
"name": "tpl",
"deprecated": false,
"deprecationMessage": "",
"type": "TemplateRef<unknown>",
"indexKey": "",
"optional": false,
"description": "",
"line": 20,
"modifierKind": [
125
]
}
],
"methodsClass": [],
"extends": [],
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [
{
"name": "tpl",
"type": "TemplateRef<unknown>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"line": 19,
"jsdoctags": [
{
"name": "tpl",
"type": "TemplateRef<unknown>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
},
{
"name": "AsyncErrorDirective",
"id": "directive-AsyncErrorDirective-0d66d917cda6e95349b21544c6fed09ee792cd86cf353cab9a71395a29342dfc9115ba1a9c915bb1851c09330826035b08479c514c9e47f9a464e03c3f71675c",
"file": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"description": "",
"rawdescription": "\n",
"sourceCode": "import { Component, Directive, TemplateRef, computed, contentChild, input } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport type { Resource } from '@angular/core';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { RemoteData, fromResource, foldRemote } from '@shared/application/remote-data';\n\n/* Slot markers. Put on <ng-template> children of <app-async>. */\n@Directive({ selector: '[appAsyncLoaded]' })\nexport class AsyncLoadedDirective {\n constructor(public tpl: TemplateRef<{ $implicit: unknown }>) {}\n}\n@Directive({ selector: '[appAsyncLoading]' })\nexport class AsyncLoadingDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncError]' })\nexport class AsyncErrorDirective {\n constructor(public tpl: TemplateRef<{ $implicit: Error | undefined; retry: () => void }>) {}\n}\n\n/**\n * Renders exactly ONE of loading / empty / error / loaded for a signal-based\n * resource (e.g. httpResource). Built on a RemoteData tagged union (see\n * core/remote-data.ts), so the states are mutually exclusive by construction —\n * the UI can never show two at once (\"impossible states\"). Unprovided slots\n * fall back to sensible defaults.\n */\n@Component({\n selector: 'app-async',\n imports: [NgTemplateOutlet, SpinnerComponent, AlertComponent, ButtonComponent],\n template: `\n <div aria-live=\"polite\" [attr.aria-busy]=\"rd().tag === 'Loading' ? 'true' : null\">\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { <ng-container [ngTemplateOutlet]=\"loadingTpl()!.tpl\" /> }\n @else { <app-spinner /> }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n <ng-container [ngTemplateOutlet]=\"errorTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: error(), retry: retry }\" />\n } @else {\n <app-alert type=\"error\">Er ging iets mis bij het laden van de gegevens.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"retry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { <ng-container [ngTemplateOutlet]=\"emptyTpl()!.tpl\" /> }\n @else { <p class=\"rhc-paragraph\">Geen gegevens gevonden.</p> }\n }\n @case ('Success') {\n <ng-container [ngTemplateOutlet]=\"loadedTpl().tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value() }\" />\n }\n }\n </div>\n `,\n})\nexport class AsyncComponent<T> {\n // Two ways to feed this component:\n // [resource] — a raw httpResource (the common case), or\n // [data] — an already-combined RemoteData (e.g. from a store via map2).\n resource = input<Resource<T>>();\n data = input<RemoteData<Error | undefined, T>>();\n isEmpty = input<(v: T) => boolean>(() => false);\n\n loadedTpl = contentChild.required(AsyncLoadedDirective);\n loadingTpl = contentChild(AsyncLoadingDirective);\n emptyTpl = contentChild(AsyncEmptyDirective);\n errorTpl = contentChild(AsyncErrorDirective);\n\n // Single source of truth: the supplied RemoteData, or the resource projected into one.\n protected rd = computed<RemoteData<Error | undefined, T>>(() => {\n const data = this.data();\n if (data) return data;\n const r = this.resource();\n return r ? fromResource(r, this.isEmpty()) : { tag: 'Loading' };\n });\n\n // value/error are pulled out via the exhaustive fold — only Success carries a\n // value, only Failure carries an error, so these can't lie.\n protected value = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: () => undefined, success: (v) => v }),\n );\n protected error = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: (e) => e, success: () => undefined }),\n );\n\n retry = () => {\n const r = this.resource();\n if (r && 'reload' in r && typeof (r as { reload?: unknown }).reload === 'function') {\n (r as { reload: () => void }).reload();\n }\n };\n}\n\n/** Convenience: import this array to get the wrapper + all slot directives. */\nexport const ASYNC = [\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const;\n",
"selector": "[appAsyncError]",
"providers": [],
"hostDirectives": [],
"standalone": false,
"inputsClass": [],
"outputsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"propertiesClass": [
{
"name": "tpl",
"deprecated": false,
"deprecationMessage": "",
"type": "TemplateRef<literal type>",
"indexKey": "",
"optional": false,
"description": "",
"line": 24,
"modifierKind": [
125
]
}
],
"methodsClass": [],
"extends": [],
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [
{
"name": "tpl",
"type": "TemplateRef<literal type>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"line": 23,
"jsdoctags": [
{
"name": "tpl",
"type": "TemplateRef<literal type>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
},
{
"name": "AsyncLoadedDirective",
"id": "directive-AsyncLoadedDirective-0d66d917cda6e95349b21544c6fed09ee792cd86cf353cab9a71395a29342dfc9115ba1a9c915bb1851c09330826035b08479c514c9e47f9a464e03c3f71675c",
"file": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"description": "",
"rawdescription": "\n",
"sourceCode": "import { Component, Directive, TemplateRef, computed, contentChild, input } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport type { Resource } from '@angular/core';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { RemoteData, fromResource, foldRemote } from '@shared/application/remote-data';\n\n/* Slot markers. Put on <ng-template> children of <app-async>. */\n@Directive({ selector: '[appAsyncLoaded]' })\nexport class AsyncLoadedDirective {\n constructor(public tpl: TemplateRef<{ $implicit: unknown }>) {}\n}\n@Directive({ selector: '[appAsyncLoading]' })\nexport class AsyncLoadingDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncError]' })\nexport class AsyncErrorDirective {\n constructor(public tpl: TemplateRef<{ $implicit: Error | undefined; retry: () => void }>) {}\n}\n\n/**\n * Renders exactly ONE of loading / empty / error / loaded for a signal-based\n * resource (e.g. httpResource). Built on a RemoteData tagged union (see\n * core/remote-data.ts), so the states are mutually exclusive by construction —\n * the UI can never show two at once (\"impossible states\"). Unprovided slots\n * fall back to sensible defaults.\n */\n@Component({\n selector: 'app-async',\n imports: [NgTemplateOutlet, SpinnerComponent, AlertComponent, ButtonComponent],\n template: `\n <div aria-live=\"polite\" [attr.aria-busy]=\"rd().tag === 'Loading' ? 'true' : null\">\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { <ng-container [ngTemplateOutlet]=\"loadingTpl()!.tpl\" /> }\n @else { <app-spinner /> }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n <ng-container [ngTemplateOutlet]=\"errorTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: error(), retry: retry }\" />\n } @else {\n <app-alert type=\"error\">Er ging iets mis bij het laden van de gegevens.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"retry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { <ng-container [ngTemplateOutlet]=\"emptyTpl()!.tpl\" /> }\n @else { <p class=\"rhc-paragraph\">Geen gegevens gevonden.</p> }\n }\n @case ('Success') {\n <ng-container [ngTemplateOutlet]=\"loadedTpl().tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value() }\" />\n }\n }\n </div>\n `,\n})\nexport class AsyncComponent<T> {\n // Two ways to feed this component:\n // [resource] — a raw httpResource (the common case), or\n // [data] — an already-combined RemoteData (e.g. from a store via map2).\n resource = input<Resource<T>>();\n data = input<RemoteData<Error | undefined, T>>();\n isEmpty = input<(v: T) => boolean>(() => false);\n\n loadedTpl = contentChild.required(AsyncLoadedDirective);\n loadingTpl = contentChild(AsyncLoadingDirective);\n emptyTpl = contentChild(AsyncEmptyDirective);\n errorTpl = contentChild(AsyncErrorDirective);\n\n // Single source of truth: the supplied RemoteData, or the resource projected into one.\n protected rd = computed<RemoteData<Error | undefined, T>>(() => {\n const data = this.data();\n if (data) return data;\n const r = this.resource();\n return r ? fromResource(r, this.isEmpty()) : { tag: 'Loading' };\n });\n\n // value/error are pulled out via the exhaustive fold — only Success carries a\n // value, only Failure carries an error, so these can't lie.\n protected value = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: () => undefined, success: (v) => v }),\n );\n protected error = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: (e) => e, success: () => undefined }),\n );\n\n retry = () => {\n const r = this.resource();\n if (r && 'reload' in r && typeof (r as { reload?: unknown }).reload === 'function') {\n (r as { reload: () => void }).reload();\n }\n };\n}\n\n/** Convenience: import this array to get the wrapper + all slot directives. */\nexport const ASYNC = [\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const;\n",
"selector": "[appAsyncLoaded]",
"providers": [],
"hostDirectives": [],
"standalone": false,
"inputsClass": [],
"outputsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"propertiesClass": [
{
"name": "tpl",
"deprecated": false,
"deprecationMessage": "",
"type": "TemplateRef<literal type>",
"indexKey": "",
"optional": false,
"description": "",
"line": 12,
"modifierKind": [
125
]
}
],
"methodsClass": [],
"extends": [],
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [
{
"name": "tpl",
"type": "TemplateRef<literal type>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"line": 11,
"jsdoctags": [
{
"name": "tpl",
"type": "TemplateRef<literal type>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
},
{
"name": "AsyncLoadingDirective",
"id": "directive-AsyncLoadingDirective-0d66d917cda6e95349b21544c6fed09ee792cd86cf353cab9a71395a29342dfc9115ba1a9c915bb1851c09330826035b08479c514c9e47f9a464e03c3f71675c",
"file": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"description": "",
"rawdescription": "\n",
"sourceCode": "import { Component, Directive, TemplateRef, computed, contentChild, input } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport type { Resource } from '@angular/core';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { RemoteData, fromResource, foldRemote } from '@shared/application/remote-data';\n\n/* Slot markers. Put on <ng-template> children of <app-async>. */\n@Directive({ selector: '[appAsyncLoaded]' })\nexport class AsyncLoadedDirective {\n constructor(public tpl: TemplateRef<{ $implicit: unknown }>) {}\n}\n@Directive({ selector: '[appAsyncLoading]' })\nexport class AsyncLoadingDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncError]' })\nexport class AsyncErrorDirective {\n constructor(public tpl: TemplateRef<{ $implicit: Error | undefined; retry: () => void }>) {}\n}\n\n/**\n * Renders exactly ONE of loading / empty / error / loaded for a signal-based\n * resource (e.g. httpResource). Built on a RemoteData tagged union (see\n * core/remote-data.ts), so the states are mutually exclusive by construction —\n * the UI can never show two at once (\"impossible states\"). Unprovided slots\n * fall back to sensible defaults.\n */\n@Component({\n selector: 'app-async',\n imports: [NgTemplateOutlet, SpinnerComponent, AlertComponent, ButtonComponent],\n template: `\n <div aria-live=\"polite\" [attr.aria-busy]=\"rd().tag === 'Loading' ? 'true' : null\">\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { <ng-container [ngTemplateOutlet]=\"loadingTpl()!.tpl\" /> }\n @else { <app-spinner /> }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n <ng-container [ngTemplateOutlet]=\"errorTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: error(), retry: retry }\" />\n } @else {\n <app-alert type=\"error\">Er ging iets mis bij het laden van de gegevens.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"retry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { <ng-container [ngTemplateOutlet]=\"emptyTpl()!.tpl\" /> }\n @else { <p class=\"rhc-paragraph\">Geen gegevens gevonden.</p> }\n }\n @case ('Success') {\n <ng-container [ngTemplateOutlet]=\"loadedTpl().tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value() }\" />\n }\n }\n </div>\n `,\n})\nexport class AsyncComponent<T> {\n // Two ways to feed this component:\n // [resource] — a raw httpResource (the common case), or\n // [data] — an already-combined RemoteData (e.g. from a store via map2).\n resource = input<Resource<T>>();\n data = input<RemoteData<Error | undefined, T>>();\n isEmpty = input<(v: T) => boolean>(() => false);\n\n loadedTpl = contentChild.required(AsyncLoadedDirective);\n loadingTpl = contentChild(AsyncLoadingDirective);\n emptyTpl = contentChild(AsyncEmptyDirective);\n errorTpl = contentChild(AsyncErrorDirective);\n\n // Single source of truth: the supplied RemoteData, or the resource projected into one.\n protected rd = computed<RemoteData<Error | undefined, T>>(() => {\n const data = this.data();\n if (data) return data;\n const r = this.resource();\n return r ? fromResource(r, this.isEmpty()) : { tag: 'Loading' };\n });\n\n // value/error are pulled out via the exhaustive fold — only Success carries a\n // value, only Failure carries an error, so these can't lie.\n protected value = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: () => undefined, success: (v) => v }),\n );\n protected error = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: (e) => e, success: () => undefined }),\n );\n\n retry = () => {\n const r = this.resource();\n if (r && 'reload' in r && typeof (r as { reload?: unknown }).reload === 'function') {\n (r as { reload: () => void }).reload();\n }\n };\n}\n\n/** Convenience: import this array to get the wrapper + all slot directives. */\nexport const ASYNC = [\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const;\n",
"selector": "[appAsyncLoading]",
"providers": [],
"hostDirectives": [],
"standalone": false,
"inputsClass": [],
"outputsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"propertiesClass": [
{
"name": "tpl",
"deprecated": false,
"deprecationMessage": "",
"type": "TemplateRef<unknown>",
"indexKey": "",
"optional": false,
"description": "",
"line": 16,
"modifierKind": [
125
]
}
],
"methodsClass": [],
"extends": [],
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [
{
"name": "tpl",
"type": "TemplateRef<unknown>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"line": 15,
"jsdoctags": [
{
"name": "tpl",
"type": "TemplateRef<unknown>",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
}
],
"components": [
{
"name": "AlertComponent",
"id": "component-AlertComponent-7d03ee2c0179c2f8d44795112c823951406810027571b5471bc765873f1aeaee550dcb89c6ebd8514ee36f2d96c8c6ba118293bf44a4a1316f61e91fa3c06f4e",
"file": "src/app/shared/ui/alert/alert.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-alert",
"styleUrls": [],
"styles": [],
"template": "<div\n class=\"utrecht-alert rhc-alert\"\n [class.utrecht-alert--info]=\"type() === 'info'\"\n [class.utrecht-alert--ok]=\"type() === 'ok'\"\n [class.utrecht-alert--warning]=\"type() === 'warning'\"\n [class.utrecht-alert--error]=\"type() === 'error'\"\n role=\"status\">\n <ng-content />\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "type",
"defaultValue": "'info'",
"deprecated": false,
"deprecationMessage": "",
"type": "AlertType",
"indexKey": "",
"optional": false,
"description": "",
"line": 21,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: alert/message banner.</p>\n",
"rawdescription": "\nAtom: alert/message banner.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\ntype AlertType = 'info' | 'ok' | 'warning' | 'error';\n\n/** Atom: alert/message banner. */\n@Component({\n selector: 'app-alert',\n template: `\n <div\n class=\"utrecht-alert rhc-alert\"\n [class.utrecht-alert--info]=\"type() === 'info'\"\n [class.utrecht-alert--ok]=\"type() === 'ok'\"\n [class.utrecht-alert--warning]=\"type() === 'warning'\"\n [class.utrecht-alert--error]=\"type() === 'error'\"\n role=\"status\">\n <ng-content />\n </div>\n `,\n})\nexport class AlertComponent {\n type = input<AlertType>('info');\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "App",
"id": "component-App-a5889755e6aaa6ebd2f471f1253c27e411d38a42361e18619621ef0e6c5a91de3cf6ba4ace13bbe1756691f1f5e80728717fd18aab2cef50180bd579fa218a6d",
"file": "src/app/app.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-root",
"styleUrls": [],
"styles": [],
"template": "<router-outlet />",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "RouterOutlet"
}
],
"description": "",
"rawdescription": "\n",
"type": "component",
"sourceCode": "import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\n\n@Component({\n selector: 'app-root',\n imports: [RouterOutlet],\n template: '<router-outlet />',\n})\nexport class App {}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "AsyncComponent",
"id": "component-AsyncComponent-0d66d917cda6e95349b21544c6fed09ee792cd86cf353cab9a71395a29342dfc9115ba1a9c915bb1851c09330826035b08479c514c9e47f9a464e03c3f71675c",
"file": "src/app/shared/ui/async/async.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-async",
"styleUrls": [],
"styles": [],
"template": "<div aria-live=\"polite\" [attr.aria-busy]=\"rd().tag === 'Loading' ? 'true' : null\">\n@switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { <ng-container [ngTemplateOutlet]=\"loadingTpl()!.tpl\" /> }\n @else { <app-spinner /> }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n <ng-container [ngTemplateOutlet]=\"errorTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: error(), retry: retry }\" />\n } @else {\n <app-alert type=\"error\">Er ging iets mis bij het laden van de gegevens.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"retry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { <ng-container [ngTemplateOutlet]=\"emptyTpl()!.tpl\" /> }\n @else { <p class=\"rhc-paragraph\">Geen gegevens gevonden.</p> }\n }\n @case ('Success') {\n <ng-container [ngTemplateOutlet]=\"loadedTpl().tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value() }\" />\n }\n}\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "data",
"deprecated": false,
"deprecationMessage": "",
"type": "RemoteData<Error | undefined, T>",
"indexKey": "",
"optional": false,
"description": "",
"line": 72,
"required": false
},
{
"name": "isEmpty",
"defaultValue": "() => false",
"deprecated": false,
"deprecationMessage": "",
"type": "(v: T) => boolean",
"indexKey": "",
"optional": false,
"description": "",
"line": 73,
"required": false
},
{
"name": "resource",
"deprecated": false,
"deprecationMessage": "",
"type": "Resource<T>",
"indexKey": "",
"optional": false,
"description": "",
"line": 71,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "emptyTpl",
"defaultValue": "contentChild(AsyncEmptyDirective)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 77
},
{
"name": "error",
"defaultValue": "computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: (e) => e, success: () => undefined }),\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 93,
"modifierKind": [
124
]
},
{
"name": "errorTpl",
"defaultValue": "contentChild(AsyncErrorDirective)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 78
},
{
"name": "loadedTpl",
"defaultValue": "contentChild.required(AsyncLoadedDirective)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 75
},
{
"name": "loadingTpl",
"defaultValue": "contentChild(AsyncLoadingDirective)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 76
},
{
"name": "rd",
"defaultValue": "computed<RemoteData<Error | undefined, T>>(() => {\n const data = this.data();\n if (data) return data;\n const r = this.resource();\n return r ? fromResource(r, this.isEmpty()) : { tag: 'Loading' };\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 81,
"modifierKind": [
124
]
},
{
"name": "retry",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 97
},
{
"name": "value",
"defaultValue": "computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: () => undefined, success: (v) => v }),\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 90,
"modifierKind": [
124
]
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "NgTemplateOutlet"
},
{
"name": "SpinnerComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "ButtonComponent",
"type": "component"
}
],
"description": "<p>Renders exactly ONE of loading / empty / error / loaded for a signal-based\nresource (e.g. httpResource). Built on a RemoteData tagged union (see\ncore/remote-data.ts), so the states are mutually exclusive by construction —\nthe UI can never show two at once (&quot;impossible states&quot;). Unprovided slots\nfall back to sensible defaults.</p>\n",
"rawdescription": "\n\nRenders exactly ONE of loading / empty / error / loaded for a signal-based\nresource (e.g. httpResource). Built on a RemoteData tagged union (see\ncore/remote-data.ts), so the states are mutually exclusive by construction —\nthe UI can never show two at once (\"impossible states\"). Unprovided slots\nfall back to sensible defaults.\n",
"type": "component",
"sourceCode": "import { Component, Directive, TemplateRef, computed, contentChild, input } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\nimport type { Resource } from '@angular/core';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { RemoteData, fromResource, foldRemote } from '@shared/application/remote-data';\n\n/* Slot markers. Put on <ng-template> children of <app-async>. */\n@Directive({ selector: '[appAsyncLoaded]' })\nexport class AsyncLoadedDirective {\n constructor(public tpl: TemplateRef<{ $implicit: unknown }>) {}\n}\n@Directive({ selector: '[appAsyncLoading]' })\nexport class AsyncLoadingDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef<unknown>) {}\n}\n@Directive({ selector: '[appAsyncError]' })\nexport class AsyncErrorDirective {\n constructor(public tpl: TemplateRef<{ $implicit: Error | undefined; retry: () => void }>) {}\n}\n\n/**\n * Renders exactly ONE of loading / empty / error / loaded for a signal-based\n * resource (e.g. httpResource). Built on a RemoteData tagged union (see\n * core/remote-data.ts), so the states are mutually exclusive by construction —\n * the UI can never show two at once (\"impossible states\"). Unprovided slots\n * fall back to sensible defaults.\n */\n@Component({\n selector: 'app-async',\n imports: [NgTemplateOutlet, SpinnerComponent, AlertComponent, ButtonComponent],\n template: `\n <div aria-live=\"polite\" [attr.aria-busy]=\"rd().tag === 'Loading' ? 'true' : null\">\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { <ng-container [ngTemplateOutlet]=\"loadingTpl()!.tpl\" /> }\n @else { <app-spinner /> }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n <ng-container [ngTemplateOutlet]=\"errorTpl()!.tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: error(), retry: retry }\" />\n } @else {\n <app-alert type=\"error\">Er ging iets mis bij het laden van de gegevens.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"retry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { <ng-container [ngTemplateOutlet]=\"emptyTpl()!.tpl\" /> }\n @else { <p class=\"rhc-paragraph\">Geen gegevens gevonden.</p> }\n }\n @case ('Success') {\n <ng-container [ngTemplateOutlet]=\"loadedTpl().tpl\"\n [ngTemplateOutletContext]=\"{ $implicit: value() }\" />\n }\n }\n </div>\n `,\n})\nexport class AsyncComponent<T> {\n // Two ways to feed this component:\n // [resource] — a raw httpResource (the common case), or\n // [data] — an already-combined RemoteData (e.g. from a store via map2).\n resource = input<Resource<T>>();\n data = input<RemoteData<Error | undefined, T>>();\n isEmpty = input<(v: T) => boolean>(() => false);\n\n loadedTpl = contentChild.required(AsyncLoadedDirective);\n loadingTpl = contentChild(AsyncLoadingDirective);\n emptyTpl = contentChild(AsyncEmptyDirective);\n errorTpl = contentChild(AsyncErrorDirective);\n\n // Single source of truth: the supplied RemoteData, or the resource projected into one.\n protected rd = computed<RemoteData<Error | undefined, T>>(() => {\n const data = this.data();\n if (data) return data;\n const r = this.resource();\n return r ? fromResource(r, this.isEmpty()) : { tag: 'Loading' };\n });\n\n // value/error are pulled out via the exhaustive fold — only Success carries a\n // value, only Failure carries an error, so these can't lie.\n protected value = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: () => undefined, success: (v) => v }),\n );\n protected error = computed(() =>\n foldRemote(this.rd(), { loading: () => undefined, empty: () => undefined, failure: (e) => e, success: () => undefined }),\n );\n\n retry = () => {\n const r = this.resource();\n if (r && 'reload' in r && typeof (r as { reload?: unknown }).reload === 'function') {\n (r as { reload: () => void }).reload();\n }\n };\n}\n\n/** Convenience: import this array to get the wrapper + all slot directives. */\nexport const ASYNC = [\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const;\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "BreadcrumbComponent",
"id": "component-BreadcrumbComponent-4dedba5c509fabd51900b3062e33290365b01e1fdc64a11c5ce7fe12f20201b340d7fcfbe61254b0d0b7a386ab40a29795bd467542fb1a353f4d9bda46e65688",
"file": "src/app/shared/layout/breadcrumb/breadcrumb.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-breadcrumb",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .list{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;list-style:none;margin:0;padding:0}\n .sep{color:var(--rhc-color-foreground-subtle)}\n "
],
"template": "<nav class=\"rhc-breadcrumb-nav utrecht-breadcrumb-nav\" aria-label=\"Kruimelpad\">\n <ol class=\"utrecht-breadcrumb-nav__list list\">\n @for (item of items(); track item.label; let last = $last) {\n <li class=\"utrecht-breadcrumb-nav__item\">\n @if (item.link && !last) {\n <a class=\"utrecht-breadcrumb-nav__link rhc-link nl-link\" [routerLink]=\"item.link\">{{ item.label }}</a>\n <span class=\"sep\" aria-hidden=\"true\">/</span>\n } @else {\n <span class=\"utrecht-breadcrumb-nav__link rhc-breadcrumb-nav__link--current\" aria-current=\"page\">{{ item.label }}</span>\n }\n </li>\n }\n </ol>\n</nav>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "items",
"deprecated": false,
"deprecationMessage": "",
"type": "BreadcrumbItem[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 38,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "RouterLink"
}
],
"description": "<p>Chrome: breadcrumb navigation. Renders the RHC/Utrecht breadcrumb pattern; the\nlast item is the current page (no link, aria-current). Domain-free — the caller\nsupplies the trail.</p>\n",
"rawdescription": "\nChrome: breadcrumb navigation. Renders the RHC/Utrecht breadcrumb pattern; the\nlast item is the current page (no link, aria-current). Domain-free — the caller\nsupplies the trail.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\nexport interface BreadcrumbItem {\n label: string;\n link?: string; // omit on the current (last) page\n}\n\n/** Chrome: breadcrumb navigation. Renders the RHC/Utrecht breadcrumb pattern; the\n last item is the current page (no link, aria-current). Domain-free — the caller\n supplies the trail. */\n@Component({\n selector: 'app-breadcrumb',\n imports: [RouterLink],\n styles: [`\n :host{display:block}\n .list{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;list-style:none;margin:0;padding:0}\n .sep{color:var(--rhc-color-foreground-subtle)}\n `],\n template: `\n <nav class=\"rhc-breadcrumb-nav utrecht-breadcrumb-nav\" aria-label=\"Kruimelpad\">\n <ol class=\"utrecht-breadcrumb-nav__list list\">\n @for (item of items(); track item.label; let last = $last) {\n <li class=\"utrecht-breadcrumb-nav__item\">\n @if (item.link && !last) {\n <a class=\"utrecht-breadcrumb-nav__link rhc-link nl-link\" [routerLink]=\"item.link\">{{ item.label }}</a>\n <span class=\"sep\" aria-hidden=\"true\">/</span>\n } @else {\n <span class=\"utrecht-breadcrumb-nav__link rhc-breadcrumb-nav__link--current\" aria-current=\"page\">{{ item.label }}</span>\n }\n </li>\n }\n </ol>\n </nav>\n `,\n})\nexport class BreadcrumbComponent {\n items = input.required<BreadcrumbItem[]>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .list{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;list-style:none;margin:0;padding:0}\n .sep{color:var(--rhc-color-foreground-subtle)}\n \n",
"extends": []
},
{
"name": "ButtonComponent",
"id": "component-ButtonComponent-6eb936521438d91cc3a2276d1b8e366496e907e8a01d7b84b327c3cc31fa1dfadbb9502c6646472736722cf7ced9950fcaa4d8435ddf28224b3e7c9b7bd29505",
"file": "src/app/shared/ui/button/button.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-button",
"styleUrls": [],
"styles": [],
"template": "<button\n [type]=\"type()\"\n [disabled]=\"disabled()\"\n class=\"utrecht-button rhc-button\"\n [class.utrecht-button--primary-action]=\"variant() === 'primary'\"\n [class.utrecht-button--secondary-action]=\"variant() === 'secondary'\"\n [class.utrecht-button--subtle]=\"variant() === 'subtle'\"\n [class.utrecht-button--danger]=\"variant() === 'danger'\"\n [class.utrecht-button--disabled]=\"disabled()\">\n <ng-content />\n</button>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "disabled",
"defaultValue": "false",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 25,
"required": false
},
{
"name": "type",
"defaultValue": "'button'",
"deprecated": false,
"deprecationMessage": "",
"type": "\"button\" | \"submit\"",
"indexKey": "",
"optional": false,
"description": "",
"line": 24,
"required": false
},
{
"name": "variant",
"defaultValue": "'primary'",
"deprecated": false,
"deprecationMessage": "",
"type": "Variant",
"indexKey": "",
"optional": false,
"description": "",
"line": 23,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: button. Thin wrapper over the Utrecht/RHC button CSS.</p>\n",
"rawdescription": "\nAtom: button. Thin wrapper over the Utrecht/RHC button CSS.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\ntype Variant = 'primary' | 'secondary' | 'subtle' | 'danger';\n\n/** Atom: button. Thin wrapper over the Utrecht/RHC button CSS. */\n@Component({\n selector: 'app-button',\n template: `\n <button\n [type]=\"type()\"\n [disabled]=\"disabled()\"\n class=\"utrecht-button rhc-button\"\n [class.utrecht-button--primary-action]=\"variant() === 'primary'\"\n [class.utrecht-button--secondary-action]=\"variant() === 'secondary'\"\n [class.utrecht-button--subtle]=\"variant() === 'subtle'\"\n [class.utrecht-button--danger]=\"variant() === 'danger'\"\n [class.utrecht-button--disabled]=\"disabled()\">\n <ng-content />\n </button>\n `,\n})\nexport class ButtonComponent {\n variant = input<Variant>('primary');\n type = input<'button' | 'submit'>('button');\n disabled = input(false);\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "ChangeRequestFormComponent",
"id": "component-ChangeRequestFormComponent-8d79b6a9ce75f62f4e04f9d3ed0867e00d005c9c05cf267503536c4eec9813372aff51a3c8686927fd07f46503b60ddb4107edcc5f57fdfa1b6b52fe2a98026c",
"file": "src/app/registratie/ui/change-request-form/change-request-form.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-change-request-form",
"styleUrls": [],
"styles": [],
"template": "<app-heading [level]=\"2\">Adreswijziging doorgeven</app-heading>\n<form (ngSubmit)=\"onSubmit()\" style=\"max-width:28rem\">\n <app-form-field label=\"Straat en huisnummer\" fieldId=\"street\" [error]=\"streetError()\">\n <app-text-input inputId=\"street\" [(ngModel)]=\"street\" name=\"street\" [invalid]=\"!!streetError()\" />\n </app-form-field>\n <app-form-field label=\"Postcode\" fieldId=\"zip\" [error]=\"zipError()\">\n <app-text-input inputId=\"zip\" [(ngModel)]=\"zip\" name=\"zip\" placeholder=\"1234 AB\" [invalid]=\"!!zipError()\" />\n </app-form-field>\n <app-form-field label=\"Woonplaats\" fieldId=\"city\">\n <app-text-input inputId=\"city\" [(ngModel)]=\"city\" name=\"city\" />\n </app-form-field>\n <div style=\"margin-top:1rem\">\n <app-button type=\"submit\" variant=\"primary\">Wijziging indienen</app-button>\n </div>\n</form>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [
{
"name": "submitted",
"deprecated": false,
"deprecationMessage": "",
"type": "ChangeRequest",
"indexKey": "",
"optional": false,
"description": "",
"line": 47,
"required": false
}
],
"propertiesClass": [
{
"name": "city",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 44
},
{
"name": "street",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 42
},
{
"name": "streetError",
"defaultValue": "signal('')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 45
},
{
"name": "zip",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 43
},
{
"name": "zipError",
"defaultValue": "signal('')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 46
}
],
"methodsClass": [
{
"name": "onSubmit",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 49,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "FormsModule",
"type": "module"
},
{
"name": "FormFieldComponent",
"type": "component"
},
{
"name": "TextInputComponent",
"type": "component"
},
{
"name": "ButtonComponent",
"type": "component"
},
{
"name": "HeadingComponent",
"type": "component"
}
],
"description": "<p>Organism: change-request (adreswijziging) form. Reuses the same form-field\nmolecule + text-input/button atoms as the login form. Field errors come\nstraight from the parser&#39;s Result — no parallel &quot;is it valid&quot; flag to drift.</p>\n",
"rawdescription": "\nOrganism: change-request (adreswijziging) form. Reuses the same form-field\nmolecule + text-input/button atoms as the login form. Field errors come\nstraight from the parser's Result — no parallel \"is it valid\" flag to drift.",
"type": "component",
"sourceCode": "import { Component, output, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\n\n/** A submitted change request carries a *parsed* postcode (branded Postcode),\n not a raw string — downstream code can't receive an unvalidated one. */\nexport interface ChangeRequest {\n street: string;\n zip: Postcode;\n city: string;\n}\n\n/** Organism: change-request (adreswijziging) form. Reuses the same form-field\n molecule + text-input/button atoms as the login form. Field errors come\n straight from the parser's Result — no parallel \"is it valid\" flag to drift. */\n@Component({\n selector: 'app-change-request-form',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent, HeadingComponent],\n template: `\n <app-heading [level]=\"2\">Adreswijziging doorgeven</app-heading>\n <form (ngSubmit)=\"onSubmit()\" style=\"max-width:28rem\">\n <app-form-field label=\"Straat en huisnummer\" fieldId=\"street\" [error]=\"streetError()\">\n <app-text-input inputId=\"street\" [(ngModel)]=\"street\" name=\"street\" [invalid]=\"!!streetError()\" />\n </app-form-field>\n <app-form-field label=\"Postcode\" fieldId=\"zip\" [error]=\"zipError()\">\n <app-text-input inputId=\"zip\" [(ngModel)]=\"zip\" name=\"zip\" placeholder=\"1234 AB\" [invalid]=\"!!zipError()\" />\n </app-form-field>\n <app-form-field label=\"Woonplaats\" fieldId=\"city\">\n <app-text-input inputId=\"city\" [(ngModel)]=\"city\" name=\"city\" />\n </app-form-field>\n <div style=\"margin-top:1rem\">\n <app-button type=\"submit\" variant=\"primary\">Wijziging indienen</app-button>\n </div>\n </form>\n `,\n})\nexport class ChangeRequestFormComponent {\n street = '';\n zip = '';\n city = '';\n streetError = signal('');\n zipError = signal('');\n submitted = output<ChangeRequest>();\n\n onSubmit() {\n const street = this.street.trim();\n this.streetError.set(street ? '' : 'Vul straat en huisnummer in.');\n\n const postcode = parsePostcode(this.zip);\n this.zipError.set(postcode.ok ? '' : postcode.error);\n\n if (!street || !postcode.ok) return;\n this.submitted.emit({ street, zip: postcode.value, city: this.city.trim() });\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "ConceptsPage",
"id": "component-ConceptsPage-0a01b17716cd0d25e6658360c5843453e9c20e94e6c616bc73509d9e849eb3c45418b941f5398de972a5969767fcb61df9b3b5b50ecd563f967da87f5b12b919",
"file": "src/app/showcase/concepts.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-concepts-page",
"styleUrls": [],
"styles": [
"\n .section { margin: 0 0 3rem }\n .lead { color: var(--rhc-color-grijs-700); max-width: 46rem; margin: 0.25rem 0 1.25rem }\n .cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1.5rem; align-items: start }\n .card { border: 1px solid var(--rhc-color-grijs-200, #e5e5e5); border-radius: 10px; padding: 1.25rem; background: #fff }\n .card--bad { border-color: var(--rhc-color-rood-300, #f0b4b4) }\n .card--good { border-color: var(--rhc-color-groen-300, #b4e0b4) }\n .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 }\n .tag::before { content: ''; width: 0.6rem; height: 0.6rem; border-radius: 50% }\n .tag.bad { color: var(--rhc-color-rood-600, #a30000) } .tag.bad::before { background: var(--rhc-color-rood-500, #d52b1e) }\n .tag.good { color: var(--rhc-color-groen-700, #277337) } .tag.good::before { background: var(--rhc-color-groen-500, #39870c) }\n .tag.plain { color: var(--rhc-color-grijs-700) } .tag.plain::before { display: none }\n pre { background: #1e2430; color: #e6e9ef; padding: 1rem; border-radius: 8px; overflow: auto; font-size: 0.82rem; line-height: 1.55; margin: 0 }\n pre .k { color: #c792ea } pre .s { color: #c3e88d } pre .c { color: #7e8aa0; font-style: italic }\n .note { font-size: 0.9rem; color: var(--rhc-color-grijs-700); margin: 0.75rem 0 0 }\n /* live state diagram */\n .machine { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem }\n .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 }\n .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 }\n .steplist { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin: 0 0 1rem }\n .pill { padding: 0.3rem 0.7rem; border-radius: 8px; background: var(--rhc-color-grijs-100, #f3f3f3); font-size: 0.8rem }\n .pill.extra { background: var(--rhc-color-geel-100, #fff6d6); border: 1px dashed var(--rhc-color-geel-600, #c79a00) }\n .arrow { color: var(--rhc-color-grijs-400, #999) }\n "
],
"template": "<app-page-shell heading=\"Onmogelijke toestanden onmogelijk maken\" backLink=\"/dashboard\">\n <p class=\"lead\">\n Vijf functionele patronen die atomic design makkelijker maakt om te tonen — telkens\n \"fout\" (de oude vorm liet het toe) naast \"goed\" (het type maakt het onmogelijk).\n </p>\n\n <!-- 1. Discriminated unions -->\n <section class=\"section\">\n <app-heading [level]=\"2\">1 · Discriminated unions</app-heading>\n <p class=\"lead\">Laat elke variant precies de gegevens dragen die kloppen — niets meer.</p>\n <div class=\"cols\">\n <div class=\"card card--bad\">\n <p class=\"tag bad\">Fout — vlakke interface</p>\n <pre [innerHTML]=\"unionBad\"></pre>\n <p class=\"note\">Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.</p>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">Goed — sum type</p>\n <app-registration-summary [reg]=\"doorgehaald\" />\n <p class=\"note\">De variant <code>Doorgehaald</code> kent geen herregistratiedatum, dus de rij bestaat simpelweg niet.</p>\n </div>\n </div>\n </section>\n\n <!-- 2. RemoteData fold -->\n <section class=\"section\">\n <app-heading [level]=\"2\">2 · RemoteData fold</app-heading>\n <p class=\"lead\">Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.</p>\n <div class=\"cols\">\n <div class=\"card card--good\">\n <p class=\"tag good\">Vier toestanden, één molecuul</p>\n <p class=\"tag plain\">Loading</p>\n <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>\n <p class=\"tag plain\">Empty</p>\n <app-async [resource]=\"emptyRes\" [isEmpty]=\"isEmpty\"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async>\n <p class=\"tag plain\">Failure</p>\n <app-async [resource]=\"errorRes\"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async>\n <p class=\"tag plain\">Success</p>\n <app-async [resource]=\"successRes\" [isEmpty]=\"isEmpty\"><ng-template appAsyncLoaded let-v><ul class=\"rhc-unordered-list\">@for (i of v; track i) {<li>{{ i }}</li>}</ul></ng-template></app-async>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">De exhaustieve fold</p>\n <pre [innerHTML]=\"foldCode\"></pre>\n <p class=\"note\">Een nieuwe variant toevoegen breekt de compile via <code>assertNever</code> tot je hem afhandelt.</p>\n </div>\n </div>\n </section>\n\n <!-- 3. Parse, don't validate -->\n <section class=\"section\">\n <app-heading [level]=\"2\">3 · Parse, don't validate</app-heading>\n <p class=\"lead\">Na het parsen onthoudt het <em>type</em> dat de waarde geldig is.</p>\n <div class=\"cols\">\n <div class=\"card\">\n <p class=\"tag good\">Smart constructor → Result</p>\n <app-text-input inputId=\"pc\" [ngModel]=\"raw()\" (ngModelChange)=\"raw.set($event)\" name=\"pc\" placeholder=\"Typ een postcode, bijv. 1234 AB\" />\n </div>\n <div class=\"card\" [class.card--good]=\"parsed().ok\" [class.card--bad]=\"!parsed().ok\">\n @if (parsed().ok) {\n <p class=\"tag good\">ok</p>\n <pre>Postcode = \"{{ $any(parsed()).value }}\"</pre>\n <p class=\"note\">Een gevalideerde <code>Postcode</code> is een ander type dan een ruwe string.</p>\n } @else {\n <p class=\"tag bad\">err</p>\n <pre>{{ $any(parsed()).error }}</pre>\n }\n </div>\n </div>\n </section>\n\n <!-- 4. State machine / wizard (live state diagram) -->\n <section class=\"section\">\n <app-heading [level]=\"2\">4 · Form als state machine</app-heading>\n <p class=\"lead\">Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de huidige.</p>\n <div class=\"cols\">\n <div class=\"card card--bad\">\n <p class=\"tag bad\">Fout — losse booleans</p>\n <pre [innerHTML]=\"machineBad\"></pre>\n <p class=\"note\">Niets verhindert \"submitting\" mét validatiefouten of een successcherm met errors.</p>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">Goed — één tagged union</p>\n <div class=\"machine\">\n @for (n of ['Editing','Submitting','Submitted','Failed']; track n) {\n <span class=\"node\" [class.on]=\"w.state().tag === n\">{{ n }}</span>\n }\n </div>\n <app-herregistratie-wizard #w />\n </div>\n </div>\n </section>\n\n <!-- 5. Fixed steps, questions revealed inline -->\n <section class=\"section\">\n <app-heading [level]=\"2\">5 · Vragenlijst met vaste stappen — \"vragen tonen, niet stappen toevoegen\"</app-heading>\n <p class=\"lead\">\n Het aantal stappen ligt vast (<code>STEPS</code>); vervolgvragen verschijnen <em>binnen</em> een stap\n op basis van eerdere antwoorden. Antwoord \"ja\" op buitenland of vul weinig uren in, en er komt een\n extra vraag bij in dezelfde stap — de voortgang \"van N\" blijft gelijk.\n </p>\n <div class=\"cols\">\n <div class=\"card card--good\">\n <p class=\"tag good\">Vaste stappen</p>\n <div class=\"steplist\">\n @for (s of iw.steps; track s; let last = $last) {\n <span class=\"pill\">{{ s }}</span>\n @if (!last) { <span class=\"arrow\">→</span> }\n }\n </div>\n <p class=\"note\">De stappen zijn altijd dezelfde; alleen de vragen <em>binnen</em> een stap verschijnen of verdwijnen.</p>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">De wizard</p>\n <app-intake-wizard #iw />\n </div>\n </div>\n </section>\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [
{
"name": "doorgehaald",
"defaultValue": "{\n bigNummer: '19012345601', naam: 'Dr. A. (Anna) de Vries', beroep: 'Arts',\n registratiedatum: '2012-09-01', geboortedatum: '1985-03-14',\n status: { tag: 'Doorgehaald', doorgehaaldOp: '2024-05-01', reden: 'Op eigen verzoek' },\n }",
"deprecated": false,
"deprecationMessage": "",
"type": "Registration",
"indexKey": "",
"optional": false,
"description": "",
"line": 176
},
{
"name": "emptyRes",
"defaultValue": "fakeResource<string[]>('resolved', [])",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 183
},
{
"name": "errorRes",
"defaultValue": "fakeResource<string[]>('error', undefined, new Error('Demo'))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 184
},
{
"name": "foldCode",
"defaultValue": "`<span class=\"k\">foldRemote</span>(rd, {\n loading: () => spinner,\n empty: () => <span class=\"s\">'geen data'</span>,\n failure: (e) => alert(e),\n success: (v) => render(v),\n}); <span class=\"c\">// mist er één → compile-fout</span>`",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 195
},
{
"name": "isEmpty",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 174
},
{
"name": "loadingRes",
"defaultValue": "fakeResource<string[]>('loading')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 182
},
{
"name": "machineBad",
"defaultValue": "`submitting = <span class=\"k\">signal</span>(false);\nsubmitted = <span class=\"k\">signal</span>(false);\nerrors = <span class=\"k\">signal</span>&lt;...&gt;({});\n<span class=\"c\">// submitting === true && errors.size > 0 ? 🤷</span>`",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 202
},
{
"name": "parsed",
"defaultValue": "computed(() => parsePostcode(this.raw()))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 188
},
{
"name": "raw",
"defaultValue": "signal('')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 187
},
{
"name": "successRes",
"defaultValue": "fakeResource<string[]>('resolved', ['Huisartsgeneeskunde', 'Spoedeisende hulp'])",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 185
},
{
"name": "unionBad",
"defaultValue": "`<span class=\"k\">interface</span> Registration {\n status: <span class=\"s\">'Geregistreerd'</span> | <span class=\"s\">'Doorgehaald'</span>;\n herregistratieDatum: string; <span class=\"c\">// altijd aanwezig 😬</span>\n}`",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 190
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "FormsModule",
"type": "module"
},
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "HeadingComponent",
"type": "component"
},
{
"name": "TextInputComponent",
"type": "component"
},
{
"name": "ASYNC"
},
{
"name": "SkeletonComponent",
"type": "component"
},
{
"name": "RegistrationSummaryComponent",
"type": "component"
},
{
"name": "HerregistratieWizardComponent",
"type": "component"
},
{
"name": "IntakeWizardComponent",
"type": "component"
}
],
"description": "<p>Teaching showcase: each section pairs the impossible-state-permitting &quot;before&quot;\nwith the &quot;after&quot; where the type system rules it out. Composition-only.</p>\n",
"rawdescription": "\nTeaching showcase: each section pairs the impossible-state-permitting \"before\"\nwith the \"after\" where the type system rules it out. Composition-only.",
"type": "component",
"sourceCode": "import { Component, computed, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport type { Resource } from '@angular/core';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ASYNC } from '@shared/ui/async/async.component';\nimport { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';\nimport { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';\nimport { HerregistratieWizardComponent } from '@herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component';\nimport { IntakeWizardComponent } from '@herregistratie/ui/intake-wizard/intake-wizard.component';\nimport { Registration } from '@registratie/domain/registration';\nimport { parsePostcode } from '@registratie/domain/value-objects/postcode';\n\n/** Minimal fake Resource so <app-async> can be driven through every state without HTTP. */\nfunction fakeResource<T>(status: string, value?: T, error?: Error): Resource<T> {\n return { value: () => value as T, status: () => status, error: () => error, hasValue: () => value !== undefined, reload: () => {} } as unknown as Resource<T>;\n}\n\n/** Teaching showcase: each section pairs the impossible-state-permitting \"before\"\n with the \"after\" where the type system rules it out. Composition-only. */\n@Component({\n selector: 'app-concepts-page',\n imports: [\n FormsModule, PageShellComponent, HeadingComponent, TextInputComponent,\n ...ASYNC, SkeletonComponent, RegistrationSummaryComponent, HerregistratieWizardComponent, IntakeWizardComponent,\n ],\n styles: [`\n .section { margin: 0 0 3rem }\n .lead { color: var(--rhc-color-grijs-700); max-width: 46rem; margin: 0.25rem 0 1.25rem }\n .cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1.5rem; align-items: start }\n .card { border: 1px solid var(--rhc-color-grijs-200, #e5e5e5); border-radius: 10px; padding: 1.25rem; background: #fff }\n .card--bad { border-color: var(--rhc-color-rood-300, #f0b4b4) }\n .card--good { border-color: var(--rhc-color-groen-300, #b4e0b4) }\n .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 }\n .tag::before { content: ''; width: 0.6rem; height: 0.6rem; border-radius: 50% }\n .tag.bad { color: var(--rhc-color-rood-600, #a30000) } .tag.bad::before { background: var(--rhc-color-rood-500, #d52b1e) }\n .tag.good { color: var(--rhc-color-groen-700, #277337) } .tag.good::before { background: var(--rhc-color-groen-500, #39870c) }\n .tag.plain { color: var(--rhc-color-grijs-700) } .tag.plain::before { display: none }\n pre { background: #1e2430; color: #e6e9ef; padding: 1rem; border-radius: 8px; overflow: auto; font-size: 0.82rem; line-height: 1.55; margin: 0 }\n pre .k { color: #c792ea } pre .s { color: #c3e88d } pre .c { color: #7e8aa0; font-style: italic }\n .note { font-size: 0.9rem; color: var(--rhc-color-grijs-700); margin: 0.75rem 0 0 }\n /* live state diagram */\n .machine { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem }\n .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 }\n .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 }\n .steplist { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin: 0 0 1rem }\n .pill { padding: 0.3rem 0.7rem; border-radius: 8px; background: var(--rhc-color-grijs-100, #f3f3f3); font-size: 0.8rem }\n .pill.extra { background: var(--rhc-color-geel-100, #fff6d6); border: 1px dashed var(--rhc-color-geel-600, #c79a00) }\n .arrow { color: var(--rhc-color-grijs-400, #999) }\n `],\n template: `\n <app-page-shell heading=\"Onmogelijke toestanden onmogelijk maken\" backLink=\"/dashboard\">\n <p class=\"lead\">\n Vijf functionele patronen die atomic design makkelijker maakt om te tonen — telkens\n \"fout\" (de oude vorm liet het toe) naast \"goed\" (het type maakt het onmogelijk).\n </p>\n\n <!-- 1. Discriminated unions -->\n <section class=\"section\">\n <app-heading [level]=\"2\">1 · Discriminated unions</app-heading>\n <p class=\"lead\">Laat elke variant precies de gegevens dragen die kloppen — niets meer.</p>\n <div class=\"cols\">\n <div class=\"card card--bad\">\n <p class=\"tag bad\">Fout — vlakke interface</p>\n <pre [innerHTML]=\"unionBad\"></pre>\n <p class=\"note\">Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.</p>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">Goed — sum type</p>\n <app-registration-summary [reg]=\"doorgehaald\" />\n <p class=\"note\">De variant <code>Doorgehaald</code> kent geen herregistratiedatum, dus de rij bestaat simpelweg niet.</p>\n </div>\n </div>\n </section>\n\n <!-- 2. RemoteData fold -->\n <section class=\"section\">\n <app-heading [level]=\"2\">2 · RemoteData fold</app-heading>\n <p class=\"lead\">Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.</p>\n <div class=\"cols\">\n <div class=\"card card--good\">\n <p class=\"tag good\">Vier toestanden, één molecuul</p>\n <p class=\"tag plain\">Loading</p>\n <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>\n <p class=\"tag plain\">Empty</p>\n <app-async [resource]=\"emptyRes\" [isEmpty]=\"isEmpty\"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async>\n <p class=\"tag plain\">Failure</p>\n <app-async [resource]=\"errorRes\"><ng-template appAsyncLoaded let-v>{{ v }}</ng-template></app-async>\n <p class=\"tag plain\">Success</p>\n <app-async [resource]=\"successRes\" [isEmpty]=\"isEmpty\"><ng-template appAsyncLoaded let-v><ul class=\"rhc-unordered-list\">@for (i of v; track i) {<li>{{ i }}</li>}</ul></ng-template></app-async>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">De exhaustieve fold</p>\n <pre [innerHTML]=\"foldCode\"></pre>\n <p class=\"note\">Een nieuwe variant toevoegen breekt de compile via <code>assertNever</code> tot je hem afhandelt.</p>\n </div>\n </div>\n </section>\n\n <!-- 3. Parse, don't validate -->\n <section class=\"section\">\n <app-heading [level]=\"2\">3 · Parse, don't validate</app-heading>\n <p class=\"lead\">Na het parsen onthoudt het <em>type</em> dat de waarde geldig is.</p>\n <div class=\"cols\">\n <div class=\"card\">\n <p class=\"tag good\">Smart constructor → Result</p>\n <app-text-input inputId=\"pc\" [ngModel]=\"raw()\" (ngModelChange)=\"raw.set($event)\" name=\"pc\" placeholder=\"Typ een postcode, bijv. 1234 AB\" />\n </div>\n <div class=\"card\" [class.card--good]=\"parsed().ok\" [class.card--bad]=\"!parsed().ok\">\n @if (parsed().ok) {\n <p class=\"tag good\">ok</p>\n <pre>Postcode = \"{{ $any(parsed()).value }}\"</pre>\n <p class=\"note\">Een gevalideerde <code>Postcode</code> is een ander type dan een ruwe string.</p>\n } @else {\n <p class=\"tag bad\">err</p>\n <pre>{{ $any(parsed()).error }}</pre>\n }\n </div>\n </div>\n </section>\n\n <!-- 4. State machine / wizard (live state diagram) -->\n <section class=\"section\">\n <app-heading [level]=\"2\">4 · Form als state machine</app-heading>\n <p class=\"lead\">Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de huidige.</p>\n <div class=\"cols\">\n <div class=\"card card--bad\">\n <p class=\"tag bad\">Fout — losse booleans</p>\n <pre [innerHTML]=\"machineBad\"></pre>\n <p class=\"note\">Niets verhindert \"submitting\" mét validatiefouten of een successcherm met errors.</p>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">Goed — één tagged union</p>\n <div class=\"machine\">\n @for (n of ['Editing','Submitting','Submitted','Failed']; track n) {\n <span class=\"node\" [class.on]=\"w.state().tag === n\">{{ n }}</span>\n }\n </div>\n <app-herregistratie-wizard #w />\n </div>\n </div>\n </section>\n\n <!-- 5. Fixed steps, questions revealed inline -->\n <section class=\"section\">\n <app-heading [level]=\"2\">5 · Vragenlijst met vaste stappen — \"vragen tonen, niet stappen toevoegen\"</app-heading>\n <p class=\"lead\">\n Het aantal stappen ligt vast (<code>STEPS</code>); vervolgvragen verschijnen <em>binnen</em> een stap\n op basis van eerdere antwoorden. Antwoord \"ja\" op buitenland of vul weinig uren in, en er komt een\n extra vraag bij in dezelfde stap — de voortgang \"van N\" blijft gelijk.\n </p>\n <div class=\"cols\">\n <div class=\"card card--good\">\n <p class=\"tag good\">Vaste stappen</p>\n <div class=\"steplist\">\n @for (s of iw.steps; track s; let last = $last) {\n <span class=\"pill\">{{ s }}</span>\n @if (!last) { <span class=\"arrow\">→</span> }\n }\n </div>\n <p class=\"note\">De stappen zijn altijd dezelfde; alleen de vragen <em>binnen</em> een stap verschijnen of verdwijnen.</p>\n </div>\n <div class=\"card card--good\">\n <p class=\"tag good\">De wizard</p>\n <app-intake-wizard #iw />\n </div>\n </div>\n </section>\n </app-page-shell>\n `,\n})\nexport class ConceptsPage {\n isEmpty = (v: string[]) => !v || v.length === 0;\n\n doorgehaald: Registration = {\n bigNummer: '19012345601', naam: 'Dr. A. (Anna) de Vries', beroep: 'Arts',\n registratiedatum: '2012-09-01', geboortedatum: '1985-03-14',\n status: { tag: 'Doorgehaald', doorgehaaldOp: '2024-05-01', reden: 'Op eigen verzoek' },\n };\n\n loadingRes = fakeResource<string[]>('loading');\n emptyRes = fakeResource<string[]>('resolved', []);\n errorRes = fakeResource<string[]>('error', undefined, new Error('Demo'));\n successRes = fakeResource<string[]>('resolved', ['Huisartsgeneeskunde', 'Spoedeisende hulp']);\n\n raw = signal('');\n parsed = computed(() => parsePostcode(this.raw()));\n\n unionBad = `<span class=\"k\">interface</span> Registration {\n status: <span class=\"s\">'Geregistreerd'</span> | <span class=\"s\">'Doorgehaald'</span>;\n herregistratieDatum: string; <span class=\"c\">// altijd aanwezig 😬</span>\n}`;\n\n foldCode = `<span class=\"k\">foldRemote</span>(rd, {\n loading: () => spinner,\n empty: () => <span class=\"s\">'geen data'</span>,\n failure: (e) => alert(e),\n success: (v) => render(v),\n}); <span class=\"c\">// mist er één → compile-fout</span>`;\n\n machineBad = `submitting = <span class=\"k\">signal</span>(false);\nsubmitted = <span class=\"k\">signal</span>(false);\nerrors = <span class=\"k\">signal</span>&lt;...&gt;({});\n<span class=\"c\">// submitting === true && errors.size > 0 ? 🤷</span>`;\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n .section { margin: 0 0 3rem }\n .lead { color: var(--rhc-color-grijs-700); max-width: 46rem; margin: 0.25rem 0 1.25rem }\n .cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: 1.5rem; align-items: start }\n .card { border: 1px solid var(--rhc-color-grijs-200, #e5e5e5); border-radius: 10px; padding: 1.25rem; background: #fff }\n .card--bad { border-color: var(--rhc-color-rood-300, #f0b4b4) }\n .card--good { border-color: var(--rhc-color-groen-300, #b4e0b4) }\n .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 }\n .tag::before { content: ''; width: 0.6rem; height: 0.6rem; border-radius: 50% }\n .tag.bad { color: var(--rhc-color-rood-600, #a30000) } .tag.bad::before { background: var(--rhc-color-rood-500, #d52b1e) }\n .tag.good { color: var(--rhc-color-groen-700, #277337) } .tag.good::before { background: var(--rhc-color-groen-500, #39870c) }\n .tag.plain { color: var(--rhc-color-grijs-700) } .tag.plain::before { display: none }\n pre { background: #1e2430; color: #e6e9ef; padding: 1rem; border-radius: 8px; overflow: auto; font-size: 0.82rem; line-height: 1.55; margin: 0 }\n pre .k { color: #c792ea } pre .s { color: #c3e88d } pre .c { color: #7e8aa0; font-style: italic }\n .note { font-size: 0.9rem; color: var(--rhc-color-grijs-700); margin: 0.75rem 0 0 }\n /* live state diagram */\n .machine { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 1rem }\n .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 }\n .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 }\n .steplist { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin: 0 0 1rem }\n .pill { padding: 0.3rem 0.7rem; border-radius: 8px; background: var(--rhc-color-grijs-100, #f3f3f3); font-size: 0.8rem }\n .pill.extra { background: var(--rhc-color-geel-100, #fff6d6); border: 1px dashed var(--rhc-color-geel-600, #c79a00) }\n .arrow { color: var(--rhc-color-grijs-400, #999) }\n \n",
"extends": []
},
{
"name": "DashboardPage",
"id": "component-DashboardPage-ddf78634bc64a1bb22179e940266e3f66193d863c79e6dd2b9322cbbbcaf1ace61e0c99e90ffc2f87633af10da911231bf12249e484ed1a834c2d6e13a643a75",
"file": "src/app/registratie/ui/dashboard.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-dashboard-page",
"styleUrls": [],
"styles": [],
"template": "<app-page-shell heading=\"Mijn BIG-registratie\">\n @if (store.pendingHerregistratie()) {\n <app-alert type=\"info\">Uw herregistratie-aanvraag is in behandeling.</app-alert>\n }\n\n <!-- ONE state for two combined services (BIG-register + BRP). -->\n <app-async [data]=\"store.profile()\">\n <ng-template appAsyncLoaded let-p>\n <app-registration-summary [reg]=\"$any(p).registration\" />\n <div class=\"rhc-card rhc-card--default\" style=\"margin-top:1rem\">\n <app-heading [level]=\"2\">Persoonsgegevens (BRP)</app-heading>\n <dl class=\"rhc-data-summary rhc-data-summary--row\">\n <app-data-row key=\"Straat\" [value]=\"$any(p).person.adres.straat\" />\n <app-data-row key=\"Postcode\" [value]=\"$any(p).person.adres.postcode\" />\n <app-data-row key=\"Woonplaats\" [value]=\"$any(p).person.adres.woonplaats\" />\n </dl>\n </div>\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"6\" />\n </ng-template>\n </app-async>\n\n <div style=\"margin-top:2rem\">\n <app-heading [level]=\"2\">Specialismen en aantekeningen</app-heading>\n <app-async [data]=\"store.aantekeningen()\">\n <ng-template appAsyncLoaded let-r>\n <app-registration-table [rows]=\"$any(r)\" />\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"3\" />\n </ng-template>\n <ng-template appAsyncEmpty>\n <p class=\"rhc-paragraph\">U heeft nog geen specialismen of aantekeningen.</p>\n </ng-template>\n </app-async>\n </div>\n\n <p style=\"margin-top:2rem\">\n <app-link to=\"/registreren\">Inschrijven in het BIG-register (registratiewizard) →</app-link>\n </p>\n <p>\n <app-link to=\"/registratie\">Gegevens bekijken of een wijziging doorgeven →</app-link>\n </p>\n <p>\n <app-link to=\"/herregistratie\">Herregistratie aanvragen →</app-link>\n </p>\n <p>\n <app-link to=\"/intake\">Herregistratie-intake (vragenlijst met vertakkingen) →</app-link>\n </p>\n <p>\n <app-link to=\"/concepts\">Functionele patronen (impossible states) →</app-link>\n </p>\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [
{
"name": "store",
"defaultValue": "inject(BigProfileStore)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 77,
"modifierKind": [
124
]
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "HeadingComponent",
"type": "component"
},
{
"name": "LinkComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "SkeletonComponent",
"type": "component"
},
{
"name": "DataRowComponent",
"type": "component"
},
{
"name": "ASYNC"
},
{
"name": "RegistrationSummaryComponent",
"type": "component"
},
{
"name": "RegistrationTableComponent",
"type": "component"
}
],
"description": "",
"rawdescription": "\n",
"type": "component",
"sourceCode": "import { Component, inject } from '@angular/core';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { LinkComponent } from '@shared/ui/link/link.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';\nimport { DataRowComponent } from '@shared/ui/data-row/data-row.component';\nimport { ASYNC } from '@shared/ui/async/async.component';\nimport { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';\nimport { RegistrationTableComponent } from '@registratie/ui/registration-table/registration-table.component';\nimport { BigProfileStore } from '@registratie/application/big-profile.store';\n\n@Component({\n selector: 'app-dashboard-page',\n imports: [\n PageShellComponent, HeadingComponent, LinkComponent, AlertComponent, SkeletonComponent,\n DataRowComponent, ...ASYNC, RegistrationSummaryComponent, RegistrationTableComponent,\n ],\n template: `\n <app-page-shell heading=\"Mijn BIG-registratie\">\n @if (store.pendingHerregistratie()) {\n <app-alert type=\"info\">Uw herregistratie-aanvraag is in behandeling.</app-alert>\n }\n\n <!-- ONE state for two combined services (BIG-register + BRP). -->\n <app-async [data]=\"store.profile()\">\n <ng-template appAsyncLoaded let-p>\n <app-registration-summary [reg]=\"$any(p).registration\" />\n <div class=\"rhc-card rhc-card--default\" style=\"margin-top:1rem\">\n <app-heading [level]=\"2\">Persoonsgegevens (BRP)</app-heading>\n <dl class=\"rhc-data-summary rhc-data-summary--row\">\n <app-data-row key=\"Straat\" [value]=\"$any(p).person.adres.straat\" />\n <app-data-row key=\"Postcode\" [value]=\"$any(p).person.adres.postcode\" />\n <app-data-row key=\"Woonplaats\" [value]=\"$any(p).person.adres.woonplaats\" />\n </dl>\n </div>\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"6\" />\n </ng-template>\n </app-async>\n\n <div style=\"margin-top:2rem\">\n <app-heading [level]=\"2\">Specialismen en aantekeningen</app-heading>\n <app-async [data]=\"store.aantekeningen()\">\n <ng-template appAsyncLoaded let-r>\n <app-registration-table [rows]=\"$any(r)\" />\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"3\" />\n </ng-template>\n <ng-template appAsyncEmpty>\n <p class=\"rhc-paragraph\">U heeft nog geen specialismen of aantekeningen.</p>\n </ng-template>\n </app-async>\n </div>\n\n <p style=\"margin-top:2rem\">\n <app-link to=\"/registreren\">Inschrijven in het BIG-register (registratiewizard) →</app-link>\n </p>\n <p>\n <app-link to=\"/registratie\">Gegevens bekijken of een wijziging doorgeven →</app-link>\n </p>\n <p>\n <app-link to=\"/herregistratie\">Herregistratie aanvragen →</app-link>\n </p>\n <p>\n <app-link to=\"/intake\">Herregistratie-intake (vragenlijst met vertakkingen) →</app-link>\n </p>\n <p>\n <app-link to=\"/concepts\">Functionele patronen (impossible states) →</app-link>\n </p>\n </app-page-shell>\n `,\n})\nexport class DashboardPage {\n protected store = inject(BigProfileStore);\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "DataRowComponent",
"id": "component-DataRowComponent-042344abd735238be6230f6fdad24c6403897f90c814e06f27d2f67c4ced32aa3d1da211a95b8c99aa782ff1665da487a93d19f21ba509ab29cd5e640fedc405",
"file": "src/app/shared/ui/data-row/data-row.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-data-row",
"styleUrls": [],
"styles": [
":host:last-child .rhc-data-summary__item { border-block-end-style: none; }"
],
"template": "<div class=\"rhc-data-summary__item\">\n <dt class=\"rhc-data-summary__item-key\">{{ key() }}</dt>\n <dd class=\"rhc-data-summary__item-value\"><ng-content>{{ value() }}</ng-content></dd>\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "key",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 18,
"required": true
},
{
"name": "value",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string | null",
"indexKey": "",
"optional": false,
"description": "",
"line": 19,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Molecule: one key/value row inside an RHC data-summary.\nWrap several of these in .rhc-data-summary (see registration-summary).</p>\n",
"rawdescription": "\nMolecule: one key/value row inside an RHC data-summary.\nWrap several of these in .rhc-data-summary (see registration-summary).",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\n/** Molecule: one key/value row inside an RHC data-summary.\n Wrap several of these in .rhc-data-summary (see registration-summary). */\n@Component({\n selector: 'app-data-row',\n // The RHC item draws a divider below every row; drop it on the last one so the\n // list ends cleanly instead of looking like a trailing empty row.\n styles: [`:host:last-child .rhc-data-summary__item { border-block-end-style: none; }`],\n template: `\n <div class=\"rhc-data-summary__item\">\n <dt class=\"rhc-data-summary__item-key\">{{ key() }}</dt>\n <dd class=\"rhc-data-summary__item-value\"><ng-content>{{ value() }}</ng-content></dd>\n </div>\n `,\n})\nexport class DataRowComponent {\n key = input.required<string>();\n value = input<string | null>('');\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": ":host:last-child .rhc-data-summary__item { border-block-end-style: none; }\n",
"extends": []
},
{
"name": "FormFieldComponent",
"id": "component-FormFieldComponent-116bcd8039c2d08b60a7dee8bdc465c6a0a04fa3e03d4b371c12d759113e2b369673acd0d7928b9ea47966065d618ed483fc316fdcdb2c740c3891a732f377e9",
"file": "src/app/shared/ui/form-field/form-field.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-form-field",
"styleUrls": [],
"styles": [],
"template": "<div class=\"rhc-form-field utrecht-form-field\" [class.utrecht-form-field--invalid]=\"!!error()\">\n <label class=\"utrecht-form-label\" [id]=\"fieldId() + '-label'\" [for]=\"fieldId()\">{{ label() }}</label>\n @if (description()) {\n <div class=\"utrecht-form-field-description\" [id]=\"fieldId() + '-desc'\">{{ description() }}</div>\n }\n <ng-content />\n @if (error()) {\n <div class=\"utrecht-form-field-error-message\" [id]=\"fieldId() + '-error'\" role=\"alert\">{{ error() }}</div>\n }\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "description",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 23,
"required": false
},
{
"name": "error",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 24,
"required": false
},
{
"name": "fieldId",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 22,
"required": true
},
{
"name": "label",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 21,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Molecule: form field = label + projected control + optional error/description.\nReused by both the login form and the change-request form.</p>\n",
"rawdescription": "\nMolecule: form field = label + projected control + optional error/description.\nReused by both the login form and the change-request form.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\n/** Molecule: form field = label + projected control + optional error/description.\n Reused by both the login form and the change-request form. */\n@Component({\n selector: 'app-form-field',\n template: `\n <div class=\"rhc-form-field utrecht-form-field\" [class.utrecht-form-field--invalid]=\"!!error()\">\n <label class=\"utrecht-form-label\" [id]=\"fieldId() + '-label'\" [for]=\"fieldId()\">{{ label() }}</label>\n @if (description()) {\n <div class=\"utrecht-form-field-description\" [id]=\"fieldId() + '-desc'\">{{ description() }}</div>\n }\n <ng-content />\n @if (error()) {\n <div class=\"utrecht-form-field-error-message\" [id]=\"fieldId() + '-error'\" role=\"alert\">{{ error() }}</div>\n }\n </div>\n `,\n})\nexport class FormFieldComponent {\n label = input.required<string>();\n fieldId = input.required<string>();\n description = input<string>();\n error = input<string>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "HeadingComponent",
"id": "component-HeadingComponent-0f0b442fc9ccbfde8481137b728000bb093868ddc931f507bbd4a86ee528820cf4c6dab36a9a82b4a940bb96c2355f42bc34a94700acc7df3e7dfeea627be67c",
"file": "src/app/shared/ui/heading/heading.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-heading",
"styleUrls": [],
"styles": [],
"template": "<ng-template #content><ng-content /></ng-template>\n@switch (level()) {\n @case (1) { <h1 class=\"rhc-heading nl-heading--level-1\"><ng-container [ngTemplateOutlet]=\"content\" /></h1> }\n @case (2) { <h2 class=\"rhc-heading nl-heading--level-2\"><ng-container [ngTemplateOutlet]=\"content\" /></h2> }\n @case (3) { <h3 class=\"rhc-heading nl-heading--level-3\"><ng-container [ngTemplateOutlet]=\"content\" /></h3> }\n @case (4) { <h4 class=\"rhc-heading nl-heading--level-4\"><ng-container [ngTemplateOutlet]=\"content\" /></h4> }\n @default { <h5 class=\"rhc-heading nl-heading--level-5\"><ng-container [ngTemplateOutlet]=\"content\" /></h5> }\n}\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "level",
"defaultValue": "2",
"deprecated": false,
"deprecationMessage": "",
"type": "1 | 2 | 3 | 4 | 5",
"indexKey": "",
"optional": false,
"description": "",
"line": 22,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "NgTemplateOutlet"
}
],
"description": "<p>Atom: heading. Renders the right h1..h5 with RHC heading styling.\nSingle <ng-content> captured in a template — multiple ng-content across</p>\n",
"rawdescription": "\nAtom: heading. Renders the right h1..h5 with RHC heading styling.\nSingle <ng-content> captured in a template — multiple ng-content across",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { NgTemplateOutlet } from '@angular/common';\n\n/** Atom: heading. Renders the right h1..h5 with RHC heading styling.\n Single <ng-content> captured in a template — multiple ng-content across\n @switch branches silently drops the projected content. */\n@Component({\n selector: 'app-heading',\n imports: [NgTemplateOutlet],\n template: `\n <ng-template #content><ng-content /></ng-template>\n @switch (level()) {\n @case (1) { <h1 class=\"rhc-heading nl-heading--level-1\"><ng-container [ngTemplateOutlet]=\"content\" /></h1> }\n @case (2) { <h2 class=\"rhc-heading nl-heading--level-2\"><ng-container [ngTemplateOutlet]=\"content\" /></h2> }\n @case (3) { <h3 class=\"rhc-heading nl-heading--level-3\"><ng-container [ngTemplateOutlet]=\"content\" /></h3> }\n @case (4) { <h4 class=\"rhc-heading nl-heading--level-4\"><ng-container [ngTemplateOutlet]=\"content\" /></h4> }\n @default { <h5 class=\"rhc-heading nl-heading--level-5\"><ng-container [ngTemplateOutlet]=\"content\" /></h5> }\n }\n `,\n})\nexport class HeadingComponent {\n level = input<1 | 2 | 3 | 4 | 5>(2);\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "HerregistratiePage",
"id": "component-HerregistratiePage-b279a94020bfc4f5742e1214bdcfcf8c1725f0ceb12f42e4483a0194cf1b392e4f277e0661b62eb7a52b156b13806bd12a8732248d37f5bfcc5a68f0e1c61341",
"file": "src/app/herregistratie/ui/herregistratie.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-herregistratie-page",
"styleUrls": [],
"styles": [],
"template": "<app-page-shell heading=\"Herregistratie aanvragen\" backLink=\"/dashboard\">\n <app-async [data]=\"eligibility()\">\n <ng-template appAsyncLoaded let-eligible>\n @if (eligible) {\n <app-alert type=\"info\">\n Uw huidige registratie verloopt binnenkort. Vraag tijdig herregistratie aan.\n </app-alert>\n <div style=\"margin-top:1.5rem\">\n <app-herregistratie-wizard />\n </div>\n } @else {\n <app-alert type=\"warning\">\n Voor uw huidige registratiestatus is herregistratie niet mogelijk.\n </app-alert>\n }\n </ng-template>\n </app-async>\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [
{
"name": "eligibility",
"defaultValue": "computed(() =>\n map(this.store.decisions(), (d) => d.eligibleForHerregistratie),\n )",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 40,
"modifierKind": [
124
]
},
{
"name": "store",
"defaultValue": "inject(BigProfileStore)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 37,
"modifierKind": [
123
]
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "ASYNC"
},
{
"name": "HerregistratieWizardComponent",
"type": "component"
}
],
"description": "<p>A whole new page built from existing building blocks. Eligibility is a\nSERVER-computed decision read from the aggregated view — the frontend renders\nit, it does not recompute the rule.</p>\n",
"rawdescription": "\nA whole new page built from existing building blocks. Eligibility is a\nSERVER-computed decision read from the aggregated view — the frontend renders\nit, it does not recompute the rule.",
"type": "component",
"sourceCode": "import { Component, computed, inject } from '@angular/core';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { ASYNC } from '@shared/ui/async/async.component';\nimport { map } from '@shared/application/remote-data';\nimport { BigProfileStore } from '@registratie/application/big-profile.store';\nimport { HerregistratieWizardComponent } from '@herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component';\n\n/** A whole new page built from existing building blocks. Eligibility is a\n SERVER-computed decision read from the aggregated view — the frontend renders\n it, it does not recompute the rule. */\n@Component({\n selector: 'app-herregistratie-page',\n imports: [PageShellComponent, AlertComponent, ...ASYNC, HerregistratieWizardComponent],\n template: `\n <app-page-shell heading=\"Herregistratie aanvragen\" backLink=\"/dashboard\">\n <app-async [data]=\"eligibility()\">\n <ng-template appAsyncLoaded let-eligible>\n @if (eligible) {\n <app-alert type=\"info\">\n Uw huidige registratie verloopt binnenkort. Vraag tijdig herregistratie aan.\n </app-alert>\n <div style=\"margin-top:1.5rem\">\n <app-herregistratie-wizard />\n </div>\n } @else {\n <app-alert type=\"warning\">\n Voor uw huidige registratiestatus is herregistratie niet mogelijk.\n </app-alert>\n }\n </ng-template>\n </app-async>\n </app-page-shell>\n `,\n})\nexport class HerregistratiePage {\n private store = inject(BigProfileStore);\n // The eligibility decision comes from the server (decisions block), not a\n // client-side rule. The UI just reads the boolean.\n protected eligibility = computed(() =>\n map(this.store.decisions(), (d) => d.eligibleForHerregistratie),\n );\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "HerregistratieWizardComponent",
"id": "component-HerregistratieWizardComponent-7f88cdc3fbae79e0e373e2178954406f5236da9c11f6ac2ee9504a64055abe457d7e3b259572c6d0a75229acbf08cd404efab0fbcac07510ff5bf34523a2b3e7",
"file": "src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-herregistratie-wizard",
"styleUrls": [],
"styles": [],
"template": "@switch (state().tag) {\n @case ('Editing') {\n <p class=\"rhc-paragraph\" style=\"color:var(--rhc-color-grijs-700)\">Stap {{ step() }} van 2</p>\n <form (ngSubmit)=\"onPrimary()\" style=\"max-width:28rem\">\n @if (step() === 1) {\n <app-form-field label=\"Gewerkte uren (afgelopen 5 jaar)\" fieldId=\"uren\" [error]=\"errUren()\">\n <app-text-input inputId=\"uren\" [ngModel]=\"draft().uren\" (ngModelChange)=\"dispatch({ tag: 'SetField', key: 'uren', value: $event })\"\n name=\"uren\" [invalid]=\"!!errUren()\" placeholder=\"bijv. 4160\" />\n </app-form-field>\n <app-form-field label=\"Aantal jaren werkzaam\" fieldId=\"jaren\" [error]=\"errJaren()\">\n <app-text-input inputId=\"jaren\" [ngModel]=\"draft().jaren\" (ngModelChange)=\"dispatch({ tag: 'SetField', key: 'jaren', value: $event })\"\n name=\"jaren\" [invalid]=\"!!errJaren()\" placeholder=\"bijv. 5\" />\n </app-form-field>\n <div style=\"display:flex;gap:0.5rem\">\n <app-button type=\"submit\" variant=\"primary\">Volgende</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n } @else {\n <app-form-field label=\"Behaalde nascholingspunten\" fieldId=\"punten\" [error]=\"errPunten()\">\n <app-text-input inputId=\"punten\" [ngModel]=\"draft().punten\" (ngModelChange)=\"dispatch({ tag: 'SetField', key: 'punten', value: $event })\"\n name=\"punten\" [invalid]=\"!!errPunten()\" placeholder=\"bijv. 200\" />\n </app-form-field>\n <div style=\"display:flex;gap:0.5rem\">\n <app-button type=\"button\" variant=\"secondary\" (click)=\"dispatch({ tag: 'Back' })\">Vorige</app-button>\n <app-button type=\"submit\" variant=\"primary\">Herregistratie aanvragen</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n }\n </form>\n }\n @case ('Submitting') {\n <app-spinner /> <span>Aanvraag wordt verwerkt…</span>\n }\n @case ('Submitted') {\n <app-alert type=\"ok\">Uw aanvraag tot herregistratie is ontvangen.</app-alert>\n }\n @case ('Failed') {\n <app-alert type=\"error\">Indienen mislukt: {{ failedError() }}</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"onRetry()\">Opnieuw proberen</app-button>\n </div>\n }\n}\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "seed",
"defaultValue": "initial",
"deprecated": false,
"deprecationMessage": "",
"type": "WizardState",
"indexKey": "",
"optional": false,
"description": "<p>Optional seed so Storybook / the showcase can mount any state directly.</p>\n",
"line": 73,
"rawdescription": "\nOptional seed so Storybook / the showcase can mount any state directly.",
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "dispatch",
"defaultValue": "this.store.dispatch",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 76,
"modifierKind": [
124
]
},
{
"name": "draft",
"defaultValue": "computed<Draft>(() => this.editing()?.draft ?? { uren: '', jaren: '', punten: '' })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 80,
"modifierKind": [
124
]
},
{
"name": "editing",
"defaultValue": "computed(() => (this.state().tag === 'Editing' ? (this.state() as Extract<WizardState, { tag: 'Editing' }>) : null))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 78,
"modifierKind": [
123
]
},
{
"name": "errJaren",
"defaultValue": "computed(() => this.editing()?.errors.jaren ?? '')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 82,
"modifierKind": [
124
]
},
{
"name": "errPunten",
"defaultValue": "computed(() => this.editing()?.errors.punten ?? '')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 83,
"modifierKind": [
124
]
},
{
"name": "errUren",
"defaultValue": "computed(() => this.editing()?.errors.uren ?? '')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 81,
"modifierKind": [
124
]
},
{
"name": "failedError",
"defaultValue": "computed(() => (this.state().tag === 'Failed' ? (this.state() as Extract<WizardState, { tag: 'Failed' }>).error : ''))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 84,
"modifierKind": [
124
]
},
{
"name": "profile",
"defaultValue": "inject(BigProfileStore)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 69,
"modifierKind": [
123
]
},
{
"name": "state",
"defaultValue": "this.store.model",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 75,
"modifierKind": [
148
]
},
{
"name": "step",
"defaultValue": "computed(() => this.editing()?.step ?? 1)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 79,
"modifierKind": [
124
]
},
{
"name": "store",
"defaultValue": "createStore<WizardState, WizardMsg>(initial, reduce)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 70,
"modifierKind": [
123
]
}
],
"methodsClass": [
{
"name": "onPrimary",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 90,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "onRetry",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 97,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "restart",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 103,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nReset the wizard to a fresh, empty start.",
"description": "<p>Reset the wizard to a fresh, empty start.</p>\n"
},
{
"name": "runIfSubmitting",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 109,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nThe effect: when we entered Submitting, call the backend command, flip the\noptimistic cross-page flag, then dispatch the result (and commit/rollback).",
"description": "<p>The effect: when we entered Submitting, call the backend command, flip the\noptimistic cross-page flag, then dispatch the result (and commit/rollback).</p>\n",
"modifierKind": [
123,
134
]
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "FormsModule",
"type": "module"
},
{
"name": "FormFieldComponent",
"type": "component"
},
{
"name": "TextInputComponent",
"type": "component"
},
{
"name": "ButtonComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "SpinnerComponent",
"type": "component"
}
],
"description": "<p>Organism: multi-step herregistratie wizard. ALL state lives in one signal\ndriven by the pure <code>reduce</code> function (see herregistratie.machine.ts) via an\nElm-style store. The UI just sends messages and folds over the state&#39;s tag —\nno booleans like <code>submitting</code>/<code>submitted</code> that could contradict each other.\nSubmitting also flips an optimistic flag on the shared BigProfileStore, so\nthe dashboard shows &quot;in behandeling&quot; immediately.</p>\n",
"rawdescription": "\nOrganism: multi-step herregistratie wizard. ALL state lives in one signal\ndriven by the pure `reduce` function (see herregistratie.machine.ts) via an\nElm-style store. The UI just sends messages and folds over the state's tag —\nno booleans like `submitting`/`submitted` that could contradict each other.\nSubmitting also flips an optimistic flag on the shared BigProfileStore, so\nthe dashboard shows \"in behandeling\" immediately.",
"type": "component",
"sourceCode": "import { Component, computed, inject, input } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { createStore } from '@shared/application/store';\nimport { BigProfileStore } from '@registratie/application/big-profile.store';\nimport { WizardState, WizardMsg, Draft, initial, reduce } from '@herregistratie/domain/herregistratie.machine';\nimport { submitHerregistratie } from '@herregistratie/application/submit-herregistratie';\n\n/** Organism: multi-step herregistratie wizard. ALL state lives in one signal\n driven by the pure `reduce` function (see herregistratie.machine.ts) via an\n Elm-style store. The UI just sends messages and folds over the state's tag —\n no booleans like `submitting`/`submitted` that could contradict each other.\n Submitting also flips an optimistic flag on the shared BigProfileStore, so\n the dashboard shows \"in behandeling\" immediately. */\n@Component({\n selector: 'app-herregistratie-wizard',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent, AlertComponent, SpinnerComponent],\n template: `\n @switch (state().tag) {\n @case ('Editing') {\n <p class=\"rhc-paragraph\" style=\"color:var(--rhc-color-grijs-700)\">Stap {{ step() }} van 2</p>\n <form (ngSubmit)=\"onPrimary()\" style=\"max-width:28rem\">\n @if (step() === 1) {\n <app-form-field label=\"Gewerkte uren (afgelopen 5 jaar)\" fieldId=\"uren\" [error]=\"errUren()\">\n <app-text-input inputId=\"uren\" [ngModel]=\"draft().uren\" (ngModelChange)=\"dispatch({ tag: 'SetField', key: 'uren', value: $event })\"\n name=\"uren\" [invalid]=\"!!errUren()\" placeholder=\"bijv. 4160\" />\n </app-form-field>\n <app-form-field label=\"Aantal jaren werkzaam\" fieldId=\"jaren\" [error]=\"errJaren()\">\n <app-text-input inputId=\"jaren\" [ngModel]=\"draft().jaren\" (ngModelChange)=\"dispatch({ tag: 'SetField', key: 'jaren', value: $event })\"\n name=\"jaren\" [invalid]=\"!!errJaren()\" placeholder=\"bijv. 5\" />\n </app-form-field>\n <div style=\"display:flex;gap:0.5rem\">\n <app-button type=\"submit\" variant=\"primary\">Volgende</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n } @else {\n <app-form-field label=\"Behaalde nascholingspunten\" fieldId=\"punten\" [error]=\"errPunten()\">\n <app-text-input inputId=\"punten\" [ngModel]=\"draft().punten\" (ngModelChange)=\"dispatch({ tag: 'SetField', key: 'punten', value: $event })\"\n name=\"punten\" [invalid]=\"!!errPunten()\" placeholder=\"bijv. 200\" />\n </app-form-field>\n <div style=\"display:flex;gap:0.5rem\">\n <app-button type=\"button\" variant=\"secondary\" (click)=\"dispatch({ tag: 'Back' })\">Vorige</app-button>\n <app-button type=\"submit\" variant=\"primary\">Herregistratie aanvragen</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n }\n </form>\n }\n @case ('Submitting') {\n <app-spinner /> <span>Aanvraag wordt verwerkt…</span>\n }\n @case ('Submitted') {\n <app-alert type=\"ok\">Uw aanvraag tot herregistratie is ontvangen.</app-alert>\n }\n @case ('Failed') {\n <app-alert type=\"error\">Indienen mislukt: {{ failedError() }}</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"onRetry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n `,\n})\nexport class HerregistratieWizardComponent {\n private profile = inject(BigProfileStore);\n private store = createStore<WizardState, WizardMsg>(initial, reduce);\n\n /** Optional seed so Storybook / the showcase can mount any state directly. */\n seed = input<WizardState>(initial);\n\n readonly state = this.store.model; // public so the showcase can highlight the live state\n protected dispatch = this.store.dispatch;\n\n private editing = computed(() => (this.state().tag === 'Editing' ? (this.state() as Extract<WizardState, { tag: 'Editing' }>) : null));\n protected step = computed(() => this.editing()?.step ?? 1);\n protected draft = computed<Draft>(() => this.editing()?.draft ?? { uren: '', jaren: '', punten: '' });\n protected errUren = computed(() => this.editing()?.errors.uren ?? '');\n protected errJaren = computed(() => this.editing()?.errors.jaren ?? '');\n protected errPunten = computed(() => this.editing()?.errors.punten ?? '');\n protected failedError = computed(() => (this.state().tag === 'Failed' ? (this.state() as Extract<WizardState, { tag: 'Failed' }>).error : ''));\n\n constructor() {\n queueMicrotask(() => this.dispatch({ tag: 'Seed', state: this.seed() }));\n }\n\n onPrimary() {\n const s = this.state();\n if (s.tag !== 'Editing') return;\n this.dispatch(s.step === 1 ? { tag: 'Next' } : { tag: 'Submit' });\n this.runIfSubmitting();\n }\n\n onRetry() {\n this.dispatch({ tag: 'Retry' });\n this.runIfSubmitting();\n }\n\n /** Reset the wizard to a fresh, empty start. */\n restart() {\n this.dispatch({ tag: 'Seed', state: initial });\n }\n\n /** The effect: when we entered Submitting, call the backend command, flip the\n optimistic cross-page flag, then dispatch the result (and commit/rollback). */\n private async runIfSubmitting() {\n const s = this.state();\n if (s.tag !== 'Submitting') return;\n this.profile.beginHerregistratie();\n const r = await submitHerregistratie(s.data);\n if (r.ok) {\n this.dispatch({ tag: 'SubmitConfirmed' });\n this.profile.confirmHerregistratie();\n } else {\n this.dispatch({ tag: 'SubmitFailed', error: r.error });\n this.profile.rollbackHerregistratie();\n }\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [],
"line": 84
},
"extends": []
},
{
"name": "IntakePage",
"id": "component-IntakePage-12d65756545a1c065f64a50a3dacfa1708ad3d1d40663c41306c4c15727ecde5b547f243cda5ec5201137f0a376e7807986a45f89fd717c7e30fac41fc22a6b3",
"file": "src/app/herregistratie/ui/intake.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-intake-page",
"styleUrls": [],
"styles": [],
"template": "<app-page-shell heading=\"Herregistratie — intake\" backLink=\"/dashboard\">\n <app-alert type=\"info\">\n Een paar vragen bepalen welke gegevens we nodig hebben. Afhankelijk van uw\n antwoorden verschijnen er extra vragen. Uw antwoorden blijven bewaard als u\n de pagina herlaadt.\n </app-alert>\n <div style=\"margin-top:1.5rem\">\n <app-intake-wizard />\n </div>\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "IntakeWizardComponent",
"type": "component"
}
],
"description": "<p>Page: the branching intake questionnaire. Built entirely from existing\nbuilding blocks (page shell + alert + the intake-wizard organism).</p>\n",
"rawdescription": "\nPage: the branching intake questionnaire. Built entirely from existing\nbuilding blocks (page shell + alert + the intake-wizard organism).",
"type": "component",
"sourceCode": "import { Component } from '@angular/core';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { IntakeWizardComponent } from '@herregistratie/ui/intake-wizard/intake-wizard.component';\n\n/** Page: the branching intake questionnaire. Built entirely from existing\n building blocks (page shell + alert + the intake-wizard organism). */\n@Component({\n selector: 'app-intake-page',\n imports: [PageShellComponent, AlertComponent, IntakeWizardComponent],\n template: `\n <app-page-shell heading=\"Herregistratie — intake\" backLink=\"/dashboard\">\n <app-alert type=\"info\">\n Een paar vragen bepalen welke gegevens we nodig hebben. Afhankelijk van uw\n antwoorden verschijnen er extra vragen. Uw antwoorden blijven bewaard als u\n de pagina herlaadt.\n </app-alert>\n <div style=\"margin-top:1.5rem\">\n <app-intake-wizard />\n </div>\n </app-page-shell>\n `,\n})\nexport class IntakePage {}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "IntakeWizardComponent",
"id": "component-IntakeWizardComponent-52bade37a06698f6b30c6175d4d62aa13679d7470643ea97895ebd96115ee352d9bc64cd17cc3fff68cc12fbbb65770febc3224779d3088980aaf1435efaa2ac",
"file": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-intake-wizard",
"styleUrls": [],
"styles": [],
"template": "@switch (state().tag) {\n @case ('Answering') {\n <p class=\"rhc-paragraph\" style=\"color:var(--rhc-color-grijs-700)\">Stap {{ cursor() + 1 }} van {{ steps.length }}</p>\n <form (ngSubmit)=\"onPrimary()\" style=\"max-width:30rem\">\n @switch (step()) {\n @case ('buitenland') {\n <app-form-field label=\"Heeft u de afgelopen 5 jaar buiten Nederland gewerkt?\" fieldId=\"buitenland\" [error]=\"err('buitenlandGewerkt')\">\n <app-radio-group name=\"buitenland\" [options]=\"jaNee\"\n [ngModel]=\"answers().buitenlandGewerkt ?? ''\" (ngModelChange)=\"set('buitenlandGewerkt', $event)\" />\n </app-form-field>\n @if (answers().buitenlandGewerkt === 'ja') {\n <app-form-field label=\"In welk land?\" fieldId=\"land\" [error]=\"err('land')\">\n <app-text-input inputId=\"land\" [ngModel]=\"answers().land ?? ''\" (ngModelChange)=\"set('land', $event)\" name=\"land\" placeholder=\"bijv. België\" />\n </app-form-field>\n <app-form-field label=\"Hoeveel uur heeft u daar gewerkt?\" fieldId=\"buitenlandseUren\" [error]=\"err('buitenlandseUren')\">\n <app-text-input inputId=\"buitenlandseUren\" [ngModel]=\"answers().buitenlandseUren ?? ''\" (ngModelChange)=\"set('buitenlandseUren', $event)\" name=\"buitenlandseUren\" placeholder=\"bijv. 800\" />\n </app-form-field>\n }\n }\n @case ('werk') {\n <app-form-field label=\"Gewerkte uren in Nederland (afgelopen 5 jaar)\" fieldId=\"uren\" [error]=\"err('uren')\">\n <app-text-input inputId=\"uren\" [ngModel]=\"answers().uren ?? ''\" (ngModelChange)=\"set('uren', $event)\" name=\"uren\" placeholder=\"bijv. 4160\" />\n </app-form-field>\n @if (scholingZichtbaar()) {\n <app-form-field label=\"U werkte relatief weinig uren. Heeft u aanvullende scholing gevolgd?\" fieldId=\"scholing\" [error]=\"err('scholingGevolgd')\">\n <app-radio-group name=\"scholing\" [options]=\"jaNee\"\n [ngModel]=\"answers().scholingGevolgd ?? ''\" (ngModelChange)=\"set('scholingGevolgd', $event)\" />\n </app-form-field>\n }\n @if (answers().scholingGevolgd === 'ja') {\n <app-form-field label=\"Behaalde nascholingspunten\" fieldId=\"punten\" [error]=\"err('punten')\">\n <app-text-input inputId=\"punten\" [ngModel]=\"answers().punten ?? ''\" (ngModelChange)=\"set('punten', $event)\" name=\"punten\" placeholder=\"bijv. 200\" />\n </app-form-field>\n }\n }\n @case ('review') {\n <app-alert type=\"info\">Controleer uw antwoorden en dien de aanvraag in.</app-alert>\n <dl class=\"rhc-data-summary rhc-data-summary--row\" style=\"margin:1rem 0\">\n <div><dt>Buiten NL gewerkt</dt><dd>{{ answers().buitenlandGewerkt ?? '—' }}</dd></div>\n @if (answers().buitenlandGewerkt === 'ja') {\n <div><dt>Land</dt><dd>{{ answers().land }}</dd></div>\n <div><dt>Buitenlandse uren</dt><dd>{{ answers().buitenlandseUren }}</dd></div>\n }\n <div><dt>Uren NL</dt><dd>{{ answers().uren }}</dd></div>\n @if (scholingZichtbaar()) {\n <div><dt>Aanvullende scholing</dt><dd>{{ answers().scholingGevolgd }}</dd></div>\n }\n @if (answers().scholingGevolgd === 'ja') {\n <div><dt>Nascholingspunten</dt><dd>{{ answers().punten }}</dd></div>\n }\n </dl>\n }\n }\n <div style=\"display:flex;gap:0.5rem;margin-top:1rem\">\n @if (cursor() > 0) {\n <app-button type=\"button\" variant=\"secondary\" (click)=\"dispatch({ tag: 'Back' })\">Vorige</app-button>\n }\n <app-button type=\"submit\" variant=\"primary\">{{ step() === 'review' ? 'Aanvraag indienen' : 'Volgende' }}</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n </form>\n }\n @case ('Submitting') {\n <app-spinner /> <span>Aanvraag wordt verwerkt…</span>\n }\n @case ('Submitted') {\n <app-alert type=\"ok\">Uw aanvraag tot herregistratie is ontvangen.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"restart()\">Opnieuw beginnen</app-button>\n </div>\n }\n @case ('Failed') {\n <app-alert type=\"error\">Indienen mislukt: {{ failedError() }}</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"onRetry()\">Opnieuw proberen</app-button>\n </div>\n }\n}\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "seed",
"defaultValue": "initial",
"deprecated": false,
"deprecationMessage": "",
"type": "IntakeState",
"indexKey": "",
"optional": false,
"description": "<p>Optional seed so Storybook / the showcase can mount any state directly.</p>\n",
"line": 129,
"rawdescription": "\nOptional seed so Storybook / the showcase can mount any state directly.",
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "answering",
"defaultValue": "computed(() => (this.state().tag === 'Answering' ? (this.state() as Extract<IntakeState, { tag: 'Answering' }>) : null))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 135,
"modifierKind": [
123
]
},
{
"name": "answers",
"defaultValue": "computed<Answers>(() => this.answering()?.answers ?? {})",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 139,
"modifierKind": [
124
]
},
{
"name": "cursor",
"defaultValue": "computed(() => this.answering()?.cursor ?? 0)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 138,
"modifierKind": [
124
]
},
{
"name": "dispatch",
"defaultValue": "this.store.dispatch",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 133,
"modifierKind": [
148
]
},
{
"name": "err",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 147,
"modifierKind": [
124
]
},
{
"name": "failedError",
"defaultValue": "computed(() => (this.state().tag === 'Failed' ? (this.state() as Extract<IntakeState, { tag: 'Failed' }>).error : ''))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 145,
"modifierKind": [
124
]
},
{
"name": "jaNee",
"defaultValue": "JA_NEE",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 131,
"modifierKind": [
148
]
},
{
"name": "policyRes",
"defaultValue": "httpResource<IntakePolicyDto>(() => 'mock/intake-policy.json', {\n defaultValue: { scholingThreshold: SCHOLING_THRESHOLD_DEFAULT },\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 124,
"modifierKind": [
123
]
},
{
"name": "profile",
"defaultValue": "inject(BigProfileStore)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 119,
"modifierKind": [
123
]
},
{
"name": "scholingThreshold",
"defaultValue": "computed(() => this.answering()?.scholingThreshold ?? SCHOLING_THRESHOLD_DEFAULT)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Server-owned threshold from the policy endpoint (mirrored into machine state).</p>\n",
"line": 142,
"rawdescription": "\nServer-owned threshold from the policy endpoint (mirrored into machine state).",
"modifierKind": [
124
]
},
{
"name": "scholingZichtbaar",
"defaultValue": "computed(() => lageUren(this.answers(), this.scholingThreshold()))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Whether the inline scholing question is shown (and required) in the &#39;werk&#39; step.</p>\n",
"line": 144,
"rawdescription": "\nWhether the inline scholing question is shown (and required) in the 'werk' step.",
"modifierKind": [
124
]
},
{
"name": "set",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 148,
"modifierKind": [
124
]
},
{
"name": "state",
"defaultValue": "this.store.model",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 132,
"modifierKind": [
148
]
},
{
"name": "step",
"defaultValue": "computed<StepId>(() => STEPS[Math.min(this.cursor(), STEPS.length - 1)])",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 140,
"modifierKind": [
124
]
},
{
"name": "steps",
"defaultValue": "STEPS",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Public so the showcase can render the (fixed) step list next to the wizard.</p>\n",
"line": 137,
"rawdescription": "\nPublic so the showcase can render the (fixed) step list next to the wizard.",
"modifierKind": [
148
]
},
{
"name": "store",
"defaultValue": "createStore<IntakeState, IntakeMsg>(initial, reduce)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 120,
"modifierKind": [
123
]
}
],
"methodsClass": [
{
"name": "onPrimary",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 179,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "onRetry",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 186,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "restart",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 191,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "restore",
"args": [],
"optional": false,
"returnType": "IntakeState | null",
"typeParameters": [],
"line": 169,
"deprecated": false,
"deprecationMessage": "",
"modifierKind": [
123
]
},
{
"name": "runIfSubmitting",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 197,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nThe effect: when we enter Submitting, call the backend, flip the optimistic\ncross-page flag, then dispatch the outcome (and commit/rollback).",
"description": "<p>The effect: when we enter Submitting, call the backend, flip the optimistic\ncross-page flag, then dispatch the outcome (and commit/rollback).</p>\n",
"modifierKind": [
123,
134
]
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "FormsModule",
"type": "module"
},
{
"name": "FormFieldComponent",
"type": "component"
},
{
"name": "TextInputComponent",
"type": "component"
},
{
"name": "RadioGroupComponent",
"type": "component"
},
{
"name": "ButtonComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "SpinnerComponent",
"type": "component"
}
],
"description": "<p>Organism: a BRANCHING intake questionnaire. All state lives in one signal\ndriven by the pure <code>reduce</code> (intake.machine.ts). Which step renders is derived\nfrom the answers via <code>visibleSteps</code>, never stored — so editing an earlier\nanswer immediately changes the remaining steps. Answers are persisted to\nlocalStorage so a page reload keeps the user&#39;s progress.</p>\n",
"rawdescription": "\nOrganism: a BRANCHING intake questionnaire. All state lives in one signal\ndriven by the pure `reduce` (intake.machine.ts). Which step renders is derived\nfrom the answers via `visibleSteps`, never stored — so editing an earlier\nanswer immediately changes the remaining steps. Answers are persisted to\nlocalStorage so a page reload keeps the user's progress.",
"type": "component",
"sourceCode": "import { Component, computed, effect, inject, input, untracked } from '@angular/core';\nimport { httpResource } from '@angular/common/http';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { RadioGroupComponent } from '@shared/ui/radio-group/radio-group.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { createStore } from '@shared/application/store';\nimport { BigProfileStore } from '@registratie/application/big-profile.store';\nimport {\n IntakeState,\n IntakeMsg,\n Answers,\n StepId,\n initial,\n reduce,\n STEPS,\n lageUren,\n SCHOLING_THRESHOLD_DEFAULT,\n} from '@herregistratie/domain/intake.machine';\nimport { IntakePolicyDto } from '@herregistratie/contracts/intake-policy.dto';\nimport { submitIntake } from '@herregistratie/application/submit-intake';\n\nconst STORAGE_KEY = 'intake-v3'; // ponytail: bump the suffix if the persisted state shape changes; no migration.\nconst JA_NEE = [{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }];\n\n/** Organism: a BRANCHING intake questionnaire. All state lives in one signal\n driven by the pure `reduce` (intake.machine.ts). Which step renders is derived\n from the answers via `visibleSteps`, never stored — so editing an earlier\n answer immediately changes the remaining steps. Answers are persisted to\n localStorage so a page reload keeps the user's progress. */\n@Component({\n selector: 'app-intake-wizard',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, RadioGroupComponent, ButtonComponent, AlertComponent, SpinnerComponent],\n template: `\n @switch (state().tag) {\n @case ('Answering') {\n <p class=\"rhc-paragraph\" style=\"color:var(--rhc-color-grijs-700)\">Stap {{ cursor() + 1 }} van {{ steps.length }}</p>\n <form (ngSubmit)=\"onPrimary()\" style=\"max-width:30rem\">\n @switch (step()) {\n @case ('buitenland') {\n <app-form-field label=\"Heeft u de afgelopen 5 jaar buiten Nederland gewerkt?\" fieldId=\"buitenland\" [error]=\"err('buitenlandGewerkt')\">\n <app-radio-group name=\"buitenland\" [options]=\"jaNee\"\n [ngModel]=\"answers().buitenlandGewerkt ?? ''\" (ngModelChange)=\"set('buitenlandGewerkt', $event)\" />\n </app-form-field>\n @if (answers().buitenlandGewerkt === 'ja') {\n <app-form-field label=\"In welk land?\" fieldId=\"land\" [error]=\"err('land')\">\n <app-text-input inputId=\"land\" [ngModel]=\"answers().land ?? ''\" (ngModelChange)=\"set('land', $event)\" name=\"land\" placeholder=\"bijv. België\" />\n </app-form-field>\n <app-form-field label=\"Hoeveel uur heeft u daar gewerkt?\" fieldId=\"buitenlandseUren\" [error]=\"err('buitenlandseUren')\">\n <app-text-input inputId=\"buitenlandseUren\" [ngModel]=\"answers().buitenlandseUren ?? ''\" (ngModelChange)=\"set('buitenlandseUren', $event)\" name=\"buitenlandseUren\" placeholder=\"bijv. 800\" />\n </app-form-field>\n }\n }\n @case ('werk') {\n <app-form-field label=\"Gewerkte uren in Nederland (afgelopen 5 jaar)\" fieldId=\"uren\" [error]=\"err('uren')\">\n <app-text-input inputId=\"uren\" [ngModel]=\"answers().uren ?? ''\" (ngModelChange)=\"set('uren', $event)\" name=\"uren\" placeholder=\"bijv. 4160\" />\n </app-form-field>\n @if (scholingZichtbaar()) {\n <app-form-field label=\"U werkte relatief weinig uren. Heeft u aanvullende scholing gevolgd?\" fieldId=\"scholing\" [error]=\"err('scholingGevolgd')\">\n <app-radio-group name=\"scholing\" [options]=\"jaNee\"\n [ngModel]=\"answers().scholingGevolgd ?? ''\" (ngModelChange)=\"set('scholingGevolgd', $event)\" />\n </app-form-field>\n }\n @if (answers().scholingGevolgd === 'ja') {\n <app-form-field label=\"Behaalde nascholingspunten\" fieldId=\"punten\" [error]=\"err('punten')\">\n <app-text-input inputId=\"punten\" [ngModel]=\"answers().punten ?? ''\" (ngModelChange)=\"set('punten', $event)\" name=\"punten\" placeholder=\"bijv. 200\" />\n </app-form-field>\n }\n }\n @case ('review') {\n <app-alert type=\"info\">Controleer uw antwoorden en dien de aanvraag in.</app-alert>\n <dl class=\"rhc-data-summary rhc-data-summary--row\" style=\"margin:1rem 0\">\n <div><dt>Buiten NL gewerkt</dt><dd>{{ answers().buitenlandGewerkt ?? '—' }}</dd></div>\n @if (answers().buitenlandGewerkt === 'ja') {\n <div><dt>Land</dt><dd>{{ answers().land }}</dd></div>\n <div><dt>Buitenlandse uren</dt><dd>{{ answers().buitenlandseUren }}</dd></div>\n }\n <div><dt>Uren NL</dt><dd>{{ answers().uren }}</dd></div>\n @if (scholingZichtbaar()) {\n <div><dt>Aanvullende scholing</dt><dd>{{ answers().scholingGevolgd }}</dd></div>\n }\n @if (answers().scholingGevolgd === 'ja') {\n <div><dt>Nascholingspunten</dt><dd>{{ answers().punten }}</dd></div>\n }\n </dl>\n }\n }\n <div style=\"display:flex;gap:0.5rem;margin-top:1rem\">\n @if (cursor() > 0) {\n <app-button type=\"button\" variant=\"secondary\" (click)=\"dispatch({ tag: 'Back' })\">Vorige</app-button>\n }\n <app-button type=\"submit\" variant=\"primary\">{{ step() === 'review' ? 'Aanvraag indienen' : 'Volgende' }}</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n </form>\n }\n @case ('Submitting') {\n <app-spinner /> <span>Aanvraag wordt verwerkt…</span>\n }\n @case ('Submitted') {\n <app-alert type=\"ok\">Uw aanvraag tot herregistratie is ontvangen.</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"restart()\">Opnieuw beginnen</app-button>\n </div>\n }\n @case ('Failed') {\n <app-alert type=\"error\">Indienen mislukt: {{ failedError() }}</app-alert>\n <div style=\"margin-top:1rem\">\n <app-button variant=\"secondary\" (click)=\"onRetry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n `,\n})\nexport class IntakeWizardComponent {\n private profile = inject(BigProfileStore);\n private store = createStore<IntakeState, IntakeMsg>(initial, reduce);\n\n // Server-owned policy: the scholing threshold is fetched, not hardcoded. The\n // backend stays the authority and re-validates on submit.\n private policyRes = httpResource<IntakePolicyDto>(() => 'mock/intake-policy.json', {\n defaultValue: { scholingThreshold: SCHOLING_THRESHOLD_DEFAULT },\n });\n\n /** Optional seed so Storybook / the showcase can mount any state directly. */\n seed = input<IntakeState>(initial);\n\n readonly jaNee = JA_NEE;\n readonly state = this.store.model;\n readonly dispatch = this.store.dispatch;\n\n private answering = computed(() => (this.state().tag === 'Answering' ? (this.state() as Extract<IntakeState, { tag: 'Answering' }>) : null));\n /** Public so the showcase can render the (fixed) step list next to the wizard. */\n readonly steps = STEPS;\n protected cursor = computed(() => this.answering()?.cursor ?? 0);\n protected answers = computed<Answers>(() => this.answering()?.answers ?? {});\n protected step = computed<StepId>(() => STEPS[Math.min(this.cursor(), STEPS.length - 1)]);\n /** Server-owned threshold from the policy endpoint (mirrored into machine state). */\n protected scholingThreshold = computed(() => this.answering()?.scholingThreshold ?? SCHOLING_THRESHOLD_DEFAULT);\n /** Whether the inline scholing question is shown (and required) in the 'werk' step. */\n protected scholingZichtbaar = computed(() => lageUren(this.answers(), this.scholingThreshold()));\n protected failedError = computed(() => (this.state().tag === 'Failed' ? (this.state() as Extract<IntakeState, { tag: 'Failed' }>).error : ''));\n\n protected err = (k: keyof Answers) => this.answering()?.errors[k] ?? '';\n protected set = (key: keyof Answers, value: string) => this.dispatch({ tag: 'SetAnswer', key, value });\n\n constructor() {\n // An explicit seed (stories) wins; otherwise resume from localStorage.\n const seeded = this.seed();\n queueMicrotask(() => this.dispatch({ tag: 'Seed', state: seeded !== initial ? seeded : (this.restore() ?? initial) }));\n // Persist only while answering; clear once the flow is done.\n effect(() => {\n const s = this.state();\n if (s.tag === 'Answering') localStorage.setItem(STORAGE_KEY, JSON.stringify(s));\n else localStorage.removeItem(STORAGE_KEY);\n });\n // Apply the server-owned threshold into machine state as it arrives. Track\n // only the policy value; untrack the dispatch (it reads the state signal\n // internally, which would otherwise make this effect loop on its own write).\n effect(() => {\n const scholingThreshold = this.policyRes.value().scholingThreshold;\n untracked(() => this.dispatch({ tag: 'SetPolicy', scholingThreshold }));\n });\n }\n\n private restore(): IntakeState | null {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as IntakeState;\n } catch {\n return null; // ponytail: corrupt entry -> start fresh, no migration.\n }\n }\n\n onPrimary() {\n const s = this.state();\n if (s.tag !== 'Answering') return;\n this.dispatch(this.step() === 'review' ? { tag: 'Submit' } : { tag: 'Next' });\n this.runIfSubmitting();\n }\n\n onRetry() {\n this.dispatch({ tag: 'Retry' });\n this.runIfSubmitting();\n }\n\n restart() {\n this.dispatch({ tag: 'Seed', state: initial });\n }\n\n /** The effect: when we enter Submitting, call the backend, flip the optimistic\n cross-page flag, then dispatch the outcome (and commit/rollback). */\n private async runIfSubmitting() {\n const s = this.state();\n if (s.tag !== 'Submitting') return;\n this.profile.beginHerregistratie();\n const r = await submitIntake(s.data);\n if (r.ok) {\n this.dispatch({ tag: 'SubmitConfirmed' });\n this.profile.confirmHerregistratie();\n } else {\n this.dispatch({ tag: 'SubmitFailed', error: r.error });\n this.profile.rollbackHerregistratie();\n }\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [],
"line": 148
},
"extends": []
},
{
"name": "LinkComponent",
"id": "component-LinkComponent-81d97782386b884f56c43d583cbf6d1005dda96974f169366a9af853aeb8b8f4e99f9cce2f9a7ef0fdafce32f51e6049483a44ab35614c486f82535b65ce2818",
"file": "src/app/shared/ui/link/link.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-link",
"styleUrls": [],
"styles": [],
"template": "<a [routerLink]=\"to()\" class=\"rhc-link nl-link utrecht-link\"><ng-content /></a>",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "to",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 11,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "RouterLink"
}
],
"description": "<p>Atom: link. Internal router link styled as an RHC/Utrecht link.</p>\n",
"rawdescription": "\nAtom: link. Internal router link styled as an RHC/Utrecht link.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\n/** Atom: link. Internal router link styled as an RHC/Utrecht link. */\n@Component({\n selector: 'app-link',\n imports: [RouterLink],\n template: `<a [routerLink]=\"to()\" class=\"rhc-link nl-link utrecht-link\"><ng-content /></a>`,\n})\nexport class LinkComponent {\n to = input.required<string>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "LoginFormComponent",
"id": "component-LoginFormComponent-8dba6a4389a532f36aaf74d6c5258521a2020ac11eb8c207aa1b0605da6f3971e1aeada702062295b5310aa9043c413de220e6062d0b8de97241f4a37f280ff0",
"file": "src/app/auth/ui/login-form/login-form.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-login-form",
"styleUrls": [],
"styles": [],
"template": "<form (ngSubmit)=\"submitted.emit(bsn)\">\n <app-form-field label=\"BSN\" fieldId=\"bsn\" description=\"9 cijfers (demo: vul iets in)\">\n <app-text-input inputId=\"bsn\" [(ngModel)]=\"bsn\" name=\"bsn\" placeholder=\"123456789\" />\n </app-form-field>\n\n <app-form-field label=\"Wachtwoord\" fieldId=\"pw\">\n <app-text-input inputId=\"pw\" type=\"password\" [(ngModel)]=\"password\" name=\"pw\" />\n </app-form-field>\n\n <div style=\"margin-top:1rem\">\n <app-button type=\"submit\" variant=\"primary\">Inloggen met DigiD</app-button>\n </div>\n</form>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [
{
"name": "submitted",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 30,
"required": false
}
],
"propertiesClass": [
{
"name": "bsn",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 28
},
{
"name": "password",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 29
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "FormsModule",
"type": "module"
},
{
"name": "FormFieldComponent",
"type": "component"
},
{
"name": "TextInputComponent",
"type": "component"
},
{
"name": "ButtonComponent",
"type": "component"
}
],
"description": "<p>Organism: DigiD-style mock login. No real auth — just composes atoms/molecules.</p>\n",
"rawdescription": "\nOrganism: DigiD-style mock login. No real auth — just composes atoms/molecules.",
"type": "component",
"sourceCode": "import { Component, output } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\n\n/** Organism: DigiD-style mock login. No real auth — just composes atoms/molecules. */\n@Component({\n selector: 'app-login-form',\n imports: [FormsModule, FormFieldComponent, TextInputComponent, ButtonComponent],\n template: `\n <form (ngSubmit)=\"submitted.emit(bsn)\">\n <app-form-field label=\"BSN\" fieldId=\"bsn\" description=\"9 cijfers (demo: vul iets in)\">\n <app-text-input inputId=\"bsn\" [(ngModel)]=\"bsn\" name=\"bsn\" placeholder=\"123456789\" />\n </app-form-field>\n\n <app-form-field label=\"Wachtwoord\" fieldId=\"pw\">\n <app-text-input inputId=\"pw\" type=\"password\" [(ngModel)]=\"password\" name=\"pw\" />\n </app-form-field>\n\n <div style=\"margin-top:1rem\">\n <app-button type=\"submit\" variant=\"primary\">Inloggen met DigiD</app-button>\n </div>\n </form>\n `,\n})\nexport class LoginFormComponent {\n bsn = '';\n password = '';\n submitted = output<string>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "LoginPage",
"id": "component-LoginPage-0cdc77f70073f58aa4eb17a72400adf1897b91d2c2d9df1dba75768c6f53a7f702d7389d6e9b43082faec0c24240d1963d21bd0f564f8980240b53009f615749",
"file": "src/app/auth/ui/login.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-login-page",
"styleUrls": [],
"styles": [],
"template": "<app-page-shell heading=\"Inloggen\" width=\"narrow\"\n intro=\"Log in op uw persoonlijke BIG-register omgeving.\">\n @if (error()) { <app-alert type=\"error\">{{ error() }}</app-alert> }\n <app-login-form (submitted)=\"login($event)\" />\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [
{
"name": "error",
"defaultValue": "signal('')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 22
},
{
"name": "router",
"defaultValue": "inject(Router)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 21,
"modifierKind": [
123
]
},
{
"name": "store",
"defaultValue": "inject(SessionStore)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 20,
"modifierKind": [
123
]
}
],
"methodsClass": [
{
"name": "login",
"args": [
{
"name": "bsn",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 24,
"deprecated": false,
"deprecationMessage": "",
"modifierKind": [
134
],
"jsdoctags": [
{
"name": "bsn",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "LoginFormComponent",
"type": "component"
}
],
"description": "",
"rawdescription": "\n",
"type": "component",
"sourceCode": "import { Component, inject, signal } from '@angular/core';\nimport { Router } from '@angular/router';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { LoginFormComponent } from '@auth/ui/login-form/login-form.component';\nimport { SessionStore } from '@auth/application/session.store';\n\n@Component({\n selector: 'app-login-page',\n imports: [PageShellComponent, AlertComponent, LoginFormComponent],\n template: `\n <app-page-shell heading=\"Inloggen\" width=\"narrow\"\n intro=\"Log in op uw persoonlijke BIG-register omgeving.\">\n @if (error()) { <app-alert type=\"error\">{{ error() }}</app-alert> }\n <app-login-form (submitted)=\"login($event)\" />\n </app-page-shell>\n `,\n})\nexport class LoginPage {\n private store = inject(SessionStore);\n private router = inject(Router);\n error = signal('');\n\n async login(bsn: string) {\n const r = await this.store.login(bsn);\n if (r.ok) this.router.navigate(['/dashboard']);\n else this.error.set(r.error);\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "PageShellComponent",
"id": "component-PageShellComponent-31358e27927ab9673066fef0e3dd43ab91c1d80ec87ecf99b63696dc8b050b0617895c809a2e76815b3ad8d4e0adfddd8ea0774c734bb7b08b0efa56bea0236c",
"file": "src/app/shared/layout/page-shell/page-shell.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-page-shell",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .body--narrow{max-inline-size:var(--app-form-narrow)}\n .crumb{margin-block-end:var(--rhc-space-max-xl)}\n .intro{margin-block-end:var(--rhc-space-max-2xl)}\n "
],
"template": "<div [class.body--narrow]=\"width() === 'narrow'\">\n @if (breadcrumb()) {\n <app-breadcrumb class=\"crumb\" [items]=\"breadcrumb()!\" />\n }\n @if (backLink()) {\n <p><app-link [to]=\"backLink()!\">← {{ backLabel() }}</app-link></p>\n }\n <app-heading [level]=\"1\">{{ heading() }}</app-heading>\n @if (intro()) {\n <p class=\"rhc-paragraph intro\">{{ intro() }}</p>\n }\n <ng-content />\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "backLabel",
"defaultValue": "'Terug naar overzicht'",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 38,
"required": false
},
{
"name": "backLink",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 37,
"required": false
},
{
"name": "breadcrumb",
"deprecated": false,
"deprecationMessage": "",
"type": "BreadcrumbItem[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 40,
"required": false
},
{
"name": "heading",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 35,
"required": true
},
{
"name": "intro",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 36,
"required": false
},
{
"name": "width",
"defaultValue": "'default'",
"deprecated": false,
"deprecationMessage": "",
"type": "\"default\" | \"narrow\"",
"indexKey": "",
"optional": false,
"description": "",
"line": 39,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "HeadingComponent",
"type": "component"
},
{
"name": "LinkComponent",
"type": "component"
},
{
"name": "BreadcrumbComponent",
"type": "component"
}
],
"description": "<p>Template: standard page body — optional breadcrumb, optional back-link, a\nheading, optional intro, and projected content. Rendered inside the persistent\nShellComponent via the router outlet, so it owns only the content (not chrome).</p>\n",
"rawdescription": "\nTemplate: standard page body — optional breadcrumb, optional back-link, a\nheading, optional intro, and projected content. Rendered inside the persistent\nShellComponent via the router outlet, so it owns only the content (not chrome).",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { LinkComponent } from '@shared/ui/link/link.component';\nimport { BreadcrumbComponent, BreadcrumbItem } from '@shared/layout/breadcrumb/breadcrumb.component';\n\n/** Template: standard page body — optional breadcrumb, optional back-link, a\n heading, optional intro, and projected content. Rendered inside the persistent\n ShellComponent via the router outlet, so it owns only the content (not chrome). */\n@Component({\n selector: 'app-page-shell',\n imports: [HeadingComponent, LinkComponent, BreadcrumbComponent],\n styles: [`\n :host{display:block}\n .body--narrow{max-inline-size:var(--app-form-narrow)}\n .crumb{margin-block-end:var(--rhc-space-max-xl)}\n .intro{margin-block-end:var(--rhc-space-max-2xl)}\n `],\n template: `\n <div [class.body--narrow]=\"width() === 'narrow'\">\n @if (breadcrumb()) {\n <app-breadcrumb class=\"crumb\" [items]=\"breadcrumb()!\" />\n }\n @if (backLink()) {\n <p><app-link [to]=\"backLink()!\">← {{ backLabel() }}</app-link></p>\n }\n <app-heading [level]=\"1\">{{ heading() }}</app-heading>\n @if (intro()) {\n <p class=\"rhc-paragraph intro\">{{ intro() }}</p>\n }\n <ng-content />\n </div>\n `,\n})\nexport class PageShellComponent {\n heading = input.required<string>();\n intro = input<string>();\n backLink = input<string>();\n backLabel = input('Terug naar overzicht');\n width = input<'default' | 'narrow'>('default');\n breadcrumb = input<BreadcrumbItem[]>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .body--narrow{max-inline-size:var(--app-form-narrow)}\n .crumb{margin-block-end:var(--rhc-space-max-xl)}\n .intro{margin-block-end:var(--rhc-space-max-2xl)}\n \n",
"extends": []
},
{
"name": "RadioGroupComponent",
"id": "component-RadioGroupComponent-a4c75390d3b62730cdd6f53a2ffcb3badde60f23c9104d57d8f8221d038f1dc96a229916ee28bbec4a54dd41e0e88c838ea46b35facfab0ddb5fea79fed37cc0",
"file": "src/app/shared/ui/radio-group/radio-group.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [
{
"name": ")"
}
],
"selector": "app-radio-group",
"styleUrls": [],
"styles": [
"\n .rhc-radio-option {\n display: flex;\n align-items: center;\n gap: var(--rhc-space-max-md);\n padding-block: var(--rhc-space-max-sm);\n }\n "
],
"template": "<div\n class=\"utrecht-form-field-radio-group\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"name() + '-label'\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() ? name() + '-error' : null\">\n @for (opt of options(); track opt.value) {\n <label class=\"utrecht-form-label utrecht-form-label--radio-button rhc-radio-option\">\n <input\n class=\"utrecht-radio-button\"\n [class.utrecht-radio-button--checked]=\"value === opt.value\"\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"opt.value\"\n [checked]=\"value === opt.value\"\n [disabled]=\"disabled\"\n (change)=\"select(opt.value)\"\n (blur)=\"onTouched()\" />\n {{ opt.label }}\n </label>\n }\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "invalid",
"defaultValue": "false",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 50,
"required": false
},
{
"name": "name",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 49,
"required": true
},
{
"name": "options",
"deprecated": false,
"deprecationMessage": "",
"type": "RadioOption[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 48,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "disabled",
"defaultValue": "false",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 53
},
{
"name": "onChange",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "function",
"indexKey": "",
"optional": false,
"description": "",
"line": 54
},
{
"name": "onTouched",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "function",
"indexKey": "",
"optional": false,
"description": "",
"line": 55
},
{
"name": "value",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 52
}
],
"methodsClass": [
{
"name": "registerOnChange",
"args": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
]
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 62,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"tagName": {
"text": "param"
}
}
]
},
{
"name": "registerOnTouched",
"args": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": []
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 63,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": [],
"tagName": {
"text": "param"
}
}
]
},
{
"name": "select",
"args": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 57,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setDisabledState",
"args": [
{
"name": "d",
"type": "boolean",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 64,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "d",
"type": "boolean",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "writeValue",
"args": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 61,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: a radio group. Thin wrapper over the Utrecht/RHC radio CSS, wired as a\nform control so it works with ngModel just like the text-input atom.</p>\n",
"rawdescription": "\nAtom: a radio group. Thin wrapper over the Utrecht/RHC radio CSS, wired as a\nform control so it works with ngModel just like the text-input atom.",
"type": "component",
"sourceCode": "import { Component, forwardRef, input } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\nexport interface RadioOption {\n value: string;\n label: string;\n}\n\n/** Atom: a radio group. Thin wrapper over the Utrecht/RHC radio CSS, wired as a\n form control so it works with ngModel just like the text-input atom. */\n@Component({\n selector: 'app-radio-group',\n styles: [`\n .rhc-radio-option {\n display: flex;\n align-items: center;\n gap: var(--rhc-space-max-md);\n padding-block: var(--rhc-space-max-sm);\n }\n `],\n template: `\n <div\n class=\"utrecht-form-field-radio-group\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"name() + '-label'\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() ? name() + '-error' : null\">\n @for (opt of options(); track opt.value) {\n <label class=\"utrecht-form-label utrecht-form-label--radio-button rhc-radio-option\">\n <input\n class=\"utrecht-radio-button\"\n [class.utrecht-radio-button--checked]=\"value === opt.value\"\n type=\"radio\"\n [name]=\"name()\"\n [value]=\"opt.value\"\n [checked]=\"value === opt.value\"\n [disabled]=\"disabled\"\n (change)=\"select(opt.value)\"\n (blur)=\"onTouched()\" />\n {{ opt.label }}\n </label>\n }\n </div>\n `,\n providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true }],\n})\nexport class RadioGroupComponent implements ControlValueAccessor {\n options = input.required<RadioOption[]>();\n name = input.required<string>();\n invalid = input(false);\n\n value = '';\n disabled = false;\n onChange: (v: string) => void = () => {};\n onTouched: () => void = () => {};\n\n select(v: string) {\n this.value = v;\n this.onChange(v);\n }\n writeValue(v: string) { this.value = v ?? ''; }\n registerOnChange(fn: (v: string) => void) { this.onChange = fn; }\n registerOnTouched(fn: () => void) { this.onTouched = fn; }\n setDisabledState(d: boolean) { this.disabled = d; }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n .rhc-radio-option {\n display: flex;\n align-items: center;\n gap: var(--rhc-space-max-md);\n padding-block: var(--rhc-space-max-sm);\n }\n \n",
"extends": [],
"implements": [
"ControlValueAccessor"
]
},
{
"name": "RegistratiePage",
"id": "component-RegistratiePage-90adf901a35dfb1d68fde165ed8bbed0a6d8c111cc033d0dc464aa1787630e07ce0ca89d287ff0b9344571fff03b13caaac81e657c32dfdddbf699563a7d4cc3",
"file": "src/app/registratie/ui/registratie.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-registratie-page",
"styleUrls": [],
"styles": [],
"template": "<app-page-shell\n heading=\"Inschrijven in het BIG-register\"\n backLink=\"/dashboard\"\n [breadcrumb]=\"[{ label: 'Mijn omgeving', link: '/dashboard' }, { label: 'Inschrijven in het BIG-register' }]\">\n <app-alert type=\"info\">\n In drie stappen schrijft u zich in: uw adres en correspondentievoorkeur, het\n diploma waarmee u zich registreert, en een controle. Uw gegevens blijven bewaard\n als u de pagina herlaadt.\n </app-alert>\n <div class=\"app-section\">\n <app-registratie-wizard />\n </div>\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "RegistratieWizardComponent",
"type": "component"
}
],
"description": "<p>Page: register in the BIG-register. Built entirely from existing building\nblocks (page shell + alert + the registratie-wizard organism).</p>\n",
"rawdescription": "\nPage: register in the BIG-register. Built entirely from existing building\nblocks (page shell + alert + the registratie-wizard organism).",
"type": "component",
"sourceCode": "import { Component } from '@angular/core';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { RegistratieWizardComponent } from '@registratie/ui/registratie-wizard/registratie-wizard.component';\n\n/** Page: register in the BIG-register. Built entirely from existing building\n blocks (page shell + alert + the registratie-wizard organism). */\n@Component({\n selector: 'app-registratie-page',\n imports: [PageShellComponent, AlertComponent, RegistratieWizardComponent],\n template: `\n <app-page-shell\n heading=\"Inschrijven in het BIG-register\"\n backLink=\"/dashboard\"\n [breadcrumb]=\"[{ label: 'Mijn omgeving', link: '/dashboard' }, { label: 'Inschrijven in het BIG-register' }]\">\n <app-alert type=\"info\">\n In drie stappen schrijft u zich in: uw adres en correspondentievoorkeur, het\n diploma waarmee u zich registreert, en een controle. Uw gegevens blijven bewaard\n als u de pagina herlaadt.\n </app-alert>\n <div class=\"app-section\">\n <app-registratie-wizard />\n </div>\n </app-page-shell>\n `,\n})\nexport class RegistratiePage {}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "RegistratieWizardComponent",
"id": "component-RegistratieWizardComponent-74dcf70dfe72a9b598de5861b8799d6a30f502bae7c2369ccee5759609bef6f4e6aaea03494efb1764da0a1d6437045516c78fb653c132564b7d49126f16fca5",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-registratie-wizard",
"styleUrls": [],
"styles": [],
"template": "@switch (state().tag) {\n @case ('Invullen') {\n <app-stepper class=\"app-section\" [steps]=\"stepLabels\" [current]=\"cursor()\" />\n <h2 #stepHeading tabindex=\"-1\" class=\"rhc-heading nl-heading--level-2 app-section\">{{ stepTitle() }}</h2>\n <form (ngSubmit)=\"onPrimary()\" class=\"app-form\">\n @switch (step()) {\n @case ('adres') {\n @if (adresStatus() === 'laden') {\n <app-skeleton height=\"2.5rem\" [count]=\"4\" />\n } @else {\n @switch (adresStatus()) {\n @case ('gevonden') {\n <app-alert type=\"info\">Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan.</app-alert>\n }\n @case ('geen') {\n <app-alert type=\"warning\">We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in.</app-alert>\n }\n @case ('fout') {\n <app-alert type=\"warning\">We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig in.</app-alert>\n }\n }\n <app-form-field label=\"Straat en huisnummer\" fieldId=\"straat\" [error]=\"err('straat')\">\n <app-text-input inputId=\"straat\" [invalid]=\"!!err('straat')\" [ngModel]=\"draft().straat ?? ''\" (ngModelChange)=\"set('straat', $event)\" name=\"straat\" />\n </app-form-field>\n <app-form-field label=\"Postcode\" fieldId=\"postcode\" [error]=\"err('postcode')\">\n <app-text-input inputId=\"postcode\" [invalid]=\"!!err('postcode')\" [ngModel]=\"draft().postcode ?? ''\" (ngModelChange)=\"set('postcode', $event)\" name=\"postcode\" placeholder=\"1234 AB\" />\n </app-form-field>\n <app-form-field label=\"Woonplaats\" fieldId=\"woonplaats\" [error]=\"err('woonplaats')\">\n <app-text-input inputId=\"woonplaats\" [invalid]=\"!!err('woonplaats')\" [ngModel]=\"draft().woonplaats ?? ''\" (ngModelChange)=\"set('woonplaats', $event)\" name=\"woonplaats\" />\n </app-form-field>\n <app-form-field label=\"Hoe wilt u correspondentie ontvangen?\" fieldId=\"correspondentie\" [error]=\"err('correspondentie')\">\n <app-radio-group name=\"correspondentie\" [options]=\"kanalen\" [invalid]=\"!!err('correspondentie')\"\n [ngModel]=\"draft().correspondentie ?? ''\" (ngModelChange)=\"setKanaal($event)\" />\n </app-form-field>\n @if (draft().correspondentie === 'email') {\n <app-form-field label=\"E-mailadres\" fieldId=\"email\" [error]=\"err('email')\">\n <app-text-input inputId=\"email\" type=\"email\" [invalid]=\"!!err('email')\" [ngModel]=\"draft().email ?? ''\" (ngModelChange)=\"set('email', $event)\" name=\"email\" placeholder=\"naam@voorbeeld.nl\" />\n </app-form-field>\n }\n }\n }\n @case ('beroep') {\n <app-async [data]=\"lookupRd()\">\n <ng-template appAsyncLoaded let-data>\n <app-form-field label=\"Kies het diploma waarmee u zich wilt registreren\" fieldId=\"diploma\" [error]=\"err('diploma')\">\n <app-radio-group name=\"diploma\" [options]=\"diplomaOptions($any(data))\" [invalid]=\"!!err('diploma')\"\n [ngModel]=\"diplomaKeuze()\" (ngModelChange)=\"onDiplomaKeuze($any(data), $event)\" />\n </app-form-field>\n\n @if (handmatigActief()) {\n <app-alert type=\"warning\">Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna handmatig beoordeeld.</app-alert>\n <app-form-field label=\"Voor welk beroep wilt u zich registreren?\" fieldId=\"hm-beroep\" [error]=\"err('diploma')\">\n <app-radio-group name=\"hm-beroep\" [options]=\"beroepOptions($any(data))\" [invalid]=\"!!err('diploma')\"\n [ngModel]=\"draft().beroep ?? ''\" (ngModelChange)=\"dispatch({ tag: 'DeclareerBeroep', beroep: $event })\" />\n </app-form-field>\n } @else if (draft().beroep) {\n <dl class=\"rhc-data-summary rhc-data-summary--row app-section\">\n <app-data-row key=\"Beroep (afgeleid uit diploma)\" [value]=\"draft().beroep ?? ''\" />\n </dl>\n }\n\n @for (q of actieveVragen($any(data)); track q.id) {\n <app-form-field [label]=\"q.vraag\" [fieldId]=\"'vraag-' + q.id\" [error]=\"vraagErr(q.id)\">\n @if (q.type === 'ja-nee') {\n <app-radio-group [name]=\"'vraag-' + q.id\" [options]=\"jaNee\" [invalid]=\"!!vraagErr(q.id)\"\n [ngModel]=\"draft().antwoorden[q.id] ?? ''\" (ngModelChange)=\"dispatch({ tag: 'SetAntwoord', vraagId: q.id, value: $event })\" [ngModelOptions]=\"{ standalone: true }\" />\n } @else {\n <app-text-input [inputId]=\"'vraag-' + q.id\" [invalid]=\"!!vraagErr(q.id)\" [ngModel]=\"draft().antwoorden[q.id] ?? ''\"\n (ngModelChange)=\"dispatch({ tag: 'SetAntwoord', vraagId: q.id, value: $event })\" [ngModelOptions]=\"{ standalone: true }\" />\n }\n </app-form-field>\n }\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"3\" />\n </ng-template>\n </app-async>\n }\n @case ('controle') {\n <app-alert type=\"info\">Controleer uw gegevens en dien de registratie in.</app-alert>\n <dl class=\"rhc-data-summary rhc-data-summary--row app-section\">\n <app-data-row key=\"Adres\" [value]=\"adresSamenvatting()\" />\n <app-data-row key=\"Herkomst adres\" [value]=\"adresHerkomstLabel()\" />\n <app-data-row key=\"Correspondentie\" [value]=\"correspondentieLabel()\" />\n @if (draft().correspondentie === 'email') {\n <app-data-row key=\"E-mailadres\" [value]=\"draft().email ?? ''\" />\n }\n <app-data-row key=\"Beroep\" [value]=\"draft().beroep ?? ''\" />\n <app-data-row key=\"Herkomst diploma\" [value]=\"diplomaHerkomstLabel()\" />\n @for (item of samenvattingVragen(); track item.vraag) {\n <app-data-row [key]=\"item.vraag\" [value]=\"item.antwoord\" />\n }\n </dl>\n <div class=\"app-button-row app-section\">\n <app-button type=\"button\" variant=\"subtle\" (click)=\"dispatch({ tag: 'GaNaarStap', cursor: 0 })\">Adres wijzigen</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"dispatch({ tag: 'GaNaarStap', cursor: 1 })\">Diploma wijzigen</app-button>\n </div>\n }\n }\n <div class=\"app-button-row app-button-row--spaced\">\n @if (cursor() > 0) {\n <app-button type=\"button\" variant=\"secondary\" (click)=\"dispatch({ tag: 'Back' })\">Vorige</app-button>\n }\n <app-button type=\"submit\" variant=\"primary\">{{ step() === 'controle' ? 'Registratie indienen' : 'Volgende' }}</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n </form>\n }\n @case ('Indienen') {\n <app-spinner /> <span>Uw registratie wordt verwerkt…</span>\n }\n @case ('Ingediend') {\n <app-alert type=\"ok\">Uw registratie is ontvangen. Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie.</app-alert>\n <div class=\"app-section\">\n <app-button variant=\"secondary\" (click)=\"restart()\">Nieuwe registratie starten</app-button>\n </div>\n }\n @case ('Mislukt') {\n <app-alert type=\"error\">Het indienen is niet gelukt: {{ failedError() }}</app-alert>\n <div class=\"app-section\">\n <app-button variant=\"secondary\" (click)=\"onRetry()\">Opnieuw proberen</app-button>\n </div>\n }\n}\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "seed",
"defaultValue": "initial",
"deprecated": false,
"deprecationMessage": "",
"type": "RegistratieState",
"indexKey": "",
"optional": false,
"description": "<p>Optional seed so Storybook / tests can mount any state directly.</p>\n",
"line": 184,
"rawdescription": "\nOptional seed so Storybook / tests can mount any state directly.",
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "actieveVragen",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>The policy questions that apply to the current choice (server-decided).</p>\n",
"line": 258,
"rawdescription": "\nThe policy questions that apply to the current choice (server-decided).",
"modifierKind": [
124
]
},
{
"name": "adresHerkomstLabel",
"defaultValue": "computed(() => ({ brp: 'Automatisch uit de BRP', handmatig: 'Handmatig ingevoerd' }[this.draft().adresHerkomst ?? 'handmatig']))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 207,
"modifierKind": [
124
]
},
{
"name": "adresRes",
"defaultValue": "this.brp.adresResource()",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 180,
"modifierKind": [
124
]
},
{
"name": "adresSamenvatting",
"defaultValue": "computed(() => {\n const d = this.draft();\n return [d.straat, [d.postcode, d.woonplaats].filter(Boolean).join(' ')].filter(Boolean).join(', ');\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 202,
"modifierKind": [
124
]
},
{
"name": "adresStatus",
"defaultValue": "computed<'laden' | 'gevonden' | 'geen' | 'fout'>(() => {\n const st = this.adresRes.status();\n if (st === 'loading' || st === 'reloading') return 'laden';\n if (st === 'error') return 'fout';\n const json = this.adresRes.value();\n const parsed = json !== undefined ? parseBrpAddress(json) : null;\n return parsed && parsed.ok && parsed.value.gevonden ? 'gevonden' : 'geen';\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>BRP lookup outcome. A failure or &quot;geen adres&quot; never blocks the wizard — both\nfall back to manual entry (PRD §7). &#39;geen&#39; covers both no-address-found and a\nmalformed response; &#39;fout&#39; is an unreachable BRP.</p>\n",
"line": 214,
"rawdescription": "\nBRP lookup outcome. A failure or \"geen adres\" never blocks the wizard — both\nfall back to manual entry (PRD §7). 'geen' covers both no-address-found and a\nmalformed response; 'fout' is an unreachable BRP.",
"modifierKind": [
124
]
},
{
"name": "beroepOptions",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 255,
"modifierKind": [
124
]
},
{
"name": "brp",
"defaultValue": "inject(BrpAdapter)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 176,
"modifierKind": [
123
]
},
{
"name": "correspondentieLabel",
"defaultValue": "computed(() => ({ email: 'Per e-mail', post: 'Per post' }[this.draft().correspondentie ?? 'post']))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 208,
"modifierKind": [
124
]
},
{
"name": "cursor",
"defaultValue": "computed(() => this.invullen()?.cursor ?? 0)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 196,
"modifierKind": [
124
]
},
{
"name": "diplomaHerkomstLabel",
"defaultValue": "computed(() => ({ duo: 'Geverifieerd via DUO', handmatig: 'Handmatig ingevoerd (wordt beoordeeld)' }[this.draft().diplomaHerkomst ?? 'handmatig']))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 209,
"modifierKind": [
124
]
},
{
"name": "diplomaKeuze",
"defaultValue": "computed(() => (this.handmatigActief() ? HANDMATIG : this.draft().diplomaId ?? ''))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>The radio selection: a diploma id, or the &quot;not listed&quot; sentinel in manual mode.</p>\n",
"line": 248,
"rawdescription": "\nThe radio selection: a diploma id, or the \"not listed\" sentinel in manual mode.",
"modifierKind": [
124
]
},
{
"name": "diplomaOptions",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 250,
"modifierKind": [
124
]
},
{
"name": "diplomasRes",
"defaultValue": "this.duo.diplomasResource()",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 181,
"modifierKind": [
123
]
},
{
"name": "dispatch",
"defaultValue": "this.store.dispatch",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 190,
"modifierKind": [
148
]
},
{
"name": "draft",
"defaultValue": "computed<Draft>(() => this.invullen()?.draft ?? { antwoorden: {} })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 197,
"modifierKind": [
124
]
},
{
"name": "duo",
"defaultValue": "inject(DuoAdapter)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 177,
"modifierKind": [
123
]
},
{
"name": "duoData",
"defaultValue": "computed<DuoLookupDto | null>(() => {\n const rd = this.lookupRd();\n return rd.tag === 'Success' ? rd.value : null;\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Parsed lookup as a plain value (or null) — used outside the beroep step (the\ncontrole summary) where the <app-async> template variable isn&#39;t in scope.</p>\n",
"line": 233,
"rawdescription": "\nParsed lookup as a plain value (or null) — used outside the beroep step (the\ncontrole summary) where the <app-async> template variable isn't in scope.",
"modifierKind": [
123
]
},
{
"name": "err",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 240,
"modifierKind": [
124
]
},
{
"name": "failedError",
"defaultValue": "computed(() => (this.state().tag === 'Mislukt' ? (this.state() as Extract<RegistratieState, { tag: 'Mislukt' }>).error : ''))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 201,
"modifierKind": [
124
]
},
{
"name": "handmatigActief",
"defaultValue": "computed(() => this.draft().diplomaHerkomst === 'handmatig')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>True while the user is entering a diploma manually (not in the DUO list).</p>\n",
"line": 246,
"rawdescription": "\nTrue while the user is entering a diploma manually (not in the DUO list).",
"modifierKind": [
124
]
},
{
"name": "invullen",
"defaultValue": "computed(() => (this.state().tag === 'Invullen' ? (this.state() as Extract<RegistratieState, { tag: 'Invullen' }>) : null))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 195,
"modifierKind": [
123
]
},
{
"name": "jaNee",
"defaultValue": "JA_NEE",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 238,
"modifierKind": [
148
]
},
{
"name": "kanalen",
"defaultValue": "KANALEN",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 186,
"modifierKind": [
148
]
},
{
"name": "lookupRd",
"defaultValue": "computed<RemoteData<Error | undefined, DuoLookupDto>>(() => {\n const rd = fromResource(this.diplomasRes);\n if (rd.tag !== 'Success') return rd;\n const parsed = parseDuoLookup(rd.value);\n return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) };\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>The DUO lookup (diplomas + manual fallback), validated at the trust boundary.</p>\n",
"line": 224,
"rawdescription": "\nThe DUO lookup (diplomas + manual fallback), validated at the trust boundary.",
"modifierKind": [
124
]
},
{
"name": "referentie",
"defaultValue": "computed(() => (this.state().tag === 'Ingediend' ? (this.state() as Extract<RegistratieState, { tag: 'Ingediend' }>).referentie : ''))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 200,
"modifierKind": [
124
]
},
{
"name": "samenvattingVragen",
"defaultValue": "computed(() => {\n const data = this.duoData();\n const d = this.draft();\n if (!data) return [] as { vraag: string; antwoord: string }[];\n const alle = [...data.diplomas.flatMap((x) => x.policyQuestions), ...data.handmatig.policyQuestions];\n return (d.vraagIds ?? []).map((id) => ({ vraag: alle.find((q) => q.id === id)?.vraag ?? id, antwoord: d.antwoorden[id] ?? '' }));\n })",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Answered policy questions for the controle summary (question text + answer).</p>\n",
"line": 264,
"rawdescription": "\nAnswered policy questions for the controle summary (question text + answer).",
"modifierKind": [
124
]
},
{
"name": "set",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 242,
"modifierKind": [
124
]
},
{
"name": "setKanaal",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 243,
"modifierKind": [
124
]
},
{
"name": "state",
"defaultValue": "this.store.model",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 189,
"modifierKind": [
148
]
},
{
"name": "step",
"defaultValue": "computed<StepId>(() => STEPS[Math.min(this.cursor(), STEPS.length - 1)])",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 198,
"modifierKind": [
124
]
},
{
"name": "stepHeading",
"defaultValue": "viewChild<ElementRef<HTMLElement>>('stepHeading')",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "<p>Focus target: the step heading receives focus on step change (a11y).</p>\n",
"line": 193,
"rawdescription": "\nFocus target: the step heading receives focus on step change (a11y).",
"modifierKind": [
123
]
},
{
"name": "stepLabels",
"defaultValue": "['Adres', 'Beroep', 'Controle']",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 187,
"modifierKind": [
148
]
},
{
"name": "stepTitle",
"defaultValue": "computed(() => this.stepTitles[Math.min(this.cursor(), this.stepTitles.length - 1)])",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 199,
"modifierKind": [
124
]
},
{
"name": "stepTitles",
"defaultValue": "['Adres en correspondentievoorkeur', 'Beroep op basis van uw diploma', 'Controleren en indienen']",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 188,
"modifierKind": [
123
]
},
{
"name": "store",
"defaultValue": "createStore<RegistratieState, RegistratieMsg>(initial, reduce)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 178,
"modifierKind": [
123
]
},
{
"name": "vraagErr",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 241,
"modifierKind": [
124
]
}
],
"methodsClass": [
{
"name": "onDiplomaKeuze",
"args": [
{
"name": "data",
"type": "DuoLookupDto",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "id",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 272,
"deprecated": false,
"deprecationMessage": "",
"modifierKind": [
124
],
"jsdoctags": [
{
"name": "data",
"type": "DuoLookupDto",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "id",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "onPrimary",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 332,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "onRetry",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 339,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "restart",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 346,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nReset the wizard to a fresh start. Reload the BRP lookup so the address\nre-prefills, keeping the form and the \"vooraf ingevuld\" note consistent.",
"description": "<p>Reset the wizard to a fresh start. Reload the BRP lookup so the address\nre-prefills, keeping the form and the &quot;vooraf ingevuld&quot; note consistent.</p>\n"
},
{
"name": "restore",
"args": [],
"optional": false,
"returnType": "RegistratieState | null",
"typeParameters": [],
"line": 322,
"deprecated": false,
"deprecationMessage": "",
"modifierKind": [
123
]
},
{
"name": "runIfIndienen",
"args": [],
"optional": false,
"returnType": "any",
"typeParameters": [],
"line": 353,
"deprecated": false,
"deprecationMessage": "",
"rawdescription": "\nThe effect: when we enter Indienen, call the backend, then dispatch the\noutcome (success carries the confirmation reference).",
"description": "<p>The effect: when we enter Indienen, call the backend, then dispatch the\noutcome (success carries the confirmation reference).</p>\n",
"modifierKind": [
123,
134
]
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "FormsModule",
"type": "module"
},
{
"name": "FormFieldComponent",
"type": "component"
},
{
"name": "TextInputComponent",
"type": "component"
},
{
"name": "RadioGroupComponent",
"type": "component"
},
{
"name": "ButtonComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "SpinnerComponent",
"type": "component"
},
{
"name": "SkeletonComponent",
"type": "component"
},
{
"name": "DataRowComponent",
"type": "component"
},
{
"name": "StepperComponent",
"type": "component"
},
{
"name": "ASYNC"
}
],
"description": "<p>Organism: the BIG-registration wizard. All state lives in one signal driven by\nthe pure <code>reduce</code> (registratie-wizard.machine.ts). The BRP address prefills the\ndraft via an effect; the DUO diploma list renders through <app-async>; choosing\na diploma reveals its server-derived beroep. The draft is persisted to\nlocalStorage so a reload keeps the user&#39;s progress. Built entirely from existing\natoms/molecules.</p>\n",
"rawdescription": "\nOrganism: the BIG-registration wizard. All state lives in one signal driven by\nthe pure `reduce` (registratie-wizard.machine.ts). The BRP address prefills the\ndraft via an effect; the DUO diploma list renders through <app-async>; choosing\na diploma reveals its server-derived beroep. The draft is persisted to\nlocalStorage so a reload keeps the user's progress. Built entirely from existing\natoms/molecules.",
"type": "component",
"sourceCode": "import { Component, ElementRef, computed, effect, inject, input, untracked, viewChild } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { FormFieldComponent } from '@shared/ui/form-field/form-field.component';\nimport { TextInputComponent } from '@shared/ui/text-input/text-input.component';\nimport { RadioGroupComponent } from '@shared/ui/radio-group/radio-group.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { SpinnerComponent } from '@shared/ui/spinner/spinner.component';\nimport { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';\nimport { DataRowComponent } from '@shared/ui/data-row/data-row.component';\nimport { StepperComponent } from '@shared/ui/stepper/stepper.component';\nimport { ASYNC } from '@shared/ui/async/async.component';\nimport { createStore } from '@shared/application/store';\nimport { RemoteData, fromResource } from '@shared/application/remote-data';\nimport { BrpAdapter, parseBrpAddress } from '@registratie/infrastructure/brp.adapter';\nimport { DuoAdapter, parseDuoLookup } from '@registratie/infrastructure/duo.adapter';\nimport { DuoLookupDto, PolicyQuestionDto } from '@registratie/contracts/duo-diplomas.dto';\nimport {\n RegistratieState,\n RegistratieMsg,\n Draft,\n DraftField,\n Correspondentie,\n StepId,\n initial,\n reduce,\n STEPS,\n} from '@registratie/domain/registratie-wizard.machine';\nimport { submitRegistratie } from '@registratie/application/submit-registratie';\n\nconst STORAGE_KEY = 'registratie-v1'; // ponytail: bump the suffix if the persisted shape changes; no migration.\nconst KANALEN = [{ value: 'email', label: 'E-mail' }, { value: 'post', label: 'Post' }];\nconst JA_NEE = [{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }];\nconst HANDMATIG = '__handmatig__'; // sentinel option: \"my diploma isn't listed\"\n\n/** Organism: the BIG-registration wizard. All state lives in one signal driven by\n the pure `reduce` (registratie-wizard.machine.ts). The BRP address prefills the\n draft via an effect; the DUO diploma list renders through <app-async>; choosing\n a diploma reveals its server-derived beroep. The draft is persisted to\n localStorage so a reload keeps the user's progress. Built entirely from existing\n atoms/molecules. */\n@Component({\n selector: 'app-registratie-wizard',\n imports: [\n FormsModule, FormFieldComponent, TextInputComponent, RadioGroupComponent, ButtonComponent,\n AlertComponent, SpinnerComponent, SkeletonComponent, DataRowComponent, StepperComponent, ...ASYNC,\n ],\n template: `\n @switch (state().tag) {\n @case ('Invullen') {\n <app-stepper class=\"app-section\" [steps]=\"stepLabels\" [current]=\"cursor()\" />\n <h2 #stepHeading tabindex=\"-1\" class=\"rhc-heading nl-heading--level-2 app-section\">{{ stepTitle() }}</h2>\n <form (ngSubmit)=\"onPrimary()\" class=\"app-form\">\n @switch (step()) {\n @case ('adres') {\n @if (adresStatus() === 'laden') {\n <app-skeleton height=\"2.5rem\" [count]=\"4\" />\n } @else {\n @switch (adresStatus()) {\n @case ('gevonden') {\n <app-alert type=\"info\">Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan.</app-alert>\n }\n @case ('geen') {\n <app-alert type=\"warning\">We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in.</app-alert>\n }\n @case ('fout') {\n <app-alert type=\"warning\">We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig in.</app-alert>\n }\n }\n <app-form-field label=\"Straat en huisnummer\" fieldId=\"straat\" [error]=\"err('straat')\">\n <app-text-input inputId=\"straat\" [invalid]=\"!!err('straat')\" [ngModel]=\"draft().straat ?? ''\" (ngModelChange)=\"set('straat', $event)\" name=\"straat\" />\n </app-form-field>\n <app-form-field label=\"Postcode\" fieldId=\"postcode\" [error]=\"err('postcode')\">\n <app-text-input inputId=\"postcode\" [invalid]=\"!!err('postcode')\" [ngModel]=\"draft().postcode ?? ''\" (ngModelChange)=\"set('postcode', $event)\" name=\"postcode\" placeholder=\"1234 AB\" />\n </app-form-field>\n <app-form-field label=\"Woonplaats\" fieldId=\"woonplaats\" [error]=\"err('woonplaats')\">\n <app-text-input inputId=\"woonplaats\" [invalid]=\"!!err('woonplaats')\" [ngModel]=\"draft().woonplaats ?? ''\" (ngModelChange)=\"set('woonplaats', $event)\" name=\"woonplaats\" />\n </app-form-field>\n <app-form-field label=\"Hoe wilt u correspondentie ontvangen?\" fieldId=\"correspondentie\" [error]=\"err('correspondentie')\">\n <app-radio-group name=\"correspondentie\" [options]=\"kanalen\" [invalid]=\"!!err('correspondentie')\"\n [ngModel]=\"draft().correspondentie ?? ''\" (ngModelChange)=\"setKanaal($event)\" />\n </app-form-field>\n @if (draft().correspondentie === 'email') {\n <app-form-field label=\"E-mailadres\" fieldId=\"email\" [error]=\"err('email')\">\n <app-text-input inputId=\"email\" type=\"email\" [invalid]=\"!!err('email')\" [ngModel]=\"draft().email ?? ''\" (ngModelChange)=\"set('email', $event)\" name=\"email\" placeholder=\"naam@voorbeeld.nl\" />\n </app-form-field>\n }\n }\n }\n @case ('beroep') {\n <app-async [data]=\"lookupRd()\">\n <ng-template appAsyncLoaded let-data>\n <app-form-field label=\"Kies het diploma waarmee u zich wilt registreren\" fieldId=\"diploma\" [error]=\"err('diploma')\">\n <app-radio-group name=\"diploma\" [options]=\"diplomaOptions($any(data))\" [invalid]=\"!!err('diploma')\"\n [ngModel]=\"diplomaKeuze()\" (ngModelChange)=\"onDiplomaKeuze($any(data), $event)\" />\n </app-form-field>\n\n @if (handmatigActief()) {\n <app-alert type=\"warning\">Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna handmatig beoordeeld.</app-alert>\n <app-form-field label=\"Voor welk beroep wilt u zich registreren?\" fieldId=\"hm-beroep\" [error]=\"err('diploma')\">\n <app-radio-group name=\"hm-beroep\" [options]=\"beroepOptions($any(data))\" [invalid]=\"!!err('diploma')\"\n [ngModel]=\"draft().beroep ?? ''\" (ngModelChange)=\"dispatch({ tag: 'DeclareerBeroep', beroep: $event })\" />\n </app-form-field>\n } @else if (draft().beroep) {\n <dl class=\"rhc-data-summary rhc-data-summary--row app-section\">\n <app-data-row key=\"Beroep (afgeleid uit diploma)\" [value]=\"draft().beroep ?? ''\" />\n </dl>\n }\n\n @for (q of actieveVragen($any(data)); track q.id) {\n <app-form-field [label]=\"q.vraag\" [fieldId]=\"'vraag-' + q.id\" [error]=\"vraagErr(q.id)\">\n @if (q.type === 'ja-nee') {\n <app-radio-group [name]=\"'vraag-' + q.id\" [options]=\"jaNee\" [invalid]=\"!!vraagErr(q.id)\"\n [ngModel]=\"draft().antwoorden[q.id] ?? ''\" (ngModelChange)=\"dispatch({ tag: 'SetAntwoord', vraagId: q.id, value: $event })\" [ngModelOptions]=\"{ standalone: true }\" />\n } @else {\n <app-text-input [inputId]=\"'vraag-' + q.id\" [invalid]=\"!!vraagErr(q.id)\" [ngModel]=\"draft().antwoorden[q.id] ?? ''\"\n (ngModelChange)=\"dispatch({ tag: 'SetAntwoord', vraagId: q.id, value: $event })\" [ngModelOptions]=\"{ standalone: true }\" />\n }\n </app-form-field>\n }\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"3\" />\n </ng-template>\n </app-async>\n }\n @case ('controle') {\n <app-alert type=\"info\">Controleer uw gegevens en dien de registratie in.</app-alert>\n <dl class=\"rhc-data-summary rhc-data-summary--row app-section\">\n <app-data-row key=\"Adres\" [value]=\"adresSamenvatting()\" />\n <app-data-row key=\"Herkomst adres\" [value]=\"adresHerkomstLabel()\" />\n <app-data-row key=\"Correspondentie\" [value]=\"correspondentieLabel()\" />\n @if (draft().correspondentie === 'email') {\n <app-data-row key=\"E-mailadres\" [value]=\"draft().email ?? ''\" />\n }\n <app-data-row key=\"Beroep\" [value]=\"draft().beroep ?? ''\" />\n <app-data-row key=\"Herkomst diploma\" [value]=\"diplomaHerkomstLabel()\" />\n @for (item of samenvattingVragen(); track item.vraag) {\n <app-data-row [key]=\"item.vraag\" [value]=\"item.antwoord\" />\n }\n </dl>\n <div class=\"app-button-row app-section\">\n <app-button type=\"button\" variant=\"subtle\" (click)=\"dispatch({ tag: 'GaNaarStap', cursor: 0 })\">Adres wijzigen</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"dispatch({ tag: 'GaNaarStap', cursor: 1 })\">Diploma wijzigen</app-button>\n </div>\n }\n }\n <div class=\"app-button-row app-button-row--spaced\">\n @if (cursor() > 0) {\n <app-button type=\"button\" variant=\"secondary\" (click)=\"dispatch({ tag: 'Back' })\">Vorige</app-button>\n }\n <app-button type=\"submit\" variant=\"primary\">{{ step() === 'controle' ? 'Registratie indienen' : 'Volgende' }}</app-button>\n <app-button type=\"button\" variant=\"subtle\" (click)=\"restart()\">Annuleren</app-button>\n </div>\n </form>\n }\n @case ('Indienen') {\n <app-spinner /> <span>Uw registratie wordt verwerkt…</span>\n }\n @case ('Ingediend') {\n <app-alert type=\"ok\">Uw registratie is ontvangen. Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie.</app-alert>\n <div class=\"app-section\">\n <app-button variant=\"secondary\" (click)=\"restart()\">Nieuwe registratie starten</app-button>\n </div>\n }\n @case ('Mislukt') {\n <app-alert type=\"error\">Het indienen is niet gelukt: {{ failedError() }}</app-alert>\n <div class=\"app-section\">\n <app-button variant=\"secondary\" (click)=\"onRetry()\">Opnieuw proberen</app-button>\n </div>\n }\n }\n `,\n})\nexport class RegistratieWizardComponent {\n private brp = inject(BrpAdapter);\n private duo = inject(DuoAdapter);\n private store = createStore<RegistratieState, RegistratieMsg>(initial, reduce);\n\n protected adresRes = this.brp.adresResource();\n private diplomasRes = this.duo.diplomasResource();\n\n /** Optional seed so Storybook / tests can mount any state directly. */\n seed = input<RegistratieState>(initial);\n\n readonly kanalen = KANALEN;\n readonly stepLabels = ['Adres', 'Beroep', 'Controle']; // short labels for the stepper\n private stepTitles = ['Adres en correspondentievoorkeur', 'Beroep op basis van uw diploma', 'Controleren en indienen'];\n readonly state = this.store.model;\n readonly dispatch = this.store.dispatch;\n\n /** Focus target: the step heading receives focus on step change (a11y). */\n private stepHeading = viewChild<ElementRef<HTMLElement>>('stepHeading');\n\n private invullen = computed(() => (this.state().tag === 'Invullen' ? (this.state() as Extract<RegistratieState, { tag: 'Invullen' }>) : null));\n protected cursor = computed(() => this.invullen()?.cursor ?? 0);\n protected draft = computed<Draft>(() => this.invullen()?.draft ?? { antwoorden: {} });\n protected step = computed<StepId>(() => STEPS[Math.min(this.cursor(), STEPS.length - 1)]);\n protected stepTitle = computed(() => this.stepTitles[Math.min(this.cursor(), this.stepTitles.length - 1)]);\n protected referentie = computed(() => (this.state().tag === 'Ingediend' ? (this.state() as Extract<RegistratieState, { tag: 'Ingediend' }>).referentie : ''));\n protected failedError = computed(() => (this.state().tag === 'Mislukt' ? (this.state() as Extract<RegistratieState, { tag: 'Mislukt' }>).error : ''));\n protected adresSamenvatting = computed(() => {\n const d = this.draft();\n return [d.straat, [d.postcode, d.woonplaats].filter(Boolean).join(' ')].filter(Boolean).join(', ');\n });\n // Readable labels for the controle summary (instead of raw enum values).\n protected adresHerkomstLabel = computed(() => ({ brp: 'Automatisch uit de BRP', handmatig: 'Handmatig ingevoerd' }[this.draft().adresHerkomst ?? 'handmatig']));\n protected correspondentieLabel = computed(() => ({ email: 'Per e-mail', post: 'Per post' }[this.draft().correspondentie ?? 'post']));\n protected diplomaHerkomstLabel = computed(() => ({ duo: 'Geverifieerd via DUO', handmatig: 'Handmatig ingevoerd (wordt beoordeeld)' }[this.draft().diplomaHerkomst ?? 'handmatig']));\n\n /** BRP lookup outcome. A failure or \"geen adres\" never blocks the wizard — both\n fall back to manual entry (PRD §7). 'geen' covers both no-address-found and a\n malformed response; 'fout' is an unreachable BRP. */\n protected adresStatus = computed<'laden' | 'gevonden' | 'geen' | 'fout'>(() => {\n const st = this.adresRes.status();\n if (st === 'loading' || st === 'reloading') return 'laden';\n if (st === 'error') return 'fout';\n const json = this.adresRes.value();\n const parsed = json !== undefined ? parseBrpAddress(json) : null;\n return parsed && parsed.ok && parsed.value.gevonden ? 'gevonden' : 'geen';\n });\n\n /** The DUO lookup (diplomas + manual fallback), validated at the trust boundary. */\n protected lookupRd = computed<RemoteData<Error | undefined, DuoLookupDto>>(() => {\n const rd = fromResource(this.diplomasRes);\n if (rd.tag !== 'Success') return rd;\n const parsed = parseDuoLookup(rd.value);\n return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) };\n });\n\n /** Parsed lookup as a plain value (or null) — used outside the beroep step (the\n controle summary) where the <app-async> template variable isn't in scope. */\n private duoData = computed<DuoLookupDto | null>(() => {\n const rd = this.lookupRd();\n return rd.tag === 'Success' ? rd.value : null;\n });\n\n readonly jaNee = JA_NEE;\n\n protected err = (k: DraftField | 'correspondentie' | 'diploma') => this.invullen()?.errors[k] ?? '';\n protected vraagErr = (id: string) => this.invullen()?.errors.antwoorden?.[id] ?? '';\n protected set = (key: DraftField, value: string) => this.dispatch({ tag: 'SetField', key, value });\n protected setKanaal = (value: string) => this.dispatch({ tag: 'SetCorrespondentie', value: value as Correspondentie });\n\n /** True while the user is entering a diploma manually (not in the DUO list). */\n protected handmatigActief = computed(() => this.draft().diplomaHerkomst === 'handmatig');\n /** The radio selection: a diploma id, or the \"not listed\" sentinel in manual mode. */\n protected diplomaKeuze = computed(() => (this.handmatigActief() ? HANDMATIG : this.draft().diplomaId ?? ''));\n\n protected diplomaOptions = (data: DuoLookupDto) => [\n ...data.diplomas.map((d) => ({ value: d.id, label: `${d.naam} — ${d.instelling} (${d.jaar})` })),\n { value: HANDMATIG, label: 'Mijn diploma staat er niet bij' },\n ];\n\n protected beroepOptions = (data: DuoLookupDto) => data.handmatig.beroepen.map((b) => ({ value: b, label: b }));\n\n /** The policy questions that apply to the current choice (server-decided). */\n protected actieveVragen = (data: DuoLookupDto): PolicyQuestionDto[] => {\n if (this.handmatigActief()) return data.handmatig.policyQuestions;\n return data.diplomas.find((d) => d.id === this.draft().diplomaId)?.policyQuestions ?? [];\n };\n\n /** Answered policy questions for the controle summary (question text + answer). */\n protected samenvattingVragen = computed(() => {\n const data = this.duoData();\n const d = this.draft();\n if (!data) return [] as { vraag: string; antwoord: string }[];\n const alle = [...data.diplomas.flatMap((x) => x.policyQuestions), ...data.handmatig.policyQuestions];\n return (d.vraagIds ?? []).map((id) => ({ vraag: alle.find((q) => q.id === id)?.vraag ?? id, antwoord: d.antwoorden[id] ?? '' }));\n });\n\n protected onDiplomaKeuze(data: DuoLookupDto, id: string) {\n if (id === HANDMATIG) {\n this.dispatch({ tag: 'KiesHandmatig', vraagIds: data.handmatig.policyQuestions.map((q) => q.id) });\n return;\n }\n const d = data.diplomas.find((x) => x.id === id);\n if (d) this.dispatch({ tag: 'KiesDiploma', diplomaId: d.id, beroep: d.beroep, vraagIds: d.policyQuestions.map((q) => q.id) });\n }\n\n constructor() {\n // An explicit seed (stories) wins; otherwise resume from localStorage.\n const seeded = this.seed();\n queueMicrotask(() => this.dispatch({ tag: 'Seed', state: seeded !== initial ? seeded : (this.restore() ?? initial) }));\n // Persist only while filling in; clear once the flow is done.\n effect(() => {\n const s = this.state();\n if (s.tag === 'Invullen') localStorage.setItem(STORAGE_KEY, JSON.stringify(s));\n else localStorage.removeItem(STORAGE_KEY);\n });\n // Prefill the address from the BRP lookup as it arrives. Track only the resource\n // value; untrack the dispatch (it reads the state signal, which would otherwise\n // make this effect loop on its own write). Don't clobber edits/restored data.\n effect(() => {\n const json = this.adresRes.value();\n if (!json) return;\n const parsed = parseBrpAddress(json);\n untracked(() => {\n const s = this.state();\n if (s.tag !== 'Invullen' || s.draft.straat) return;\n // ponytail: slice 2 handles gevonden === false -> manual entry stays handmatig.\n if (parsed.ok && parsed.value.gevonden && parsed.value.adres) {\n const a = parsed.value.adres;\n this.dispatch({ tag: 'PrefillAdres', straat: a.straat, postcode: a.postcode, woonplaats: a.woonplaats });\n }\n });\n });\n // A11y: move focus to the step heading when the STEP changes (depends only on\n // cursor(), which is value-stable across keystrokes — so typing never steals\n // focus). Skip the first run so we don't grab focus on initial load.\n let firstStep = true;\n effect(() => {\n this.cursor();\n if (firstStep) { firstStep = false; return; }\n untracked(() => {\n if (this.state().tag !== 'Invullen') return;\n queueMicrotask(() => this.stepHeading()?.nativeElement.focus());\n });\n });\n }\n\n private restore(): RegistratieState | null {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) return null;\n try {\n return JSON.parse(raw) as RegistratieState;\n } catch {\n return null; // ponytail: corrupt entry -> start fresh, no migration.\n }\n }\n\n onPrimary() {\n const s = this.state();\n if (s.tag !== 'Invullen') return;\n this.dispatch(this.step() === 'controle' ? { tag: 'Submit' } : { tag: 'Next' });\n this.runIfIndienen();\n }\n\n onRetry() {\n this.dispatch({ tag: 'Retry' });\n this.runIfIndienen();\n }\n\n /** Reset the wizard to a fresh start. Reload the BRP lookup so the address\n re-prefills, keeping the form and the \"vooraf ingevuld\" note consistent. */\n restart() {\n this.dispatch({ tag: 'Seed', state: initial });\n this.adresRes.reload();\n }\n\n /** The effect: when we enter Indienen, call the backend, then dispatch the\n outcome (success carries the confirmation reference). */\n private async runIfIndienen() {\n const s = this.state();\n if (s.tag !== 'Indienen') return;\n const r = await submitRegistratie(s.data);\n if (r.ok) this.dispatch({ tag: 'SubmitConfirmed', referentie: r.value });\n else this.dispatch({ tag: 'SubmitFailed', error: r.error });\n }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"constructorObj": {
"name": "constructor",
"description": "",
"deprecated": false,
"deprecationMessage": "",
"args": [],
"line": 279
},
"extends": []
},
{
"name": "RegistrationDetailPage",
"id": "component-RegistrationDetailPage-5aa270b47adfa8bd0334225a51df3a1656468e109d14ca9967660455a734884f4fb358b0f2a6c97e19b4df859f4c5e9773d5dd9c728dbcd785a3d43a60472811",
"file": "src/app/registratie/ui/registration-detail.page.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-registration-detail-page",
"styleUrls": [],
"styles": [],
"template": "<app-page-shell heading=\"Mijn gegevens\" backLink=\"/dashboard\">\n <app-async [data]=\"store.profile()\">\n <ng-template appAsyncLoaded let-p>\n <app-registration-summary [reg]=\"$any(p).registration\" />\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"6\" />\n </ng-template>\n </app-async>\n\n <div style=\"margin-top:2rem\">\n @if (submitted()) {\n <app-alert type=\"ok\">Uw adreswijziging is ontvangen. U ontvangt binnen 5 werkdagen bericht.</app-alert>\n } @else {\n <app-change-request-form (submitted)=\"submitted.set(true)\" />\n }\n </div>\n</app-page-shell>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [
{
"name": "store",
"defaultValue": "inject(BigProfileStore)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 38,
"modifierKind": [
124
]
},
{
"name": "submitted",
"defaultValue": "signal(false)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 39
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "PageShellComponent",
"type": "component"
},
{
"name": "AlertComponent",
"type": "component"
},
{
"name": "SkeletonComponent",
"type": "component"
},
{
"name": "ASYNC"
},
{
"name": "RegistrationSummaryComponent",
"type": "component"
},
{
"name": "ChangeRequestFormComponent",
"type": "component"
}
],
"description": "",
"rawdescription": "\n",
"type": "component",
"sourceCode": "import { Component, inject, signal } from '@angular/core';\nimport { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { SkeletonComponent } from '@shared/ui/skeleton/skeleton.component';\nimport { ASYNC } from '@shared/ui/async/async.component';\nimport { RegistrationSummaryComponent } from '@registratie/ui/registration-summary/registration-summary.component';\nimport { ChangeRequestFormComponent } from '@registratie/ui/change-request-form/change-request-form.component';\nimport { BigProfileStore } from '@registratie/application/big-profile.store';\n\n@Component({\n selector: 'app-registration-detail-page',\n imports: [\n PageShellComponent, AlertComponent, SkeletonComponent, ...ASYNC,\n RegistrationSummaryComponent, ChangeRequestFormComponent,\n ],\n template: `\n <app-page-shell heading=\"Mijn gegevens\" backLink=\"/dashboard\">\n <app-async [data]=\"store.profile()\">\n <ng-template appAsyncLoaded let-p>\n <app-registration-summary [reg]=\"$any(p).registration\" />\n </ng-template>\n <ng-template appAsyncLoading>\n <app-skeleton height=\"2.5rem\" [count]=\"6\" />\n </ng-template>\n </app-async>\n\n <div style=\"margin-top:2rem\">\n @if (submitted()) {\n <app-alert type=\"ok\">Uw adreswijziging is ontvangen. U ontvangt binnen 5 werkdagen bericht.</app-alert>\n } @else {\n <app-change-request-form (submitted)=\"submitted.set(true)\" />\n }\n </div>\n </app-page-shell>\n `,\n})\nexport class RegistrationDetailPage {\n protected store = inject(BigProfileStore);\n submitted = signal(false);\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "RegistrationSummaryComponent",
"id": "component-RegistrationSummaryComponent-15a446478708503b850ebbf635068398874034b3824504dc3a05802f6743029578088280540988dacfb6c768b92a2ee0371368369f455edc6dda03cf5f179e17",
"file": "src/app/registratie/ui/registration-summary/registration-summary.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-registration-summary",
"styleUrls": [],
"styles": [],
"template": "<div class=\"rhc-card rhc-card--default\">\n <dl class=\"rhc-data-summary rhc-data-summary--row\">\n <app-data-row key=\"BIG-nummer\" [value]=\"reg().bigNummer\" />\n <app-data-row key=\"Naam\" [value]=\"reg().naam\" />\n <app-data-row key=\"Beroep\" [value]=\"reg().beroep\" />\n <app-data-row key=\"Status\">\n <app-status-badge [label]=\"label()\" [color]=\"color()\" />\n </app-data-row>\n <app-data-row key=\"Registratiedatum\" [value]=\"reg().registratiedatum | date:'longDate'\" />\n <!-- Each status variant renders only the row its own data supports. -->\n @switch (reg().status.tag) {\n @case ('Geregistreerd') {\n <app-data-row key=\"Uiterste herregistratie\" [value]=\"$any(reg().status).herregistratieDatum | date:'longDate'\" />\n }\n @case ('Geschorst') {\n <app-data-row key=\"Geschorst tot\" [value]=\"$any(reg().status).geschorstTot | date:'longDate'\" />\n <app-data-row key=\"Reden\" [value]=\"$any(reg().status).reden\" />\n }\n @case ('Doorgehaald') {\n <app-data-row key=\"Doorgehaald op\" [value]=\"$any(reg().status).doorgehaaldOp | date:'longDate'\" />\n <app-data-row key=\"Reden\" [value]=\"$any(reg().status).reden\" />\n }\n }\n </dl>\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "reg",
"deprecated": false,
"deprecationMessage": "",
"type": "Registration",
"indexKey": "",
"optional": false,
"description": "",
"line": 41,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "color",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 43,
"modifierKind": [
124
]
},
{
"name": "label",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 42,
"modifierKind": [
124
]
}
],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "DatePipe",
"type": "pipe"
},
{
"name": "DataRowComponent",
"type": "component"
},
{
"name": "StatusBadgeComponent",
"type": "component"
}
],
"description": "<p>Organism: registration summary card. Composes data-row molecules + status-badge atom.</p>\n",
"rawdescription": "\nOrganism: registration summary card. Composes data-row molecules + status-badge atom.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { Registration } from '@registratie/domain/registration';\nimport { statusColor, statusLabel } from '@registratie/domain/registration.policy';\nimport { DataRowComponent } from '@shared/ui/data-row/data-row.component';\nimport { StatusBadgeComponent } from '@shared/ui/status-badge/status-badge.component';\n\n/** Organism: registration summary card. Composes data-row molecules + status-badge atom. */\n@Component({\n selector: 'app-registration-summary',\n imports: [DatePipe, DataRowComponent, StatusBadgeComponent],\n template: `\n <div class=\"rhc-card rhc-card--default\">\n <dl class=\"rhc-data-summary rhc-data-summary--row\">\n <app-data-row key=\"BIG-nummer\" [value]=\"reg().bigNummer\" />\n <app-data-row key=\"Naam\" [value]=\"reg().naam\" />\n <app-data-row key=\"Beroep\" [value]=\"reg().beroep\" />\n <app-data-row key=\"Status\">\n <app-status-badge [label]=\"label()\" [color]=\"color()\" />\n </app-data-row>\n <app-data-row key=\"Registratiedatum\" [value]=\"reg().registratiedatum | date:'longDate'\" />\n <!-- Each status variant renders only the row its own data supports. -->\n @switch (reg().status.tag) {\n @case ('Geregistreerd') {\n <app-data-row key=\"Uiterste herregistratie\" [value]=\"$any(reg().status).herregistratieDatum | date:'longDate'\" />\n }\n @case ('Geschorst') {\n <app-data-row key=\"Geschorst tot\" [value]=\"$any(reg().status).geschorstTot | date:'longDate'\" />\n <app-data-row key=\"Reden\" [value]=\"$any(reg().status).reden\" />\n }\n @case ('Doorgehaald') {\n <app-data-row key=\"Doorgehaald op\" [value]=\"$any(reg().status).doorgehaaldOp | date:'longDate'\" />\n <app-data-row key=\"Reden\" [value]=\"$any(reg().status).reden\" />\n }\n }\n </dl>\n </div>\n `,\n})\nexport class RegistrationSummaryComponent {\n reg = input.required<Registration>();\n protected label = () => statusLabel(this.reg().status.tag);\n protected color = () => statusColor(this.reg().status.tag);\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "RegistrationTableComponent",
"id": "component-RegistrationTableComponent-d1fcc222260a33b20e1562a1bf325269cd07ebaed4838e1d74e883be3fbc1ef521d9250ba478316f93f8e133c0c5576cde367822159d6dff36d7b9da2c4d0a79",
"file": "src/app/registratie/ui/registration-table/registration-table.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-registration-table",
"styleUrls": [],
"styles": [],
"template": "<div class=\"utrecht-table-container utrecht-table-container--overflow-inline\">\n <table class=\"utrecht-table utrecht-table--html-table utrecht-table--alternate-row-color\">\n <thead>\n <tr>\n <th class=\"utrecht-table__header-cell\">Type</th>\n <th class=\"utrecht-table__header-cell\">Omschrijving</th>\n <th class=\"utrecht-table__header-cell\">Datum</th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.omschrijving) {\n <tr>\n <td class=\"utrecht-table__cell\">{{ row.type }}</td>\n <td class=\"utrecht-table__cell\">{{ row.omschrijving }}</td>\n <td class=\"utrecht-table__cell\">{{ row.datum | date:'mediumDate' }}</td>\n </tr>\n }\n </tbody>\n </table>\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "rows",
"deprecated": false,
"deprecationMessage": "",
"type": "Aantekening[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 33,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "DatePipe",
"type": "pipe"
}
],
"description": "<p>Organism: table of specialismen/aantekeningen.</p>\n",
"rawdescription": "\nOrganism: table of specialismen/aantekeningen.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { DatePipe } from '@angular/common';\nimport { Aantekening } from '@registratie/domain/registration';\n\n/** Organism: table of specialismen/aantekeningen. */\n@Component({\n selector: 'app-registration-table',\n imports: [DatePipe],\n template: `\n <div class=\"utrecht-table-container utrecht-table-container--overflow-inline\">\n <table class=\"utrecht-table utrecht-table--html-table utrecht-table--alternate-row-color\">\n <thead>\n <tr>\n <th class=\"utrecht-table__header-cell\">Type</th>\n <th class=\"utrecht-table__header-cell\">Omschrijving</th>\n <th class=\"utrecht-table__header-cell\">Datum</th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.omschrijving) {\n <tr>\n <td class=\"utrecht-table__cell\">{{ row.type }}</td>\n <td class=\"utrecht-table__cell\">{{ row.omschrijving }}</td>\n <td class=\"utrecht-table__cell\">{{ row.datum | date:'mediumDate' }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n `,\n})\nexport class RegistrationTableComponent {\n rows = input.required<Aantekening[]>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": []
},
{
"name": "ShellComponent",
"id": "component-ShellComponent-9395255b45520c41ee6d43e2baf02d475e80d4ba8451334ef421b7debb14942f207c8e905bdac73b80b2d2b6f1b26b8ec7b341e2504bf31f8c0c326c59dbadac",
"file": "src/app/shared/layout/shell/shell.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-shell",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .skip{position:absolute;left:var(--app-skip-link-offset)}\n .layout{min-block-size:100vh;align-items:stretch}\n .main{flex:1;inline-size:100%;box-sizing:border-box}\n .content{max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-3xl) var(--rhc-space-max-2xl);box-sizing:border-box}\n "
],
"template": "<a href=\"#main\" class=\"rhc-skip-link skip\">Naar de inhoud</a>\n<div class=\"utrecht-page-layout utrecht-page-layout--stretch layout\">\n <app-site-header />\n <main id=\"main\" class=\"utrecht-page-content main\">\n <div class=\"content\">\n <router-outlet />\n </div>\n </main>\n <app-site-footer />\n</div>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "RouterOutlet"
},
{
"name": "SiteHeaderComponent",
"type": "component"
},
{
"name": "SiteFooterComponent",
"type": "component"
}
],
"description": "<p>Template: persistent app chrome. Header + footer mount once; only the routed\ncontent inside <router-outlet> changes (and cross-fades — see styles.scss).</p>\n",
"rawdescription": "\nTemplate: persistent app chrome. Header + footer mount once; only the routed\ncontent inside <router-outlet> changes (and cross-fades — see styles.scss).",
"type": "component",
"sourceCode": "import { Component } from '@angular/core';\nimport { RouterOutlet } from '@angular/router';\nimport { SiteHeaderComponent } from '@shared/layout/site-header/site-header.component';\nimport { SiteFooterComponent } from '@shared/layout/site-footer/site-footer.component';\n\n/** Template: persistent app chrome. Header + footer mount once; only the routed\n content inside <router-outlet> changes (and cross-fades — see styles.scss). */\n@Component({\n selector: 'app-shell',\n imports: [RouterOutlet, SiteHeaderComponent, SiteFooterComponent],\n styles: [`\n :host{display:block}\n .skip{position:absolute;left:var(--app-skip-link-offset)}\n .layout{min-block-size:100vh;align-items:stretch}\n .main{flex:1;inline-size:100%;box-sizing:border-box}\n .content{max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-3xl) var(--rhc-space-max-2xl);box-sizing:border-box}\n `],\n template: `\n <a href=\"#main\" class=\"rhc-skip-link skip\">Naar de inhoud</a>\n <div class=\"utrecht-page-layout utrecht-page-layout--stretch layout\">\n <app-site-header />\n <main id=\"main\" class=\"utrecht-page-content main\">\n <div class=\"content\">\n <router-outlet />\n </div>\n </main>\n <app-site-footer />\n </div>\n `,\n})\nexport class ShellComponent {}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .skip{position:absolute;left:var(--app-skip-link-offset)}\n .layout{min-block-size:100vh;align-items:stretch}\n .main{flex:1;inline-size:100%;box-sizing:border-box}\n .content{max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-3xl) var(--rhc-space-max-2xl);box-sizing:border-box}\n \n",
"extends": []
},
{
"name": "SiteFooterComponent",
"id": "component-SiteFooterComponent-57d90cda3179b0a39d4bc90cc725913dfa228828585709666584e469de964abc65dbf2b93f2155fc42fc071f61db4649924a05b2fc8e3af31a53fa8143744162",
"file": "src/app/shared/layout/site-footer/site-footer.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-site-footer",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .bar{background:var(--rhc-color-donkerblauw-700);color:var(--rhc-color-wit);margin-block-start:var(--rhc-space-max-5xl);inline-size:100%}\n .inner{max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-2xl);display:flex;gap:var(--rhc-space-max-3xl);flex-wrap:wrap;box-sizing:border-box}\n .end{margin-inline-start:auto}\n "
],
"template": "<footer class=\"utrecht-page-footer bar\">\n <div class=\"inner\">\n <span>BIG-register</span>\n <span>CIBG — Ministerie van Volksgezondheid, Welzijn en Sport</span>\n <span class=\"end\">Demo / POC — geen echte gegevens</span>\n </div>\n</footer>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Organism: site footer.</p>\n",
"rawdescription": "\nOrganism: site footer.",
"type": "component",
"sourceCode": "import { Component } from '@angular/core';\n\n/** Organism: site footer. */\n@Component({\n selector: 'app-site-footer',\n styles: [`\n :host{display:block}\n .bar{background:var(--rhc-color-donkerblauw-700);color:var(--rhc-color-wit);margin-block-start:var(--rhc-space-max-5xl);inline-size:100%}\n .inner{max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-2xl);display:flex;gap:var(--rhc-space-max-3xl);flex-wrap:wrap;box-sizing:border-box}\n .end{margin-inline-start:auto}\n `],\n template: `\n <footer class=\"utrecht-page-footer bar\">\n <div class=\"inner\">\n <span>BIG-register</span>\n <span>CIBG — Ministerie van Volksgezondheid, Welzijn en Sport</span>\n <span class=\"end\">Demo / POC — geen echte gegevens</span>\n </div>\n </footer>\n `,\n})\nexport class SiteFooterComponent {}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .bar{background:var(--rhc-color-donkerblauw-700);color:var(--rhc-color-wit);margin-block-start:var(--rhc-space-max-5xl);inline-size:100%}\n .inner{max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-2xl);display:flex;gap:var(--rhc-space-max-3xl);flex-wrap:wrap;box-sizing:border-box}\n .end{margin-inline-start:auto}\n \n",
"extends": []
},
{
"name": "SiteHeaderComponent",
"id": "component-SiteHeaderComponent-f5e80a4225213863b1448920f4a6300aec28b9ca4de6a6a0acad2e69a6aa0359cdd8194aa71fd6f8fcff7e0200b6715201f09241ac3bc32314c5af3392a4639e",
"file": "src/app/shared/layout/site-header/site-header.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-site-header",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .bar{background:var(--rhc-color-lintblauw-700);color:var(--rhc-color-wit);inline-size:100%}\n .inner{display:flex;align-items:center;gap:var(--rhc-space-max-xl);max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-xl) var(--rhc-space-max-2xl);box-sizing:border-box}\n .brand{display:flex;align-items:center;gap:var(--rhc-space-max-lg);color:inherit;text-decoration:none}\n .mark{display:inline-block;inline-size:var(--rhc-space-max-md);block-size:var(--rhc-space-max-4xl);background:var(--rhc-color-wit)}\n .name{font-weight:var(--rhc-text-font-weight-bold);line-height:var(--rhc-text-line-height-sm)}\n .sub{font-weight:var(--rhc-text-font-weight-regular);font-size:var(--rhc-text-font-size-sm)}\n .portal{margin-inline-start:auto;font-weight:var(--rhc-text-font-weight-semi-bold)}\n "
],
"template": "<header class=\"utrecht-page-header bar\">\n <div class=\"inner\">\n <a routerLink=\"/dashboard\" class=\"brand\">\n <span aria-hidden=\"true\" class=\"mark\"></span>\n <span class=\"name\">\n Rijksoverheid<br><span class=\"sub\">BIG-register</span>\n </span>\n </a>\n <span class=\"portal\">{{ subtitle() }}</span>\n </div>\n</header>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "subtitle",
"defaultValue": "'Mijn omgeving'",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 36,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [
{
"name": "RouterLink"
}
],
"description": "<p>Organism: site header with Rijksoverheid-style wordmark + title.\nponytail: text wordmark instead of the licensed Rijksoverheid logo.</p>\n",
"rawdescription": "\nOrganism: site header with Rijksoverheid-style wordmark + title.\nponytail: text wordmark instead of the licensed Rijksoverheid logo.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\nimport { RouterLink } from '@angular/router';\n\n/** Organism: site header with Rijksoverheid-style wordmark + title.\n ponytail: text wordmark instead of the licensed Rijksoverheid logo. */\n@Component({\n selector: 'app-site-header',\n imports: [RouterLink],\n // :host display:block so the full-bleed bar fills the flex column (custom\n // elements default to display:inline, which collapsed the bar to its content).\n styles: [`\n :host{display:block}\n .bar{background:var(--rhc-color-lintblauw-700);color:var(--rhc-color-wit);inline-size:100%}\n .inner{display:flex;align-items:center;gap:var(--rhc-space-max-xl);max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-xl) var(--rhc-space-max-2xl);box-sizing:border-box}\n .brand{display:flex;align-items:center;gap:var(--rhc-space-max-lg);color:inherit;text-decoration:none}\n .mark{display:inline-block;inline-size:var(--rhc-space-max-md);block-size:var(--rhc-space-max-4xl);background:var(--rhc-color-wit)}\n .name{font-weight:var(--rhc-text-font-weight-bold);line-height:var(--rhc-text-line-height-sm)}\n .sub{font-weight:var(--rhc-text-font-weight-regular);font-size:var(--rhc-text-font-size-sm)}\n .portal{margin-inline-start:auto;font-weight:var(--rhc-text-font-weight-semi-bold)}\n `],\n template: `\n <header class=\"utrecht-page-header bar\">\n <div class=\"inner\">\n <a routerLink=\"/dashboard\" class=\"brand\">\n <span aria-hidden=\"true\" class=\"mark\"></span>\n <span class=\"name\">\n Rijksoverheid<br><span class=\"sub\">BIG-register</span>\n </span>\n </a>\n <span class=\"portal\">{{ subtitle() }}</span>\n </div>\n </header>\n `,\n})\nexport class SiteHeaderComponent {\n subtitle = input('Mijn omgeving');\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .bar{background:var(--rhc-color-lintblauw-700);color:var(--rhc-color-wit);inline-size:100%}\n .inner{display:flex;align-items:center;gap:var(--rhc-space-max-xl);max-inline-size:var(--app-content-max);margin-inline:auto;padding:var(--rhc-space-max-xl) var(--rhc-space-max-2xl);box-sizing:border-box}\n .brand{display:flex;align-items:center;gap:var(--rhc-space-max-lg);color:inherit;text-decoration:none}\n .mark{display:inline-block;inline-size:var(--rhc-space-max-md);block-size:var(--rhc-space-max-4xl);background:var(--rhc-color-wit)}\n .name{font-weight:var(--rhc-text-font-weight-bold);line-height:var(--rhc-text-line-height-sm)}\n .sub{font-weight:var(--rhc-text-font-weight-regular);font-size:var(--rhc-text-font-size-sm)}\n .portal{margin-inline-start:auto;font-weight:var(--rhc-text-font-weight-semi-bold)}\n \n",
"extends": []
},
{
"name": "SkeletonComponent",
"id": "component-SkeletonComponent-d2768b972782fa90a72f8142960ff79ff9f04fc345e50775c982bcb5f646c5b14d64c013ee0cd03616032c9789e8d6cb3829cee2d296fb2c573d693d0d419fc7",
"file": "src/app/shared/ui/skeleton/skeleton.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-skeleton",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .sk{background:linear-gradient(90deg,var(--rhc-color-cool-grey-200) 25%,var(--rhc-color-cool-grey-100) 37%,var(--rhc-color-cool-grey-200) 63%);\n background-size:400% 100%;animation:sh 1.4s ease infinite;border-radius:var(--rhc-border-radius-md);\n margin-block-end:var(--rhc-space-max-lg)}\n @keyframes sh{0%{background-position:100% 0}100%{background-position:0 0}}\n "
],
"template": "@if (visible()) {\n @for (l of lines(); track $index) {\n <div class=\"sk\" aria-hidden=\"true\" [style.width]=\"width()\" [style.height]=\"height()\"></div>\n }\n}\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "count",
"defaultValue": "1",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 25,
"required": false
},
{
"name": "delay",
"defaultValue": "150",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 26,
"required": false
},
{
"name": "height",
"defaultValue": "'1rem'",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 24,
"required": false
},
{
"name": "width",
"defaultValue": "'100%'",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 23,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "lines",
"defaultValue": "computed(() => Array(this.count()).fill(0))",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 28,
"modifierKind": [
124
]
},
{
"name": "timer",
"deprecated": false,
"deprecationMessage": "",
"type": "ReturnType<unknown>",
"indexKey": "",
"optional": true,
"description": "",
"line": 29,
"modifierKind": [
123
]
},
{
"name": "visible",
"defaultValue": "signal(false)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 27,
"modifierKind": [
124
]
}
],
"methodsClass": [
{
"name": "ngOnDestroy",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 32,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "ngOnInit",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 31,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: skeleton placeholder (grey shimmer). Delay-gated so it never flashes\non fast responses. Render <code>count</code> lines shaped roughly like the content.</p>\n",
"rawdescription": "\nAtom: skeleton placeholder (grey shimmer). Delay-gated so it never flashes\non fast responses. Render `count` lines shaped roughly like the content.",
"type": "component",
"sourceCode": "import { Component, OnDestroy, OnInit, computed, input, signal } from '@angular/core';\n\n/** Atom: skeleton placeholder (grey shimmer). Delay-gated so it never flashes\n on fast responses. Render `count` lines shaped roughly like the content. */\n@Component({\n selector: 'app-skeleton',\n styles: [`\n :host{display:block}\n .sk{background:linear-gradient(90deg,var(--rhc-color-cool-grey-200) 25%,var(--rhc-color-cool-grey-100) 37%,var(--rhc-color-cool-grey-200) 63%);\n background-size:400% 100%;animation:sh 1.4s ease infinite;border-radius:var(--rhc-border-radius-md);\n margin-block-end:var(--rhc-space-max-lg)}\n @keyframes sh{0%{background-position:100% 0}100%{background-position:0 0}}\n `],\n template: `\n @if (visible()) {\n @for (l of lines(); track $index) {\n <div class=\"sk\" aria-hidden=\"true\" [style.width]=\"width()\" [style.height]=\"height()\"></div>\n }\n }\n `,\n})\nexport class SkeletonComponent implements OnInit, OnDestroy {\n width = input('100%');\n height = input('1rem');\n count = input(1);\n delay = input(150);\n protected visible = signal(false);\n protected lines = computed(() => Array(this.count()).fill(0));\n private timer?: ReturnType<typeof setTimeout>;\n\n ngOnInit() { this.timer = setTimeout(() => this.visible.set(true), this.delay()); }\n ngOnDestroy() { clearTimeout(this.timer); }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .sk{background:linear-gradient(90deg,var(--rhc-color-cool-grey-200) 25%,var(--rhc-color-cool-grey-100) 37%,var(--rhc-color-cool-grey-200) 63%);\n background-size:400% 100%;animation:sh 1.4s ease infinite;border-radius:var(--rhc-border-radius-md);\n margin-block-end:var(--rhc-space-max-lg)}\n @keyframes sh{0%{background-position:100% 0}100%{background-position:0 0}}\n \n",
"extends": [],
"implements": [
"OnInit",
"OnDestroy"
]
},
{
"name": "SpinnerComponent",
"id": "component-SpinnerComponent-a2815cf269d64067cc661956599274ab670cf13aa1a86ca3945a1ab725dc3b48a1e481d171799205b25682e3ed47cada574109891f4c56ea3bd92991d0f883e7",
"file": "src/app/shared/ui/spinner/spinner.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-spinner",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .sp{inline-size:var(--rhc-space-max-3xl);block-size:var(--rhc-space-max-3xl);border-radius:var(--rhc-border-radius-round);\n border:var(--rhc-space-max-xs) solid var(--rhc-color-cool-grey-300);\n border-block-start-color:var(--rhc-color-lintblauw-700);\n animation:sp 0.8s linear infinite;margin:var(--rhc-space-max-2xl) auto}\n @keyframes sp{to{transform:rotate(360deg)}}\n "
],
"template": "@if (visible()) {\n <div class=\"sp\" role=\"status\" aria-label=\"Bezig met laden\"></div>\n}\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "delay",
"defaultValue": "250",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 22,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "timer",
"deprecated": false,
"deprecationMessage": "",
"type": "ReturnType<unknown>",
"indexKey": "",
"optional": true,
"description": "",
"line": 24,
"modifierKind": [
123
]
},
{
"name": "visible",
"defaultValue": "signal(false)",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 23,
"modifierKind": [
124
]
}
],
"methodsClass": [
{
"name": "ngOnDestroy",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 27,
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "ngOnInit",
"args": [],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 26,
"deprecated": false,
"deprecationMessage": ""
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: spinner that only appears after <code>delay</code> ms — fast responses never\nflash a spinner, slow ones get feedback.</p>\n",
"rawdescription": "\nAtom: spinner that only appears after `delay` ms — fast responses never\nflash a spinner, slow ones get feedback.",
"type": "component",
"sourceCode": "import { Component, OnDestroy, OnInit, input, signal } from '@angular/core';\n\n/** Atom: spinner that only appears after `delay` ms — fast responses never\n flash a spinner, slow ones get feedback. */\n@Component({\n selector: 'app-spinner',\n styles: [`\n :host{display:block}\n .sp{inline-size:var(--rhc-space-max-3xl);block-size:var(--rhc-space-max-3xl);border-radius:var(--rhc-border-radius-round);\n border:var(--rhc-space-max-xs) solid var(--rhc-color-cool-grey-300);\n border-block-start-color:var(--rhc-color-lintblauw-700);\n animation:sp 0.8s linear infinite;margin:var(--rhc-space-max-2xl) auto}\n @keyframes sp{to{transform:rotate(360deg)}}\n `],\n template: `\n @if (visible()) {\n <div class=\"sp\" role=\"status\" aria-label=\"Bezig met laden\"></div>\n }\n `,\n})\nexport class SpinnerComponent implements OnInit, OnDestroy {\n delay = input(250);\n protected visible = signal(false);\n private timer?: ReturnType<typeof setTimeout>;\n\n ngOnInit() { this.timer = setTimeout(() => this.visible.set(true), this.delay()); }\n ngOnDestroy() { clearTimeout(this.timer); }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .sp{inline-size:var(--rhc-space-max-3xl);block-size:var(--rhc-space-max-3xl);border-radius:var(--rhc-border-radius-round);\n border:var(--rhc-space-max-xs) solid var(--rhc-color-cool-grey-300);\n border-block-start-color:var(--rhc-color-lintblauw-700);\n animation:sp 0.8s linear infinite;margin:var(--rhc-space-max-2xl) auto}\n @keyframes sp{to{transform:rotate(360deg)}}\n \n",
"extends": [],
"implements": [
"OnInit",
"OnDestroy"
]
},
{
"name": "StatusBadgeComponent",
"id": "component-StatusBadgeComponent-d676e2f81945ad5a6ae182822c7739effac5631c595651ab0ece77aa868063dc41d50d27773dc7448d1c5267030644b434bbd609600127ba78b4237e371066c6",
"file": "src/app/shared/ui/status-badge/status-badge.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-status-badge",
"styleUrls": [],
"styles": [
".badge{display:inline-flex;align-items:center;gap:var(--rhc-space-max-md)}"
],
"template": "<span class=\"badge\">\n <span class=\"rhc-dot-badge\" [style.background-color]=\"color()\" aria-hidden=\"true\"></span>\n <span>{{ label() }}</span>\n</span>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "color",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 18,
"required": true
},
{
"name": "label",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 17,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: a coloured dot + label. Purely presentational and domain-free — the\ncaller decides what colour and label mean (e.g. via registration.policy).\nThis keeps the shared UI kernel free of any domain knowledge.</p>\n",
"rawdescription": "\nAtom: a coloured dot + label. Purely presentational and domain-free — the\ncaller decides what colour and label mean (e.g. via registration.policy).\nThis keeps the shared UI kernel free of any domain knowledge.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\n/** Atom: a coloured dot + label. Purely presentational and domain-free — the\n caller decides what colour and label mean (e.g. via registration.policy).\n This keeps the shared UI kernel free of any domain knowledge. */\n@Component({\n selector: 'app-status-badge',\n styles: [`.badge{display:inline-flex;align-items:center;gap:var(--rhc-space-max-md)}`],\n template: `\n <span class=\"badge\">\n <span class=\"rhc-dot-badge\" [style.background-color]=\"color()\" aria-hidden=\"true\"></span>\n <span>{{ label() }}</span>\n </span>\n `,\n})\nexport class StatusBadgeComponent {\n label = input.required<string>();\n color = input.required<string>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": ".badge{display:inline-flex;align-items:center;gap:var(--rhc-space-max-md)}\n",
"extends": []
},
{
"name": "StepperComponent",
"id": "component-StepperComponent-ca92d637825cdea2afd87a8afb4b5446cb07d6f3429ebfa9ebdbc9f84569dfff27fb0a8274cae1d5a87440ee7821930479154f40dd4d29cbcb441e10341ca9c8",
"file": "src/app/shared/ui/stepper/stepper.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [],
"selector": "app-stepper",
"styleUrls": [],
"styles": [
"\n :host{display:block}\n .sr-only{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n .steps{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-xl);list-style:none;margin:0;padding:0}\n .step{display:flex;align-items:center;gap:var(--rhc-space-max-md);color:var(--rhc-color-foreground-subtle)}\n .num{\n display:grid;place-items:center;\n inline-size:var(--rhc-space-max-4xl);block-size:var(--rhc-space-max-4xl);\n border-radius:var(--rhc-border-radius-round);\n border:var(--rhc-space-max-xs) solid var(--rhc-color-cool-grey-400);\n font-weight:var(--rhc-text-font-weight-bold);\n }\n .step--done .num{background:var(--rhc-color-lintblauw-500);border-color:var(--rhc-color-lintblauw-500);color:var(--rhc-color-wit)}\n .step--current{color:var(--rhc-color-foreground-default)}\n .step--current .num{background:var(--rhc-color-lintblauw-700);border-color:var(--rhc-color-lintblauw-700);color:var(--rhc-color-wit)}\n .step--current .label{font-weight:var(--rhc-text-font-weight-semi-bold)}\n "
],
"template": "<nav aria-label=\"Voortgang\">\n <p class=\"sr-only\">Stap {{ current() + 1 }} van {{ steps().length }}: {{ steps()[current()] }}</p>\n <ol class=\"steps\">\n @for (label of steps(); track label; let i = $index) {\n <li\n class=\"step\"\n [class.step--current]=\"i === current()\"\n [class.step--done]=\"i < current()\"\n [attr.aria-current]=\"i === current() ? 'step' : null\">\n <span class=\"num\" aria-hidden=\"true\">{{ i + 1 }}</span>\n <span class=\"label\">{{ label }}</span>\n </li>\n }\n </ol>\n</nav>\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "current",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"indexKey": "",
"optional": false,
"description": "",
"line": 45,
"required": true
},
{
"name": "steps",
"deprecated": false,
"deprecationMessage": "",
"type": "string[]",
"indexKey": "",
"optional": false,
"description": "",
"line": 44,
"required": true
}
],
"outputsClass": [],
"propertiesClass": [],
"methodsClass": [],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Molecule: a horizontal step indicator for a multi-step flow. Visual progress\nplus accessible semantics — an ordered list with <code>aria-current=&quot;step&quot;</code> on the\nactive step and a visually-hidden &quot;Stap X van Y&quot; summary. Domain-free.</p>\n",
"rawdescription": "\nMolecule: a horizontal step indicator for a multi-step flow. Visual progress\nplus accessible semantics — an ordered list with `aria-current=\"step\"` on the\nactive step and a visually-hidden \"Stap X van Y\" summary. Domain-free.",
"type": "component",
"sourceCode": "import { Component, input } from '@angular/core';\n\n/** Molecule: a horizontal step indicator for a multi-step flow. Visual progress\n plus accessible semantics — an ordered list with `aria-current=\"step\"` on the\n active step and a visually-hidden \"Stap X van Y\" summary. Domain-free. */\n@Component({\n selector: 'app-stepper',\n styles: [`\n :host{display:block}\n .sr-only{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n .steps{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-xl);list-style:none;margin:0;padding:0}\n .step{display:flex;align-items:center;gap:var(--rhc-space-max-md);color:var(--rhc-color-foreground-subtle)}\n .num{\n display:grid;place-items:center;\n inline-size:var(--rhc-space-max-4xl);block-size:var(--rhc-space-max-4xl);\n border-radius:var(--rhc-border-radius-round);\n border:var(--rhc-space-max-xs) solid var(--rhc-color-cool-grey-400);\n font-weight:var(--rhc-text-font-weight-bold);\n }\n .step--done .num{background:var(--rhc-color-lintblauw-500);border-color:var(--rhc-color-lintblauw-500);color:var(--rhc-color-wit)}\n .step--current{color:var(--rhc-color-foreground-default)}\n .step--current .num{background:var(--rhc-color-lintblauw-700);border-color:var(--rhc-color-lintblauw-700);color:var(--rhc-color-wit)}\n .step--current .label{font-weight:var(--rhc-text-font-weight-semi-bold)}\n `],\n template: `\n <nav aria-label=\"Voortgang\">\n <p class=\"sr-only\">Stap {{ current() + 1 }} van {{ steps().length }}: {{ steps()[current()] }}</p>\n <ol class=\"steps\">\n @for (label of steps(); track label; let i = $index) {\n <li\n class=\"step\"\n [class.step--current]=\"i === current()\"\n [class.step--done]=\"i < current()\"\n [attr.aria-current]=\"i === current() ? 'step' : null\">\n <span class=\"num\" aria-hidden=\"true\">{{ i + 1 }}</span>\n <span class=\"label\">{{ label }}</span>\n </li>\n }\n </ol>\n </nav>\n `,\n})\nexport class StepperComponent {\n steps = input.required<string[]>();\n current = input.required<number>();\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "\n :host{display:block}\n .sr-only{position:absolute;inline-size:1px;block-size:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n .steps{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-xl);list-style:none;margin:0;padding:0}\n .step{display:flex;align-items:center;gap:var(--rhc-space-max-md);color:var(--rhc-color-foreground-subtle)}\n .num{\n display:grid;place-items:center;\n inline-size:var(--rhc-space-max-4xl);block-size:var(--rhc-space-max-4xl);\n border-radius:var(--rhc-border-radius-round);\n border:var(--rhc-space-max-xs) solid var(--rhc-color-cool-grey-400);\n font-weight:var(--rhc-text-font-weight-bold);\n }\n .step--done .num{background:var(--rhc-color-lintblauw-500);border-color:var(--rhc-color-lintblauw-500);color:var(--rhc-color-wit)}\n .step--current{color:var(--rhc-color-foreground-default)}\n .step--current .num{background:var(--rhc-color-lintblauw-700);border-color:var(--rhc-color-lintblauw-700);color:var(--rhc-color-wit)}\n .step--current .label{font-weight:var(--rhc-text-font-weight-semi-bold)}\n \n",
"extends": []
},
{
"name": "TextInputComponent",
"id": "component-TextInputComponent-712f6ffbc84c3ae2868d0a7dbb2c885712e01d4e340d36f57db0ed7d49d33dbcbab13fb8d0655c3e52f4557af2ed88b899e48167c10c5d9a7cec6bc3cd55d5ae",
"file": "src/app/shared/ui/text-input/text-input.component.ts",
"encapsulation": [],
"entryComponents": [],
"inputs": [],
"outputs": [],
"providers": [
{
"name": ")"
}
],
"selector": "app-text-input",
"styleUrls": [],
"styles": [],
"template": "<input\n class=\"utrecht-textbox rhc-text-input\"\n [class.utrecht-textbox--invalid]=\"invalid()\"\n [type]=\"type()\"\n [id]=\"inputId()\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() && inputId() ? inputId() + '-error' : null\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\" />\n",
"templateUrl": [],
"viewProviders": [],
"hostDirectives": [],
"inputsClass": [
{
"name": "inputId",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 27,
"required": false
},
{
"name": "invalid",
"defaultValue": "false",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 26,
"required": false
},
{
"name": "placeholder",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"indexKey": "",
"optional": false,
"description": "",
"line": 25,
"required": false
},
{
"name": "type",
"defaultValue": "'text'",
"deprecated": false,
"deprecationMessage": "",
"type": "\"text\" | \"password\" | \"email\"",
"indexKey": "",
"optional": false,
"description": "",
"line": 24,
"required": false
}
],
"outputsClass": [],
"propertiesClass": [
{
"name": "disabled",
"defaultValue": "false",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"indexKey": "",
"optional": false,
"description": "",
"line": 30
},
{
"name": "onChange",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "function",
"indexKey": "",
"optional": false,
"description": "",
"line": 31
},
{
"name": "onTouched",
"defaultValue": "() => {...}",
"deprecated": false,
"deprecationMessage": "",
"type": "function",
"indexKey": "",
"optional": false,
"description": "",
"line": 32
},
{
"name": "value",
"defaultValue": "''",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"indexKey": "",
"optional": false,
"description": "",
"line": 29
}
],
"methodsClass": [
{
"name": "onInput",
"args": [
{
"name": "e",
"type": "Event",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 34,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "e",
"type": "Event",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "registerOnChange",
"args": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
]
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 39,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"tagName": {
"text": "param"
}
}
]
},
{
"name": "registerOnTouched",
"args": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": []
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 40,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "fn",
"type": "function",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"function": [],
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setDisabledState",
"args": [
{
"name": "d",
"type": "boolean",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 41,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "d",
"type": "boolean",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "writeValue",
"args": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": ""
}
],
"optional": false,
"returnType": "void",
"typeParameters": [],
"line": 38,
"deprecated": false,
"deprecationMessage": "",
"jsdoctags": [
{
"name": "v",
"type": "string",
"optional": false,
"dotDotDotToken": false,
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"deprecated": false,
"deprecationMessage": "",
"hostBindings": [],
"hostListeners": [],
"standalone": false,
"imports": [],
"description": "<p>Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive).</p>\n",
"rawdescription": "\nAtom: text input. Utrecht textbox wired up as a form control (ngModel/reactive).",
"type": "component",
"sourceCode": "import { Component, forwardRef, input } from '@angular/core';\nimport { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';\n\n/** Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive). */\n@Component({\n selector: 'app-text-input',\n template: `\n <input\n class=\"utrecht-textbox rhc-text-input\"\n [class.utrecht-textbox--invalid]=\"invalid()\"\n [type]=\"type()\"\n [id]=\"inputId()\"\n [attr.aria-invalid]=\"invalid() ? 'true' : null\"\n [attr.aria-describedby]=\"invalid() && inputId() ? inputId() + '-error' : null\"\n [placeholder]=\"placeholder()\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n (input)=\"onInput($event)\"\n (blur)=\"onTouched()\" />\n `,\n providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TextInputComponent), multi: true }],\n})\nexport class TextInputComponent implements ControlValueAccessor {\n type = input<'text' | 'password' | 'email'>('text');\n placeholder = input('');\n invalid = input(false);\n inputId = input<string>();\n\n value = '';\n disabled = false;\n onChange: (v: string) => void = () => {};\n onTouched: () => void = () => {};\n\n onInput(e: Event) {\n this.value = (e.target as HTMLInputElement).value;\n this.onChange(this.value);\n }\n writeValue(v: string) { this.value = v ?? ''; }\n registerOnChange(fn: (v: string) => void) { this.onChange = fn; }\n registerOnTouched(fn: () => void) { this.onTouched = fn; }\n setDisabledState(d: boolean) { this.disabled = d; }\n}\n",
"assetsDirs": [],
"styleUrlsData": "",
"stylesData": "",
"extends": [],
"implements": [
"ControlValueAccessor"
]
}
],
"modules": [],
"miscellaneous": {
"variables": [
{
"name": "appConfig",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/app.config.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "ApplicationConfig",
"defaultValue": "{\n providers: [\n provideBrowserGlobalErrorListeners(),\n provideRouter(routes, withViewTransitions()),\n provideHttpClient(withInterceptors([scenarioInterceptor])),\n { provide: LOCALE_ID, useValue: 'nl' },\n ]\n}"
},
{
"name": "ASYNC",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/ui/async/async.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"defaultValue": "[\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const",
"rawdescription": "Convenience: import this array to get the wrapper + all slot directives.",
"description": "<p>Convenience: import this array to get the wrapper + all slot directives.</p>\n"
},
{
"name": "authGuard",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/auth/auth.guard.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "CanActivateFn",
"defaultValue": "() => {\n const store = inject(SessionStore);\n const router = inject(Router);\n return store.isAuthenticated() ? true : router.createUrlTree(['/login']);\n}",
"rawdescription": "Route guard: only let authenticated users in; otherwise redirect to /login.",
"description": "<p>Route guard: only let authenticated users in; otherwise redirect to /login.</p>\n"
},
{
"name": "EMPTY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "DuoLookupDto",
"defaultValue": "{ diplomas: [], handmatig: { beroepen: [], policyQuestions: [] } }"
},
{
"name": "emptyDraft",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "Draft",
"defaultValue": "{ antwoorden: {} }"
},
{
"name": "err",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"defaultValue": "<E>(error: E): Result<E, never> => ({ ok: false, error })"
},
{
"name": "HANDMATIG",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'__handmatig__'"
},
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "WizardState",
"defaultValue": "{ tag: 'Editing', step: 1, draft: { uren: '', jaren: '', punten: '' }, errors: {} }"
},
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "IntakeState",
"defaultValue": "{ tag: 'Answering', answers: {}, cursor: 0, errors: {}, scholingThreshold: SCHOLING_THRESHOLD_DEFAULT }"
},
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "RegistratieState",
"defaultValue": "{ tag: 'Invullen', draft: emptyDraft, cursor: 0, errors: {} }"
},
{
"name": "JA_NEE",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"defaultValue": "[{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]"
},
{
"name": "JA_NEE",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"defaultValue": "[{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]"
},
{
"name": "KANALEN",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"defaultValue": "[{ value: 'email', label: 'E-mail' }, { value: 'post', label: 'Post' }]"
},
{
"name": "ok",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"defaultValue": "<T>(value: T): Result<never, T> => ({ ok: true, value })"
},
{
"name": "routes",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/app.routes.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "Routes",
"defaultValue": "[\n {\n path: '',\n component: ShellComponent, // persistent header/footer; only children swap\n children: [\n { path: '', pathMatch: 'full', redirectTo: 'login' },\n { path: 'login', loadComponent: () => \"import('@auth/ui/login.page').then(m => m.LoginPage)\" },\n { path: 'dashboard', canActivate: [authGuard], loadComponent: () => \"import('@registratie/ui/dashboard.page').then(m => m.DashboardPage)\" },\n { path: 'registratie', canActivate: [authGuard], loadComponent: () => \"import('@registratie/ui/registration-detail.page').then(m => m.RegistrationDetailPage)\" },\n { path: 'registreren', canActivate: [authGuard], loadComponent: () => \"import('@registratie/ui/registratie.page').then(m => m.RegistratiePage)\" },\n { path: 'herregistratie', canActivate: [authGuard], loadComponent: () => \"import('@herregistratie/ui/herregistratie.page').then(m => m.HerregistratiePage)\" },\n { path: 'intake', canActivate: [authGuard], loadComponent: () => \"import('@herregistratie/ui/intake.page').then(m => m.IntakePage)\" },\n { path: 'concepts', loadComponent: () => \"import('./showcase/concepts.page').then(m => m.ConceptsPage)\" },\n { path: '**', redirectTo: 'login' },\n ],\n },\n]"
},
{
"name": "scenarioInterceptor",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/infrastructure/scenario.interceptor.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "HttpInterceptorFn",
"defaultValue": "(req, next) => {\n if (!req.url.includes('mock/')) return next(req);\n\n switch (currentScenario()) {\n case 'slow':\n return next(req).pipe(delay(2500));\n case 'loading':\n return next(req).pipe(delay(600_000)); // effectively never resolves\n case 'empty':\n return of(new HttpResponse({ status: 200, body: [] })).pipe(delay(400));\n case 'error':\n return timer(400).pipe(\n switchMap(() => throwError(() =>\n new HttpErrorResponse({ status: 500, statusText: 'Demo-fout', url: req.url }))),\n );\n default:\n return next(req);\n }\n}",
"rawdescription": "Demo-only: rewrites the timing/outcome of mock data requests based on\n?scenario= so loading / empty / error states can be shown on demand.\nReal requests are untouched.",
"description": "<p>Demo-only: rewrites the timing/outcome of mock data requests based on\n?scenario= so loading / empty / error states can be shown on demand.\nReal requests are untouched.</p>\n"
},
{
"name": "SCHOLING_THRESHOLD_DEFAULT",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"defaultValue": "1000",
"rawdescription": "Demo fallback only — the real threshold is a SERVER-OWNED policy value fetched\nat runtime (see IntakePolicyDto / SetPolicy). ponytail: default is the offline\nfallback; the server value wins.",
"description": "<p>Demo fallback only — the real threshold is a SERVER-OWNED policy value fetched\nat runtime (see IntakePolicyDto / SetPolicy). ponytail: default is the offline\nfallback; the server value wins.</p>\n"
},
{
"name": "STEPS",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "StepId[]",
"defaultValue": "['buitenland', 'werk', 'review']",
"rawdescription": "The fixed step list. Number of steps never changes; questions reveal inline.",
"description": "<p>The fixed step list. Number of steps never changes; questions reveal inline.</p>\n"
},
{
"name": "STEPS",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "StepId[]",
"defaultValue": "['adres', 'beroep', 'controle']",
"rawdescription": "The fixed step list. Number of steps never changes; questions reveal inline.",
"description": "<p>The fixed step list. Number of steps never changes; questions reveal inline.</p>\n"
},
{
"name": "STORAGE_KEY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/auth/application/session.store.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'session-v1'"
},
{
"name": "STORAGE_KEY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'intake-v3'"
},
{
"name": "STORAGE_KEY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'registratie-v1'"
},
{
"name": "VALID",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/infrastructure/scenario.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "Scenario[]",
"defaultValue": "['default', 'slow', 'loading', 'empty', 'error']"
}
],
"functions": [
{
"name": "andThen",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Chain a second source that depends on the first one&#39;s value.</p>\n",
"args": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, B>",
"jsdoctags": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "assertNever",
"file": "src/app/shared/kernel/fp.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Tiny native-TS functional toolkit. No dependency — this is the whole &quot;library&quot;.\nReused by every &quot;impossible states&quot; concept in the POC.</p>\n",
"args": [
{
"name": "x",
"type": "never",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "never",
"jsdoctags": [
{
"name": "x",
"type": "never",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "back",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "back",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "back",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "createStore",
"file": "src/app/shared/application/store.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "init",
"type": "Model",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "update",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Store<Model, Msg>",
"jsdoctags": [
{
"name": "init",
"type": "Model",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "update",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "currentScenario",
"file": "src/app/shared/infrastructure/scenario.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Reads ?scenario= from the URL so a demo can force each async state.</p>\n",
"args": [],
"returnType": "Scenario"
},
{
"name": "currentStep",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Which step the cursor currently points at (clamped to the fixed list).</p>\n",
"args": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "StepId",
"jsdoctags": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "currentStep",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Which step the cursor currently points at (clamped to the fixed list).</p>\n",
"args": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "StepId",
"jsdoctags": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "declareerBeroep",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Declare the beroep for a manually-entered diploma (chosen from a fixed list).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "fakeResource",
"file": "src/app/showcase/concepts.page.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Minimal fake Resource so <app-async> can be driven through every state without HTTP.</p>\n",
"args": [
{
"name": "status",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "T",
"deprecated": false,
"deprecationMessage": "",
"optional": true
},
{
"name": "error",
"type": "Error",
"deprecated": false,
"deprecationMessage": "",
"optional": true
}
],
"returnType": "Resource<T>",
"jsdoctags": [
{
"name": "status",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "T",
"deprecated": false,
"deprecationMessage": "",
"optional": true,
"tagName": {
"text": "param"
}
},
{
"name": "error",
"type": "Error",
"deprecated": false,
"deprecationMessage": "",
"optional": true,
"tagName": {
"text": "param"
}
}
]
},
{
"name": "foldRemote",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Exhaustive fold: you must handle every case, checked at compile time.</p>\n",
"args": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "h",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "R",
"jsdoctags": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "h",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "fromResource",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Project Angular&#39;s loosely-typed Resource into a RemoteData value.</p>\n",
"args": [
{
"name": "r",
"type": "Resource",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "isEmpty",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "() => false"
}
],
"returnType": "RemoteData<Error | undefined, T>",
"jsdoctags": [
{
"name": "r",
"type": "Resource",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "isEmpty",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "() => false",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "gaNaarStap",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Jump back to an earlier step to correct data (controle → step N). Forward\njumps are not allowed (would skip validation). Preserves the draft.</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "cursor",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "cursor",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "herregistratieDeadline",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The herregistratie deadline, if the status has one (only the active state does).</p>\n",
"args": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Date | null",
"jsdoctags": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "isAuthenticated",
"file": "src/app/auth/domain/session.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Session",
"jsdoctags": [
{
"name": "s",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "isHerregistratieEligible",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A registration may apply for herregistratie only while active and within the\nwindow before its deadline. A struck-off or suspended registration may not.\nSERVER-OWNED RULE: this now runs on the backend (BFF), which ships the result\nas <code>decisions.eligibleForHerregistratie</code> in the dashboard view. Kept here as\nthe reference implementation + unit test; the frontend no longer calls it.</p>\n",
"args": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "today",
"type": "Date",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "windowMonths",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "12"
}
],
"returnType": "boolean",
"jsdoctags": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "today",
"type": "Date",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "windowMonths",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "12",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "isStatusConsistent",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Invariant check used in tests/demos: a non-active status must not carry a\nherregistratie date. The union already enforces this structurally; this is\nthe runtime statement of the same rule.</p>\n",
"args": [
{
"name": "status",
"type": "RegistrationStatus",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "boolean",
"jsdoctags": [
{
"name": "status",
"type": "RegistrationStatus",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "kiesDiploma",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Pick a DUO diploma; the beroep is derived from it and the applicable policy\nquestions (<code>vraagIds</code>) come with it (both server-computed, passed in).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "diplomaId",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "diplomaId",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "kiesHandmatig",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Switch to manual diploma entry: the diploma isn&#39;t in DUO, so the MAXIMAL\npolicy-question set applies and the entry is flagged handmatig/unverified. The\nberoep is declared separately (declareerBeroep).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "lageUren",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>True when NL-hours are low enough that the scholing question must be answered.\nThe threshold is passed in (server-owned), not hardcoded.</p>\n",
"args": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "unknown",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "SCHOLING_THRESHOLD_DEFAULT"
}
],
"returnType": "boolean",
"jsdoctags": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "unknown",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "SCHOLING_THRESHOLD_DEFAULT",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "map",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Transform the value inside a Success; pass other states through unchanged.</p>\n",
"args": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, B>",
"jsdoctags": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "map2",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Combine two sources into one. Use this to merge e.g. a BIG-register call\nand a BRP call into a single state the page can render.</p>\n",
"args": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, R>",
"jsdoctags": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "map3",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Combine three sources (built on map2).</p>\n",
"args": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "c",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, R>",
"jsdoctags": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "c",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "next",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Step 1 → 2: advance only when BOTH step-1 fields parse. Illegal elsewhere = no-op.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "next",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "next",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseBigNummer",
"file": "src/app/registratie/domain/value-objects/big-nummer.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, BigNummer>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseBrpAddress",
"file": "src/app/registratie/infrastructure/brp.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Trust-boundary parse: validate the untrusted response shape. &quot;Geen adres&quot; is a\nvalid outcome (gevonden: false), not a malformed response. ponytail: hand-written;\nreach for a schema lib once the contract count grows.</p>\n",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, BrpAddressDto>",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseDashboardView",
"file": "src/app/registratie/infrastructure/dashboard-view.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Trust-boundary parse: validate the untrusted response shape and map the DTO\nonto our own domain model. Hand-written on purpose — no Zod for a single\ncontract. ponytail: reach for a schema lib once the contract count grows.</p>\n",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, DashboardView>",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseDuoLookup",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Trust-boundary parse: validate the untrusted response shape (diplomas, each\nwith derived beroep + policy questions, plus the manual-entry fallback).</p>\n",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, DuoLookupDto>",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseEmail",
"file": "src/app/registratie/domain/value-objects/email.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, Email>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parsePostcode",
"file": "src/app/registratie/domain/value-objects/postcode.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, Postcode>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseQuestions",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "[] | null",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseUren",
"file": "src/app/registratie/domain/value-objects/uren.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, Uren>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "prefillAdres",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Prefill the address from a BRP lookup and flag its origin (PRD §7).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "straat",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "postcode",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "woonplaats",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "straat",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "postcode",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "woonplaats",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "WizardMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "WizardMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "IntakeMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "IntakeMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "RegistratieMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "RegistratieMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "resolve",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Resolve the async submit. Only meaningful while Submitting.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "resolve",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "resolve",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "restore",
"file": "src/app/auth/application/session.store.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Restore a persisted session (best-effort; corrupt entry → logged out).</p>\n",
"args": [],
"returnType": "Session | null"
},
{
"name": "setAnswer",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setAntwoord",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "vraagId",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "vraagId",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setCorrespondentie",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "Correspondentie",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "Correspondentie",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setField",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Update one draft field while editing; ignored in any other state.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setField",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "key",
"type": "DraftField",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "key",
"type": "DraftField",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setPolicy",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Apply a server-owned policy value (e.g. the scholing threshold).</p>\n",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "statusColor",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Brand colour token for a status. assertNever forces a colour for every new\nstatus variant at compile time.</p>\n",
"args": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "string",
"jsdoctags": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "statusLabel",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Domain logic for a registration — pure functions, NO Angular. This is where\n&quot;what the business rules say&quot; lives, separate from &quot;how it looks&quot; (UI) and\n&quot;where the data comes from&quot; (infrastructure). Keeping it framework-free means\nit is trivial to read and unit-test.</p>\n",
"args": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "string",
"jsdoctags": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submit",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Step 2 submit: parse everything; move to Submitting only with Valid data.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submit",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submit",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submitHerregistratie",
"file": "src/app/herregistratie/application/submit-herregistratie.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The mutation/command: send a herregistratie application to the backend.\nReturns a Result so the caller can branch on success/failure without\ntry/catch. ponytail: faked with a timer; swap for a real POST when there&#39;s\nan API. The &quot;uren must be &gt; 0&quot; rule lets the demo show a failure path.</p>\n",
"args": [
{
"name": "data",
"type": "Valid",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Promise<Result<string, void>>",
"jsdoctags": [
{
"name": "data",
"type": "Valid",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submitIntake",
"file": "src/app/herregistratie/application/submit-intake.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Command: send the intake questionnaire to the backend. Returns a Result so the\ncaller branches on success/failure without try/catch. ponytail: faked with a\ntimer; swap for a real POST when there&#39;s an API. The &quot;uren must be &gt; 0&quot; rule\nlets the demo show the failure path.</p>\n",
"args": [
{
"name": "data",
"type": "ValidIntake",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Promise<Result<string, void>>",
"jsdoctags": [
{
"name": "data",
"type": "ValidIntake",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submitRegistratie",
"file": "src/app/registratie/application/submit-registratie.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Command: send the completed registration to the backend, which invokes the\ndomain command to create/finalize the registration aggregate. Returns a Result\nso the caller branches on success/failure without try/catch; on success it\nyields the confirmation reference (PRD §9). ponytail: faked with a timer; swap\nfor a real POST when there&#39;s an API. The &quot;manually entered diploma&quot; rule lets\nthe demo show the failure path.</p>\n",
"args": [
{
"name": "data",
"type": "ValidRegistratie",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Promise<Result<string, string>>",
"jsdoctags": [
{
"name": "data",
"type": "ValidRegistratie",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validate",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Parse every field; on success hand back a Valid, else the per-field errors.</p>\n",
"args": [
{
"name": "draft",
"type": "Draft",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Partial<Record<Draft, string>>, Valid>",
"jsdoctags": [
{
"name": "draft",
"type": "Draft",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateAll",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Parse the whole questionnaire into a ValidIntake (called on submit).</p>\n",
"args": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, ValidIntake>",
"jsdoctags": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateAll",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Parse the whole wizard into a ValidRegistratie (called on submit).</p>\n",
"args": [
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, ValidRegistratie>",
"jsdoctags": [
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateStep",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Validate every question currently visible in ONE step. Errors keyed per field.</p>\n",
"args": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, void>",
"jsdoctags": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateStep",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Validate every question currently visible in ONE step. Errors keyed per field.</p>\n",
"args": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, void>",
"jsdoctags": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"typealiases": [
{
"name": "AantekeningType",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"Specialisme\" | \"Aantekening\"",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A note is either a recognised specialism or a plain annotation — a closed set,\nnot an open string, so a typo can&#39;t slip through.</p>\n",
"kind": 193
},
{
"name": "AdresHerkomst",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"brp\" | \"handmatig\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Where a piece of data came from — recorded on the aggregate (PRD §5).</p>\n",
"kind": 193
},
{
"name": "AlertType",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"info\" | \"ok\" | \"warning\" | \"error\"",
"file": "src/app/shared/ui/alert/alert.component.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "BigNummer",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<string | BigNummer>",
"file": "src/app/registratie/domain/value-objects/big-nummer.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: a BIG registration number — 11 digits.</p>\n",
"kind": 184
},
{
"name": "Brand",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "unknown",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Nominal typing: Brand&lt;string, &#39;Postcode&#39;&gt; is assignable from a plain string\nonly through an explicit cast — so a smart constructor is the only minter.</p>\n",
"kind": 194
},
{
"name": "Correspondentie",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"email\" | \"post\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "DiplomaHerkomst",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"duo\" | \"handmatig\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "DraftField",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"straat\" | \"postcode\" | \"woonplaats\" | \"email\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Text fields settable via SetField.</p>\n",
"kind": 193
},
{
"name": "Email",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<string | Email>",
"file": "src/app/registratie/domain/value-objects/email.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: an e-mail address. &quot;Parse, don&#39;t validate&quot; — an Email is a\ndistinct type from a raw string, mintable only via parseEmail, so holding one\nis proof it is well-formed. Format-only check (the FE keeps format validation\nfor instant feedback; the backend stays the authority — see ADR-0001).</p>\n",
"kind": 184
},
{
"name": "Err",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Error | undefined",
"file": "src/app/registratie/application/big-profile.store.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "Errors",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Partial<Record<Answers, string>>",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Per-field error map: one message per question, since a step holds several.</p>\n",
"kind": 184
},
{
"name": "IntakeMsg",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "IntakeState",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "JaNee",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"ja\" | \"nee\"",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A FIXED 3-step wizard with progressive disclosure. The steps never change in\nnumber (always <code>STEPS</code>); instead, follow-up questions appear <em>inline within a\nstep</em> depending on earlier answers — answer &quot;buiten Nederland gewerkt? → ja&quot;\nand the country/hours questions reveal in the same step; report few hours and\nthe scholing-question reveals inside the &#39;werk&#39; step. &quot;Is this field required\nright now&quot; is a pure function (<code>validateStep</code>/<code>lageUren</code>), so it&#39;s trivial to\ntest and impossible to get out of sync with the data.</p>\n",
"kind": 193
},
{
"name": "Postcode",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<string | Postcode>",
"file": "src/app/registratie/domain/value-objects/postcode.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: a Dutch postcode. &quot;Parse, don&#39;t validate&quot; — a Postcode is a\ndistinct type from a raw string, mintable only via parsePostcode, so holding\none is proof it is well-formed.</p>\n",
"kind": 184
},
{
"name": "RegistratieMsg",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "RegistratieState",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "RegistrationStatus",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Registration status as a discriminated union: each variant owns exactly the\ndata that makes sense for it. Only an active (Geregistreerd) registration has\na herregistratie date; a struck-off (Doorgehaald) one cannot carry one. The\nold flat interface allowed that impossible combination — this makes it\nunrepresentable.</p>\n",
"kind": 193
},
{
"name": "RemoteData",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/shared/application/remote-data.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The four mutually-exclusive states of an async fetch, as a tagged union.\nCrucially the data lives ON the state: only <code>Failure</code> has an <code>error</code>, only\n<code>Success</code> has a <code>value</code>. &quot;Loaded but no value&quot; or &quot;error with stale value&quot;\nare unrepresentable — Richard Feldman&#39;s RemoteData.</p>\n",
"kind": 193
},
{
"name": "Result",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A computation that either succeeded with a value or failed with an error.\nPlain objects (no classes) to match the signal/httpResource ergonomics.</p>\n",
"kind": 193
},
{
"name": "Scenario",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"default\" | \"slow\" | \"loading\" | \"empty\" | \"error\"",
"file": "src/app/shared/infrastructure/scenario.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "StatusTag",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "RegistrationStatus",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Just the discriminant — for atoms that only need the label/color.</p>\n",
"kind": 200
},
{
"name": "StepId",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"buitenland\" | \"werk\" | \"review\"",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The three fixed steps. Each step groups one or more questions.</p>\n",
"kind": 193
},
{
"name": "StepId",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"adres\" | \"beroep\" | \"controle\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A FIXED 3-step registration wizard. The steps never change in number (always\n<code>STEPS</code>): (1) adres + correspondentievoorkeur, (2) beroep o.b.v. diploma,\n(3) controle &amp; indienen. Follow-up questions appear <em>inline within a step</em>\n(e.g. choosing &#39;email&#39; reveals the e-mail field). &quot;Is this field required\nright now&quot; is a pure function (<code>validateStep</code>), so it is trivial to test and\nimpossible to get out of sync with the data. Invariants live here, not in the\nUI: the wizard reaches <code>Indienen</code> only when a complete <code>ValidRegistratie</code> parses.</p>\n",
"kind": 193
},
{
"name": "Uren",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<number | Uren>",
"file": "src/app/registratie/domain/value-objects/uren.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: a non-negative whole number of hours.</p>\n",
"kind": 184
},
{
"name": "Variant",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"primary\" | \"secondary\" | \"subtle\" | \"danger\"",
"file": "src/app/shared/ui/button/button.component.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "WizardMsg",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Every event that can happen to the wizard, as one message type. The component\nsends a WizardMsg; <code>reduce</code> decides the next state. This is the Elm\nModel+Msg+update pattern: ONE pure function describes all state changes.</p>\n",
"kind": 193
},
{
"name": "WizardState",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The whole wizard as one tagged union. <code>step</code> and <code>errors</code> exist ONLY while\nEditing; Submitting/Submitted/Failed carry a <code>Valid</code> payload and nothing else.\nSo &quot;submitting while a field is invalid&quot; or &quot;showing the success screen with\nerrors set&quot; are unrepresentable — the bug class is gone by construction.</p>\n",
"kind": 193
}
],
"enumerations": [],
"groupedVariables": {
"src/app/app.config.ts": [
{
"name": "appConfig",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/app.config.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "ApplicationConfig",
"defaultValue": "{\n providers: [\n provideBrowserGlobalErrorListeners(),\n provideRouter(routes, withViewTransitions()),\n provideHttpClient(withInterceptors([scenarioInterceptor])),\n { provide: LOCALE_ID, useValue: 'nl' },\n ]\n}"
}
],
"src/app/shared/ui/async/async.component.ts": [
{
"name": "ASYNC",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/ui/async/async.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"defaultValue": "[\n AsyncComponent, AsyncLoadedDirective, AsyncLoadingDirective,\n AsyncEmptyDirective, AsyncErrorDirective,\n] as const",
"rawdescription": "Convenience: import this array to get the wrapper + all slot directives.",
"description": "<p>Convenience: import this array to get the wrapper + all slot directives.</p>\n"
}
],
"src/app/auth/auth.guard.ts": [
{
"name": "authGuard",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/auth/auth.guard.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "CanActivateFn",
"defaultValue": "() => {\n const store = inject(SessionStore);\n const router = inject(Router);\n return store.isAuthenticated() ? true : router.createUrlTree(['/login']);\n}",
"rawdescription": "Route guard: only let authenticated users in; otherwise redirect to /login.",
"description": "<p>Route guard: only let authenticated users in; otherwise redirect to /login.</p>\n"
}
],
"src/app/registratie/infrastructure/duo.adapter.ts": [
{
"name": "EMPTY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "DuoLookupDto",
"defaultValue": "{ diplomas: [], handmatig: { beroepen: [], policyQuestions: [] } }"
}
],
"src/app/registratie/domain/registratie-wizard.machine.ts": [
{
"name": "emptyDraft",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "Draft",
"defaultValue": "{ antwoorden: {} }"
},
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "RegistratieState",
"defaultValue": "{ tag: 'Invullen', draft: emptyDraft, cursor: 0, errors: {} }"
},
{
"name": "STEPS",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "StepId[]",
"defaultValue": "['adres', 'beroep', 'controle']",
"rawdescription": "The fixed step list. Number of steps never changes; questions reveal inline.",
"description": "<p>The fixed step list. Number of steps never changes; questions reveal inline.</p>\n"
}
],
"src/app/shared/kernel/fp.ts": [
{
"name": "err",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"defaultValue": "<E>(error: E): Result<E, never> => ({ ok: false, error })"
},
{
"name": "ok",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "unknown",
"defaultValue": "<T>(value: T): Result<never, T> => ({ ok: true, value })"
}
],
"src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts": [
{
"name": "HANDMATIG",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'__handmatig__'"
},
{
"name": "JA_NEE",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"defaultValue": "[{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]"
},
{
"name": "KANALEN",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"defaultValue": "[{ value: 'email', label: 'E-mail' }, { value: 'post', label: 'Post' }]"
},
{
"name": "STORAGE_KEY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'registratie-v1'"
}
],
"src/app/herregistratie/domain/herregistratie.machine.ts": [
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "WizardState",
"defaultValue": "{ tag: 'Editing', step: 1, draft: { uren: '', jaren: '', punten: '' }, errors: {} }"
}
],
"src/app/herregistratie/domain/intake.machine.ts": [
{
"name": "initial",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "IntakeState",
"defaultValue": "{ tag: 'Answering', answers: {}, cursor: 0, errors: {}, scholingThreshold: SCHOLING_THRESHOLD_DEFAULT }"
},
{
"name": "SCHOLING_THRESHOLD_DEFAULT",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "number",
"defaultValue": "1000",
"rawdescription": "Demo fallback only — the real threshold is a SERVER-OWNED policy value fetched\nat runtime (see IntakePolicyDto / SetPolicy). ponytail: default is the offline\nfallback; the server value wins.",
"description": "<p>Demo fallback only — the real threshold is a SERVER-OWNED policy value fetched\nat runtime (see IntakePolicyDto / SetPolicy). ponytail: default is the offline\nfallback; the server value wins.</p>\n"
},
{
"name": "STEPS",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "StepId[]",
"defaultValue": "['buitenland', 'werk', 'review']",
"rawdescription": "The fixed step list. Number of steps never changes; questions reveal inline.",
"description": "<p>The fixed step list. Number of steps never changes; questions reveal inline.</p>\n"
}
],
"src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts": [
{
"name": "JA_NEE",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "[]",
"defaultValue": "[{ value: 'ja', label: 'Ja' }, { value: 'nee', label: 'Nee' }]"
},
{
"name": "STORAGE_KEY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'intake-v3'"
}
],
"src/app/app.routes.ts": [
{
"name": "routes",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/app.routes.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "Routes",
"defaultValue": "[\n {\n path: '',\n component: ShellComponent, // persistent header/footer; only children swap\n children: [\n { path: '', pathMatch: 'full', redirectTo: 'login' },\n { path: 'login', loadComponent: () => \"import('@auth/ui/login.page').then(m => m.LoginPage)\" },\n { path: 'dashboard', canActivate: [authGuard], loadComponent: () => \"import('@registratie/ui/dashboard.page').then(m => m.DashboardPage)\" },\n { path: 'registratie', canActivate: [authGuard], loadComponent: () => \"import('@registratie/ui/registration-detail.page').then(m => m.RegistrationDetailPage)\" },\n { path: 'registreren', canActivate: [authGuard], loadComponent: () => \"import('@registratie/ui/registratie.page').then(m => m.RegistratiePage)\" },\n { path: 'herregistratie', canActivate: [authGuard], loadComponent: () => \"import('@herregistratie/ui/herregistratie.page').then(m => m.HerregistratiePage)\" },\n { path: 'intake', canActivate: [authGuard], loadComponent: () => \"import('@herregistratie/ui/intake.page').then(m => m.IntakePage)\" },\n { path: 'concepts', loadComponent: () => \"import('./showcase/concepts.page').then(m => m.ConceptsPage)\" },\n { path: '**', redirectTo: 'login' },\n ],\n },\n]"
}
],
"src/app/shared/infrastructure/scenario.interceptor.ts": [
{
"name": "scenarioInterceptor",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/infrastructure/scenario.interceptor.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "HttpInterceptorFn",
"defaultValue": "(req, next) => {\n if (!req.url.includes('mock/')) return next(req);\n\n switch (currentScenario()) {\n case 'slow':\n return next(req).pipe(delay(2500));\n case 'loading':\n return next(req).pipe(delay(600_000)); // effectively never resolves\n case 'empty':\n return of(new HttpResponse({ status: 200, body: [] })).pipe(delay(400));\n case 'error':\n return timer(400).pipe(\n switchMap(() => throwError(() =>\n new HttpErrorResponse({ status: 500, statusText: 'Demo-fout', url: req.url }))),\n );\n default:\n return next(req);\n }\n}",
"rawdescription": "Demo-only: rewrites the timing/outcome of mock data requests based on\n?scenario= so loading / empty / error states can be shown on demand.\nReal requests are untouched.",
"description": "<p>Demo-only: rewrites the timing/outcome of mock data requests based on\n?scenario= so loading / empty / error states can be shown on demand.\nReal requests are untouched.</p>\n"
}
],
"src/app/auth/application/session.store.ts": [
{
"name": "STORAGE_KEY",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/auth/application/session.store.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "string",
"defaultValue": "'session-v1'"
}
],
"src/app/shared/infrastructure/scenario.ts": [
{
"name": "VALID",
"ctype": "miscellaneous",
"subtype": "variable",
"file": "src/app/shared/infrastructure/scenario.ts",
"deprecated": false,
"deprecationMessage": "",
"type": "Scenario[]",
"defaultValue": "['default', 'slow', 'loading', 'empty', 'error']"
}
]
},
"groupedFunctions": {
"src/app/shared/application/remote-data.ts": [
{
"name": "andThen",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Chain a second source that depends on the first one&#39;s value.</p>\n",
"args": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, B>",
"jsdoctags": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "foldRemote",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Exhaustive fold: you must handle every case, checked at compile time.</p>\n",
"args": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "h",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "R",
"jsdoctags": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "h",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "fromResource",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Project Angular&#39;s loosely-typed Resource into a RemoteData value.</p>\n",
"args": [
{
"name": "r",
"type": "Resource",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "isEmpty",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "() => false"
}
],
"returnType": "RemoteData<Error | undefined, T>",
"jsdoctags": [
{
"name": "r",
"type": "Resource",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "isEmpty",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "() => false",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "map",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Transform the value inside a Success; pass other states through unchanged.</p>\n",
"args": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, B>",
"jsdoctags": [
{
"name": "rd",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "map2",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Combine two sources into one. Use this to merge e.g. a BIG-register call\nand a BRP call into a single state the page can render.</p>\n",
"args": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, R>",
"jsdoctags": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "map3",
"file": "src/app/shared/application/remote-data.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Combine three sources (built on map2).</p>\n",
"args": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "c",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RemoteData<E, R>",
"jsdoctags": [
{
"name": "a",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "b",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "c",
"type": "RemoteData",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "f",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/shared/kernel/fp.ts": [
{
"name": "assertNever",
"file": "src/app/shared/kernel/fp.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Tiny native-TS functional toolkit. No dependency — this is the whole &quot;library&quot;.\nReused by every &quot;impossible states&quot; concept in the POC.</p>\n",
"args": [
{
"name": "x",
"type": "never",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "never",
"jsdoctags": [
{
"name": "x",
"type": "never",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/herregistratie/domain/herregistratie.machine.ts": [
{
"name": "back",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "next",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Step 1 → 2: advance only when BOTH step-1 fields parse. Illegal elsewhere = no-op.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "WizardMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "WizardMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "resolve",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Resolve the async submit. Only meaningful while Submitting.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setField",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Update one draft field while editing; ignored in any other state.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submit",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Step 2 submit: parse everything; move to Submitting only with Valid data.</p>\n",
"args": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "WizardState",
"jsdoctags": [
{
"name": "s",
"type": "WizardState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validate",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Parse every field; on success hand back a Valid, else the per-field errors.</p>\n",
"args": [
{
"name": "draft",
"type": "Draft",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Partial<Record<Draft, string>>, Valid>",
"jsdoctags": [
{
"name": "draft",
"type": "Draft",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/herregistratie/domain/intake.machine.ts": [
{
"name": "back",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "currentStep",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Which step the cursor currently points at (clamped to the fixed list).</p>\n",
"args": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "StepId",
"jsdoctags": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "lageUren",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>True when NL-hours are low enough that the scholing question must be answered.\nThe threshold is passed in (server-owned), not hardcoded.</p>\n",
"args": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "unknown",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "SCHOLING_THRESHOLD_DEFAULT"
}
],
"returnType": "boolean",
"jsdoctags": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "unknown",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "SCHOLING_THRESHOLD_DEFAULT",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "next",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "IntakeMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "IntakeMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "resolve",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setAnswer",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "key",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setPolicy",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Apply a server-owned policy value (e.g. the scholing threshold).</p>\n",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submit",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "IntakeState",
"jsdoctags": [
{
"name": "s",
"type": "IntakeState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateAll",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Parse the whole questionnaire into a ValidIntake (called on submit).</p>\n",
"args": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, ValidIntake>",
"jsdoctags": [
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateStep",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Validate every question currently visible in ONE step. Errors keyed per field.</p>\n",
"args": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, void>",
"jsdoctags": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "a",
"type": "Answers",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "scholingThreshold",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/domain/registratie-wizard.machine.ts": [
{
"name": "back",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "currentStep",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Which step the cursor currently points at (clamped to the fixed list).</p>\n",
"args": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "StepId",
"jsdoctags": [
{
"name": "s",
"type": "Extract",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "declareerBeroep",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Declare the beroep for a manually-entered diploma (chosen from a fixed list).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "gaNaarStap",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Jump back to an earlier step to correct data (controle → step N). Forward\njumps are not allowed (would skip validation). Preserves the draft.</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "cursor",
"type": "number",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "cursor",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "kiesDiploma",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Pick a DUO diploma; the beroep is derived from it and the applicable policy\nquestions (<code>vraagIds</code>) come with it (both server-computed, passed in).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "diplomaId",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "diplomaId",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "beroep",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "kiesHandmatig",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Switch to manual diploma entry: the diploma isn&#39;t in DUO, so the MAXIMAL\npolicy-question set applies and the entry is flagged handmatig/unverified. The\nberoep is declared separately (declareerBeroep).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "vraagIds",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "next",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "prefillAdres",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Prefill the address from a BRP lookup and flag its origin (PRD §7).</p>\n",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "straat",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "postcode",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "woonplaats",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "straat",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "postcode",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "woonplaats",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "reduce",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "m",
"type": "RegistratieMsg",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "m",
"type": "RegistratieMsg",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "resolve",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "r",
"type": "Result",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setAntwoord",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "vraagId",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "vraagId",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setCorrespondentie",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "Correspondentie",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "Correspondentie",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "setField",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "key",
"type": "DraftField",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "key",
"type": "DraftField",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "submit",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "RegistratieState",
"jsdoctags": [
{
"name": "s",
"type": "RegistratieState",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateAll",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Parse the whole wizard into a ValidRegistratie (called on submit).</p>\n",
"args": [
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, ValidRegistratie>",
"jsdoctags": [
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "validateStep",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Validate every question currently visible in ONE step. Errors keyed per field.</p>\n",
"args": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<Errors, void>",
"jsdoctags": [
{
"name": "step",
"type": "StepId",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "d",
"type": "Draft",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/shared/application/store.ts": [
{
"name": "createStore",
"file": "src/app/shared/application/store.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "init",
"type": "Model",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "update",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Store<Model, Msg>",
"jsdoctags": [
{
"name": "init",
"type": "Model",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "update",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/shared/infrastructure/scenario.ts": [
{
"name": "currentScenario",
"file": "src/app/shared/infrastructure/scenario.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Reads ?scenario= from the URL so a demo can force each async state.</p>\n",
"args": [],
"returnType": "Scenario"
}
],
"src/app/showcase/concepts.page.ts": [
{
"name": "fakeResource",
"file": "src/app/showcase/concepts.page.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Minimal fake Resource so <app-async> can be driven through every state without HTTP.</p>\n",
"args": [
{
"name": "status",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "value",
"type": "T",
"deprecated": false,
"deprecationMessage": "",
"optional": true
},
{
"name": "error",
"type": "Error",
"deprecated": false,
"deprecationMessage": "",
"optional": true
}
],
"returnType": "Resource<T>",
"jsdoctags": [
{
"name": "status",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "value",
"type": "T",
"deprecated": false,
"deprecationMessage": "",
"optional": true,
"tagName": {
"text": "param"
}
},
{
"name": "error",
"type": "Error",
"deprecated": false,
"deprecationMessage": "",
"optional": true,
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/domain/registration.policy.ts": [
{
"name": "herregistratieDeadline",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The herregistratie deadline, if the status has one (only the active state does).</p>\n",
"args": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Date | null",
"jsdoctags": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "isHerregistratieEligible",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A registration may apply for herregistratie only while active and within the\nwindow before its deadline. A struck-off or suspended registration may not.\nSERVER-OWNED RULE: this now runs on the backend (BFF), which ships the result\nas <code>decisions.eligibleForHerregistratie</code> in the dashboard view. Kept here as\nthe reference implementation + unit test; the frontend no longer calls it.</p>\n",
"args": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "today",
"type": "Date",
"deprecated": false,
"deprecationMessage": ""
},
{
"name": "windowMonths",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "12"
}
],
"returnType": "boolean",
"jsdoctags": [
{
"name": "reg",
"type": "Registration",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "today",
"type": "Date",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
},
{
"name": "windowMonths",
"type": "number",
"deprecated": false,
"deprecationMessage": "",
"defaultValue": "12",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "isStatusConsistent",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Invariant check used in tests/demos: a non-active status must not carry a\nherregistratie date. The union already enforces this structurally; this is\nthe runtime statement of the same rule.</p>\n",
"args": [
{
"name": "status",
"type": "RegistrationStatus",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "boolean",
"jsdoctags": [
{
"name": "status",
"type": "RegistrationStatus",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "statusColor",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Brand colour token for a status. assertNever forces a colour for every new\nstatus variant at compile time.</p>\n",
"args": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "string",
"jsdoctags": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "statusLabel",
"file": "src/app/registratie/domain/registration.policy.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Domain logic for a registration — pure functions, NO Angular. This is where\n&quot;what the business rules say&quot; lives, separate from &quot;how it looks&quot; (UI) and\n&quot;where the data comes from&quot; (infrastructure). Keeping it framework-free means\nit is trivial to read and unit-test.</p>\n",
"args": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "string",
"jsdoctags": [
{
"name": "tag",
"type": "StatusTag",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/auth/domain/session.ts": [
{
"name": "isAuthenticated",
"file": "src/app/auth/domain/session.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "s",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Session",
"jsdoctags": [
{
"name": "s",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/domain/value-objects/big-nummer.ts": [
{
"name": "parseBigNummer",
"file": "src/app/registratie/domain/value-objects/big-nummer.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, BigNummer>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/infrastructure/brp.adapter.ts": [
{
"name": "parseBrpAddress",
"file": "src/app/registratie/infrastructure/brp.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Trust-boundary parse: validate the untrusted response shape. &quot;Geen adres&quot; is a\nvalid outcome (gevonden: false), not a malformed response. ponytail: hand-written;\nreach for a schema lib once the contract count grows.</p>\n",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, BrpAddressDto>",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/infrastructure/dashboard-view.adapter.ts": [
{
"name": "parseDashboardView",
"file": "src/app/registratie/infrastructure/dashboard-view.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Trust-boundary parse: validate the untrusted response shape and map the DTO\nonto our own domain model. Hand-written on purpose — no Zod for a single\ncontract. ponytail: reach for a schema lib once the contract count grows.</p>\n",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, DashboardView>",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/infrastructure/duo.adapter.ts": [
{
"name": "parseDuoLookup",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Trust-boundary parse: validate the untrusted response shape (diplomas, each\nwith derived beroep + policy questions, plus the manual-entry fallback).</p>\n",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, DuoLookupDto>",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
},
{
"name": "parseQuestions",
"file": "src/app/registratie/infrastructure/duo.adapter.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "[] | null",
"jsdoctags": [
{
"name": "json",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/domain/value-objects/email.ts": [
{
"name": "parseEmail",
"file": "src/app/registratie/domain/value-objects/email.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, Email>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/domain/value-objects/postcode.ts": [
{
"name": "parsePostcode",
"file": "src/app/registratie/domain/value-objects/postcode.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, Postcode>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/domain/value-objects/uren.ts": [
{
"name": "parseUren",
"file": "src/app/registratie/domain/value-objects/uren.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"args": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Result<string, Uren>",
"jsdoctags": [
{
"name": "raw",
"type": "string",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/auth/application/session.store.ts": [
{
"name": "restore",
"file": "src/app/auth/application/session.store.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Restore a persisted session (best-effort; corrupt entry → logged out).</p>\n",
"args": [],
"returnType": "Session | null"
}
],
"src/app/herregistratie/application/submit-herregistratie.ts": [
{
"name": "submitHerregistratie",
"file": "src/app/herregistratie/application/submit-herregistratie.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The mutation/command: send a herregistratie application to the backend.\nReturns a Result so the caller can branch on success/failure without\ntry/catch. ponytail: faked with a timer; swap for a real POST when there&#39;s\nan API. The &quot;uren must be &gt; 0&quot; rule lets the demo show a failure path.</p>\n",
"args": [
{
"name": "data",
"type": "Valid",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Promise<Result<string, void>>",
"jsdoctags": [
{
"name": "data",
"type": "Valid",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/herregistratie/application/submit-intake.ts": [
{
"name": "submitIntake",
"file": "src/app/herregistratie/application/submit-intake.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Command: send the intake questionnaire to the backend. Returns a Result so the\ncaller branches on success/failure without try/catch. ponytail: faked with a\ntimer; swap for a real POST when there&#39;s an API. The &quot;uren must be &gt; 0&quot; rule\nlets the demo show the failure path.</p>\n",
"args": [
{
"name": "data",
"type": "ValidIntake",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Promise<Result<string, void>>",
"jsdoctags": [
{
"name": "data",
"type": "ValidIntake",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
],
"src/app/registratie/application/submit-registratie.ts": [
{
"name": "submitRegistratie",
"file": "src/app/registratie/application/submit-registratie.ts",
"ctype": "miscellaneous",
"subtype": "function",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Command: send the completed registration to the backend, which invokes the\ndomain command to create/finalize the registration aggregate. Returns a Result\nso the caller branches on success/failure without try/catch; on success it\nyields the confirmation reference (PRD §9). ponytail: faked with a timer; swap\nfor a real POST when there&#39;s an API. The &quot;manually entered diploma&quot; rule lets\nthe demo show the failure path.</p>\n",
"args": [
{
"name": "data",
"type": "ValidRegistratie",
"deprecated": false,
"deprecationMessage": ""
}
],
"returnType": "Promise<Result<string, string>>",
"jsdoctags": [
{
"name": "data",
"type": "ValidRegistratie",
"deprecated": false,
"deprecationMessage": "",
"tagName": {
"text": "param"
}
}
]
}
]
},
"groupedEnumerations": {},
"groupedTypeAliases": {
"src/app/registratie/domain/registration.ts": [
{
"name": "AantekeningType",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"Specialisme\" | \"Aantekening\"",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A note is either a recognised specialism or a plain annotation — a closed set,\nnot an open string, so a typo can&#39;t slip through.</p>\n",
"kind": 193
},
{
"name": "RegistrationStatus",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Registration status as a discriminated union: each variant owns exactly the\ndata that makes sense for it. Only an active (Geregistreerd) registration has\na herregistratie date; a struck-off (Doorgehaald) one cannot carry one. The\nold flat interface allowed that impossible combination — this makes it\nunrepresentable.</p>\n",
"kind": 193
},
{
"name": "StatusTag",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "RegistrationStatus",
"file": "src/app/registratie/domain/registration.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Just the discriminant — for atoms that only need the label/color.</p>\n",
"kind": 200
}
],
"src/app/registratie/domain/registratie-wizard.machine.ts": [
{
"name": "AdresHerkomst",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"brp\" | \"handmatig\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Where a piece of data came from — recorded on the aggregate (PRD §5).</p>\n",
"kind": 193
},
{
"name": "Correspondentie",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"email\" | \"post\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "DiplomaHerkomst",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"duo\" | \"handmatig\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "DraftField",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"straat\" | \"postcode\" | \"woonplaats\" | \"email\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Text fields settable via SetField.</p>\n",
"kind": 193
},
{
"name": "RegistratieMsg",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "RegistratieState",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "StepId",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"adres\" | \"beroep\" | \"controle\"",
"file": "src/app/registratie/domain/registratie-wizard.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A FIXED 3-step registration wizard. The steps never change in number (always\n<code>STEPS</code>): (1) adres + correspondentievoorkeur, (2) beroep o.b.v. diploma,\n(3) controle &amp; indienen. Follow-up questions appear <em>inline within a step</em>\n(e.g. choosing &#39;email&#39; reveals the e-mail field). &quot;Is this field required\nright now&quot; is a pure function (<code>validateStep</code>), so it is trivial to test and\nimpossible to get out of sync with the data. Invariants live here, not in the\nUI: the wizard reaches <code>Indienen</code> only when a complete <code>ValidRegistratie</code> parses.</p>\n",
"kind": 193
}
],
"src/app/shared/ui/alert/alert.component.ts": [
{
"name": "AlertType",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"info\" | \"ok\" | \"warning\" | \"error\"",
"file": "src/app/shared/ui/alert/alert.component.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
}
],
"src/app/registratie/domain/value-objects/big-nummer.ts": [
{
"name": "BigNummer",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<string | BigNummer>",
"file": "src/app/registratie/domain/value-objects/big-nummer.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: a BIG registration number — 11 digits.</p>\n",
"kind": 184
}
],
"src/app/shared/kernel/fp.ts": [
{
"name": "Brand",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "unknown",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Nominal typing: Brand&lt;string, &#39;Postcode&#39;&gt; is assignable from a plain string\nonly through an explicit cast — so a smart constructor is the only minter.</p>\n",
"kind": 194
},
{
"name": "Result",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type",
"file": "src/app/shared/kernel/fp.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A computation that either succeeded with a value or failed with an error.\nPlain objects (no classes) to match the signal/httpResource ergonomics.</p>\n",
"kind": 193
}
],
"src/app/registratie/domain/value-objects/email.ts": [
{
"name": "Email",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<string | Email>",
"file": "src/app/registratie/domain/value-objects/email.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: an e-mail address. &quot;Parse, don&#39;t validate&quot; — an Email is a\ndistinct type from a raw string, mintable only via parseEmail, so holding one\nis proof it is well-formed. Format-only check (the FE keeps format validation\nfor instant feedback; the backend stays the authority — see ADR-0001).</p>\n",
"kind": 184
}
],
"src/app/registratie/application/big-profile.store.ts": [
{
"name": "Err",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Error | undefined",
"file": "src/app/registratie/application/big-profile.store.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
}
],
"src/app/herregistratie/domain/intake.machine.ts": [
{
"name": "Errors",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Partial<Record<Answers, string>>",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Per-field error map: one message per question, since a step holds several.</p>\n",
"kind": 184
},
{
"name": "IntakeMsg",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "IntakeState",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
},
{
"name": "JaNee",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"ja\" | \"nee\"",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>A FIXED 3-step wizard with progressive disclosure. The steps never change in\nnumber (always <code>STEPS</code>); instead, follow-up questions appear <em>inline within a\nstep</em> depending on earlier answers — answer &quot;buiten Nederland gewerkt? → ja&quot;\nand the country/hours questions reveal in the same step; report few hours and\nthe scholing-question reveals inside the &#39;werk&#39; step. &quot;Is this field required\nright now&quot; is a pure function (<code>validateStep</code>/<code>lageUren</code>), so it&#39;s trivial to\ntest and impossible to get out of sync with the data.</p>\n",
"kind": 193
},
{
"name": "StepId",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"buitenland\" | \"werk\" | \"review\"",
"file": "src/app/herregistratie/domain/intake.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The three fixed steps. Each step groups one or more questions.</p>\n",
"kind": 193
}
],
"src/app/registratie/domain/value-objects/postcode.ts": [
{
"name": "Postcode",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<string | Postcode>",
"file": "src/app/registratie/domain/value-objects/postcode.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: a Dutch postcode. &quot;Parse, don&#39;t validate&quot; — a Postcode is a\ndistinct type from a raw string, mintable only via parsePostcode, so holding\none is proof it is well-formed.</p>\n",
"kind": 184
}
],
"src/app/shared/application/remote-data.ts": [
{
"name": "RemoteData",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/shared/application/remote-data.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The four mutually-exclusive states of an async fetch, as a tagged union.\nCrucially the data lives ON the state: only <code>Failure</code> has an <code>error</code>, only\n<code>Success</code> has a <code>value</code>. &quot;Loaded but no value&quot; or &quot;error with stale value&quot;\nare unrepresentable — Richard Feldman&#39;s RemoteData.</p>\n",
"kind": 193
}
],
"src/app/shared/infrastructure/scenario.ts": [
{
"name": "Scenario",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"default\" | \"slow\" | \"loading\" | \"empty\" | \"error\"",
"file": "src/app/shared/infrastructure/scenario.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
}
],
"src/app/registratie/domain/value-objects/uren.ts": [
{
"name": "Uren",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "Brand<number | Uren>",
"file": "src/app/registratie/domain/value-objects/uren.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Value object: a non-negative whole number of hours.</p>\n",
"kind": 184
}
],
"src/app/shared/ui/button/button.component.ts": [
{
"name": "Variant",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "\"primary\" | \"secondary\" | \"subtle\" | \"danger\"",
"file": "src/app/shared/ui/button/button.component.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "",
"kind": 193
}
],
"src/app/herregistratie/domain/herregistratie.machine.ts": [
{
"name": "WizardMsg",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>Every event that can happen to the wizard, as one message type. The component\nsends a WizardMsg; <code>reduce</code> decides the next state. This is the Elm\nModel+Msg+update pattern: ONE pure function describes all state changes.</p>\n",
"kind": 193
},
{
"name": "WizardState",
"ctype": "miscellaneous",
"subtype": "typealias",
"rawtype": "literal type | literal type | literal type | literal type",
"file": "src/app/herregistratie/domain/herregistratie.machine.ts",
"deprecated": false,
"deprecationMessage": "",
"description": "<p>The whole wizard as one tagged union. <code>step</code> and <code>errors</code> exist ONLY while\nEditing; Submitting/Submitted/Failed carry a <code>Valid</code> payload and nothing else.\nSo &quot;submitting while a field is invalid&quot; or &quot;showing the success screen with\nerrors set&quot; are unrepresentable — the bug class is gone by construction.</p>\n",
"kind": 193
}
]
}
},
"routes": {
"name": "<root>",
"kind": "module",
"children": [
{
"name": "ShellComponent",
"kind": "component",
"filename": "src/app/app.routes.ts"
},
{
"name": "login",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "dashboard",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "registratie",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "registreren",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "herregistratie",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "intake",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "concepts",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "**",
"kind": "route-path",
"filename": "src/app/app.routes.ts"
},
{
"name": "login",
"kind": "route-redirect",
"filename": "src/app/app.routes.ts"
},
{
"name": "login",
"kind": "route-redirect",
"filename": "src/app/app.routes.ts"
}
]
},
"coverage": {
"count": 45,
"status": "medium",
"files": [
{
"filePath": "src/app/app.config.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "appConfig",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/app.routes.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "routes",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/app.ts",
"type": "component",
"linktype": "component",
"name": "App",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/auth/application/session.store.ts",
"type": "injectable",
"linktype": "injectable",
"name": "SessionStore",
"coveragePercent": 25,
"coverageCount": "2/8",
"status": "low"
},
{
"filePath": "src/app/auth/application/session.store.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "restore",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/auth/application/session.store.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "STORAGE_KEY",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/auth/auth.guard.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "authGuard",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/auth/domain/session.ts",
"type": "interface",
"linktype": "interface",
"name": "Session",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/auth/domain/session.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "isAuthenticated",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/auth/infrastructure/digid.adapter.ts",
"type": "injectable",
"linktype": "injectable",
"name": "DigidAdapter",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/auth/ui/login-form/login-form.component.ts",
"type": "component",
"linktype": "component",
"name": "LoginFormComponent",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/auth/ui/login.page.ts",
"type": "component",
"linktype": "component",
"name": "LoginPage",
"coveragePercent": 0,
"coverageCount": "0/5",
"status": "low"
},
{
"filePath": "src/app/herregistratie/application/submit-herregistratie.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "submitHerregistratie",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/application/submit-intake.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "submitIntake",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/contracts/intake-policy.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "IntakePolicyDto",
"coveragePercent": 100,
"coverageCount": "2/2",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "Draft",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "Valid",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "back",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "next",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "reduce",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "resolve",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "setField",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "submit",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "validate",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "initial",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "WizardMsg",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/herregistratie.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "WizardState",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "Answers",
"coveragePercent": 14,
"coverageCount": "1/7",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "ValidIntake",
"coveragePercent": 14,
"coverageCount": "1/7",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "back",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "currentStep",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "lageUren",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "next",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "reduce",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "resolve",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "setAnswer",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "setPolicy",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "submit",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "validateAll",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "validateStep",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "initial",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "SCHOLING_THRESHOLD_DEFAULT",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "STEPS",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Errors",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "IntakeMsg",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "IntakeState",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "JaNee",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/domain/intake.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "StepId",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts",
"type": "component",
"linktype": "component",
"name": "HerregistratieWizardComponent",
"coveragePercent": 22,
"coverageCount": "4/18",
"status": "low"
},
{
"filePath": "src/app/herregistratie/ui/herregistratie.page.ts",
"type": "component",
"linktype": "component",
"name": "HerregistratiePage",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"type": "component",
"linktype": "component",
"name": "IntakeWizardComponent",
"coveragePercent": 25,
"coverageCount": "6/24",
"status": "low"
},
{
"filePath": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "JA_NEE",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "STORAGE_KEY",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/herregistratie/ui/intake.page.ts",
"type": "component",
"linktype": "component",
"name": "IntakePage",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/application/big-profile.store.ts",
"type": "injectable",
"linktype": "injectable",
"name": "BigProfileStore",
"coveragePercent": 42,
"coverageCount": "6/14",
"status": "medium"
},
{
"filePath": "src/app/registratie/application/big-profile.store.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Err",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/application/submit-registratie.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "submitRegistratie",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/contracts/brp-address.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "BrpAddressDto",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/registratie/contracts/dashboard-view.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "DashboardView",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/registratie/contracts/dashboard-view.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "DashboardViewDto",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/registratie/contracts/dashboard-view.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "HerregistratieDecisions",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "DuoDiplomaDto",
"coveragePercent": 0,
"coverageCount": "0/7",
"status": "low"
},
{
"filePath": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "DuoLookupDto",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "ManualDiplomaPolicyDto",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/registratie/contracts/duo-diplomas.dto.ts",
"type": "interface",
"linktype": "interface",
"name": "PolicyQuestionDto",
"coveragePercent": 0,
"coverageCount": "0/4",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/big-profile.ts",
"type": "interface",
"linktype": "interface",
"name": "BigProfile",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/registratie/domain/person.ts",
"type": "interface",
"linktype": "interface",
"name": "Adres",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/person.ts",
"type": "interface",
"linktype": "interface",
"name": "Person",
"coveragePercent": 0,
"coverageCount": "0/4",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "Draft",
"coveragePercent": 8,
"coverageCount": "1/12",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "Errors",
"coveragePercent": 12,
"coverageCount": "1/8",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "interface",
"linktype": "interface",
"name": "ValidRegistratie",
"coveragePercent": 11,
"coverageCount": "1/9",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "back",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "currentStep",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "declareerBeroep",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "gaNaarStap",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "kiesDiploma",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "kiesHandmatig",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "next",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "prefillAdres",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "reduce",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "resolve",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "setAntwoord",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "setCorrespondentie",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "setField",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "submit",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "validateAll",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "validateStep",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "emptyDraft",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "initial",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "STEPS",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "AdresHerkomst",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Correspondentie",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "DiplomaHerkomst",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "DraftField",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "RegistratieMsg",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "RegistratieState",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registratie-wizard.machine.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "StepId",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.policy.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "herregistratieDeadline",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.policy.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "isHerregistratieEligible",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.policy.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "isStatusConsistent",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.policy.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "statusColor",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.policy.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "statusLabel",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.ts",
"type": "interface",
"linktype": "interface",
"name": "Aantekening",
"coveragePercent": 0,
"coverageCount": "0/4",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registration.ts",
"type": "interface",
"linktype": "interface",
"name": "Registration",
"coveragePercent": 0,
"coverageCount": "0/7",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/registration.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "AantekeningType",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "RegistrationStatus",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/registration.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "StatusTag",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/value-objects/big-nummer.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseBigNummer",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/value-objects/big-nummer.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "BigNummer",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/value-objects/email.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseEmail",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/value-objects/email.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Email",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/value-objects/postcode.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parsePostcode",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/value-objects/postcode.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Postcode",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/domain/value-objects/uren.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseUren",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/domain/value-objects/uren.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Uren",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/infrastructure/big-register.adapter.ts",
"type": "injectable",
"linktype": "injectable",
"name": "BigRegisterAdapter",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/registratie/infrastructure/brp.adapter.ts",
"type": "injectable",
"linktype": "injectable",
"name": "BrpAdapter",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/registratie/infrastructure/brp.adapter.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseBrpAddress",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/infrastructure/dashboard-view.adapter.ts",
"type": "injectable",
"linktype": "injectable",
"name": "DashboardViewAdapter",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/registratie/infrastructure/dashboard-view.adapter.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseDashboardView",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/infrastructure/duo.adapter.ts",
"type": "injectable",
"linktype": "injectable",
"name": "DuoAdapter",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/registratie/infrastructure/duo.adapter.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseDuoLookup",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/infrastructure/duo.adapter.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "parseQuestions",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/infrastructure/duo.adapter.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "EMPTY",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/change-request-form/change-request-form.component.ts",
"type": "component",
"linktype": "component",
"name": "ChangeRequestFormComponent",
"coveragePercent": 12,
"coverageCount": "1/8",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/change-request-form/change-request-form.component.ts",
"type": "interface",
"linktype": "interface",
"name": "ChangeRequest",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/dashboard.page.ts",
"type": "component",
"linktype": "component",
"name": "DashboardPage",
"coveragePercent": 0,
"coverageCount": "0/2",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"type": "component",
"linktype": "component",
"name": "RegistratieWizardComponent",
"coveragePercent": 26,
"coverageCount": "12/45",
"status": "medium"
},
{
"filePath": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "HANDMATIG",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "JA_NEE",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "KANALEN",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "STORAGE_KEY",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registratie.page.ts",
"type": "component",
"linktype": "component",
"name": "RegistratiePage",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/registratie/ui/registration-detail.page.ts",
"type": "component",
"linktype": "component",
"name": "RegistrationDetailPage",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registration-summary/registration-summary.component.ts",
"type": "component",
"linktype": "component",
"name": "RegistrationSummaryComponent",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/registratie/ui/registration-table/registration-table.component.ts",
"type": "component",
"linktype": "component",
"name": "RegistrationTableComponent",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "andThen",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "foldRemote",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "fromResource",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "map",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "map2",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "map3",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/remote-data.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "RemoteData",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/store.ts",
"type": "interface",
"linktype": "interface",
"name": "Store",
"coveragePercent": 100,
"coverageCount": "3/3",
"status": "very-good"
},
{
"filePath": "src/app/shared/application/store.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "createStore",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/infrastructure/scenario.interceptor.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "scenarioInterceptor",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/infrastructure/scenario.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "currentScenario",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/infrastructure/scenario.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "VALID",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/infrastructure/scenario.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Scenario",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/kernel/fp.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "assertNever",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/kernel/fp.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "err",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/kernel/fp.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "ok",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/kernel/fp.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Brand",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/kernel/fp.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Result",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/layout/breadcrumb/breadcrumb.component.ts",
"type": "component",
"linktype": "component",
"name": "BreadcrumbComponent",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/shared/layout/breadcrumb/breadcrumb.component.ts",
"type": "interface",
"linktype": "interface",
"name": "BreadcrumbItem",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/shared/layout/page-shell/page-shell.component.ts",
"type": "component",
"linktype": "component",
"name": "PageShellComponent",
"coveragePercent": 14,
"coverageCount": "1/7",
"status": "low"
},
{
"filePath": "src/app/shared/layout/shell/shell.component.ts",
"type": "component",
"linktype": "component",
"name": "ShellComponent",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/layout/site-footer/site-footer.component.ts",
"type": "component",
"linktype": "component",
"name": "SiteFooterComponent",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/layout/site-header/site-header.component.ts",
"type": "component",
"linktype": "component",
"name": "SiteHeaderComponent",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/alert/alert.component.ts",
"type": "component",
"linktype": "component",
"name": "AlertComponent",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/alert/alert.component.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "AlertType",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/ui/async/async.component.ts",
"type": "component",
"linktype": "component",
"name": "AsyncComponent",
"coveragePercent": 8,
"coverageCount": "1/12",
"status": "low"
},
{
"filePath": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"linktype": "directive",
"name": "AsyncEmptyDirective",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"linktype": "directive",
"name": "AsyncErrorDirective",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"linktype": "directive",
"name": "AsyncLoadedDirective",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/shared/ui/async/async.component.ts",
"type": "directive",
"linktype": "directive",
"name": "AsyncLoadingDirective",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/shared/ui/async/async.component.ts",
"type": "variable",
"linktype": "miscellaneous",
"linksubtype": "variable",
"name": "ASYNC",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
},
{
"filePath": "src/app/shared/ui/button/button.component.ts",
"type": "component",
"linktype": "component",
"name": "ButtonComponent",
"coveragePercent": 25,
"coverageCount": "1/4",
"status": "low"
},
{
"filePath": "src/app/shared/ui/button/button.component.ts",
"type": "type alias",
"linktype": "miscellaneous",
"linksubtype": "typealias",
"name": "Variant",
"coveragePercent": 0,
"coverageCount": "0/1",
"status": "low"
},
{
"filePath": "src/app/shared/ui/data-row/data-row.component.ts",
"type": "component",
"linktype": "component",
"name": "DataRowComponent",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/form-field/form-field.component.ts",
"type": "component",
"linktype": "component",
"name": "FormFieldComponent",
"coveragePercent": 20,
"coverageCount": "1/5",
"status": "low"
},
{
"filePath": "src/app/shared/ui/heading/heading.component.ts",
"type": "component",
"linktype": "component",
"name": "HeadingComponent",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/link/link.component.ts",
"type": "component",
"linktype": "component",
"name": "LinkComponent",
"coveragePercent": 50,
"coverageCount": "1/2",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/radio-group/radio-group.component.ts",
"type": "component",
"linktype": "component",
"name": "RadioGroupComponent",
"coveragePercent": 7,
"coverageCount": "1/13",
"status": "low"
},
{
"filePath": "src/app/shared/ui/radio-group/radio-group.component.ts",
"type": "interface",
"linktype": "interface",
"name": "RadioOption",
"coveragePercent": 0,
"coverageCount": "0/3",
"status": "low"
},
{
"filePath": "src/app/shared/ui/skeleton/skeleton.component.ts",
"type": "component",
"linktype": "component",
"name": "SkeletonComponent",
"coveragePercent": 10,
"coverageCount": "1/10",
"status": "low"
},
{
"filePath": "src/app/shared/ui/spinner/spinner.component.ts",
"type": "component",
"linktype": "component",
"name": "SpinnerComponent",
"coveragePercent": 16,
"coverageCount": "1/6",
"status": "low"
},
{
"filePath": "src/app/shared/ui/status-badge/status-badge.component.ts",
"type": "component",
"linktype": "component",
"name": "StatusBadgeComponent",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/stepper/stepper.component.ts",
"type": "component",
"linktype": "component",
"name": "StepperComponent",
"coveragePercent": 33,
"coverageCount": "1/3",
"status": "medium"
},
{
"filePath": "src/app/shared/ui/text-input/text-input.component.ts",
"type": "component",
"linktype": "component",
"name": "TextInputComponent",
"coveragePercent": 7,
"coverageCount": "1/14",
"status": "low"
},
{
"filePath": "src/app/showcase/concepts.page.ts",
"type": "component",
"linktype": "component",
"name": "ConceptsPage",
"coveragePercent": 8,
"coverageCount": "1/12",
"status": "low"
},
{
"filePath": "src/app/showcase/concepts.page.ts",
"type": "function",
"linktype": "miscellaneous",
"linksubtype": "function",
"name": "fakeResource",
"coveragePercent": 100,
"coverageCount": "1/1",
"status": "very-good"
}
]
}
}