feat(boundaries): WP-04 — ui ↛ infrastructure + showcase sanction
Move the two wizard lookups behind application-layer facades so ui/ no longer injects infrastructure adapters directly: - RegistratieLookupStore (BRP address + DUO diplomas): owns the resources, runs the trust-boundary parse, exposes adresStatus/prefillAdres/duoLookup. - IntakePolicyStore (scholing threshold): owns the policy resource, exposes the derived threshold. Add the lint rule ui/ + layout/ ↛ **/infrastructure/** (@typescript-eslint variant so it composes with the base direction rules; stories/specs exempted as test scaffolding). Add the documented showcase sanction (may read every context). Fix the docs' inventory: 6 contexts / 5 layers, +brief, +contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
11
CLAUDE.md
11
CLAUDE.md
@@ -34,7 +34,8 @@ advisories are pinned via `package.json` `overrides`; the shipped bundle audits
|
||||
### 1. DDD: contexts then layers, dependencies point inward
|
||||
|
||||
`src/app/<context>/<layer>/`. Contexts: `shared`, `auth`, `registratie`,
|
||||
`herregistratie`, `showcase` (teaching page, not a feature).
|
||||
`herregistratie`, `brief` (letter-composition teaching slice), `showcase` (teaching
|
||||
page, not a feature; **sanctioned** to read every context — nothing imports it).
|
||||
|
||||
| Layer | Job | Angular allowed? |
|
||||
| ----------------- | ----------------------------------------- | -------------------------------- |
|
||||
@@ -45,9 +46,11 @@ advisories are pinned via `package.json` `overrides`; the shipped bundle audits
|
||||
| `ui/` | how it looks (components, pages) | yes |
|
||||
|
||||
**Dependencies only point inward**: `ui → application → domain`; everyone may use
|
||||
`shared`; never the reverse. Cross-context only `herregistratie → registratie → shared`,
|
||||
`auth → shared`. Imports use aliases as direction statements: `@shared/* @auth/*
|
||||
@registratie/* @herregistratie/*`. `domain/` imports nothing from Angular.
|
||||
`shared`; never the reverse. `ui`/`layout` never import `infrastructure` directly
|
||||
(reach data through an application store/command) — lint-enforced. Cross-context only
|
||||
`herregistratie → registratie → shared`, `auth → shared`, `brief → shared`. Imports use
|
||||
aliases as direction statements: `@shared/* @auth/* @registratie/* @herregistratie/*
|
||||
@brief/*`. `domain/` imports nothing from Angular.
|
||||
|
||||
### 2. Atomic design: folder = layer
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ re-registration — "herregistratie").
|
||||
|
||||
---
|
||||
|
||||
## 1. The big picture: three "contexts", four "layers"
|
||||
## 1. The big picture: six "contexts", five "layers"
|
||||
|
||||
The code is split first by **business area** (a "bounded context" in DDD terms),
|
||||
then inside each area by **layer**.
|
||||
@@ -27,9 +27,14 @@ src/app/
|
||||
auth/ logging in / the current session
|
||||
registratie/ the user's BIG registration + personal data
|
||||
herregistratie/ the re-registration application flow
|
||||
showcase/ a teaching page; not a real feature
|
||||
brief/ letter-composition teaching slice
|
||||
showcase/ a teaching page; not a real feature (may read every context)
|
||||
```
|
||||
|
||||
`showcase/` is a **sanctioned exception** to the direction rules: its whole point is
|
||||
showing multiple contexts side by side, so it may import any context. Nothing imports
|
||||
`showcase`. (Enforced in `eslint.config.mjs`; same precedent as the `debug-state` panel.)
|
||||
|
||||
### The atomic-design hierarchy, visualised
|
||||
|
||||
The UI is built bottom-up: tiny **atoms** combine into **molecules**, which combine
|
||||
@@ -53,7 +58,7 @@ organism** (`intake-wizard`) — everything else (`form-field`, `text-input`, `b
|
||||
`alert`, `spinner`, the page shell) was reused unchanged. That is the payoff of the
|
||||
hierarchy.
|
||||
|
||||
Inside a context you'll see the same four folders. They answer four different
|
||||
Inside a context you'll see the same five folders. They answer five different
|
||||
questions:
|
||||
|
||||
| Layer | Answers… | May import Angular? | Example here |
|
||||
@@ -61,14 +66,18 @@ questions:
|
||||
| `domain/` | What are the business rules and data? | **No** (pure TS) | `registration.ts`, `registration.policy.ts` |
|
||||
| `application/` | How do we coordinate a task / state? | Yes (signals) | `big-profile.store.ts` |
|
||||
| `infrastructure/` | Where does data come from? | Yes (HTTP) | `big-register.adapter.ts`, `brp.adapter.ts` |
|
||||
| `contracts/` | What's the FE⇄BE wire shape? | **No** (pure DTOs) | `dashboard-view.dto.ts` |
|
||||
| `ui/` | How does it look? | Yes (components) | `dashboard.page.ts` |
|
||||
|
||||
**The one rule that keeps it sane: dependencies only point _inward_.** UI may use
|
||||
application, application may use domain, everyone may use `shared`. Never the
|
||||
other way around. The `domain/` layer imports nothing from Angular, so the
|
||||
business rules are plain functions you can read and test in isolation.
|
||||
other way around. In particular **`ui/` and `layout/` never import `infrastructure/`
|
||||
directly** — they reach data through an application store or command (lint-enforced).
|
||||
The `domain/` layer imports nothing from Angular, so the business rules are plain
|
||||
functions you can read and test in isolation.
|
||||
|
||||
Allowed direction: `herregistratie → registratie → shared`, `auth → shared`.
|
||||
Allowed direction: `herregistratie → registratie → shared`, `auth → shared`,
|
||||
`brief → shared` (`showcase` may read every context; see above).
|
||||
|
||||
### Why the `shared/` kernel is split too
|
||||
|
||||
@@ -79,7 +88,7 @@ Allowed direction: `herregistratie → registratie → shared`, `auth → shared
|
||||
- `shared/infrastructure/` — the demo HTTP interceptor.
|
||||
|
||||
Imports use path aliases so they read as direction statements:
|
||||
`@shared/*`, `@auth/*`, `@registratie/*`, `@herregistratie/*`.
|
||||
`@shared/*`, `@auth/*`, `@registratie/*`, `@herregistratie/*`, `@brief/*`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -43,7 +43,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 | done |
|
||||
| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | todo |
|
||||
| [WP-04](WP-04-ui-not-infrastructure.md) | Boundaries II: `ui ↛ infrastructure` + showcase sanction | 0 · gates | done |
|
||||
| [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 |
|
||||
| [WP-07](WP-07-brief-idioms.md) | Brief on the shared idioms + RemoteData MDX | 1 · FP/DDD | todo |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# WP-04 — Boundaries II: `ui ↛ infrastructure` + showcase sanction
|
||||
|
||||
Status: todo
|
||||
Status: done (pending commit)
|
||||
Phase: 0 — enforcement & gates
|
||||
|
||||
## Why
|
||||
@@ -54,11 +54,13 @@ Phase 0 — a pure move of wiring, no behavior change.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] Rule active; lint green; no disables beyond the documented showcase + debug-state
|
||||
exemptions.
|
||||
- [ ] No `**/ui/**` file imports from `**/infrastructure/**`.
|
||||
- [ ] Both wizards behave unchanged (specs pass; manual smoke).
|
||||
- [ ] CLAUDE.md and ARCHITECTURE.md list 6 contexts / 5 layers.
|
||||
- [x] Rule active; lint green; no disables beyond the documented showcase + debug-state
|
||||
exemptions. (Probed: a ui→infra import errors.)
|
||||
- [x] No `**/ui/**` file imports from `**/infrastructure/**` (production; stories/specs
|
||||
exempted — test scaffolding wires the real client).
|
||||
- [x] Both wizards behave unchanged (178 specs pass; storybook a11y suite mounts both
|
||||
wizards green — behaviour is a pure wiring move behind root facades).
|
||||
- [x] CLAUDE.md and ARCHITECTURE.md list 6 contexts / 5 layers.
|
||||
|
||||
## Verification
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -143,4 +143,44 @@ export default [
|
||||
plugins: { '@typescript-eslint': tseslint.plugin },
|
||||
rules: { '@typescript-eslint/no-restricted-imports': 'off' },
|
||||
},
|
||||
|
||||
// ui/ and layout/ are the presentation layer: dependencies point inward
|
||||
// (ui → application → domain, CLAUDE.md §1), so they must NOT import
|
||||
// infrastructure/ directly — they reach data through an application store or
|
||||
// command. (Stories/specs are test scaffolding and may wire the real client.)
|
||||
// Uses the @typescript-eslint variant so it composes with the base
|
||||
// no-restricted-imports context-direction rules above (last-wins is per rule name).
|
||||
{
|
||||
files: ['src/app/**/ui/**/*.ts', 'src/app/**/layout/**/*.ts'],
|
||||
ignores: ['**/*.stories.ts', '**/*.spec.ts'],
|
||||
plugins: { '@typescript-eslint': tseslint.plugin },
|
||||
rules: {
|
||||
'@typescript-eslint/no-restricted-imports': [
|
||||
'error',
|
||||
{
|
||||
patterns: [
|
||||
{
|
||||
group: [
|
||||
'@shared/infrastructure/*',
|
||||
'@auth/infrastructure/*',
|
||||
'@registratie/infrastructure/*',
|
||||
'@herregistratie/infrastructure/*',
|
||||
'@brief/infrastructure/*',
|
||||
],
|
||||
allowTypeImports: true,
|
||||
message: 'ui/ and layout/ must not import infrastructure/ directly (CLAUDE.md §1: ui → application → domain). Reach data through an application store or command. (Type-only DTO imports are fine: use `import type`.)',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
// Sanctioned exception: showcase/ is the teaching page whose whole point is showing
|
||||
// multiple contexts side by side (ARCHITECTURE.md §6). It may read every context;
|
||||
// nothing imports showcase. Same precedent as the shared/ui/debug-state exemption.
|
||||
{
|
||||
files: ['src/app/showcase/**/*.ts'],
|
||||
rules: { 'no-restricted-imports': 'off' },
|
||||
},
|
||||
];
|
||||
|
||||
19
src/app/herregistratie/application/intake-policy.store.ts
Normal file
19
src/app/herregistratie/application/intake-policy.store.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { Injectable, computed, inject } from '@angular/core';
|
||||
import { SCHOLING_THRESHOLD_DEFAULT } from '../domain/intake.machine';
|
||||
import { IntakePolicyAdapter } from '../infrastructure/intake-policy.adapter';
|
||||
|
||||
/**
|
||||
* Application-layer facade for the server-owned intake policy (the scholing
|
||||
* threshold config value). It owns the httpResource (created here, in the required
|
||||
* injection context) and exposes the threshold as a derived signal, falling back to
|
||||
* the domain default until the backend answers. The UI reaches the network through
|
||||
* application/, never infrastructure/ directly (CLAUDE.md §1). The backend stays the
|
||||
* authority and re-validates on submit.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IntakePolicyStore {
|
||||
private policy = inject(IntakePolicyAdapter);
|
||||
private policyRes = this.policy.policyResource();
|
||||
|
||||
readonly scholingThreshold = computed(() => this.policyRes.value()?.scholingThreshold ?? SCHOLING_THRESHOLD_DEFAULT);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
SCHOLING_THRESHOLD_DEFAULT,
|
||||
} from '@herregistratie/domain/intake.machine';
|
||||
import { createDraftSync } from '@registratie/application/draft-sync';
|
||||
import { IntakePolicyAdapter } from '@herregistratie/infrastructure/intake-policy.adapter';
|
||||
import { IntakePolicyStore } from '@herregistratie/application/intake-policy.store';
|
||||
|
||||
/** Organism: a BRANCHING intake questionnaire. All state lives in one signal
|
||||
driven by the pure `reduce` (intake.machine.ts). Which step renders is derived
|
||||
@@ -120,13 +120,11 @@ import { IntakePolicyAdapter } from '@herregistratie/infrastructure/intake-polic
|
||||
})
|
||||
export class IntakeWizardComponent {
|
||||
private profile = inject(BigProfileStore);
|
||||
private policy = inject(IntakePolicyAdapter);
|
||||
// Server-owned policy (scholing threshold): fetched from the backend via the
|
||||
// application facade, not hardcoded. The backend stays the authority on submit.
|
||||
private policyStore = inject(IntakePolicyStore);
|
||||
private store = createStore<IntakeState, IntakeMsg>(initial, reduce);
|
||||
|
||||
// Server-owned policy: the scholing threshold is fetched from the backend, not
|
||||
// hardcoded. The backend stays the authority and re-validates on submit.
|
||||
private policyRes = this.policy.policyResource();
|
||||
|
||||
/** Optional seed so Storybook / the showcase can mount any state directly. */
|
||||
seed = input<IntakeState>(initial);
|
||||
|
||||
@@ -201,7 +199,7 @@ export class IntakeWizardComponent {
|
||||
// only the policy value; untrack the dispatch (it reads the state signal
|
||||
// internally, which would otherwise make this effect loop on its own write).
|
||||
effect(() => {
|
||||
const scholingThreshold = this.policyRes.value()?.scholingThreshold ?? SCHOLING_THRESHOLD_DEFAULT;
|
||||
const scholingThreshold = this.policyStore.scholingThreshold();
|
||||
untracked(() => this.dispatch({ tag: 'SetPolicy', scholingThreshold }));
|
||||
});
|
||||
}
|
||||
|
||||
58
src/app/registratie/application/registratie-lookup.store.ts
Normal file
58
src/app/registratie/application/registratie-lookup.store.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { Injectable, computed, inject } from '@angular/core';
|
||||
import { RemoteData, fromResource } from '@shared/application/remote-data';
|
||||
import { DuoLookupDto } from '../contracts/duo-diplomas.dto';
|
||||
import { BrpAdapter, parseBrpAddress } from '../infrastructure/brp.adapter';
|
||||
import { DuoAdapter, parseDuoLookup } from '../infrastructure/duo.adapter';
|
||||
|
||||
type Err = Error | undefined;
|
||||
|
||||
/**
|
||||
* Application-layer facade for the registratie wizard's two lookups (BRP address,
|
||||
* DUO diplomas). It owns the httpResources (created here, in the required injection
|
||||
* context), runs the trust-boundary parse, and exposes RemoteData / derived signals
|
||||
* — so the UI reaches the network through application/, never infrastructure/
|
||||
* directly (CLAUDE.md §1: ui → application → domain). Same facade shape as
|
||||
* BigProfileStore.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class RegistratieLookupStore {
|
||||
private brp = inject(BrpAdapter);
|
||||
private duo = inject(DuoAdapter);
|
||||
|
||||
private adresRes = this.brp.adresResource();
|
||||
private diplomasRes = this.duo.diplomasResource();
|
||||
|
||||
/** BRP lookup outcome. A failure or "geen adres" never blocks the wizard — both
|
||||
fall back to manual entry (PRD §7). 'geen' covers both no-address-found and a
|
||||
malformed response; 'fout' is an unreachable BRP. */
|
||||
readonly adresStatus = computed<'laden' | 'gevonden' | 'geen' | 'fout'>(() => {
|
||||
const st = this.adresRes.status();
|
||||
if (st === 'loading' || st === 'reloading') return 'laden';
|
||||
if (st === 'error') return 'fout';
|
||||
const json = this.adresRes.value();
|
||||
const parsed = json !== undefined ? parseBrpAddress(json) : null;
|
||||
return parsed && parsed.ok && parsed.value.gevonden ? 'gevonden' : 'geen';
|
||||
});
|
||||
|
||||
/** The address to prefill the draft with, once BRP resolves with a found address;
|
||||
null otherwise (loading, error, no address, malformed). */
|
||||
readonly prefillAdres = computed<{ straat: string; postcode: string; woonplaats: string } | null>(() => {
|
||||
const json = this.adresRes.value();
|
||||
if (json === undefined) return null;
|
||||
const parsed = parseBrpAddress(json);
|
||||
return parsed.ok && parsed.value.gevonden && parsed.value.adres ? parsed.value.adres : null;
|
||||
});
|
||||
|
||||
/** The DUO lookup (diplomas + manual fallback), validated at the trust boundary. */
|
||||
readonly duoLookup = computed<RemoteData<Err, DuoLookupDto>>(() => {
|
||||
const rd = fromResource(this.diplomasRes);
|
||||
if (rd.tag !== 'Success') return rd;
|
||||
const parsed = parseDuoLookup(rd.value);
|
||||
return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) };
|
||||
});
|
||||
|
||||
/** Reload the BRP lookup (e.g. when the wizard restarts) so the address re-prefills. */
|
||||
reloadAdres() {
|
||||
this.adresRes.reload();
|
||||
}
|
||||
}
|
||||
@@ -14,9 +14,8 @@ import { ASYNC } from '@shared/ui/async/async.component';
|
||||
import { AddressFieldsComponent } from '@registratie/ui/address-fields/address-fields.component';
|
||||
import { createStore } from '@shared/application/store';
|
||||
import { whenTag } from '@shared/kernel/fp';
|
||||
import { RemoteData, fromResource } from '@shared/application/remote-data';
|
||||
import { BrpAdapter, parseBrpAddress } from '@registratie/infrastructure/brp.adapter';
|
||||
import { DuoAdapter, parseDuoLookup } from '@registratie/infrastructure/duo.adapter';
|
||||
import { RemoteData } from '@shared/application/remote-data';
|
||||
import { RegistratieLookupStore } from '@registratie/application/registratie-lookup.store';
|
||||
import { DuoLookupDto, PolicyQuestionDto } from '@registratie/contracts/duo-diplomas.dto';
|
||||
import {
|
||||
RegistratieState,
|
||||
@@ -194,8 +193,7 @@ const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';
|
||||
`,
|
||||
})
|
||||
export class RegistratieWizardComponent {
|
||||
private brp = inject(BrpAdapter);
|
||||
private duo = inject(DuoAdapter);
|
||||
private lookup = inject(RegistratieLookupStore);
|
||||
private uploadAdapter = inject(UploadAdapter);
|
||||
private store = createStore<RegistratieState, RegistratieMsg>(initial, reduce);
|
||||
|
||||
@@ -204,9 +202,6 @@ export class RegistratieWizardComponent {
|
||||
protected previewUrlFor = (documentId: string): string | undefined =>
|
||||
documentId.startsWith('demo-') ? undefined : this.uploadAdapter.contentUrl(documentId);
|
||||
|
||||
protected adresRes = this.brp.adresResource();
|
||||
private diplomasRes = this.duo.diplomasResource();
|
||||
|
||||
/** Optional seed so Storybook / tests can mount any state directly. */
|
||||
seed = input<RegistratieState>(initial);
|
||||
|
||||
@@ -286,25 +281,10 @@ export class RegistratieWizardComponent {
|
||||
protected correspondentieLabel = computed(() => ({ email: $localize`:@@regWizard.corr.email:Per e-mail`, post: $localize`:@@regWizard.corr.post:Per post` }[this.draft().correspondentie ?? 'post']));
|
||||
protected diplomaHerkomstLabel = computed(() => ({ duo: $localize`:@@regWizard.herkomst.diplomaDuo:Geverifieerd via DUO`, handmatig: $localize`:@@regWizard.herkomst.diplomaHandmatig:Handmatig ingevoerd (wordt beoordeeld)` }[this.draft().diplomaHerkomst ?? 'handmatig']));
|
||||
|
||||
/** BRP lookup outcome. A failure or"geen adres" never blocks the wizard — both
|
||||
fall back to manual entry (PRD §7). 'geen' covers both no-address-found and a
|
||||
malformed response; 'fout' is an unreachable BRP. */
|
||||
protected adresStatus = computed<'laden' | 'gevonden' | 'geen' | 'fout'>(() => {
|
||||
const st = this.adresRes.status();
|
||||
if (st === 'loading' || st === 'reloading') return 'laden';
|
||||
if (st === 'error') return 'fout';
|
||||
const json = this.adresRes.value();
|
||||
const parsed = json !== undefined ? parseBrpAddress(json) : null;
|
||||
return parsed && parsed.ok && parsed.value.gevonden ? 'gevonden' : 'geen';
|
||||
});
|
||||
|
||||
/** The DUO lookup (diplomas + manual fallback), validated at the trust boundary. */
|
||||
protected lookupRd = computed<RemoteData<Error | undefined, DuoLookupDto>>(() => {
|
||||
const rd = fromResource(this.diplomasRes);
|
||||
if (rd.tag !== 'Success') return rd;
|
||||
const parsed = parseDuoLookup(rd.value);
|
||||
return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) };
|
||||
});
|
||||
/** BRP lookup outcome (laden/gevonden/geen/fout) and the parsed DUO lookup, both
|
||||
served by the application facade — the wizard renders, it does not fetch/parse. */
|
||||
protected adresStatus = this.lookup.adresStatus;
|
||||
protected lookupRd: () => RemoteData<Error | undefined, DuoLookupDto> = this.lookup.duoLookup;
|
||||
|
||||
/** Parsed lookup as a plain value (or null) — used outside the beroep step (the
|
||||
controle summary) where the <app-async> template variable isn't in scope. */
|
||||
@@ -362,21 +342,17 @@ export class RegistratieWizardComponent {
|
||||
// (`?aanvraag=<id>`), or start fresh. Persistence is the draftSync controller's job.
|
||||
const seeded = this.seed();
|
||||
queueMicrotask(() => (seeded !== initial ? this.dispatch({ tag: 'Seed', state: seeded }) : this.draftSync.resume()));
|
||||
// Prefill the address from the BRP lookup as it arrives. Track only the resource
|
||||
// value; untrack the dispatch (it reads the state signal, which would otherwise
|
||||
// make this effect loop on its own write). Don't clobber edits/restored data.
|
||||
// Prefill the address from the BRP lookup as it arrives. Track only the facade's
|
||||
// parsed prefill signal; untrack the dispatch (it reads the state signal, which
|
||||
// would otherwise make this effect loop on its own write). Don't clobber
|
||||
// edits/restored data. A null prefill (loading/error/geen adres) leaves manual entry.
|
||||
effect(() => {
|
||||
const json = this.adresRes.value();
|
||||
if (!json) return;
|
||||
const parsed = parseBrpAddress(json);
|
||||
const a = this.lookup.prefillAdres();
|
||||
if (!a) return;
|
||||
untracked(() => {
|
||||
const s = this.state();
|
||||
if (s.tag !== 'Invullen' || s.draft.straat) return;
|
||||
// ponytail: slice 2 handles gevonden === false -> manual entry stays handmatig.
|
||||
if (parsed.ok && parsed.value.gevonden && parsed.value.adres) {
|
||||
const a = parsed.value.adres;
|
||||
this.dispatch({ tag: 'PrefillAdres', straat: a.straat, postcode: a.postcode, woonplaats: a.woonplaats });
|
||||
}
|
||||
this.dispatch({ tag: 'PrefillAdres', straat: a.straat, postcode: a.postcode, woonplaats: a.woonplaats });
|
||||
});
|
||||
});
|
||||
// A11y: focus management (step heading on step change, error summary on a
|
||||
@@ -400,7 +376,7 @@ export class RegistratieWizardComponent {
|
||||
restart() {
|
||||
this.draftSync.reset(); // discard the current Concept; a fresh one starts on next progress
|
||||
this.dispatch({ tag: 'Seed', state: initial });
|
||||
this.adresRes.reload();
|
||||
this.lookup.reloadAdres();
|
||||
}
|
||||
|
||||
/** The effect: when we enter Indienen, submit through the aanvraag lifecycle
|
||||
|
||||
Reference in New Issue
Block a user