refactor: WizardStatus to a payload-carrying WizardPhase (RD-10)
The wizard shell took two inputs to say one thing: a flat WizardStatus string and a separate errorMessage input. Each wizard needed three computeds (failedError, errorMessage, shellStatus) to take the state apart and put it back together for the shell. WizardPhase replaces both inputs with one discriminated union. Its Failed variant carries the message directly, so no data travels through a second channel. Each wizard now maps its own tags onto WizardPhase in one computed, composing the localized failure prefix at the same spot errorMessage did before. The three machines and their own vocabulary (Editing/Answering/Invullen, Indienen/Ingediend/Mislukt) are unchanged; only the shell's input contract changes. The shell reads the Failed message via the existing whenTag helper, because @switch cannot narrow a union in an Angular template. Both $localize ids (wizard.indienenMislukt, regWizard.indienenMislukt) keep byte-identical source text, so no locale file changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+14
-13
@@ -6,7 +6,7 @@ import { AlertComponent } from '@shared/ui/alert/alert.component';
|
||||
import {
|
||||
WizardShellComponent,
|
||||
WizardError,
|
||||
WizardStatus,
|
||||
WizardPhase,
|
||||
naarStapLabel,
|
||||
} from '@shared/layout/wizard-shell/wizard-shell.component';
|
||||
import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.component';
|
||||
@@ -50,11 +50,10 @@ import { UploadState, initialUpload, deliveryRefs } from '@shared/domain/upload.
|
||||
[stepTitle]="stepTitle()"
|
||||
i18n-processName="@@herregWizard.processName"
|
||||
processName="Herregistratie aanvragen"
|
||||
[status]="shellStatus()"
|
||||
[phase]="phase()"
|
||||
[primaryLabel]="primaryLabel()"
|
||||
[canGoBack]="step() > 1"
|
||||
[errors]="errorList()"
|
||||
[errorMessage]="errorMessage()"
|
||||
(primary)="dispatch({ tag: 'Primary' })"
|
||||
(back)="dispatch({ tag: 'Back' })"
|
||||
(cancel)="restart()"
|
||||
@@ -214,7 +213,6 @@ export class HerregistratieWizardComponent {
|
||||
protected errJaren = computed(() => this.editing()?.errors.jaren ?? '');
|
||||
protected errPunten = computed(() => this.editing()?.errors.punten ?? '');
|
||||
protected errDocumenten = computed(() => this.editing()?.errors.documenten ?? '');
|
||||
protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');
|
||||
protected uploadCtl = createUploadController({
|
||||
wizardId: 'herregistratie',
|
||||
getUpload: () => this.upload(),
|
||||
@@ -234,19 +232,22 @@ export class HerregistratieWizardComponent {
|
||||
protected goToStep(index: number) {
|
||||
this.dispatch({ tag: 'GaNaarStap', step: (index + 1) as 1 | 2 | 3 });
|
||||
}
|
||||
protected errorMessage = computed(
|
||||
() => $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${this.failedError()}`,
|
||||
);
|
||||
protected shellStatus = computed<WizardStatus>(() => {
|
||||
switch (this.state().tag) {
|
||||
/** Maps this machine's own tags onto the shell's `WizardPhase` vocabulary,
|
||||
composing the localized failure prefix so the `Failed` message arrives intact. */
|
||||
protected phase = computed<WizardPhase>(() => {
|
||||
const s = this.state();
|
||||
switch (s.tag) {
|
||||
case 'Editing':
|
||||
return 'editing';
|
||||
return { tag: 'Editing' };
|
||||
case 'Submitting':
|
||||
return 'submitting';
|
||||
return { tag: 'Submitting' };
|
||||
case 'Submitted':
|
||||
return 'submitted';
|
||||
return { tag: 'Submitted' };
|
||||
case 'Failed':
|
||||
return 'failed';
|
||||
return {
|
||||
tag: 'Failed',
|
||||
message: $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${s.error}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
/** Current step's field errors, flattened for the shell's error summary. */
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.comp
|
||||
import {
|
||||
WizardShellComponent,
|
||||
WizardError,
|
||||
WizardStatus,
|
||||
WizardPhase,
|
||||
naarStapLabel,
|
||||
} from '@shared/layout/wizard-shell/wizard-shell.component';
|
||||
import { createStore } from '@shared/application/store';
|
||||
@@ -59,11 +59,10 @@ import { IntakePolicyStore } from '@herregistratie/application/intake-policy.sto
|
||||
[stepTitle]="stepTitle()"
|
||||
i18n-processName="@@intake.processName"
|
||||
processName="Herregistratie-intake"
|
||||
[status]="shellStatus()"
|
||||
[phase]="phase()"
|
||||
[primaryLabel]="primaryLabel()"
|
||||
[canGoBack]="cursor() > 0"
|
||||
[errors]="errorList()"
|
||||
[errorMessage]="errorMessage()"
|
||||
(primary)="dispatch({ tag: 'Primary' })"
|
||||
(back)="dispatch({ tag: 'Back' })"
|
||||
(cancel)="restart()"
|
||||
@@ -323,7 +322,6 @@ export class IntakeWizardComponent {
|
||||
);
|
||||
/** Whether the inline scholing question is shown (and required) in the 'werk' step. */
|
||||
protected scholingZichtbaar = computed(() => lageUren(this.answers(), this.scholingThreshold()));
|
||||
protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');
|
||||
|
||||
// --- Presentational wiring for the shared wizard shell ---------------------
|
||||
readonly stepLabels = [
|
||||
@@ -342,19 +340,22 @@ export class IntakeWizardComponent {
|
||||
const next = this.cursor() + 1;
|
||||
return naarStapLabel(next + 1, this.stepLabels[next]);
|
||||
});
|
||||
protected errorMessage = computed(
|
||||
() => $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${this.failedError()}`,
|
||||
);
|
||||
protected shellStatus = computed<WizardStatus>(() => {
|
||||
switch (this.state().tag) {
|
||||
/** Maps this machine's own tags onto the shell's `WizardPhase` vocabulary,
|
||||
composing the localized failure prefix so the `Failed` message arrives intact. */
|
||||
protected phase = computed<WizardPhase>(() => {
|
||||
const s = this.state();
|
||||
switch (s.tag) {
|
||||
case 'Answering':
|
||||
return 'editing';
|
||||
return { tag: 'Editing' };
|
||||
case 'Submitting':
|
||||
return 'submitting';
|
||||
return { tag: 'Submitting' };
|
||||
case 'Submitted':
|
||||
return 'submitted';
|
||||
return { tag: 'Submitted' };
|
||||
case 'Failed':
|
||||
return 'failed';
|
||||
return {
|
||||
tag: 'Failed',
|
||||
message: $localize`:@@wizard.indienenMislukt:Indienen mislukt:` + ` ${s.error}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
/** Current step's field errors, flattened for the shell's error summary. The
|
||||
|
||||
+15
-15
@@ -14,7 +14,7 @@ import { ConfirmationComponent } from '@shared/ui/confirmation/confirmation.comp
|
||||
import {
|
||||
WizardShellComponent,
|
||||
WizardError,
|
||||
WizardStatus,
|
||||
WizardPhase,
|
||||
naarStapLabel,
|
||||
} from '@shared/layout/wizard-shell/wizard-shell.component';
|
||||
import { ASYNC } from '@shared/ui/async/async.component';
|
||||
@@ -83,11 +83,10 @@ const NL_TAALVAARDIGHEID_VRAAG = 'nl-taalvaardigheid';
|
||||
[stepTitle]="stepTitle()"
|
||||
i18n-processName="@@regWizard.processName"
|
||||
processName="Inschrijven in het BIG-register"
|
||||
[status]="shellStatus()"
|
||||
[phase]="phase()"
|
||||
[primaryLabel]="primaryLabel()"
|
||||
[canGoBack]="cursor() > 0"
|
||||
[errors]="errorList()"
|
||||
[errorMessage]="errorMessage()"
|
||||
i18n-submittingLabel="@@regWizard.submitting"
|
||||
submittingLabel="Uw registratie wordt verwerkt…"
|
||||
(primary)="dispatch({ tag: 'Primary' })"
|
||||
@@ -440,7 +439,6 @@ export class RegistratieWizardComponent {
|
||||
() => this.stepTitles[Math.min(this.cursor(), this.stepTitles.length - 1)],
|
||||
);
|
||||
protected referentie = computed(() => whenTag(this.state(), 'Ingediend')?.referentie ?? '');
|
||||
protected failedError = computed(() => whenTag(this.state(), 'Mislukt')?.error ?? '');
|
||||
|
||||
// --- Presentational wiring for the shared wizard shell ---------------------
|
||||
protected primaryLabel = computed(() => {
|
||||
@@ -448,21 +446,23 @@ export class RegistratieWizardComponent {
|
||||
const next = this.cursor() + 1;
|
||||
return naarStapLabel(next + 1, this.stepLabels[next]);
|
||||
});
|
||||
protected errorMessage = computed(
|
||||
() =>
|
||||
$localize`:@@regWizard.indienenMislukt:Het indienen is niet gelukt:` +
|
||||
` ${this.failedError()}`,
|
||||
);
|
||||
protected shellStatus = computed<WizardStatus>(() => {
|
||||
switch (this.state().tag) {
|
||||
/** Maps this machine's own tags onto the shell's `WizardPhase` vocabulary,
|
||||
composing the localized failure prefix so the `Failed` message arrives intact. */
|
||||
protected phase = computed<WizardPhase>(() => {
|
||||
const s = this.state();
|
||||
switch (s.tag) {
|
||||
case 'Invullen':
|
||||
return 'editing';
|
||||
return { tag: 'Editing' };
|
||||
case 'Indienen':
|
||||
return 'submitting';
|
||||
return { tag: 'Submitting' };
|
||||
case 'Ingediend':
|
||||
return 'submitted';
|
||||
return { tag: 'Submitted' };
|
||||
case 'Mislukt':
|
||||
return 'failed';
|
||||
return {
|
||||
tag: 'Failed',
|
||||
message:
|
||||
$localize`:@@regWizard.indienenMislukt:Het indienen is niet gelukt:` + ` ${s.error}`,
|
||||
};
|
||||
}
|
||||
});
|
||||
/** Current step's errors (incl. per-question), flattened for the error summary. */
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
# RD-10 — Let the wizard shell carry the error, not drop it
|
||||
|
||||
Status: done
|
||||
Source: PLAN.md 1b#4
|
||||
|
||||
## Why
|
||||
|
||||
`WizardStatus` is a payload-free string union:
|
||||
|
||||
```ts
|
||||
export type WizardStatus = 'editing' | 'submitting' | 'submitted' | 'failed';
|
||||
```
|
||||
|
||||
Each wizard flattens its own state tag down to it with an identical 12-line switch, which
|
||||
**throws the error away**. The error then has to travel separately, through a second
|
||||
`errorMessage` input, and each wizard needs three computeds to take apart and reassemble what
|
||||
one union could have carried intact:
|
||||
|
||||
| Wizard | `failedError` | `errorMessage` | `shellStatus` |
|
||||
| ----------------------- | ------------- | -------------- | ------------- |
|
||||
| `herregistratie-wizard` | 217 | 238 | 240-251 |
|
||||
| `intake-wizard` | 326 | 346 | 348-359 |
|
||||
| `registratie-wizard` | 443 | 453 | 456-467 |
|
||||
|
||||
Nine computeds and three switches exist because the type at the seam is too weak. One
|
||||
payload-carrying union replaces all of it with three computeds — one per wizard.
|
||||
|
||||
## Read first
|
||||
|
||||
- `libs/shared/src/layout/wizard-shell/wizard-shell.component.ts` — `WizardStatus` at 19,
|
||||
`status` input at 148, `errorMessage` input at 152, the `@switch` at 56-133 (the
|
||||
`@case ('failed')` at 131 is the only consumer of `errorMessage`)
|
||||
- `libs/shared/src/layout/wizard-shell/wizard-shell.stories.ts` — `base` at ~39 and the five
|
||||
stories that set `status`
|
||||
- The three `shellStatus`/`errorMessage`/`failedError` computeds listed above
|
||||
- `libs/shared/src/kernel/fp.ts:27` — `whenTag`, which returns `Extract<…> | null`
|
||||
|
||||
## Decisions (pre-made, don't relitigate)
|
||||
|
||||
1. **Replace `WizardStatus` and the `errorMessage` input with one payload-carrying union:**
|
||||
|
||||
```ts
|
||||
export type WizardPhase =
|
||||
| { tag: 'Editing' }
|
||||
| { tag: 'Submitting' }
|
||||
| { tag: 'Submitted' }
|
||||
| { tag: 'Failed'; message: string };
|
||||
```
|
||||
|
||||
The shell takes `phase = input.required<WizardPhase>()`. The `errorMessage` input is
|
||||
**deleted** — nothing else reads it.
|
||||
|
||||
2. **Keep the three mapping computeds. Do not try to remove them.** Each machine's tags are
|
||||
its own and genuinely differ — `Editing`/`Answering`/`Invullen`, and registratie's Dutch
|
||||
`Invullen`/`Indienen`/`Ingediend`/`Mislukt`. Those are not the shell's vocabulary and must
|
||||
not become it. What changes is that each mapping now returns a `WizardPhase` carrying the
|
||||
message, so **`failedError` and `errorMessage` fold into it** and each wizard goes from
|
||||
three computeds to one.
|
||||
|
||||
3. **Compose the localized prefix inside the new computed**, exactly as `errorMessage` does
|
||||
today, so both ids survive byte-identically:
|
||||
- `@@wizard.indienenMislukt` — "Indienen mislukt:" (herregistratie and intake)
|
||||
- `@@regWizard.indienenMislukt` — "Het indienen is niet gelukt:" (registratie)
|
||||
|
||||
The prefix differs per wizard, which is exactly why the mapping stays in the wizard. **Do
|
||||
not** move either string into the shell, and do not reword them — same id with different
|
||||
source text fails extraction.
|
||||
|
||||
4. **`@switch` cannot narrow a union in an Angular template.** So the `Failed` branch reads
|
||||
the message through the existing helper: `whenTag(this.phase(), 'Failed')?.message ?? ''`.
|
||||
Note `whenTag` returns `| null`, not `| undefined`. Do not add a new narrowing helper —
|
||||
this is the idiom all five form components already use.
|
||||
|
||||
5. **Update `wizard-shell.stories.ts` in the same commit.** `base` carries
|
||||
`errorMessage: ''` and five stories set `status:`; all become `phase:`. The failed story's
|
||||
message ("Het indienen is niet gelukt: netwerkfout.") moves inside the phase object. Both
|
||||
Storybook instances glob this file.
|
||||
|
||||
6. **Do not touch `errorList` or `WizardError`.** Those carry the current step's _field_
|
||||
errors for the shell's error summary — a different concern from the submit failure, on a
|
||||
different axis. They stay exactly as they are.
|
||||
|
||||
## Files
|
||||
|
||||
- `libs/shared/src/layout/wizard-shell/wizard-shell.component.ts`
|
||||
- `libs/shared/src/layout/wizard-shell/wizard-shell.stories.ts`
|
||||
- `apps/ssp/src/app/herregistratie/ui/herregistratie-wizard/herregistratie-wizard.component.ts`
|
||||
- `apps/ssp/src/app/herregistratie/ui/intake-wizard/intake-wizard.component.ts`
|
||||
- `apps/ssp/src/app/registratie/ui/registratie-wizard/registratie-wizard.component.ts`
|
||||
|
||||
No machine changes. No xlf changes.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Add `WizardPhase` to the shell, swap `status` + `errorMessage` for one `phase` input, and
|
||||
read the failed message per decision 4.
|
||||
2. Delete `WizardStatus`.
|
||||
3. In each wizard, collapse `failedError` + `errorMessage` + `shellStatus` into one
|
||||
`phase` computed returning a `WizardPhase`, keeping the localized prefix composition.
|
||||
4. Update the shell's stories per decision 5.
|
||||
5. Update this ticket's `Status:` to `done` and the README's RD-10 row to `done`.
|
||||
6. Commit all of it together.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
The weak type is gone, and no wizard still needs three computeds to say one thing. These
|
||||
commands were dry-run against the tree before this ticket was written, and the last two are
|
||||
**path-scoped deliberately** — an unscoped version of either can never return nothing:
|
||||
|
||||
```bash
|
||||
git grep -n "WizardStatus" -- apps libs # MUST return nothing
|
||||
|
||||
W=libs/shared/src/layout/wizard-shell
|
||||
H=apps/ssp/src/app/herregistratie/ui
|
||||
R=apps/ssp/src/app/registratie/ui/registratie-wizard
|
||||
git grep -n "errorMessage" -- $W $H $R # MUST return nothing
|
||||
git grep -n "failedError" -- $H $R # MUST return nothing
|
||||
```
|
||||
|
||||
Why the scoping, so nobody "fixes" correct code to satisfy a bad check:
|
||||
|
||||
- **`errorMessage` legitimately exists elsewhere** — `brief/infrastructure/letter-preview.adapter.ts`,
|
||||
its spec, `reveal-bignummer.adapter.ts`, and the generated `libs/shared/docs/behaviour-spec.mdx`.
|
||||
All unrelated to this seam. Leave them.
|
||||
- **`failedError` legitimately survives in the two single-step forms** —
|
||||
`besluit-form.component.ts` and `change-request-form.component.ts`. RD-06 gave those their
|
||||
own `Failed` branch and they keep their own computed. This ticket touches only the three
|
||||
wizards.
|
||||
|
||||
Both `$localize` ids survive unchanged, so no new translation is needed. Scope to source —
|
||||
an unscoped `-- apps` also matches the three locale files, which must **not** change:
|
||||
|
||||
```bash
|
||||
git grep -l "wizard.indienenMislukt" -- $H # exactly 2: herregistratie + intake
|
||||
git grep -l "regWizard.indienenMislukt" -- $R # exactly 1: registratie
|
||||
|
||||
# The locale files must be untouched by this ticket:
|
||||
git diff --name-only HEAD | git grep -c "locale/messages" || true # expect no locale diff
|
||||
```
|
||||
|
||||
For reference, the ids already exist in `apps/ssp/src/locale/messages.xlf`,
|
||||
`apps/ssp/src/locale/messages.en.xlf` and `apps/behandelportal/src/locale/messages.en.xlf`.
|
||||
Keeping the source text byte-identical is what lets all three stay as they are.
|
||||
|
||||
```bash
|
||||
npm run ci # exits 0
|
||||
npm run ci --full # exits 0 — required, this changes stories
|
||||
```
|
||||
|
||||
Then confirm the error still reaches the user: seed each wizard's failed state in Storybook
|
||||
and check the alert shows the full message, prefix included. That is the behaviour this
|
||||
ticket exists to protect, and the type change is what makes losing it impossible.
|
||||
|
||||
## Verification
|
||||
|
||||
`npm run ci --full`. `--full` is mandatory: this edits `wizard-shell.stories.ts`, and only
|
||||
`build-storybook` plus the axe run exercise it. Both Storybook instances glob the shared
|
||||
library, so both must build.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- `errorList` / `WizardError` (decision 6).
|
||||
- The three machines. This ticket changes only the UI seam.
|
||||
- Splitting any wizard into steps. RD-22 and RD-23.
|
||||
- `UploadStatus`'s `type:` discriminant. Optional RD-35.
|
||||
|
||||
## Risks
|
||||
|
||||
- **`ng build --localize` fails on a changed `$localize` id or source text.** Keep both
|
||||
template literals byte-identical and only move where they are composed (decision 3).
|
||||
- **`whenTag` returns `null`, not `undefined`.** `?? ''` covers both, but a `=== undefined`
|
||||
check would silently fail.
|
||||
- **`input.required` has no default**, unlike the `errorMessage = input('')` it replaces.
|
||||
Every call site must pass `phase`, including all five stories. A missed story fails at
|
||||
runtime in Storybook, not at compile time — which is why `--full` is mandatory here.
|
||||
- **Do not let the shell learn the machines' tags.** If `WizardPhase` grows an `Invullen` or
|
||||
`Answering` member, the mapping has leaked into the shared layer and the change has made
|
||||
things worse.
|
||||
@@ -104,7 +104,7 @@ two. Note that RD-15 exists because 22 abandoned agent worktrees are still on di
|
||||
| RD-07 | Add `Primary` to the 3 wizard machines + specs | 05 | | done |
|
||||
| RD-08 | Migrate the 3 wizards to the effect map + `Primary` | 07 | yes | done |
|
||||
| RD-09 | Teach the effect map: ARCHITECTURE §2d + fp-tea (2 docs, no generator) | 08 | | done |
|
||||
| RD-10 | `WizardStatus` to a payload-carrying `WizardPhase` | 08 | yes | todo |
|
||||
| RD-10 | `WizardStatus` to a payload-carrying `WizardPhase` | 08 | yes | done |
|
||||
| RD-11 | Fold the lifecycle projection into `remote-data.ts`; PascalCase 3 machines | 01 | | todo |
|
||||
| RD-12 | `ActionState` becomes `action` on `BriefState.Loaded` | 11 | | todo |
|
||||
| RD-13 | Same for org-template, folding `pendingPublish` in | 12 | | todo |
|
||||
|
||||
@@ -1,9 +1,19 @@
|
||||
import { Component, ElementRef, effect, input, output, untracked, viewChild } from '@angular/core';
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
computed,
|
||||
effect,
|
||||
input,
|
||||
output,
|
||||
untracked,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ButtonComponent } from '@shared/ui/button/button.component';
|
||||
import { AlertComponent } from '@shared/ui/alert/alert.component';
|
||||
import { SpinnerComponent } from '@shared/ui/spinner/spinner.component';
|
||||
import { StepperComponent } from '@shared/ui/stepper/stepper.component';
|
||||
import { whenTag } from '@shared/kernel/fp';
|
||||
|
||||
/** CIBG procesnavigatie primary-button copy for a non-final step: "Naar stap 2 - Werk".
|
||||
Shared so every wizard's `primaryLabel` reads the same way. */
|
||||
@@ -16,7 +26,13 @@ export interface WizardError {
|
||||
readonly message: string;
|
||||
}
|
||||
|
||||
export type WizardStatus = 'editing' | 'submitting' | 'submitted' | 'failed';
|
||||
/** The wizard shell's lifecycle union. The `Failed` variant carries the localized
|
||||
message intact, so the shell needs no separate input to say what went wrong. */
|
||||
export type WizardPhase =
|
||||
| { tag: 'Editing' }
|
||||
| { tag: 'Submitting' }
|
||||
| { tag: 'Submitted' }
|
||||
| { tag: 'Failed'; message: string };
|
||||
|
||||
/**
|
||||
* Template: the canonical shell every wizard renders into, so they cannot drift.
|
||||
@@ -52,8 +68,8 @@ export type WizardStatus = 'editing' | 'submitting' | 'submitted' | 'failed';
|
||||
`,
|
||||
],
|
||||
template: `
|
||||
@switch (status()) {
|
||||
@case ('editing') {
|
||||
@switch (phase().tag) {
|
||||
@case ('Editing') {
|
||||
<app-stepper
|
||||
class="app-section"
|
||||
[steps]="steps()"
|
||||
@@ -122,14 +138,14 @@ export type WizardStatus = 'editing' | 'submitting' | 'submitted' | 'failed';
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
@case ('submitting') {
|
||||
@case ('Submitting') {
|
||||
<app-spinner /> <span>{{ submittingLabel() }}</span>
|
||||
}
|
||||
@case ('submitted') {
|
||||
@case ('Submitted') {
|
||||
<ng-content select="[wizardSuccess]" />
|
||||
}
|
||||
@case ('failed') {
|
||||
<app-alert type="error">{{ errorMessage() }}</app-alert>
|
||||
@case ('Failed') {
|
||||
<app-alert type="error">{{ failedMessage() }}</app-alert>
|
||||
<div class="app-section">
|
||||
<app-button variant="secondary" (click)="retry.emit()" i18n="@@wizard.opnieuwProberen"
|
||||
>Opnieuw proberen</app-button
|
||||
@@ -145,13 +161,16 @@ export class WizardShellComponent {
|
||||
stepTitle = input.required<string>();
|
||||
/** Overall process name, shown above the step title (e.g. "Herregistratie aanvragen"). */
|
||||
processName = input('');
|
||||
status = input.required<WizardStatus>();
|
||||
phase = input.required<WizardPhase>();
|
||||
primaryLabel = input.required<string>();
|
||||
canGoBack = input(false);
|
||||
errors = input<readonly WizardError[]>([]);
|
||||
errorMessage = input('');
|
||||
submittingLabel = input($localize`:@@wizard.submitting:Aanvraag wordt verwerkt…`);
|
||||
|
||||
/** The `Failed` message, or '' otherwise. `@switch` can't narrow a union in a
|
||||
template, so the narrowing happens here via the shared `whenTag` helper. */
|
||||
protected failedMessage = computed(() => whenTag(this.phase(), 'Failed')?.message ?? '');
|
||||
|
||||
primary = output<void>();
|
||||
back = output<void>();
|
||||
cancel = output<void>();
|
||||
|
||||
@@ -8,8 +8,8 @@ const meta: Meta<WizardShellComponent> = {
|
||||
props: args,
|
||||
template: `
|
||||
<app-wizard-shell
|
||||
[steps]="steps" [current]="current" [stepTitle]="stepTitle" [processName]="processName" [status]="status"
|
||||
[primaryLabel]="primaryLabel" [canGoBack]="canGoBack" [errors]="errors" [errorMessage]="errorMessage"
|
||||
[steps]="steps" [current]="current" [stepTitle]="stepTitle" [processName]="processName" [phase]="phase"
|
||||
[primaryLabel]="primaryLabel" [canGoBack]="canGoBack" [errors]="errors"
|
||||
(goToStep)="goToStep($event)">
|
||||
<p class="rhc-paragraph">Voorbeeld-stapinhoud (de stapvelden worden hier geprojecteerd).</p>
|
||||
<div wizardSuccess><p class="rhc-paragraph">Uw aanvraag is ontvangen.</p></div>
|
||||
@@ -36,23 +36,25 @@ const base = {
|
||||
primaryLabel: 'Volgende',
|
||||
canGoBack: true,
|
||||
errors: [],
|
||||
errorMessage: '',
|
||||
goToStep: () => {},
|
||||
};
|
||||
|
||||
export const Editing: Story = { args: { ...base, status: 'editing' } };
|
||||
export const Editing: Story = { args: { ...base, phase: { tag: 'Editing' } } };
|
||||
export const EditingMetFouten: Story = {
|
||||
args: {
|
||||
...base,
|
||||
status: 'editing',
|
||||
phase: { tag: 'Editing' },
|
||||
errors: [
|
||||
{ id: 'uren', message: 'Vul het aantal gewerkte uren in.' },
|
||||
{ id: 'diploma', message: 'Kies een diploma.' },
|
||||
],
|
||||
},
|
||||
};
|
||||
export const Submitting: Story = { args: { ...base, status: 'submitting' } };
|
||||
export const Submitted: Story = { args: { ...base, status: 'submitted' } };
|
||||
export const Submitting: Story = { args: { ...base, phase: { tag: 'Submitting' } } };
|
||||
export const Submitted: Story = { args: { ...base, phase: { tag: 'Submitted' } } };
|
||||
export const Failed: Story = {
|
||||
args: { ...base, status: 'failed', errorMessage: 'Het indienen is niet gelukt: netwerkfout.' },
|
||||
args: {
|
||||
...base,
|
||||
phase: { tag: 'Failed', message: 'Het indienen is niet gelukt: netwerkfout.' },
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user