diff --git a/frontend/.editorconfig b/frontend/.editorconfig deleted file mode 100644 index f166060..0000000 --- a/frontend/.editorconfig +++ /dev/null @@ -1,17 +0,0 @@ -# Editor configuration, see https://editorconfig.org -root = true - -[*] -charset = utf-8 -indent_style = space -indent_size = 2 -insert_final_newline = true -trim_trailing_whitespace = true - -[*.ts] -quote_type = single -ij_typescript_use_double_quotes = false - -[*.md] -max_line_length = off -trim_trailing_whitespace = false diff --git a/frontend/src/app/components/settings-page/settings-page.html b/frontend/src/app/components/settings-page/settings-page.html index ddbcd70..d40b548 100644 --- a/frontend/src/app/components/settings-page/settings-page.html +++ b/frontend/src/app/components/settings-page/settings-page.html @@ -1,158 +1,99 @@
-
-
-

Préférences

-

Réglages

-

Adaptez LMNP Organizer à votre façon de travailler.

-
-
-
-
- -
-

Interface

-

Apparence

-

Le réglage est mémorisé uniquement sur cet appareil.

-
-
- - ClairUne interface lumineuse et douce. - SombreUn contraste réduit en faible lumière. - SystèmeSuit automatiquement votre appareil. - -
-
-
- -
-

Bailleur

-

Profil personnel

-

Ces coordonnées sont utilisées dans vos baux et vos quittances.

-
-
-
- @if (profileStatus() === 'loading') { -
-
- } @else if (profileStatus() === 'error') { -
- - -
- } @else { -
- @if (saveError()) { - - } - @if (saveSuccess()) { -

{{ saveSuccess() }}

- } -
-
- Prénom - Nom - E-mail - Téléphone - Adresse postale -
-
- -
-
-
- } -
-
-
-
- -
-

Compte

-

Session

-

Gérez la session ouverte sur cet appareil.

-
-
-
-
- {{ session.username() }}{{ session.email() }} -
- -
-
-
+
+
+

Préférences

+

Réglages

+

Adaptez LMNP Organizer à votre façon de travailler.

+
+
+
+
+ +
+

Interface

+

Apparence

+

Le réglage est mémorisé uniquement sur cet appareil.

+
+
+ + ClairUne interface + lumineuse et douce. + SombreUn contraste + réduit en faible lumière. + SystèmeSuit + automatiquement votre appareil. + +
+
+
+ +
+

Bailleur

+

Profil personnel

+

Ces coordonnées sont utilisées dans vos baux et vos quittances.

+
+
+
+ @if (profileStatus() === 'loading') { +
+
+ } @else if (profileStatus() === 'error') { +
+ + +
+ } @else { +
+ @if (saveError()) { + + } + @if (saveSuccess()) { +

{{ saveSuccess() }}

+ } +
+
+ Prénom + Nom + E-mail + Téléphone + Adresse + postale +
+
+ +
+
+
+ } +
+
+
+
+ +
+

Compte

+

Session

+

Gérez la session ouverte sur cet appareil.

+
+
+
+
+ {{ session.username() }}{{ session.email() }} +
+ +
+
+ \ No newline at end of file diff --git a/frontend/src/app/components/settings-page/settings-page.spec.ts b/frontend/src/app/components/settings-page/settings-page.spec.ts index 98adeb8..c20b3ce 100644 --- a/frontend/src/app/components/settings-page/settings-page.spec.ts +++ b/frontend/src/app/components/settings-page/settings-page.spec.ts @@ -47,18 +47,18 @@ describe('SettingsPage', () => { fixture.detectChanges(); await fixture.whenStable(); - expect(fixture.nativeElement.querySelector('input[name=firstName]').value).toBe('Camille'); - expect(fixture.nativeElement.querySelector('input[name=email]').value).toBe( + expect(fixture.nativeElement.querySelector('input#firstName').value).toBe('Camille'); + expect(fixture.nativeElement.querySelector('input#email').value).toBe( 'camille@example.com', ); - expect(fixture.nativeElement.querySelector('input[name=address]').value).toBe('1 rue de Paris'); + expect(fixture.nativeElement.querySelector('input#address').value).toBe('1 rue de Paris'); }); it('shows a useful error instead of saving an incomplete profile', () => { const fixture = TestBed.createComponent(SettingsPage); fixture.detectChanges(); const component = fixture.componentInstance; - component.phone.set(''); + component.profileModel.update((p) => ({ ...p, phone: '' })); component.save(); fixture.detectChanges(); @@ -82,7 +82,7 @@ describe('SettingsPage', () => { const fixture = TestBed.createComponent(SettingsPage); fixture.detectChanges(); const component = fixture.componentInstance; - component.email.set('alex@example.com'); + component.profileModel.update((p) => ({ ...p, email: 'alex@example.com' })); component.save(); fixture.detectChanges(); @@ -96,6 +96,6 @@ describe('SettingsPage', () => { address: '1 rue de Paris', }); expect(fixture.nativeElement.textContent).toContain('Cette adresse e-mail est déjà utilisée.'); - expect(fixture.nativeElement.querySelector('input[name=email]').value).toBe('alex@example.com'); + expect(fixture.nativeElement.querySelector('input#email').value).toBe('alex@example.com'); }); }); diff --git a/frontend/src/app/components/settings-page/settings-page.ts b/frontend/src/app/components/settings-page/settings-page.ts index 0ae95cd..6b78251 100644 --- a/frontend/src/app/components/settings-page/settings-page.ts +++ b/frontend/src/app/components/settings-page/settings-page.ts @@ -7,6 +7,7 @@ import { MatInputModule } from '@angular/material/input'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatRadioModule } from '@angular/material/radio'; import { FormsModule } from '@angular/forms'; +import { FormRoot, FormField, form, required, email } from '@angular/forms/signals'; import { HttpErrorResponse } from '@angular/common/http'; import { LandlordProfile, LandlordProfileService } from '../../services/landlord-profile.service'; import { SessionService } from '../../services/session.service'; @@ -17,6 +18,8 @@ import { ThemeService } from '../../services/theme.service'; imports: [ FontAwesomeModule, FormsModule, + FormRoot, + FormField, MatButtonModule, MatFormFieldModule, MatInputModule, @@ -36,11 +39,24 @@ export class SettingsPage { readonly profileStatus = signal<'loading' | 'ready' | 'error'>('loading'); readonly saving = signal(false); - readonly firstName = signal(''); - readonly lastName = signal(''); - readonly email = signal(''); - readonly phone = signal(''); - readonly address = signal(''); + + readonly profileModel = signal({ + firstName: '', + lastName: '', + email: '', + phone: '', + address: '', + }); + + readonly profileForm = form(this.profileModel, (f) => { + required(f.firstName, { message: 'Veuillez renseigner tous les champs obligatoires.' }); + required(f.lastName, { message: 'Veuillez renseigner tous les champs obligatoires.' }); + required(f.email, { message: 'Veuillez renseigner tous les champs obligatoires.' }); + required(f.phone, { message: 'Veuillez renseigner tous les champs obligatoires.' }); + required(f.address, { message: 'Veuillez renseigner tous les champs obligatoires.' }); + email(f.email, { message: 'Saisissez une adresse e-mail valide.' }); + }); + readonly profileError = signal(''); readonly saveError = signal(''); readonly saveSuccess = signal(''); @@ -58,7 +74,7 @@ export class SettingsPage { this.profileError.set(''); this.landlordProfile.getProfile().subscribe({ next: (profile) => { - this.applyProfile(profile); + this.profileModel.set(profile); this.profileStatus.set('ready'); }, error: () => { @@ -70,21 +86,20 @@ export class SettingsPage { save(): void { if (this.saving()) return; - const profile = { - firstName: this.firstName().trim(), - lastName: this.lastName().trim(), - email: this.email().trim(), - phone: this.phone().trim(), - address: this.address().trim(), - }; this.saveSuccess.set(''); - this.saveError.set(this.validationError(profile)); - if (this.saveError()) return; + this.profileForm().markAsTouched(); + if (this.profileForm().invalid()) { + const errors = this.profileForm().errorSummary(); + this.saveError.set(errors[0]?.message ?? 'Saisissez des informations valides.'); + return; + } + + this.saveError.set(''); this.saving.set(true); - this.landlordProfile.updateProfile(profile).subscribe({ + this.landlordProfile.updateProfile(this.profileModel()).subscribe({ next: (savedProfile) => { - this.applyProfile(savedProfile); + this.profileModel.set(savedProfile); this.saveSuccess.set('Profil bailleur enregistré.'); this.saving.set(false); }, @@ -95,28 +110,6 @@ export class SettingsPage { }); } - private validationError(profile: LandlordProfile): string { - if (Object.values(profile).some((value) => !value)) { - return 'Veuillez renseigner tous les champs obligatoires.'; - } - if ( - !profile.email.includes('@') || - profile.email.startsWith('@') || - profile.email.endsWith('@') - ) { - return 'Saisissez une adresse e-mail valide.'; - } - return ''; - } - - private applyProfile(profile: LandlordProfile): void { - this.firstName.set(profile.firstName); - this.lastName.set(profile.lastName); - this.email.set(profile.email); - this.phone.set(profile.phone); - this.address.set(profile.address); - } - private saveErrorMessage(error: unknown): string { if (error instanceof HttpErrorResponse && this.hasMessage(error.error)) { return error.error.message;