Align apartment core validation errors (#43)

This commit is contained in:
nessar committed 2026-08-17 14:18:16 +02:00
1 parent 878da155bd
commit b9f5540394
13 files changed
+454 -56

No files matched your search

@@ -13,9 +13,17 @@ import {
} from '../../services/apartment.service';
import { ApartmentLegalProfileForm } from '../apartment-legal-profile-form/apartment-legal-profile-form';
import { PrivateAreaEditor } from '../private-area-editor/private-area-editor';
import { FieldValidationError } from '../../services/http-error-message';
const requiredFieldsError =
'Veuillez remplir tous les champs obligatoires (Nom, Adresse, Prix > 0).';
const nameRequiredError = 'Le nom de l’appartement est obligatoire.';
const addressRequiredError = 'L’adresse complète est obligatoire.';
const purchasePriceError = 'Le prix d’acquisition doit être strictement positif.';
type CoreField = 'name' | 'address' | 'purchasePrice';
const serverErrorCodes: Record<CoreField, ReadonlySet<string>> = {
name: new Set(['not_blank']),
address: new Set(['not_blank']),
purchasePrice: new Set(['positive']),
};
@Component({
selector: 'app-apartment-form',
@@ -37,8 +45,10 @@ export class ApartmentForm {
readonly apartment = input<Apartment | null>(null);
readonly saving = input(false);
readonly saveError = input('');
readonly saveFieldErrors = input<FieldValidationError[]>([]);
readonly closed = output<void>();
readonly saved = output<Apartment>();
readonly fieldChanged = output<string>();
readonly apartmentModel = signal<Apartment>({
name: '',
@@ -50,10 +60,10 @@ export class ApartmentForm {
});
readonly apartmentForm = form(this.apartmentModel, (f) => {
required(f.name, { message: requiredFieldsError });
required(f.address, { message: requiredFieldsError });
required(f.purchasePrice, { message: requiredFieldsError });
min(f.purchasePrice, 1, { message: requiredFieldsError });
required(f.name, { message: nameRequiredError });
required(f.address, { message: addressRequiredError });
required(f.purchasePrice, { message: purchasePriceError });
min(f.purchasePrice, Number.MIN_VALUE, { message: purchasePriceError });
min(f.legalProfile.habitableSurface, 0, {
message: 'La surface habitable doit être positive ou nulle.',
});
@@ -87,7 +97,7 @@ export class ApartmentForm {
!this.apartmentModel().address.trim()
) {
const firstError = this.apartmentForm().errorSummary()[0];
this.error.set(firstError?.message ?? requiredFieldsError);
this.error.set(firstError?.message ?? this.coreFieldError() ?? nameRequiredError);
return;
}
@@ -106,6 +116,48 @@ export class ApartmentForm {
this.apartmentModel.update((apartment) => ({ ...apartment, privateAreas }));
}
nameError(): string {
return this.fieldError('name', nameRequiredError, this.apartmentForm.name().touched());
}
addressError(): string {
return this.fieldError(
'address',
addressRequiredError,
this.apartmentForm.address().touched(),
);
}
purchasePriceError(): string {
return this.fieldError(
'purchasePrice',
purchasePriceError,
this.apartmentForm.purchasePrice().touched(),
);
}
private coreFieldError(): string | null {
return this.nameError() || this.addressError() || this.purchasePriceError() || null;
}
private fieldError(path: CoreField, clientMessage: string, touched: boolean): string {
if (
touched &&
((path === 'name' && !this.apartmentModel().name.trim()) ||
(path === 'address' && !this.apartmentModel().address.trim()) ||
(path === 'purchasePrice' && this.apartmentModel().purchasePrice <= 0))
) {
return clientMessage;
}
return this.hasKnownServerError(path) ? clientMessage : '';
}
private hasKnownServerError(field: CoreField): boolean {
return this.saveFieldErrors().some(
(error) => error.field === field && serverErrorCodes[field].has(error.code),
);
}
private resetForm(apartment: Apartment | null): void {
this.apartmentModel.set({
id: apartment?.id,