import { Component, inject, type OnInit, signal } from '@angular/core'; import { BffApiV1Service, type CurrentRegistration, type SubmitAccepted } from 'api-client'; import { AuthService } from 'auth'; import { UtrechtComponentsModule } from 'ui'; /** * The self-service submit page: a signed-in zorgprofessional confirms and submits their BIG * registration. The bsn comes from the DigiD token (not a form field), so this is a confirm-and- * submit flow that posts to the BFF and shows the returned reference (ADR-0010; S-08c). After * submitting they can withdraw it — "trek aanvraag in" — keyed by that reference (S-11c). * * On load it asks the BFF for the caller's current open registration and restores the submitted view * if there is one, so a page refresh no longer strands an in-flight registration (S-26). */ @Component({ selector: 'app-registration-page', imports: [UtrechtComponentsModule], templateUrl: './registration-page.html', }) export class RegistrationPage implements OnInit { private readonly auth = inject(AuthService); private readonly bff = inject(BffApiV1Service); protected readonly bsn = this.auth.bsn; protected readonly submitting = signal(false); protected readonly reference = signal(undefined); protected readonly submitted = signal(false); protected readonly failed = signal(false); protected readonly withdrawing = signal(false); protected readonly withdrawn = signal(false); protected readonly withdrawFailed = signal(false); protected readonly providingDocuments = signal(false); protected readonly documentsProvided = signal(false); protected readonly provideDocumentsFailed = signal(false); protected readonly selectedFile = signal(undefined); /** Resume an existing in-flight registration after a refresh (S-26): the BFF returns the caller's * current open registration, or 204 (empty body) when there is none — in which case we show the * submit form as before. Failures are non-fatal for the same reason. */ ngOnInit(): void { this.bff.getSelfServiceRegistrations().subscribe({ next: (current: CurrentRegistration | void) => { if (current && current.registrationId) { this.reference.set(current.registrationId); this.submitted.set(true); } }, error: () => { // No resumable registration (or the lookup failed) — fall back to the submit form. }, }); } submit(): void { this.submitting.set(true); this.failed.set(false); this.bff.postSelfServiceRegistrations().subscribe({ next: (accepted: SubmitAccepted) => { this.reference.set(accepted.registrationId); this.submitted.set(true); this.submitting.set(false); }, // Surface the failure instead of swallowing it: re-enable the button so the user can retry. error: () => { this.failed.set(true); this.submitting.set(false); }, }); } onFileSelected(event: Event): void { const input = event.target as HTMLInputElement; this.selectedFile.set(input.files?.[0] ?? undefined); } async provideDocuments(): Promise { const reference = this.reference(); const file = this.selectedFile(); if (!reference || !file) { return; } this.providingDocuments.set(true); this.provideDocumentsFailed.set(false); let contentBase64: string; try { contentBase64 = await readAsBase64(file); } catch { this.provideDocumentsFailed.set(true); this.providingDocuments.set(false); return; } this.bff .postSelfServiceRegistrationsIdDocuments(reference, { contentBase64, fileName: file.name, contentType: file.type || 'application/pdf', }) .subscribe({ next: () => { this.documentsProvided.set(true); this.providingDocuments.set(false); }, // Surface the failure instead of swallowing it: keep the action so the user can retry. error: () => { this.provideDocumentsFailed.set(true); this.providingDocuments.set(false); }, }); } withdraw(): void { const reference = this.reference(); if (!reference) { return; } this.withdrawing.set(true); this.withdrawFailed.set(false); this.bff.postSelfServiceRegistrationsIdWithdraw(reference).subscribe({ next: () => { this.withdrawn.set(true); this.withdrawing.set(false); }, // Surface the failure instead of swallowing it: keep the action so the user can retry. error: () => { this.withdrawFailed.set(true); this.withdrawing.set(false); }, }); } } /** Read a file's bytes as a base64 string (without the `data:...;base64,` prefix). */ function readAsBase64(file: File): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => resolve(((reader.result as string) ?? '').split(',', 2)[1] ?? ''); reader.onerror = () => reject(reader.error ?? new Error('Could not read the file.')); reader.readAsDataURL(file); }); }