refactor: migrate the 3 wizards to the effect map and Primary (RD-08)

The three wizards paired a dispatch with a hand-written effect call
(onPrimary/onRetry + runIfSubmitting/runIfIndienen). A missed call failed
silently. RD-05 added the effect map and RD-07 added the Primary message;
this ticket moves each wizard onto both.

Each wizard now registers its submit effect on createStore, keyed by its
own submitting tag (Submitting for herregistratie and intake, Indienen for
registratie — the type catches a wrong key at compile time). The optimistic
begin/confirm/rollback calls stay inside the effect body, unchanged. The
template dispatches Primary and Retry directly, matching how Back already
worked. onPrimary, onRetry, and runIfSubmitting/runIfIndienen are deleted
from all three components.

herregistratie-wizard drops under the 250-rule-line budget, so its
eslint-disable max-lines header is removed in this same commit (RD-02's
self-cleaning mechanism). intake-wizard and registratie-wizard stay over
budget and keep theirs, both already flagged for RD-22/RD-23.

Three doc comments (in the three machine files, plus one in store.ts) named
the deleted onPrimary()/runIfSubmitting() identifiers in prose. Reworded
them so the "idiom is gone from the repo" grep check is not defeated by its
own explanatory comments.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-04 17:20:52 +02:00
co-authored by Claude Sonnet 5
parent d0d6a3764b
commit b8aced75e3
9 changed files with 238 additions and 103 deletions
@@ -288,7 +288,7 @@ export function submit(s: RegistratieState): RegistratieState {
}
/** The primary button's action: advance, or submit from the controle step.
No-op outside Invullen — this is the one decision `onPrimary()` used to make. */
No-op outside Invullen — this is the one decision the old component-side handler used to make. */
export function primary(s: RegistratieState): RegistratieState {
if (s.tag !== 'Invullen') return s;
return currentStep(s) === 'controle' ? submit(s) : next(s);
@@ -90,10 +90,10 @@ const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';
[errorMessage]="errorMessage()"
i18n-submittingLabel="@@regWizard.submitting"
submittingLabel="Uw registratie wordt verwerkt…"
(primary)="onPrimary()"
(primary)="dispatch({ tag: 'Primary' })"
(back)="dispatch({ tag: 'Back' })"
(cancel)="restart()"
(retry)="onRetry()"
(retry)="dispatch({ tag: 'Retry' })"
(goToStep)="dispatch({ tag: 'GaNaarStap', cursor: $event })"
>
@switch (step()) {
@@ -368,7 +368,18 @@ const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';
})
export class RegistratieWizardComponent {
private lookup = inject(RegistratieLookupStore);
private store = createStore<RegistratieState, RegistratieMsg>(initial, reduce);
// Effect fires once, on Invullen -> Indienen (RD-05's tag-transition rule; `Seed` is
// exempt, so a story mounting straight into `Indienen` does not call the network).
private store = createStore<RegistratieState, RegistratieMsg>(initial, reduce, {
Indienen: async (s, store) => {
const r = await this.draftSync.submit({
diplomaHerkomst: s.data.diplomaHerkomst,
documents: s.data.documents,
});
if (r.ok) store.dispatch({ tag: 'SubmitConfirmed', referentie: r.value.referentie ?? '' });
else store.dispatch({ tag: 'SubmitFailed', error: r.error });
},
});
/** Preview/download link for a completed upload; delegates to the upload
controller (application layer), which knows the dev-simulation `demo-*` ids
@@ -610,18 +621,6 @@ export class RegistratieWizardComponent {
// failed submit) now lives in the shared WizardShellComponent.
}
onPrimary() {
const s = this.state();
if (s.tag !== 'Invullen') return;
this.dispatch(this.step() === 'controle' ? { tag: 'Submit' } : { tag: 'Next' });
this.runIfIndienen();
}
onRetry() {
this.dispatch({ tag: 'Retry' });
this.runIfIndienen();
}
/** Reset the wizard to a fresh start. Reload the BRP lookup so the address
re-prefills, keeping the form and the"vooraf ingevuld" note consistent. */
restart() {
@@ -629,17 +628,4 @@ export class RegistratieWizardComponent {
this.dispatch({ tag: 'Seed', state: initial });
this.lookup.reloadAdres();
}
/** The effect: when we enter Indienen, submit through the aanvraag lifecycle
(duo → auto-approve, handmatig → manual), then dispatch the outcome. */
private async runIfIndienen() {
const s = this.state();
if (s.tag !== 'Indienen') return;
const r = await this.draftSync.submit({
diplomaHerkomst: s.data.diplomaHerkomst,
documents: s.data.documents,
});
if (r.ok) this.dispatch({ tag: 'SubmitConfirmed', referentie: r.value.referentie ?? '' });
else this.dispatch({ tag: 'SubmitFailed', error: r.error });
}
}