import { Injectable, computed, inject, signal } from '@angular/core'; import { RemoteData, fromResource, map } from '@shared/application/remote-data'; import { Aantekening } from '../domain/registration'; import { BigProfile } from '../domain/big-profile'; import { HerregistratieDecisions } from '../contracts/dashboard-view.dto'; import { BigRegisterAdapter } from '../infrastructure/big-register.adapter'; import { DashboardView, DashboardViewAdapter, parseDashboardView } from '../infrastructure/dashboard-view.adapter'; type Err = Error | undefined; /** * The single source of truth for the logged-in professional's profile, shared * across pages (providedIn:'root' = one instance). It owns the httpResources * (created here, in the required injection context) and exposes them as * RemoteData signals. * * The dashboard data now comes from ONE screen-shaped ("BFF-lite") call that * returns registration + person + server-computed `decisions`. One request → one * consistent snapshot, instead of stitching three independently loading/erroring * resources together client-side. See docs/architecture/0001-bff-lite-decision-dtos.md. */ @Injectable({ providedIn: 'root' }) export class BigProfileStore { private big = inject(BigRegisterAdapter); private viewAdapter = inject(DashboardViewAdapter); private viewRes = this.viewAdapter.dashboardViewResource(); private aantekeningenRes = this.big.aantekeningenResource(); /** The aggregated view, validated at the trust boundary (DTO → domain). */ private view = computed>(() => { const rd = fromResource(this.viewRes); if (rd.tag !== 'Success') return rd; const parsed = parseDashboardView(rd.value); return parsed.ok ? { tag: 'Success', value: parsed.value } : { tag: 'Failure', error: new Error(parsed.error) }; }); /** Registration + person, from the single aggregated call. */ readonly profile = computed>(() => map(this.view(), (v) => v.profile)); /** Server-computed decisions (e.g. herregistratie eligibility) — rendered, not recomputed. */ readonly decisions = computed>(() => map(this.view(), (v) => v.decisions)); /** Specialisms/notes stay a separate stream (they have their own empty state). */ readonly aantekeningen = computed>(() => { const rd = fromResource(this.aantekeningenRes, (v) => !v || v.length === 0); return rd.tag === 'Success' ? { tag: 'Success', value: rd.value ?? [] } : rd; }); // --- Optimistic herregistratie state, shared with the dashboard ----------- private pending = signal(false); /** True while a herregistratie submission is in flight or just submitted. */ readonly pendingHerregistratie = this.pending.asReadonly(); beginHerregistratie() { this.pending.set(true); // optimistic: show it immediately on the dashboard } confirmHerregistratie() { this.pending.set(false); this.viewRes.reload(); // invalidate: re-fetch the now-updated view (registration + decisions) } rollbackHerregistratie() { this.pending.set(false); // submission failed — undo the optimistic flag } }