feat: isolate tenants and attachments by landlord (#26)

This commit is contained in:
nessar committed 2026-08-17 14:18:15 +02:00
1 parent f4acbffc9d
commit fca199dfdf
15 files changed
+849 -136

No files matched your search

@@ -15,12 +15,7 @@
<p>Renseignez les coordonnées nécessaires à la gestion du bail.</p>
</div>
</header>
<form
class="entity-form"
[formRoot]="tenantForm"
(ngSubmit)="save()"
[attr.aria-busy]="saving()"
>
<form class="entity-form" [formRoot]="tenantForm" (ngSubmit)="save()" [attr.aria-busy]="saving()">
@if (saveError()) {
<div class="alert-error" role="alert">{{ saveError() }}</div>
}
@@ -35,14 +30,32 @@
matInput
id="firstName"
[formField]="tenantForm.firstName"
/></mat-form-field>
[attr.aria-describedby]="
tenantForm.firstName().touched() && tenantForm.firstName().invalid()
? 'firstName-error'
: null
"
/>
@if (tenantForm.firstName().touched() && tenantForm.firstName().invalid()) {
<mat-error id="firstName-error">Le prénom est obligatoire.</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline"
><mat-label>Nom</mat-label
><input
matInput
id="lastName"
[formField]="tenantForm.lastName"
/></mat-form-field>
[attr.aria-describedby]="
tenantForm.lastName().touched() && tenantForm.lastName().invalid()
? 'lastName-error'
: null
"
/>
@if (tenantForm.lastName().touched() && tenantForm.lastName().invalid()) {
<mat-error id="lastName-error">Le nom est obligatoire.</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline"
><mat-label>E-mail</mat-label
><input
@@ -51,22 +64,27 @@
id="email"
[formField]="tenantForm.email"
autocomplete="email"
/></mat-form-field>
[attr.aria-describedby]="
tenantForm.email().touched() && tenantForm.email().invalid() ? 'email-error' : null
"
/>
@if (tenantForm.email().touched() && tenantForm.email().invalid()) {
<mat-error id="email-error">
@if (tenantForm.email().getError('required')) {
L’e-mail est obligatoire.
} @else {
Saisissez une adresse e-mail valide.
}
</mat-error>
}
</mat-form-field>
<mat-form-field appearance="outline"
><mat-label>Téléphone</mat-label
><input
matInput
type="tel"
id="phone"
[formField]="tenantForm.phone"
autocomplete="tel"
><input matInput type="tel" id="phone" [formField]="tenantForm.phone" autocomplete="tel"
/></mat-form-field>
<mat-form-field appearance="outline" class="full-field"
><mat-label>Adresse complète</mat-label
><input
matInput
id="address"
[formField]="tenantForm.address"
><input matInput id="address" [formField]="tenantForm.address"
/></mat-form-field>
</div>
<div class="form-actions">
@@ -43,4 +43,28 @@ describe('TenantForm', () => {
expect(fixture.componentInstance.tenantForm().touched()).toBe(true);
expect(submitted).not.toHaveBeenCalled();
});
it('shows actionable validation guidance linked to invalid fields', () => {
const fixture = TestBed.createComponent(TenantForm);
fixture.detectChanges();
fixture.componentInstance.tenantModel.set({
firstName: '',
lastName: 'Martin',
email: 'invalid-email',
phone: '',
address: '',
});
fixture.componentInstance.save();
fixture.detectChanges();
const firstNameInput = fixture.nativeElement.querySelector('#firstName');
const emailInput = fixture.nativeElement.querySelector('#email');
const firstNameError = fixture.nativeElement.querySelector('#firstName-error');
const emailError = fixture.nativeElement.querySelector('#email-error');
expect(firstNameError?.textContent ?? '').toContain('Le prénom est obligatoire');
expect(emailError?.textContent ?? '').toContain('Saisissez une adresse e-mail valide');
expect(firstNameInput.getAttribute('aria-describedby')).toContain('firstName-error');
expect(emailInput.getAttribute('aria-describedby')).toContain('email-error');
});
});