{ "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": "

Person identity as supplied by the BRP (Basisregistratie Personen).

\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>;\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): 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 {\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 {\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): 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": "

One record carried across every step (and persisted). All optional: the user\nfills it in gradually, and branches may never ask some fields.

\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": "

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.

\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 \n `,\n})\nexport class BreadcrumbComponent {\n items = input.required();\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": "

WIRE CONTRACT for the BRP address lookup ("BFF-lite" — one screen-shaped call).

\n

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's own wire format. See docs/architecture/0001-bff-lite-decision-dtos.md.

\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", "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 Adreswijziging doorgeven\n
\n \n \n \n \n \n \n \n \n \n
\n Wijziging indienen\n
\n
\n `,\n})\nexport class ChangeRequestFormComponent {\n street = '';\n zip = '';\n city = '';\n streetError = signal('');\n zipError = signal('');\n submitted = output();\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": "

A submitted change request carries a parsed postcode (branded Postcode),\nnot a raw string — downstream code can't receive an unvalidated one.

\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": "

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.

\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": "

WIRE CONTRACT for the dashboard screen — the "BFF-lite" response.

\n

In 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

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.

\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> }\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>, Valid> {\n const uren = parseUren(draft.uren);\n const jaren = parseUren(draft.jaren);\n const punten = parseUren(draft.punten);\n const errors: Partial> = {};\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> = {};\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): 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": "

What the user is typing (raw, possibly invalid).

\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; // 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;\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;\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): 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 {\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 = {};\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 {\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): 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", "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": "

One 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).

\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": "

WIRE CONTRACT for the DUO diploma lookup ("BFF-lite" — one screen-shaped call\nreturning everything the beroep step needs).

\n

Each 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

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", "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; // 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;\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;\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): 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 {\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 = {};\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 {\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): 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", "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": "

Per-field error map. antwoorden holds per-policy-question errors, keyed by\nquestion id (a step can show several questions).

\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": "

Server-computed decisions. The eligibility rule lives on the backend; the\noptional reason lets the UI explain itself without knowing the rule.

\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": "

Below this many NL-hours the scholing question is required.

\n", "line": 11, "rawdescription": "\nBelow this many NL-hours the scholing question is required." } ], "indexSignatures": [], "kind": 172, "description": "

WIRE CONTRACT for intake policy values owned by the backend.

\n

This 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", "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 \n @for (opt of options(); track opt.value) {\n \n }\n \n `,\n providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true }],\n})\nexport class RadioGroupComponent implements ControlValueAccessor {\n options = input.required();\n name = input.required();\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": "

Who is logged in. Framework-free domain type.

\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 {\n /** The current state, as a read-only Angular signal. */\n readonly model: Signal;\n /** Send a message; the model becomes update(model, msg). */\n dispatch(msg: Msg): void;\n}\n\nexport function createStore(\n init: Model,\n update: (model: Model, msg: Msg) => Model,\n): Store {\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", "indexKey": "", "optional": false, "description": "

The current state, as a read-only Angular signal.

\n", "line": 15, "rawdescription": "\nThe current state, as a read-only Angular signal.", "modifierKind": [ 148 ] } ], "indexSignatures": [], "kind": 174, "description": "

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\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

Side effects (HTTP, timers) do NOT go in update — that stays pure and easy\nto test. Instead, effectful "command" functions call the network and then\ndispatch a message describing what happened (e.g. Loaded / Failed).

\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": "

Send a message; the model becomes update(model, msg).

\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> }\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>, Valid> {\n const uren = parseUren(draft.uren);\n const jaren = parseUren(draft.jaren);\n const punten = parseUren(draft.punten);\n const errors: Partial> = {};\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> = {};\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): 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": "

What we have AFTER parsing — branded/typed, guaranteed valid.

\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>;\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): 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 {\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 {\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): 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": "

What we have after the review step parses — guaranteed valid/typed.

\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; // 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;\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;\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): 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 {\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 = {};\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 {\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): 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", "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": "

What we have after the controle step parses — guaranteed valid/typed.

\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>(() =>\n fromResource(this.aantekeningenRes, (v) => v.length === 0),\n )", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "

Specialisms/notes stay a separate stream (they have their own empty state).

\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>(() => map(this.view(), (v) => v.decisions))", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "

Server-computed decisions (e.g. herregistratie eligibility) — rendered, not recomputed.

\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": "

True while a herregistratie submission is in flight or just submitted.

\n", "line": 52, "rawdescription": "\nTrue while a herregistratie submission is in flight or just submitted.", "modifierKind": [ 148 ] }, { "name": "profile", "defaultValue": "computed>(() => map(this.view(), (v) => v.profile))", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "

Registration + person, from the single aggregated call.

\n", "line": 39, "rawdescription": "\nRegistration + person, from the single aggregated call.", "modifierKind": [ 148 ] }, { "name": "view", "defaultValue": "computed>(() => {\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": "

The aggregated view, validated at the trust boundary (DTO → domain).

\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": "

The 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

The 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", "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>(() => {\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>(() => map(this.view(), (v) => v.profile));\n\n /** Server-computed decisions (e.g. herregistratie eligibility) — rendered, not recomputed. */\n readonly decisions = computed>(() => map(this.view(), (v) => v.decisions));\n\n /** Specialisms/notes stay a separate stream (they have their own empty state). */\n readonly aantekeningen = computed>(() =>\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": "

Infrastructure 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

Note: registration + person are now served via the aggregated dashboard-view\nendpoint (see DashboardViewAdapter). Only the notes stream remains separate.

\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(() => '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": "

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\nis a static mock; pointing at a real backend touches only this file + the DTO.

\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(() => '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 {\n if (typeof json !== 'object' || json === null) return err('brp-address: not an object');\n const dto = json as Partial;\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": "

Infrastructure 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", "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(() => '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 {\n if (typeof json !== 'object' || json === null) return err('dashboard-view: not an object');\n const dto = json as Partial;\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>", "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": "

Infrastructure: talks to the (mock) DigiD identity provider.

\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> {\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": "

Infrastructure 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", "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(() => '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;\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 {\n if (typeof json !== 'object' || json === null) return err('duo-lookup: not an object');\n const dto = json as Partial;\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;\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(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>", "typeParameters": [], "line": 44, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nEffectful command: authenticate, then store the session on success.", "description": "

Effectful command: authenticate, then store the session on success.

\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": "

Holds 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", "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(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> {\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 children of . */\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) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef) {}\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
\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { }\n @else { }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n \n } @else {\n Er ging iets mis bij het laden van de gegevens.\n
\n Opnieuw proberen\n
\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { }\n @else {

Geen gegevens gevonden.

}\n }\n @case ('Success') {\n \n }\n }\n
\n `,\n})\nexport class AsyncComponent {\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>();\n data = input>();\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>(() => {\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", "indexKey": "", "optional": false, "description": "", "line": 20, "modifierKind": [ 125 ] } ], "methodsClass": [], "extends": [], "constructorObj": { "name": "constructor", "description": "", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "tpl", "type": "TemplateRef", "optional": false, "dotDotDotToken": false, "deprecated": false, "deprecationMessage": "" } ], "line": 19, "jsdoctags": [ { "name": "tpl", "type": "TemplateRef", "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 children of . */\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) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef) {}\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
\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { }\n @else { }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n \n } @else {\n Er ging iets mis bij het laden van de gegevens.\n
\n Opnieuw proberen\n
\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { }\n @else {

Geen gegevens gevonden.

}\n }\n @case ('Success') {\n \n }\n }\n
\n `,\n})\nexport class AsyncComponent {\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>();\n data = input>();\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>(() => {\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", "indexKey": "", "optional": false, "description": "", "line": 24, "modifierKind": [ 125 ] } ], "methodsClass": [], "extends": [], "constructorObj": { "name": "constructor", "description": "", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "tpl", "type": "TemplateRef", "optional": false, "dotDotDotToken": false, "deprecated": false, "deprecationMessage": "" } ], "line": 23, "jsdoctags": [ { "name": "tpl", "type": "TemplateRef", "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 children of . */\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) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef) {}\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
\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { }\n @else { }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n \n } @else {\n Er ging iets mis bij het laden van de gegevens.\n
\n Opnieuw proberen\n
\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { }\n @else {

Geen gegevens gevonden.

}\n }\n @case ('Success') {\n \n }\n }\n
\n `,\n})\nexport class AsyncComponent {\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>();\n data = input>();\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>(() => {\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", "indexKey": "", "optional": false, "description": "", "line": 12, "modifierKind": [ 125 ] } ], "methodsClass": [], "extends": [], "constructorObj": { "name": "constructor", "description": "", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "tpl", "type": "TemplateRef", "optional": false, "dotDotDotToken": false, "deprecated": false, "deprecationMessage": "" } ], "line": 11, "jsdoctags": [ { "name": "tpl", "type": "TemplateRef", "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 children of . */\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) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef) {}\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
\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { }\n @else { }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n \n } @else {\n Er ging iets mis bij het laden van de gegevens.\n
\n Opnieuw proberen\n
\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { }\n @else {

Geen gegevens gevonden.

}\n }\n @case ('Success') {\n \n }\n }\n
\n `,\n})\nexport class AsyncComponent {\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>();\n data = input>();\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>(() => {\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", "indexKey": "", "optional": false, "description": "", "line": 16, "modifierKind": [ 125 ] } ], "methodsClass": [], "extends": [], "constructorObj": { "name": "constructor", "description": "", "deprecated": false, "deprecationMessage": "", "args": [ { "name": "tpl", "type": "TemplateRef", "optional": false, "dotDotDotToken": false, "deprecated": false, "deprecationMessage": "" } ], "line": 15, "jsdoctags": [ { "name": "tpl", "type": "TemplateRef", "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": "\n \n\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": "

Atom: alert/message banner.

\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 \n \n \n `,\n})\nexport class AlertComponent {\n type = input('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": "", "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: '',\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": "
\n@switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { }\n @else { }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n \n } @else {\n Er ging iets mis bij het laden van de gegevens.\n
\n Opnieuw proberen\n
\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { }\n @else {

Geen gegevens gevonden.

}\n }\n @case ('Success') {\n \n }\n}\n
\n", "templateUrl": [], "viewProviders": [], "hostDirectives": [], "inputsClass": [ { "name": "data", "deprecated": false, "deprecationMessage": "", "type": "RemoteData", "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", "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>(() => {\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": "

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 ("impossible states"). Unprovided slots\nfall back to sensible defaults.

\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 children of . */\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) {}\n}\n@Directive({ selector: '[appAsyncEmpty]' })\nexport class AsyncEmptyDirective {\n constructor(public tpl: TemplateRef) {}\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
\n @switch (rd().tag) {\n @case ('Loading') {\n @if (loadingTpl()) { }\n @else { }\n }\n @case ('Failure') {\n @if (errorTpl()) {\n \n } @else {\n Er ging iets mis bij het laden van de gegevens.\n
\n Opnieuw proberen\n
\n }\n }\n @case ('Empty') {\n @if (emptyTpl()) { }\n @else {

Geen gegevens gevonden.

}\n }\n @case ('Success') {\n \n }\n }\n
\n `,\n})\nexport class AsyncComponent {\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>();\n data = input>();\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>(() => {\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": "\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": "

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.

\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 \n `,\n})\nexport class BreadcrumbComponent {\n items = input.required();\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": "\n \n\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": "

Atom: button. Thin wrapper over the Utrecht/RHC button CSS.

\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 \n \n \n `,\n})\nexport class ButtonComponent {\n variant = input('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": "Adreswijziging doorgeven\n
\n \n \n \n \n \n \n \n \n \n
\n Wijziging indienen\n
\n
\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": "

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's Result — no parallel "is it valid" flag to drift.

\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 Adreswijziging doorgeven\n
\n \n \n \n \n \n \n \n \n \n
\n Wijziging indienen\n
\n
\n `,\n})\nexport class ChangeRequestFormComponent {\n street = '';\n zip = '';\n city = '';\n streetError = signal('');\n zipError = signal('');\n submitted = output();\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": "\n

\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

\n\n \n
\n 1 · Discriminated unions\n

Laat elke variant precies de gegevens dragen die kloppen — niets meer.

\n
\n
\n

Fout — vlakke interface

\n
\n        

Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.

\n
\n
\n

Goed — sum type

\n \n

De variant Doorgehaald kent geen herregistratiedatum, dus de rij bestaat simpelweg niet.

\n
\n
\n
\n\n \n
\n 2 · RemoteData fold\n

Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.

\n
\n
\n

Vier toestanden, één molecuul

\n

Loading

\n {{ v }}\n

Empty

\n {{ v }}\n

Failure

\n {{ v }}\n

Success

\n
    @for (i of v; track i) {
  • {{ i }}
  • }
\n
\n
\n

De exhaustieve fold

\n
\n        

Een nieuwe variant toevoegen breekt de compile via assertNever tot je hem afhandelt.

\n
\n
\n
\n\n \n
\n 3 · Parse, don't validate\n

Na het parsen onthoudt het type dat de waarde geldig is.

\n
\n
\n

Smart constructor → Result

\n \n
\n
\n @if (parsed().ok) {\n

ok

\n
Postcode = \"{{ $any(parsed()).value }}\"
\n

Een gevalideerde Postcode is een ander type dan een ruwe string.

\n } @else {\n

err

\n
{{ $any(parsed()).error }}
\n }\n
\n
\n
\n\n \n
\n 4 · Form als state machine\n

Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de huidige.

\n
\n
\n

Fout — losse booleans

\n
\n        

Niets verhindert \"submitting\" mét validatiefouten of een successcherm met errors.

\n
\n
\n

Goed — één tagged union

\n
\n @for (n of ['Editing','Submitting','Submitted','Failed']; track n) {\n {{ n }}\n }\n
\n \n
\n
\n
\n\n \n
\n 5 · Vragenlijst met vaste stappen — \"vragen tonen, niet stappen toevoegen\"\n

\n Het aantal stappen ligt vast (STEPS); vervolgvragen verschijnen binnen 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

\n
\n
\n

Vaste stappen

\n
\n @for (s of iw.steps; track s; let last = $last) {\n {{ s }}\n @if (!last) { }\n }\n
\n

De stappen zijn altijd dezelfde; alleen de vragen binnen een stap verschijnen of verdwijnen.

\n
\n
\n

De wizard

\n \n
\n
\n
\n
\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('resolved', [])", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "", "line": 183 }, { "name": "errorRes", "defaultValue": "fakeResource('error', undefined, new Error('Demo'))", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "", "line": 184 }, { "name": "foldCode", "defaultValue": "`foldRemote(rd, {\n loading: () => spinner,\n empty: () => 'geen data',\n failure: (e) => alert(e),\n success: (v) => render(v),\n}); // mist er één → compile-fout`", "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('loading')", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "", "line": 182 }, { "name": "machineBad", "defaultValue": "`submitting = signal(false);\nsubmitted = signal(false);\nerrors = signal<...>({});\n// submitting === true && errors.size > 0 ? 🤷`", "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('resolved', ['Huisartsgeneeskunde', 'Spoedeisende hulp'])", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "", "line": 185 }, { "name": "unionBad", "defaultValue": "`interface Registration {\n status: 'Geregistreerd' | 'Doorgehaald';\n herregistratieDatum: string; // altijd aanwezig 😬\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": "

Teaching showcase: each section pairs the impossible-state-permitting "before"\nwith the "after" where the type system rules it out. Composition-only.

\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 can be driven through every state without HTTP. */\nfunction fakeResource(status: string, value?: T, error?: Error): Resource {\n return { value: () => value as T, status: () => status, error: () => error, hasValue: () => value !== undefined, reload: () => {} } as unknown as Resource;\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 \n

\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

\n\n \n
\n 1 · Discriminated unions\n

Laat elke variant precies de gegevens dragen die kloppen — niets meer.

\n
\n
\n

Fout — vlakke interface

\n
\n            

Een doorgehaalde registratie houdt tóch een herregistratiedatum: onmogelijke toestand.

\n
\n
\n

Goed — sum type

\n \n

De variant Doorgehaald kent geen herregistratiedatum, dus de rij bestaat simpelweg niet.

\n
\n
\n
\n\n \n
\n 2 · RemoteData fold\n

Eén waarde met vier elkaar uitsluitende toestanden in plaats van drie losse booleans.

\n
\n
\n

Vier toestanden, één molecuul

\n

Loading

\n {{ v }}\n

Empty

\n {{ v }}\n

Failure

\n {{ v }}\n

Success

\n
    @for (i of v; track i) {
  • {{ i }}
  • }
\n
\n
\n

De exhaustieve fold

\n
\n            

Een nieuwe variant toevoegen breekt de compile via assertNever tot je hem afhandelt.

\n
\n
\n
\n\n \n
\n 3 · Parse, don't validate\n

Na het parsen onthoudt het type dat de waarde geldig is.

\n
\n
\n

Smart constructor → Result

\n \n
\n
\n @if (parsed().ok) {\n

ok

\n
Postcode = \"{{ $any(parsed()).value }}\"
\n

Een gevalideerde Postcode is een ander type dan een ruwe string.

\n } @else {\n

err

\n
{{ $any(parsed()).error }}
\n }\n
\n
\n
\n\n \n
\n 4 · Form als state machine\n

Eén tagged union stuurt de UI. Speel met de wizard — de gemarkeerde toestand is de huidige.

\n
\n
\n

Fout — losse booleans

\n
\n            

Niets verhindert \"submitting\" mét validatiefouten of een successcherm met errors.

\n
\n
\n

Goed — één tagged union

\n
\n @for (n of ['Editing','Submitting','Submitted','Failed']; track n) {\n {{ n }}\n }\n
\n \n
\n
\n
\n\n \n
\n 5 · Vragenlijst met vaste stappen — \"vragen tonen, niet stappen toevoegen\"\n

\n Het aantal stappen ligt vast (STEPS); vervolgvragen verschijnen binnen 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

\n
\n
\n

Vaste stappen

\n
\n @for (s of iw.steps; track s; let last = $last) {\n {{ s }}\n @if (!last) { }\n }\n
\n

De stappen zijn altijd dezelfde; alleen de vragen binnen een stap verschijnen of verdwijnen.

\n
\n
\n

De wizard

\n \n
\n
\n
\n
\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('loading');\n emptyRes = fakeResource('resolved', []);\n errorRes = fakeResource('error', undefined, new Error('Demo'));\n successRes = fakeResource('resolved', ['Huisartsgeneeskunde', 'Spoedeisende hulp']);\n\n raw = signal('');\n parsed = computed(() => parsePostcode(this.raw()));\n\n unionBad = `interface Registration {\n status: 'Geregistreerd' | 'Doorgehaald';\n herregistratieDatum: string; // altijd aanwezig 😬\n}`;\n\n foldCode = `foldRemote(rd, {\n loading: () => spinner,\n empty: () => 'geen data',\n failure: (e) => alert(e),\n success: (v) => render(v),\n}); // mist er één → compile-fout`;\n\n machineBad = `submitting = signal(false);\nsubmitted = signal(false);\nerrors = signal<...>({});\n// submitting === true && errors.size > 0 ? 🤷`;\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": "\n @if (store.pendingHerregistratie()) {\n Uw herregistratie-aanvraag is in behandeling.\n }\n\n \n \n \n \n
\n Persoonsgegevens (BRP)\n
\n \n \n \n
\n
\n
\n \n \n \n
\n\n
\n Specialismen en aantekeningen\n \n \n \n \n \n \n \n \n

U heeft nog geen specialismen of aantekeningen.

\n
\n
\n
\n\n

\n Inschrijven in het BIG-register (registratiewizard) →\n

\n

\n Gegevens bekijken of een wijziging doorgeven →\n

\n

\n Herregistratie aanvragen →\n

\n

\n Herregistratie-intake (vragenlijst met vertakkingen) →\n

\n

\n Functionele patronen (impossible states) →\n

\n
\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 \n @if (store.pendingHerregistratie()) {\n Uw herregistratie-aanvraag is in behandeling.\n }\n\n \n \n \n \n
\n Persoonsgegevens (BRP)\n
\n \n \n \n
\n
\n
\n \n \n \n
\n\n
\n Specialismen en aantekeningen\n \n \n \n \n \n \n \n \n

U heeft nog geen specialismen of aantekeningen.

\n
\n
\n
\n\n

\n Inschrijven in het BIG-register (registratiewizard) →\n

\n

\n Gegevens bekijken of een wijziging doorgeven →\n

\n

\n Herregistratie aanvragen →\n

\n

\n Herregistratie-intake (vragenlijst met vertakkingen) →\n

\n

\n Functionele patronen (impossible states) →\n

\n
\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": "
\n
{{ key() }}
\n
{{ value() }}
\n
\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": "

Molecule: one key/value row inside an RHC data-summary.\nWrap several of these in .rhc-data-summary (see registration-summary).

\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
\n
{{ key() }}
\n
{{ value() }}
\n
\n `,\n})\nexport class DataRowComponent {\n key = input.required();\n value = input('');\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": "
\n \n @if (description()) {\n
{{ description() }}
\n }\n \n @if (error()) {\n
{{ error() }}
\n }\n
\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": "

Molecule: form field = label + projected control + optional error/description.\nReused by both the login form and the change-request form.

\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
\n \n @if (description()) {\n
{{ description() }}
\n }\n \n @if (error()) {\n
{{ error() }}
\n }\n
\n `,\n})\nexport class FormFieldComponent {\n label = input.required();\n fieldId = input.required();\n description = input();\n error = input();\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": "\n@switch (level()) {\n @case (1) {

}\n @case (2) {

}\n @case (3) {

}\n @case (4) {

}\n @default {
}\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": "

Atom: heading. Renders the right h1..h5 with RHC heading styling.\nSingle captured in a template — multiple ng-content across

\n", "rawdescription": "\nAtom: heading. Renders the right h1..h5 with RHC heading styling.\nSingle 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 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 \n @switch (level()) {\n @case (1) {

}\n @case (2) {

}\n @case (3) {

}\n @case (4) {

}\n @default {
}\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": "\n \n \n @if (eligible) {\n \n Uw huidige registratie verloopt binnenkort. Vraag tijdig herregistratie aan.\n \n
\n \n
\n } @else {\n \n Voor uw huidige registratiestatus is herregistratie niet mogelijk.\n \n }\n
\n
\n
\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": "

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.

\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 \n \n \n @if (eligible) {\n \n Uw huidige registratie verloopt binnenkort. Vraag tijdig herregistratie aan.\n \n
\n \n
\n } @else {\n \n Voor uw huidige registratiestatus is herregistratie niet mogelijk.\n \n }\n
\n
\n
\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

Stap {{ step() }} van 2

\n
\n @if (step() === 1) {\n \n \n \n \n \n \n
\n Volgende\n Annuleren\n
\n } @else {\n \n \n \n
\n Vorige\n Herregistratie aanvragen\n Annuleren\n
\n }\n
\n }\n @case ('Submitting') {\n Aanvraag wordt verwerkt…\n }\n @case ('Submitted') {\n Uw aanvraag tot herregistratie is ontvangen.\n }\n @case ('Failed') {\n Indienen mislukt: {{ failedError() }}\n
\n Opnieuw proberen\n
\n }\n}\n", "templateUrl": [], "viewProviders": [], "hostDirectives": [], "inputsClass": [ { "name": "seed", "defaultValue": "initial", "deprecated": false, "deprecationMessage": "", "type": "WizardState", "indexKey": "", "optional": false, "description": "

Optional seed so Storybook / the showcase can mount any state directly.

\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(() => 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) : 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).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(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": "

Reset the wizard to a fresh, empty start.

\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": "

The effect: when we entered Submitting, call the backend command, flip the\noptimistic cross-page flag, then dispatch the result (and commit/rollback).

\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": "

Organism: 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.

\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

Stap {{ step() }} van 2

\n
\n @if (step() === 1) {\n \n \n \n \n \n \n
\n Volgende\n Annuleren\n
\n } @else {\n \n \n \n
\n Vorige\n Herregistratie aanvragen\n Annuleren\n
\n }\n
\n }\n @case ('Submitting') {\n Aanvraag wordt verwerkt…\n }\n @case ('Submitted') {\n Uw aanvraag tot herregistratie is ontvangen.\n }\n @case ('Failed') {\n Indienen mislukt: {{ failedError() }}\n
\n Opnieuw proberen\n
\n }\n }\n `,\n})\nexport class HerregistratieWizardComponent {\n private profile = inject(BigProfileStore);\n private store = createStore(initial, reduce);\n\n /** Optional seed so Storybook / the showcase can mount any state directly. */\n seed = input(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) : null));\n protected step = computed(() => this.editing()?.step ?? 1);\n protected draft = computed(() => 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).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": "\n \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 \n
\n \n
\n
\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": "

Page: the branching intake questionnaire. Built entirely from existing\nbuilding blocks (page shell + alert + the intake-wizard organism).

\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 \n \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 \n
\n \n
\n
\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

Stap {{ cursor() + 1 }} van {{ steps.length }}

\n
\n @switch (step()) {\n @case ('buitenland') {\n \n \n \n @if (answers().buitenlandGewerkt === 'ja') {\n \n \n \n \n \n \n }\n }\n @case ('werk') {\n \n \n \n @if (scholingZichtbaar()) {\n \n \n \n }\n @if (answers().scholingGevolgd === 'ja') {\n \n \n \n }\n }\n @case ('review') {\n Controleer uw antwoorden en dien de aanvraag in.\n
\n
Buiten NL gewerkt
{{ answers().buitenlandGewerkt ?? '—' }}
\n @if (answers().buitenlandGewerkt === 'ja') {\n
Land
{{ answers().land }}
\n
Buitenlandse uren
{{ answers().buitenlandseUren }}
\n }\n
Uren NL
{{ answers().uren }}
\n @if (scholingZichtbaar()) {\n
Aanvullende scholing
{{ answers().scholingGevolgd }}
\n }\n @if (answers().scholingGevolgd === 'ja') {\n
Nascholingspunten
{{ answers().punten }}
\n }\n
\n }\n }\n
\n @if (cursor() > 0) {\n Vorige\n }\n {{ step() === 'review' ? 'Aanvraag indienen' : 'Volgende' }}\n Annuleren\n
\n
\n }\n @case ('Submitting') {\n Aanvraag wordt verwerkt…\n }\n @case ('Submitted') {\n Uw aanvraag tot herregistratie is ontvangen.\n
\n Opnieuw beginnen\n
\n }\n @case ('Failed') {\n Indienen mislukt: {{ failedError() }}\n
\n Opnieuw proberen\n
\n }\n}\n", "templateUrl": [], "viewProviders": [], "hostDirectives": [], "inputsClass": [ { "name": "seed", "defaultValue": "initial", "deprecated": false, "deprecationMessage": "", "type": "IntakeState", "indexKey": "", "optional": false, "description": "

Optional seed so Storybook / the showcase can mount any state directly.

\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) : null))", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "", "line": 135, "modifierKind": [ 123 ] }, { "name": "answers", "defaultValue": "computed(() => 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).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(() => '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": "

Server-owned threshold from the policy endpoint (mirrored into machine state).

\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": "

Whether the inline scholing question is shown (and required) in the 'werk' step.

\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(() => 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": "

Public so the showcase can render the (fixed) step list next to the wizard.

\n", "line": 137, "rawdescription": "\nPublic so the showcase can render the (fixed) step list next to the wizard.", "modifierKind": [ 148 ] }, { "name": "store", "defaultValue": "createStore(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": "

The effect: when we enter Submitting, call the backend, flip the optimistic\ncross-page flag, then dispatch the outcome (and commit/rollback).

\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": "

Organism: 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.

\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

Stap {{ cursor() + 1 }} van {{ steps.length }}

\n
\n @switch (step()) {\n @case ('buitenland') {\n \n \n \n @if (answers().buitenlandGewerkt === 'ja') {\n \n \n \n \n \n \n }\n }\n @case ('werk') {\n \n \n \n @if (scholingZichtbaar()) {\n \n \n \n }\n @if (answers().scholingGevolgd === 'ja') {\n \n \n \n }\n }\n @case ('review') {\n Controleer uw antwoorden en dien de aanvraag in.\n
\n
Buiten NL gewerkt
{{ answers().buitenlandGewerkt ?? '—' }}
\n @if (answers().buitenlandGewerkt === 'ja') {\n
Land
{{ answers().land }}
\n
Buitenlandse uren
{{ answers().buitenlandseUren }}
\n }\n
Uren NL
{{ answers().uren }}
\n @if (scholingZichtbaar()) {\n
Aanvullende scholing
{{ answers().scholingGevolgd }}
\n }\n @if (answers().scholingGevolgd === 'ja') {\n
Nascholingspunten
{{ answers().punten }}
\n }\n
\n }\n }\n
\n @if (cursor() > 0) {\n Vorige\n }\n {{ step() === 'review' ? 'Aanvraag indienen' : 'Volgende' }}\n Annuleren\n
\n
\n }\n @case ('Submitting') {\n Aanvraag wordt verwerkt…\n }\n @case ('Submitted') {\n Uw aanvraag tot herregistratie is ontvangen.\n
\n Opnieuw beginnen\n
\n }\n @case ('Failed') {\n Indienen mislukt: {{ failedError() }}\n
\n Opnieuw proberen\n
\n }\n }\n `,\n})\nexport class IntakeWizardComponent {\n private profile = inject(BigProfileStore);\n private store = createStore(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(() => '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(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) : 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(() => this.answering()?.answers ?? {});\n protected step = computed(() => 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).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": "", "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": "

Atom: link. Internal router link styled as an RHC/Utrecht link.

\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: ``,\n})\nexport class LinkComponent {\n to = input.required();\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": "
\n \n \n \n\n \n \n \n\n
\n Inloggen met DigiD\n
\n
\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": "

Organism: DigiD-style mock login. No real auth — just composes atoms/molecules.

\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
\n \n \n \n\n \n \n \n\n
\n Inloggen met DigiD\n
\n
\n `,\n})\nexport class LoginFormComponent {\n bsn = '';\n password = '';\n submitted = output();\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": "\n @if (error()) { {{ error() }} }\n \n\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 \n @if (error()) { {{ error() }} }\n \n \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": "
\n @if (breadcrumb()) {\n \n }\n @if (backLink()) {\n

← {{ backLabel() }}

\n }\n {{ heading() }}\n @if (intro()) {\n

{{ intro() }}

\n }\n \n
\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": "

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).

\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
\n @if (breadcrumb()) {\n \n }\n @if (backLink()) {\n

← {{ backLabel() }}

\n }\n {{ heading() }}\n @if (intro()) {\n

{{ intro() }}

\n }\n \n
\n `,\n})\nexport class PageShellComponent {\n heading = input.required();\n intro = input();\n backLink = input();\n backLabel = input('Terug naar overzicht');\n width = input<'default' | 'narrow'>('default');\n breadcrumb = input();\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": "\n @for (opt of options(); track opt.value) {\n \n }\n\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": "

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.

\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 \n @for (opt of options(); track opt.value) {\n \n }\n \n `,\n providers: [{ provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => RadioGroupComponent), multi: true }],\n})\nexport class RadioGroupComponent implements ControlValueAccessor {\n options = input.required();\n name = input.required();\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": "\n \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 \n
\n \n
\n\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": "

Page: register in the BIG-register. Built entirely from existing building\nblocks (page shell + alert + the registratie-wizard organism).

\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 \n \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 \n
\n \n
\n \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 \n

{{ stepTitle() }}

\n
\n @switch (step()) {\n @case ('adres') {\n @if (adresStatus() === 'laden') {\n \n } @else {\n @switch (adresStatus()) {\n @case ('gevonden') {\n Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan.\n }\n @case ('geen') {\n We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in.\n }\n @case ('fout') {\n We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig in.\n }\n }\n \n \n \n \n \n \n \n \n \n \n \n \n @if (draft().correspondentie === 'email') {\n \n \n \n }\n }\n }\n @case ('beroep') {\n \n \n \n \n \n\n @if (handmatigActief()) {\n Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna handmatig beoordeeld.\n \n \n \n } @else if (draft().beroep) {\n
\n \n
\n }\n\n @for (q of actieveVragen($any(data)); track q.id) {\n \n @if (q.type === 'ja-nee') {\n \n } @else {\n \n }\n \n }\n
\n \n \n \n
\n }\n @case ('controle') {\n Controleer uw gegevens en dien de registratie in.\n
\n \n \n \n @if (draft().correspondentie === 'email') {\n \n }\n \n \n @for (item of samenvattingVragen(); track item.vraag) {\n \n }\n
\n
\n Adres wijzigen\n Diploma wijzigen\n
\n }\n }\n
\n @if (cursor() > 0) {\n Vorige\n }\n {{ step() === 'controle' ? 'Registratie indienen' : 'Volgende' }}\n Annuleren\n
\n \n }\n @case ('Indienen') {\n Uw registratie wordt verwerkt…\n }\n @case ('Ingediend') {\n Uw registratie is ontvangen. Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie.\n
\n Nieuwe registratie starten\n
\n }\n @case ('Mislukt') {\n Het indienen is niet gelukt: {{ failedError() }}\n
\n Opnieuw proberen\n
\n }\n}\n", "templateUrl": [], "viewProviders": [], "hostDirectives": [], "inputsClass": [ { "name": "seed", "defaultValue": "initial", "deprecated": false, "deprecationMessage": "", "type": "RegistratieState", "indexKey": "", "optional": false, "description": "

Optional seed so Storybook / tests can mount any state directly.

\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": "

The policy questions that apply to the current choice (server-decided).

\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": "

BRP 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.

\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": "

The radio selection: a diploma id, or the "not listed" sentinel in manual mode.

\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(() => 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(() => {\n const rd = this.lookupRd();\n return rd.tag === 'Success' ? rd.value : null;\n })", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "

Parsed lookup as a plain value (or null) — used outside the beroep step (the\ncontrole summary) where the template variable isn't in scope.

\n", "line": 233, "rawdescription": "\nParsed lookup as a plain value (or null) — used outside the beroep step (the\ncontrole summary) where the 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).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": "

True while the user is entering a diploma manually (not in the DUO list).

\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) : 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>(() => {\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": "

The DUO lookup (diplomas + manual fallback), validated at the trust boundary.

\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).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": "

Answered policy questions for the controle summary (question text + answer).

\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(() => 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>('stepHeading')", "deprecated": false, "deprecationMessage": "", "type": "unknown", "indexKey": "", "optional": false, "description": "

Focus target: the step heading receives focus on step change (a11y).

\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(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": "

Reset the wizard to a fresh start. Reload the BRP lookup so the address\nre-prefills, keeping the form and the "vooraf ingevuld" note consistent.

\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": "

The effect: when we enter Indienen, call the backend, then dispatch the\noutcome (success carries the confirmation reference).

\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": "

Organism: 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 ; 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.

\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 ; 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 ; 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 \n

{{ stepTitle() }}

\n
\n @switch (step()) {\n @case ('adres') {\n @if (adresStatus() === 'laden') {\n \n } @else {\n @switch (adresStatus()) {\n @case ('gevonden') {\n Vooraf ingevuld op basis van de BRP. Controleer en pas zo nodig aan.\n }\n @case ('geen') {\n We vonden geen adres in de BRP. Vul uw adres hieronder handmatig in.\n }\n @case ('fout') {\n We konden de BRP nu niet bereiken. Vul uw adres hieronder handmatig in.\n }\n }\n \n \n \n \n \n \n \n \n \n \n \n \n @if (draft().correspondentie === 'email') {\n \n \n \n }\n }\n }\n @case ('beroep') {\n \n \n \n \n \n\n @if (handmatigActief()) {\n Een handmatig ingevoerd diploma kan niet automatisch worden geverifieerd. Kies uw beroep en beantwoord de aanvullende vragen; uw aanvraag wordt daarna handmatig beoordeeld.\n \n \n \n } @else if (draft().beroep) {\n
\n \n
\n }\n\n @for (q of actieveVragen($any(data)); track q.id) {\n \n @if (q.type === 'ja-nee') {\n \n } @else {\n \n }\n \n }\n
\n \n \n \n
\n }\n @case ('controle') {\n Controleer uw gegevens en dien de registratie in.\n
\n \n \n \n @if (draft().correspondentie === 'email') {\n \n }\n \n \n @for (item of samenvattingVragen(); track item.vraag) {\n \n }\n
\n
\n Adres wijzigen\n Diploma wijzigen\n
\n }\n }\n
\n @if (cursor() > 0) {\n Vorige\n }\n {{ step() === 'controle' ? 'Registratie indienen' : 'Volgende' }}\n Annuleren\n
\n \n }\n @case ('Indienen') {\n Uw registratie wordt verwerkt…\n }\n @case ('Ingediend') {\n Uw registratie is ontvangen. Uw referentienummer is {{ referentie() }}. Bewaar dit nummer voor uw administratie.\n
\n Nieuwe registratie starten\n
\n }\n @case ('Mislukt') {\n Het indienen is niet gelukt: {{ failedError() }}\n
\n Opnieuw proberen\n
\n }\n }\n `,\n})\nexport class RegistratieWizardComponent {\n private brp = inject(BrpAdapter);\n private duo = inject(DuoAdapter);\n private store = createStore(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(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>('stepHeading');\n\n private invullen = computed(() => (this.state().tag === 'Invullen' ? (this.state() as Extract) : null));\n protected cursor = computed(() => this.invullen()?.cursor ?? 0);\n protected draft = computed(() => this.invullen()?.draft ?? { antwoorden: {} });\n protected step = computed(() => 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).referentie : ''));\n protected failedError = computed(() => (this.state().tag === 'Mislukt' ? (this.state() as Extract).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>(() => {\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 template variable isn't in scope. */\n private duoData = computed(() => {\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": "\n \n \n \n \n \n \n \n \n\n
\n @if (submitted()) {\n Uw adreswijziging is ontvangen. U ontvangt binnen 5 werkdagen bericht.\n } @else {\n \n }\n
\n
\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 \n \n \n \n \n \n \n \n \n\n
\n @if (submitted()) {\n Uw adreswijziging is ontvangen. U ontvangt binnen 5 werkdagen bericht.\n } @else {\n \n }\n
\n
\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": "
\n
\n \n \n \n \n \n \n \n \n @switch (reg().status.tag) {\n @case ('Geregistreerd') {\n \n }\n @case ('Geschorst') {\n \n \n }\n @case ('Doorgehaald') {\n \n \n }\n }\n
\n
\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": "

Organism: registration summary card. Composes data-row molecules + status-badge atom.

\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
\n
\n \n \n \n \n \n \n \n \n @switch (reg().status.tag) {\n @case ('Geregistreerd') {\n \n }\n @case ('Geschorst') {\n \n \n }\n @case ('Doorgehaald') {\n \n \n }\n }\n
\n
\n `,\n})\nexport class RegistrationSummaryComponent {\n reg = input.required();\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": "
\n \n \n \n \n \n \n \n \n \n @for (row of rows(); track row.omschrijving) {\n \n \n \n \n \n }\n \n
TypeOmschrijvingDatum
{{ row.type }}{{ row.omschrijving }}{{ row.datum | date:'mediumDate' }}
\n
\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": "

Organism: table of specialismen/aantekeningen.

\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
\n \n \n \n \n \n \n \n \n \n @for (row of rows(); track row.omschrijving) {\n \n \n \n \n \n }\n \n
TypeOmschrijvingDatum
{{ row.type }}{{ row.omschrijving }}{{ row.datum | date:'mediumDate' }}
\n
\n `,\n})\nexport class RegistrationTableComponent {\n rows = input.required();\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": "Naar de inhoud\n
\n \n
\n
\n \n
\n
\n \n
\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": "

Template: persistent app chrome. Header + footer mount once; only the routed\ncontent inside changes (and cross-fades — see styles.scss).

\n", "rawdescription": "\nTemplate: persistent app chrome. Header + footer mount once; only the routed\ncontent inside 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 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 Naar de inhoud\n
\n \n
\n
\n \n
\n
\n \n
\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": "
\n
\n BIG-register\n CIBG — Ministerie van Volksgezondheid, Welzijn en Sport\n Demo / POC — geen echte gegevens\n
\n
\n", "templateUrl": [], "viewProviders": [], "hostDirectives": [], "inputsClass": [], "outputsClass": [], "propertiesClass": [], "methodsClass": [], "deprecated": false, "deprecationMessage": "", "hostBindings": [], "hostListeners": [], "standalone": false, "imports": [], "description": "

Organism: site footer.

\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
\n
\n BIG-register\n CIBG — Ministerie van Volksgezondheid, Welzijn en Sport\n Demo / POC — geen echte gegevens\n
\n
\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": "
\n \n
\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": "

Organism: site header with Rijksoverheid-style wordmark + title.\nponytail: text wordmark instead of the licensed Rijksoverheid logo.

\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
\n \n
\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
\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", "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": "

Atom: skeleton placeholder (grey shimmer). Delay-gated so it never flashes\non fast responses. Render count lines shaped roughly like the content.

\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
\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;\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
\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", "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": "

Atom: spinner that only appears after delay ms — fast responses never\nflash a spinner, slow ones get feedback.

\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
\n }\n `,\n})\nexport class SpinnerComponent implements OnInit, OnDestroy {\n delay = input(250);\n protected visible = signal(false);\n private timer?: ReturnType;\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": "\n \n {{ label() }}\n\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": "

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.

\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 \n \n {{ label() }}\n \n `,\n})\nexport class StatusBadgeComponent {\n label = input.required();\n color = input.required();\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": "\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": "

Molecule: 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.

\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 \n `,\n})\nexport class StepperComponent {\n steps = input.required();\n current = input.required();\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": "\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": "

Atom: text input. Utrecht textbox wired up as a form control (ngModel/reactive).

\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 \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();\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": "

Convenience: import this array to get the wrapper + all slot directives.

\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": "

Route guard: only let authenticated users in; otherwise redirect to /login.

\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": "(error: E): Result => ({ 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": "(value: T): Result => ({ 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": "

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.

\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": "

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.

\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": "

The fixed step list. Number of steps never changes; questions reveal inline.

\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": "

The fixed step list. Number of steps never changes; questions reveal inline.

\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": "

Chain a second source that depends on the first one's value.

\n", "args": [ { "name": "rd", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "f", "deprecated": false, "deprecationMessage": "" } ], "returnType": "RemoteData", "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": "

Tiny native-TS functional toolkit. No dependency — this is the whole "library".\nReused by every "impossible states" concept in the POC.

\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", "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": "

Reads ?scenario= from the URL so a demo can force each async state.

\n", "args": [], "returnType": "Scenario" }, { "name": "currentStep", "file": "src/app/herregistratie/domain/intake.machine.ts", "ctype": "miscellaneous", "subtype": "function", "deprecated": false, "deprecationMessage": "", "description": "

Which step the cursor currently points at (clamped to the fixed list).

\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": "

Which step the cursor currently points at (clamped to the fixed list).

\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": "

Declare the beroep for a manually-entered diploma (chosen from a fixed list).

\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": "

Minimal fake Resource so can be driven through every state without HTTP.

\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", "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": "

Exhaustive fold: you must handle every case, checked at compile time.

\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": "

Project Angular's loosely-typed Resource into a RemoteData value.

\n", "args": [ { "name": "r", "type": "Resource", "deprecated": false, "deprecationMessage": "" }, { "name": "isEmpty", "deprecated": false, "deprecationMessage": "", "defaultValue": "() => false" } ], "returnType": "RemoteData", "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": "

Jump back to an earlier step to correct data (controle → step N). Forward\njumps are not allowed (would skip validation). Preserves the draft.

\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": "

The herregistratie deadline, if the status has one (only the active state does).

\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": "

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 decisions.eligibleForHerregistratie in the dashboard view. Kept here as\nthe reference implementation + unit test; the frontend no longer calls it.

\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": "

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.

\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": "

Pick a DUO diploma; the beroep is derived from it and the applicable policy\nquestions (vraagIds) come with it (both server-computed, passed in).

\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": "

Switch to manual diploma entry: the diploma isn't in DUO, so the MAXIMAL\npolicy-question set applies and the entry is flagged handmatig/unverified. The\nberoep is declared separately (declareerBeroep).

\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": "

True when NL-hours are low enough that the scholing question must be answered.\nThe threshold is passed in (server-owned), not hardcoded.

\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": "

Transform the value inside a Success; pass other states through unchanged.

\n", "args": [ { "name": "rd", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "f", "deprecated": false, "deprecationMessage": "" } ], "returnType": "RemoteData", "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": "

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.

\n", "args": [ { "name": "a", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "b", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "f", "deprecated": false, "deprecationMessage": "" } ], "returnType": "RemoteData", "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": "

Combine three sources (built on map2).

\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", "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": "

Step 1 → 2: advance only when BOTH step-1 fields parse. Illegal elsewhere = no-op.

\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", "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": "

Trust-boundary parse: validate the untrusted response shape. "Geen adres" is a\nvalid outcome (gevonden: false), not a malformed response. ponytail: hand-written;\nreach for a schema lib once the contract count grows.

\n", "args": [ { "name": "json", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

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.

\n", "args": [ { "name": "json", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

Trust-boundary parse: validate the untrusted response shape (diplomas, each\nwith derived beroep + policy questions, plus the manual-entry fallback).

\n", "args": [ { "name": "json", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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", "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", "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", "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": "

Prefill the address from a BRP lookup and flag its origin (PRD §7).

\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": "

Resolve the async submit. Only meaningful while Submitting.

\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": "

Restore a persisted session (best-effort; corrupt entry → logged out).

\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": "

Update one draft field while editing; ignored in any other state.

\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": "

Apply a server-owned policy value (e.g. the scholing threshold).

\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": "

Brand colour token for a status. assertNever forces a colour for every new\nstatus variant at compile time.

\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": "

Domain logic for a registration — pure functions, NO Angular. This is where\n"what the business rules say" lives, separate from "how it looks" (UI) and\n"where the data comes from" (infrastructure). Keeping it framework-free means\nit is trivial to read and unit-test.

\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": "

Step 2 submit: parse everything; move to Submitting only with Valid data.

\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": "

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's\nan API. The "uren must be > 0" rule lets the demo show a failure path.

\n", "args": [ { "name": "data", "type": "Valid", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Promise>", "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": "

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's an API. The "uren must be > 0" rule\nlets the demo show the failure path.

\n", "args": [ { "name": "data", "type": "ValidIntake", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Promise>", "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": "

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's an API. The "manually entered diploma" rule lets\nthe demo show the failure path.

\n", "args": [ { "name": "data", "type": "ValidRegistratie", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Promise>", "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": "

Parse every field; on success hand back a Valid, else the per-field errors.

\n", "args": [ { "name": "draft", "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result>, 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": "

Parse the whole questionnaire into a ValidIntake (called on submit).

\n", "args": [ { "name": "a", "type": "Answers", "deprecated": false, "deprecationMessage": "" }, { "name": "scholingThreshold", "type": "number", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

Parse the whole wizard into a ValidRegistratie (called on submit).

\n", "args": [ { "name": "d", "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

Validate every question currently visible in ONE step. Errors keyed per field.

\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", "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": "

Validate every question currently visible in ONE step. Errors keyed per field.

\n", "args": [ { "name": "step", "type": "StepId", "deprecated": false, "deprecationMessage": "" }, { "name": "d", "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

A note is either a recognised specialism or a plain annotation — a closed set,\nnot an open string, so a typo can't slip through.

\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": "

Where a piece of data came from — recorded on the aggregate (PRD §5).

\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", "file": "src/app/registratie/domain/value-objects/big-nummer.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: a BIG registration number — 11 digits.

\n", "kind": 184 }, { "name": "Brand", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "unknown", "file": "src/app/shared/kernel/fp.ts", "deprecated": false, "deprecationMessage": "", "description": "

Nominal typing: Brand<string, 'Postcode'> is assignable from a plain string\nonly through an explicit cast — so a smart constructor is the only minter.

\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": "

Text fields settable via SetField.

\n", "kind": 193 }, { "name": "Email", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "Brand", "file": "src/app/registratie/domain/value-objects/email.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: an e-mail address. "Parse, don't validate" — 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).

\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>", "file": "src/app/herregistratie/domain/intake.machine.ts", "deprecated": false, "deprecationMessage": "", "description": "

Per-field error map: one message per question, since a step holds several.

\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": "

A FIXED 3-step wizard with progressive disclosure. The steps never change in\nnumber (always STEPS); instead, follow-up questions appear inline within a\nstep depending on earlier answers — answer "buiten Nederland gewerkt? → ja"\nand the country/hours questions reveal in the same step; report few hours and\nthe scholing-question reveals inside the 'werk' step. "Is this field required\nright now" is a pure function (validateStep/lageUren), so it's trivial to\ntest and impossible to get out of sync with the data.

\n", "kind": 193 }, { "name": "Postcode", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "Brand", "file": "src/app/registratie/domain/value-objects/postcode.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: a Dutch postcode. "Parse, don't validate" — a Postcode is a\ndistinct type from a raw string, mintable only via parsePostcode, so holding\none is proof it is well-formed.

\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": "

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.

\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": "

The four mutually-exclusive states of an async fetch, as a tagged union.\nCrucially the data lives ON the state: only Failure has an error, only\nSuccess has a value. "Loaded but no value" or "error with stale value"\nare unrepresentable — Richard Feldman's RemoteData.

\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": "

A computation that either succeeded with a value or failed with an error.\nPlain objects (no classes) to match the signal/httpResource ergonomics.

\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": "

Just the discriminant — for atoms that only need the label/color.

\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": "

The three fixed steps. Each step groups one or more questions.

\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": "

A FIXED 3-step registration wizard. The steps never change in number (always\nSTEPS): (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\nright now" is a pure function (validateStep), 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 Indienen only when a complete ValidRegistratie parses.

\n", "kind": 193 }, { "name": "Uren", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "Brand", "file": "src/app/registratie/domain/value-objects/uren.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: a non-negative whole number of hours.

\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": "

Every event that can happen to the wizard, as one message type. The component\nsends a WizardMsg; reduce decides the next state. This is the Elm\nModel+Msg+update pattern: ONE pure function describes all state changes.

\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": "

The whole wizard as one tagged union. step and errors exist ONLY while\nEditing; Submitting/Submitted/Failed carry a Valid payload and nothing else.\nSo "submitting while a field is invalid" or "showing the success screen with\nerrors set" are unrepresentable — the bug class is gone by construction.

\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": "

Convenience: import this array to get the wrapper + all slot directives.

\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": "

Route guard: only let authenticated users in; otherwise redirect to /login.

\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": "

The fixed step list. Number of steps never changes; questions reveal inline.

\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": "(error: E): Result => ({ ok: false, error })" }, { "name": "ok", "ctype": "miscellaneous", "subtype": "variable", "file": "src/app/shared/kernel/fp.ts", "deprecated": false, "deprecationMessage": "", "type": "unknown", "defaultValue": "(value: T): Result => ({ 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": "

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.

\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": "

The fixed step list. Number of steps never changes; questions reveal inline.

\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": "

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.

\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": "

Chain a second source that depends on the first one's value.

\n", "args": [ { "name": "rd", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "f", "deprecated": false, "deprecationMessage": "" } ], "returnType": "RemoteData", "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": "

Exhaustive fold: you must handle every case, checked at compile time.

\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": "

Project Angular's loosely-typed Resource into a RemoteData value.

\n", "args": [ { "name": "r", "type": "Resource", "deprecated": false, "deprecationMessage": "" }, { "name": "isEmpty", "deprecated": false, "deprecationMessage": "", "defaultValue": "() => false" } ], "returnType": "RemoteData", "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": "

Transform the value inside a Success; pass other states through unchanged.

\n", "args": [ { "name": "rd", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "f", "deprecated": false, "deprecationMessage": "" } ], "returnType": "RemoteData", "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": "

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.

\n", "args": [ { "name": "a", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "b", "type": "RemoteData", "deprecated": false, "deprecationMessage": "" }, { "name": "f", "deprecated": false, "deprecationMessage": "" } ], "returnType": "RemoteData", "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": "

Combine three sources (built on map2).

\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", "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": "

Tiny native-TS functional toolkit. No dependency — this is the whole "library".\nReused by every "impossible states" concept in the POC.

\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": "

Step 1 → 2: advance only when BOTH step-1 fields parse. Illegal elsewhere = no-op.

\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": "

Resolve the async submit. Only meaningful while Submitting.

\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": "

Update one draft field while editing; ignored in any other state.

\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": "

Step 2 submit: parse everything; move to Submitting only with Valid data.

\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": "

Parse every field; on success hand back a Valid, else the per-field errors.

\n", "args": [ { "name": "draft", "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result>, 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": "

Which step the cursor currently points at (clamped to the fixed list).

\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": "

True when NL-hours are low enough that the scholing question must be answered.\nThe threshold is passed in (server-owned), not hardcoded.

\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": "

Apply a server-owned policy value (e.g. the scholing threshold).

\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": "

Parse the whole questionnaire into a ValidIntake (called on submit).

\n", "args": [ { "name": "a", "type": "Answers", "deprecated": false, "deprecationMessage": "" }, { "name": "scholingThreshold", "type": "number", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

Validate every question currently visible in ONE step. Errors keyed per field.

\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", "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": "

Which step the cursor currently points at (clamped to the fixed list).

\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": "

Declare the beroep for a manually-entered diploma (chosen from a fixed list).

\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": "

Jump back to an earlier step to correct data (controle → step N). Forward\njumps are not allowed (would skip validation). Preserves the draft.

\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": "

Pick a DUO diploma; the beroep is derived from it and the applicable policy\nquestions (vraagIds) come with it (both server-computed, passed in).

\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": "

Switch to manual diploma entry: the diploma isn't in DUO, so the MAXIMAL\npolicy-question set applies and the entry is flagged handmatig/unverified. The\nberoep is declared separately (declareerBeroep).

\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": "

Prefill the address from a BRP lookup and flag its origin (PRD §7).

\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": "

Parse the whole wizard into a ValidRegistratie (called on submit).

\n", "args": [ { "name": "d", "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

Validate every question currently visible in ONE step. Errors keyed per field.

\n", "args": [ { "name": "step", "type": "StepId", "deprecated": false, "deprecationMessage": "" }, { "name": "d", "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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", "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": "

Reads ?scenario= from the URL so a demo can force each async state.

\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": "

Minimal fake Resource so can be driven through every state without HTTP.

\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", "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": "

The herregistratie deadline, if the status has one (only the active state does).

\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": "

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 decisions.eligibleForHerregistratie in the dashboard view. Kept here as\nthe reference implementation + unit test; the frontend no longer calls it.

\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": "

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.

\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": "

Brand colour token for a status. assertNever forces a colour for every new\nstatus variant at compile time.

\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": "

Domain logic for a registration — pure functions, NO Angular. This is where\n"what the business rules say" lives, separate from "how it looks" (UI) and\n"where the data comes from" (infrastructure). Keeping it framework-free means\nit is trivial to read and unit-test.

\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", "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": "

Trust-boundary parse: validate the untrusted response shape. "Geen adres" is a\nvalid outcome (gevonden: false), not a malformed response. ponytail: hand-written;\nreach for a schema lib once the contract count grows.

\n", "args": [ { "name": "json", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

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.

\n", "args": [ { "name": "json", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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": "

Trust-boundary parse: validate the untrusted response shape (diplomas, each\nwith derived beroep + policy questions, plus the manual-entry fallback).

\n", "args": [ { "name": "json", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Result", "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", "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", "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", "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": "

Restore a persisted session (best-effort; corrupt entry → logged out).

\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": "

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's\nan API. The "uren must be > 0" rule lets the demo show a failure path.

\n", "args": [ { "name": "data", "type": "Valid", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Promise>", "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": "

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's an API. The "uren must be > 0" rule\nlets the demo show the failure path.

\n", "args": [ { "name": "data", "type": "ValidIntake", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Promise>", "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": "

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's an API. The "manually entered diploma" rule lets\nthe demo show the failure path.

\n", "args": [ { "name": "data", "type": "ValidRegistratie", "deprecated": false, "deprecationMessage": "" } ], "returnType": "Promise>", "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": "

A note is either a recognised specialism or a plain annotation — a closed set,\nnot an open string, so a typo can't slip through.

\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": "

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.

\n", "kind": 193 }, { "name": "StatusTag", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "RegistrationStatus", "file": "src/app/registratie/domain/registration.ts", "deprecated": false, "deprecationMessage": "", "description": "

Just the discriminant — for atoms that only need the label/color.

\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": "

Where a piece of data came from — recorded on the aggregate (PRD §5).

\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": "

Text fields settable via SetField.

\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": "

A FIXED 3-step registration wizard. The steps never change in number (always\nSTEPS): (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\nright now" is a pure function (validateStep), 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 Indienen only when a complete ValidRegistratie parses.

\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", "file": "src/app/registratie/domain/value-objects/big-nummer.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: a BIG registration number — 11 digits.

\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": "

Nominal typing: Brand<string, 'Postcode'> is assignable from a plain string\nonly through an explicit cast — so a smart constructor is the only minter.

\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": "

A computation that either succeeded with a value or failed with an error.\nPlain objects (no classes) to match the signal/httpResource ergonomics.

\n", "kind": 193 } ], "src/app/registratie/domain/value-objects/email.ts": [ { "name": "Email", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "Brand", "file": "src/app/registratie/domain/value-objects/email.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: an e-mail address. "Parse, don't validate" — 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).

\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>", "file": "src/app/herregistratie/domain/intake.machine.ts", "deprecated": false, "deprecationMessage": "", "description": "

Per-field error map: one message per question, since a step holds several.

\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": "

A FIXED 3-step wizard with progressive disclosure. The steps never change in\nnumber (always STEPS); instead, follow-up questions appear inline within a\nstep depending on earlier answers — answer "buiten Nederland gewerkt? → ja"\nand the country/hours questions reveal in the same step; report few hours and\nthe scholing-question reveals inside the 'werk' step. "Is this field required\nright now" is a pure function (validateStep/lageUren), so it's trivial to\ntest and impossible to get out of sync with the data.

\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": "

The three fixed steps. Each step groups one or more questions.

\n", "kind": 193 } ], "src/app/registratie/domain/value-objects/postcode.ts": [ { "name": "Postcode", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "Brand", "file": "src/app/registratie/domain/value-objects/postcode.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: a Dutch postcode. "Parse, don't validate" — a Postcode is a\ndistinct type from a raw string, mintable only via parsePostcode, so holding\none is proof it is well-formed.

\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": "

The four mutually-exclusive states of an async fetch, as a tagged union.\nCrucially the data lives ON the state: only Failure has an error, only\nSuccess has a value. "Loaded but no value" or "error with stale value"\nare unrepresentable — Richard Feldman's RemoteData.

\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", "file": "src/app/registratie/domain/value-objects/uren.ts", "deprecated": false, "deprecationMessage": "", "description": "

Value object: a non-negative whole number of hours.

\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": "

Every event that can happen to the wizard, as one message type. The component\nsends a WizardMsg; reduce decides the next state. This is the Elm\nModel+Msg+update pattern: ONE pure function describes all state changes.

\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": "

The whole wizard as one tagged union. step and errors exist ONLY while\nEditing; Submitting/Submitted/Failed carry a Valid payload and nothing else.\nSo "submitting while a field is invalid" or "showing the success screen with\nerrors set" are unrepresentable — the bug class is gone by construction.

\n", "kind": 193 } ] } }, "routes": { "name": "", "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" } ] } }