204 WP-NN/RB-NN comments named a closed ticket instead of the code they sit next to. git blame already records history and stays correct when code moves; the comment does not. This sweep removes the reference and keeps the sentence, across 95 files in apps/ and libs/ plus the behaviour-spec generator's header text. Eleven references stay: five story files justify an a11y disable per the README's rule, and one line in a11y.mdx documents that convention. Two sentences needed a rewrite, not a deletion, so the reference's meaning survives its removal. behaviour-spec.mdx is regenerated, not hand-edited. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
73 lines
2.9 KiB
TypeScript
73 lines
2.9 KiB
TypeScript
import { Injectable, inject, signal } from '@angular/core';
|
|
import { RemoteData } from '@shared/application/remote-data';
|
|
import { runSubmit, SUBMIT_FAILED } from '@shared/application/submit';
|
|
import { Aanvraag } from '@registratie/domain/aanvraag';
|
|
import { AanvragenAdapter, parseAanvragen } from '@registratie/infrastructure/aanvragen.adapter';
|
|
|
|
type Err = Error | undefined;
|
|
|
|
/**
|
|
* The dashboard's view of the user's aanvragen — the backend is the
|
|
* system of record (PRD 0001). One root singleton OWNS the list as a writable
|
|
* RemoteData signal (CLAUDE.md §3: change state only through methods). Cancel removes
|
|
* the row SYNCHRONOUSLY, so the block disappears deterministically — no dependence on
|
|
* change-detection timing, HTTP caching, or a resource `reload()`. `reload()` re-fetches
|
|
* so a page revisit reflects auto-approval (Concept → In behandeling → Goedgekeurd is
|
|
* computed server-side on read). Cancel goes through `runSubmit` and rolls back plus
|
|
* surfaces `lastError` on failure.
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class AanvragenStore {
|
|
private adapter = inject(AanvragenAdapter);
|
|
|
|
private state = signal<RemoteData<Err, Aanvraag[]>>({ tag: 'Loading' });
|
|
readonly aanvragen = this.state.asReadonly();
|
|
|
|
/** Set on a failed cancel: the optimistic removal already rolled back by
|
|
then, this is only the message for the alert the page renders above the list. */
|
|
private error = signal<string | null>(null);
|
|
readonly lastError = this.error.asReadonly();
|
|
|
|
constructor() {
|
|
void this.load();
|
|
}
|
|
|
|
/** Fetch + parse at the trust boundary, then publish as RemoteData. Keeps the
|
|
last-good value on a resync (only shows Loading on the first load). */
|
|
async load() {
|
|
if (this.state().tag !== 'Success') this.state.set({ tag: 'Loading' });
|
|
try {
|
|
const parsed = parseAanvragen(await this.adapter.list());
|
|
this.state.set(
|
|
parsed.ok
|
|
? { tag: 'Success', value: parsed.value }
|
|
: { tag: 'Failure', error: new Error(parsed.error) },
|
|
);
|
|
} catch (e) {
|
|
this.state.set({ tag: 'Failure', error: e as Error });
|
|
}
|
|
}
|
|
|
|
/** Re-fetch (e.g. on dashboard revisit) so auto-approval transitions show up. */
|
|
reload() {
|
|
void this.load();
|
|
}
|
|
|
|
/** Cancel a Concept: drop it now (synchronous, guaranteed), then confirm the DELETE.
|
|
No resync — the delete succeeded, so the optimistic removal is authoritative. On
|
|
failure, roll back AND surface the error — a silent reappearance leaves the
|
|
user guessing why the block came back. */
|
|
async cancel(id: string) {
|
|
const before = this.state();
|
|
if (before.tag === 'Success') {
|
|
this.state.set({ tag: 'Success', value: before.value.filter((a) => a.id !== id) });
|
|
}
|
|
this.error.set(null);
|
|
const r = await runSubmit(() => this.adapter.cancel(id), SUBMIT_FAILED);
|
|
if (!r.ok) {
|
|
this.state.set(before); // roll back: the block reappears
|
|
this.error.set(r.error);
|
|
}
|
|
}
|
|
}
|