refactor: rename Application → Aanvraag across the wire (Step 1/8)

The wire said Application, the domain said Aanvraag — one aggregate with
two names at every hop. Rename the backend DTOs and the /applications
route to /aanvragen, regenerate the typed client, and rename the frontend
adapter/store to match.

Renamed: ApplicationSummaryDto/DetailDto, CreateApplicationRequest,
SubmitApplicationRequest/Response → Aanvraag* equivalents;
ApplicationsAdapter/Store → AanvragenAdapter/Store;
applications.adapter.ts/applications.store.ts → aanvragen.*.

Left untouched: the admin Case/Zaak vocabulary (/admin/cases,
AdminCasesStore) — a separate read model, not part of this rename; the
internal BigRegister.Domain.Applications namespace and the Applications
EF table (renaming those needs a new EF migration, out of scope here).

Part of the dashboard-readability refactor (see the approved plan).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-04 14:33:16 +02:00
co-authored by Claude Opus 5
parent faad772f85
commit 194cccfd02
36 changed files with 400 additions and 412 deletions
@@ -0,0 +1,72 @@
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 (RB-20).
*/
@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 (RB-20): 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 (RB-20) — 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);
}
}
}