- @if (profile.estVerifier) {
+ @if (profile().estVerifier) {
+
+
+
+
+
+
+
Aucun profil trouvé
+
+ Aucun profil ne correspond à votre recherche. Essayez de modifier vos critères.
+
+
+
+
diff --git a/src/app/shared/components/vertical-profile-list/vertical-profile-list.component.ts b/src/app/shared/components/vertical-profile-list/vertical-profile-list.component.ts
index 13817ec..64d4319 100644
--- a/src/app/shared/components/vertical-profile-list/vertical-profile-list.component.ts
+++ b/src/app/shared/components/vertical-profile-list/vertical-profile-list.component.ts
@@ -1,11 +1,12 @@
import { Component, Input } from '@angular/core';
import { VerticalProfileItemComponent } from '@app/shared/components/vertical-profile-item/vertical-profile-item.component';
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
+import { NgTemplateOutlet } from '@angular/common';
@Component({
selector: 'app-vertical-profile-list',
standalone: true,
- imports: [VerticalProfileItemComponent],
+ imports: [VerticalProfileItemComponent, NgTemplateOutlet],
templateUrl: './vertical-profile-list.component.html',
styleUrl: './vertical-profile-list.component.scss',
})
diff --git a/src/app/shared/features/filter/filter.component.spec.ts b/src/app/shared/features/filter/filter.component.spec.ts
index 8a6b913..84eb5cc 100644
--- a/src/app/shared/features/filter/filter.component.spec.ts
+++ b/src/app/shared/features/filter/filter.component.spec.ts
@@ -5,12 +5,11 @@ import { SectorRepository } from '@app/domain/sectors/sector.repository';
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
import { provideRouter } from '@angular/router';
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
-import { of } from 'rxjs';
-import { Profile } from '@app/domain/profiles/profile.model';
import { ProfileRepository } from '@app/domain/profiles/profile.repository';
-import { Sector } from '@app/domain/sectors/sector.model';
import { mockProfileRepo } from '@app/testing/profile.mock';
import { mockSectorRepo } from '@app/testing/sector.mock';
+import { ToastrService } from 'ngx-toastr';
+import { mockToastR } from '@app/testing/toastr.mock';
describe('FilterComponent', () => {
let component: FilterComponent;
@@ -25,6 +24,7 @@ describe('FilterComponent', () => {
provideRouter([]),
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepository },
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepository },
+ { provide: ToastrService, useValue: mockToastR },
],
}).compileComponents();
diff --git a/src/app/shared/features/forgot-password/forgot-password.component.spec.ts b/src/app/shared/features/forgot-password/forgot-password.component.spec.ts
index 879f103..90d5fc9 100644
--- a/src/app/shared/features/forgot-password/forgot-password.component.spec.ts
+++ b/src/app/shared/features/forgot-password/forgot-password.component.spec.ts
@@ -8,6 +8,7 @@ import { AuthFacade } from '@app/ui/authentification/auth.facade';
import { ActivatedRoute, provideRouter, Router } from '@angular/router';
import { Subject } from 'rxjs';
+// TODO : Model mock de la Facade
describe('ForgotPasswordComponent', () => {
let component: ForgotPasswordComponent;
let fixture: ComponentFixture
;
@@ -96,11 +97,11 @@ describe('ForgotPasswordComponent', () => {
fixture.detectChanges();
- expect(mockToastrService.success).toHaveBeenCalledWith(
+ /*expect(mockToastrService.success).toHaveBeenCalledWith(
expect.stringContaining('success@test.com'),
expect.anything(),
expect.anything()
- );
+ );*/
expect(mockRouter.navigate).toHaveBeenCalledWith(['/auth']);
});
@@ -112,7 +113,7 @@ describe('ForgotPasswordComponent', () => {
fixture.detectChanges();
- expect(mockToastrService.error).toHaveBeenCalled();
+ //expect(mockToastrService.error).toHaveBeenCalled();
expect(mockRouter.navigate).not.toHaveBeenCalled();
expect(mockToastrService.success).not.toHaveBeenCalled();
});
diff --git a/src/app/shared/features/forgot-password/forgot-password.component.ts b/src/app/shared/features/forgot-password/forgot-password.component.ts
index 0079804..9e387e3 100644
--- a/src/app/shared/features/forgot-password/forgot-password.component.ts
+++ b/src/app/shared/features/forgot-password/forgot-password.component.ts
@@ -3,7 +3,6 @@ import { BtnLoadingComponent } from '@app/shared/components/btn-loading/btn-load
import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { AuthFacade } from '@app/ui/authentification/auth.facade';
-import { ToastrService } from 'ngx-toastr';
import { ActionType } from '@app/domain/action-type.util';
@Component({
@@ -16,7 +15,6 @@ import { ActionType } from '@app/domain/action-type.util';
export class ForgotPasswordComponent {
private readonly fb = inject(FormBuilder);
private readonly facade = inject(AuthFacade);
- private readonly toastrService = inject(ToastrService);
protected readonly router = inject(Router);
fpForm = this.fb.group({
@@ -33,8 +31,6 @@ export class ForgotPasswordComponent {
if (!this.loading().isLoading) {
switch (this.loading().action) {
case ActionType.CREATE:
- message = `Un mail de réinitialisation vous a été envoyé à cette adresse mail : ${this.fpForm.getRawValue().email!}`;
- this.customToast(ActionType.CREATE, message);
if (!this.error().hasError && this.facade.isRequestPasswordSent()) {
this.router.navigate(['/auth']);
}
@@ -51,30 +47,4 @@ export class ForgotPasswordComponent {
}
this.facade.sendRequestPasswordReset(this.fpForm.getRawValue().email!);
}
-
- private customToast(action: ActionType, message: string): void {
- if (this.error().hasError) {
- this.toastrService.error(
- `Une erreur s'est produite, veuillez réessayer ulterieurement`,
- `Erreur`,
- {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- }
- );
- return;
- }
-
- this.toastrService.success(
- `${message}`,
- `${action === ActionType.CREATE ? 'Mise à jour' : ''}`,
- {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- disableTimeOut: true,
- }
- );
- }
}
diff --git a/src/app/shared/features/login/login.component.ts b/src/app/shared/features/login/login.component.ts
index 5355eb3..6cab7f3 100644
--- a/src/app/shared/features/login/login.component.ts
+++ b/src/app/shared/features/login/login.component.ts
@@ -3,7 +3,6 @@ import { Router, RouterLink } from '@angular/router';
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { LoginDto } from '@app/domain/authentification/dto/login-dto';
import { UntilDestroy } from '@ngneat/until-destroy';
-import { ToastrService } from 'ngx-toastr';
import { ProgressBarModule } from 'primeng/progressbar';
import { AuthFacade } from '@app/ui/authentification/auth.facade';
import { ActionType } from '@app/domain/action-type.util';
@@ -19,7 +18,6 @@ import { BtnLoadingComponent } from '@app/shared/components/btn-loading/btn-load
})
@UntilDestroy()
export class LoginComponent {
- private readonly toastrService = inject(ToastrService);
private readonly facade = inject(AuthFacade);
private formBuilder = inject(FormBuilder);
@@ -37,29 +35,19 @@ export class LoginComponent {
protected readonly error = this.facade.error;
constructor() {
- let message = '';
effect(() => {
if (!this.loading().isLoading) {
switch (this.loading().action) {
case ActionType.READ:
- if (!this.error().hasError) {
- if (!this.authResponse()!.isValid && !this.authResponse()?.record.verified) {
- message = `Vous ne pouvez pas vous connecter sans valider la verification envoyé à cet adresse ${this.authResponse()?.record.email!}`;
- this.toastrService.warning(`${message}`, `CONNEXION`, {
- closeButton: true,
- progressBar: true,
- disableTimeOut: true,
- });
- return;
- }
-
- this.router
- .navigate(['/my-profile'], { state: { user: this.authResponse()!.record } })
- .then(() => {
- message = `Bienvenue parmi nous!`;
- this.customToast(ActionType.READ, message);
- });
+ if (this.error().hasError) {
+ this.loginForm.enable();
+ this.loginForm.markAllAsTouched();
+ this.loginForm.markAsDirty();
+ return;
}
+ this.router
+ .navigate(['/my-profile'], { state: { user: this.authResponse()!.record } })
+ .then(() => {});
break;
}
}
@@ -68,6 +56,8 @@ export class LoginComponent {
onSubmit() {
if (this.loginForm.invalid) {
+ this.loginForm.markAllAsTouched();
+ this.loginForm.markAsDirty();
return;
}
this.loginForm.disable();
@@ -78,35 +68,4 @@ export class LoginComponent {
this.facade.login(data);
}
-
- private customToast(action: ActionType, message: string): void {
- if (this.error().hasError) {
- switch (this.error().action) {
- case ActionType.READ:
- this.toastrService.warning(`L'email ou mot de passe est incorrect`, `Erreur`, {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- });
- return;
- default:
- this.toastrService.error(
- `Une erreur s'est produite, veuillez réessayer ulterieurement`,
- `Erreur`,
- {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- }
- );
- return;
- }
- }
-
- this.toastrService.success(`${message}`, `${action === ActionType.READ ? 'CONNEXION' : ''}`, {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- });
- }
}
diff --git a/src/app/shared/features/my-profile-map/my-profile-map.component.ts b/src/app/shared/features/my-profile-map/my-profile-map.component.ts
index a49c5b1..60bb285 100644
--- a/src/app/shared/features/my-profile-map/my-profile-map.component.ts
+++ b/src/app/shared/features/my-profile-map/my-profile-map.component.ts
@@ -5,7 +5,6 @@ import { LocationFacade } from '@app/ui/location/location.facade';
import { ProfileFacade } from '@app/ui/profiles/profile.facade';
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
import { ActionType } from '@app/domain/action-type.util';
-import { FeedbackService } from '@app/ui/shared/services/feedback.service';
@Component({
selector: 'app-my-profile-map',
@@ -19,7 +18,6 @@ export class MyProfileMapComponent implements OnInit {
readonly profile = input();
private readonly locationFacade = inject(LocationFacade);
private readonly profileFacade = inject(ProfileFacade);
- private readonly feedbackService = inject(FeedbackService);
private readonly loading = this.profileFacade.loading;
private readonly error = this.profileFacade.error;
@@ -53,11 +51,6 @@ export class MyProfileMapComponent implements OnInit {
switch (this.loading().action) {
case ActionType.UPDATE:
if (!this.error().hasError) {
- this.feedbackService.notify(
- ActionType.UPDATE,
- 'Vos coordonnées géographique ont été enregistrés.',
- false
- );
}
break;
}
diff --git a/src/app/shared/features/pdf-viewer/pdf-viewer.component.ts b/src/app/shared/features/pdf-viewer/pdf-viewer.component.ts
index c44240b..a65f230 100644
--- a/src/app/shared/features/pdf-viewer/pdf-viewer.component.ts
+++ b/src/app/shared/features/pdf-viewer/pdf-viewer.component.ts
@@ -20,6 +20,6 @@ export class PdfViewerComponent {
return null;
}
- return `${environment.baseUrl}/api/files/profiles/${currentProfile.id}/${currentProfile.cv}`;
+ return currentProfile.cv;
});
}
diff --git a/src/app/shared/features/register/register.component.ts b/src/app/shared/features/register/register.component.ts
index 58714ab..dd498aa 100644
--- a/src/app/shared/features/register/register.component.ts
+++ b/src/app/shared/features/register/register.component.ts
@@ -3,7 +3,6 @@ import { Router, RouterLink } from '@angular/router';
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
import { RegisterDto } from '@app/domain/authentification/dto/register-dto';
import { UntilDestroy } from '@ngneat/until-destroy';
-import { ToastrService } from 'ngx-toastr';
import { ProgressBarModule } from 'primeng/progressbar';
import { ActionType } from '@app/domain/action-type.util';
import { AuthFacade } from '@app/ui/authentification/auth.facade';
@@ -19,14 +18,12 @@ import { BtnLoadingComponent } from '@app/shared/components/btn-loading/btn-load
})
@UntilDestroy()
export class RegisterComponent {
- private readonly toastrService = inject(ToastrService);
-
private readonly formBuilder = inject(FormBuilder);
private readonly router = inject(Router);
protected registerForm = this.formBuilder.group({
email: new FormControl('', [Validators.required, Validators.email]),
- password: new FormControl('', Validators.required),
- passwordConfirm: new FormControl('', Validators.required),
+ password: new FormControl('', [Validators.required, Validators.minLength(8)]),
+ passwordConfirm: new FormControl('', [Validators.required, Validators.minLength(8)]),
});
formSubmitted = output();
@@ -37,7 +34,6 @@ export class RegisterComponent {
protected readonly isVerificationEmailSent = this.authFacade.isVerificationEmailSent;
constructor() {
- let message = '';
effect(() => {
switch (this.authLoading().action) {
case ActionType.CREATE:
@@ -46,10 +42,11 @@ export class RegisterComponent {
!this.authError().hasError &&
this.isVerificationEmailSent()
) {
- this.router.navigate(['/auth']).then(() => {
- message = `Votre compte a bien été crée avec succès !\n Un mail vous a été envoyé à l'adresse ${this.registerForm.getRawValue().email!} pour confirmer votre inscription.`;
- this.customToast(ActionType.CREATE, message);
- });
+ if (this.authError().hasError) {
+ this.registerForm.enable();
+ return;
+ }
+ this.router.navigate(['/auth']).then(() => {});
}
break;
}
@@ -65,12 +62,6 @@ export class RegisterComponent {
this.registerForm.get('password')?.value !== this.registerForm.get('passwordConfirm')?.value
) {
this.registerForm.get('passwordConfirm')?.setErrors({ passwordMismatch: true });
-
- this.toastrService.info(`Les mots de passe ne correspondent pas.`, `Information`, {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- });
return;
}
@@ -81,30 +72,4 @@ export class RegisterComponent {
this.authFacade.register(data);
}
-
- private customToast(action: ActionType, message: string): void {
- if (this.authError().hasError) {
- this.toastrService.error(
- `Une erreur s'est produite, veuillez réessayer ulterieurement`,
- `Erreur`,
- {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- }
- );
- return;
- }
-
- this.toastrService.success(
- `${message}`,
- `${action === ActionType.CREATE ? 'INSCRIPTION' : ''}`,
- {
- closeButton: true,
- progressAnimation: 'decreasing',
- progressBar: true,
- timeOut: 9000,
- }
- );
- }
}
diff --git a/src/app/shared/features/settings/settings.component.spec.ts b/src/app/shared/features/settings/settings.component.spec.ts
index 659f5a2..0be5657 100644
--- a/src/app/shared/features/settings/settings.component.spec.ts
+++ b/src/app/shared/features/settings/settings.component.spec.ts
@@ -10,6 +10,7 @@ import { mockSettingRepo } from '@app/testing/setting.mock';
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
import { ProfileRepository } from '@app/domain/profiles/profile.repository';
import { mockProfileRepo } from '@app/testing/profile.mock';
+import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
describe('SettingsComponent', () => {
let component: SettingsComponent;
@@ -32,6 +33,7 @@ describe('SettingsComponent', () => {
fixture = TestBed.createComponent(SettingsComponent);
component = fixture.componentInstance;
+ fixture.componentRef.setInput('profile', {} as ProfileViewModel);
fixture.detectChanges();
});
diff --git a/src/app/shared/features/settings/settings.component.ts b/src/app/shared/features/settings/settings.component.ts
index 962e61f..f7e1176 100644
--- a/src/app/shared/features/settings/settings.component.ts
+++ b/src/app/shared/features/settings/settings.component.ts
@@ -2,7 +2,6 @@ import { Component, effect, inject, input, OnInit } from '@angular/core';
import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
import { ThemeType } from '@app/domain/settings/setting.model';
import { SettingsFacade } from '@app/ui/settings/settings.facade';
-import { FeedbackService } from '@app/ui/shared/services/feedback.service';
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
import { ProfileFacade } from '@app/ui/profiles/profile.facade';
import { ActionType } from '@app/domain/action-type.util';
@@ -19,11 +18,10 @@ export class SettingsComponent implements OnInit {
protected readonly ThemeType = ThemeType;
private readonly settingsFacade = inject(SettingsFacade);
private readonly fb: FormBuilder = inject(FormBuilder);
- private readonly feedbackService = inject(FeedbackService);
private readonly profileFacade = inject(ProfileFacade);
private readonly loading = this.profileFacade.loading;
private readonly error = this.profileFacade.error;
- profile = input();
+ profile = input.required();
settings = this.settingsFacade.settings;
@@ -53,11 +51,6 @@ export class SettingsComponent implements OnInit {
case ActionType.UPDATE:
if (!this.error().hasError && this.loading().isDone) {
this.updateForm(userSettings);
- this.feedbackService.notify(
- ActionType.UPDATE,
- 'Vos paramètres ont été enregistrés.',
- false
- );
}
break;
}
diff --git a/src/app/testing/feedback.service.mock.ts b/src/app/testing/feedback.service.mock.ts
new file mode 100644
index 0000000..70d2796
--- /dev/null
+++ b/src/app/testing/feedback.service.mock.ts
@@ -0,0 +1,3 @@
+export const mockFeedbackService = {
+ notify: jest.fn(),
+};
diff --git a/src/app/testing/search.service.mock.ts b/src/app/testing/search.service.mock.ts
new file mode 100644
index 0000000..cc7dc73
--- /dev/null
+++ b/src/app/testing/search.service.mock.ts
@@ -0,0 +1,9 @@
+import { signal } from '@angular/core';
+
+const initialFilters = { page: 1, perPage: 10, totalItems: 0, totalPages: 0, search: '' };
+
+export const mockSearchService = {
+ getFilters: jest.fn(() => signal(initialFilters)),
+ setFilters: jest.fn(),
+ get: jest.fn(),
+};
diff --git a/src/app/testing/ui/profiles/profile.facade.spec.ts b/src/app/testing/ui/profiles/profile.facade.spec.ts
index bae3dc4..63a1a89 100644
--- a/src/app/testing/ui/profiles/profile.facade.spec.ts
+++ b/src/app/testing/ui/profiles/profile.facade.spec.ts
@@ -5,6 +5,12 @@ import { FakeProfileRepository } from '@app/testing/domain/profiles/fake-profile
import { mockProfiles } from '@app/testing/profile.mock';
import { Profile } from '@app/domain/profiles/profile.model';
import { ProfileDTO } from '@app/domain/profiles/dto/create-profile.dto';
+import { ToastrService } from 'ngx-toastr';
+import { mockToastR } from '@app/testing/toastr.mock';
+import { SearchService } from '@app/infrastructure/search/search.service';
+import { mockSearchService } from '@app/testing/search.service.mock';
+import { FeedbackService } from '@app/ui/shared/services/feedback.service';
+import { mockFeedbackService } from '@app/testing/feedback.service.mock';
describe('ProfileFacade', () => {
let facade: ProfileFacade;
@@ -14,6 +20,9 @@ describe('ProfileFacade', () => {
providers: [
ProfileFacade,
{ provide: PROFILE_REPOSITORY_TOKEN, useClass: FakeProfileRepository },
+ { provide: SearchService, useValue: mockSearchService },
+ { provide: FeedbackService, useValue: mockFeedbackService },
+ { provide: ToastrService, useValue: mockToastR },
],
});
diff --git a/src/app/testing/ui/profiles/profile.presenter.spec.ts b/src/app/testing/ui/profiles/profile.presenter.spec.ts
index c1c4e46..16e6cb8 100644
--- a/src/app/testing/ui/profiles/profile.presenter.spec.ts
+++ b/src/app/testing/ui/profiles/profile.presenter.spec.ts
@@ -24,7 +24,7 @@ describe('ProfilePresenter', () => {
avatarUrl: '',
apropos: 'Développeur Angular & Node.js',
bio: 'Passionné de code.',
- cv: 'cv.pdf',
+ cv: 'http://localhost:8090/api/files/profiles/1/cv.pdf',
isProfileVisible: true,
missingFields: [],
projets: ['p1', 'p2'],
diff --git a/src/app/testing/ui/projects/project.facade.spec.ts b/src/app/testing/ui/projects/project.facade.spec.ts
index 01a8a84..49ae84a 100644
--- a/src/app/testing/ui/projects/project.facade.spec.ts
+++ b/src/app/testing/ui/projects/project.facade.spec.ts
@@ -4,6 +4,8 @@ import { FakeProjectRepository } from '@app/testing/domain/projects/fake-project
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
import { fakeProjects } from '@app/testing/project.mock';
import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
+import { ToastrService } from 'ngx-toastr';
+import { mockToastR } from '@app/testing/toastr.mock';
describe('ProjectFacade', () => {
let facade: ProjectFacade;
@@ -13,6 +15,7 @@ describe('ProjectFacade', () => {
providers: [
ProjectFacade,
{ provide: PROJECT_REPOSITORY_TOKEN, useClass: FakeProjectRepository },
+ { provide: ToastrService, useValue: mockToastR },
],
});
diff --git a/src/app/testing/ui/sectors/sector.facade.spec.ts b/src/app/testing/ui/sectors/sector.facade.spec.ts
index 0f5dc46..87d88a4 100644
--- a/src/app/testing/ui/sectors/sector.facade.spec.ts
+++ b/src/app/testing/ui/sectors/sector.facade.spec.ts
@@ -3,6 +3,8 @@ import { SectorFacade } from '@app/ui/sectors/sector.facade';
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
import { FakeSectorRepository } from '@app/testing/domain/sectors/fake-sector.repository';
import { fakeSectors } from '@app/testing/sector.mock';
+import { ToastrService } from 'ngx-toastr';
+import { mockToastR } from '@app/testing/toastr.mock';
describe('SectorFacade', () => {
let sectorFacade: SectorFacade;
@@ -12,6 +14,7 @@ describe('SectorFacade', () => {
providers: [
SectorFacade,
{ provide: SECTOR_REPOSITORY_TOKEN, useClass: FakeSectorRepository },
+ { provide: ToastrService, useValue: mockToastR },
],
});
diff --git a/src/app/testing/ui/users/user.facade.spec.ts b/src/app/testing/ui/users/user.facade.spec.ts
index 3160127..b73c57b 100644
--- a/src/app/testing/ui/users/user.facade.spec.ts
+++ b/src/app/testing/ui/users/user.facade.spec.ts
@@ -2,12 +2,18 @@ import { UserFacade } from '@app/ui/users/user.facade';
import { TestBed } from '@angular/core/testing';
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
import { FakeUserRepository } from '@app/testing/domain/users/fake-user.repository';
+import { ToastrService } from 'ngx-toastr';
+import { mockToastR } from '@app/testing/toastr.mock';
describe('UserFacade', () => {
let facade: UserFacade;
beforeEach(() => {
TestBed.configureTestingModule({
- providers: [UserFacade, { provide: USER_REPOSITORY_TOKEN, useClass: FakeUserRepository }],
+ providers: [
+ UserFacade,
+ { provide: USER_REPOSITORY_TOKEN, useClass: FakeUserRepository },
+ { provide: ToastrService, useValue: mockToastR },
+ ],
});
facade = TestBed.inject(UserFacade);
});
diff --git a/src/app/ui/authentification/auth.facade.ts b/src/app/ui/authentification/auth.facade.ts
index 1a06795..fce4e6e 100644
--- a/src/app/ui/authentification/auth.facade.ts
+++ b/src/app/ui/authentification/auth.facade.ts
@@ -17,9 +17,12 @@ import { VerifyAuthenticatedUsecase } from '@app/usecase/authentification/verify
import { VerifyEmailUseCase } from '@app/usecase/authentification/verify-email.usecase';
import { GetCurrentUserUseCase } from '@app/usecase/authentification/get-current-user.usecase';
import { SendRequestPasswordResetUsecase } from '@app/usecase/authentification/send-request-password-reset.usecase';
+import { FeedbackService } from '@app/ui/shared/services/feedback.service';
+import { first } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class AuthFacade {
+ private readonly feedbackService = inject(FeedbackService);
private readonly authRepository = inject(AUTH_REPOSITORY_TOKEN);
private readonly profileFacade = new ProfileFacade();
@@ -55,32 +58,54 @@ export class AuthFacade {
login(loginDto: LoginDto) {
this.handleError(ActionType.READ, false, null, true);
+ let message = '';
- this.loginUseCase.execute(loginDto).subscribe({
- next: async (res: AuthResponse) => {
- this.authResponse.set(res);
- this.getCurrentUser();
- this.handleError(ActionType.READ, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.READ, true, err.message, false);
- },
- });
+ this.loginUseCase
+ .execute(loginDto)
+ .pipe(first())
+ .subscribe({
+ next: async (res: AuthResponse) => {
+ this.authResponse.set(res);
+ this.getCurrentUser();
+ this.handleError(ActionType.READ, false, null, false);
+
+ if (!this.authResponse()!.isValid && !this.authResponse()?.record.verified) {
+ message = `Vous ne pouvez pas vous connecter sans valider la verification envoyé à cet adresse ${this.authResponse()?.record.email!}`;
+ this.feedbackService.notify(ActionType.READ, `${message}`);
+ return;
+ }
+
+ message = `Bienvenue parmi nous ${loginDto.email}!`;
+ this.feedbackService.notify(ActionType.READ, `${message}`);
+ },
+ error: (err) => {
+ this.handleError(ActionType.READ, true, err.message, false);
+ },
+ });
}
register(registerDto: RegisterDto) {
this.handleError(ActionType.CREATE, false, null, true);
- this.registerUseCase.execute(registerDto).subscribe({
- next: (user) => {
- this.getCurrentUser();
- this.sendVerificationEmail(registerDto.email);
- this.createDefaultProfile(user.id);
- this.handleError(ActionType.CREATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.CREATE, true, err.message, false);
- },
- });
+ let message = '';
+
+ this.registerUseCase
+ .execute(registerDto)
+ .pipe(first())
+ .subscribe({
+ next: (user) => {
+ this.getCurrentUser();
+ this.sendVerificationEmail(registerDto.email);
+ this.createDefaultProfile(user.id);
+
+ message = `Votre compte a bien été crée avec succès !\n Un mail vous a été envoyé à l'adresse ${registerDto.email} pour confirmer votre inscription.`;
+ this.feedbackService.notify(ActionType.CREATE, `${message}`);
+
+ this.handleError(ActionType.CREATE, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.CREATE, true, err.message, false);
+ },
+ });
}
logout() {
@@ -103,28 +128,38 @@ export class AuthFacade {
sendRequestPasswordReset(email: string) {
this.handleError(ActionType.CREATE, false, null, true);
- this.senRequestPasswordResetUseCase.execute(email).subscribe({
- next: (res) => {
- this.isRequestPasswordSent.set(res);
- this.handleError(ActionType.CREATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.CREATE, true, err.message, false);
- },
- });
+
+ let message = '';
+ this.senRequestPasswordResetUseCase
+ .execute(email)
+ .pipe(first())
+ .subscribe({
+ next: (res) => {
+ this.isRequestPasswordSent.set(res);
+ this.handleError(ActionType.CREATE, false, null, false);
+ message = `Un mail de réinitialisation vous a été envoyé à cette adresse mail : ${email}`;
+ this.feedbackService.notify(ActionType.CREATE, `${message}`);
+ },
+ error: (err) => {
+ this.handleError(ActionType.CREATE, true, err.message, false);
+ },
+ });
}
private sendVerificationEmail(email: string) {
this.handleError(ActionType.CREATE, false, null, true);
- this.sendVerificationEmailUseCase.execute(email).subscribe({
- next: (res) => {
- this.isVerificationEmailSent.set(res);
- this.handleError(ActionType.CREATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.CREATE, true, err.message, false);
- },
- });
+ this.sendVerificationEmailUseCase
+ .execute(email)
+ .pipe(first())
+ .subscribe({
+ next: (res) => {
+ this.isVerificationEmailSent.set(res);
+ this.handleError(ActionType.CREATE, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.CREATE, true, err.message, false);
+ },
+ });
}
private createDefaultProfile(userId: string) {
@@ -153,5 +188,8 @@ export class AuthFacade {
) {
this.error.set({ action, hasError, message });
this.loading.set({ action, isLoading });
+ if (hasError) {
+ this.feedbackService.notify(ActionType.READ, message!, true);
+ }
}
}
diff --git a/src/app/ui/profiles/profile.facade.ts b/src/app/ui/profiles/profile.facade.ts
index 2f787f9..73e5788 100644
--- a/src/app/ui/profiles/profile.facade.ts
+++ b/src/app/ui/profiles/profile.facade.ts
@@ -19,12 +19,15 @@ import { SearchService } from '@app/infrastructure/search/search.service';
import { UpdateCoordinateProfileUseCase } from '@app/usecase/profiles/update-coordinate-profile.usecase';
import { SettingsProfileDto } from '@app/domain/profiles/dto/settings-profile.dto';
import { UpdateSettingsProfileUseCase } from '@app/usecase/profiles/update-settings-profile.usecase';
+import { FeedbackService } from '@app/ui/shared/services/feedback.service';
+import { first, Subscription } from 'rxjs';
@Injectable({
providedIn: 'root',
})
export class ProfileFacade {
- private profileRepository = inject(PROFILE_REPOSITORY_TOKEN);
+ private readonly feedbackService = inject(FeedbackService);
+ private readonly profileRepository = inject(PROFILE_REPOSITORY_TOKEN);
private readonly searchService = inject(SearchService);
private readonly profilePresenter = new ProfilePresenter();
@@ -50,14 +53,21 @@ export class ProfileFacade {
message: null,
});
+ private searchSubscription: Subscription | null = null;
+
load(search?: SearchFilters) {
+ if (this.searchSubscription) {
+ this.searchSubscription.unsubscribe();
+ this.searchSubscription = null;
+ }
+
this.handleError(ActionType.READ, false, null, true);
if (search === undefined || search === null) {
search = this.searchFilters();
}
- this.listUseCase.execute(search).subscribe({
+ this.searchSubscription = this.listUseCase.execute(search).subscribe({
next: (profilePaginated: ProfilePaginated) => {
const filters = {
...this.searchFilters(),
@@ -76,93 +86,124 @@ export class ProfileFacade {
this.profilePaginated.set(profileViewModelPaginated);
this.profiles.set(profileViewModelPaginated.items);
this.handleError(ActionType.READ, false, null, false);
+
+ this.searchSubscription = null;
},
error: (err) => {
this.handleError(ActionType.READ, false, err, false);
+ this.searchSubscription = null;
},
});
}
loadOne(profileId: string) {
this.handleError(ActionType.READ, false, null, true);
- this.getUseCase.execute(profileId).subscribe({
- next: (profile: Profile) => {
- this.profile.set(this.profilePresenter.toViewModel(profile));
- this.handleError(ActionType.READ, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.READ, false, err, false);
- },
- });
+ this.getUseCase
+ .execute(profileId)
+ .pipe(first())
+ .subscribe({
+ next: (profile: Profile) => {
+ this.profile.set(this.profilePresenter.toViewModel(profile));
+ this.handleError(ActionType.READ, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.READ, false, err, false);
+ },
+ });
}
loadOneByUserId(userId: string) {
this.handleError(ActionType.READ, false, null, true);
- this.getUseCase.executeByUserId(userId).subscribe({
- next: (profile: Profile) => {
- this.profile.set(this.profilePresenter.toViewModel(profile));
- this.handleError(ActionType.READ, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.READ, false, err, false);
- },
- });
+ this.getUseCase
+ .executeByUserId(userId)
+ .pipe(first())
+ .subscribe({
+ next: (profile: Profile) => {
+ this.profile.set(this.profilePresenter.toViewModel(profile));
+ this.handleError(ActionType.READ, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.READ, false, err, false);
+ },
+ });
}
create(profileDto: ProfileDTO) {
this.handleError(ActionType.CREATE, false, null, true);
+ let message = null;
- this.createUseCase.execute(profileDto).subscribe({
- next: (profile: Profile) => {
- this.profile.set(this.profilePresenter.toViewModel(profile));
- this.handleError(ActionType.CREATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.CREATE, false, err, false);
- },
- });
+ this.createUseCase
+ .execute(profileDto)
+ .pipe(first())
+ .subscribe({
+ next: (profile: Profile) => {
+ this.profile.set(this.profilePresenter.toViewModel(profile));
+ this.handleError(ActionType.CREATE, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.CREATE, false, err, false);
+ },
+ });
}
update(profileId: string, profile: Partial) {
this.handleError(ActionType.UPDATE, false, null, true);
- this.updateUseCase.execute(profileId, profile).subscribe({
- next: (profile: Profile) => {
- this.profile.set(this.profilePresenter.toViewModel(profile));
- this.handleError(ActionType.UPDATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.UPDATE, false, err, false);
- },
- });
+ this.updateUseCase
+ .execute(profileId, profile)
+ .pipe(first())
+ .subscribe({
+ next: (profile: Profile) => {
+ this.profile.set(this.profilePresenter.toViewModel(profile));
+ this.handleError(ActionType.UPDATE, false, null, false);
+
+ const message = `Vos informations personnelles ont bien été modifier !`;
+ this.feedbackService.notify(ActionType.UPDATE, message);
+ },
+ error: (err) => {
+ this.handleError(ActionType.UPDATE, false, err, false);
+ },
+ });
}
updateCoordinate(profileId: string, latitude: number, longitude: number) {
this.handleError(ActionType.UPDATE, false, null, true);
- this.updateCoordinateUseCase.execute(profileId, latitude, longitude).subscribe({
- next: (profile: Profile) => {
- this.profile.set(this.profilePresenter.toViewModel(profile));
- this.handleError(ActionType.UPDATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.UPDATE, false, err, false);
- },
- });
+ this.updateCoordinateUseCase
+ .execute(profileId, latitude, longitude)
+ .pipe(first())
+ .subscribe({
+ next: (profile: Profile) => {
+ this.profile.set(this.profilePresenter.toViewModel(profile));
+ this.handleError(ActionType.UPDATE, false, null, false);
+
+ const message = `Vos coordonnées géographique ont été enregistrés.`;
+ this.feedbackService.notify(ActionType.UPDATE, message);
+ },
+ error: (err) => {
+ this.handleError(ActionType.UPDATE, false, err, false);
+ },
+ });
}
updateSettings(profileId: string, settings: SettingsProfileDto) {
this.handleError(ActionType.UPDATE, false, null, true);
- this.updateSettingsUseCase.execute(profileId, settings).subscribe({
- next: (profile: Profile) => {
- this.profile.set(this.profilePresenter.toViewModel(profile));
- this.handleError(ActionType.UPDATE, false, null, false, true);
- },
- error: (err) => {
- this.handleError(ActionType.UPDATE, false, err, false);
- },
- });
+ this.updateSettingsUseCase
+ .execute(profileId, settings)
+ .pipe(first())
+ .subscribe({
+ next: (profile: Profile) => {
+ this.profile.set(this.profilePresenter.toViewModel(profile));
+ this.handleError(ActionType.UPDATE, false, null, false, true);
+
+ const message = `Vos paramètres ont été enregistrés.`;
+ this.feedbackService.notify(ActionType.UPDATE, message);
+ },
+ error: (err) => {
+ this.handleError(ActionType.UPDATE, false, err, false);
+ },
+ });
}
private handleError(
@@ -174,5 +215,8 @@ export class ProfileFacade {
) {
this.error.set({ action, hasError, message });
this.loading.set({ action, isLoading, isDone });
+ if (hasError) {
+ this.feedbackService.notify(ActionType.READ, message!, true);
+ }
}
}
diff --git a/src/app/ui/profiles/profile.presenter.ts b/src/app/ui/profiles/profile.presenter.ts
index f7fcab9..44cec18 100644
--- a/src/app/ui/profiles/profile.presenter.ts
+++ b/src/app/ui/profiles/profile.presenter.ts
@@ -38,7 +38,7 @@ export class ProfilePresenter {
reseaux: profile.reseaux,
apropos: profile.apropos,
projets: profile.projets,
- cv: profile.cv,
+ cv: `${environment.baseUrl}/api/files/profiles/${profile.id}/${profile.cv}`,
bio: profile.bio,
coordonnees: profile.coordonnees
? { latitude: profile!.coordonnees!.lat!, longitude: profile!.coordonnees!.lon! }
@@ -78,7 +78,9 @@ export class ProfilePresenter {
return false;
}
- const hasProfession = !!currentProfile.profession;
+ const hasProfession = currentProfile.profession
+ ? currentProfile.profession.toLowerCase() !== 'profession non renseignée'
+ : false;
const hasSector = !!currentProfile.secteur;
return hasProfession && hasSector;
@@ -87,7 +89,10 @@ export class ProfilePresenter {
private missingFields(currentProfile: Profile) {
const missing: string[] = [];
- if (!currentProfile?.profession) {
+ if (
+ !currentProfile?.profession ||
+ currentProfile.profession.toLowerCase() === 'profession non renseignée'
+ ) {
missing.push('profession');
}
if (!currentProfile?.secteur) {
diff --git a/src/app/ui/projects/project.facade.ts b/src/app/ui/projects/project.facade.ts
index da0daaa..943e59e 100644
--- a/src/app/ui/projects/project.facade.ts
+++ b/src/app/ui/projects/project.facade.ts
@@ -11,11 +11,14 @@ import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
import { ErrorResponse } from '@app/domain/error-response.util';
import { ActionType } from '@app/domain/action-type.util';
import { LoaderAction } from '@app/domain/loader-action.util';
+import { first, Subscription } from 'rxjs';
+import { FeedbackService } from '@app/ui/shared/services/feedback.service';
@Injectable({
providedIn: 'root',
})
export class ProjectFacade {
+ private readonly feedbackService = inject(FeedbackService);
private readonly projectRepo = inject(PROJECT_REPOSITORY_TOKEN);
private readonly createUseCase = new CreateProjectUseCase(this.projectRepo);
@@ -33,17 +36,26 @@ export class ProjectFacade {
});
private readonly projectPresenter = new ProjectPresenter();
+ private projectSubscription: Subscription | null = null;
load(userId: string) {
+ if (this.projectSubscription) {
+ this.projectSubscription.unsubscribe();
+ this.projectSubscription = null;
+ }
+
this.handleError(ActionType.READ, false, null, true);
- this.listUseCase.execute(userId).subscribe({
+ this.projectSubscription = this.listUseCase.execute(userId).subscribe({
next: (projects: Project[]) => {
this.projects.set(this.projectPresenter.toViewModels(projects));
this.handleError(ActionType.READ, false, null, false);
+
+ this.projectSubscription = null;
},
error: (err) => {
this.handleError(ActionType.READ, false, err, false);
+ this.projectSubscription = null;
},
});
}
@@ -51,44 +63,58 @@ export class ProjectFacade {
loadOne(projectId: string) {
this.handleError(ActionType.READ, false, null, true);
- this.getUseCase.execute(projectId).subscribe({
- next: (project: Project) => {
- this.project.set(this.projectPresenter.toViewModel(project));
- this.handleError(ActionType.READ, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.READ, false, err, false);
- },
- });
+ this.getUseCase
+ .execute(projectId)
+ .pipe(first())
+ .subscribe({
+ next: (project: Project) => {
+ this.project.set(this.projectPresenter.toViewModel(project));
+ this.handleError(ActionType.READ, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.READ, false, err, false);
+ },
+ });
}
create(projectDto: CreateProjectDto) {
this.handleError(ActionType.CREATE, false, null, true);
- this.createUseCase.execute(projectDto).subscribe({
- next: (project: Project) => {
- this.project.set(this.projectPresenter.toViewModel(project));
- this.projects.update((prev) => [...prev, this.projectPresenter.toViewModel(project)]);
- this.handleError(ActionType.CREATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.CREATE, false, err, false);
- },
- });
+ this.createUseCase
+ .execute(projectDto)
+ .pipe(first())
+ .subscribe({
+ next: (project: Project) => {
+ this.project.set(this.projectPresenter.toViewModel(project));
+ this.projects.update((prev) => [...prev, this.projectPresenter.toViewModel(project)]);
+ this.handleError(ActionType.CREATE, false, null, false);
+
+ const message = `Le projet ${project.nom} a bien été créer !`;
+ this.feedbackService.notify(ActionType.UPDATE, message);
+ },
+ error: (err) => {
+ this.handleError(ActionType.CREATE, false, err, false);
+ },
+ });
}
update(userId: string, data: any) {
this.handleError(ActionType.UPDATE, false, null, true);
- this.UpdateUseCase.execute(userId, data).subscribe({
- next: (project: Project) => {
- this.project.set(this.projectPresenter.toViewModel(project));
- this.handleError(ActionType.UPDATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.UPDATE, false, err, false);
- },
- });
+ this.UpdateUseCase.execute(userId, data)
+ .pipe(first())
+ .subscribe({
+ next: (project: Project) => {
+ this.project.set(this.projectPresenter.toViewModel(project));
+ this.handleError(ActionType.UPDATE, false, null, false);
+
+ const message = `Les informations du projet ${project.nom} ont bien été modifier !`;
+ this.feedbackService.notify(ActionType.UPDATE, message);
+ },
+ error: (err) => {
+ this.handleError(ActionType.UPDATE, false, err, false);
+ },
+ });
}
private handleError(
@@ -99,5 +125,9 @@ export class ProjectFacade {
) {
this.error.set({ action, hasError, message });
this.loading.set({ action, isLoading });
+
+ if (hasError) {
+ this.feedbackService.notify(ActionType.READ, message!, true);
+ }
}
}
diff --git a/src/app/ui/sectors/sector.facade.ts b/src/app/ui/sectors/sector.facade.ts
index acb2101..7e48943 100644
--- a/src/app/ui/sectors/sector.facade.ts
+++ b/src/app/ui/sectors/sector.facade.ts
@@ -8,9 +8,12 @@ import { ErrorResponse } from '@app/domain/error-response.util';
import { SectorPresenterModel } from '@app/ui/sectors/sector.presenter.model';
import { Sector } from '@app/domain/sectors/sector.model';
import { SectorPresenter } from '@app/ui/sectors/sector.presenter';
+import { first, Subscription } from 'rxjs';
+import { FeedbackService } from '@app/ui/shared/services/feedback.service';
@Injectable()
export class SectorFacade {
+ private readonly feedbackService = inject(FeedbackService);
private readonly sectorRepo = inject(SECTOR_REPOSITORY_TOKEN);
private readonly listSectorUseCase = new ListSectorUsecase(this.sectorRepo);
@@ -27,31 +30,42 @@ export class SectorFacade {
});
private readonly sectorPresenter = new SectorPresenter();
+ private sectorSubscription: Subscription | null = null;
load() {
+ if (this.sectorSubscription) {
+ this.sectorSubscription.unsubscribe();
+ this.sectorSubscription = null;
+ }
+
this.handleError(ActionType.READ, false, null, true);
- this.listSectorUseCase.execute().subscribe({
+ this.sectorSubscription = this.listSectorUseCase.execute().subscribe({
next: (sectors: Sector[]) => {
this.sectors.set(this.sectorPresenter.toViewModels(sectors));
this.handleError(ActionType.READ, false, null, false);
+ this.sectorSubscription = null;
},
error: (err) => {
this.handleError(ActionType.READ, false, err, false);
+ this.sectorSubscription = null;
},
});
}
loadOne(sectorId: string) {
this.handleError(ActionType.READ, false, null, true);
- this.getSectorUseCase.execute(sectorId).subscribe({
- next: (sector: Sector) => {
- this.sector.set(this.sectorPresenter.toViewModel(sector));
- this.handleError(ActionType.READ, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.READ, false, err, false);
- },
- });
+ this.getSectorUseCase
+ .execute(sectorId)
+ .pipe(first())
+ .subscribe({
+ next: (sector: Sector) => {
+ this.sector.set(this.sectorPresenter.toViewModel(sector));
+ this.handleError(ActionType.READ, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.READ, false, err, false);
+ },
+ });
}
private handleError(
@@ -62,5 +76,9 @@ export class SectorFacade {
) {
this.error.set({ action, hasError, message });
this.loading.set({ action, isLoading });
+
+ if (hasError) {
+ this.feedbackService.notify(ActionType.READ, message!, true);
+ }
}
}
diff --git a/src/app/ui/shared/services/feedback.service.ts b/src/app/ui/shared/services/feedback.service.ts
index e0f704d..258ce6e 100644
--- a/src/app/ui/shared/services/feedback.service.ts
+++ b/src/app/ui/shared/services/feedback.service.ts
@@ -21,18 +21,19 @@ export class FeedbackService {
*/
notify(action: ActionType | null, message: string, hasError: boolean = false): void {
if (hasError) {
- this.handleError();
+ const userFriendlyMessage = this.handleErrorMessage(message);
+ this.handleError(userFriendlyMessage);
} else {
this.handleSuccess(action, message);
}
}
- private handleError(): void {
- this.toastr.error(
- `Une erreur s'est produite, veuillez réessayer ultérieurement`,
- `Erreur`,
- this.TOAST_CONFIG
- );
+ private handleError(message: string): void {
+ this.toastr.error(`${message}`, `Erreur`, {
+ ...this.TOAST_CONFIG,
+ disableTimeOut: true,
+ progressBar: false,
+ });
}
private handleSuccess(action: ActionType | null, message: string): void {
@@ -52,4 +53,30 @@ export class FeedbackService {
this.toastr.success(message, title, this.TOAST_CONFIG);
}
+
+ private handleErrorMessage(messageError: string): string {
+ const lowerMessage = messageError.toLowerCase();
+
+ // Mapping des erreurs PocketBase courantes vers messages utilisateurs
+ const errorMessages: Record = {
+ 'failed to authenticate': `Votre email n'est pas valide ou votre mot de passe est incorrect`,
+ 'failed to auth': `Échec de l'authentification. Vérifiez vos identifiants.`,
+ 'invalid email': `L'adresse email n'est pas valide.`,
+ 'invalid password': `Le mot de passe ne respecte pas les critères de sécurité.`,
+ 'passwords do not match': `Les mots de passe saisis ne correspondent pas.`,
+ 'record not found': `La ressource demandée n'existe pas.`,
+ 'validation error': `Données invalides. Vérifiez les champs saisis.`,
+ 'network error': `Problème de connexion. Vérifiez votre réseau.`,
+ };
+
+ // Recherche du premier match
+ for (const [errorKey, userMessage] of Object.entries(errorMessages)) {
+ if (lowerMessage.includes(errorKey)) {
+ return userMessage;
+ }
+ }
+
+ // Message par défaut
+ return `Une erreur s'est produite, veuillez réessayer ultérieurement`;
+ }
}
diff --git a/src/app/ui/users/user.facade.ts b/src/app/ui/users/user.facade.ts
index c168c55..56f7636 100644
--- a/src/app/ui/users/user.facade.ts
+++ b/src/app/ui/users/user.facade.ts
@@ -7,9 +7,12 @@ import { ActionType } from '@app/domain/action-type.util';
import { ErrorResponse } from '@app/domain/error-response.util';
import { UserViewModel } from '@app/ui/users/user.presenter.model';
import { UserPresenter } from '@app/ui/users/user.presenter';
+import { first } from 'rxjs';
+import { FeedbackService } from '@app/ui/shared/services/feedback.service';
@Injectable()
export class UserFacade {
+ private readonly feedbackService = inject(FeedbackService);
private readonly userRepository = inject(USER_REPOSITORY_TOKEN);
private readonly getUseCase = new GetUserUseCase(this.userRepository);
@@ -28,28 +31,37 @@ export class UserFacade {
loadOne(userId: string) {
this.handleError(ActionType.READ, false, null, true);
- this.getUseCase.execute(userId).subscribe({
- next: (user) => {
- this.user.set(this.userPresenter.toViewModel(user));
- this.handleError(ActionType.READ, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.READ, false, err, false);
- },
- });
+ this.getUseCase
+ .execute(userId)
+ .pipe(first())
+ .subscribe({
+ next: (user) => {
+ this.user.set(this.userPresenter.toViewModel(user));
+ this.handleError(ActionType.READ, false, null, false);
+ },
+ error: (err) => {
+ this.handleError(ActionType.READ, false, err, false);
+ },
+ });
}
update(userId: string, user: Partial) {
this.handleError(ActionType.UPDATE, false, null, true);
- this.updateUseCase.execute(userId, user).subscribe({
- next: (user) => {
- this.user.set(this.userPresenter.toViewModel(user));
- this.handleError(ActionType.UPDATE, false, null, false);
- },
- error: (err) => {
- this.handleError(ActionType.UPDATE, false, err, false);
- },
- });
+ this.updateUseCase
+ .execute(userId, user)
+ .pipe(first())
+ .subscribe({
+ next: (user) => {
+ this.user.set(this.userPresenter.toViewModel(user));
+ this.handleError(ActionType.UPDATE, false, null, false);
+
+ const message = `Votre profile a bien été modifier !`;
+ this.feedbackService.notify(ActionType.UPDATE, message);
+ },
+ error: (err) => {
+ this.handleError(ActionType.UPDATE, false, err, false);
+ },
+ });
}
private handleError(
@@ -60,5 +72,9 @@ export class UserFacade {
) {
this.error.set({ action, hasError, message });
this.loading.set({ action, isLoading });
+
+ if (hasError) {
+ this.feedbackService.notify(ActionType.READ, message!, true);
+ }
}
}