refacto : refactoring des classes facades
This commit is contained in:
@@ -18,7 +18,8 @@
|
||||
"builder": "@angular-devkit/build-angular:application",
|
||||
"options": {
|
||||
"allowedCommonJsDependencies": [
|
||||
"pocketbase"
|
||||
"pocketbase",
|
||||
"leaflet"
|
||||
],
|
||||
"outputPath": "dist/",
|
||||
"index": "src/index.html",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trouve-ton-profile",
|
||||
"version": "1.3.0",
|
||||
"version": "1.3.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "bash replace-prod-env.sh src/environments/environment.development.ts $ENV_URL && ng serve",
|
||||
|
||||
@@ -8,6 +8,8 @@ import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||
import { SettingRepository } from '@app/domain/settings/setting.repository';
|
||||
import { mockSettingRepo } from '@app/testing/setting.mock';
|
||||
import { SETTING_REPOSITORY_TOKEN } from '@app/infrastructure/settings/setting-repository.token';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
let component: AppComponent;
|
||||
@@ -41,6 +43,7 @@ describe('AppComponent', () => {
|
||||
imports: [AppComponent],
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
{ provide: AUTH_REPOSITORY_TOKEN, useValue: mockAuthRepository },
|
||||
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepo },
|
||||
{ provide: SETTING_REPOSITORY_TOKEN, useValue: mockSettingRepository },
|
||||
|
||||
@@ -348,7 +348,7 @@
|
||||
d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1115 0z"
|
||||
/>
|
||||
</svg>
|
||||
Localisation
|
||||
<span class="hidden sm:inline">Localisation</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
||||
@@ -95,9 +95,7 @@
|
||||
<img
|
||||
alt="{{ user().name }}"
|
||||
class="object-cover w-full h-full"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{
|
||||
user().avatar
|
||||
}}"
|
||||
src="{{ profile().avatarUrl }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
} @else {
|
||||
|
||||
@@ -8,6 +8,8 @@ import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repo
|
||||
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||
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('ProfileListComponent', () => {
|
||||
let component: ProfileListComponent;
|
||||
@@ -22,6 +24,7 @@ describe('ProfileListComponent', () => {
|
||||
provideRouter([]),
|
||||
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepository },
|
||||
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepository },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Router } from '@angular/router';
|
||||
import { SearchFilters } from '@app/domain/search/search-filters';
|
||||
import { FilterComponent } from '@app/shared/features/filter/filter.component';
|
||||
import { PaginationComponent } from '@app/shared/features/pagination/pagination.component';
|
||||
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
|
||||
import { ProfileListMapComponent } from '@app/shared/features/profile-list-map/profile-list-map.component';
|
||||
|
||||
type ViewMode = 'list' | 'map';
|
||||
@@ -52,18 +51,11 @@ export class ProfileListComponent {
|
||||
this.facade.load(filters);
|
||||
}
|
||||
|
||||
// Nouvelles méthodes
|
||||
switchToListView(): void {
|
||||
this.viewMode.set('list');
|
||||
}
|
||||
|
||||
switchToMapView(): void {
|
||||
this.viewMode.set('map');
|
||||
// Recharger les profils avec localisation si nécessaire
|
||||
}
|
||||
|
||||
onProfileClickFromMap(profile: ProfileViewModel): void {
|
||||
// Navigation vers le profil
|
||||
this.router.navigate(['/profiles', profile.id]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { provideRouter } from '@angular/router';
|
||||
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
||||
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||
import { mockSectorRepo } from '@app/testing/sector.mock';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('ChipsComponent', () => {
|
||||
let component: ChipsComponent;
|
||||
@@ -18,6 +20,7 @@ describe('ChipsComponent', () => {
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepository },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, inject, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, inject, input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { TitleCasePipe } from '@angular/common';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { SectorFacade } from '@app/ui/sectors/sector.facade';
|
||||
@@ -13,7 +13,7 @@ import { SectorFacade } from '@app/ui/sectors/sector.facade';
|
||||
})
|
||||
@UntilDestroy()
|
||||
export class ChipsComponent implements OnChanges {
|
||||
@Input({ required: true }) sectorId: string | null = null;
|
||||
sectorId = input.required<string>();
|
||||
|
||||
private readonly sectorFacade = inject(SectorFacade);
|
||||
protected sector = this.sectorFacade.sector;
|
||||
@@ -21,8 +21,8 @@ export class ChipsComponent implements OnChanges {
|
||||
protected readonly error = this.sectorFacade.error;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (this.sectorId && !this.loading().isLoading) {
|
||||
this.sectorFacade.loadOne(this.sectorId);
|
||||
if (this.sectorId() && !this.loading().isLoading) {
|
||||
this.sectorFacade.loadOne(this.sectorId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MyProfileProjectItemComponent } from './my-profile-project-item.component';
|
||||
import { of } from 'rxjs';
|
||||
import { Project } from '@app/domain/projects/project.model';
|
||||
import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
|
||||
import { mockProjectRepo } from '@app/testing/project.mock';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('MyProfileProjectItemComponent', () => {
|
||||
let component: MyProfileProjectItemComponent;
|
||||
@@ -20,11 +20,15 @@ describe('MyProfileProjectItemComponent', () => {
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: PROJECT_REPOSITORY_TOKEN, useValue: mockProjectRepository },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(MyProfileProjectItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.componentRef.setInput('projectId', 'fakeId');
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
await fixture.whenStable();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, input, OnInit } from '@angular/core';
|
||||
import { environment } from '@env/environment';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
@@ -12,12 +12,12 @@ import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
})
|
||||
export class MyProfileProjectItemComponent implements OnInit {
|
||||
protected readonly environment = environment;
|
||||
@Input({ required: true }) projectId = '';
|
||||
projectId = input.required<string>();
|
||||
|
||||
private readonly projectFacade = new ProjectFacade();
|
||||
protected project = this.projectFacade.project;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectFacade.loadOne(this.projectId);
|
||||
this.projectFacade.loadOne(this.projectId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
|
||||
import { mockProfileRepo } from '@app/testing/profile.mock';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('MyProfileProjectListComponent', () => {
|
||||
let component: MyProfileProjectListComponent;
|
||||
@@ -17,6 +19,7 @@ describe('MyProfileProjectListComponent', () => {
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: PROJECT_REPOSITORY_TOKEN, useValue: mockProjectRepository },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit, signal } from '@angular/core';
|
||||
import { Component, input, OnInit, signal } from '@angular/core';
|
||||
import { PaginatorModule } from 'primeng/paginator';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
@@ -14,8 +14,8 @@ import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
})
|
||||
@UntilDestroy()
|
||||
export class MyProfileProjectListComponent implements OnInit {
|
||||
@Input({ required: true }) projectIds: string[] = [];
|
||||
@Input({ required: true }) userId = '';
|
||||
projectIds = input.required<string[]>();
|
||||
userId = input<string>('');
|
||||
|
||||
protected projectIdSelected = signal<string | null>(null);
|
||||
|
||||
@@ -23,7 +23,7 @@ export class MyProfileProjectListComponent implements OnInit {
|
||||
protected projects = this.projectFacade.projects;
|
||||
|
||||
ngOnInit(): void {
|
||||
this.projectFacade.load(this.userId);
|
||||
this.projectFacade.load(this.userId());
|
||||
}
|
||||
|
||||
onProjectFormSubmitted($event: string | null) {
|
||||
|
||||
@@ -27,6 +27,7 @@ describe('MyProfileUpdateCvFormComponent', () => {
|
||||
|
||||
fixture = TestBed.createComponent(MyProfileUpdateCvFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
await fixture.whenStable();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Component, effect, inject, Input } from '@angular/core';
|
||||
import { Component, effect, input } from '@angular/core';
|
||||
import { NgClass } from '@angular/common';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
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';
|
||||
@@ -14,9 +13,7 @@ import { Profile } from '@app/domain/profiles/profile.model';
|
||||
styleUrl: './my-profile-update-cv-form.component.scss',
|
||||
})
|
||||
export class MyProfileUpdateCvFormComponent {
|
||||
@Input({ required: true }) profile: ProfileViewModel | undefined = undefined;
|
||||
|
||||
private readonly toastrService = inject(ToastrService);
|
||||
profile = input.required<ProfileViewModel>();
|
||||
|
||||
file: File | null = null; // Variable to store file
|
||||
|
||||
@@ -29,13 +26,6 @@ export class MyProfileUpdateCvFormComponent {
|
||||
switch (this.loading().action) {
|
||||
case ActionType.UPDATE:
|
||||
if (!this.loading() && !this.error().hasError) {
|
||||
//this.authService.updateUser();
|
||||
|
||||
this.toastrService.success(` Votre CV a bien été modifier !`, `Mise à jour`, {
|
||||
closeButton: true,
|
||||
progressAnimation: 'decreasing',
|
||||
progressBar: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -45,7 +35,7 @@ export class MyProfileUpdateCvFormComponent {
|
||||
if (this.file != null) {
|
||||
const formData = new FormData();
|
||||
formData.append('cv', this.file); // "avatar" est le nom du champ dans PocketBase
|
||||
this.profileFacade.update(this.profile?.id!, formData as Partial<Profile>);
|
||||
this.profileFacade.update(this.profile()?.id!, formData as Partial<Profile>);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
} from '@angular/forms';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { MyProfileUpdateCvFormComponent } from '@app/shared/components/my-profile-update-cv-form/my-profile-update-cv-form.component';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
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';
|
||||
@@ -32,7 +31,6 @@ import { LoadingComponent } from '@app/shared/components/loading/loading.compone
|
||||
})
|
||||
@UntilDestroy()
|
||||
export class MyProfileUpdateFormComponent implements OnInit {
|
||||
private readonly toastrService = inject(ToastrService);
|
||||
protected readonly ActionType = ActionType;
|
||||
|
||||
@Input({ required: true }) profile: ProfileViewModel = {} as ProfileViewModel;
|
||||
@@ -50,14 +48,10 @@ export class MyProfileUpdateFormComponent implements OnInit {
|
||||
protected readonly sectorError = this.sectorFacade.error;
|
||||
|
||||
constructor() {
|
||||
let message = '';
|
||||
|
||||
effect(() => {
|
||||
if (!this.loading().isLoading) {
|
||||
switch (this.loading().action) {
|
||||
case ActionType.UPDATE:
|
||||
message = `Vos informations personnelles ont bien été modifier !`;
|
||||
this.customToast(ActionType.UPDATE, message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -127,29 +121,4 @@ export class MyProfileUpdateFormComponent implements OnInit {
|
||||
|
||||
this.profileFacade.update(this.profile.id, data);
|
||||
}
|
||||
|
||||
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.UPDATE ? 'Mise à jour' : ''}`,
|
||||
{
|
||||
closeButton: true,
|
||||
progressAnimation: 'decreasing',
|
||||
progressBar: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@if (projectId) {
|
||||
@if (projectId()) {
|
||||
<div class="space-y-6">
|
||||
<!-- Section Image du projet -->
|
||||
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg p-6 animate-fade-in">
|
||||
@@ -22,7 +22,7 @@
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white">Image du projet</h3>
|
||||
</div>
|
||||
|
||||
@if (projectId === 'add'.toLowerCase()) {
|
||||
@if (projectId() === 'add'.toLowerCase()) {
|
||||
<app-project-picture-form [project]="undefined" />
|
||||
} @else {
|
||||
<app-project-picture-form [project]="project()" />
|
||||
|
||||
@@ -5,8 +5,6 @@ import { ToastrService } from 'ngx-toastr';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
|
||||
import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||
import { of } from 'rxjs';
|
||||
import { Project } from '@app/domain/projects/project.model';
|
||||
import { AUTH_REPOSITORY_TOKEN } from '@app/infrastructure/authentification/auth-repository.token';
|
||||
import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-repository.token';
|
||||
import { AuthRepository } from '@app/domain/authentification/auth.repository';
|
||||
@@ -40,6 +38,8 @@ describe('MyProfileUpdateProjectFormComponent', () => {
|
||||
|
||||
fixture = TestBed.createComponent(MyProfileUpdateProjectFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.componentRef.setInput('projectId', 'fakeId');
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,17 +1,7 @@
|
||||
import {
|
||||
Component,
|
||||
effect,
|
||||
inject,
|
||||
Input,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
output,
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import { Component, effect, inject, input, output } from '@angular/core';
|
||||
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||
import { PaginatorModule } from 'primeng/paginator';
|
||||
import { ProjectPictureFormComponent } from '@app/shared/components/project-picture-form/project-picture-form.component';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
import { CreateProjectDto } from '@app/domain/projects/dto/create-project.dto';
|
||||
import { ActionType } from '@app/domain/action-type.util';
|
||||
@@ -25,10 +15,8 @@ import { AuthFacade } from '@app/ui/authentification/auth.facade';
|
||||
templateUrl: './my-profile-update-project-form.component.html',
|
||||
styleUrl: './my-profile-update-project-form.component.scss',
|
||||
})
|
||||
export class MyProfileUpdateProjectFormComponent implements OnInit, OnChanges {
|
||||
@Input({ required: true }) projectId: string | null = null;
|
||||
|
||||
private readonly toastrService = inject(ToastrService);
|
||||
export class MyProfileUpdateProjectFormComponent {
|
||||
projectId = input.required<string | null>();
|
||||
|
||||
private readonly projectFacade = new ProjectFacade();
|
||||
protected readonly ActionType = ActionType;
|
||||
@@ -50,17 +38,12 @@ export class MyProfileUpdateProjectFormComponent implements OnInit, OnChanges {
|
||||
formIsUpdated = output<string | null>();
|
||||
|
||||
constructor() {
|
||||
let message = '';
|
||||
effect(() => {
|
||||
if (!this.loading().isLoading) {
|
||||
switch (this.loading().action) {
|
||||
case ActionType.CREATE:
|
||||
message = `Le projet ${this.projectForm.getRawValue().nom} a bien été créer !`;
|
||||
this.customToast(ActionType.CREATE, message);
|
||||
break;
|
||||
case ActionType.UPDATE:
|
||||
message = `Les informations du projet ${this.projectForm.getRawValue().nom} ont bien été modifier !`;
|
||||
this.customToast(ActionType.UPDATE, message);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -73,20 +56,26 @@ export class MyProfileUpdateProjectFormComponent implements OnInit, OnChanges {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.projectId == 'add'.toLowerCase()) {
|
||||
this.projectForm.setValue({
|
||||
nom: '',
|
||||
description: '',
|
||||
lien: '',
|
||||
});
|
||||
}
|
||||
|
||||
if (this.projectId != null && this.projectId != 'add'.toLowerCase()) {
|
||||
this.projectFacade.loadOne(this.projectId);
|
||||
}
|
||||
effect(
|
||||
() => {
|
||||
const id = this.projectId();
|
||||
if (id) {
|
||||
if (id.toLowerCase() === 'add') {
|
||||
// Mode Création : On vide le formulaire
|
||||
this.projectForm.reset({
|
||||
nom: '',
|
||||
description: '',
|
||||
lien: '',
|
||||
});
|
||||
} else {
|
||||
// Mode Édition : On charge les données
|
||||
this.projectFacade.loadOne(id);
|
||||
}
|
||||
}
|
||||
},
|
||||
{ allowSignalWrites: true }
|
||||
);
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
@@ -94,7 +83,7 @@ export class MyProfileUpdateProjectFormComponent implements OnInit, OnChanges {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.projectId != null && this.projectId != 'add'.toLowerCase()) {
|
||||
if (this.projectId() != null && this.projectId() != 'add'.toLowerCase()) {
|
||||
// Update
|
||||
this.projectFacade.update(this.project()!.id, this.projectForm.getRawValue());
|
||||
} else {
|
||||
@@ -107,35 +96,4 @@ export class MyProfileUpdateProjectFormComponent implements OnInit, OnChanges {
|
||||
this.projectFacade.create(projectDto);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.projectId = changes['projectId'].currentValue;
|
||||
this.ngOnInit();
|
||||
}
|
||||
|
||||
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.formIsUpdated.emit(this.project()!.id);
|
||||
this.toastrService.success(
|
||||
`${message}`,
|
||||
`${action === ActionType.UPDATE ? 'Mise à jour' : 'Nouveau projet'}`,
|
||||
{
|
||||
closeButton: true,
|
||||
progressAnimation: 'decreasing',
|
||||
progressBar: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import { ProfileRepository } from '@app/domain/profiles/profile.repository';
|
||||
import { mockProfileRepo } from '@app/testing/profile.mock';
|
||||
import { mockAuthRepo } from '@app/testing/auth.mock';
|
||||
import { mockThemeService } from '@app/testing/theme.mock';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('NavBarComponent', () => {
|
||||
let component: NavBarComponent;
|
||||
@@ -38,6 +40,7 @@ describe('NavBarComponent', () => {
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: ThemeService, useValue: mockTheme },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
{ provide: AUTH_REPOSITORY_TOKEN, useValue: mockAuthRepository },
|
||||
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepository },
|
||||
],
|
||||
|
||||
@@ -5,6 +5,8 @@ import { ProjectRepository } from '@app/domain/projects/project.repository';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-repository.token';
|
||||
import { mockProjectRepo } from '@app/testing/project.mock';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('ProjectListComponent', () => {
|
||||
let component: ProjectListComponent;
|
||||
@@ -18,6 +20,7 @@ describe('ProjectListComponent', () => {
|
||||
providers: [
|
||||
provideRouter([]),
|
||||
{ provide: PROJECT_REPOSITORY_TOKEN, useValue: mockProjectRepository },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { Component, input, OnChanges, SimpleChanges } from '@angular/core';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { ProjectItemComponent } from '@app/shared/components/project-item/project-item.component';
|
||||
import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
@@ -12,13 +12,13 @@ import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
})
|
||||
@UntilDestroy()
|
||||
export class ProjectListComponent implements OnChanges {
|
||||
@Input({ required: true }) userProjectId = '';
|
||||
userProjectId = input.required<string>({});
|
||||
|
||||
private readonly projectFacade = new ProjectFacade();
|
||||
|
||||
protected projects = this.projectFacade.projects;
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.projectFacade.load(this.userProjectId);
|
||||
this.projectFacade.load(this.userProjectId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, effect, inject, Input, output, signal } from '@angular/core';
|
||||
import { Component, effect, Input, output, signal } from '@angular/core';
|
||||
import { NgClass, NgTemplateOutlet } from '@angular/common';
|
||||
import { environment } from '@env/environment';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { ProjectViewModel } from '@app/ui/projects/project.presenter.model';
|
||||
import { ProjectFacade } from '@app/ui/projects/project.facade';
|
||||
import { ActionType } from '@app/domain/action-type.util';
|
||||
@@ -20,7 +19,6 @@ export class ProjectPictureFormComponent {
|
||||
protected readonly ActionType = ActionType;
|
||||
|
||||
onFormSubmitted = output<any>();
|
||||
private readonly toastrService = inject(ToastrService);
|
||||
|
||||
private readonly projectFacade = new ProjectFacade();
|
||||
protected readonly loading = this.projectFacade.loading;
|
||||
@@ -36,8 +34,6 @@ export class ProjectPictureFormComponent {
|
||||
if (!this.loading().isLoading && this.onSubmitted()) {
|
||||
switch (this.loading().action) {
|
||||
case ActionType.UPDATE:
|
||||
message = `L'aperçu du projet ${this.project!.nom} ont bien été modifier !`;
|
||||
this.customToast(ActionType.UPDATE, message);
|
||||
this.onSubmitted.set(false);
|
||||
this.onFormSubmitted.emit('');
|
||||
break;
|
||||
@@ -71,29 +67,4 @@ export class ProjectPictureFormComponent {
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
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.UPDATE ? 'Mise à jour' : ''}`,
|
||||
{
|
||||
closeButton: true,
|
||||
progressAnimation: 'decreasing',
|
||||
progressBar: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ export class UserAvatarFormComponent {
|
||||
if (!this.loading().isLoading) {
|
||||
switch (this.loading().action) {
|
||||
case ActionType.UPDATE:
|
||||
message = `Votre photo de profile a bien été modifier !`;
|
||||
if (this.onSubmitted()) {
|
||||
this.customToast(ActionType.UPDATE, message);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
@if (user() !== undefined) {
|
||||
<a [routerLink]="[slug()]" [state]="{ user: user(), profile }" class="block group">
|
||||
<a
|
||||
[routerLink]="[profile()!.slug!]"
|
||||
[state]="{ user: user(), profile: profile() }"
|
||||
class="block group"
|
||||
>
|
||||
<!-- Card du profil -->
|
||||
<div
|
||||
class="relative bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden transition-all duration-300 hover:shadow-2xl hover:-translate-y-2"
|
||||
>
|
||||
<!-- Badge vérifié (position absolue en haut à droite) -->
|
||||
@if (profile.estVerifier) {
|
||||
@if (profile().estVerifier) {
|
||||
<div class="absolute top-3 right-3 z-10 animate-pulse-slow">
|
||||
<div class="bg-purple-500/20 backdrop-blur-md p-2 rounded-full">
|
||||
<svg
|
||||
@@ -29,10 +33,10 @@
|
||||
<!-- Avatar avec bordure gradient -->
|
||||
<div class="relative inline-block mb-4">
|
||||
<div class="w-32 h-32 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 p-1">
|
||||
@if (user().avatar) {
|
||||
@if (profile().avatarUrl) {
|
||||
<img
|
||||
class="w-full h-full rounded-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500 group-hover:scale-105"
|
||||
src="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{ user().avatar }}"
|
||||
src="{{ profile().avatarUrl }}"
|
||||
alt="{{ user().username }}"
|
||||
loading="lazy"
|
||||
/>
|
||||
@@ -61,12 +65,12 @@
|
||||
{{ user().username }}
|
||||
</h3>
|
||||
} @else {
|
||||
<h3 class="text-lg font-bold text-gray-500 dark:text-gray-400 mb-2">Non mentionné</h3>
|
||||
<h3 class="text-lg font-bold text-gray-500 dark:text-gray-400 mb-2">Non renseigné</h3>
|
||||
}
|
||||
|
||||
<!-- Profession -->
|
||||
<p class="text-sm font-semibold text-gray-600 dark:text-gray-300 mb-2">
|
||||
{{ profile.profession || 'Profession non renseignée' }}
|
||||
{{ profile().profession || 'Profession non renseignée' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ import { VerticalProfileItemComponent } from './vertical-profile-item.component'
|
||||
import { UserRepository } from '@app/domain/users/user.repository';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
|
||||
import { User } from '@app/domain/users/user.model';
|
||||
import { of } from 'rxjs';
|
||||
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
||||
import { SectorRepository } from '@app/domain/sectors/sector.repository';
|
||||
import { Sector } from '@app/domain/sectors/sector.model';
|
||||
import { mockUserRepo } from '@app/testing/user.mock';
|
||||
import { mockSectorRepo } from '@app/testing/sector.mock';
|
||||
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { mockToastR } from '@app/testing/toastr.mock';
|
||||
|
||||
describe('VerticalProfileItemComponent', () => {
|
||||
let component: VerticalProfileItemComponent;
|
||||
@@ -19,6 +19,11 @@ describe('VerticalProfileItemComponent', () => {
|
||||
let mockUserRepository: jest.Mocked<Partial<UserRepository>> = mockUserRepo;
|
||||
let mockSectorRepository: jest.Mocked<Partial<SectorRepository>> = mockSectorRepo;
|
||||
|
||||
const mockProfile: Partial<ProfileViewModel> = {
|
||||
id: 'fakeId',
|
||||
utilisateur: 'fakeUtilisateur',
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [VerticalProfileItemComponent],
|
||||
@@ -26,11 +31,15 @@ describe('VerticalProfileItemComponent', () => {
|
||||
provideRouter([]),
|
||||
{ provide: USER_REPOSITORY_TOKEN, useValue: mockUserRepository },
|
||||
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepository },
|
||||
{ provide: ToastrService, useValue: mockToastR },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(VerticalProfileItemComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
fixture.componentRef.setInput('profile', mockProfile);
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, computed, inject, Input, OnInit } from '@angular/core';
|
||||
import { Component, computed, inject, input, OnInit } from '@angular/core';
|
||||
import { Router, RouterLink } from '@angular/router';
|
||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||
import { environment } from '@env/environment';
|
||||
@@ -15,9 +15,10 @@ import { UserFacade } from '@app/ui/users/user.facade';
|
||||
})
|
||||
@UntilDestroy()
|
||||
export class VerticalProfileItemComponent implements OnInit {
|
||||
@Input({ required: true }) profile: ProfileViewModel = {} as ProfileViewModel;
|
||||
protected router = inject(Router);
|
||||
profile = input.required<ProfileViewModel>();
|
||||
protected readonly router = inject(Router);
|
||||
private readonly facade = inject(UserFacade);
|
||||
protected readonly environment = environment;
|
||||
|
||||
protected user = this.facade.user;
|
||||
protected readonly loading = this.facade.loading;
|
||||
@@ -25,13 +26,13 @@ export class VerticalProfileItemComponent implements OnInit {
|
||||
|
||||
protected slug = computed(() => {
|
||||
const slug = this.user().slug ?? '';
|
||||
const profileId = this.profile.id ? this.profile.id : '';
|
||||
const profileId = this.profile().id ? this.profile().id : '';
|
||||
return slug === '' ? profileId : slug.concat('-', profileId);
|
||||
});
|
||||
|
||||
ngOnInit(): void {
|
||||
this.facade.loadOne(this.profile.utilisateur);
|
||||
if (this.profile()) {
|
||||
this.facade.loadOne(this.profile().utilisateur);
|
||||
}
|
||||
}
|
||||
|
||||
protected readonly environment = environment;
|
||||
}
|
||||
|
||||
@@ -5,38 +5,40 @@
|
||||
@for (profile of profiles; track profile.id) {
|
||||
<app-vertical-profile-item [profile]="profile" />
|
||||
} @empty {
|
||||
<!-- Message si aucun profil -->
|
||||
<div class="col-span-full">
|
||||
<div
|
||||
class="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-700 rounded-xl p-12 text-center"
|
||||
>
|
||||
<div
|
||||
class="inline-flex w-20 h-20 bg-white dark:bg-gray-600 rounded-full items-center justify-center mb-6 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-10 h-10 text-gray-400 dark:text-gray-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">
|
||||
Aucun profil trouvé
|
||||
</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 max-w-md mx-auto">
|
||||
Aucun profil ne correspond à votre recherche. Essayez de modifier vos critères.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ng-container *ngTemplateOutlet="emptyMessage" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<ng-template #emptyMessage>
|
||||
<!-- Message si aucun profil -->
|
||||
<div class="col-span-full">
|
||||
<div
|
||||
class="bg-gradient-to-r from-gray-50 to-gray-100 dark:from-gray-800 dark:to-gray-700 rounded-xl p-12 text-center"
|
||||
>
|
||||
<div
|
||||
class="inline-flex w-20 h-20 bg-white dark:bg-gray-600 rounded-full items-center justify-center mb-6 shadow-lg"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
class="w-10 h-10 text-gray-400 dark:text-gray-500"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="text-xl font-bold text-gray-900 dark:text-white mb-3">Aucun profil trouvé</h3>
|
||||
<p class="text-gray-600 dark:text-gray-300 max-w-md mx-auto">
|
||||
Aucun profil ne correspond à votre recherche. Essayez de modifier vos critères.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
@@ -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',
|
||||
})
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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<ForgotPasswordComponent>;
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ProfileViewModel>();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@ export class PdfViewerComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${environment.baseUrl}/api/files/profiles/${currentProfile.id}/${currentProfile.cv}`;
|
||||
return currentProfile.cv;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<any>();
|
||||
@@ -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,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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<ProfileViewModel>();
|
||||
profile = input.required<ProfileViewModel>();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
3
src/app/testing/feedback.service.mock.ts
Normal file
3
src/app/testing/feedback.service.mock.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const mockFeedbackService = {
|
||||
notify: jest.fn(),
|
||||
};
|
||||
9
src/app/testing/search.service.mock.ts
Normal file
9
src/app/testing/search.service.mock.ts
Normal file
@@ -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(),
|
||||
};
|
||||
@@ -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 },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -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 },
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Profile>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string, string> = {
|
||||
'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`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<UserViewModel>) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user