Add registratie wizard, BFF dashboard-view, contracts/value-objects, and architecture docs

Checkpoint of in-progress work: the registration wizard (address prefill,
DUO diploma lookup, policy questions), decision-DTO contracts, parse-don't-
validate value objects, infrastructure adapters, plus CLAUDE.md and the
architecture/ADR docs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 17:23:52 +02:00
parent 8a8a2f0f29
commit 64385999eb
58 changed files with 7271 additions and 556 deletions

View File

@@ -1,22 +1,45 @@
import { Injectable, computed, inject, signal } from '@angular/core';
import { Injectable, computed, effect, inject, signal } from '@angular/core';
import { Result } from '@shared/kernel/fp';
import { Session } from '../domain/session';
import { DigidAdapter } from '../infrastructure/digid.adapter';
const STORAGE_KEY = 'session-v1';
/** Restore a persisted session (best-effort; corrupt entry → logged out). */
function restore(): Session | null {
try {
const raw = sessionStorage.getItem(STORAGE_KEY);
return raw ? (JSON.parse(raw) as Session) : null;
} catch {
return null;
}
}
/**
* Holds the current session for the whole app. Because it is providedIn:'root'
* there is exactly one instance — every component that injects it sees the same
* session signal, so logging in is instantly visible everywhere (the guard, the
* header, etc.). ponytail: in-memory only; a refresh logs you out.
* header, etc.). The session is mirrored to sessionStorage so a refresh or a
* deep-link to a protected route keeps you logged in; it clears when the tab
* closes. ponytail: sessionStorage, not localStorage — no cross-tab sync, which
* matches a single-session portal.
*/
@Injectable({ providedIn: 'root' })
export class SessionStore {
private digid = inject(DigidAdapter);
private _session = signal<Session | null>(null);
private _session = signal<Session | null>(restore());
readonly session = this._session.asReadonly();
readonly isAuthenticated = computed(() => this._session() !== null);
constructor() {
effect(() => {
const s = this._session();
if (s) sessionStorage.setItem(STORAGE_KEY, JSON.stringify(s));
else sessionStorage.removeItem(STORAGE_KEY);
});
}
/** Effectful command: authenticate, then store the session on success. */
async login(bsn: string): Promise<Result<string, Session>> {
const r = await this.digid.authenticate(bsn);