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'; 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 { ChangeRequestState, ChangeRequestMsg, initial, reduce } from '@registratie/domain/change-request.machine'; import { createSubmitChangeRequest } from '@registratie/application/submit-change-request'; /** * Organism: change-request (adreswijziging) form. Uses the SAME idiom as the * wizards — all state in one signal driven by the pure `reduce` * (change-request.machine.ts), submitted via a `submit-*` command returning * `Result`. Renders the shared ``; the server re-validates. */ @Component({ selector: 'app-change-request-form', imports: [FormsModule, ButtonComponent, HeadingComponent, AlertComponent, AddressFieldsComponent], template: ` @if (state().tag === 'Submitted') { Uw adreswijziging is ontvangen (referentie {{ referentie() }}). U ontvangt binnen 5 werkdagen bericht.
Nieuwe wijziging doorgeven
} @else { Adreswijziging doorgeven
* verplichte velden
@if (failedError()) { Het indienen is niet gelukt: {{ failedError() }} } {{ state().tag === 'Submitting' ? submitBezigLabel : submitLabel }} } `, }) export class ChangeRequestFormComponent { // 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. */ seed = input(initial); readonly state = this.store.model; protected dispatch = this.store.dispatch; protected readonly submitLabel = $localize`:@@changeRequest.submit:Wijziging indienen`; protected readonly submitBezigLabel = $localize`:@@changeRequest.submitBezig:Bezig met indienen…`; private editing = computed(() => whenTag(this.state(), 'Editing')); protected errors = computed(() => this.editing()?.errors ?? {}); 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). */ protected adres = computed(() => { const s = this.state(); if (s.tag === 'Editing') return s.draft; if (s.tag === 'Submitting' || s.tag === 'Failed') { return { straat: s.data.straat, postcode: s.data.postcode, woonplaats: s.data.woonplaats }; } return { straat: '', postcode: '', woonplaats: '' }; // Submitted shows the success alert, not the fields }); constructor() { queueMicrotask(() => this.dispatch({ tag: 'Seed', state: this.seed() })); } onSubmit() { this.dispatch({ tag: 'Submit' }); this.runIfSubmitting(); } /** Effect: when we entered Submitting, call the command, then dispatch the outcome. */ private async runIfSubmitting() { const s = this.state(); if (s.tag !== 'Submitting') return; 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 }); } }