From f9b76e7f6ae50d6b0338f33e6ba5b1714ac23dfb Mon Sep 17 00:00:00 2001 From: Edwin van den Houdt Date: Thu, 2 Jul 2026 20:19:58 +0200 Subject: [PATCH] =?UTF-8?q?feat(boundaries):=20WP-03=20=E2=80=94=20contrac?= =?UTF-8?q?ts=20purity=20+=20ApiClient=20confinement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lint-enforce two architecture rules that were only documented (ADR-0001), landing the rules with the fixes so the build stays green: - contracts/ imports nothing: dashboard-view.dto.ts is now pure wire shapes (inline string-union enums, no domain imports). The DashboardView FE-view type moves to the adapter, which maps wire → domain (compiler-enforced seam). - ApiClient lives only in infrastructure: change-request-form (UI) no longer injects ApiClient — a new ChangeRequestAdapter owns the client and the submit becomes a createSubmitChangeRequest() command factory (createDraftSync shape). draft-sync's wire-DTO import becomes type-only (allowed via allowTypeImports). - Role type moves to shared/domain/role.ts; the ?role= reader stays in shared/infrastructure/role.ts. - eslint: contracts import-ban + @typescript-eslint/no-restricted-imports on api-client (value-only; type imports permitted; infra + shared/upload exempt). Also fixes a PRE-EXISTING bug found while verifying the flow: change-request-form never imported FormsModule, so (ngSubmit) didn't bind and the submit button did a native form submit (page reload) instead of submitting. Verified end-to-end in the running app: submit → command → adapter → backend → reference, success alert shown. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/backlog/README.md | 2 +- docs/backlog/WP-03-contracts-purity.md | 15 +- documentation.json | 4081 +++++++++-------- eslint.config.mjs | 51 + src/app/brief/application/brief.store.ts | 3 +- .../letter-composer.component.ts | 2 +- .../application/big-profile.store.ts | 4 +- src/app/registratie/application/draft-sync.ts | 2 +- .../application/submit-change-request.ts | 24 +- .../contracts/dashboard-view.dto.ts | 61 +- .../infrastructure/change-request.adapter.ts | 23 + .../infrastructure/dashboard-view.adapter.ts | 30 +- .../change-request-form.component.ts | 15 +- src/app/shared/domain/role.ts | 7 + src/app/shared/infrastructure/role.ts | 17 +- 15 files changed, 2358 insertions(+), 1979 deletions(-) create mode 100644 src/app/registratie/infrastructure/change-request.adapter.ts create mode 100644 src/app/shared/domain/role.ts diff --git a/docs/backlog/README.md b/docs/backlog/README.md index 39cf737..9ec1f13 100644 --- a/docs/backlog/README.md +++ b/docs/backlog/README.md @@ -42,7 +42,7 @@ for its existing violations, so every WP ends green. |----|-------|-------|--------| | [WP-01](WP-01-axe-ci-gate.md) | Axe-on-every-story CI gate | 0 · gates | done | | [WP-02](WP-02-check-tokens.md) | Harden `check:tokens` + fix what it catches | 0 · gates | done | -| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | todo | +| [WP-03](WP-03-contracts-purity.md) | Boundaries I: contracts purity + ApiClient confinement | 0 · gates | done | | [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | todo | | [WP-05](WP-05-parse-boundaries.md) | Parse-don't-validate closure + MDX | 1 · FP/DDD | todo | | [WP-06](WP-06-typed-async.md) | Generic async template contexts — kill `$any()` | 1 · FP/DDD | todo | diff --git a/docs/backlog/WP-03-contracts-purity.md b/docs/backlog/WP-03-contracts-purity.md index 12b9e76..da83b89 100644 --- a/docs/backlog/WP-03-contracts-purity.md +++ b/docs/backlog/WP-03-contracts-purity.md @@ -1,6 +1,6 @@ # WP-03 — Boundaries I: contracts purity + ApiClient confinement -Status: todo +Status: done (pending commit) Phase: 0 — enforcement & gates ## Why @@ -56,11 +56,14 @@ lint-enforced. Rule + fixes land together so this WP ends green. ## Acceptance criteria -- [ ] `dashboard-view.dto.ts` has no import statements. -- [ ] `grep -rn "api-client" src/app --include="*.ts" | grep -v infrastructure` returns - only the generated client itself (and its provider wiring, if outside infra). -- [ ] Both eslint rules active; a planted violation fails lint. -- [ ] Change-request flow works (existing machine/command specs pass). +- [x] `dashboard-view.dto.ts` has no import statements. +- [x] No value import of `@shared/infrastructure/api-client` outside `infrastructure/` + (the only remaining non-infra reference is draft-sync's type-only DTO import, + allowed by `allowTypeImports`). ApiClient confinement is lint-enforced. +- [x] Both eslint rules active; planted violations fail lint (contracts→domain, + UI→ApiClient value); type-only DTO import still passes. +- [x] Change-request flow works — verified end-to-end in the running app (submit → + command → adapter → backend → reference `BIG-2026-…`, success alert); specs green. ## Verification diff --git a/documentation.json b/documentation.json index ef5264a..9b683e5 100644 --- a/documentation.json +++ b/documentation.json @@ -372,6 +372,54 @@ "methods": [], "extends": [] }, + { + "name": "AdresDto", + "id": "interface-AdresDto-10d81af9017230c2e0704c882140be892d1e2a8d6343f8d169bcdb8e99516bc135a9b17faf4ea0fb5d4da561476db228b7f2dab7d27e4c262acdecbe1b7f6241-1", + "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "export type RegistrationStatusDto =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\nexport interface RegistrationDto {\n bigNummer: string;\n naam: string;\n beroep: string;\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatusDto;\n}\n\nexport interface AdresDto {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface PersonDto {\n naam: string;\n geboortedatum: string; // ISO date\n adres: AdresDto;\n}\n\n/** Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\n the eligibility rule lives on the backend; the optional reason lets the UI\n explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\nexport interface DashboardViewDto {\n registration: RegistrationDto;\n person: PersonDto;\n decisions: HerregistratieDecisions;\n}\n", + "properties": [ + { + "name": "postcode", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 37 + }, + { + "name": "straat", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 36 + }, + { + "name": "woonplaats", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 38 + } + ], + "indexSignatures": [], + "kind": 172, + "methods": [], + "extends": [], + "isDuplicate": true, + "duplicateId": 1, + "duplicateName": "AdresDto-1" + }, { "name": "AdresValue", "id": "interface-AdresValue-486bc51fe6e0580e73e1eaff7fadd0391ce7111e5b66294eff93f0f0dc84f19a80abb6dd269909450a94d13a2d7d24aa3b167dbe8472717ecd4208f00c5ae6bc", @@ -1122,7 +1170,42 @@ }, { "name": "BrpAddressDto", - "id": "interface-BrpAddressDto-029e98a059f908b1aab86b91327ec0c2a409efb766692f042935997cd36e685859ecf375e579b912496b2dac205dfd2c751f3a301b7486f0da9cb04cda7c704f", + "id": "interface-BrpAddressDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", + "file": "src/app/shared/infrastructure/api-client.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "export class ApiClient {\n private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };\n private baseUrl: string;\n protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;\n\n constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {\n this.http = http ? http : window as any;\n this.baseUrl = baseUrl ?? \"\";\n }\n\n /**\n * @return OK\n */\n health(): Promise {\n let url_ = this.baseUrl + \"/health\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHealth(_response);\n });\n }\n\n protected processHealth(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n ready(): Promise {\n let url_ = this.baseUrl + \"/health/ready\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReady(_response);\n });\n }\n\n protected processReady(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n dashboardView(): Promise {\n let url_ = this.baseUrl + \"/api/v1/dashboard-view\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDashboardView(_response);\n });\n }\n\n protected processDashboardView(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DashboardViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n notes(): Promise {\n let url_ = this.baseUrl + \"/api/v1/notes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processNotes(_response);\n });\n }\n\n protected processNotes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AantekeningDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n address(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brp/address\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processAddress(_response);\n });\n }\n\n protected processAddress(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BrpAddressDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n diplomas(): Promise {\n let url_ = this.baseUrl + \"/api/v1/duo/diplomas\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDiplomas(_response);\n });\n }\n\n protected processDiplomas(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DuoLookupDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n policy(): Promise {\n let url_ = this.baseUrl + \"/api/v1/intake/policy\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processPolicy(_response);\n });\n }\n\n protected processPolicy(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as IntakePolicyDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n registrations(body: RegistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/registrations\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processRegistrations(_response);\n });\n }\n\n protected processRegistrations(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n herregistraties(body: HerregistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/herregistraties\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHerregistraties(_response);\n });\n }\n\n protected processHerregistraties(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n intakes(body: IntakeRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/intakes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processIntakes(_response);\n });\n }\n\n protected processIntakes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n changeRequests(body: ChangeRequestRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/change-requests\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processChangeRequests(_response);\n });\n }\n\n protected processChangeRequests(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param diplomaHerkomst (optional) \n * @param taalvaardigheid (optional) \n * @return OK\n */\n categories(wizardId: string, diplomaHerkomst?: string | undefined, taalvaardigheid?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/categories?\";\n if (wizardId === undefined || wizardId === null)\n throw new globalThis.Error(\"The parameter 'wizardId' must be defined and cannot be null.\");\n else\n url_ += \"wizardId=\" + encodeURIComponent(\"\" + wizardId) + \"&\";\n if (diplomaHerkomst === null)\n throw new globalThis.Error(\"The parameter 'diplomaHerkomst' cannot be null.\");\n else if (diplomaHerkomst !== undefined)\n url_ += \"diplomaHerkomst=\" + encodeURIComponent(\"\" + diplomaHerkomst) + \"&\";\n if (taalvaardigheid === null)\n throw new globalThis.Error(\"The parameter 'taalvaardigheid' cannot be null.\");\n else if (taalvaardigheid !== undefined)\n url_ += \"taalvaardigheid=\" + encodeURIComponent(\"\" + taalvaardigheid) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processCategories(_response);\n });\n }\n\n protected processCategories(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadCategoriesDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n content(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}/content\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processContent(_response);\n });\n }\n\n protected processContent(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param localIds (optional) \n * @return OK\n */\n status(localIds?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/status?\";\n if (localIds === null)\n throw new globalThis.Error(\"The parameter 'localIds' cannot be null.\");\n else if (localIds !== undefined)\n url_ += \"localIds=\" + encodeURIComponent(\"\" + localIds) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processStatus(_response);\n });\n }\n\n protected processStatus(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadStatusDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads(_response);\n });\n }\n\n protected processUploads(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads2(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/admin/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads2(_response);\n });\n }\n\n protected processUploads2(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n return throwException(\"Forbidden\", status, _responseText, _headers);\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsAll(): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsAll(_response);\n });\n }\n\n protected processApplicationsAll(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return Created\n */\n applicationsPOST(body: CreateApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPOST(_response);\n });\n }\n\n protected processApplicationsPOST(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 201) {\n return response.text().then((_responseText) => {\n let result201: any = null;\n result201 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result201;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsGET(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsGET(_response);\n });\n }\n\n protected processApplicationsGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsPUT(id: string, body: DraftSyncRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPUT(_response);\n });\n }\n\n protected processApplicationsPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsDELETE(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsDELETE(_response);\n });\n }\n\n protected processApplicationsDELETE(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n submit(id: string, body: SubmitApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}/submit\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSubmit(_response);\n });\n }\n\n protected processSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as SubmitApplicationResponse;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefGET(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefGET(_response);\n });\n }\n\n protected processBriefGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefPUT(body: SaveBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefPUT(_response);\n });\n }\n\n protected processBriefPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefSubmit(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/submit\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefSubmit(_response);\n });\n }\n\n protected processBriefSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n approve(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/approve\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApprove(_response);\n });\n }\n\n protected processApprove(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n reject(body: RejectBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reject\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReject(_response);\n });\n }\n\n protected processReject(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n send(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/send\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSend(_response);\n });\n }\n\n protected processSend(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefReset(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reset\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefReset(_response);\n });\n }\n\n protected processBriefReset(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n}\n\nexport interface AantekeningDto {\n type?: string | undefined;\n omschrijving?: string | undefined;\n datum?: string | undefined;\n}\n\nexport interface AanvraagStatusDto {\n tag?: string | undefined;\n stepIndex?: number | undefined;\n stepCount?: number | undefined;\n referentie?: string | undefined;\n manual?: boolean | undefined;\n reden?: string | undefined;\n}\n\nexport interface AdresDto {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface ApplicationDetailDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n draft?: any | undefined;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface ApplicationSummaryDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface BriefDto {\n briefId?: string | undefined;\n beroep?: string | undefined;\n templateId?: string | undefined;\n placeholders?: PlaceholderDefDto[] | undefined;\n sections?: LetterSectionDto[] | undefined;\n status?: BriefStatusDto;\n drafterId?: string | undefined;\n}\n\nexport interface BriefStatusDto {\n tag?: string | undefined;\n submittedBy?: string | undefined;\n submittedAt?: string | undefined;\n approvedBy?: string | undefined;\n approvedAt?: string | undefined;\n rejectedBy?: string | undefined;\n rejectedAt?: string | undefined;\n comments?: string | undefined;\n sentAt?: string | undefined;\n}\n\nexport interface BriefViewDto {\n brief?: BriefDto;\n availablePassages?: LibraryPassageDto[] | undefined;\n}\n\nexport interface BrpAddressDto {\n gevonden?: boolean;\n adres?: AdresDto;\n}\n\nexport interface ChangeRequestRequest {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface CreateApplicationRequest {\n type?: string | undefined;\n}\n\nexport interface DashboardViewDto {\n registration?: RegistrationDto;\n person?: PersonDto;\n decisions?: HerregistratieDecisionsDto;\n}\n\nexport interface DocumentCategoryDto {\n categoryId?: string | undefined;\n label?: string | undefined;\n description?: string | undefined;\n required?: boolean;\n acceptedTypes?: string[] | undefined;\n maxSizeMb?: number;\n multiple?: boolean;\n allowPostDelivery?: boolean;\n}\n\nexport interface DocumentRefDto {\n categoryId?: string | undefined;\n channel?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport interface DraftSyncRequest {\n draft?: any;\n stepIndex?: number;\n stepCount?: number;\n documentIds?: string[] | undefined;\n}\n\nexport interface DuoDiplomaDto {\n id?: string | undefined;\n naam?: string | undefined;\n instelling?: string | undefined;\n jaar?: number;\n beroep?: string | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface DuoLookupDto {\n diplomas?: DuoDiplomaDto[] | undefined;\n handmatig?: ManualDiplomaPolicyDto;\n}\n\nexport interface HerregistratieDecisionsDto {\n eligibleForHerregistratie?: boolean;\n herregistratieReason?: string | undefined;\n}\n\nexport interface HerregistratieRequest {\n uren?: number;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface IntakePolicyDto {\n scholingThreshold?: number;\n}\n\nexport interface IntakeRequest {\n uren?: number;\n}\n\nexport interface LetterBlockDto {\n type?: string | undefined;\n blockId?: string | undefined;\n content?: RichTextBlockDto;\n sourcePassageId?: string | undefined;\n sourceVersion?: number | undefined;\n edited?: boolean | undefined;\n}\n\nexport interface LetterSectionDto {\n sectionKey?: string | undefined;\n title?: string | undefined;\n required?: boolean;\n blocks?: LetterBlockDto[] | undefined;\n locked?: boolean;\n}\n\nexport interface LibraryPassageDto {\n passageId?: string | undefined;\n scope?: string | undefined;\n sectionKey?: string | undefined;\n label?: string | undefined;\n content?: RichTextBlockDto;\n version?: number;\n beroep?: string | undefined;\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen?: string[] | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface ParagraphDto {\n nodes?: RichTextNodeDto[] | undefined;\n list?: string | undefined;\n}\n\nexport interface PersonDto {\n naam?: string | undefined;\n geboortedatum?: string | undefined;\n adres?: AdresDto;\n}\n\nexport interface PlaceholderDefDto {\n key?: string | undefined;\n label?: string | undefined;\n autoResolvable?: boolean;\n fillable?: boolean | undefined;\n deprecated?: boolean | undefined;\n}\n\nexport interface PolicyQuestionDto {\n id?: string | undefined;\n vraag?: string | undefined;\n type?: string | undefined;\n}\n\nexport interface ProblemDetails {\n type?: string | undefined;\n title?: string | undefined;\n status?: number | undefined;\n detail?: string | undefined;\n instance?: string | undefined;\n\n [key: string]: any;\n}\n\nexport interface ReferentieResponse {\n referentie?: string | undefined;\n}\n\nexport interface RegistratieRequest {\n diplomaHerkomst?: string | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface RegistrationDto {\n bigNummer?: string | undefined;\n naam?: string | undefined;\n beroep?: string | undefined;\n registratiedatum?: string | undefined;\n geboortedatum?: string | undefined;\n status?: RegistrationStatusDto;\n}\n\nexport interface RegistrationStatusDto {\n tag?: string | undefined;\n herregistratieDatum?: string | undefined;\n geschorstTot?: string | undefined;\n reden?: string | undefined;\n doorgehaaldOp?: string | undefined;\n}\n\nexport interface RejectBriefRequest {\n comments?: string | undefined;\n}\n\nexport interface RichTextBlockDto {\n paragraphs?: ParagraphDto[] | undefined;\n}\n\nexport interface RichTextNodeDto {\n type?: string | undefined;\n text?: string | undefined;\n marks?: string[] | undefined;\n key?: string | undefined;\n}\n\nexport interface SaveBriefRequest {\n sections?: LetterSectionDto[] | undefined;\n}\n\nexport interface SubmitApplicationRequest {\n diplomaHerkomst?: string | undefined;\n uren?: number | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface SubmitApplicationResponse {\n referentie?: string | undefined;\n status?: AanvraagStatusDto;\n}\n\nexport interface UploadCategoriesDto {\n categories?: DocumentCategoryDto[] | undefined;\n}\n\nexport interface UploadStatusDto {\n results?: UploadStatusItemDto[] | undefined;\n}\n\nexport interface UploadStatusItemDto {\n localId?: string | undefined;\n status?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport class SwaggerException extends Error {\n override message: string;\n status: number;\n response: string;\n headers: { [key: string]: any; };\n result: any;\n\n constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {\n super();\n\n this.message = message;\n this.status = status;\n this.response = response;\n this.headers = headers;\n this.result = result;\n }\n\n protected isSwaggerException = true;\n\n static isSwaggerException(obj: any): obj is SwaggerException {\n return obj.isSwaggerException === true;\n }\n}\n\nfunction throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {\n if (result !== null && result !== undefined)\n throw result;\n else\n throw new SwaggerException(message, status, response, headers, null);\n}", + "properties": [ + { + "name": "adres", + "deprecated": false, + "deprecationMessage": "", + "type": "AdresDto", + "indexKey": "", + "optional": true, + "description": "", + "line": 1319 + }, + { + "name": "gevonden", + "deprecated": false, + "deprecationMessage": "", + "type": "boolean", + "indexKey": "", + "optional": true, + "description": "", + "line": 1318 + } + ], + "indexSignatures": [], + "kind": 172, + "methods": [], + "extends": [] + }, + { + "name": "BrpAddressDto", + "id": "interface-BrpAddressDto-029e98a059f908b1aab86b91327ec0c2a409efb766692f042935997cd36e685859ecf375e579b912496b2dac205dfd2c751f3a301b7486f0da9cb04cda7c704f-1", "file": "src/app/registratie/contracts/brp-address.dto.ts", "deprecated": false, "deprecationMessage": "", @@ -1155,41 +1238,6 @@ "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": "BrpAddressDto", - "id": "interface-BrpAddressDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b-1", - "file": "src/app/shared/infrastructure/api-client.ts", - "deprecated": false, - "deprecationMessage": "", - "type": "interface", - "sourceCode": "export class ApiClient {\n private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };\n private baseUrl: string;\n protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;\n\n constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {\n this.http = http ? http : window as any;\n this.baseUrl = baseUrl ?? \"\";\n }\n\n /**\n * @return OK\n */\n health(): Promise {\n let url_ = this.baseUrl + \"/health\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHealth(_response);\n });\n }\n\n protected processHealth(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n ready(): Promise {\n let url_ = this.baseUrl + \"/health/ready\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReady(_response);\n });\n }\n\n protected processReady(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n dashboardView(): Promise {\n let url_ = this.baseUrl + \"/api/v1/dashboard-view\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDashboardView(_response);\n });\n }\n\n protected processDashboardView(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DashboardViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n notes(): Promise {\n let url_ = this.baseUrl + \"/api/v1/notes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processNotes(_response);\n });\n }\n\n protected processNotes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AantekeningDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n address(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brp/address\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processAddress(_response);\n });\n }\n\n protected processAddress(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BrpAddressDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n diplomas(): Promise {\n let url_ = this.baseUrl + \"/api/v1/duo/diplomas\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDiplomas(_response);\n });\n }\n\n protected processDiplomas(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DuoLookupDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n policy(): Promise {\n let url_ = this.baseUrl + \"/api/v1/intake/policy\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processPolicy(_response);\n });\n }\n\n protected processPolicy(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as IntakePolicyDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n registrations(body: RegistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/registrations\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processRegistrations(_response);\n });\n }\n\n protected processRegistrations(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n herregistraties(body: HerregistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/herregistraties\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHerregistraties(_response);\n });\n }\n\n protected processHerregistraties(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n intakes(body: IntakeRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/intakes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processIntakes(_response);\n });\n }\n\n protected processIntakes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n changeRequests(body: ChangeRequestRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/change-requests\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processChangeRequests(_response);\n });\n }\n\n protected processChangeRequests(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param diplomaHerkomst (optional) \n * @param taalvaardigheid (optional) \n * @return OK\n */\n categories(wizardId: string, diplomaHerkomst?: string | undefined, taalvaardigheid?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/categories?\";\n if (wizardId === undefined || wizardId === null)\n throw new globalThis.Error(\"The parameter 'wizardId' must be defined and cannot be null.\");\n else\n url_ += \"wizardId=\" + encodeURIComponent(\"\" + wizardId) + \"&\";\n if (diplomaHerkomst === null)\n throw new globalThis.Error(\"The parameter 'diplomaHerkomst' cannot be null.\");\n else if (diplomaHerkomst !== undefined)\n url_ += \"diplomaHerkomst=\" + encodeURIComponent(\"\" + diplomaHerkomst) + \"&\";\n if (taalvaardigheid === null)\n throw new globalThis.Error(\"The parameter 'taalvaardigheid' cannot be null.\");\n else if (taalvaardigheid !== undefined)\n url_ += \"taalvaardigheid=\" + encodeURIComponent(\"\" + taalvaardigheid) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processCategories(_response);\n });\n }\n\n protected processCategories(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadCategoriesDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n content(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}/content\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processContent(_response);\n });\n }\n\n protected processContent(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param localIds (optional) \n * @return OK\n */\n status(localIds?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/status?\";\n if (localIds === null)\n throw new globalThis.Error(\"The parameter 'localIds' cannot be null.\");\n else if (localIds !== undefined)\n url_ += \"localIds=\" + encodeURIComponent(\"\" + localIds) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processStatus(_response);\n });\n }\n\n protected processStatus(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadStatusDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads(_response);\n });\n }\n\n protected processUploads(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads2(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/admin/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads2(_response);\n });\n }\n\n protected processUploads2(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n return throwException(\"Forbidden\", status, _responseText, _headers);\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsAll(): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsAll(_response);\n });\n }\n\n protected processApplicationsAll(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return Created\n */\n applicationsPOST(body: CreateApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPOST(_response);\n });\n }\n\n protected processApplicationsPOST(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 201) {\n return response.text().then((_responseText) => {\n let result201: any = null;\n result201 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result201;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsGET(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsGET(_response);\n });\n }\n\n protected processApplicationsGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsPUT(id: string, body: DraftSyncRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPUT(_response);\n });\n }\n\n protected processApplicationsPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsDELETE(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsDELETE(_response);\n });\n }\n\n protected processApplicationsDELETE(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n submit(id: string, body: SubmitApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}/submit\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSubmit(_response);\n });\n }\n\n protected processSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as SubmitApplicationResponse;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefGET(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefGET(_response);\n });\n }\n\n protected processBriefGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefPUT(body: SaveBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefPUT(_response);\n });\n }\n\n protected processBriefPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefSubmit(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/submit\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefSubmit(_response);\n });\n }\n\n protected processBriefSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n approve(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/approve\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApprove(_response);\n });\n }\n\n protected processApprove(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n reject(body: RejectBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reject\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReject(_response);\n });\n }\n\n protected processReject(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n send(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/send\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSend(_response);\n });\n }\n\n protected processSend(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefReset(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reset\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefReset(_response);\n });\n }\n\n protected processBriefReset(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n}\n\nexport interface AantekeningDto {\n type?: string | undefined;\n omschrijving?: string | undefined;\n datum?: string | undefined;\n}\n\nexport interface AanvraagStatusDto {\n tag?: string | undefined;\n stepIndex?: number | undefined;\n stepCount?: number | undefined;\n referentie?: string | undefined;\n manual?: boolean | undefined;\n reden?: string | undefined;\n}\n\nexport interface AdresDto {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface ApplicationDetailDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n draft?: any | undefined;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface ApplicationSummaryDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface BriefDto {\n briefId?: string | undefined;\n beroep?: string | undefined;\n templateId?: string | undefined;\n placeholders?: PlaceholderDefDto[] | undefined;\n sections?: LetterSectionDto[] | undefined;\n status?: BriefStatusDto;\n drafterId?: string | undefined;\n}\n\nexport interface BriefStatusDto {\n tag?: string | undefined;\n submittedBy?: string | undefined;\n submittedAt?: string | undefined;\n approvedBy?: string | undefined;\n approvedAt?: string | undefined;\n rejectedBy?: string | undefined;\n rejectedAt?: string | undefined;\n comments?: string | undefined;\n sentAt?: string | undefined;\n}\n\nexport interface BriefViewDto {\n brief?: BriefDto;\n availablePassages?: LibraryPassageDto[] | undefined;\n}\n\nexport interface BrpAddressDto {\n gevonden?: boolean;\n adres?: AdresDto;\n}\n\nexport interface ChangeRequestRequest {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface CreateApplicationRequest {\n type?: string | undefined;\n}\n\nexport interface DashboardViewDto {\n registration?: RegistrationDto;\n person?: PersonDto;\n decisions?: HerregistratieDecisionsDto;\n}\n\nexport interface DocumentCategoryDto {\n categoryId?: string | undefined;\n label?: string | undefined;\n description?: string | undefined;\n required?: boolean;\n acceptedTypes?: string[] | undefined;\n maxSizeMb?: number;\n multiple?: boolean;\n allowPostDelivery?: boolean;\n}\n\nexport interface DocumentRefDto {\n categoryId?: string | undefined;\n channel?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport interface DraftSyncRequest {\n draft?: any;\n stepIndex?: number;\n stepCount?: number;\n documentIds?: string[] | undefined;\n}\n\nexport interface DuoDiplomaDto {\n id?: string | undefined;\n naam?: string | undefined;\n instelling?: string | undefined;\n jaar?: number;\n beroep?: string | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface DuoLookupDto {\n diplomas?: DuoDiplomaDto[] | undefined;\n handmatig?: ManualDiplomaPolicyDto;\n}\n\nexport interface HerregistratieDecisionsDto {\n eligibleForHerregistratie?: boolean;\n herregistratieReason?: string | undefined;\n}\n\nexport interface HerregistratieRequest {\n uren?: number;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface IntakePolicyDto {\n scholingThreshold?: number;\n}\n\nexport interface IntakeRequest {\n uren?: number;\n}\n\nexport interface LetterBlockDto {\n type?: string | undefined;\n blockId?: string | undefined;\n content?: RichTextBlockDto;\n sourcePassageId?: string | undefined;\n sourceVersion?: number | undefined;\n edited?: boolean | undefined;\n}\n\nexport interface LetterSectionDto {\n sectionKey?: string | undefined;\n title?: string | undefined;\n required?: boolean;\n blocks?: LetterBlockDto[] | undefined;\n locked?: boolean;\n}\n\nexport interface LibraryPassageDto {\n passageId?: string | undefined;\n scope?: string | undefined;\n sectionKey?: string | undefined;\n label?: string | undefined;\n content?: RichTextBlockDto;\n version?: number;\n beroep?: string | undefined;\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen?: string[] | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface ParagraphDto {\n nodes?: RichTextNodeDto[] | undefined;\n list?: string | undefined;\n}\n\nexport interface PersonDto {\n naam?: string | undefined;\n geboortedatum?: string | undefined;\n adres?: AdresDto;\n}\n\nexport interface PlaceholderDefDto {\n key?: string | undefined;\n label?: string | undefined;\n autoResolvable?: boolean;\n fillable?: boolean | undefined;\n deprecated?: boolean | undefined;\n}\n\nexport interface PolicyQuestionDto {\n id?: string | undefined;\n vraag?: string | undefined;\n type?: string | undefined;\n}\n\nexport interface ProblemDetails {\n type?: string | undefined;\n title?: string | undefined;\n status?: number | undefined;\n detail?: string | undefined;\n instance?: string | undefined;\n\n [key: string]: any;\n}\n\nexport interface ReferentieResponse {\n referentie?: string | undefined;\n}\n\nexport interface RegistratieRequest {\n diplomaHerkomst?: string | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface RegistrationDto {\n bigNummer?: string | undefined;\n naam?: string | undefined;\n beroep?: string | undefined;\n registratiedatum?: string | undefined;\n geboortedatum?: string | undefined;\n status?: RegistrationStatusDto;\n}\n\nexport interface RegistrationStatusDto {\n tag?: string | undefined;\n herregistratieDatum?: string | undefined;\n geschorstTot?: string | undefined;\n reden?: string | undefined;\n doorgehaaldOp?: string | undefined;\n}\n\nexport interface RejectBriefRequest {\n comments?: string | undefined;\n}\n\nexport interface RichTextBlockDto {\n paragraphs?: ParagraphDto[] | undefined;\n}\n\nexport interface RichTextNodeDto {\n type?: string | undefined;\n text?: string | undefined;\n marks?: string[] | undefined;\n key?: string | undefined;\n}\n\nexport interface SaveBriefRequest {\n sections?: LetterSectionDto[] | undefined;\n}\n\nexport interface SubmitApplicationRequest {\n diplomaHerkomst?: string | undefined;\n uren?: number | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface SubmitApplicationResponse {\n referentie?: string | undefined;\n status?: AanvraagStatusDto;\n}\n\nexport interface UploadCategoriesDto {\n categories?: DocumentCategoryDto[] | undefined;\n}\n\nexport interface UploadStatusDto {\n results?: UploadStatusItemDto[] | undefined;\n}\n\nexport interface UploadStatusItemDto {\n localId?: string | undefined;\n status?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport class SwaggerException extends Error {\n override message: string;\n status: number;\n response: string;\n headers: { [key: string]: any; };\n result: any;\n\n constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {\n super();\n\n this.message = message;\n this.status = status;\n this.response = response;\n this.headers = headers;\n this.result = result;\n }\n\n protected isSwaggerException = true;\n\n static isSwaggerException(obj: any): obj is SwaggerException {\n return obj.isSwaggerException === true;\n }\n}\n\nfunction throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {\n if (result !== null && result !== undefined)\n throw result;\n else\n throw new SwaggerException(message, status, response, headers, null);\n}", - "properties": [ - { - "name": "adres", - "deprecated": false, - "deprecationMessage": "", - "type": "AdresDto", - "indexKey": "", - "optional": true, - "description": "", - "line": 1319 - }, - { - "name": "gevonden", - "deprecated": false, - "deprecationMessage": "", - "type": "boolean", - "indexKey": "", - "optional": true, - "description": "", - "line": 1318 - } - ], - "indexSignatures": [], - "kind": 172, - "methods": [], "extends": [], "isDuplicate": true, "duplicateId": 1, @@ -1341,12 +1389,12 @@ }, { "name": "DashboardView", - "id": "interface-DashboardView-b39e266ec8fabdaddad87f1e15a5b1b86e45d84ac2c95028b4e676c2eb425fd675c77383c3d4f8f88f4a072f3eb88a954eb0f8755597224ebef59da18d24fda0", - "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "id": "interface-DashboardView-c58fb64e1122f4fd879cc04af00e47d5f21ebcd704c19b3ee6ff824dc42bc58cbaf30d615093402c466690d62d19c307b82fa5afbff32e4feeadf419e21ac189", + "file": "src/app/registratie/infrastructure/dashboard-view.adapter.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", + "sourceCode": "import { Injectable, inject, resource } from '@angular/core';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { DashboardViewDto, HerregistratieDecisions } from '@registratie/contracts/dashboard-view.dto';\nimport { Registration } from '@registratie/domain/registration';\nimport { Person } from '@registratie/domain/person';\nimport { BigProfile } from '@registratie/domain/big-profile';\nimport { ApiClient } from '@shared/infrastructure/api-client';\n\n/**\n * The parsed, frontend-side view: the wire DTO mapped onto our own domain model.\n * Lives HERE, not in contracts/, because it references domain types — contracts\n * stays import-free. This split is the decoupling seam (CLAUDE.md §1, ADR-0001).\n */\nexport interface DashboardView {\n profile: BigProfile;\n decisions: HerregistratieDecisions;\n}\n\n/**\n * Infrastructure adapter for the screen-shaped (\"BFF-lite\") dashboard endpoint.\n * ONE call returns registration + person + server-computed decisions. The data\n * comes from the .NET backend (`GET /api/dashboard-view`) via the generated typed\n * client; the decisions (e.g. herregistratie eligibility) are computed there.\n */\n@Injectable({ providedIn: 'root' })\nexport class DashboardViewAdapter {\n private client = inject(ApiClient);\n\n // The value is still untrusted JSON — parseDashboardView validates it at the\n // boundary and maps DTO → domain before the app uses it.\n //\n // SEAM (G5): retry-with-backoff for non-mutating reads wraps the loader here —\n // e.g. `loader: () => withBackoff(() => this.client.dashboardView())` — since the\n // adapter is the single place HTTP lives. Reads are safe to retry; MUTATING calls\n // (the submit-* commands) must NEVER auto-retry — and don't. Manual retry\n // (resource.reload via ) covers the UX today, so backoff stays unbuilt.\n dashboardViewResource() {\n return resource({ loader: () => this.client.dashboardView() });\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 // Map wire → domain. The shapes are identical today, so this reads as an\n // identity copy — but the TYPES differ (wire DTO vs domain), so the moment the\n // wire diverges the compiler forces a real mapping here. That's the seam.\n const registration: Registration = {\n bigNummer: reg.bigNummer,\n naam: reg.naam,\n beroep: reg.beroep,\n registratiedatum: reg.registratiedatum,\n geboortedatum: reg.geboortedatum,\n status: reg.status,\n };\n const persoon: Person = { naam: person.naam, geboortedatum: person.geboortedatum, adres: person.adres };\n\n return ok({\n profile: { registration, person: persoon },\n decisions: { eligibleForHerregistratie: d.eligibleForHerregistratie, herregistratieReason: d.herregistratieReason },\n });\n}\n", "properties": [ { "name": "decisions", @@ -1356,7 +1404,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 35 + "line": 16 }, { "name": "profile", @@ -1366,66 +1414,19 @@ "indexKey": "", "optional": false, "description": "", - "line": 34 + "line": 15 } ], "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.", + "description": "

The parsed, frontend-side view: the wire DTO mapped onto our own domain model.\nLives HERE, not in contracts/, because it references domain types — contracts\nstays import-free. This split is the decoupling seam (CLAUDE.md §1, ADR-0001).

\n", + "rawdescription": "\n\nThe parsed, frontend-side view: the wire DTO mapped onto our own domain model.\nLives HERE, not in contracts/, because it references domain types — contracts\nstays import-free. This split is the decoupling seam (CLAUDE.md §1, ADR-0001).\n", "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": "DashboardViewDto", - "id": "interface-DashboardViewDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b-1", + "id": "interface-DashboardViewDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", "file": "src/app/shared/infrastructure/api-client.ts", "deprecated": false, "deprecationMessage": "", @@ -1466,6 +1467,51 @@ "indexSignatures": [], "kind": 172, "methods": [], + "extends": [] + }, + { + "name": "DashboardViewDto", + "id": "interface-DashboardViewDto-10d81af9017230c2e0704c882140be892d1e2a8d6343f8d169bcdb8e99516bc135a9b17faf4ea0fb5d4da561476db228b7f2dab7d27e4c262acdecbe1b7f6241-1", + "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "export type RegistrationStatusDto =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\nexport interface RegistrationDto {\n bigNummer: string;\n naam: string;\n beroep: string;\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatusDto;\n}\n\nexport interface AdresDto {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface PersonDto {\n naam: string;\n geboortedatum: string; // ISO date\n adres: AdresDto;\n}\n\n/** Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\n the eligibility rule lives on the backend; the optional reason lets the UI\n explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\nexport interface DashboardViewDto {\n registration: RegistrationDto;\n person: PersonDto;\n decisions: HerregistratieDecisions;\n}\n", + "properties": [ + { + "name": "decisions", + "deprecated": false, + "deprecationMessage": "", + "type": "HerregistratieDecisions", + "indexKey": "", + "optional": false, + "description": "", + "line": 58 + }, + { + "name": "person", + "deprecated": false, + "deprecationMessage": "", + "type": "PersonDto", + "indexKey": "", + "optional": false, + "description": "", + "line": 57 + }, + { + "name": "registration", + "deprecated": false, + "deprecationMessage": "", + "type": "RegistrationDto", + "indexKey": "", + "optional": false, + "description": "", + "line": 56 + } + ], + "indexSignatures": [], + "kind": 172, + "methods": [], "extends": [], "isDuplicate": true, "duplicateId": 1, @@ -1842,54 +1888,7 @@ }, { "name": "Draft", - "id": "interface-Draft-f2c910e7845c4a1e95cc75170624db8664de0937455edc1bf8423b6389c310b7ef9ea149dc07ea49f0a4ecad039dbaf09b226864660b29dce075e717125dab0d", - "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';\nimport {\n UploadState,\n UploadMsg,\n initialUpload,\n reduceUpload,\n requiredCategoriesSatisfied,\n deliveryRefs,\n} from '@shared/upload/upload.machine';\n\n/** What the user is typing (raw, possibly invalid). */\nexport interface Draft {\n uren: string;\n jaren: string;\n punten: string;\n}\n\nexport type StepErrors = Partial>;\n\n/** What we have AFTER parsing — branded/typed, guaranteed valid. */\nexport interface Valid {\n uren: Uren;\n jaren: number;\n punten: number;\n documents: Array<{ categoryId: string; channel: 'digital' | 'post'; documentId?: string }>;\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 | 3; draft: Draft; errors: StepErrors; upload: UploadState }\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: {}, upload: initialUpload };\n\n/** Has the user meaningfully started, so it's worth persisting as a Concept? */\nexport function hasProgress(s: Extract): boolean {\n return s.step > 1 || !!s.draft.uren || !!s.draft.jaren || !!s.draft.punten || deliveryRefs(s.upload).some((r) => r.channel === 'digital' && !!r.documentId);\n}\n\n/** Parse every field; on success hand back a Valid, else the per-field errors. */\nfunction validate(draft: Draft, upload: UploadState): Result {\n const uren = parseUren(draft.uren);\n const jaren = parseUren(draft.jaren);\n const punten = parseUren(draft.punten);\n const errors: StepErrors = {};\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 (!requiredCategoriesSatisfied(upload)) {\n errors.documenten = $localize`:@@validation.documenten:Lever de verplichte documenten aan (upload of kies \"per post nasturen\").`;\n }\n if (uren.ok && jaren.ok && punten.ok && !errors.documenten) {\n return { ok: true, value: { uren: uren.value, jaren: jaren.value, punten: punten.value, documents: deliveryRefs(upload) } };\n }\n return { ok: false, error: errors };\n}\n\n/** Advance one step, gating on that step's fields. Illegal elsewhere = no-op. */\nexport function next(s: WizardState): WizardState {\n if (s.tag !== 'Editing') return s;\n const errors: StepErrors = {};\n if (s.step === 1) {\n const uren = parseUren(s.draft.uren);\n const jaren = parseUren(s.draft.jaren);\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 if (s.step === 2) {\n const punten = parseUren(s.draft.punten);\n if (!punten.ok) errors.punten = punten.error;\n return punten.ok ? { ...s, step: 3, errors: {} } : { ...s, errors };\n }\n return s;\n}\n\nexport function back(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step === 1) return s;\n return { ...s, step: (s.step - 1) as 1 | 2, 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). */\nexport function gaNaarStap(s: WizardState, step: 1 | 2 | 3): WizardState {\n if (s.tag !== 'Editing' || step >= s.step) return s;\n return { ...s, step, errors: {} };\n}\n\n/** Step 3 submit: parse everything + require documents; Submitting only with Valid. */\nexport function submit(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 3) return s;\n const result = validate(s.draft, s.upload);\n return result.ok ? { tag: 'Submitting', data: result.value } : { ...s, errors: result.error };\n}\n\n/** Route an upload sub-message through the pure upload reducer (Editing only). */\nexport function upload(s: WizardState, msg: UploadMsg): WizardState {\n if (s.tag !== 'Editing') return s;\n return { ...s, upload: reduceUpload(s.upload, msg) };\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: 'GaNaarStap'; step: 1 | 2 | 3 }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed' }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Upload'; msg: UploadMsg }\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 'GaNaarStap':\n return gaNaarStap(s, m.step);\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 'Upload':\n return upload(s, m.msg);\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": 15 - }, - { - "name": "punten", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "indexKey": "", - "optional": false, - "description": "", - "line": 16 - }, - { - "name": "uren", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "indexKey": "", - "optional": false, - "description": "", - "line": 14 - } - ], - "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-69e87ea9a6c36bd372087191f1524450d62f799934711812731fbbd8f034a9e92b7928082471c429b82990c34c47b84b6493735cb994ec6fdfb81b200e990467-1", + "id": "interface-Draft-69e87ea9a6c36bd372087191f1524450d62f799934711812731fbbd8f034a9e92b7928082471c429b82990c34c47b84b6493735cb994ec6fdfb81b200e990467", "file": "src/app/registratie/domain/change-request.machine.ts", "deprecated": false, "deprecationMessage": "", @@ -1932,14 +1931,11 @@ "description": "

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

\n", "rawdescription": "\nWhat the user is typing (raw, possibly invalid).", "methods": [], - "extends": [], - "isDuplicate": true, - "duplicateId": 1, - "duplicateName": "Draft-1" + "extends": [] }, { "name": "Draft", - "id": "interface-Draft-b7c3c5d493aa141ca68cb964e63408db7152b07336b239864d3c430b84d8eb3e650d1a3abaf3fda253444af942c45f86138a5d8229832d49bda94eec35c33546-2", + "id": "interface-Draft-b7c3c5d493aa141ca68cb964e63408db7152b07336b239864d3c430b84d8eb3e650d1a3abaf3fda253444af942c45f86138a5d8229832d49bda94eec35c33546-1", "file": "src/app/registratie/domain/registratie-wizard.machine.ts", "deprecated": false, "deprecationMessage": "", @@ -2064,17 +2060,67 @@ "methods": [], "extends": [], "isDuplicate": true, + "duplicateId": 1, + "duplicateName": "Draft-1" + }, + { + "name": "Draft", + "id": "interface-Draft-f2c910e7845c4a1e95cc75170624db8664de0937455edc1bf8423b6389c310b7ef9ea149dc07ea49f0a4ecad039dbaf09b226864660b29dce075e717125dab0d-2", + "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';\nimport {\n UploadState,\n UploadMsg,\n initialUpload,\n reduceUpload,\n requiredCategoriesSatisfied,\n deliveryRefs,\n} from '@shared/upload/upload.machine';\n\n/** What the user is typing (raw, possibly invalid). */\nexport interface Draft {\n uren: string;\n jaren: string;\n punten: string;\n}\n\nexport type StepErrors = Partial>;\n\n/** What we have AFTER parsing — branded/typed, guaranteed valid. */\nexport interface Valid {\n uren: Uren;\n jaren: number;\n punten: number;\n documents: Array<{ categoryId: string; channel: 'digital' | 'post'; documentId?: string }>;\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 | 3; draft: Draft; errors: StepErrors; upload: UploadState }\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: {}, upload: initialUpload };\n\n/** Has the user meaningfully started, so it's worth persisting as a Concept? */\nexport function hasProgress(s: Extract): boolean {\n return s.step > 1 || !!s.draft.uren || !!s.draft.jaren || !!s.draft.punten || deliveryRefs(s.upload).some((r) => r.channel === 'digital' && !!r.documentId);\n}\n\n/** Parse every field; on success hand back a Valid, else the per-field errors. */\nfunction validate(draft: Draft, upload: UploadState): Result {\n const uren = parseUren(draft.uren);\n const jaren = parseUren(draft.jaren);\n const punten = parseUren(draft.punten);\n const errors: StepErrors = {};\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 (!requiredCategoriesSatisfied(upload)) {\n errors.documenten = $localize`:@@validation.documenten:Lever de verplichte documenten aan (upload of kies \"per post nasturen\").`;\n }\n if (uren.ok && jaren.ok && punten.ok && !errors.documenten) {\n return { ok: true, value: { uren: uren.value, jaren: jaren.value, punten: punten.value, documents: deliveryRefs(upload) } };\n }\n return { ok: false, error: errors };\n}\n\n/** Advance one step, gating on that step's fields. Illegal elsewhere = no-op. */\nexport function next(s: WizardState): WizardState {\n if (s.tag !== 'Editing') return s;\n const errors: StepErrors = {};\n if (s.step === 1) {\n const uren = parseUren(s.draft.uren);\n const jaren = parseUren(s.draft.jaren);\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 if (s.step === 2) {\n const punten = parseUren(s.draft.punten);\n if (!punten.ok) errors.punten = punten.error;\n return punten.ok ? { ...s, step: 3, errors: {} } : { ...s, errors };\n }\n return s;\n}\n\nexport function back(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step === 1) return s;\n return { ...s, step: (s.step - 1) as 1 | 2, 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). */\nexport function gaNaarStap(s: WizardState, step: 1 | 2 | 3): WizardState {\n if (s.tag !== 'Editing' || step >= s.step) return s;\n return { ...s, step, errors: {} };\n}\n\n/** Step 3 submit: parse everything + require documents; Submitting only with Valid. */\nexport function submit(s: WizardState): WizardState {\n if (s.tag !== 'Editing' || s.step !== 3) return s;\n const result = validate(s.draft, s.upload);\n return result.ok ? { tag: 'Submitting', data: result.value } : { ...s, errors: result.error };\n}\n\n/** Route an upload sub-message through the pure upload reducer (Editing only). */\nexport function upload(s: WizardState, msg: UploadMsg): WizardState {\n if (s.tag !== 'Editing') return s;\n return { ...s, upload: reduceUpload(s.upload, msg) };\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: 'GaNaarStap'; step: 1 | 2 | 3 }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed' }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Upload'; msg: UploadMsg }\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 'GaNaarStap':\n return gaNaarStap(s, m.step);\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 'Upload':\n return upload(s, m.msg);\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": 15 + }, + { + "name": "punten", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 16 + }, + { + "name": "uren", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 14 + } + ], + "indexSignatures": [], + "kind": 172, + "description": "

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

\n", + "rawdescription": "\nWhat the user is typing (raw, possibly invalid).", + "methods": [], + "extends": [], + "isDuplicate": true, "duplicateId": 2, "duplicateName": "Draft-2" }, { "name": "DraftSnapshot", - "id": "interface-DraftSnapshot-cc72ba6f5f75034dff8993670c479d720530ec464d0fac4d0e95885e435aebb6fe8bfd0c428423ad9aad3b66f927d61b63ed473008f7f041251dd8248b79464a", + "id": "interface-DraftSnapshot-e575992a23399ee78afe66c219bd8a146ede9c3330705108a5ee488100dd204c54c96e1a8e354dfcd03f8f842249ad8ee59f6bd9967431b700e9050ca058b295", "file": "src/app/registratie/application/draft-sync.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { DestroyRef, effect, inject } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Result } from '@shared/kernel/fp';\nimport { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';\nimport { SubmitApplicationRequest, SubmitApplicationResponse } from '@shared/infrastructure/api-client';\nimport { AanvraagType } from '@registratie/domain/aanvraag';\nimport { ApplicationsAdapter, parseApplications } from '@registratie/infrastructure/applications.adapter';\n\n/** What a wizard persists per step: the opaque machine snapshot + progress + docs. */\nexport interface DraftSnapshot {\n draft: unknown;\n stepIndex: number;\n stepCount: number;\n documentIds: string[];\n}\n\nexport interface DraftSyncDeps {\n type: AanvraagType;\n /** The machine snapshot while it's worth persisting; null when not (pristine/done). */\n snapshot: () => DraftSnapshot | null;\n /** Seed the machine from a resumed draft. Called at most once, on init, and ONLY\n with a real draft on a still-pristine machine — see `applyResume`. */\n onResume: (draft: unknown) => void;\n /** Draft-sync only runs in the real app — false in Storybook/tests (explicit seed). */\n enabled: () => boolean;\n}\n\nconst DEBOUNCE_MS = 600; // ponytail: fixed debounce; tune if the sync feels laggy/chatty.\n\n/**\n * The effectful glue that replaces per-wizard sessionStorage with a backend-owned\n * Concept (PRD 0001, phase D). Instantiated in a field initializer (like\n * `createStore`/`createUploadController`). Responsibilities:\n *\n * - resume: a `?aanvraag=` link wins; otherwise resume the ONE existing Concept of\n * this type (at most one per type), seeding the machine from its saved draft;\n * - create-on-first-progress: when no Concept exists, one is created lazily the first\n * time the wizard reports a non-null snapshot, and its id is stamped into the URL;\n * - debounced draft sync on every subsequent change.\n *\n * Inert without a Router (stories) or when `enabled()` is false — no network, no resume.\n */\nexport function createDraftSync(deps: DraftSyncDeps) {\n const adapter = inject(ApplicationsAdapter);\n const router = inject(Router, { optional: true });\n const route = inject(ActivatedRoute, { optional: true });\n const active = () => deps.enabled() && !!router && !!route;\n\n let id: string | undefined;\n let ensuring: Promise | undefined; // in-flight create, so we never create twice\n let timer: ReturnType | undefined;\n // Resolves once resume() has decided whether a Concept of this type already exists;\n // gates ensureId so a fast typist can't create a duplicate before that lookup lands.\n let resumeGate: Promise = Promise.resolve();\n\n const ensureId = async (): Promise => {\n await resumeGate;\n if (id) return id;\n ensuring ??= adapter.create(deps.type).then((newId) => {\n id = newId;\n // Stamp the id into the URL (no navigation) so a reload resumes this Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: newId }, queryParamsHandling: 'merge', replaceUrl: true });\n return newId;\n });\n return ensuring;\n };\n\n // Apply a resumed draft only when it's safe to: a late lookup must never clobber\n // progress the user already made while it was in flight, and \"start fresh\" needs no\n // dispatch (the machine already starts fresh). snapshot() is non-null once the user\n // has real progress.\n const applyResume = (draft: unknown | null) => {\n if (draft == null || deps.snapshot() != null) return;\n deps.onResume(draft);\n };\n\n const flush = async () => {\n const snap = deps.snapshot();\n if (!snap) return;\n const theId = await ensureId();\n await adapter.syncDraft(theId, { draft: snap.draft, stepIndex: snap.stepIndex, stepCount: snap.stepCount, documentIds: snap.documentIds });\n };\n\n // One effect watches the snapshot; each change resets a debounce timer. The timer's\n // callback only does network I/O (never dispatch), so it can't livelock the store.\n effect(() => {\n if (!active()) return;\n const snap = deps.snapshot(); // tracked: fires on every machine change\n if (!snap) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => void flush(), DEBOUNCE_MS);\n });\n\n inject(DestroyRef).onDestroy(() => timer && clearTimeout(timer));\n\n // Attach to a specific Concept id and seed the machine from its draft. A non-Concept\n // (submitted/gone) id is treated as fresh so it can't reopen as an editable draft.\n const load = (linked: string): Promise => {\n id = linked;\n return adapter\n .detail(linked)\n .then((dto) => {\n if (dto.status && dto.status.tag !== 'Concept') {\n id = undefined;\n applyResume(null);\n return;\n }\n applyResume(dto.draft ?? null);\n })\n .catch(() => {\n id = undefined;\n applyResume(null); // unknown/deleted id → start fresh\n });\n };\n\n // Find the user's existing Concept of this type (at most one), if any.\n const findConcept = async (): Promise => {\n try {\n const parsed = parseApplications(await adapter.list());\n return parsed.ok ? parsed.value.find((a) => a.type === deps.type && a.status.tag === 'Concept')?.id : undefined;\n } catch {\n return undefined;\n }\n };\n\n return {\n /** Resolve the initial state: a `?aanvraag` link wins; else resume this type's\n existing Concept; else start fresh (a Concept is created on first progress). */\n async resume() {\n let release!: () => void;\n resumeGate = new Promise((r) => (release = r));\n try {\n if (!active()) {\n applyResume(null);\n return;\n }\n const linked = route!.snapshot.queryParamMap.get('aanvraag');\n if (linked) {\n await load(linked);\n return;\n }\n const existing = await findConcept();\n if (existing) {\n await load(existing);\n // Stamp the id into the URL so a reload resumes the same Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: existing }, queryParamsHandling: 'merge', replaceUrl: true });\n return;\n }\n applyResume(null);\n } finally {\n release();\n }\n },\n\n /** Submit through the aanvraag lifecycle: ensure the Concept exists, then\n `POST /applications/{id}/submit` (server sets autoApprovable + transitions).\n Folded into a Result like the old submit-* commands. */\n submit(body: SubmitApplicationRequest): Promise> {\n return runSubmit(async () => adapter.submit(await ensureId(), body), SUBMIT_FAILED);\n },\n\n /** Restart: discard the current in-progress Concept (delete it) and detach, so a\n fresh one is created on next progress. Keeps the one-per-type invariant. A\n submitted id can't be deleted (409, caught) — that submission correctly remains,\n and detaching still lets the user start a new Concept. */\n reset() {\n if (id) {\n void adapter.cancel(id).catch(() => {}); // Concept → deleted; submitted → 409, kept\n id = undefined;\n ensuring = undefined;\n }\n if (active()) void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: null }, queryParamsHandling: 'merge', replaceUrl: true });\n },\n };\n}\n", + "sourceCode": "import { DestroyRef, effect, inject } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Result } from '@shared/kernel/fp';\nimport { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';\nimport type { SubmitApplicationRequest, SubmitApplicationResponse } from '@shared/infrastructure/api-client';\nimport { AanvraagType } from '@registratie/domain/aanvraag';\nimport { ApplicationsAdapter, parseApplications } from '@registratie/infrastructure/applications.adapter';\n\n/** What a wizard persists per step: the opaque machine snapshot + progress + docs. */\nexport interface DraftSnapshot {\n draft: unknown;\n stepIndex: number;\n stepCount: number;\n documentIds: string[];\n}\n\nexport interface DraftSyncDeps {\n type: AanvraagType;\n /** The machine snapshot while it's worth persisting; null when not (pristine/done). */\n snapshot: () => DraftSnapshot | null;\n /** Seed the machine from a resumed draft. Called at most once, on init, and ONLY\n with a real draft on a still-pristine machine — see `applyResume`. */\n onResume: (draft: unknown) => void;\n /** Draft-sync only runs in the real app — false in Storybook/tests (explicit seed). */\n enabled: () => boolean;\n}\n\nconst DEBOUNCE_MS = 600; // ponytail: fixed debounce; tune if the sync feels laggy/chatty.\n\n/**\n * The effectful glue that replaces per-wizard sessionStorage with a backend-owned\n * Concept (PRD 0001, phase D). Instantiated in a field initializer (like\n * `createStore`/`createUploadController`). Responsibilities:\n *\n * - resume: a `?aanvraag=` link wins; otherwise resume the ONE existing Concept of\n * this type (at most one per type), seeding the machine from its saved draft;\n * - create-on-first-progress: when no Concept exists, one is created lazily the first\n * time the wizard reports a non-null snapshot, and its id is stamped into the URL;\n * - debounced draft sync on every subsequent change.\n *\n * Inert without a Router (stories) or when `enabled()` is false — no network, no resume.\n */\nexport function createDraftSync(deps: DraftSyncDeps) {\n const adapter = inject(ApplicationsAdapter);\n const router = inject(Router, { optional: true });\n const route = inject(ActivatedRoute, { optional: true });\n const active = () => deps.enabled() && !!router && !!route;\n\n let id: string | undefined;\n let ensuring: Promise | undefined; // in-flight create, so we never create twice\n let timer: ReturnType | undefined;\n // Resolves once resume() has decided whether a Concept of this type already exists;\n // gates ensureId so a fast typist can't create a duplicate before that lookup lands.\n let resumeGate: Promise = Promise.resolve();\n\n const ensureId = async (): Promise => {\n await resumeGate;\n if (id) return id;\n ensuring ??= adapter.create(deps.type).then((newId) => {\n id = newId;\n // Stamp the id into the URL (no navigation) so a reload resumes this Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: newId }, queryParamsHandling: 'merge', replaceUrl: true });\n return newId;\n });\n return ensuring;\n };\n\n // Apply a resumed draft only when it's safe to: a late lookup must never clobber\n // progress the user already made while it was in flight, and \"start fresh\" needs no\n // dispatch (the machine already starts fresh). snapshot() is non-null once the user\n // has real progress.\n const applyResume = (draft: unknown | null) => {\n if (draft == null || deps.snapshot() != null) return;\n deps.onResume(draft);\n };\n\n const flush = async () => {\n const snap = deps.snapshot();\n if (!snap) return;\n const theId = await ensureId();\n await adapter.syncDraft(theId, { draft: snap.draft, stepIndex: snap.stepIndex, stepCount: snap.stepCount, documentIds: snap.documentIds });\n };\n\n // One effect watches the snapshot; each change resets a debounce timer. The timer's\n // callback only does network I/O (never dispatch), so it can't livelock the store.\n effect(() => {\n if (!active()) return;\n const snap = deps.snapshot(); // tracked: fires on every machine change\n if (!snap) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => void flush(), DEBOUNCE_MS);\n });\n\n inject(DestroyRef).onDestroy(() => timer && clearTimeout(timer));\n\n // Attach to a specific Concept id and seed the machine from its draft. A non-Concept\n // (submitted/gone) id is treated as fresh so it can't reopen as an editable draft.\n const load = (linked: string): Promise => {\n id = linked;\n return adapter\n .detail(linked)\n .then((dto) => {\n if (dto.status && dto.status.tag !== 'Concept') {\n id = undefined;\n applyResume(null);\n return;\n }\n applyResume(dto.draft ?? null);\n })\n .catch(() => {\n id = undefined;\n applyResume(null); // unknown/deleted id → start fresh\n });\n };\n\n // Find the user's existing Concept of this type (at most one), if any.\n const findConcept = async (): Promise => {\n try {\n const parsed = parseApplications(await adapter.list());\n return parsed.ok ? parsed.value.find((a) => a.type === deps.type && a.status.tag === 'Concept')?.id : undefined;\n } catch {\n return undefined;\n }\n };\n\n return {\n /** Resolve the initial state: a `?aanvraag` link wins; else resume this type's\n existing Concept; else start fresh (a Concept is created on first progress). */\n async resume() {\n let release!: () => void;\n resumeGate = new Promise((r) => (release = r));\n try {\n if (!active()) {\n applyResume(null);\n return;\n }\n const linked = route!.snapshot.queryParamMap.get('aanvraag');\n if (linked) {\n await load(linked);\n return;\n }\n const existing = await findConcept();\n if (existing) {\n await load(existing);\n // Stamp the id into the URL so a reload resumes the same Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: existing }, queryParamsHandling: 'merge', replaceUrl: true });\n return;\n }\n applyResume(null);\n } finally {\n release();\n }\n },\n\n /** Submit through the aanvraag lifecycle: ensure the Concept exists, then\n `POST /applications/{id}/submit` (server sets autoApprovable + transitions).\n Folded into a Result like the old submit-* commands. */\n submit(body: SubmitApplicationRequest): Promise> {\n return runSubmit(async () => adapter.submit(await ensureId(), body), SUBMIT_FAILED);\n },\n\n /** Restart: discard the current in-progress Concept (delete it) and detach, so a\n fresh one is created on next progress. Keeps the one-per-type invariant. A\n submitted id can't be deleted (409, caught) — that submission correctly remains,\n and detaching still lets the user start a new Concept. */\n reset() {\n if (id) {\n void adapter.cancel(id).catch(() => {}); // Concept → deleted; submitted → 409, kept\n id = undefined;\n ensuring = undefined;\n }\n if (active()) void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: null }, queryParamsHandling: 'merge', replaceUrl: true });\n },\n };\n}\n", "properties": [ { "name": "documentIds", @@ -2126,12 +2172,12 @@ }, { "name": "DraftSyncDeps", - "id": "interface-DraftSyncDeps-cc72ba6f5f75034dff8993670c479d720530ec464d0fac4d0e95885e435aebb6fe8bfd0c428423ad9aad3b66f927d61b63ed473008f7f041251dd8248b79464a", + "id": "interface-DraftSyncDeps-e575992a23399ee78afe66c219bd8a146ede9c3330705108a5ee488100dd204c54c96e1a8e354dfcd03f8f842249ad8ee59f6bd9967431b700e9050ca058b295", "file": "src/app/registratie/application/draft-sync.ts", "deprecated": false, "deprecationMessage": "", "type": "interface", - "sourceCode": "import { DestroyRef, effect, inject } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Result } from '@shared/kernel/fp';\nimport { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';\nimport { SubmitApplicationRequest, SubmitApplicationResponse } from '@shared/infrastructure/api-client';\nimport { AanvraagType } from '@registratie/domain/aanvraag';\nimport { ApplicationsAdapter, parseApplications } from '@registratie/infrastructure/applications.adapter';\n\n/** What a wizard persists per step: the opaque machine snapshot + progress + docs. */\nexport interface DraftSnapshot {\n draft: unknown;\n stepIndex: number;\n stepCount: number;\n documentIds: string[];\n}\n\nexport interface DraftSyncDeps {\n type: AanvraagType;\n /** The machine snapshot while it's worth persisting; null when not (pristine/done). */\n snapshot: () => DraftSnapshot | null;\n /** Seed the machine from a resumed draft. Called at most once, on init, and ONLY\n with a real draft on a still-pristine machine — see `applyResume`. */\n onResume: (draft: unknown) => void;\n /** Draft-sync only runs in the real app — false in Storybook/tests (explicit seed). */\n enabled: () => boolean;\n}\n\nconst DEBOUNCE_MS = 600; // ponytail: fixed debounce; tune if the sync feels laggy/chatty.\n\n/**\n * The effectful glue that replaces per-wizard sessionStorage with a backend-owned\n * Concept (PRD 0001, phase D). Instantiated in a field initializer (like\n * `createStore`/`createUploadController`). Responsibilities:\n *\n * - resume: a `?aanvraag=` link wins; otherwise resume the ONE existing Concept of\n * this type (at most one per type), seeding the machine from its saved draft;\n * - create-on-first-progress: when no Concept exists, one is created lazily the first\n * time the wizard reports a non-null snapshot, and its id is stamped into the URL;\n * - debounced draft sync on every subsequent change.\n *\n * Inert without a Router (stories) or when `enabled()` is false — no network, no resume.\n */\nexport function createDraftSync(deps: DraftSyncDeps) {\n const adapter = inject(ApplicationsAdapter);\n const router = inject(Router, { optional: true });\n const route = inject(ActivatedRoute, { optional: true });\n const active = () => deps.enabled() && !!router && !!route;\n\n let id: string | undefined;\n let ensuring: Promise | undefined; // in-flight create, so we never create twice\n let timer: ReturnType | undefined;\n // Resolves once resume() has decided whether a Concept of this type already exists;\n // gates ensureId so a fast typist can't create a duplicate before that lookup lands.\n let resumeGate: Promise = Promise.resolve();\n\n const ensureId = async (): Promise => {\n await resumeGate;\n if (id) return id;\n ensuring ??= adapter.create(deps.type).then((newId) => {\n id = newId;\n // Stamp the id into the URL (no navigation) so a reload resumes this Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: newId }, queryParamsHandling: 'merge', replaceUrl: true });\n return newId;\n });\n return ensuring;\n };\n\n // Apply a resumed draft only when it's safe to: a late lookup must never clobber\n // progress the user already made while it was in flight, and \"start fresh\" needs no\n // dispatch (the machine already starts fresh). snapshot() is non-null once the user\n // has real progress.\n const applyResume = (draft: unknown | null) => {\n if (draft == null || deps.snapshot() != null) return;\n deps.onResume(draft);\n };\n\n const flush = async () => {\n const snap = deps.snapshot();\n if (!snap) return;\n const theId = await ensureId();\n await adapter.syncDraft(theId, { draft: snap.draft, stepIndex: snap.stepIndex, stepCount: snap.stepCount, documentIds: snap.documentIds });\n };\n\n // One effect watches the snapshot; each change resets a debounce timer. The timer's\n // callback only does network I/O (never dispatch), so it can't livelock the store.\n effect(() => {\n if (!active()) return;\n const snap = deps.snapshot(); // tracked: fires on every machine change\n if (!snap) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => void flush(), DEBOUNCE_MS);\n });\n\n inject(DestroyRef).onDestroy(() => timer && clearTimeout(timer));\n\n // Attach to a specific Concept id and seed the machine from its draft. A non-Concept\n // (submitted/gone) id is treated as fresh so it can't reopen as an editable draft.\n const load = (linked: string): Promise => {\n id = linked;\n return adapter\n .detail(linked)\n .then((dto) => {\n if (dto.status && dto.status.tag !== 'Concept') {\n id = undefined;\n applyResume(null);\n return;\n }\n applyResume(dto.draft ?? null);\n })\n .catch(() => {\n id = undefined;\n applyResume(null); // unknown/deleted id → start fresh\n });\n };\n\n // Find the user's existing Concept of this type (at most one), if any.\n const findConcept = async (): Promise => {\n try {\n const parsed = parseApplications(await adapter.list());\n return parsed.ok ? parsed.value.find((a) => a.type === deps.type && a.status.tag === 'Concept')?.id : undefined;\n } catch {\n return undefined;\n }\n };\n\n return {\n /** Resolve the initial state: a `?aanvraag` link wins; else resume this type's\n existing Concept; else start fresh (a Concept is created on first progress). */\n async resume() {\n let release!: () => void;\n resumeGate = new Promise((r) => (release = r));\n try {\n if (!active()) {\n applyResume(null);\n return;\n }\n const linked = route!.snapshot.queryParamMap.get('aanvraag');\n if (linked) {\n await load(linked);\n return;\n }\n const existing = await findConcept();\n if (existing) {\n await load(existing);\n // Stamp the id into the URL so a reload resumes the same Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: existing }, queryParamsHandling: 'merge', replaceUrl: true });\n return;\n }\n applyResume(null);\n } finally {\n release();\n }\n },\n\n /** Submit through the aanvraag lifecycle: ensure the Concept exists, then\n `POST /applications/{id}/submit` (server sets autoApprovable + transitions).\n Folded into a Result like the old submit-* commands. */\n submit(body: SubmitApplicationRequest): Promise> {\n return runSubmit(async () => adapter.submit(await ensureId(), body), SUBMIT_FAILED);\n },\n\n /** Restart: discard the current in-progress Concept (delete it) and detach, so a\n fresh one is created on next progress. Keeps the one-per-type invariant. A\n submitted id can't be deleted (409, caught) — that submission correctly remains,\n and detaching still lets the user start a new Concept. */\n reset() {\n if (id) {\n void adapter.cancel(id).catch(() => {}); // Concept → deleted; submitted → 409, kept\n id = undefined;\n ensuring = undefined;\n }\n if (active()) void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: null }, queryParamsHandling: 'merge', replaceUrl: true });\n },\n };\n}\n", + "sourceCode": "import { DestroyRef, effect, inject } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Result } from '@shared/kernel/fp';\nimport { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';\nimport type { SubmitApplicationRequest, SubmitApplicationResponse } from '@shared/infrastructure/api-client';\nimport { AanvraagType } from '@registratie/domain/aanvraag';\nimport { ApplicationsAdapter, parseApplications } from '@registratie/infrastructure/applications.adapter';\n\n/** What a wizard persists per step: the opaque machine snapshot + progress + docs. */\nexport interface DraftSnapshot {\n draft: unknown;\n stepIndex: number;\n stepCount: number;\n documentIds: string[];\n}\n\nexport interface DraftSyncDeps {\n type: AanvraagType;\n /** The machine snapshot while it's worth persisting; null when not (pristine/done). */\n snapshot: () => DraftSnapshot | null;\n /** Seed the machine from a resumed draft. Called at most once, on init, and ONLY\n with a real draft on a still-pristine machine — see `applyResume`. */\n onResume: (draft: unknown) => void;\n /** Draft-sync only runs in the real app — false in Storybook/tests (explicit seed). */\n enabled: () => boolean;\n}\n\nconst DEBOUNCE_MS = 600; // ponytail: fixed debounce; tune if the sync feels laggy/chatty.\n\n/**\n * The effectful glue that replaces per-wizard sessionStorage with a backend-owned\n * Concept (PRD 0001, phase D). Instantiated in a field initializer (like\n * `createStore`/`createUploadController`). Responsibilities:\n *\n * - resume: a `?aanvraag=` link wins; otherwise resume the ONE existing Concept of\n * this type (at most one per type), seeding the machine from its saved draft;\n * - create-on-first-progress: when no Concept exists, one is created lazily the first\n * time the wizard reports a non-null snapshot, and its id is stamped into the URL;\n * - debounced draft sync on every subsequent change.\n *\n * Inert without a Router (stories) or when `enabled()` is false — no network, no resume.\n */\nexport function createDraftSync(deps: DraftSyncDeps) {\n const adapter = inject(ApplicationsAdapter);\n const router = inject(Router, { optional: true });\n const route = inject(ActivatedRoute, { optional: true });\n const active = () => deps.enabled() && !!router && !!route;\n\n let id: string | undefined;\n let ensuring: Promise | undefined; // in-flight create, so we never create twice\n let timer: ReturnType | undefined;\n // Resolves once resume() has decided whether a Concept of this type already exists;\n // gates ensureId so a fast typist can't create a duplicate before that lookup lands.\n let resumeGate: Promise = Promise.resolve();\n\n const ensureId = async (): Promise => {\n await resumeGate;\n if (id) return id;\n ensuring ??= adapter.create(deps.type).then((newId) => {\n id = newId;\n // Stamp the id into the URL (no navigation) so a reload resumes this Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: newId }, queryParamsHandling: 'merge', replaceUrl: true });\n return newId;\n });\n return ensuring;\n };\n\n // Apply a resumed draft only when it's safe to: a late lookup must never clobber\n // progress the user already made while it was in flight, and \"start fresh\" needs no\n // dispatch (the machine already starts fresh). snapshot() is non-null once the user\n // has real progress.\n const applyResume = (draft: unknown | null) => {\n if (draft == null || deps.snapshot() != null) return;\n deps.onResume(draft);\n };\n\n const flush = async () => {\n const snap = deps.snapshot();\n if (!snap) return;\n const theId = await ensureId();\n await adapter.syncDraft(theId, { draft: snap.draft, stepIndex: snap.stepIndex, stepCount: snap.stepCount, documentIds: snap.documentIds });\n };\n\n // One effect watches the snapshot; each change resets a debounce timer. The timer's\n // callback only does network I/O (never dispatch), so it can't livelock the store.\n effect(() => {\n if (!active()) return;\n const snap = deps.snapshot(); // tracked: fires on every machine change\n if (!snap) return;\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => void flush(), DEBOUNCE_MS);\n });\n\n inject(DestroyRef).onDestroy(() => timer && clearTimeout(timer));\n\n // Attach to a specific Concept id and seed the machine from its draft. A non-Concept\n // (submitted/gone) id is treated as fresh so it can't reopen as an editable draft.\n const load = (linked: string): Promise => {\n id = linked;\n return adapter\n .detail(linked)\n .then((dto) => {\n if (dto.status && dto.status.tag !== 'Concept') {\n id = undefined;\n applyResume(null);\n return;\n }\n applyResume(dto.draft ?? null);\n })\n .catch(() => {\n id = undefined;\n applyResume(null); // unknown/deleted id → start fresh\n });\n };\n\n // Find the user's existing Concept of this type (at most one), if any.\n const findConcept = async (): Promise => {\n try {\n const parsed = parseApplications(await adapter.list());\n return parsed.ok ? parsed.value.find((a) => a.type === deps.type && a.status.tag === 'Concept')?.id : undefined;\n } catch {\n return undefined;\n }\n };\n\n return {\n /** Resolve the initial state: a `?aanvraag` link wins; else resume this type's\n existing Concept; else start fresh (a Concept is created on first progress). */\n async resume() {\n let release!: () => void;\n resumeGate = new Promise((r) => (release = r));\n try {\n if (!active()) {\n applyResume(null);\n return;\n }\n const linked = route!.snapshot.queryParamMap.get('aanvraag');\n if (linked) {\n await load(linked);\n return;\n }\n const existing = await findConcept();\n if (existing) {\n await load(existing);\n // Stamp the id into the URL so a reload resumes the same Concept.\n void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: existing }, queryParamsHandling: 'merge', replaceUrl: true });\n return;\n }\n applyResume(null);\n } finally {\n release();\n }\n },\n\n /** Submit through the aanvraag lifecycle: ensure the Concept exists, then\n `POST /applications/{id}/submit` (server sets autoApprovable + transitions).\n Folded into a Result like the old submit-* commands. */\n submit(body: SubmitApplicationRequest): Promise> {\n return runSubmit(async () => adapter.submit(await ensureId(), body), SUBMIT_FAILED);\n },\n\n /** Restart: discard the current in-progress Concept (delete it) and detach, so a\n fresh one is created on next progress. Keeps the one-per-type invariant. A\n submitted id can't be deleted (409, caught) — that submission correctly remains,\n and detaching still lets the user start a new Concept. */\n reset() {\n if (id) {\n void adapter.cancel(id).catch(() => {}); // Concept → deleted; submitted → 409, kept\n id = undefined;\n ensuring = undefined;\n }\n if (active()) void router!.navigate([], { relativeTo: route!, queryParams: { aanvraag: null }, queryParamsHandling: 'merge', replaceUrl: true });\n },\n };\n}\n", "properties": [ { "name": "enabled", @@ -2239,82 +2285,7 @@ }, { "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": "DuoDiplomaDto", - "id": "interface-DuoDiplomaDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b-1", + "id": "interface-DuoDiplomaDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", "file": "src/app/shared/infrastructure/api-client.ts", "deprecated": false, "deprecationMessage": "", @@ -2385,6 +2356,81 @@ "indexSignatures": [], "kind": 172, "methods": [], + "extends": [] + }, + { + "name": "DuoDiplomaDto", + "id": "interface-DuoDiplomaDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1-1", + "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": [], "isDuplicate": true, "duplicateId": 1, @@ -2392,7 +2438,42 @@ }, { "name": "DuoLookupDto", - "id": "interface-DuoLookupDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1", + "id": "interface-DuoLookupDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", + "file": "src/app/shared/infrastructure/api-client.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "export class ApiClient {\n private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };\n private baseUrl: string;\n protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;\n\n constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {\n this.http = http ? http : window as any;\n this.baseUrl = baseUrl ?? \"\";\n }\n\n /**\n * @return OK\n */\n health(): Promise {\n let url_ = this.baseUrl + \"/health\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHealth(_response);\n });\n }\n\n protected processHealth(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n ready(): Promise {\n let url_ = this.baseUrl + \"/health/ready\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReady(_response);\n });\n }\n\n protected processReady(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n dashboardView(): Promise {\n let url_ = this.baseUrl + \"/api/v1/dashboard-view\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDashboardView(_response);\n });\n }\n\n protected processDashboardView(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DashboardViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n notes(): Promise {\n let url_ = this.baseUrl + \"/api/v1/notes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processNotes(_response);\n });\n }\n\n protected processNotes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AantekeningDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n address(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brp/address\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processAddress(_response);\n });\n }\n\n protected processAddress(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BrpAddressDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n diplomas(): Promise {\n let url_ = this.baseUrl + \"/api/v1/duo/diplomas\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDiplomas(_response);\n });\n }\n\n protected processDiplomas(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DuoLookupDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n policy(): Promise {\n let url_ = this.baseUrl + \"/api/v1/intake/policy\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processPolicy(_response);\n });\n }\n\n protected processPolicy(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as IntakePolicyDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n registrations(body: RegistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/registrations\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processRegistrations(_response);\n });\n }\n\n protected processRegistrations(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n herregistraties(body: HerregistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/herregistraties\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHerregistraties(_response);\n });\n }\n\n protected processHerregistraties(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n intakes(body: IntakeRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/intakes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processIntakes(_response);\n });\n }\n\n protected processIntakes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n changeRequests(body: ChangeRequestRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/change-requests\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processChangeRequests(_response);\n });\n }\n\n protected processChangeRequests(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param diplomaHerkomst (optional) \n * @param taalvaardigheid (optional) \n * @return OK\n */\n categories(wizardId: string, diplomaHerkomst?: string | undefined, taalvaardigheid?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/categories?\";\n if (wizardId === undefined || wizardId === null)\n throw new globalThis.Error(\"The parameter 'wizardId' must be defined and cannot be null.\");\n else\n url_ += \"wizardId=\" + encodeURIComponent(\"\" + wizardId) + \"&\";\n if (diplomaHerkomst === null)\n throw new globalThis.Error(\"The parameter 'diplomaHerkomst' cannot be null.\");\n else if (diplomaHerkomst !== undefined)\n url_ += \"diplomaHerkomst=\" + encodeURIComponent(\"\" + diplomaHerkomst) + \"&\";\n if (taalvaardigheid === null)\n throw new globalThis.Error(\"The parameter 'taalvaardigheid' cannot be null.\");\n else if (taalvaardigheid !== undefined)\n url_ += \"taalvaardigheid=\" + encodeURIComponent(\"\" + taalvaardigheid) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processCategories(_response);\n });\n }\n\n protected processCategories(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadCategoriesDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n content(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}/content\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processContent(_response);\n });\n }\n\n protected processContent(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param localIds (optional) \n * @return OK\n */\n status(localIds?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/status?\";\n if (localIds === null)\n throw new globalThis.Error(\"The parameter 'localIds' cannot be null.\");\n else if (localIds !== undefined)\n url_ += \"localIds=\" + encodeURIComponent(\"\" + localIds) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processStatus(_response);\n });\n }\n\n protected processStatus(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadStatusDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads(_response);\n });\n }\n\n protected processUploads(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads2(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/admin/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads2(_response);\n });\n }\n\n protected processUploads2(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n return throwException(\"Forbidden\", status, _responseText, _headers);\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsAll(): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsAll(_response);\n });\n }\n\n protected processApplicationsAll(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return Created\n */\n applicationsPOST(body: CreateApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPOST(_response);\n });\n }\n\n protected processApplicationsPOST(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 201) {\n return response.text().then((_responseText) => {\n let result201: any = null;\n result201 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result201;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsGET(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsGET(_response);\n });\n }\n\n protected processApplicationsGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsPUT(id: string, body: DraftSyncRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPUT(_response);\n });\n }\n\n protected processApplicationsPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsDELETE(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsDELETE(_response);\n });\n }\n\n protected processApplicationsDELETE(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n submit(id: string, body: SubmitApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}/submit\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSubmit(_response);\n });\n }\n\n protected processSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as SubmitApplicationResponse;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefGET(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefGET(_response);\n });\n }\n\n protected processBriefGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefPUT(body: SaveBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefPUT(_response);\n });\n }\n\n protected processBriefPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefSubmit(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/submit\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefSubmit(_response);\n });\n }\n\n protected processBriefSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n approve(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/approve\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApprove(_response);\n });\n }\n\n protected processApprove(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n reject(body: RejectBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reject\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReject(_response);\n });\n }\n\n protected processReject(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n send(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/send\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSend(_response);\n });\n }\n\n protected processSend(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefReset(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reset\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefReset(_response);\n });\n }\n\n protected processBriefReset(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n}\n\nexport interface AantekeningDto {\n type?: string | undefined;\n omschrijving?: string | undefined;\n datum?: string | undefined;\n}\n\nexport interface AanvraagStatusDto {\n tag?: string | undefined;\n stepIndex?: number | undefined;\n stepCount?: number | undefined;\n referentie?: string | undefined;\n manual?: boolean | undefined;\n reden?: string | undefined;\n}\n\nexport interface AdresDto {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface ApplicationDetailDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n draft?: any | undefined;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface ApplicationSummaryDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface BriefDto {\n briefId?: string | undefined;\n beroep?: string | undefined;\n templateId?: string | undefined;\n placeholders?: PlaceholderDefDto[] | undefined;\n sections?: LetterSectionDto[] | undefined;\n status?: BriefStatusDto;\n drafterId?: string | undefined;\n}\n\nexport interface BriefStatusDto {\n tag?: string | undefined;\n submittedBy?: string | undefined;\n submittedAt?: string | undefined;\n approvedBy?: string | undefined;\n approvedAt?: string | undefined;\n rejectedBy?: string | undefined;\n rejectedAt?: string | undefined;\n comments?: string | undefined;\n sentAt?: string | undefined;\n}\n\nexport interface BriefViewDto {\n brief?: BriefDto;\n availablePassages?: LibraryPassageDto[] | undefined;\n}\n\nexport interface BrpAddressDto {\n gevonden?: boolean;\n adres?: AdresDto;\n}\n\nexport interface ChangeRequestRequest {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface CreateApplicationRequest {\n type?: string | undefined;\n}\n\nexport interface DashboardViewDto {\n registration?: RegistrationDto;\n person?: PersonDto;\n decisions?: HerregistratieDecisionsDto;\n}\n\nexport interface DocumentCategoryDto {\n categoryId?: string | undefined;\n label?: string | undefined;\n description?: string | undefined;\n required?: boolean;\n acceptedTypes?: string[] | undefined;\n maxSizeMb?: number;\n multiple?: boolean;\n allowPostDelivery?: boolean;\n}\n\nexport interface DocumentRefDto {\n categoryId?: string | undefined;\n channel?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport interface DraftSyncRequest {\n draft?: any;\n stepIndex?: number;\n stepCount?: number;\n documentIds?: string[] | undefined;\n}\n\nexport interface DuoDiplomaDto {\n id?: string | undefined;\n naam?: string | undefined;\n instelling?: string | undefined;\n jaar?: number;\n beroep?: string | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface DuoLookupDto {\n diplomas?: DuoDiplomaDto[] | undefined;\n handmatig?: ManualDiplomaPolicyDto;\n}\n\nexport interface HerregistratieDecisionsDto {\n eligibleForHerregistratie?: boolean;\n herregistratieReason?: string | undefined;\n}\n\nexport interface HerregistratieRequest {\n uren?: number;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface IntakePolicyDto {\n scholingThreshold?: number;\n}\n\nexport interface IntakeRequest {\n uren?: number;\n}\n\nexport interface LetterBlockDto {\n type?: string | undefined;\n blockId?: string | undefined;\n content?: RichTextBlockDto;\n sourcePassageId?: string | undefined;\n sourceVersion?: number | undefined;\n edited?: boolean | undefined;\n}\n\nexport interface LetterSectionDto {\n sectionKey?: string | undefined;\n title?: string | undefined;\n required?: boolean;\n blocks?: LetterBlockDto[] | undefined;\n locked?: boolean;\n}\n\nexport interface LibraryPassageDto {\n passageId?: string | undefined;\n scope?: string | undefined;\n sectionKey?: string | undefined;\n label?: string | undefined;\n content?: RichTextBlockDto;\n version?: number;\n beroep?: string | undefined;\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen?: string[] | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface ParagraphDto {\n nodes?: RichTextNodeDto[] | undefined;\n list?: string | undefined;\n}\n\nexport interface PersonDto {\n naam?: string | undefined;\n geboortedatum?: string | undefined;\n adres?: AdresDto;\n}\n\nexport interface PlaceholderDefDto {\n key?: string | undefined;\n label?: string | undefined;\n autoResolvable?: boolean;\n fillable?: boolean | undefined;\n deprecated?: boolean | undefined;\n}\n\nexport interface PolicyQuestionDto {\n id?: string | undefined;\n vraag?: string | undefined;\n type?: string | undefined;\n}\n\nexport interface ProblemDetails {\n type?: string | undefined;\n title?: string | undefined;\n status?: number | undefined;\n detail?: string | undefined;\n instance?: string | undefined;\n\n [key: string]: any;\n}\n\nexport interface ReferentieResponse {\n referentie?: string | undefined;\n}\n\nexport interface RegistratieRequest {\n diplomaHerkomst?: string | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface RegistrationDto {\n bigNummer?: string | undefined;\n naam?: string | undefined;\n beroep?: string | undefined;\n registratiedatum?: string | undefined;\n geboortedatum?: string | undefined;\n status?: RegistrationStatusDto;\n}\n\nexport interface RegistrationStatusDto {\n tag?: string | undefined;\n herregistratieDatum?: string | undefined;\n geschorstTot?: string | undefined;\n reden?: string | undefined;\n doorgehaaldOp?: string | undefined;\n}\n\nexport interface RejectBriefRequest {\n comments?: string | undefined;\n}\n\nexport interface RichTextBlockDto {\n paragraphs?: ParagraphDto[] | undefined;\n}\n\nexport interface RichTextNodeDto {\n type?: string | undefined;\n text?: string | undefined;\n marks?: string[] | undefined;\n key?: string | undefined;\n}\n\nexport interface SaveBriefRequest {\n sections?: LetterSectionDto[] | undefined;\n}\n\nexport interface SubmitApplicationRequest {\n diplomaHerkomst?: string | undefined;\n uren?: number | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface SubmitApplicationResponse {\n referentie?: string | undefined;\n status?: AanvraagStatusDto;\n}\n\nexport interface UploadCategoriesDto {\n categories?: DocumentCategoryDto[] | undefined;\n}\n\nexport interface UploadStatusDto {\n results?: UploadStatusItemDto[] | undefined;\n}\n\nexport interface UploadStatusItemDto {\n localId?: string | undefined;\n status?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport class SwaggerException extends Error {\n override message: string;\n status: number;\n response: string;\n headers: { [key: string]: any; };\n result: any;\n\n constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {\n super();\n\n this.message = message;\n this.status = status;\n this.response = response;\n this.headers = headers;\n this.result = result;\n }\n\n protected isSwaggerException = true;\n\n static isSwaggerException(obj: any): obj is SwaggerException {\n return obj.isSwaggerException === true;\n }\n}\n\nfunction throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {\n if (result !== null && result !== undefined)\n throw result;\n else\n throw new SwaggerException(message, status, response, headers, null);\n}", + "properties": [ + { + "name": "diplomas", + "deprecated": false, + "deprecationMessage": "", + "type": "DuoDiplomaDto[] | undefined", + "indexKey": "", + "optional": true, + "description": "", + "line": 1372 + }, + { + "name": "handmatig", + "deprecated": false, + "deprecationMessage": "", + "type": "ManualDiplomaPolicyDto", + "indexKey": "", + "optional": true, + "description": "", + "line": 1373 + } + ], + "indexSignatures": [], + "kind": 172, + "methods": [], + "extends": [] + }, + { + "name": "DuoLookupDto", + "id": "interface-DuoLookupDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1-1", "file": "src/app/registratie/contracts/duo-diplomas.dto.ts", "deprecated": false, "deprecationMessage": "", @@ -2425,41 +2506,6 @@ "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": "DuoLookupDto", - "id": "interface-DuoLookupDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b-1", - "file": "src/app/shared/infrastructure/api-client.ts", - "deprecated": false, - "deprecationMessage": "", - "type": "interface", - "sourceCode": "export class ApiClient {\n private http: { fetch(url: RequestInfo, init?: RequestInit): Promise };\n private baseUrl: string;\n protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;\n\n constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise }) {\n this.http = http ? http : window as any;\n this.baseUrl = baseUrl ?? \"\";\n }\n\n /**\n * @return OK\n */\n health(): Promise {\n let url_ = this.baseUrl + \"/health\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHealth(_response);\n });\n }\n\n protected processHealth(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n ready(): Promise {\n let url_ = this.baseUrl + \"/health/ready\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReady(_response);\n });\n }\n\n protected processReady(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n dashboardView(): Promise {\n let url_ = this.baseUrl + \"/api/v1/dashboard-view\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDashboardView(_response);\n });\n }\n\n protected processDashboardView(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DashboardViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n notes(): Promise {\n let url_ = this.baseUrl + \"/api/v1/notes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processNotes(_response);\n });\n }\n\n protected processNotes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as AantekeningDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n address(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brp/address\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processAddress(_response);\n });\n }\n\n protected processAddress(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BrpAddressDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n diplomas(): Promise {\n let url_ = this.baseUrl + \"/api/v1/duo/diplomas\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processDiplomas(_response);\n });\n }\n\n protected processDiplomas(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as DuoLookupDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n policy(): Promise {\n let url_ = this.baseUrl + \"/api/v1/intake/policy\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processPolicy(_response);\n });\n }\n\n protected processPolicy(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as IntakePolicyDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n registrations(body: RegistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/registrations\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processRegistrations(_response);\n });\n }\n\n protected processRegistrations(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n herregistraties(body: HerregistratieRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/herregistraties\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processHerregistraties(_response);\n });\n }\n\n protected processHerregistraties(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n intakes(body: IntakeRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/intakes\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processIntakes(_response);\n });\n }\n\n protected processIntakes(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n changeRequests(body: ChangeRequestRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/change-requests\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processChangeRequests(_response);\n });\n }\n\n protected processChangeRequests(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ReferentieResponse;\n return result200;\n });\n } else if (status === 422) {\n return response.text().then((_responseText) => {\n let result422: any = null;\n result422 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Unprocessable Content\", status, _responseText, _headers, result422);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param diplomaHerkomst (optional) \n * @param taalvaardigheid (optional) \n * @return OK\n */\n categories(wizardId: string, diplomaHerkomst?: string | undefined, taalvaardigheid?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/categories?\";\n if (wizardId === undefined || wizardId === null)\n throw new globalThis.Error(\"The parameter 'wizardId' must be defined and cannot be null.\");\n else\n url_ += \"wizardId=\" + encodeURIComponent(\"\" + wizardId) + \"&\";\n if (diplomaHerkomst === null)\n throw new globalThis.Error(\"The parameter 'diplomaHerkomst' cannot be null.\");\n else if (diplomaHerkomst !== undefined)\n url_ += \"diplomaHerkomst=\" + encodeURIComponent(\"\" + diplomaHerkomst) + \"&\";\n if (taalvaardigheid === null)\n throw new globalThis.Error(\"The parameter 'taalvaardigheid' cannot be null.\");\n else if (taalvaardigheid !== undefined)\n url_ += \"taalvaardigheid=\" + encodeURIComponent(\"\" + taalvaardigheid) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processCategories(_response);\n });\n }\n\n protected processCategories(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadCategoriesDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n content(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}/content\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processContent(_response);\n });\n }\n\n protected processContent(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @param localIds (optional) \n * @return OK\n */\n status(localIds?: string | undefined): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/status?\";\n if (localIds === null)\n throw new globalThis.Error(\"The parameter 'localIds' cannot be null.\");\n else if (localIds !== undefined)\n url_ += \"localIds=\" + encodeURIComponent(\"\" + localIds) + \"&\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processStatus(_response);\n });\n }\n\n protected processStatus(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadStatusDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads(_response);\n });\n }\n\n protected processUploads(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n uploads2(documentId: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/admin/uploads/{documentId}\";\n if (documentId === undefined || documentId === null)\n throw new globalThis.Error(\"The parameter 'documentId' must be defined.\");\n url_ = url_.replace(\"{documentId}\", encodeURIComponent(\"\" + documentId));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processUploads2(_response);\n });\n }\n\n protected processUploads2(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n return throwException(\"Forbidden\", status, _responseText, _headers);\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsAll(): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsAll(_response);\n });\n }\n\n protected processApplicationsAll(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationSummaryDto[];\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return Created\n */\n applicationsPOST(body: CreateApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPOST(_response);\n });\n }\n\n protected processApplicationsPOST(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 201) {\n return response.text().then((_responseText) => {\n let result201: any = null;\n result201 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result201;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n applicationsGET(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsGET(_response);\n });\n }\n\n protected processApplicationsGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ApplicationDetailDto;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsPUT(id: string, body: DraftSyncRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsPUT(_response);\n });\n }\n\n protected processApplicationsPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return No Content\n */\n applicationsDELETE(id: string): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"DELETE\",\n headers: {\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApplicationsDELETE(_response);\n });\n }\n\n protected processApplicationsDELETE(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 204) {\n return response.text().then((_responseText) => {\n return;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n submit(id: string, body: SubmitApplicationRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/applications/{id}/submit\";\n if (id === undefined || id === null)\n throw new globalThis.Error(\"The parameter 'id' must be defined.\");\n url_ = url_.replace(\"{id}\", encodeURIComponent(\"\" + id));\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSubmit(_response);\n });\n }\n\n protected processSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as SubmitApplicationResponse;\n return result200;\n });\n } else if (status === 404) {\n return response.text().then((_responseText) => {\n return throwException(\"Not Found\", status, _responseText, _headers);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefGET(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"GET\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefGET(_response);\n });\n }\n\n protected processBriefGET(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefPUT(body: SaveBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"PUT\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefPUT(_response);\n });\n }\n\n protected processBriefPUT(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefSubmit(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/submit\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefSubmit(_response);\n });\n }\n\n protected processBriefSubmit(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n approve(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/approve\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processApprove(_response);\n });\n }\n\n protected processApprove(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n reject(body: RejectBriefRequest): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reject\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n const content_ = JSON.stringify(body);\n\n let options_: RequestInit = {\n body: content_,\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processReject(_response);\n });\n }\n\n protected processReject(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 403) {\n return response.text().then((_responseText) => {\n let result403: any = null;\n result403 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Forbidden\", status, _responseText, _headers, result403);\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n send(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/send\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processSend(_response);\n });\n }\n\n protected processSend(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefDto;\n return result200;\n });\n } else if (status === 409) {\n return response.text().then((_responseText) => {\n let result409: any = null;\n result409 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;\n return throwException(\"Conflict\", status, _responseText, _headers, result409);\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n\n /**\n * @return OK\n */\n briefReset(): Promise {\n let url_ = this.baseUrl + \"/api/v1/brief/reset\";\n url_ = url_.replace(/[?&]$/, \"\");\n\n let options_: RequestInit = {\n method: \"POST\",\n headers: {\n \"Accept\": \"application/json\"\n }\n };\n\n return this.http.fetch(url_, options_).then((_response: Response) => {\n return this.processBriefReset(_response);\n });\n }\n\n protected processBriefReset(response: Response): Promise {\n const status = response.status;\n let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };\n if (status === 200) {\n return response.text().then((_responseText) => {\n let result200: any = null;\n result200 = _responseText === \"\" ? null : JSON.parse(_responseText, this.jsonParseReviver) as BriefViewDto;\n return result200;\n });\n } else if (status !== 200 && status !== 204) {\n return response.text().then((_responseText) => {\n return throwException(\"An unexpected server error occurred.\", status, _responseText, _headers);\n });\n }\n return Promise.resolve(null as any);\n }\n}\n\nexport interface AantekeningDto {\n type?: string | undefined;\n omschrijving?: string | undefined;\n datum?: string | undefined;\n}\n\nexport interface AanvraagStatusDto {\n tag?: string | undefined;\n stepIndex?: number | undefined;\n stepCount?: number | undefined;\n referentie?: string | undefined;\n manual?: boolean | undefined;\n reden?: string | undefined;\n}\n\nexport interface AdresDto {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface ApplicationDetailDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n draft?: any | undefined;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface ApplicationSummaryDto {\n id?: string | undefined;\n type?: string | undefined;\n status?: AanvraagStatusDto;\n documentIds?: string[] | undefined;\n createdAt?: string | undefined;\n updatedAt?: string | undefined;\n submittedAt?: string | undefined;\n}\n\nexport interface BriefDto {\n briefId?: string | undefined;\n beroep?: string | undefined;\n templateId?: string | undefined;\n placeholders?: PlaceholderDefDto[] | undefined;\n sections?: LetterSectionDto[] | undefined;\n status?: BriefStatusDto;\n drafterId?: string | undefined;\n}\n\nexport interface BriefStatusDto {\n tag?: string | undefined;\n submittedBy?: string | undefined;\n submittedAt?: string | undefined;\n approvedBy?: string | undefined;\n approvedAt?: string | undefined;\n rejectedBy?: string | undefined;\n rejectedAt?: string | undefined;\n comments?: string | undefined;\n sentAt?: string | undefined;\n}\n\nexport interface BriefViewDto {\n brief?: BriefDto;\n availablePassages?: LibraryPassageDto[] | undefined;\n}\n\nexport interface BrpAddressDto {\n gevonden?: boolean;\n adres?: AdresDto;\n}\n\nexport interface ChangeRequestRequest {\n straat?: string | undefined;\n postcode?: string | undefined;\n woonplaats?: string | undefined;\n}\n\nexport interface CreateApplicationRequest {\n type?: string | undefined;\n}\n\nexport interface DashboardViewDto {\n registration?: RegistrationDto;\n person?: PersonDto;\n decisions?: HerregistratieDecisionsDto;\n}\n\nexport interface DocumentCategoryDto {\n categoryId?: string | undefined;\n label?: string | undefined;\n description?: string | undefined;\n required?: boolean;\n acceptedTypes?: string[] | undefined;\n maxSizeMb?: number;\n multiple?: boolean;\n allowPostDelivery?: boolean;\n}\n\nexport interface DocumentRefDto {\n categoryId?: string | undefined;\n channel?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport interface DraftSyncRequest {\n draft?: any;\n stepIndex?: number;\n stepCount?: number;\n documentIds?: string[] | undefined;\n}\n\nexport interface DuoDiplomaDto {\n id?: string | undefined;\n naam?: string | undefined;\n instelling?: string | undefined;\n jaar?: number;\n beroep?: string | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface DuoLookupDto {\n diplomas?: DuoDiplomaDto[] | undefined;\n handmatig?: ManualDiplomaPolicyDto;\n}\n\nexport interface HerregistratieDecisionsDto {\n eligibleForHerregistratie?: boolean;\n herregistratieReason?: string | undefined;\n}\n\nexport interface HerregistratieRequest {\n uren?: number;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface IntakePolicyDto {\n scholingThreshold?: number;\n}\n\nexport interface IntakeRequest {\n uren?: number;\n}\n\nexport interface LetterBlockDto {\n type?: string | undefined;\n blockId?: string | undefined;\n content?: RichTextBlockDto;\n sourcePassageId?: string | undefined;\n sourceVersion?: number | undefined;\n edited?: boolean | undefined;\n}\n\nexport interface LetterSectionDto {\n sectionKey?: string | undefined;\n title?: string | undefined;\n required?: boolean;\n blocks?: LetterBlockDto[] | undefined;\n locked?: boolean;\n}\n\nexport interface LibraryPassageDto {\n passageId?: string | undefined;\n scope?: string | undefined;\n sectionKey?: string | undefined;\n label?: string | undefined;\n content?: RichTextBlockDto;\n version?: number;\n beroep?: string | undefined;\n}\n\nexport interface ManualDiplomaPolicyDto {\n beroepen?: string[] | undefined;\n policyQuestions?: PolicyQuestionDto[] | undefined;\n}\n\nexport interface ParagraphDto {\n nodes?: RichTextNodeDto[] | undefined;\n list?: string | undefined;\n}\n\nexport interface PersonDto {\n naam?: string | undefined;\n geboortedatum?: string | undefined;\n adres?: AdresDto;\n}\n\nexport interface PlaceholderDefDto {\n key?: string | undefined;\n label?: string | undefined;\n autoResolvable?: boolean;\n fillable?: boolean | undefined;\n deprecated?: boolean | undefined;\n}\n\nexport interface PolicyQuestionDto {\n id?: string | undefined;\n vraag?: string | undefined;\n type?: string | undefined;\n}\n\nexport interface ProblemDetails {\n type?: string | undefined;\n title?: string | undefined;\n status?: number | undefined;\n detail?: string | undefined;\n instance?: string | undefined;\n\n [key: string]: any;\n}\n\nexport interface ReferentieResponse {\n referentie?: string | undefined;\n}\n\nexport interface RegistratieRequest {\n diplomaHerkomst?: string | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface RegistrationDto {\n bigNummer?: string | undefined;\n naam?: string | undefined;\n beroep?: string | undefined;\n registratiedatum?: string | undefined;\n geboortedatum?: string | undefined;\n status?: RegistrationStatusDto;\n}\n\nexport interface RegistrationStatusDto {\n tag?: string | undefined;\n herregistratieDatum?: string | undefined;\n geschorstTot?: string | undefined;\n reden?: string | undefined;\n doorgehaaldOp?: string | undefined;\n}\n\nexport interface RejectBriefRequest {\n comments?: string | undefined;\n}\n\nexport interface RichTextBlockDto {\n paragraphs?: ParagraphDto[] | undefined;\n}\n\nexport interface RichTextNodeDto {\n type?: string | undefined;\n text?: string | undefined;\n marks?: string[] | undefined;\n key?: string | undefined;\n}\n\nexport interface SaveBriefRequest {\n sections?: LetterSectionDto[] | undefined;\n}\n\nexport interface SubmitApplicationRequest {\n diplomaHerkomst?: string | undefined;\n uren?: number | undefined;\n documents?: DocumentRefDto[] | undefined;\n}\n\nexport interface SubmitApplicationResponse {\n referentie?: string | undefined;\n status?: AanvraagStatusDto;\n}\n\nexport interface UploadCategoriesDto {\n categories?: DocumentCategoryDto[] | undefined;\n}\n\nexport interface UploadStatusDto {\n results?: UploadStatusItemDto[] | undefined;\n}\n\nexport interface UploadStatusItemDto {\n localId?: string | undefined;\n status?: string | undefined;\n documentId?: string | undefined;\n}\n\nexport class SwaggerException extends Error {\n override message: string;\n status: number;\n response: string;\n headers: { [key: string]: any; };\n result: any;\n\n constructor(message: string, status: number, response: string, headers: { [key: string]: any; }, result: any) {\n super();\n\n this.message = message;\n this.status = status;\n this.response = response;\n this.headers = headers;\n this.result = result;\n }\n\n protected isSwaggerException = true;\n\n static isSwaggerException(obj: any): obj is SwaggerException {\n return obj.isSwaggerException === true;\n }\n}\n\nfunction throwException(message: string, status: number, response: string, headers: { [key: string]: any; }, result?: any): any {\n if (result !== null && result !== undefined)\n throw result;\n else\n throw new SwaggerException(message, status, response, headers, null);\n}", - "properties": [ - { - "name": "diplomas", - "deprecated": false, - "deprecationMessage": "", - "type": "DuoDiplomaDto[] | undefined", - "indexKey": "", - "optional": true, - "description": "", - "line": 1372 - }, - { - "name": "handmatig", - "deprecated": false, - "deprecationMessage": "", - "type": "ManualDiplomaPolicyDto", - "indexKey": "", - "optional": true, - "description": "", - "line": 1373 - } - ], - "indexSignatures": [], - "kind": 172, - "methods": [], "extends": [], "isDuplicate": true, "duplicateId": 1, @@ -2650,12 +2696,12 @@ }, { "name": "HerregistratieDecisions", - "id": "interface-HerregistratieDecisions-b39e266ec8fabdaddad87f1e15a5b1b86e45d84ac2c95028b4e676c2eb425fd675c77383c3d4f8f88f4a072f3eb88a954eb0f8755597224ebef59da18d24fda0", + "id": "interface-HerregistratieDecisions-10d81af9017230c2e0704c882140be892d1e2a8d6343f8d169bcdb8e99516bc135a9b17faf4ea0fb5d4da561476db228b7f2dab7d27e4c262acdecbe1b7f6241", "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", + "sourceCode": "export type RegistrationStatusDto =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\nexport interface RegistrationDto {\n bigNummer: string;\n naam: string;\n beroep: string;\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatusDto;\n}\n\nexport interface AdresDto {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface PersonDto {\n naam: string;\n geboortedatum: string; // ISO date\n adres: AdresDto;\n}\n\n/** Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\n the eligibility rule lives on the backend; the optional reason lets the UI\n explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\nexport interface DashboardViewDto {\n registration: RegistrationDto;\n person: PersonDto;\n decisions: HerregistratieDecisions;\n}\n", "properties": [ { "name": "eligibleForHerregistratie", @@ -2665,7 +2711,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 26 + "line": 51 }, { "name": "herregistratieReason", @@ -2675,13 +2721,13 @@ "indexKey": "", "optional": true, "description": "", - "line": 27 + "line": 52 } ], "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.", + "description": "

Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\nthe eligibility rule lives on the backend; the optional reason lets the UI\nexplain itself without knowing the rule.

\n", + "rawdescription": "\nServer-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\nthe eligibility rule lives on the backend; the optional reason lets the UI\nexplain itself without knowing the rule.", "methods": [], "extends": [] }, @@ -3220,42 +3266,7 @@ }, { "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": "ManualDiplomaPolicyDto", - "id": "interface-ManualDiplomaPolicyDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b-1", + "id": "interface-ManualDiplomaPolicyDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", "file": "src/app/shared/infrastructure/api-client.ts", "deprecated": false, "deprecationMessage": "", @@ -3286,6 +3297,41 @@ "indexSignatures": [], "kind": 172, "methods": [], + "extends": [] + }, + { + "name": "ManualDiplomaPolicyDto", + "id": "interface-ManualDiplomaPolicyDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1-1", + "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": [], "isDuplicate": true, "duplicateId": 1, @@ -3457,6 +3503,54 @@ "methods": [], "extends": [] }, + { + "name": "PersonDto", + "id": "interface-PersonDto-10d81af9017230c2e0704c882140be892d1e2a8d6343f8d169bcdb8e99516bc135a9b17faf4ea0fb5d4da561476db228b7f2dab7d27e4c262acdecbe1b7f6241-1", + "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "export type RegistrationStatusDto =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\nexport interface RegistrationDto {\n bigNummer: string;\n naam: string;\n beroep: string;\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatusDto;\n}\n\nexport interface AdresDto {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface PersonDto {\n naam: string;\n geboortedatum: string; // ISO date\n adres: AdresDto;\n}\n\n/** Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\n the eligibility rule lives on the backend; the optional reason lets the UI\n explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\nexport interface DashboardViewDto {\n registration: RegistrationDto;\n person: PersonDto;\n decisions: HerregistratieDecisions;\n}\n", + "properties": [ + { + "name": "adres", + "deprecated": false, + "deprecationMessage": "", + "type": "AdresDto", + "indexKey": "", + "optional": false, + "description": "", + "line": 44 + }, + { + "name": "geboortedatum", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 43 + }, + { + "name": "naam", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 42 + } + ], + "indexSignatures": [], + "kind": 172, + "methods": [], + "extends": [], + "isDuplicate": true, + "duplicateId": 1, + "duplicateName": "PersonDto-1" + }, { "name": "PlaceholderDef", "id": "interface-PlaceholderDef-7cd6b7f0437b0e74cbd408b88e6dceda3d58ed0171b5fc3baf66911b7dae607149d424cee85829f75b69eef30cea3d3978b49dd3d8ecdd5311e71533576fd713", @@ -3662,52 +3756,7 @@ }, { "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": "PolicyQuestionDto", - "id": "interface-PolicyQuestionDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b-1", + "id": "interface-PolicyQuestionDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", "file": "src/app/shared/infrastructure/api-client.ts", "deprecated": false, "deprecationMessage": "", @@ -3748,6 +3797,51 @@ "indexSignatures": [], "kind": 172, "methods": [], + "extends": [] + }, + { + "name": "PolicyQuestionDto", + "id": "interface-PolicyQuestionDto-03b724eabc3fb95affe36abb0bef921275f0c9eeefd35fab64dd906d4d76e5a38fe849098232c0aaeaf7dd32fa6c0a7b7f2a1bfbbf4c03de4a440f860b3c46e1-1", + "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": [], "isDuplicate": true, "duplicateId": 1, @@ -4138,6 +4232,84 @@ "methods": [], "extends": [] }, + { + "name": "RegistrationDto", + "id": "interface-RegistrationDto-10d81af9017230c2e0704c882140be892d1e2a8d6343f8d169bcdb8e99516bc135a9b17faf4ea0fb5d4da561476db228b7f2dab7d27e4c262acdecbe1b7f6241-1", + "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "export type RegistrationStatusDto =\n | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date\n | { tag: 'Geschorst'; geschorstTot: string; reden: string }\n | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string };\n\nexport interface RegistrationDto {\n bigNummer: string;\n naam: string;\n beroep: string;\n registratiedatum: string; // ISO date\n geboortedatum: string;\n status: RegistrationStatusDto;\n}\n\nexport interface AdresDto {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\nexport interface PersonDto {\n naam: string;\n geboortedatum: string; // ISO date\n adres: AdresDto;\n}\n\n/** Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001):\n the eligibility rule lives on the backend; the optional reason lets the UI\n explain itself without knowing the rule. */\nexport interface HerregistratieDecisions {\n eligibleForHerregistratie: boolean;\n herregistratieReason?: string;\n}\n\nexport interface DashboardViewDto {\n registration: RegistrationDto;\n person: PersonDto;\n decisions: HerregistratieDecisions;\n}\n", + "properties": [ + { + "name": "beroep", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 29 + }, + { + "name": "bigNummer", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 27 + }, + { + "name": "geboortedatum", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 31 + }, + { + "name": "naam", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 28 + }, + { + "name": "registratiedatum", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 30 + }, + { + "name": "status", + "deprecated": false, + "deprecationMessage": "", + "type": "RegistrationStatusDto", + "indexKey": "", + "optional": false, + "description": "", + "line": 32 + } + ], + "indexSignatures": [], + "kind": 172, + "methods": [], + "extends": [], + "isDuplicate": true, + "duplicateId": 1, + "duplicateName": "RegistrationDto-1" + }, { "name": "RegistrationStatusDto", "id": "interface-RegistrationStatusDto-134629d19fe3b5e6a76daeb4f10c282202eb1b8380b939c25450bb44b86fcb3df7a15e5ead886ad958b8bb7f158457387fb6c11872acdb4893c4933c2fdef07b", @@ -5112,7 +5284,54 @@ }, { "name": "Valid", - "id": "interface-Valid-f2c910e7845c4a1e95cc75170624db8664de0937455edc1bf8423b6389c310b7ef9ea149dc07ea49f0a4ecad039dbaf09b226864660b29dce075e717125dab0d", + "id": "interface-Valid-69e87ea9a6c36bd372087191f1524450d62f799934711812731fbbd8f034a9e92b7928082471c429b82990c34c47b84b6493735cb994ec6fdfb81b200e990467", + "file": "src/app/registratie/domain/change-request.machine.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "interface", + "sourceCode": "import { Result, assertNever } from '@shared/kernel/fp';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\n\n/** What the user is typing (raw, possibly invalid). */\nexport interface Draft {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\n/** After parsing — postcode is the branded type, so downstream can't get a raw one. */\nexport interface Valid {\n straat: string;\n postcode: Postcode;\n woonplaats: string;\n}\n\nexport type Errors = Partial>;\n\n/**\n * The change-request (adreswijziging) form as one tagged union — the SAME idiom\n * as the wizards, just single-step. `draft`/`errors` exist only while Editing;\n * Submitting/Submitted/Failed carry the parsed `Valid`. Illegal states (submitting\n * an invalid draft, a success screen with errors) are unrepresentable.\n */\nexport type State =\n | { tag: 'Editing'; draft: Draft; errors: Errors }\n | { tag: 'Submitting'; data: Valid }\n | { tag: 'Submitted'; data: Valid; referentie: string }\n | { tag: 'Failed'; data: Valid; error: string };\n\nexport const initial: State = {\n tag: 'Editing',\n draft: { straat: '', postcode: '', woonplaats: '' },\n errors: {},\n};\n\n/** Parse via the value objects; on success hand back a Valid, else per-field errors. */\nfunction validate(draft: Draft): Result {\n const straat = draft.straat.trim();\n const postcode = parsePostcode(draft.postcode);\n const errors: Errors = {};\n if (!straat) errors.straat = $localize`:@@validation.straat:Vul straat en huisnummer in.`;\n if (!postcode.ok) errors.postcode = postcode.error;\n if (straat && postcode.ok) {\n return { ok: true, value: { straat, postcode: postcode.value, woonplaats: draft.woonplaats.trim() } };\n }\n return { ok: false, error: errors };\n}\n\nexport type Msg =\n | { tag: 'SetField'; key: keyof Draft; value: string }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed'; referentie: string }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Reset' }\n | { tag: 'Seed'; state: State }; // mount a specific state (stories/tests)\n\nexport function reduce(s: State, m: Msg): State {\n switch (m.tag) {\n case 'SetField':\n return s.tag === 'Editing' ? { ...s, draft: { ...s.draft, [m.key]: m.value } } : s;\n case 'Submit': {\n if (s.tag !== 'Editing') return s;\n const r = validate(s.draft);\n return r.ok ? { tag: 'Submitting', data: r.value } : { ...s, errors: r.error };\n }\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, referentie: m.referentie } : s;\n case 'SubmitFailed':\n return s.tag === 'Submitting' ? { tag: 'Failed', data: s.data, error: m.error } : s;\n case 'Reset':\n return initial;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n", + "properties": [ + { + "name": "postcode", + "deprecated": false, + "deprecationMessage": "", + "type": "Postcode", + "indexKey": "", + "optional": false, + "description": "", + "line": 14 + }, + { + "name": "straat", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 13 + }, + { + "name": "woonplaats", + "deprecated": false, + "deprecationMessage": "", + "type": "string", + "indexKey": "", + "optional": false, + "description": "", + "line": 15 + } + ], + "indexSignatures": [], + "kind": 172, + "description": "

After parsing — postcode is the branded type, so downstream can't get a raw one.

\n", + "rawdescription": "\nAfter parsing — postcode is the branded type, so downstream can't get a raw one.", + "methods": [], + "extends": [] + }, + { + "name": "Valid", + "id": "interface-Valid-f2c910e7845c4a1e95cc75170624db8664de0937455edc1bf8423b6389c310b7ef9ea149dc07ea49f0a4ecad039dbaf09b226864660b29dce075e717125dab0d-1", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", "deprecated": false, "deprecationMessage": "", @@ -5165,53 +5384,6 @@ "description": "

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

\n", "rawdescription": "\nWhat we have AFTER parsing — branded/typed, guaranteed valid.", "methods": [], - "extends": [] - }, - { - "name": "Valid", - "id": "interface-Valid-69e87ea9a6c36bd372087191f1524450d62f799934711812731fbbd8f034a9e92b7928082471c429b82990c34c47b84b6493735cb994ec6fdfb81b200e990467-1", - "file": "src/app/registratie/domain/change-request.machine.ts", - "deprecated": false, - "deprecationMessage": "", - "type": "interface", - "sourceCode": "import { Result, assertNever } from '@shared/kernel/fp';\nimport { Postcode, parsePostcode } from '@registratie/domain/value-objects/postcode';\n\n/** What the user is typing (raw, possibly invalid). */\nexport interface Draft {\n straat: string;\n postcode: string;\n woonplaats: string;\n}\n\n/** After parsing — postcode is the branded type, so downstream can't get a raw one. */\nexport interface Valid {\n straat: string;\n postcode: Postcode;\n woonplaats: string;\n}\n\nexport type Errors = Partial>;\n\n/**\n * The change-request (adreswijziging) form as one tagged union — the SAME idiom\n * as the wizards, just single-step. `draft`/`errors` exist only while Editing;\n * Submitting/Submitted/Failed carry the parsed `Valid`. Illegal states (submitting\n * an invalid draft, a success screen with errors) are unrepresentable.\n */\nexport type State =\n | { tag: 'Editing'; draft: Draft; errors: Errors }\n | { tag: 'Submitting'; data: Valid }\n | { tag: 'Submitted'; data: Valid; referentie: string }\n | { tag: 'Failed'; data: Valid; error: string };\n\nexport const initial: State = {\n tag: 'Editing',\n draft: { straat: '', postcode: '', woonplaats: '' },\n errors: {},\n};\n\n/** Parse via the value objects; on success hand back a Valid, else per-field errors. */\nfunction validate(draft: Draft): Result {\n const straat = draft.straat.trim();\n const postcode = parsePostcode(draft.postcode);\n const errors: Errors = {};\n if (!straat) errors.straat = $localize`:@@validation.straat:Vul straat en huisnummer in.`;\n if (!postcode.ok) errors.postcode = postcode.error;\n if (straat && postcode.ok) {\n return { ok: true, value: { straat, postcode: postcode.value, woonplaats: draft.woonplaats.trim() } };\n }\n return { ok: false, error: errors };\n}\n\nexport type Msg =\n | { tag: 'SetField'; key: keyof Draft; value: string }\n | { tag: 'Submit' }\n | { tag: 'Retry' }\n | { tag: 'SubmitConfirmed'; referentie: string }\n | { tag: 'SubmitFailed'; error: string }\n | { tag: 'Reset' }\n | { tag: 'Seed'; state: State }; // mount a specific state (stories/tests)\n\nexport function reduce(s: State, m: Msg): State {\n switch (m.tag) {\n case 'SetField':\n return s.tag === 'Editing' ? { ...s, draft: { ...s.draft, [m.key]: m.value } } : s;\n case 'Submit': {\n if (s.tag !== 'Editing') return s;\n const r = validate(s.draft);\n return r.ok ? { tag: 'Submitting', data: r.value } : { ...s, errors: r.error };\n }\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, referentie: m.referentie } : s;\n case 'SubmitFailed':\n return s.tag === 'Submitting' ? { tag: 'Failed', data: s.data, error: m.error } : s;\n case 'Reset':\n return initial;\n case 'Seed':\n return m.state;\n default:\n return assertNever(m);\n }\n}\n", - "properties": [ - { - "name": "postcode", - "deprecated": false, - "deprecationMessage": "", - "type": "Postcode", - "indexKey": "", - "optional": false, - "description": "", - "line": 14 - }, - { - "name": "straat", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "indexKey": "", - "optional": false, - "description": "", - "line": 13 - }, - { - "name": "woonplaats", - "deprecated": false, - "deprecationMessage": "", - "type": "string", - "indexKey": "", - "optional": false, - "description": "", - "line": 15 - } - ], - "indexSignatures": [], - "kind": 172, - "description": "

After parsing — postcode is the branded type, so downstream can't get a raw one.

\n", - "rawdescription": "\nAfter parsing — postcode is the branded type, so downstream can't get a raw one.", - "methods": [], "extends": [], "isDuplicate": true, "duplicateId": 1, @@ -5916,7 +6088,7 @@ }, { "name": "BigProfileStore", - "id": "injectable-BigProfileStore-5439ec3a52b1d508e83c45fca30dd27ccb365828f5bc64eb2baf1206382e9a3dd3f45e1593fc52341541b73bafe13e20afb852c974b59d9083594e3a65ea0cff", + "id": "injectable-BigProfileStore-3f9a194c94a1529ae5b3cc36908ab937645546c3c07642555a6596bca458ff482b05353f089ad22303bc036da99d42fe3195c056c84632be1da9cbfeceae8b08", "file": "src/app/registratie/application/big-profile.store.ts", "properties": [ { @@ -6101,7 +6273,7 @@ "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 const rd = fromResource(this.aantekeningenRes, (v) => !v || v.length === 0);\n return rd.tag === 'Success' ? { tag: 'Success', value: rd.value ?? [] } : rd;\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", + "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 } from '../contracts/dashboard-view.dto';\nimport { BigRegisterAdapter } from '../infrastructure/big-register.adapter';\nimport { DashboardView, 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 const rd = fromResource(this.aantekeningenRes, (v) => !v || v.length === 0);\n return rd.tag === 'Success' ? { tag: 'Success', value: rd.value ?? [] } : rd;\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" }, @@ -6314,7 +6486,7 @@ }, { "name": "BriefStore", - "id": "injectable-BriefStore-ef8f6038df18abcfbe7c1ebb379b79dbbc6b46e4fdb7a4340855796671f6813da7f3a320a8882d9271c70897ebf3ac02a08be01a23525d0b5ea0f53d61ddc46e", + "id": "injectable-BriefStore-64471bf32d35f4e72408e188a2e67790945b41f71863fe2b974a960ea92cf922c96fa00b3129099db77c62406b2efdb9556fd13878fe02e50eff5559b0789a96", "file": "src/app/brief/application/brief.store.ts", "properties": [ { @@ -6326,7 +6498,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 17, + "line": 18, "modifierKind": [ 123 ] @@ -6340,7 +6512,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 91 + "line": 92 }, { "name": "brief", @@ -6351,7 +6523,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 27, + "line": 28, "modifierKind": [ 123 ] @@ -6365,7 +6537,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 22, + "line": 23, "modifierKind": [ 148 ] @@ -6379,7 +6551,7 @@ "indexKey": "", "optional": false, "description": "

Submit is allowed only when required sections are filled AND no blocking errors.

\n", - "line": 41, + "line": 42, "rawdescription": "\nSubmit is allowed only when required sections are filled AND no blocking errors.", "modifierKind": [ 148 @@ -6394,7 +6566,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 38, + "line": 39, "modifierKind": [ 148 ] @@ -6408,7 +6580,7 @@ "indexKey": "", "optional": false, "description": "

The single source of truth for edit permission: drafter, and a status the reducer\ntreats as editable (draft, or rejected — which reopens to draft on the first edit).

\n", - "line": 34, + "line": 35, "rawdescription": "\nThe single source of truth for edit permission: drafter, and a status the reducer\ntreats as editable (draft, or rejected — which reopens to draft on the first edit).", "modifierKind": [ 148 @@ -6423,7 +6595,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 23, + "line": 24, "modifierKind": [ 148 ] @@ -6437,7 +6609,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 20, + "line": 21, "modifierKind": [ 148 ] @@ -6451,7 +6623,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 92 + "line": 93 }, { "name": "role", @@ -6462,7 +6634,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 21, + "line": 22, "modifierKind": [ 148 ] @@ -6476,7 +6648,7 @@ "indexKey": "", "optional": false, "description": "

Surfaced autosave state for the indicator + aria-live region.

\n", - "line": 25, + "line": 26, "rawdescription": "\nSurfaced autosave state for the indicator + aria-live region.", "modifierKind": [ 148 @@ -6490,7 +6662,7 @@ "indexKey": "", "optional": true, "description": "", - "line": 58, + "line": 59, "modifierKind": [ 123 ] @@ -6504,7 +6676,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 93 + "line": 94 }, { "name": "store", @@ -6515,7 +6687,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 18, + "line": 19, "modifierKind": [ 123 ] @@ -6529,7 +6701,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 90 + "line": 91 }, { "name": "unresolved", @@ -6540,7 +6712,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 39, + "line": 40, "modifierKind": [ 148 ] @@ -6562,7 +6734,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 111, + "line": 112, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -6597,7 +6769,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 53, + "line": 54, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nAn edit: apply it optimistically in the pure reducer, then debounce-save.", @@ -6622,7 +6794,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 65, + "line": 66, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -6636,7 +6808,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 46, + "line": 47, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -6649,7 +6821,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 79, + "line": 80, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nDemo \"start over\": recreate the brief server-side and load the fresh view.", @@ -6664,7 +6836,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 59, + "line": 60, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -6687,7 +6859,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 97, + "line": 98, "deprecated": false, "deprecationMessage": "", "modifierKind": [ @@ -6714,7 +6886,7 @@ "deprecationMessage": "", "description": "

Root singleton for the letter: the Elm store (Model + dispatch), the derived\nread-model, and the commands (effects) that call the adapter and dispatch the\noutcome. Mirrors BigProfileStore. All of editable, diagnostics,\nunresolved, canSubmit are DERIVED here — never stored.

\n", "rawdescription": "\n\nRoot singleton for the letter: the Elm store (Model + dispatch), the derived\nread-model, and the commands (effects) that call the adapter and dispatch the\noutcome. Mirrors `BigProfileStore`. All of `editable`, `diagnostics`,\n`unresolved`, `canSubmit` are DERIVED here — never stored.\n", - "sourceCode": "import { Injectable, computed, inject, signal } from '@angular/core';\nimport { Result } from '@shared/kernel/fp';\nimport { createStore } from '@shared/application/store';\nimport { Role, currentRole } from '@shared/infrastructure/role';\nimport { Brief, allDiagnostics, canSubmit, hasBlockingErrors, unresolvedPlaceholders } from '@brief/domain/brief';\nimport { BriefMsg, BriefState, initial, reduce } from '@brief/domain/brief.machine';\nimport { BriefAdapter } from '@brief/infrastructure/brief.adapter';\n\n/**\n * Root singleton for the letter: the Elm store (Model + dispatch), the derived\n * read-model, and the commands (effects) that call the adapter and dispatch the\n * outcome. Mirrors `BigProfileStore`. All of `editable`, `diagnostics`,\n * `unresolved`, `canSubmit` are DERIVED here — never stored.\n */\n@Injectable({ providedIn: 'root' })\nexport class BriefStore {\n private adapter = inject(BriefAdapter);\n private store = createStore(initial, reduce);\n\n readonly model = this.store.model;\n readonly role: Role = currentRole();\n readonly busy = signal(false);\n readonly lastError = signal(null);\n /** Surfaced autosave state for the indicator + aria-live region. */\n readonly saveState = signal<'idle' | 'saving' | 'saved' | 'error'>('idle');\n\n private brief = computed(() => {\n const s = this.model();\n return s.tag === 'loaded' ? s.brief : null;\n });\n\n /** The single source of truth for edit permission: drafter, and a status the reducer\n treats as editable (draft, or rejected — which reopens to draft on the first edit). */\n readonly editable = computed(() => {\n const b = this.brief();\n return !!b && (b.status.tag === 'draft' || b.status.tag === 'rejected') && this.role === 'drafter';\n });\n readonly diagnostics = computed(() => (this.brief() ? allDiagnostics(this.brief()!) : []));\n readonly unresolved = computed(() => (this.brief() ? unresolvedPlaceholders(this.brief()!) : []));\n /** Submit is allowed only when required sections are filled AND no blocking errors. */\n readonly canSubmit = computed(() => {\n const b = this.brief();\n return !!b && canSubmit(b) && !hasBlockingErrors(this.diagnostics());\n });\n\n async load() {\n const r = await this.adapter.load();\n if (r.ok) this.store.dispatch({ tag: 'BriefLoaded', brief: r.value.brief, availablePassages: r.value.availablePassages });\n else this.store.dispatch({ tag: 'BriefLoadFailed', reason: r.error });\n }\n\n /** An edit: apply it optimistically in the pure reducer, then debounce-save. */\n edit(msg: BriefMsg) {\n this.store.dispatch(msg);\n this.scheduleSave();\n }\n\n private saveTimer?: ReturnType;\n private scheduleSave() {\n if (!this.editable()) return;\n clearTimeout(this.saveTimer);\n // ponytail: 600ms debounce like the wizard draft-sync; the server is the store of record.\n this.saveTimer = setTimeout(() => void this.flushSave(), 600);\n }\n private async flushSave() {\n const b = this.brief();\n if (!b) return;\n this.saveState.set('saving');\n const r = await this.adapter.save(b.sections);\n if (r.ok) {\n this.saveState.set('saved');\n } else {\n this.lastError.set(r.error);\n this.saveState.set('error');\n }\n }\n\n /** Demo \"start over\": recreate the brief server-side and load the fresh view. */\n async resetDemo() {\n this.busy.set(true);\n this.lastError.set(null);\n clearTimeout(this.saveTimer);\n const r = await this.adapter.reset();\n this.busy.set(false);\n this.saveState.set('idle');\n if (r.ok) this.store.dispatch({ tag: 'BriefLoaded', brief: r.value.brief, availablePassages: r.value.availablePassages });\n else this.lastError.set(r.error);\n }\n\n submit = () => this.transition(() => this.adapter.submit());\n approve = () => this.transition(() => this.adapter.approve());\n reject = (comments: string) => this.transition(() => this.adapter.reject(comments));\n send = () => this.transition(() => this.adapter.send());\n\n // A transition: flush any pending save, call the server (authoritative), then mirror\n // the returned status through the pure reducer's guarded transition.\n private async transition(action: () => Promise>) {\n this.busy.set(true);\n this.lastError.set(null);\n clearTimeout(this.saveTimer);\n await this.flushSave();\n const r = await action();\n this.busy.set(false);\n if (!r.ok) {\n this.lastError.set(r.error);\n return;\n }\n this.applyServerStatus(r.value);\n }\n\n private applyServerStatus(brief: Brief) {\n const s = brief.status;\n switch (s.tag) {\n case 'submitted':\n this.store.dispatch({ tag: 'Submitted', by: s.submittedBy, at: s.submittedAt });\n break;\n case 'approved':\n this.store.dispatch({ tag: 'Approved', by: s.approvedBy, at: s.approvedAt });\n break;\n case 'rejected':\n this.store.dispatch({ tag: 'Rejected', by: s.rejectedBy, at: s.rejectedAt, comments: s.comments });\n break;\n case 'sent':\n this.store.dispatch({ tag: 'Sent', at: s.sentAt });\n break;\n case 'draft':\n // reopened by a save on a rejected letter — reducer already handled it locally.\n break;\n }\n }\n}\n", + "sourceCode": "import { Injectable, computed, inject, signal } from '@angular/core';\nimport { Result } from '@shared/kernel/fp';\nimport { createStore } from '@shared/application/store';\nimport { Role } from '@shared/domain/role';\nimport { currentRole } from '@shared/infrastructure/role';\nimport { Brief, allDiagnostics, canSubmit, hasBlockingErrors, unresolvedPlaceholders } from '@brief/domain/brief';\nimport { BriefMsg, BriefState, initial, reduce } from '@brief/domain/brief.machine';\nimport { BriefAdapter } from '@brief/infrastructure/brief.adapter';\n\n/**\n * Root singleton for the letter: the Elm store (Model + dispatch), the derived\n * read-model, and the commands (effects) that call the adapter and dispatch the\n * outcome. Mirrors `BigProfileStore`. All of `editable`, `diagnostics`,\n * `unresolved`, `canSubmit` are DERIVED here — never stored.\n */\n@Injectable({ providedIn: 'root' })\nexport class BriefStore {\n private adapter = inject(BriefAdapter);\n private store = createStore(initial, reduce);\n\n readonly model = this.store.model;\n readonly role: Role = currentRole();\n readonly busy = signal(false);\n readonly lastError = signal(null);\n /** Surfaced autosave state for the indicator + aria-live region. */\n readonly saveState = signal<'idle' | 'saving' | 'saved' | 'error'>('idle');\n\n private brief = computed(() => {\n const s = this.model();\n return s.tag === 'loaded' ? s.brief : null;\n });\n\n /** The single source of truth for edit permission: drafter, and a status the reducer\n treats as editable (draft, or rejected — which reopens to draft on the first edit). */\n readonly editable = computed(() => {\n const b = this.brief();\n return !!b && (b.status.tag === 'draft' || b.status.tag === 'rejected') && this.role === 'drafter';\n });\n readonly diagnostics = computed(() => (this.brief() ? allDiagnostics(this.brief()!) : []));\n readonly unresolved = computed(() => (this.brief() ? unresolvedPlaceholders(this.brief()!) : []));\n /** Submit is allowed only when required sections are filled AND no blocking errors. */\n readonly canSubmit = computed(() => {\n const b = this.brief();\n return !!b && canSubmit(b) && !hasBlockingErrors(this.diagnostics());\n });\n\n async load() {\n const r = await this.adapter.load();\n if (r.ok) this.store.dispatch({ tag: 'BriefLoaded', brief: r.value.brief, availablePassages: r.value.availablePassages });\n else this.store.dispatch({ tag: 'BriefLoadFailed', reason: r.error });\n }\n\n /** An edit: apply it optimistically in the pure reducer, then debounce-save. */\n edit(msg: BriefMsg) {\n this.store.dispatch(msg);\n this.scheduleSave();\n }\n\n private saveTimer?: ReturnType;\n private scheduleSave() {\n if (!this.editable()) return;\n clearTimeout(this.saveTimer);\n // ponytail: 600ms debounce like the wizard draft-sync; the server is the store of record.\n this.saveTimer = setTimeout(() => void this.flushSave(), 600);\n }\n private async flushSave() {\n const b = this.brief();\n if (!b) return;\n this.saveState.set('saving');\n const r = await this.adapter.save(b.sections);\n if (r.ok) {\n this.saveState.set('saved');\n } else {\n this.lastError.set(r.error);\n this.saveState.set('error');\n }\n }\n\n /** Demo \"start over\": recreate the brief server-side and load the fresh view. */\n async resetDemo() {\n this.busy.set(true);\n this.lastError.set(null);\n clearTimeout(this.saveTimer);\n const r = await this.adapter.reset();\n this.busy.set(false);\n this.saveState.set('idle');\n if (r.ok) this.store.dispatch({ tag: 'BriefLoaded', brief: r.value.brief, availablePassages: r.value.availablePassages });\n else this.lastError.set(r.error);\n }\n\n submit = () => this.transition(() => this.adapter.submit());\n approve = () => this.transition(() => this.adapter.approve());\n reject = (comments: string) => this.transition(() => this.adapter.reject(comments));\n send = () => this.transition(() => this.adapter.send());\n\n // A transition: flush any pending save, call the server (authoritative), then mirror\n // the returned status through the pure reducer's guarded transition.\n private async transition(action: () => Promise>) {\n this.busy.set(true);\n this.lastError.set(null);\n clearTimeout(this.saveTimer);\n await this.flushSave();\n const r = await action();\n this.busy.set(false);\n if (!r.ok) {\n this.lastError.set(r.error);\n return;\n }\n this.applyServerStatus(r.value);\n }\n\n private applyServerStatus(brief: Brief) {\n const s = brief.status;\n switch (s.tag) {\n case 'submitted':\n this.store.dispatch({ tag: 'Submitted', by: s.submittedBy, at: s.submittedAt });\n break;\n case 'approved':\n this.store.dispatch({ tag: 'Approved', by: s.approvedBy, at: s.approvedAt });\n break;\n case 'rejected':\n this.store.dispatch({ tag: 'Rejected', by: s.rejectedBy, at: s.rejectedAt, comments: s.comments });\n break;\n case 'sent':\n this.store.dispatch({ tag: 'Sent', at: s.sentAt });\n break;\n case 'draft':\n // reopened by a save on a rejected letter — reducer already handled it locally.\n break;\n }\n }\n}\n", "extends": [], "type": "injectable" }, @@ -6758,9 +6930,74 @@ "extends": [], "type": "injectable" }, + { + "name": "ChangeRequestAdapter", + "id": "injectable-ChangeRequestAdapter-3d8898a68bcd237859453ab52edf528af737dcf38c01b739a658d29bdef7d85bb29f9b72d8c4f56fb34b88723404b63d4554497f77a26683869d9ec9f42b5922", + "file": "src/app/registratie/infrastructure/change-request.adapter.ts", + "properties": [ + { + "name": "client", + "defaultValue": "inject(ApiClient)", + "deprecated": false, + "deprecationMessage": "", + "type": "unknown", + "indexKey": "", + "optional": false, + "description": "", + "line": 13, + "modifierKind": [ + 123 + ] + } + ], + "methods": [ + { + "name": "changeRequest", + "args": [ + { + "name": "data", + "type": "Valid", + "optional": false, + "dotDotDotToken": false, + "deprecated": false, + "deprecationMessage": "" + } + ], + "optional": false, + "returnType": "Promise", + "typeParameters": [], + "line": 15, + "deprecated": false, + "deprecationMessage": "", + "modifierKind": [ + 134 + ], + "jsdoctags": [ + { + "name": "data", + "type": "Valid", + "optional": false, + "dotDotDotToken": false, + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], + "deprecated": false, + "deprecationMessage": "", + "description": "

Infrastructure adapter for the adreswijziging POST (/api/v1/change-requests) —\nthe single place the network client lives for change requests, so the command\nand the UI never touch ApiClient. Returns the server reference; the server\nre-validates and is the authority.

\n", + "rawdescription": "\n\nInfrastructure adapter for the adreswijziging POST (`/api/v1/change-requests`) —\nthe single place the network client lives for change requests, so the command\nand the UI never touch `ApiClient`. Returns the server reference; the server\nre-validates and is the authority.\n", + "sourceCode": "import { Injectable, inject } from '@angular/core';\nimport { ApiClient } from '@shared/infrastructure/api-client';\nimport { Valid } from '@registratie/domain/change-request.machine';\n\n/**\n * Infrastructure adapter for the adreswijziging POST (`/api/v1/change-requests`) —\n * the single place the network client lives for change requests, so the command\n * and the UI never touch `ApiClient`. Returns the server reference; the server\n * re-validates and is the authority.\n */\n@Injectable({ providedIn: 'root' })\nexport class ChangeRequestAdapter {\n private client = inject(ApiClient);\n\n async changeRequest(data: Valid): Promise {\n const res = await this.client.changeRequests({\n straat: data.straat,\n postcode: data.postcode,\n woonplaats: data.woonplaats,\n });\n return res.referentie ?? '';\n }\n}\n", + "extends": [], + "type": "injectable" + }, { "name": "DashboardViewAdapter", - "id": "injectable-DashboardViewAdapter-6de764741d263caeed749d480aa0aee15bb4d5df7e4a9c6732aed0c31dacda4fcc9189e3f5d2002e15c0cecd06fca1e68dd12c02c650a85631887b4b3411e3a6", + "id": "injectable-DashboardViewAdapter-c58fb64e1122f4fd879cc04af00e47d5f21ebcd704c19b3ee6ff824dc42bc58cbaf30d615093402c466690d62d19c307b82fa5afbff32e4feeadf419e21ac189", "file": "src/app/registratie/infrastructure/dashboard-view.adapter.ts", "properties": [ { @@ -6772,7 +7009,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 14, + "line": 27, "modifierKind": [ 123 ] @@ -6785,7 +7022,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 24, + "line": 37, "deprecated": false, "deprecationMessage": "" } @@ -6794,7 +7031,7 @@ "deprecationMessage": "", "description": "

Infrastructure adapter for the screen-shaped ("BFF-lite") dashboard endpoint.\nONE call returns registration + person + server-computed decisions. The data\ncomes from the .NET backend (GET /api/dashboard-view) via the generated typed\nclient; the decisions (e.g. herregistratie eligibility) are computed there.

\n", "rawdescription": "\n\nInfrastructure adapter for the screen-shaped (\"BFF-lite\") dashboard endpoint.\nONE call returns registration + person + server-computed decisions. The data\ncomes from the .NET backend (`GET /api/dashboard-view`) via the generated typed\nclient; the decisions (e.g. herregistratie eligibility) are computed there.\n", - "sourceCode": "import { Injectable, inject, resource } from '@angular/core';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { DashboardViewDto, DashboardView } from '@registratie/contracts/dashboard-view.dto';\nimport { ApiClient } from '@shared/infrastructure/api-client';\n\n/**\n * Infrastructure adapter for the screen-shaped (\"BFF-lite\") dashboard endpoint.\n * ONE call returns registration + person + server-computed decisions. The data\n * comes from the .NET backend (`GET /api/dashboard-view`) via the generated typed\n * client; the decisions (e.g. herregistratie eligibility) are computed there.\n */\n@Injectable({ providedIn: 'root' })\nexport class DashboardViewAdapter {\n private client = inject(ApiClient);\n\n // The value is still untrusted JSON — parseDashboardView validates it at the\n // boundary and maps DTO → domain before the app uses it.\n //\n // SEAM (G5): retry-with-backoff for non-mutating reads wraps the loader here —\n // e.g. `loader: () => withBackoff(() => this.client.dashboardView())` — since the\n // adapter is the single place HTTP lives. Reads are safe to retry; MUTATING calls\n // (the submit-* commands) must NEVER auto-retry — and don't. Manual retry\n // (resource.reload via ) covers the UX today, so backoff stays unbuilt.\n dashboardViewResource() {\n return resource({ loader: () => this.client.dashboardView() });\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", + "sourceCode": "import { Injectable, inject, resource } from '@angular/core';\nimport { Result, ok, err } from '@shared/kernel/fp';\nimport { DashboardViewDto, HerregistratieDecisions } from '@registratie/contracts/dashboard-view.dto';\nimport { Registration } from '@registratie/domain/registration';\nimport { Person } from '@registratie/domain/person';\nimport { BigProfile } from '@registratie/domain/big-profile';\nimport { ApiClient } from '@shared/infrastructure/api-client';\n\n/**\n * The parsed, frontend-side view: the wire DTO mapped onto our own domain model.\n * Lives HERE, not in contracts/, because it references domain types — contracts\n * stays import-free. This split is the decoupling seam (CLAUDE.md §1, ADR-0001).\n */\nexport interface DashboardView {\n profile: BigProfile;\n decisions: HerregistratieDecisions;\n}\n\n/**\n * Infrastructure adapter for the screen-shaped (\"BFF-lite\") dashboard endpoint.\n * ONE call returns registration + person + server-computed decisions. The data\n * comes from the .NET backend (`GET /api/dashboard-view`) via the generated typed\n * client; the decisions (e.g. herregistratie eligibility) are computed there.\n */\n@Injectable({ providedIn: 'root' })\nexport class DashboardViewAdapter {\n private client = inject(ApiClient);\n\n // The value is still untrusted JSON — parseDashboardView validates it at the\n // boundary and maps DTO → domain before the app uses it.\n //\n // SEAM (G5): retry-with-backoff for non-mutating reads wraps the loader here —\n // e.g. `loader: () => withBackoff(() => this.client.dashboardView())` — since the\n // adapter is the single place HTTP lives. Reads are safe to retry; MUTATING calls\n // (the submit-* commands) must NEVER auto-retry — and don't. Manual retry\n // (resource.reload via ) covers the UX today, so backoff stays unbuilt.\n dashboardViewResource() {\n return resource({ loader: () => this.client.dashboardView() });\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 // Map wire → domain. The shapes are identical today, so this reads as an\n // identity copy — but the TYPES differ (wire DTO vs domain), so the moment the\n // wire diverges the compiler forces a real mapping here. That's the seam.\n const registration: Registration = {\n bigNummer: reg.bigNummer,\n naam: reg.naam,\n beroep: reg.beroep,\n registratiedatum: reg.registratiedatum,\n geboortedatum: reg.geboortedatum,\n status: reg.status,\n };\n const persoon: Person = { naam: person.naam, geboortedatum: person.geboortedatum, adres: person.adres };\n\n return ok({\n profile: { registration, person: persoon },\n decisions: { eligibleForHerregistratie: d.eligibleForHerregistratie, herregistratieReason: d.herregistratieReason },\n });\n}\n", "extends": [], "type": "injectable" }, @@ -11826,7 +12063,7 @@ }, { "name": "ChangeRequestFormComponent", - "id": "component-ChangeRequestFormComponent-96afe38373a9aa77bdf7b0ea154cc533e853d21165551f855bf77f518e59a3df4fa48261071c66b458fef0aae0fed4b5fcebfd88d1e4efc6a9056eea370b33a2", + "id": "component-ChangeRequestFormComponent-3107a834da8a273b59ee696f400c2421e183946491a8d55a38bc2c085ebe8f69e43290c44de05dd9d20b7f18ab86d4095a68475f62389dcb189dfc8a01f37885", "file": "src/app/registratie/ui/change-request-form/change-request-form.component.ts", "encapsulation": [], "entryComponents": [], @@ -11850,7 +12087,7 @@ "indexKey": "", "optional": false, "description": "

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

\n", - "line": 57, + "line": 60, "rawdescription": "\nOptional seed so Storybook / tests can mount any state directly.", "required": false } @@ -11866,26 +12103,12 @@ "indexKey": "", "optional": false, "description": "

The address shown in the fields — the live draft while editing, the parsed\ndata while submitting/failed (so the user sees what they sent).

\n", - "line": 72, + "line": 75, "rawdescription": "\nThe address shown in the fields — the live draft while editing, the parsed\ndata while submitting/failed (so the user sees what they sent).", "modifierKind": [ 124 ] }, - { - "name": "apiClient", - "defaultValue": "inject(ApiClient)", - "deprecated": false, - "deprecationMessage": "", - "type": "unknown", - "indexKey": "", - "optional": false, - "description": "", - "line": 53, - "modifierKind": [ - 123 - ] - }, { "name": "dispatch", "defaultValue": "this.store.dispatch", @@ -11895,7 +12118,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 60, + "line": 63, "modifierKind": [ 124 ] @@ -11909,7 +12132,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 65, + "line": 68, "modifierKind": [ 123 ] @@ -11923,7 +12146,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 66, + "line": 69, "modifierKind": [ 124 ] @@ -11937,7 +12160,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 67, + "line": 70, "modifierKind": [ 124 ] @@ -11951,7 +12174,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 68, + "line": 71, "modifierKind": [ 124 ] @@ -11965,7 +12188,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 59, + "line": 62, "modifierKind": [ 148 ] @@ -11979,7 +12202,21 @@ "indexKey": "", "optional": false, "description": "", - "line": 54, + "line": 57, + "modifierKind": [ + 123 + ] + }, + { + "name": "submit", + "defaultValue": "createSubmitChangeRequest()", + "deprecated": false, + "deprecationMessage": "", + "type": "unknown", + "indexKey": "", + "optional": false, + "description": "", + "line": 56, "modifierKind": [ 123 ] @@ -11993,7 +12230,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 63, + "line": 66, "modifierKind": [ 124, 148 @@ -12008,7 +12245,7 @@ "indexKey": "", "optional": false, "description": "", - "line": 62, + "line": 65, "modifierKind": [ 124, 148 @@ -12022,7 +12259,7 @@ "optional": false, "returnType": "void", "typeParameters": [], - "line": 85, + "line": 88, "deprecated": false, "deprecationMessage": "" }, @@ -12032,7 +12269,7 @@ "optional": false, "returnType": "any", "typeParameters": [], - "line": 91, + "line": 94, "deprecated": false, "deprecationMessage": "", "rawdescription": "\nEffect: when we entered Submitting, call the command, then dispatch the outcome.", @@ -12049,6 +12286,10 @@ "hostListeners": [], "standalone": false, "imports": [ + { + "name": "FormsModule", + "type": "module" + }, { "name": "ButtonComponent", "type": "component" @@ -12069,7 +12310,7 @@ "description": "

Organism: change-request (adreswijziging) form. Uses the SAME idiom as the\nwizards — all state in one signal driven by the pure reduce\n(change-request.machine.ts), submitted via a submit-* command returning\nResult. Renders the shared <app-address-fields>; the server re-validates.

\n", "rawdescription": "\n\nOrganism: change-request (adreswijziging) form. Uses the SAME idiom as the\nwizards — all state in one signal driven by the pure `reduce`\n(change-request.machine.ts), submitted via a `submit-*` command returning\n`Result`. Renders the shared ``; the server re-validates.\n", "type": "component", - "sourceCode": "import { Component, computed, inject, input } from '@angular/core';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { AddressFieldsComponent, AdresValue, AdresErrors } from '@registratie/ui/address-fields/address-fields.component';\nimport { createStore } from '@shared/application/store';\nimport { whenTag } from '@shared/kernel/fp';\nimport { State, Msg, initial, reduce } from '@registratie/domain/change-request.machine';\nimport { submitChangeRequest } from '@registratie/application/submit-change-request';\nimport { ApiClient } from '@shared/infrastructure/api-client';\n\n/**\n * Organism: change-request (adreswijziging) form. Uses the SAME idiom as the\n * wizards — all state in one signal driven by the pure `reduce`\n * (change-request.machine.ts), submitted via a `submit-*` command returning\n * `Result`. Renders the shared ``; the server re-validates.\n */\n@Component({\n selector: 'app-change-request-form',\n imports: [ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent],\n template: `\n @if (state().tag === 'Submitted') {\n \n Uw adreswijziging is ontvangen (referentie {{ referentie() }}). U ontvangt binnen 5 werkdagen bericht.\n \n
\n Nieuwe wijziging doorgeven\n
\n } @else {\n Adreswijziging doorgeven\n
\n
\n
* verplichte velden
\n
\n \n\n @if (failedError()) {\n Het indienen is niet gelukt: {{ failedError() }}\n }\n\n \n {{ state().tag === 'Submitting' ? submitBezigLabel : submitLabel }}\n \n \n }\n `,\n})\nexport class ChangeRequestFormComponent {\n private apiClient = inject(ApiClient);\n private store = createStore(initial, reduce);\n\n /** Optional seed so Storybook / tests can mount any state directly. */\n seed = input(initial);\n\n readonly state = this.store.model;\n protected dispatch = this.store.dispatch;\n\n protected readonly submitLabel = $localize`:@@changeRequest.submit:Wijziging indienen`;\n protected readonly submitBezigLabel = $localize`:@@changeRequest.submitBezig:Bezig met indienen…`;\n\n private editing = computed(() => whenTag(this.state(), 'Editing'));\n protected errors = computed(() => this.editing()?.errors ?? {});\n protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');\n protected referentie = computed(() => whenTag(this.state(), 'Submitted')?.referentie ?? '');\n\n /** The address shown in the fields — the live draft while editing, the parsed\n data while submitting/failed (so the user sees what they sent). */\n protected adres = computed(() => {\n const s = this.state();\n if (s.tag === 'Editing') return s.draft;\n if (s.tag === 'Submitting' || s.tag === 'Failed') {\n return { straat: s.data.straat, postcode: s.data.postcode, woonplaats: s.data.woonplaats };\n }\n return { straat: '', postcode: '', woonplaats: '' }; // Submitted shows the success alert, not the fields\n });\n\n constructor() {\n queueMicrotask(() => this.dispatch({ tag: 'Seed', state: this.seed() }));\n }\n\n onSubmit() {\n this.dispatch({ tag: 'Submit' });\n this.runIfSubmitting();\n }\n\n /** Effect: when we entered Submitting, call the command, then dispatch the outcome. */\n private async runIfSubmitting() {\n const s = this.state();\n if (s.tag !== 'Submitting') return;\n const r = await submitChangeRequest(this.apiClient, 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", + "sourceCode": "import { Component, computed, input } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { AddressFieldsComponent, AdresValue, AdresErrors } from '@registratie/ui/address-fields/address-fields.component';\nimport { createStore } from '@shared/application/store';\nimport { whenTag } from '@shared/kernel/fp';\nimport { State, Msg, initial, reduce } from '@registratie/domain/change-request.machine';\nimport { createSubmitChangeRequest } from '@registratie/application/submit-change-request';\n\n/**\n * Organism: change-request (adreswijziging) form. Uses the SAME idiom as the\n * wizards — all state in one signal driven by the pure `reduce`\n * (change-request.machine.ts), submitted via a `submit-*` command returning\n * `Result`. Renders the shared ``; the server re-validates.\n */\n@Component({\n selector: 'app-change-request-form',\n imports: [FormsModule, ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent],\n template: `\n @if (state().tag === 'Submitted') {\n \n Uw adreswijziging is ontvangen (referentie {{ referentie() }}). U ontvangt binnen 5 werkdagen bericht.\n \n
\n Nieuwe wijziging doorgeven\n
\n } @else {\n Adreswijziging doorgeven\n
\n
\n
* verplichte velden
\n
\n \n\n @if (failedError()) {\n Het indienen is niet gelukt: {{ failedError() }}\n }\n\n \n {{ state().tag === 'Submitting' ? submitBezigLabel : submitLabel }}\n \n \n }\n `,\n})\nexport class ChangeRequestFormComponent {\n // The submit command owns the ApiClient dependency (via the change-request\n // adapter); the UI holds only this bound command. Field initializer = injection\n // context, like createStore below.\n private submit = createSubmitChangeRequest();\n private store = createStore(initial, reduce);\n\n /** Optional seed so Storybook / tests can mount any state directly. */\n seed = input(initial);\n\n readonly state = this.store.model;\n protected dispatch = this.store.dispatch;\n\n protected readonly submitLabel = $localize`:@@changeRequest.submit:Wijziging indienen`;\n protected readonly submitBezigLabel = $localize`:@@changeRequest.submitBezig:Bezig met indienen…`;\n\n private editing = computed(() => whenTag(this.state(), 'Editing'));\n protected errors = computed(() => this.editing()?.errors ?? {});\n protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');\n protected referentie = computed(() => whenTag(this.state(), 'Submitted')?.referentie ?? '');\n\n /** The address shown in the fields — the live draft while editing, the parsed\n data while submitting/failed (so the user sees what they sent). */\n protected adres = computed(() => {\n const s = this.state();\n if (s.tag === 'Editing') return s.draft;\n if (s.tag === 'Submitting' || s.tag === 'Failed') {\n return { straat: s.data.straat, postcode: s.data.postcode, woonplaats: s.data.woonplaats };\n }\n return { straat: '', postcode: '', woonplaats: '' }; // Submitted shows the success alert, not the fields\n });\n\n constructor() {\n queueMicrotask(() => this.dispatch({ tag: 'Seed', state: this.seed() }));\n }\n\n onSubmit() {\n this.dispatch({ tag: 'Submit' });\n this.runIfSubmitting();\n }\n\n /** Effect: when we entered Submitting, call the command, then dispatch the outcome. */\n private async runIfSubmitting() {\n const s = this.state();\n if (s.tag !== 'Submitting') return;\n const r = await this.submit(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": "", @@ -12079,7 +12320,7 @@ "deprecated": false, "deprecationMessage": "", "args": [], - "line": 79 + "line": 82 }, "extends": [] }, @@ -15718,7 +15959,7 @@ }, { "name": "LetterComposerComponent", - "id": "component-LetterComposerComponent-8d35f6b68df252fa13a7d7b9aba8efad1c597f832fd67b92dca1a8c89f7e082dc065218d790ba1303d6428b773804f260d139caacd9cf987323f168fde593c15", + "id": "component-LetterComposerComponent-fb5f52a6baeed05e91d38f1cade19e37f1122c2ccf22f8a0661ddc4f2bc1c44b9d13e6f60adc0eccc1a7e0731682a7ab94c8a65ece7b0ce62bcdc82ad21e3f0b", "file": "src/app/brief/ui/letter-composer/letter-composer.component.ts", "encapsulation": [], "entryComponents": [], @@ -16086,7 +16327,7 @@ "description": "

Organism: the whole letter — status badge, the editable sections (drafter) or a\nread-only preview (approver / locked), the diagnostics panel, and the action bar\nappropriate to status × role. Presentational: emits edit + transition intents.

\n", "rawdescription": "\nOrganism: the whole letter — status badge, the editable sections (drafter) or a\nread-only preview (approver / locked), the diagnostics panel, and the action bar\nappropriate to status × role. Presentational: emits edit + transition intents.", "type": "component", - "sourceCode": "import { Component, computed, input, output } from '@angular/core';\nimport { Role } from '@shared/infrastructure/role';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { StatusBadgeComponent } from '@shared/ui/status-badge/status-badge.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component';\nimport { Brief, LibraryPassage } from '@brief/domain/brief';\nimport { Diagnostic } from '@brief/domain/placeholders';\nimport { BriefMsg } from '@brief/domain/brief.machine';\nimport { LetterSectionComponent } from '@brief/ui/letter-section/letter-section.component';\nimport { LetterPreviewComponent } from '@brief/ui/letter-preview/letter-preview.component';\nimport { DiagnosticsPanelComponent } from '@brief/ui/diagnostics-panel/diagnostics-panel.component';\nimport { RejectionCommentsComponent } from '@brief/ui/rejection-comments/rejection-comments.component';\n\n/** Organism: the whole letter — status badge, the editable sections (drafter) or a\n read-only preview (approver / locked), the diagnostics panel, and the action bar\n appropriate to status × role. Presentational: emits edit + transition intents. */\n@Component({\n selector: 'app-letter-composer',\n imports: [\n HeadingComponent, StatusBadgeComponent, ButtonComponent, AlertComponent,\n LetterSectionComponent, LetterPreviewComponent, DiagnosticsPanelComponent, RejectionCommentsComponent,\n ],\n styles: [`\n :host{display:block}\n .head{display:flex;justify-content:space-between;align-items:center;gap:var(--rhc-space-max-md);flex-wrap:wrap;margin-block-end:var(--rhc-space-max-lg)}\n .sections{display:grid;gap:var(--rhc-space-max-2xl)}\n .panel{margin-block:var(--rhc-space-max-xl)}\n .bar{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;margin-block-start:var(--rhc-space-max-xl)}\n `],\n template: `\n
\n {{ title() }}\n \n
\n\n @if (status() === 'rejected') {\n \n }\n\n @if (editable()) {\n
\n @for (section of brief().sections; track section.sectionKey) {\n \n }\n
\n } @else {\n \n }\n\n
\n \n
\n\n
\n @switch (status()) {\n @case ('draft') {\n @if (editable()) {\n {{ submitLabel() }}\n @if (!canSubmit()) { {{ submitHint() }} }\n }\n }\n @case ('rejected') {\n @if (editable()) {\n {{ resubmitLabel() }}\n }\n }\n @case ('submitted') {\n @if (role() === 'approver') {\n {{ approveLabel() }}\n \n } @else {\n {{ awaitingText() }}\n }\n }\n @case ('approved') {\n {{ sendLabel() }}\n }\n @case ('sent') {\n {{ sentText() }}\n }\n }\n
\n `,\n})\nexport class LetterComposerComponent {\n brief = input.required();\n availablePassages = input([]);\n diagnostics = input([]);\n editable = input(false);\n role = input.required();\n canSubmit = input(false);\n busy = input(false);\n\n edit = output();\n submit = output();\n approve = output();\n reject = output();\n send = output();\n locate = output();\n\n title = input($localize`:@@brief.title:Brief aan de zorgverlener`);\n submitLabel = input($localize`:@@brief.submit:Indienen ter beoordeling`);\n resubmitLabel = input($localize`:@@brief.resubmit:Opnieuw indienen`);\n submitHint = input($localize`:@@brief.submitHint:Vul eerst alle verplichte secties en los fouten op.`);\n approveLabel = input($localize`:@@brief.approve:Goedkeuren`);\n sendLabel = input($localize`:@@brief.send:Versturen`);\n awaitingText = input($localize`:@@brief.awaiting:De brief wacht op beoordeling door een collega.`);\n sentText = input($localize`:@@brief.sent:De brief is verzonden.`);\n\n protected status = computed(() => this.brief().status.tag);\n protected rejectComments = computed(() => {\n const s = this.brief().status;\n return s.tag === 'rejected' ? s.comments : '';\n });\n\n // The insert menu offers only valid, fillable, non-deprecated fields — inserting an\n // unknown/retired key is structurally impossible.\n protected menu = computed(() =>\n this.brief()\n .placeholders.filter((p) => p.fillable !== false && !p.deprecated)\n .map((p) => ({ key: p.key, label: p.label, autoResolvable: p.autoResolvable })),\n );\n\n protected statusLabel = computed(() => {\n switch (this.status()) {\n case 'draft': return $localize`:@@brief.status.draft:Concept`;\n case 'submitted': return $localize`:@@brief.status.submitted:Ter beoordeling`;\n case 'approved': return $localize`:@@brief.status.approved:Goedgekeurd`;\n case 'rejected': return $localize`:@@brief.status.rejected:Afgewezen`;\n case 'sent': return $localize`:@@brief.status.sent:Verzonden`;\n }\n });\n\n protected statusColor = computed(() => {\n switch (this.status()) {\n case 'draft': return 'var(--rhc-color-border-strong)';\n case 'submitted': return 'var(--rhc-color-oranje-500)';\n case 'approved':\n case 'sent': return 'var(--rhc-color-groen-500)';\n case 'rejected': return 'var(--rhc-color-rood-500)';\n }\n });\n}\n", + "sourceCode": "import { Component, computed, input, output } from '@angular/core';\nimport { Role } from '@shared/domain/role';\nimport { HeadingComponent } from '@shared/ui/heading/heading.component';\nimport { StatusBadgeComponent } from '@shared/ui/status-badge/status-badge.component';\nimport { ButtonComponent } from '@shared/ui/button/button.component';\nimport { AlertComponent } from '@shared/ui/alert/alert.component';\nimport { PlaceholderOption } from '@shared/ui/rich-text-editor/rich-text-editor.component';\nimport { Brief, LibraryPassage } from '@brief/domain/brief';\nimport { Diagnostic } from '@brief/domain/placeholders';\nimport { BriefMsg } from '@brief/domain/brief.machine';\nimport { LetterSectionComponent } from '@brief/ui/letter-section/letter-section.component';\nimport { LetterPreviewComponent } from '@brief/ui/letter-preview/letter-preview.component';\nimport { DiagnosticsPanelComponent } from '@brief/ui/diagnostics-panel/diagnostics-panel.component';\nimport { RejectionCommentsComponent } from '@brief/ui/rejection-comments/rejection-comments.component';\n\n/** Organism: the whole letter — status badge, the editable sections (drafter) or a\n read-only preview (approver / locked), the diagnostics panel, and the action bar\n appropriate to status × role. Presentational: emits edit + transition intents. */\n@Component({\n selector: 'app-letter-composer',\n imports: [\n HeadingComponent, StatusBadgeComponent, ButtonComponent, AlertComponent,\n LetterSectionComponent, LetterPreviewComponent, DiagnosticsPanelComponent, RejectionCommentsComponent,\n ],\n styles: [`\n :host{display:block}\n .head{display:flex;justify-content:space-between;align-items:center;gap:var(--rhc-space-max-md);flex-wrap:wrap;margin-block-end:var(--rhc-space-max-lg)}\n .sections{display:grid;gap:var(--rhc-space-max-2xl)}\n .panel{margin-block:var(--rhc-space-max-xl)}\n .bar{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;margin-block-start:var(--rhc-space-max-xl)}\n `],\n template: `\n
\n {{ title() }}\n \n
\n\n @if (status() === 'rejected') {\n \n }\n\n @if (editable()) {\n
\n @for (section of brief().sections; track section.sectionKey) {\n \n }\n
\n } @else {\n \n }\n\n
\n \n
\n\n
\n @switch (status()) {\n @case ('draft') {\n @if (editable()) {\n {{ submitLabel() }}\n @if (!canSubmit()) { {{ submitHint() }} }\n }\n }\n @case ('rejected') {\n @if (editable()) {\n {{ resubmitLabel() }}\n }\n }\n @case ('submitted') {\n @if (role() === 'approver') {\n {{ approveLabel() }}\n \n } @else {\n {{ awaitingText() }}\n }\n }\n @case ('approved') {\n {{ sendLabel() }}\n }\n @case ('sent') {\n {{ sentText() }}\n }\n }\n
\n `,\n})\nexport class LetterComposerComponent {\n brief = input.required();\n availablePassages = input([]);\n diagnostics = input([]);\n editable = input(false);\n role = input.required();\n canSubmit = input(false);\n busy = input(false);\n\n edit = output();\n submit = output();\n approve = output();\n reject = output();\n send = output();\n locate = output();\n\n title = input($localize`:@@brief.title:Brief aan de zorgverlener`);\n submitLabel = input($localize`:@@brief.submit:Indienen ter beoordeling`);\n resubmitLabel = input($localize`:@@brief.resubmit:Opnieuw indienen`);\n submitHint = input($localize`:@@brief.submitHint:Vul eerst alle verplichte secties en los fouten op.`);\n approveLabel = input($localize`:@@brief.approve:Goedkeuren`);\n sendLabel = input($localize`:@@brief.send:Versturen`);\n awaitingText = input($localize`:@@brief.awaiting:De brief wacht op beoordeling door een collega.`);\n sentText = input($localize`:@@brief.sent:De brief is verzonden.`);\n\n protected status = computed(() => this.brief().status.tag);\n protected rejectComments = computed(() => {\n const s = this.brief().status;\n return s.tag === 'rejected' ? s.comments : '';\n });\n\n // The insert menu offers only valid, fillable, non-deprecated fields — inserting an\n // unknown/retired key is structurally impossible.\n protected menu = computed(() =>\n this.brief()\n .placeholders.filter((p) => p.fillable !== false && !p.deprecated)\n .map((p) => ({ key: p.key, label: p.label, autoResolvable: p.autoResolvable })),\n );\n\n protected statusLabel = computed(() => {\n switch (this.status()) {\n case 'draft': return $localize`:@@brief.status.draft:Concept`;\n case 'submitted': return $localize`:@@brief.status.submitted:Ter beoordeling`;\n case 'approved': return $localize`:@@brief.status.approved:Goedgekeurd`;\n case 'rejected': return $localize`:@@brief.status.rejected:Afgewezen`;\n case 'sent': return $localize`:@@brief.status.sent:Verzonden`;\n }\n });\n\n protected statusColor = computed(() => {\n switch (this.status()) {\n case 'draft': return 'var(--rhc-color-border-strong)';\n case 'submitted': return 'var(--rhc-color-oranje-500)';\n case 'approved':\n case 'sent': return 'var(--rhc-color-groen-500)';\n case 'rejected': return 'var(--rhc-color-rood-500)';\n }\n });\n}\n", "assetsDirs": [], "styleUrlsData": "", "stylesData": "\n :host{display:block}\n .head{display:flex;justify-content:space-between;align-items:center;gap:var(--rhc-space-max-md);flex-wrap:wrap;margin-block-end:var(--rhc-space-max-lg)}\n .sections{display:grid;gap:var(--rhc-space-max-2xl)}\n .panel{margin-block:var(--rhc-space-max-xl)}\n .bar{display:flex;flex-wrap:wrap;gap:var(--rhc-space-max-md);align-items:center;margin-block-start:var(--rhc-space-max-xl)}\n \n", @@ -21663,26 +21904,6 @@ "type": "BriefState", "defaultValue": "{ tag: 'loading' }" }, - { - "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: {}, upload: initialUpload }" - }, - { - "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", @@ -21703,6 +21924,26 @@ "type": "RegistratieState", "defaultValue": "{ tag: 'Invullen', draft: emptyDraft, cursor: 0, errors: {}, upload: initialUpload }" }, + { + "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: {}, upload: initialUpload }" + }, + { + "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": "initialUpload", "ctype": "miscellaneous", @@ -21953,11 +22194,11 @@ "name": "STEPS", "ctype": "miscellaneous", "subtype": "variable", - "file": "src/app/herregistratie/domain/intake.machine.ts", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", "deprecated": false, "deprecationMessage": "", "type": "StepId[]", - "defaultValue": "['buitenland', 'werk', 'review']", + "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" }, @@ -21965,11 +22206,11 @@ "name": "STEPS", "ctype": "miscellaneous", "subtype": "variable", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "file": "src/app/herregistratie/domain/intake.machine.ts", "deprecated": false, "deprecationMessage": "", "type": "StepId[]", - "defaultValue": "['adres', 'beroep', 'controle']", + "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" }, @@ -22267,6 +22508,35 @@ } ] }, + { + "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": "back", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -22325,35 +22595,6 @@ } ] }, - { - "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": "blockActions", "file": "src/app/registratie/domain/block-actions.ts", @@ -22742,6 +22983,16 @@ } ] }, + { + "name": "createSubmitChangeRequest", + "file": "src/app/registratie/application/submit-change-request.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Command factory: binds the change-request adapter (which owns the ApiClient)\nin an injection context and returns the submit function the form calls. Same\nfield-initializer shape as createStore/createDraftSync, so the UI holds an\napplication command — not the network client. Returns a Result, never a thrown\nerror, so the form's reduce can branch on the outcome.

\n", + "args": [] + }, { "name": "createUploadController", "file": "src/app/shared/upload/upload-controller.ts", @@ -22777,7 +23028,7 @@ "subtype": "function", "deprecated": false, "deprecationMessage": "", - "description": "", + "description": "

Dev-only role stand-in (the reading MECHANISM; the Role type is domain). This\nPOC has one faked self-service user and no real identities, so the two-person\nletter workflow (drafter vs approver) is driven by a ?role= query param —\nexactly the pattern of the ?scenario= toggle. The backend receives it as an\nX-Role header (see role.interceptor) and enforces the approver≠drafter rule;\nthe FE derives editable from it.

\n", "args": [], "returnType": "Role" }, @@ -22794,7 +23045,7 @@ }, { "name": "currentStep", - "file": "src/app/herregistratie/domain/intake.machine.ts", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", "ctype": "miscellaneous", "subtype": "function", "deprecated": false, @@ -22823,7 +23074,7 @@ }, { "name": "currentStep", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "file": "src/app/herregistratie/domain/intake.machine.ts", "ctype": "miscellaneous", "subtype": "function", "deprecated": false, @@ -23379,6 +23630,50 @@ } ] }, + { + "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": "gaNaarStap", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -23465,50 +23760,6 @@ } ] }, - { - "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": "groupParagraphs", "file": "src/app/brief/ui/letter-preview/letter-preview.component.ts", @@ -23563,6 +23814,35 @@ } ] }, + { + "name": "hasProgress", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Has the user meaningfully started, so it's worth persisting as a Concept? Excludes\nthe automatic BRP address prefill on step 0 — a bare page visit creates nothing.\nponytail: an address typed at step 0 without any of these signals is not yet\npersisted (created once they advance/choose); accepted regression vs. sessionStorage.

\n", + "args": [ + { + "name": "s", + "type": "Extract", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "boolean", + "jsdoctags": [ + { + "name": "s", + "type": "Extract", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, { "name": "hasProgress", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -23621,35 +23901,6 @@ } ] }, - { - "name": "hasProgress", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", - "ctype": "miscellaneous", - "subtype": "function", - "deprecated": false, - "deprecationMessage": "", - "description": "

Has the user meaningfully started, so it's worth persisting as a Concept? Excludes\nthe automatic BRP address prefill on step 0 — a bare page visit creates nothing.\nponytail: an address typed at step 0 without any of these signals is not yet\npersisted (created once they advance/choose); accepted regression vs. sessionStorage.

\n", - "args": [ - { - "name": "s", - "type": "Extract", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "boolean", - "jsdoctags": [ - { - "name": "s", - "type": "Extract", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, { "name": "herregistratieDeadline", "file": "src/app/registratie/domain/registration.policy.ts", @@ -24784,6 +25035,35 @@ } ] }, + { + "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": "next", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -24842,35 +25122,6 @@ } ] }, - { - "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": "nextLocalIndex", "file": "src/app/brief/domain/brief.machine.ts", @@ -25861,94 +26112,6 @@ } ] }, - { - "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/change-request.machine.ts", @@ -26037,6 +26200,94 @@ } ] }, + { + "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": "reduceUpload", "file": "src/app/shared/upload/upload.machine.ts", @@ -26296,6 +26547,50 @@ } ] }, + { + "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": "resolve", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -26384,50 +26679,6 @@ } ] }, - { - "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", @@ -26714,63 +26965,6 @@ } ] }, - { - "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", @@ -26830,6 +27024,63 @@ } ] }, + { + "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": "setPolicy", "file": "src/app/herregistratie/domain/intake.machine.ts", @@ -27001,6 +27252,35 @@ } ] }, + { + "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": "submit", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -27059,79 +27339,6 @@ } ] }, - { - "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": "submitChangeRequest", - "file": "src/app/registratie/application/submit-change-request.ts", - "ctype": "miscellaneous", - "subtype": "function", - "deprecated": false, - "deprecationMessage": "", - "description": "

Command: POST an address change to the backend (/api/v1/change-requests),\nwhich re-validates and returns a reference. Same shape as the other submit\ncommands — a Result, never a thrown error — so the form's reduce can branch.

\n", - "args": [ - { - "name": "client", - "type": "ApiClient", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "data", - "type": "Valid", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "Promise>", - "jsdoctags": [ - { - "name": "client", - "type": "ApiClient", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "data", - "type": "Valid", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, { "name": "tasksFromProfile", "file": "src/app/registratie/domain/tasks.ts", @@ -27480,6 +27687,50 @@ } ] }, + { + "name": "upload", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Route an upload sub-message through the pure upload reducer (Invullen only).

\n", + "args": [ + { + "name": "s", + "type": "RegistratieState", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "msg", + "type": "UploadMsg", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "RegistratieState", + "jsdoctags": [ + { + "name": "s", + "type": "RegistratieState", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "msg", + "type": "UploadMsg", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + }, { "name": "upload", "file": "src/app/herregistratie/domain/herregistratie.machine.ts", @@ -27525,41 +27776,26 @@ ] }, { - "name": "upload", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "name": "validate", + "file": "src/app/registratie/domain/change-request.machine.ts", "ctype": "miscellaneous", "subtype": "function", "deprecated": false, "deprecationMessage": "", - "description": "

Route an upload sub-message through the pure upload reducer (Invullen only).

\n", + "description": "

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

\n", "args": [ { - "name": "s", - "type": "RegistratieState", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "msg", - "type": "UploadMsg", + "name": "draft", + "type": "Draft", "deprecated": false, "deprecationMessage": "" } ], - "returnType": "RegistratieState", + "returnType": "Result", "jsdoctags": [ { - "name": "s", - "type": "RegistratieState", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "msg", - "type": "UploadMsg", + "name": "draft", + "type": "Draft", "deprecated": false, "deprecationMessage": "", "tagName": { @@ -27613,31 +27849,46 @@ ] }, { - "name": "validate", - "file": "src/app/registratie/domain/change-request.machine.ts", + "name": "validateAll", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", "ctype": "miscellaneous", "subtype": "function", "deprecated": false, "deprecationMessage": "", - "description": "

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

\n", + "description": "

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

\n", "args": [ { - "name": "draft", + "name": "d", "type": "Draft", "deprecated": false, "deprecationMessage": "" + }, + { + "name": "upload", + "type": "UploadState", + "deprecated": false, + "deprecationMessage": "" } ], - "returnType": "Result", + "returnType": "Result", "jsdoctags": [ { - "name": "draft", + "name": "d", "type": "Draft", "deprecated": false, "deprecationMessage": "", "tagName": { "text": "param" } + }, + { + "name": "upload", + "type": "UploadState", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } } ] }, @@ -27686,14 +27937,20 @@ ] }, { - "name": "validateAll", + "name": "validateStep", "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", + "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", @@ -27707,8 +27964,17 @@ "deprecationMessage": "" } ], - "returnType": "Result", + "returnType": "Result", "jsdoctags": [ + { + "name": "step", + "type": "StepId", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, { "name": "d", "type": "Draft", @@ -27788,65 +28054,6 @@ } ] }, - { - "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": "" - }, - { - "name": "upload", - "type": "UploadState", - "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" - } - }, - { - "name": "upload", - "type": "UploadState", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - }, { "name": "whenTag", "file": "src/app/shared/kernel/fp.ts", @@ -28203,22 +28410,22 @@ "name": "Errors", "ctype": "miscellaneous", "subtype": "typealias", - "rawtype": "Partial>", - "file": "src/app/herregistratie/domain/intake.machine.ts", + "rawtype": "Partial>", + "file": "src/app/registratie/domain/change-request.machine.ts", "deprecated": false, "deprecationMessage": "", - "description": "

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

\n", + "description": "", "kind": 184 }, { "name": "Errors", "ctype": "miscellaneous", "subtype": "typealias", - "rawtype": "Partial>", - "file": "src/app/registratie/domain/change-request.machine.ts", + "rawtype": "Partial>", + "file": "src/app/herregistratie/domain/intake.machine.ts", "deprecated": false, "deprecationMessage": "", - "description": "", + "description": "

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

\n", "kind": 184 }, { @@ -28353,6 +28560,17 @@ "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": "RegistrationStatusDto", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "literal type | literal type | literal type", + "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

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

\n

PURE wire shapes: this file imports NOTHING (CLAUDE.md §1, ADR-0001). Enums are\ninlined string-literal unions that describe the wire, not the domain. The\nadapter's parseDashboardView validates this untrusted shape and MAPS it onto\nthe FE domain model (Registration/Person/BigProfile) — that map is the\ndecoupling seam: the wire can change without the domain following.

\n

In production these types are GENERATED from the OpenAPI/TypeSpec spec (one\nsource of truth for both sides), and the decisions block is computed BY THE\nBACKEND — never recomputed on the client. The frontend renders decisions; it\ndoes not own 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", + "kind": 193 + }, { "name": "RemoteData", "ctype": "miscellaneous", @@ -28391,10 +28609,10 @@ "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "\"drafter\" | \"approver\"", - "file": "src/app/shared/infrastructure/role.ts", + "file": "src/app/shared/domain/role.ts", "deprecated": false, "deprecationMessage": "", - "description": "

Dev-only role stand-in. This POC has one faked self-service user and no real\nidentities, so the two-person letter workflow (drafter vs approver) is driven by\na ?role= query param — exactly the pattern of the ?scenario= toggle. The\nbackend receives it as an X-Role header (see role.interceptor) and enforces the\napprover≠drafter rule; the FE derives editable from it.

\n", + "description": "

The two-person letter workflow's role: who is acting, a drafter or an approver.\nA pure domain type (no framework, no reading mechanism) — the ?role= reader and\nthe X-Role header live in shared/infrastructure/role.ts. Consumers (brief.store,\nletter-composer) depend on this type, not on how the role is obtained.

\n", "kind": 193 }, { @@ -28445,22 +28663,22 @@ "name": "StepId", "ctype": "miscellaneous", "subtype": "typealias", - "rawtype": "\"buitenland\" | \"werk\" | \"review\"", - "file": "src/app/herregistratie/domain/intake.machine.ts", + "rawtype": "\"adres\" | \"beroep\" | \"controle\"", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", "deprecated": false, "deprecationMessage": "", - "description": "

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

\n", + "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": "StepId", "ctype": "miscellaneous", "subtype": "typealias", - "rawtype": "\"adres\" | \"beroep\" | \"controle\"", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "rawtype": "\"buitenland\" | \"werk\" | \"review\"", + "file": "src/app/herregistratie/domain/intake.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", + "description": "

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

\n", "kind": 193 }, { @@ -28889,6 +29107,18 @@ "defaultValue": "{ tag: 'loading' }" } ], + "src/app/registratie/domain/change-request.machine.ts": [ + { + "name": "initial", + "ctype": "miscellaneous", + "subtype": "variable", + "file": "src/app/registratie/domain/change-request.machine.ts", + "deprecated": false, + "deprecationMessage": "", + "type": "State", + "defaultValue": "{\n tag: 'Editing',\n draft: { straat: '', postcode: '', woonplaats: '' },\n errors: {},\n}" + } + ], "src/app/herregistratie/domain/herregistratie.machine.ts": [ { "name": "initial", @@ -28937,18 +29167,6 @@ "description": "

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

\n" } ], - "src/app/registratie/domain/change-request.machine.ts": [ - { - "name": "initial", - "ctype": "miscellaneous", - "subtype": "variable", - "file": "src/app/registratie/domain/change-request.machine.ts", - "deprecated": false, - "deprecationMessage": "", - "type": "State", - "defaultValue": "{\n tag: 'Editing',\n draft: { straat: '', postcode: '', woonplaats: '' },\n errors: {},\n}" - } - ], "src/app/shared/ui/radio-group/radio-group.component.ts": [ { "name": "JA_NEE", @@ -30890,6 +31108,826 @@ ] } ], + "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": "hasProgress", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Has the user meaningfully started, so it's worth persisting as a Concept? Excludes\nthe automatic BRP address prefill on step 0 — a bare page visit creates nothing.\nponytail: an address typed at step 0 without any of these signals is not yet\npersisted (created once they advance/choose); accepted regression vs. sessionStorage.

\n", + "args": [ + { + "name": "s", + "type": "Extract", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "boolean", + "jsdoctags": [ + { + "name": "s", + "type": "Extract", + "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": "upload", + "file": "src/app/registratie/domain/registratie-wizard.machine.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Route an upload sub-message through the pure upload reducer (Invullen only).

\n", + "args": [ + { + "name": "s", + "type": "RegistratieState", + "deprecated": false, + "deprecationMessage": "" + }, + { + "name": "msg", + "type": "UploadMsg", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "RegistratieState", + "jsdoctags": [ + { + "name": "s", + "type": "RegistratieState", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "msg", + "type": "UploadMsg", + "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": "" + }, + { + "name": "upload", + "type": "UploadState", + "deprecated": false, + "deprecationMessage": "" + } + ], + "returnType": "Result", + "jsdoctags": [ + { + "name": "d", + "type": "Draft", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + }, + { + "name": "upload", + "type": "UploadState", + "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": "" + }, + { + "name": "upload", + "type": "UploadState", + "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" + } + }, + { + "name": "upload", + "type": "UploadState", + "deprecated": false, + "deprecationMessage": "", + "tagName": { + "text": "param" + } + } + ] + } + ], "src/app/herregistratie/domain/herregistratie.machine.ts": [ { "name": "back", @@ -31812,826 +32850,6 @@ ] } ], - "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": "hasProgress", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", - "ctype": "miscellaneous", - "subtype": "function", - "deprecated": false, - "deprecationMessage": "", - "description": "

Has the user meaningfully started, so it's worth persisting as a Concept? Excludes\nthe automatic BRP address prefill on step 0 — a bare page visit creates nothing.\nponytail: an address typed at step 0 without any of these signals is not yet\npersisted (created once they advance/choose); accepted regression vs. sessionStorage.

\n", - "args": [ - { - "name": "s", - "type": "Extract", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "boolean", - "jsdoctags": [ - { - "name": "s", - "type": "Extract", - "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": "upload", - "file": "src/app/registratie/domain/registratie-wizard.machine.ts", - "ctype": "miscellaneous", - "subtype": "function", - "deprecated": false, - "deprecationMessage": "", - "description": "

Route an upload sub-message through the pure upload reducer (Invullen only).

\n", - "args": [ - { - "name": "s", - "type": "RegistratieState", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "msg", - "type": "UploadMsg", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "RegistratieState", - "jsdoctags": [ - { - "name": "s", - "type": "RegistratieState", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "msg", - "type": "UploadMsg", - "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": "" - }, - { - "name": "upload", - "type": "UploadState", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "Result", - "jsdoctags": [ - { - "name": "d", - "type": "Draft", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "upload", - "type": "UploadState", - "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": "" - }, - { - "name": "upload", - "type": "UploadState", - "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" - } - }, - { - "name": "upload", - "type": "UploadState", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - } - ], "src/app/registratie/domain/block-actions.ts": [ { "name": "blockActions", @@ -33359,6 +33577,18 @@ ] } ], + "src/app/registratie/application/submit-change-request.ts": [ + { + "name": "createSubmitChangeRequest", + "file": "src/app/registratie/application/submit-change-request.ts", + "ctype": "miscellaneous", + "subtype": "function", + "deprecated": false, + "deprecationMessage": "", + "description": "

Command factory: binds the change-request adapter (which owns the ApiClient)\nin an injection context and returns the submit function the form calls. Same\nfield-initializer shape as createStore/createDraftSync, so the UI holds an\napplication command — not the network client. Returns a Result, never a thrown\nerror, so the form's reduce can branch on the outcome.

\n", + "args": [] + } + ], "src/app/shared/upload/upload-controller.ts": [ { "name": "createUploadController", @@ -33397,7 +33627,7 @@ "subtype": "function", "deprecated": false, "deprecationMessage": "", - "description": "", + "description": "

Dev-only role stand-in (the reading MECHANISM; the Role type is domain). This\nPOC has one faked self-service user and no real identities, so the two-person\nletter workflow (drafter vs approver) is driven by a ?role= query param —\nexactly the pattern of the ?scenario= toggle. The backend receives it as an\nX-Role header (see role.interceptor) and enforces the approver≠drafter rule;\nthe FE derives editable from it.

\n", "args": [], "returnType": "Role" } @@ -34974,52 +35204,6 @@ ] } ], - "src/app/registratie/application/submit-change-request.ts": [ - { - "name": "submitChangeRequest", - "file": "src/app/registratie/application/submit-change-request.ts", - "ctype": "miscellaneous", - "subtype": "function", - "deprecated": false, - "deprecationMessage": "", - "description": "

Command: POST an address change to the backend (/api/v1/change-requests),\nwhich re-validates and returns a reference. Same shape as the other submit\ncommands — a Result, never a thrown error — so the form's reduce can branch.

\n", - "args": [ - { - "name": "client", - "type": "ApiClient", - "deprecated": false, - "deprecationMessage": "" - }, - { - "name": "data", - "type": "Valid", - "deprecated": false, - "deprecationMessage": "" - } - ], - "returnType": "Promise>", - "jsdoctags": [ - { - "name": "client", - "type": "ApiClient", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - }, - { - "name": "data", - "type": "Valid", - "deprecated": false, - "deprecationMessage": "", - "tagName": { - "text": "param" - } - } - ] - } - ], "src/app/shared/infrastructure/api-client.ts": [ { "name": "throwException", @@ -35584,6 +35768,41 @@ "kind": 193 } ], + "src/app/registratie/domain/change-request.machine.ts": [ + { + "name": "Errors", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "Partial>", + "file": "src/app/registratie/domain/change-request.machine.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 184 + }, + { + "name": "Msg", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type", + "file": "src/app/registratie/domain/change-request.machine.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "", + "kind": 193 + }, + { + "name": "State", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "literal type | literal type | literal type | literal type", + "file": "src/app/registratie/domain/change-request.machine.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

The change-request (adreswijziging) form as one tagged union — the SAME idiom\nas the wizards, just single-step. draft/errors exist only while Editing;\nSubmitting/Submitted/Failed carry the parsed Valid. Illegal states (submitting\nan invalid draft, a success screen with errors) are unrepresentable.

\n", + "kind": 193 + } + ], "src/app/herregistratie/domain/intake.machine.ts": [ { "name": "Errors", @@ -35641,41 +35860,6 @@ "kind": 193 } ], - "src/app/registratie/domain/change-request.machine.ts": [ - { - "name": "Errors", - "ctype": "miscellaneous", - "subtype": "typealias", - "rawtype": "Partial>", - "file": "src/app/registratie/domain/change-request.machine.ts", - "deprecated": false, - "deprecationMessage": "", - "description": "", - "kind": 184 - }, - { - "name": "Msg", - "ctype": "miscellaneous", - "subtype": "typealias", - "rawtype": "literal type | literal type | literal type | literal type | literal type | literal type | literal type", - "file": "src/app/registratie/domain/change-request.machine.ts", - "deprecated": false, - "deprecationMessage": "", - "description": "", - "kind": 193 - }, - { - "name": "State", - "ctype": "miscellaneous", - "subtype": "typealias", - "rawtype": "literal type | literal type | literal type | literal type", - "file": "src/app/registratie/domain/change-request.machine.ts", - "deprecated": false, - "deprecationMessage": "", - "description": "

The change-request (adreswijziging) form as one tagged union — the SAME idiom\nas the wizards, just single-step. draft/errors exist only while Editing;\nSubmitting/Submitted/Failed carry the parsed Valid. Illegal states (submitting\nan invalid draft, a success screen with errors) are unrepresentable.

\n", - "kind": 193 - } - ], "src/app/shared/kernel/rich-text.ts": [ { "name": "Mark", @@ -35726,6 +35910,19 @@ "kind": 188 } ], + "src/app/registratie/contracts/dashboard-view.dto.ts": [ + { + "name": "RegistrationStatusDto", + "ctype": "miscellaneous", + "subtype": "typealias", + "rawtype": "literal type | literal type | literal type", + "file": "src/app/registratie/contracts/dashboard-view.dto.ts", + "deprecated": false, + "deprecationMessage": "", + "description": "

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

\n

PURE wire shapes: this file imports NOTHING (CLAUDE.md §1, ADR-0001). Enums are\ninlined string-literal unions that describe the wire, not the domain. The\nadapter's parseDashboardView validates this untrusted shape and MAPS it onto\nthe FE domain model (Registration/Person/BigProfile) — that map is the\ndecoupling seam: the wire can change without the domain following.

\n

In production these types are GENERATED from the OpenAPI/TypeSpec spec (one\nsource of truth for both sides), and the decisions block is computed BY THE\nBACKEND — never recomputed on the client. The frontend renders decisions; it\ndoes not own 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", + "kind": 193 + } + ], "src/app/shared/application/remote-data.ts": [ { "name": "RemoteData", @@ -35739,16 +35936,16 @@ "kind": 193 } ], - "src/app/shared/infrastructure/role.ts": [ + "src/app/shared/domain/role.ts": [ { "name": "Role", "ctype": "miscellaneous", "subtype": "typealias", "rawtype": "\"drafter\" | \"approver\"", - "file": "src/app/shared/infrastructure/role.ts", + "file": "src/app/shared/domain/role.ts", "deprecated": false, "deprecationMessage": "", - "description": "

Dev-only role stand-in. This POC has one faked self-service user and no real\nidentities, so the two-person letter workflow (drafter vs approver) is driven by\na ?role= query param — exactly the pattern of the ?scenario= toggle. The\nbackend receives it as an X-Role header (see role.interceptor) and enforces the\napprover≠drafter rule; the FE derives editable from it.

\n", + "description": "

The two-person letter workflow's role: who is acting, a drafter or an approver.\nA pure domain type (no framework, no reading mechanism) — the ?role= reader and\nthe X-Role header live in shared/infrastructure/role.ts. Consumers (brief.store,\nletter-composer) depend on this type, not on how the role is obtained.

\n", "kind": 193 } ], @@ -37190,7 +37387,7 @@ "type": "function", "linktype": "miscellaneous", "linksubtype": "function", - "name": "submitChangeRequest", + "name": "createSubmitChangeRequest", "coveragePercent": 100, "coverageCount": "1/1", "status": "very-good" @@ -37208,18 +37405,18 @@ "filePath": "src/app/registratie/contracts/dashboard-view.dto.ts", "type": "interface", "linktype": "interface", - "name": "DashboardView", - "coveragePercent": 33, - "coverageCount": "1/3", - "status": "medium" + "name": "AdresDto", + "coveragePercent": 0, + "coverageCount": "0/4", + "status": "low" }, { "filePath": "src/app/registratie/contracts/dashboard-view.dto.ts", "type": "interface", "linktype": "interface", "name": "DashboardViewDto", - "coveragePercent": 25, - "coverageCount": "1/4", + "coveragePercent": 0, + "coverageCount": "0/4", "status": "low" }, { @@ -37231,6 +37428,34 @@ "coverageCount": "1/3", "status": "medium" }, + { + "filePath": "src/app/registratie/contracts/dashboard-view.dto.ts", + "type": "interface", + "linktype": "interface", + "name": "PersonDto", + "coveragePercent": 0, + "coverageCount": "0/4", + "status": "low" + }, + { + "filePath": "src/app/registratie/contracts/dashboard-view.dto.ts", + "type": "interface", + "linktype": "interface", + "name": "RegistrationDto", + "coveragePercent": 0, + "coverageCount": "0/7", + "status": "low" + }, + { + "filePath": "src/app/registratie/contracts/dashboard-view.dto.ts", + "type": "type alias", + "linktype": "miscellaneous", + "linksubtype": "typealias", + "name": "RegistrationStatusDto", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, { "filePath": "src/app/registratie/contracts/duo-diplomas.dto.ts", "type": "interface", @@ -38051,6 +38276,15 @@ "coverageCount": "1/1", "status": "very-good" }, + { + "filePath": "src/app/registratie/infrastructure/change-request.adapter.ts", + "type": "injectable", + "linktype": "injectable", + "name": "ChangeRequestAdapter", + "coveragePercent": 33, + "coverageCount": "1/3", + "status": "medium" + }, { "filePath": "src/app/registratie/infrastructure/dashboard-view.adapter.ts", "type": "injectable", @@ -38060,6 +38294,15 @@ "coverageCount": "1/3", "status": "medium" }, + { + "filePath": "src/app/registratie/infrastructure/dashboard-view.adapter.ts", + "type": "interface", + "linktype": "interface", + "name": "DashboardView", + "coveragePercent": 33, + "coverageCount": "1/3", + "status": "medium" + }, { "filePath": "src/app/registratie/infrastructure/dashboard-view.adapter.ts", "type": "function", @@ -38377,6 +38620,16 @@ "coverageCount": "0/1", "status": "low" }, + { + "filePath": "src/app/shared/domain/role.ts", + "type": "type alias", + "linktype": "miscellaneous", + "linksubtype": "typealias", + "name": "Role", + "coveragePercent": 100, + "coverageCount": "1/1", + "status": "very-good" + }, { "filePath": "src/app/shared/infrastructure/api-client.provider.ts", "type": "function", @@ -38858,16 +39111,6 @@ "linktype": "miscellaneous", "linksubtype": "function", "name": "currentRole", - "coveragePercent": 0, - "coverageCount": "0/1", - "status": "low" - }, - { - "filePath": "src/app/shared/infrastructure/role.ts", - "type": "type alias", - "linktype": "miscellaneous", - "linksubtype": "typealias", - "name": "Role", "coveragePercent": 100, "coverageCount": "1/1", "status": "very-good" diff --git a/eslint.config.mjs b/eslint.config.mjs index de31748..257e3a2 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -92,4 +92,55 @@ export default [ ], }, }, + + // contracts/ is the FE⇄BE wire seam: pure DTO shapes that must import NOTHING + // (CLAUDE.md §1, ADR-0001) — not Angular, not a context alias, not relative app + // code. Enums are inlined string-literal unions; the adapter's parse* maps them. + // (This comes after the per-context rules so it wins for contracts files.) + { + files: ['src/app/**/contracts/**/*.ts'], + rules: { + 'no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: ['@angular/**', '@shared/**', '@auth/**', '@registratie/**', '@herregistratie/**', '@brief/**', './*', '../*', './**', '../**'], + message: 'contracts/ is the wire seam — it must import NOTHING (pure DTO shapes). Map wire → domain in the infrastructure adapter, not here.', + }, + ], + }, + ], + }, + }, + + // BFF-lite anti-corruption boundary (ADR-0001): the ApiClient (the network + // client) may be imported as a VALUE only from infrastructure-role files. + // Type-only imports of generated wire DTOs are allowed anywhere — they grant no + // network access. UI/application reach the network through an adapter or command. + { + files: ['src/app/**/*.ts'], + plugins: { '@typescript-eslint': tseslint.plugin }, + rules: { + '@typescript-eslint/no-restricted-imports': [ + 'error', + { + patterns: [ + { + group: ['@shared/infrastructure/api-client'], + allowTypeImports: true, + message: 'The ApiClient lives only in infrastructure/ adapters (ADR-0001). UI/application call an adapter or a command, not the network client. (Type-only DTO imports are fine: use `import type`.)', + }, + ], + }, + ], + }, + }, + // …the infrastructure adapters ARE that boundary and own the client. shared/upload + // is a feature-scoped adapter that lives outside a /infrastructure/ folder. + { + files: ['src/app/**/infrastructure/**/*.ts', 'src/app/shared/upload/**/*.ts'], + plugins: { '@typescript-eslint': tseslint.plugin }, + rules: { '@typescript-eslint/no-restricted-imports': 'off' }, + }, ]; diff --git a/src/app/brief/application/brief.store.ts b/src/app/brief/application/brief.store.ts index ee87b4f..703d5f4 100644 --- a/src/app/brief/application/brief.store.ts +++ b/src/app/brief/application/brief.store.ts @@ -1,7 +1,8 @@ import { Injectable, computed, inject, signal } from '@angular/core'; import { Result } from '@shared/kernel/fp'; import { createStore } from '@shared/application/store'; -import { Role, currentRole } from '@shared/infrastructure/role'; +import { Role } from '@shared/domain/role'; +import { currentRole } from '@shared/infrastructure/role'; import { Brief, allDiagnostics, canSubmit, hasBlockingErrors, unresolvedPlaceholders } from '@brief/domain/brief'; import { BriefMsg, BriefState, initial, reduce } from '@brief/domain/brief.machine'; import { BriefAdapter } from '@brief/infrastructure/brief.adapter'; diff --git a/src/app/brief/ui/letter-composer/letter-composer.component.ts b/src/app/brief/ui/letter-composer/letter-composer.component.ts index aeab0c0..b4c3614 100644 --- a/src/app/brief/ui/letter-composer/letter-composer.component.ts +++ b/src/app/brief/ui/letter-composer/letter-composer.component.ts @@ -1,5 +1,5 @@ import { Component, computed, input, output } from '@angular/core'; -import { Role } from '@shared/infrastructure/role'; +import { Role } from '@shared/domain/role'; import { HeadingComponent } from '@shared/ui/heading/heading.component'; import { StatusBadgeComponent } from '@shared/ui/status-badge/status-badge.component'; import { ButtonComponent } from '@shared/ui/button/button.component'; diff --git a/src/app/registratie/application/big-profile.store.ts b/src/app/registratie/application/big-profile.store.ts index 74e4e5a..a79cbba 100644 --- a/src/app/registratie/application/big-profile.store.ts +++ b/src/app/registratie/application/big-profile.store.ts @@ -2,9 +2,9 @@ import { Injectable, computed, inject, signal } from '@angular/core'; import { RemoteData, fromResource, map } from '@shared/application/remote-data'; import { Aantekening } from '../domain/registration'; import { BigProfile } from '../domain/big-profile'; -import { HerregistratieDecisions, DashboardView } from '../contracts/dashboard-view.dto'; +import { HerregistratieDecisions } from '../contracts/dashboard-view.dto'; import { BigRegisterAdapter } from '../infrastructure/big-register.adapter'; -import { DashboardViewAdapter, parseDashboardView } from '../infrastructure/dashboard-view.adapter'; +import { DashboardView, DashboardViewAdapter, parseDashboardView } from '../infrastructure/dashboard-view.adapter'; type Err = Error | undefined; diff --git a/src/app/registratie/application/draft-sync.ts b/src/app/registratie/application/draft-sync.ts index 9b9d049..2ee4c08 100644 --- a/src/app/registratie/application/draft-sync.ts +++ b/src/app/registratie/application/draft-sync.ts @@ -2,7 +2,7 @@ import { DestroyRef, effect, inject } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Result } from '@shared/kernel/fp'; import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit'; -import { SubmitApplicationRequest, SubmitApplicationResponse } from '@shared/infrastructure/api-client'; +import type { SubmitApplicationRequest, SubmitApplicationResponse } from '@shared/infrastructure/api-client'; import { AanvraagType } from '@registratie/domain/aanvraag'; import { ApplicationsAdapter, parseApplications } from '@registratie/infrastructure/applications.adapter'; diff --git a/src/app/registratie/application/submit-change-request.ts b/src/app/registratie/application/submit-change-request.ts index 02ae11c..43fe11a 100644 --- a/src/app/registratie/application/submit-change-request.ts +++ b/src/app/registratie/application/submit-change-request.ts @@ -1,20 +1,18 @@ +import { inject } from '@angular/core'; import { Result } from '@shared/kernel/fp'; import { Valid } from '@registratie/domain/change-request.machine'; -import { ApiClient } from '@shared/infrastructure/api-client'; import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit'; +import { ChangeRequestAdapter } from '@registratie/infrastructure/change-request.adapter'; /** - * Command: POST an address change to the backend (`/api/v1/change-requests`), - * which re-validates and returns a reference. Same shape as the other submit - * commands — a `Result`, never a thrown error — so the form's reduce can branch. + * Command factory: binds the change-request adapter (which owns the `ApiClient`) + * in an injection context and returns the submit function the form calls. Same + * field-initializer shape as `createStore`/`createDraftSync`, so the UI holds an + * application command — not the network client. Returns a `Result`, never a thrown + * error, so the form's reduce can branch on the outcome. */ -export function submitChangeRequest(client: ApiClient, data: Valid): Promise> { - return runSubmit(async () => { - const res = await client.changeRequests({ - straat: data.straat, - postcode: data.postcode, - woonplaats: data.woonplaats, - }); - return res.referentie ?? ''; - }, SUBMIT_FAILED); +export function createSubmitChangeRequest() { + const adapter = inject(ChangeRequestAdapter); + return (data: Valid): Promise> => + runSubmit(() => adapter.changeRequest(data), SUBMIT_FAILED); } diff --git a/src/app/registratie/contracts/dashboard-view.dto.ts b/src/app/registratie/contracts/dashboard-view.dto.ts index 790a3d3..cc86842 100644 --- a/src/app/registratie/contracts/dashboard-view.dto.ts +++ b/src/app/registratie/contracts/dashboard-view.dto.ts @@ -1,36 +1,59 @@ -import { Registration } from '@registratie/domain/registration'; -import { Person } from '@registratie/domain/person'; -import { BigProfile } from '@registratie/domain/big-profile'; - /** * WIRE CONTRACT for the dashboard screen — the "BFF-lite" response. * - * In production this type is GENERATED from the OpenAPI/TypeSpec spec (one source - * of truth for both sides), and the `decisions` block is computed BY THE BACKEND - * — never recomputed on the client. The frontend renders decisions; it does not - * own the rules. See docs/architecture/0001-bff-lite-decision-dtos.md. + * PURE wire shapes: this file imports NOTHING (CLAUDE.md §1, ADR-0001). Enums are + * inlined string-literal unions that describe the wire, not the domain. The + * adapter's `parseDashboardView` validates this untrusted shape and MAPS it onto + * the FE domain model (Registration/Person/BigProfile) — that map is the + * decoupling seam: the wire can change without the domain following. + * + * In production these types are GENERATED from the OpenAPI/TypeSpec spec (one + * source of truth for both sides), and the `decisions` block is computed BY THE + * BACKEND — never recomputed on the client. The frontend renders decisions; it + * does not own the rules. See docs/architecture/0001-bff-lite-decision-dtos.md. * * One screen-shaped call replaces the previous three (BIG-register + BRP + …), * so the page always sees one consistent snapshot instead of three independently * loading/erroring resources. */ -export interface DashboardViewDto { - registration: Registration; - person: Person; - decisions: HerregistratieDecisions; + +/** Registration status on the wire: the discriminant tags as they arrive. */ +export type RegistrationStatusDto = + | { tag: 'Geregistreerd'; herregistratieDatum: string } // ISO date + | { tag: 'Geschorst'; geschorstTot: string; reden: string } + | { tag: 'Doorgehaald'; doorgehaaldOp: string; reden: string }; + +export interface RegistrationDto { + bigNummer: string; + naam: string; + beroep: string; + registratiedatum: string; // ISO date + geboortedatum: string; + status: RegistrationStatusDto; } -/** Server-computed decisions. The eligibility rule lives on the backend; the - optional reason lets the UI explain itself without knowing the rule. */ +export interface AdresDto { + straat: string; + postcode: string; + woonplaats: string; +} + +export interface PersonDto { + naam: string; + geboortedatum: string; // ISO date + adres: AdresDto; +} + +/** Server-computed decisions. Rendered by the FE as-is (decision DTO, ADR-0001): + the eligibility rule lives on the backend; the optional reason lets the UI + explain itself without knowing the rule. */ export interface HerregistratieDecisions { eligibleForHerregistratie: boolean; herregistratieReason?: string; } -/** The parsed, frontend-side view (DTO mapped onto our own domain model). Keeping - this distinct from DashboardViewDto is the decoupling seam: the wire shape can - change without the FE domain following, and vice-versa. */ -export interface DashboardView { - profile: BigProfile; +export interface DashboardViewDto { + registration: RegistrationDto; + person: PersonDto; decisions: HerregistratieDecisions; } diff --git a/src/app/registratie/infrastructure/change-request.adapter.ts b/src/app/registratie/infrastructure/change-request.adapter.ts new file mode 100644 index 0000000..30747dc --- /dev/null +++ b/src/app/registratie/infrastructure/change-request.adapter.ts @@ -0,0 +1,23 @@ +import { Injectable, inject } from '@angular/core'; +import { ApiClient } from '@shared/infrastructure/api-client'; +import { Valid } from '@registratie/domain/change-request.machine'; + +/** + * Infrastructure adapter for the adreswijziging POST (`/api/v1/change-requests`) — + * the single place the network client lives for change requests, so the command + * and the UI never touch `ApiClient`. Returns the server reference; the server + * re-validates and is the authority. + */ +@Injectable({ providedIn: 'root' }) +export class ChangeRequestAdapter { + private client = inject(ApiClient); + + async changeRequest(data: Valid): Promise { + const res = await this.client.changeRequests({ + straat: data.straat, + postcode: data.postcode, + woonplaats: data.woonplaats, + }); + return res.referentie ?? ''; + } +} diff --git a/src/app/registratie/infrastructure/dashboard-view.adapter.ts b/src/app/registratie/infrastructure/dashboard-view.adapter.ts index e85eb17..d510763 100644 --- a/src/app/registratie/infrastructure/dashboard-view.adapter.ts +++ b/src/app/registratie/infrastructure/dashboard-view.adapter.ts @@ -1,8 +1,21 @@ import { Injectable, inject, resource } from '@angular/core'; import { Result, ok, err } from '@shared/kernel/fp'; -import { DashboardViewDto, DashboardView } from '@registratie/contracts/dashboard-view.dto'; +import { DashboardViewDto, HerregistratieDecisions } from '@registratie/contracts/dashboard-view.dto'; +import { Registration } from '@registratie/domain/registration'; +import { Person } from '@registratie/domain/person'; +import { BigProfile } from '@registratie/domain/big-profile'; import { ApiClient } from '@shared/infrastructure/api-client'; +/** + * The parsed, frontend-side view: the wire DTO mapped onto our own domain model. + * Lives HERE, not in contracts/, because it references domain types — contracts + * stays import-free. This split is the decoupling seam (CLAUDE.md §1, ADR-0001). + */ +export interface DashboardView { + profile: BigProfile; + decisions: HerregistratieDecisions; +} + /** * Infrastructure adapter for the screen-shaped ("BFF-lite") dashboard endpoint. * ONE call returns registration + person + server-computed decisions. The data @@ -48,8 +61,21 @@ export function parseDashboardView(json: unknown): Result return err('dashboard-view: missing/invalid decisions'); } + // Map wire → domain. The shapes are identical today, so this reads as an + // identity copy — but the TYPES differ (wire DTO vs domain), so the moment the + // wire diverges the compiler forces a real mapping here. That's the seam. + const registration: Registration = { + bigNummer: reg.bigNummer, + naam: reg.naam, + beroep: reg.beroep, + registratiedatum: reg.registratiedatum, + geboortedatum: reg.geboortedatum, + status: reg.status, + }; + const persoon: Person = { naam: person.naam, geboortedatum: person.geboortedatum, adres: person.adres }; + return ok({ - profile: { registration: reg, person }, + profile: { registration, person: persoon }, decisions: { eligibleForHerregistratie: d.eligibleForHerregistratie, herregistratieReason: d.herregistratieReason }, }); } diff --git a/src/app/registratie/ui/change-request-form/change-request-form.component.ts b/src/app/registratie/ui/change-request-form/change-request-form.component.ts index 09f95d6..bd6faa3 100644 --- a/src/app/registratie/ui/change-request-form/change-request-form.component.ts +++ b/src/app/registratie/ui/change-request-form/change-request-form.component.ts @@ -1,4 +1,5 @@ -import { Component, computed, inject, input } from '@angular/core'; +import { Component, computed, input } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { ButtonComponent } from '@shared/ui/button/button.component'; import { HeadingComponent } from '@shared/ui/heading/heading.component'; import { AlertComponent } from '@shared/ui/alert/alert.component'; @@ -6,8 +7,7 @@ import { AddressFieldsComponent, AdresValue, AdresErrors } from '@registratie/ui import { createStore } from '@shared/application/store'; import { whenTag } from '@shared/kernel/fp'; import { State, Msg, initial, reduce } from '@registratie/domain/change-request.machine'; -import { submitChangeRequest } from '@registratie/application/submit-change-request'; -import { ApiClient } from '@shared/infrastructure/api-client'; +import { createSubmitChangeRequest } from '@registratie/application/submit-change-request'; /** * Organism: change-request (adreswijziging) form. Uses the SAME idiom as the @@ -17,7 +17,7 @@ import { ApiClient } from '@shared/infrastructure/api-client'; */ @Component({ selector: 'app-change-request-form', - imports: [ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent], + imports: [FormsModule, ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent], template: ` @if (state().tag === 'Submitted') { @@ -50,7 +50,10 @@ import { ApiClient } from '@shared/infrastructure/api-client'; `, }) export class ChangeRequestFormComponent { - private apiClient = inject(ApiClient); + // The submit command owns the ApiClient dependency (via the change-request + // adapter); the UI holds only this bound command. Field initializer = injection + // context, like createStore below. + private submit = createSubmitChangeRequest(); private store = createStore(initial, reduce); /** Optional seed so Storybook / tests can mount any state directly. */ @@ -91,7 +94,7 @@ export class ChangeRequestFormComponent { private async runIfSubmitting() { const s = this.state(); if (s.tag !== 'Submitting') return; - const r = await submitChangeRequest(this.apiClient, s.data); + const r = await this.submit(s.data); if (r.ok) this.dispatch({ tag: 'SubmitConfirmed', referentie: r.value }); else this.dispatch({ tag: 'SubmitFailed', error: r.error }); } diff --git a/src/app/shared/domain/role.ts b/src/app/shared/domain/role.ts new file mode 100644 index 0000000..d8b8cc1 --- /dev/null +++ b/src/app/shared/domain/role.ts @@ -0,0 +1,7 @@ +/** + * The two-person letter workflow's role: who is acting, a drafter or an approver. + * A pure domain type (no framework, no reading mechanism) — the `?role=` reader and + * the X-Role header live in shared/infrastructure/role.ts. Consumers (brief.store, + * letter-composer) depend on this type, not on how the role is obtained. + */ +export type Role = 'drafter' | 'approver'; diff --git a/src/app/shared/infrastructure/role.ts b/src/app/shared/infrastructure/role.ts index 565850b..cf1708b 100644 --- a/src/app/shared/infrastructure/role.ts +++ b/src/app/shared/infrastructure/role.ts @@ -1,12 +1,13 @@ -/** - * Dev-only role stand-in. This POC has one faked self-service user and no real - * identities, so the two-person letter workflow (drafter vs approver) is driven by - * a `?role=` query param — exactly the pattern of the `?scenario=` toggle. The - * backend receives it as an `X-Role` header (see role.interceptor) and enforces the - * approver≠drafter rule; the FE derives `editable` from it. - */ -export type Role = 'drafter' | 'approver'; +import { Role } from '@shared/domain/role'; +/** + * Dev-only role stand-in (the reading MECHANISM; the `Role` type is domain). This + * POC has one faked self-service user and no real identities, so the two-person + * letter workflow (drafter vs approver) is driven by a `?role=` query param — + * exactly the pattern of the `?scenario=` toggle. The backend receives it as an + * `X-Role` header (see role.interceptor) and enforces the approver≠drafter rule; + * the FE derives `editable` from it. + */ export function currentRole(): Role { return new URLSearchParams(window.location.search).get('role') === 'approver' ? 'approver' : 'drafter'; }