feat(portal-frontend): implement edit-applicant-details form with write-through disclaimer
Shared form for writeThrough and owned modes. Shows the ADR-002 disclaimer only in writeThrough mode (no client-side validation stricter than legacy provides). Maps 400 ErrorsResponse to per-field inline errors, 422/409 to a banner with the server's own text - never a client-side switch over invariant names. Embedded in case-detail with reload() on save so the actions block refreshes.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, inject } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { ErrorsResponse, InvariantViolationResponse, MessageResponse } from '../../shared/api-error.types';
|
||||
import { CaseDetailService } from '../case-detail.service';
|
||||
import { ActionLink, ApplicantDetailsRequest } from '../case-detail.types';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-applicant-details',
|
||||
imports: [FormsModule],
|
||||
templateUrl: './edit-applicant-details.html',
|
||||
styleUrl: './edit-applicant-details.css',
|
||||
})
|
||||
export class EditApplicantDetails implements OnInit {
|
||||
private readonly caseDetailService = inject(CaseDetailService);
|
||||
|
||||
@Input({ required: true }) actionLink!: ActionLink;
|
||||
@Input({ required: true }) initial!: ApplicantDetailsRequest;
|
||||
|
||||
@Output() readonly saved = new EventEmitter<void>();
|
||||
|
||||
surname = '';
|
||||
initials = '';
|
||||
street = '';
|
||||
number = '';
|
||||
postalCode = '';
|
||||
city = '';
|
||||
email = '';
|
||||
phone = '';
|
||||
preferredChannel = '';
|
||||
|
||||
fieldErrors: Record<string, string> = {};
|
||||
banner: string | null = null;
|
||||
|
||||
get isWriteThrough(): boolean {
|
||||
return this.actionLink.mode === 'writeThrough';
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.surname = this.initial.surname;
|
||||
this.initials = this.initial.initials;
|
||||
this.street = this.initial.address?.street ?? '';
|
||||
this.number = this.initial.address?.number ?? '';
|
||||
this.postalCode = this.initial.address?.postalCode ?? '';
|
||||
this.city = this.initial.address?.city ?? '';
|
||||
this.email = this.initial.email ?? '';
|
||||
this.phone = this.initial.phone ?? '';
|
||||
this.preferredChannel = this.initial.preferredChannel;
|
||||
}
|
||||
|
||||
submit(): void {
|
||||
this.fieldErrors = {};
|
||||
this.banner = null;
|
||||
|
||||
const body: ApplicantDetailsRequest = {
|
||||
surname: this.surname,
|
||||
initials: this.initials,
|
||||
address: this.street ? { street: this.street, number: this.number, postalCode: this.postalCode, city: this.city } : null,
|
||||
email: this.email || null,
|
||||
phone: this.phone || null,
|
||||
preferredChannel: this.preferredChannel,
|
||||
};
|
||||
|
||||
this.caseDetailService.updateDetails(this.actionLink.href, body).subscribe({
|
||||
next: () => this.saved.emit(),
|
||||
error: (error: HttpErrorResponse) => this.handleError(error),
|
||||
});
|
||||
}
|
||||
|
||||
private handleError(error: HttpErrorResponse): void {
|
||||
switch (error.status) {
|
||||
case 400: {
|
||||
const body = error.error as ErrorsResponse;
|
||||
this.fieldErrors = Object.fromEntries(body.errors.map((fieldError) => [fieldError.field, fieldError.message]));
|
||||
break;
|
||||
}
|
||||
case 422: {
|
||||
const body = error.error as InvariantViolationResponse;
|
||||
this.banner = `${body.invariant}: ${body.message}`;
|
||||
break;
|
||||
}
|
||||
case 409: {
|
||||
const body = error.error as MessageResponse;
|
||||
this.banner = body.message;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.banner = 'Onbekende fout bij opslaan.';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user