diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 277bfeb..8eb843a 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -10,14 +10,13 @@ describe('AppComponent', () => { let component: AppComponent; let fixture: ComponentFixture; - let mockAuthRepository: jest.Mocked; - let mockProfileRepo: jest.Mocked; + let mockAuthRepository: jest.Mocked>; + let mockProfileRepo: jest.Mocked>; beforeEach(async () => { mockAuthRepository = { get: jest.fn(), login: jest.fn(), - update: jest.fn(), sendVerificationEmail: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), diff --git a/src/app/core/guard/authentication/auth.guard.spec.ts b/src/app/core/guard/authentication/auth.guard.spec.ts index c213609..6756e63 100644 --- a/src/app/core/guard/authentication/auth.guard.spec.ts +++ b/src/app/core/guard/authentication/auth.guard.spec.ts @@ -10,8 +10,8 @@ import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-r describe('authGuard', () => { let mockRouter: Partial; - let mockAuthRepository: jest.Mocked; - let mockProfileRepo: jest.Mocked; + let mockAuthRepository: jest.Mocked>; + let mockProfileRepo: jest.Mocked>; const executeGuard: CanActivateFn = (...guardParameters) => TestBed.runInInjectionContext(() => authGuard(...guardParameters)); @@ -24,7 +24,6 @@ describe('authGuard', () => { mockAuthRepository = { get: jest.fn(), login: jest.fn(), - update: jest.fn(), sendVerificationEmail: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), diff --git a/src/app/routes/my-profile/my-profile.component.html b/src/app/routes/my-profile/my-profile.component.html index c8c663e..9f8d667 100644 --- a/src/app/routes/my-profile/my-profile.component.html +++ b/src/app/routes/my-profile/my-profile.component.html @@ -57,55 +57,61 @@ -
+
-
-
-
- @if (user().avatar) { - {{ user().username }} - } @else { - {{ user().username }} - } + @if (user() != undefined) { +
+
+
+ @if (user()!.avatar) { + {{ user()!.username }} + } @else { + {{ user()!.username }} + } +
-
+ } -
- @if (user().name) { -

- {{ user().name }} -

- } @else if (user().username) { -

- {{ user().username }} -

- } @else { -

- {{ user().email }} -

- } + @if (user() != undefined) { +
+ @if (user()!.name) { +

+ {{ user()!.name }} +

+ } @else if (user()!.username) { +

+ {{ user()!.username }} +

+ } @else { +

+ {{ user()!.email }} +

+ } -

- {{ profile().profession | uppercase }} -

-
+

+ {{ profile().profession | uppercase }} +

+
+ }
@@ -115,30 +121,32 @@
+ }
@switch (menu().toLowerCase()) { @case ('home') { - + @if (!userLoading().isLoading) { + + } @else { + + } } @case ('projects') { } @default { - + @if (!userLoading().isLoading) { + + } @else { + + } } }
diff --git a/src/app/routes/my-profile/my-profile.component.spec.ts b/src/app/routes/my-profile/my-profile.component.spec.ts index 6f85e2c..72a48a5 100644 --- a/src/app/routes/my-profile/my-profile.component.spec.ts +++ b/src/app/routes/my-profile/my-profile.component.spec.ts @@ -15,8 +15,8 @@ describe('MyProfileComponent', () => { let fixture: ComponentFixture; let mockProfileRepo: ProfileRepository; - let mockUserRepo: UserRepository; let mockToastrService: Partial; + let mockUserRepo: Partial; beforeEach(async () => { mockProfileRepo = { diff --git a/src/app/routes/my-profile/my-profile.component.ts b/src/app/routes/my-profile/my-profile.component.ts index ce86df1..1e3f9f3 100644 --- a/src/app/routes/my-profile/my-profile.component.ts +++ b/src/app/routes/my-profile/my-profile.component.ts @@ -1,4 +1,4 @@ -import { Component, computed, inject, OnInit, signal } from '@angular/core'; +import { Component, inject, OnInit, signal } from '@angular/core'; import { ActivatedRoute, RouterOutlet } from '@angular/router'; import { User } from '@app/domain/users/user.model'; import { Location, UpperCasePipe } from '@angular/common'; @@ -11,6 +11,8 @@ import { MyProfileProjectListComponent } from '@app/shared/components/my-profile import { MyProfileUpdateFormComponent } from '@app/shared/components/my-profile-update-form/my-profile-update-form.component'; import { PdfViewerComponent } from '@app/shared/features/pdf-viewer/pdf-viewer.component'; import { ProfileFacade } from '@app/ui/profiles/profile.facade'; +import { UserFacade } from '@app/ui/users/user.facade'; +import { LoadingComponent } from '@app/shared/components/loading/loading.component'; @Component({ selector: 'app-my-profile', @@ -24,7 +26,9 @@ import { ProfileFacade } from '@app/ui/profiles/profile.facade'; MyProfileUpdateFormComponent, PdfViewerComponent, UpperCasePipe, + LoadingComponent, ], + providers: [UserFacade], templateUrl: './my-profile.component.html', styleUrl: './my-profile.component.scss', }) @@ -33,17 +37,14 @@ export class MyProfileComponent implements OnInit { protected readonly environment = environment; protected menu = signal('home'); - protected myProfileQrCode = `${environment.production}`; - protected location = inject(Location); protected readonly route = inject(ActivatedRoute); protected extraData: { user: User } = this.route.snapshot.data['user']; - protected user = computed(() => { - if (this.extraData != undefined) return this.extraData.user; - return {} as User; - }); + private readonly userFacade = inject(UserFacade); + protected user = this.userFacade.user; + protected readonly userLoading = this.userFacade.loading; private readonly profileFacade = new ProfileFacade(); protected profile = this.profileFacade.profile; @@ -51,7 +52,9 @@ export class MyProfileComponent implements OnInit { protected readonly error = this.profileFacade.error; ngOnInit(): void { - this.myProfileQrCode = `${this.myProfileQrCode}/profiles/${this.user().id}`; - this.profileFacade.loadOne(this.user().id); + if (this.extraData != undefined) { + this.profileFacade.loadOne(this.extraData.user.id); + this.userFacade.loadOne(this.extraData.user.id); + } } } diff --git a/src/app/routes/profile/profile-detail/profile-detail.component.html b/src/app/routes/profile/profile-detail/profile-detail.component.html index cf76607..03acc52 100644 --- a/src/app/routes/profile/profile-detail/profile-detail.component.html +++ b/src/app/routes/profile/profile-detail/profile-detail.component.html @@ -207,30 +207,32 @@
-
-

- - - - À propos -

-

- {{ profile().apropos }} -

-
+ + + + À propos + +

+ {{ profile().apropos }} +

+
+ }
{ let component: MyProfileUpdateProjectFormComponent; let fixture: ComponentFixture; - let mockToastrService: Partial; - let mockProjectRepository: jest.Mocked; + let mockToastrService: jest.Mocked>; + let mockProjectRepository: jest.Mocked>; - let mockAuthRepository: jest.Mocked; - let mockProfileRepo: jest.Mocked; + let mockAuthRepository: jest.Mocked>; + let mockProfileRepo: jest.Mocked>; beforeEach(async () => { mockToastrService = { @@ -38,7 +38,6 @@ describe('MyProfileUpdateProjectFormComponent', () => { mockAuthRepository = { get: jest.fn(), login: jest.fn(), - update: jest.fn(), sendVerificationEmail: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), diff --git a/src/app/shared/components/nav-bar/nav-bar.component.spec.ts b/src/app/shared/components/nav-bar/nav-bar.component.spec.ts index 6e9b195..6b305e8 100644 --- a/src/app/shared/components/nav-bar/nav-bar.component.spec.ts +++ b/src/app/shared/components/nav-bar/nav-bar.component.spec.ts @@ -14,10 +14,10 @@ import { ProfileRepository } from '@app/domain/profiles/profile.repository'; describe('NavBarComponent', () => { let component: NavBarComponent; let fixture: ComponentFixture; - let mockThemeService: Partial; + let mockThemeService: jest.Mocked>; - let mockAuthRepository: jest.Mocked; - let mockProfileRepo: jest.Mocked; + let mockAuthRepository: jest.Mocked>; + let mockProfileRepo: jest.Mocked>; const user: User = { id: 'adbc123', @@ -41,7 +41,6 @@ describe('NavBarComponent', () => { mockAuthRepository = { get: jest.fn(), login: jest.fn(), - update: jest.fn(), sendVerificationEmail: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), diff --git a/src/app/shared/components/reseaux/reseaux.component.ts b/src/app/shared/components/reseaux/reseaux.component.ts index cc58f60..33eb327 100644 --- a/src/app/shared/components/reseaux/reseaux.component.ts +++ b/src/app/shared/components/reseaux/reseaux.component.ts @@ -1,12 +1,11 @@ import { Component, inject, Input } from '@angular/core'; -import { JsonPipe } from '@angular/common'; import { ThemeService } from '@app/core/services/theme/theme.service'; import { UntilDestroy } from '@ngneat/until-destroy'; @Component({ selector: 'app-reseaux', standalone: true, - imports: [JsonPipe], + imports: [], templateUrl: './reseaux.component.html', styleUrl: './reseaux.component.scss', }) diff --git a/src/app/shared/components/user-avatar-form/user-avatar-form.component.html b/src/app/shared/components/user-avatar-form/user-avatar-form.component.html index a5f4004..9725a56 100644 --- a/src/app/shared/components/user-avatar-form/user-avatar-form.component.html +++ b/src/app/shared/components/user-avatar-form/user-avatar-form.component.html @@ -5,7 +5,7 @@ } - @if (user) { + @if (user != undefined) {
diff --git a/src/app/shared/components/user-avatar-form/user-avatar-form.component.ts b/src/app/shared/components/user-avatar-form/user-avatar-form.component.ts index c7d333d..50df5cd 100644 --- a/src/app/shared/components/user-avatar-form/user-avatar-form.component.ts +++ b/src/app/shared/components/user-avatar-form/user-avatar-form.component.ts @@ -1,4 +1,4 @@ -import { Component, effect, inject, Input, output, signal } from '@angular/core'; +import { Component, effect, inject, Input, signal } from '@angular/core'; import { User } from '@app/domain/users/user.model'; import { ReactiveFormsModule } from '@angular/forms'; import { environment } from '@env/environment'; @@ -7,21 +7,24 @@ import { ToastrService } from 'ngx-toastr'; import { UserFacade } from '@app/ui/users/user.facade'; import { ActionType } from '@app/domain/action-type.util'; import { LoadingComponent } from '@app/shared/components/loading/loading.component'; +import { UntilDestroy } from '@ngneat/until-destroy'; +import { UserViewModel } from '@app/ui/users/user.presenter.model'; @Component({ selector: 'app-user-avatar-form', standalone: true, + providers: [UserFacade], imports: [ReactiveFormsModule, NgClass, NgTemplateOutlet, LoadingComponent], templateUrl: './user-avatar-form.component.html', styleUrl: './user-avatar-form.component.scss', }) +@UntilDestroy() export class UserAvatarFormComponent { private readonly toastrService = inject(ToastrService); protected readonly environment = environment; private readonly facade = inject(UserFacade); - @Input({ required: true }) user: User | undefined = undefined; - onFormSubmitted = output(); + @Input({ required: true }) user: UserViewModel | undefined = undefined; file: File | null = null; // Variable to store file imagePreviewUrl: string | null = null; // URL for image preview @@ -34,13 +37,13 @@ export class UserAvatarFormComponent { let message = ''; effect(() => { - if (!this.loading().isLoading && this.onSubmitted()) { + if (!this.loading().isLoading) { switch (this.loading().action) { case ActionType.UPDATE: message = `Votre photo de profile a bien été modifier !`; - this.customToast(ActionType.UPDATE, message); - this.onSubmitted.set(false); - this.onFormSubmitted.emit(''); + if (this.onSubmitted()) { + this.customToast(ActionType.UPDATE, message); + } break; } } @@ -52,7 +55,7 @@ export class UserAvatarFormComponent { const formData = new FormData(); formData.append('avatar', this.file); // "avatar" est le nom du champ dans PocketBase - this.facade.update(this.user?.id!, formData as Partial); + this.facade.update(this.user!.id, formData as Partial); this.onSubmitted.set(true); } } diff --git a/src/app/shared/components/user-form/user-form.component.ts b/src/app/shared/components/user-form/user-form.component.ts index 05ca737..f2614b5 100644 --- a/src/app/shared/components/user-form/user-form.component.ts +++ b/src/app/shared/components/user-form/user-form.component.ts @@ -1,4 +1,4 @@ -import { Component, effect, inject, Input, OnInit, output, signal } from '@angular/core'; +import { Component, effect, inject, Input, OnInit, signal } from '@angular/core'; import { FormBuilder, FormControl, @@ -18,6 +18,7 @@ import { NgTemplateOutlet } from '@angular/common'; selector: 'app-user-form', standalone: true, imports: [ReactiveFormsModule, LoadingComponent, NgTemplateOutlet], + providers: [UserFacade], templateUrl: './user-form.component.html', styleUrl: './user-form.component.scss', }) @@ -27,12 +28,12 @@ export class UserFormComponent implements OnInit { private readonly facade = inject(UserFacade); protected readonly ActionType = ActionType; - @Input({ required: true }) user: User | undefined = undefined; - onFormSubmitted = output(); + @Input({ required: true }) userId: string | undefined = undefined; private fb = inject(FormBuilder); protected userForm!: FormGroup; + protected readonly user = this.facade.user; protected readonly loading = this.facade.loading; protected readonly error = this.facade.error; protected onSubmitted = signal(false); @@ -41,23 +42,32 @@ export class UserFormComponent implements OnInit { let message = ''; effect(() => { - if (!this.loading().isLoading && this.onSubmitted()) { + if (!this.loading().isLoading) { switch (this.loading().action) { case ActionType.UPDATE: message = `Vos informations personnelles ont bien été modifier !`; - this.customToast(ActionType.UPDATE, message); - this.onSubmitted.set(false); + if (this.onSubmitted()) { + this.customToast(ActionType.UPDATE, message); + } + break; + case ActionType.READ: + this.userForm.setValue({ + firstname: this.user().name.split(' ').slice(0, -1).join(' ') ?? '', + name: this.user().name.split(' ').slice(-1)[0] ?? '', + }); break; } } }); } ngOnInit(): void { + if (this.userId !== undefined) { + this.facade.loadOne(this.userId!); + } + this.userForm = this.fb.group({ - firstname: new FormControl(this.user?.name?.split(' ').slice(0, -1).join(' ') ?? '', [ - Validators.required, - ]), - name: new FormControl(this.user?.name?.split(' ').slice(-1)[0] ?? '', [Validators.required]), + firstname: new FormControl('', [Validators.required]), + name: new FormControl('', [Validators.required]), }); } @@ -72,9 +82,7 @@ export class UserFormComponent implements OnInit { name: this.userForm.getRawValue()!.firstname! + ' ' + this.userForm.getRawValue()!.name!, } as User; - this.facade.update(this.user?.id!, data); - - this.onFormSubmitted.emit(data); + this.facade.update(this.userId!, data); this.onSubmitted.set(true); } diff --git a/src/app/shared/components/vertical-profile-item/vertical-profile-item.component.ts b/src/app/shared/components/vertical-profile-item/vertical-profile-item.component.ts index 3f89955..a210637 100644 --- a/src/app/shared/components/vertical-profile-item/vertical-profile-item.component.ts +++ b/src/app/shared/components/vertical-profile-item/vertical-profile-item.component.ts @@ -10,6 +10,7 @@ import { UserFacade } from '@app/ui/users/user.facade'; @Component({ selector: 'app-vertical-profile-item', standalone: true, + providers: [UserFacade], imports: [ChipsComponent, ReseauxComponent, RouterLink], templateUrl: './vertical-profile-item.component.html', styleUrl: './vertical-profile-item.component.scss', diff --git a/src/app/shared/features/login/login.component.spec.ts b/src/app/shared/features/login/login.component.spec.ts index 56cd93b..79a6415 100644 --- a/src/app/shared/features/login/login.component.spec.ts +++ b/src/app/shared/features/login/login.component.spec.ts @@ -15,8 +15,8 @@ describe('LoginComponent', () => { // Mocks des services let mockToastrService: Partial; - let mockAuthRepository: jest.Mocked; - let mockProfileRepo: jest.Mocked; + let mockAuthRepository: jest.Mocked>; + let mockProfileRepo: jest.Mocked>; beforeEach(async () => { mockToastrService = { @@ -29,7 +29,6 @@ describe('LoginComponent', () => { mockAuthRepository = { get: jest.fn(), login: jest.fn(), - update: jest.fn(), sendVerificationEmail: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), diff --git a/src/app/shared/features/login/login.component.ts b/src/app/shared/features/login/login.component.ts index 5dceae9..5355eb3 100644 --- a/src/app/shared/features/login/login.component.ts +++ b/src/app/shared/features/login/login.component.ts @@ -43,10 +43,20 @@ export class LoginComponent { 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!`; + message = `Bienvenue parmi nous!`; this.customToast(ActionType.READ, message); }); } diff --git a/src/app/shared/features/register/register.component.spec.ts b/src/app/shared/features/register/register.component.spec.ts index f63678d..079160b 100644 --- a/src/app/shared/features/register/register.component.spec.ts +++ b/src/app/shared/features/register/register.component.spec.ts @@ -12,9 +12,9 @@ describe('RegisterComponent', () => { let component: RegisterComponent; let fixture: ComponentFixture; - let mockToastrService: Partial; - let mockProfileRepo: ProfileRepository; - let mockAuthRepository: jest.Mocked; + let mockToastrService: jest.Mocked>; + let mockProfileRepo: jest.Mocked>; + let mockAuthRepository: jest.Mocked>; beforeEach(async () => { mockProfileRepo = { @@ -33,7 +33,6 @@ describe('RegisterComponent', () => { mockAuthRepository = { get: jest.fn(), login: jest.fn(), - update: jest.fn(), sendVerificationEmail: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), diff --git a/src/app/shared/features/update-user/update-user.component.html b/src/app/shared/features/update-user/update-user.component.html index a4f03e6..548d469 100644 --- a/src/app/shared/features/update-user/update-user.component.html +++ b/src/app/shared/features/update-user/update-user.component.html @@ -28,7 +28,7 @@
- +
} diff --git a/src/app/shared/features/update-user/update-user.component.spec.ts b/src/app/shared/features/update-user/update-user.component.spec.ts index 779c075..5418c0e 100644 --- a/src/app/shared/features/update-user/update-user.component.spec.ts +++ b/src/app/shared/features/update-user/update-user.component.spec.ts @@ -1,14 +1,34 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { UpdateUserComponent } from './update-user.component'; +import { provideRouter } from '@angular/router'; +import { UserRepository } from '@app/domain/users/user.repository'; +import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token'; +import { ToastrService } from 'ngx-toastr'; describe('UpdateUserComponent', () => { let component: UpdateUserComponent; let fixture: ComponentFixture; + let mockUserRepo: jest.Mocked>; + let mockToastrService: jest.Mocked>; + beforeEach(async () => { + mockUserRepo = { + getUserById: jest.fn(), + }; + mockToastrService = { + warning: jest.fn(), + success: jest.fn(), + error: jest.fn(), + }; await TestBed.configureTestingModule({ imports: [UpdateUserComponent], + providers: [ + provideRouter([]), + { provide: USER_REPOSITORY_TOKEN, useValue: mockUserRepo }, + { provide: ToastrService, useValue: mockToastrService }, + ], }).compileComponents(); fixture = TestBed.createComponent(UpdateUserComponent); diff --git a/src/app/shared/features/update-user/update-user.component.ts b/src/app/shared/features/update-user/update-user.component.ts index 997e3ee..78fe44e 100644 --- a/src/app/shared/features/update-user/update-user.component.ts +++ b/src/app/shared/features/update-user/update-user.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; -import { User } from '@app/domain/users/user.model'; import { UserFormComponent } from '@app/shared/components/user-form/user-form.component'; import { UserAvatarFormComponent } from '@app/shared/components/user-avatar-form/user-avatar-form.component'; +import { UserViewModel } from '@app/ui/users/user.presenter.model'; @Component({ selector: 'app-update-user', @@ -11,5 +11,5 @@ import { UserAvatarFormComponent } from '@app/shared/components/user-avatar-form styleUrl: './update-user.component.scss', }) export class UpdateUserComponent { - @Input({ required: true }) user: User | undefined = undefined; + @Input({ required: true }) user: UserViewModel | undefined = undefined; } diff --git a/src/app/ui/profiles/profile.facade.ts b/src/app/ui/profiles/profile.facade.ts index dc7bd2a..f6abc21 100644 --- a/src/app/ui/profiles/profile.facade.ts +++ b/src/app/ui/profiles/profile.facade.ts @@ -48,7 +48,6 @@ export class ProfileFacade { loadOne(userId: string) { this.handleError(ActionType.READ, false, null, true); - this.getUseCase.execute(userId).subscribe({ next: (profile: Profile) => { this.profile.set(ProfilePresenter.toViewModel(profile)); diff --git a/src/app/ui/users/user.facade.ts b/src/app/ui/users/user.facade.ts index 8c8298e..c168c55 100644 --- a/src/app/ui/users/user.facade.ts +++ b/src/app/ui/users/user.facade.ts @@ -8,9 +8,7 @@ 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'; -@Injectable({ - providedIn: 'root', -}) +@Injectable() export class UserFacade { private readonly userRepository = inject(USER_REPOSITORY_TOKEN);