Step 2 (code quality): dedup + stop FE recomputing a server rule
- H1: tasksFromProfile takes the server's eligibleForHerregistratie decision
instead of recomputing isHerregistratieEligible — the FE renders the rule,
doesn't own it (ADR-0001). Policy reference impl kept for tests.
- M1: one shared runSubmit(fn, fallback) wrapper; the 4 submit-* commands keep
only their payload mapping. +spec.
- M2: whenTag() kernel helper removes 10 repeated `as Extract<U,{tag}>` casts
across the wizard/form components.
M4 (shared JA_NEE) folded into the upcoming i18n pass (clean dedup needs
$localize labels to sit in shared without breaking the English-shared-UI rule).
L1 already resolved by the restyle commit.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { HeadingComponent } from '@shared/ui/heading/heading.component';
|
||||
import { AlertComponent } from '@shared/ui/alert/alert.component';
|
||||
import { AddressFieldsComponent, AdresValue, AdresErrors } from '@registratie/ui/address-fields/address-fields.component';
|
||||
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';
|
||||
@@ -55,10 +56,10 @@ export class ChangeRequestFormComponent {
|
||||
readonly state = this.store.model;
|
||||
protected dispatch = this.store.dispatch;
|
||||
|
||||
private editing = computed(() => (this.state().tag === 'Editing' ? (this.state() as Extract<State, { tag: 'Editing' }>) : null));
|
||||
private editing = computed(() => whenTag(this.state(), 'Editing'));
|
||||
protected errors = computed<AdresErrors>(() => this.editing()?.errors ?? {});
|
||||
protected failedError = computed(() => (this.state().tag === 'Failed' ? (this.state() as Extract<State, { tag: 'Failed' }>).error : ''));
|
||||
protected referentie = computed(() => (this.state().tag === 'Submitted' ? (this.state() as Extract<State, { tag: 'Submitted' }>).referentie : ''));
|
||||
protected failedError = computed(() => whenTag(this.state(), 'Failed')?.error ?? '');
|
||||
protected referentie = computed(() => whenTag(this.state(), 'Submitted')?.referentie ?? '');
|
||||
|
||||
/** The address shown in the fields — the live draft while editing, the parsed
|
||||
data while submitting/failed (so the user sees what they sent). */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { PageShellComponent } from '@shared/layout/page-shell/page-shell.component';
|
||||
import { HeadingComponent } from '@shared/ui/heading/heading.component';
|
||||
import { LinkComponent } from '@shared/ui/link/link.component';
|
||||
@@ -103,10 +103,15 @@ import { tasksFromProfile } from '@registratie/domain/tasks';
|
||||
})
|
||||
export class DashboardPage {
|
||||
protected store = inject(BigProfileStore);
|
||||
private readonly today = new Date();
|
||||
|
||||
/** Server-computed eligibility (rendered, not recomputed). */
|
||||
private readonly eligible = computed(() => {
|
||||
const d = this.store.decisions();
|
||||
return d.tag === 'Success' && d.value.eligibleForHerregistratie;
|
||||
});
|
||||
|
||||
protected tasksFor(reg: Registration) {
|
||||
return tasksFromProfile(reg, this.today);
|
||||
return tasksFromProfile(reg, this.eligible());
|
||||
}
|
||||
|
||||
/** Portal sections (navigation, not actions). */
|
||||
|
||||
@@ -11,6 +11,7 @@ import { WizardShellComponent, WizardError, WizardStatus } from '@shared/layout/
|
||||
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';
|
||||
@@ -180,13 +181,13 @@ export class RegistratieWizardComponent {
|
||||
readonly state = this.store.model;
|
||||
readonly dispatch = this.store.dispatch;
|
||||
|
||||
private invullen = computed(() => (this.state().tag === 'Invullen' ? (this.state() as Extract<RegistratieState, { tag: 'Invullen' }>) : null));
|
||||
private invullen = computed(() => whenTag(this.state(), 'Invullen'));
|
||||
protected cursor = computed(() => this.invullen()?.cursor ?? 0);
|
||||
protected draft = computed<Draft>(() => this.invullen()?.draft ?? { antwoorden: {} });
|
||||
protected step = computed<StepId>(() => STEPS[Math.min(this.cursor(), STEPS.length - 1)]);
|
||||
protected stepTitle = computed(() => this.stepTitles[Math.min(this.cursor(), this.stepTitles.length - 1)]);
|
||||
protected referentie = computed(() => (this.state().tag === 'Ingediend' ? (this.state() as Extract<RegistratieState, { tag: 'Ingediend' }>).referentie : ''));
|
||||
protected failedError = computed(() => (this.state().tag === 'Mislukt' ? (this.state() as Extract<RegistratieState, { tag: 'Mislukt' }>).error : ''));
|
||||
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(() => (this.step() === 'controle' ? 'Registratie indienen' : 'Volgende'));
|
||||
|
||||
Reference in New Issue
Block a user