Files
atomic-design-poc/apps/ssp/src/app/registratie/application/registratie-lookup.store.ts
T
ehoandClaude Sonnet 5 c7aed8d308 fix: narrow parseBrpAddress's return type instead of leaking the DTO
BrpAddressDto's fields are generated as optional, so returning it
directly from parseBrpAddress lost the narrowing the runtime check
already did. This broke the build once registratie-lookup.store.ts
assigned the parsed address into a stricter local type. Map to a
proper BrpAddress domain shape at the trust boundary instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 15:17:06 +02:00

63 lines
2.7 KiB
TypeScript

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.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();
}
}