user => clean archi
This commit is contained in:
@@ -10,13 +10,18 @@
|
|||||||
"tsc": "tsc --noEmit",
|
"tsc": "tsc --noEmit",
|
||||||
"tsc:watch": "tsc --noEmit --watch",
|
"tsc:watch": "tsc --noEmit --watch",
|
||||||
"prettier": "prettier --write \"src/**/*.{ts,html,scss,css,md,json}\"",
|
"prettier": "prettier --write \"src/**/*.{ts,html,scss,css,md,json}\"",
|
||||||
|
"prettier:check": "prettier --check \"src/**/*.{ts,html,scss,css,md,json}\"",
|
||||||
|
"format": "npm run prettier && npm run lint:fix",
|
||||||
|
"lint": "ng lint",
|
||||||
|
"lint:fix": "ng lint --fix",
|
||||||
|
"clean:imports": "ts-unused-exports tsconfig.json --excludePathsFromReport=\"src/main.ts;src/environments\" && npm run lint:fix",
|
||||||
|
"fix:all": "npm run format && npm run tsc",
|
||||||
"check:all": "npm run format && npm run tsc && npm run lint && npm run test",
|
"check:all": "npm run format && npm run tsc && npm run lint && npm run test",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"test:coverage": "jest --coverage",
|
"test:coverage": "jest --coverage",
|
||||||
"test:ci": "jest --runInBand",
|
"test:ci": "jest --runInBand",
|
||||||
"serve:ssr:TrouveTonProfile": "node dist/trouve-ton-profile/server/server.mjs",
|
"serve:ssr:TrouveTonProfile": "node dist/trouve-ton-profile/server/server.mjs"
|
||||||
"lint": "ng lint"
|
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import { PROJECT_REPOSITORY_TOKEN } from '@app/infrastructure/projects/project-r
|
|||||||
import { PbProjectRepository } from '@app/infrastructure/projects/pb-project.repository';
|
import { PbProjectRepository } from '@app/infrastructure/projects/pb-project.repository';
|
||||||
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
import { SECTOR_REPOSITORY_TOKEN } from '@app/infrastructure/sectors/sector-repository.token';
|
||||||
import { PbSectorRepository } from '@app/infrastructure/sectors/pb-sector.repository';
|
import { PbSectorRepository } from '@app/infrastructure/sectors/pb-sector.repository';
|
||||||
|
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
|
||||||
|
import { PbUserRepository } from '@app/infrastructure/users/pb-user.repository';
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -34,6 +36,7 @@ export const appConfig: ApplicationConfig = {
|
|||||||
{ provide: PROFILE_REPOSITORY_TOKEN, useExisting: PbProfileRepository },
|
{ provide: PROFILE_REPOSITORY_TOKEN, useExisting: PbProfileRepository },
|
||||||
{ provide: PROJECT_REPOSITORY_TOKEN, useExisting: PbProjectRepository },
|
{ provide: PROJECT_REPOSITORY_TOKEN, useExisting: PbProjectRepository },
|
||||||
{ provide: SECTOR_REPOSITORY_TOKEN, useExisting: PbSectorRepository },
|
{ provide: SECTOR_REPOSITORY_TOKEN, useExisting: PbSectorRepository },
|
||||||
|
{ provide: USER_REPOSITORY_TOKEN, useExisting: PbUserRepository },
|
||||||
provideToastr({
|
provideToastr({
|
||||||
timeOut: 10000,
|
timeOut: 10000,
|
||||||
positionClass: 'toast-top-right',
|
positionClass: 'toast-top-right',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { TestBed } from '@angular/core/testing';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { myProfileResolver } from './my-profile.resolver';
|
import { myProfileResolver } from './my-profile.resolver';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
describe('myProfileResolver', () => {
|
describe('myProfileResolver', () => {
|
||||||
let mockRouter: Partial<Router>;
|
let mockRouter: Partial<Router>;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ResolveFn, Router } from '@angular/router';
|
import { ResolveFn, Router } from '@angular/router';
|
||||||
import { User } from '@app/shared/models/user';
|
|
||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
export const myProfileResolver: ResolveFn<{ user: User }> = (route, state) => {
|
export const myProfileResolver: ResolveFn<{ user: User }> = (route, state) => {
|
||||||
const router = inject(Router);
|
const router = inject(Router);
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { TestBed } from '@angular/core/testing';
|
|||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
import { detailResolver } from './detail.resolver';
|
import { detailResolver } from './detail.resolver';
|
||||||
import { User } from '@app/shared/models/user';
|
|
||||||
import { Profile } from '@app/domain/profiles/profile.model';
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
describe('detailResolver', () => {
|
describe('detailResolver', () => {
|
||||||
let mockRoute: Partial<Router>;
|
let mockRoute: Partial<Router>;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ResolveFn, Router } from '@angular/router';
|
import { ResolveFn, Router } from '@angular/router';
|
||||||
import { inject } from '@angular/core';
|
import { inject } from '@angular/core';
|
||||||
import { User } from '@app/shared/models/user';
|
|
||||||
import { Profile } from '@app/domain/profiles/profile.model';
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
export const detailResolver: ResolveFn<{ user: User; profile: Profile }> = (route, state) => {
|
export const detailResolver: ResolveFn<{ user: User; profile: Profile }> = (route, state) => {
|
||||||
const paramValue = route.params['name'];
|
const paramValue = route.params['name'];
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { TestBed } from '@angular/core/testing';
|
|||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { LoginDto } from '@app/shared/models/login-dto';
|
import { LoginDto } from '@app/shared/models/login-dto';
|
||||||
import { RegisterDto } from '@app/shared/models/register-dto';
|
import { RegisterDto } from '@app/shared/models/register-dto';
|
||||||
import { User } from '@app/shared/models/user';
|
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
describe('AuthService', () => {
|
describe('AuthService', () => {
|
||||||
let authService: AuthService;
|
let authService: AuthService;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import PocketBase from 'pocketbase';
|
|||||||
import { environment } from '@env/environment';
|
import { environment } from '@env/environment';
|
||||||
import { Auth } from '@app/shared/models/auth';
|
import { Auth } from '@app/shared/models/auth';
|
||||||
import { RegisterDto } from '@app/shared/models/register-dto';
|
import { RegisterDto } from '@app/shared/models/register-dto';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root',
|
providedIn: 'root',
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
|
||||||
|
|
||||||
import { UserService } from './user.service';
|
|
||||||
import { Router } from '@angular/router';
|
|
||||||
|
|
||||||
describe('UserService', () => {
|
|
||||||
let service: UserService;
|
|
||||||
|
|
||||||
const routerSpy = {
|
|
||||||
navigate: jest.fn(),
|
|
||||||
navigateByUrl: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
providers: [
|
|
||||||
{ provide: Router, useValue: routerSpy }, // <<— spy: neutralise la navigation
|
|
||||||
],
|
|
||||||
imports: [],
|
|
||||||
});
|
|
||||||
service = TestBed.inject(UserService);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be created', () => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import PocketBase from 'pocketbase';
|
|
||||||
import { environment } from '@env/environment';
|
|
||||||
import { from } from 'rxjs';
|
|
||||||
import { User } from '@app/shared/models/user';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class UserService {
|
|
||||||
getUserById(id: string) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection('users').getOne<User>(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUser(id: string, data: User | any) {
|
|
||||||
const pb = new PocketBase(environment.baseUrl);
|
|
||||||
return from(pb.collection('users').update<User>(id, data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
src/app/domain/users/user.repository.ts
Normal file
7
src/app/domain/users/user.repository.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
|
export interface UserRepository {
|
||||||
|
getUserById(userId: string): Observable<User>;
|
||||||
|
update(userId: string, user: Partial<User> | User): Observable<User>;
|
||||||
|
}
|
||||||
21
src/app/infrastructure/users/pb-user.repository.ts
Normal file
21
src/app/infrastructure/users/pb-user.repository.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
import { from, Observable } from 'rxjs';
|
||||||
|
import { environment } from '@env/environment';
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class PbUserRepository implements UserRepository {
|
||||||
|
private pb = new PocketBase(environment.baseUrl);
|
||||||
|
|
||||||
|
getUserById(userId: string): Observable<User> {
|
||||||
|
return from(this.pb.collection('users').getOne<User>(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
update(userId: string, user: Partial<User> | User): Observable<User> {
|
||||||
|
return from(this.pb.collection('users').update<User>(userId, user));
|
||||||
|
}
|
||||||
|
}
|
||||||
4
src/app/infrastructure/users/user-repository.token.ts
Normal file
4
src/app/infrastructure/users/user-repository.token.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { InjectionToken } from '@angular/core';
|
||||||
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
|
||||||
|
export const USER_REPOSITORY_TOKEN = new InjectionToken<UserRepository>('UserRepository');
|
||||||
@@ -7,12 +7,15 @@ import { PROFILE_REPOSITORY_TOKEN } from '@app/infrastructure/profiles/profile-r
|
|||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { Profile } from '@app/domain/profiles/profile.model';
|
import { Profile } from '@app/domain/profiles/profile.model';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
|
||||||
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
|
||||||
describe('MyProfileComponent', () => {
|
describe('MyProfileComponent', () => {
|
||||||
let component: MyProfileComponent;
|
let component: MyProfileComponent;
|
||||||
let fixture: ComponentFixture<MyProfileComponent>;
|
let fixture: ComponentFixture<MyProfileComponent>;
|
||||||
|
|
||||||
let mockProfileRepo: ProfileRepository;
|
let mockProfileRepo: ProfileRepository;
|
||||||
|
let mockUserRepo: UserRepository;
|
||||||
let mockToastrService: Partial<ToastrService>;
|
let mockToastrService: Partial<ToastrService>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -22,6 +25,12 @@ describe('MyProfileComponent', () => {
|
|||||||
update: jest.fn(),
|
update: jest.fn(),
|
||||||
getByUserId: jest.fn().mockReturnValue(of({} as Profile)),
|
getByUserId: jest.fn().mockReturnValue(of({} as Profile)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mockUserRepo = {
|
||||||
|
update: jest.fn(),
|
||||||
|
getUserById: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
mockToastrService = {
|
mockToastrService = {
|
||||||
warning: jest.fn(),
|
warning: jest.fn(),
|
||||||
success: jest.fn(),
|
success: jest.fn(),
|
||||||
@@ -33,6 +42,7 @@ describe('MyProfileComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
provideRouter([]),
|
provideRouter([]),
|
||||||
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepo },
|
{ provide: PROFILE_REPOSITORY_TOKEN, useValue: mockProfileRepo },
|
||||||
|
{ provide: USER_REPOSITORY_TOKEN, useValue: mockUserRepo },
|
||||||
{ provide: ToastrService, useValue: mockToastrService },
|
{ provide: ToastrService, useValue: mockToastrService },
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, computed, inject, OnInit, signal } from '@angular/core';
|
import { Component, computed, inject, OnInit, signal } from '@angular/core';
|
||||||
import { ActivatedRoute, RouterOutlet } from '@angular/router';
|
import { ActivatedRoute, RouterOutlet } from '@angular/router';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { Location, UpperCasePipe } from '@angular/common';
|
import { Location, UpperCasePipe } from '@angular/common';
|
||||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { environment } from '@env/environment';
|
import { environment } from '@env/environment';
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Component, computed, inject } from '@angular/core';
|
import { Component, computed, inject } from '@angular/core';
|
||||||
import { ActivatedRoute, RouterLink } from '@angular/router';
|
import { ActivatedRoute, RouterLink } from '@angular/router';
|
||||||
import { UpperCasePipe } from '@angular/common';
|
import { UpperCasePipe } from '@angular/common';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
||||||
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
||||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
|
|||||||
@@ -8,5 +8,5 @@ import { Component, Input } from '@angular/core';
|
|||||||
styleUrl: './loading.component.scss',
|
styleUrl: './loading.component.scss',
|
||||||
})
|
})
|
||||||
export class LoadingComponent {
|
export class LoadingComponent {
|
||||||
@Input() message: string = 'Chargement...';
|
@Input() message = 'Chargement...';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
|
||||||
import { NgClass } from '@angular/common';
|
|
||||||
import { PaginatorModule } from 'primeng/paginator';
|
import { PaginatorModule } from 'primeng/paginator';
|
||||||
import { ProjectPictureFormComponent } from '@app/shared/components/project-picture-form/project-picture-form.component';
|
import { ProjectPictureFormComponent } from '@app/shared/components/project-picture-form/project-picture-form.component';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
@@ -22,13 +21,7 @@ import { LoadingComponent } from '@app/shared/components/loading/loading.compone
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'app-my-profile-update-project-form',
|
selector: 'app-my-profile-update-project-form',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [PaginatorModule, ReactiveFormsModule, ProjectPictureFormComponent, LoadingComponent],
|
||||||
PaginatorModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
NgClass,
|
|
||||||
ProjectPictureFormComponent,
|
|
||||||
LoadingComponent,
|
|
||||||
],
|
|
||||||
templateUrl: './my-profile-update-project-form.component.html',
|
templateUrl: './my-profile-update-project-form.component.html',
|
||||||
styleUrl: './my-profile-update-project-form.component.scss',
|
styleUrl: './my-profile-update-project-form.component.scss',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { AuthService } from '@app/core/services/authentication/auth.service';
|
|||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
import { signal } from '@angular/core';
|
import { signal } from '@angular/core';
|
||||||
import { Auth } from '@app/shared/models/auth';
|
import { Auth } from '@app/shared/models/auth';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
describe('NavBarComponent', () => {
|
describe('NavBarComponent', () => {
|
||||||
let component: NavBarComponent;
|
let component: NavBarComponent;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { UserAvatarFormComponent } from './user-avatar-form.component';
|
|||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { AuthService } from '@app/core/services/authentication/auth.service';
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
|
||||||
|
|
||||||
describe('UserAvatarFormComponent', () => {
|
describe('UserAvatarFormComponent', () => {
|
||||||
let component: UserAvatarFormComponent;
|
let component: UserAvatarFormComponent;
|
||||||
@@ -12,7 +13,7 @@ describe('UserAvatarFormComponent', () => {
|
|||||||
|
|
||||||
let mockToastrService: Partial<ToastrService>;
|
let mockToastrService: Partial<ToastrService>;
|
||||||
let mockAuthService: Partial<AuthService>;
|
let mockAuthService: Partial<AuthService>;
|
||||||
let mockUserService: Partial<UserService>;
|
let mockUserRepo: UserRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mockToastrService = {
|
mockToastrService = {
|
||||||
@@ -25,19 +26,18 @@ describe('UserAvatarFormComponent', () => {
|
|||||||
updateUser: jest.fn(),
|
updateUser: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mockUserService = {
|
mockUserRepo = {
|
||||||
updateUser: jest.fn().mockReturnValue({
|
update: jest.fn(),
|
||||||
subscribe: jest.fn(),
|
getUserById: jest.fn(),
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [UserAvatarFormComponent],
|
imports: [UserAvatarFormComponent],
|
||||||
providers: [
|
providers: [
|
||||||
provideRouter([]),
|
provideRouter([]),
|
||||||
|
{ provide: USER_REPOSITORY_TOKEN, useValue: mockUserRepo },
|
||||||
{ provide: ToastrService, useValue: mockToastrService },
|
{ provide: ToastrService, useValue: mockToastrService },
|
||||||
{ provide: AuthService, useValue: mockAuthService },
|
{ provide: AuthService, useValue: mockAuthService },
|
||||||
{ provide: UserService, useValue: mockUserService },
|
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { Component, inject, Input, output } from '@angular/core';
|
import { Component, effect, inject, Input, output } from '@angular/core';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { AuthService } from '@app/core/services/authentication/auth.service';
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
|
||||||
import { environment } from '@env/environment';
|
import { environment } from '@env/environment';
|
||||||
import { NgClass } from '@angular/common';
|
import { NgClass } from '@angular/common';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { UserFacade } from '@app/ui/users/user.facade';
|
||||||
|
import { ActionType } from '@app/domain/action-type.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-avatar-form',
|
selector: 'app-user-avatar-form',
|
||||||
@@ -17,47 +18,42 @@ import { ToastrService } from 'ngx-toastr';
|
|||||||
export class UserAvatarFormComponent {
|
export class UserAvatarFormComponent {
|
||||||
private readonly toastrService = inject(ToastrService);
|
private readonly toastrService = inject(ToastrService);
|
||||||
protected readonly environment = environment;
|
protected readonly environment = environment;
|
||||||
|
private readonly facade = inject(UserFacade);
|
||||||
@Input({ required: true }) user: User | undefined = undefined;
|
@Input({ required: true }) user: User | undefined = undefined;
|
||||||
|
|
||||||
onFormSubmitted = output<any>();
|
onFormSubmitted = output<any>();
|
||||||
private userService = inject(UserService);
|
|
||||||
|
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
|
|
||||||
file: File | null = null; // Variable to store file
|
file: File | null = null; // Variable to store file
|
||||||
imagePreviewUrl: string | null = null; // URL for image preview
|
imagePreviewUrl: string | null = null; // URL for image preview
|
||||||
|
|
||||||
|
protected readonly loading = this.facade.loading;
|
||||||
|
protected readonly error = this.facade.error;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
let message = '';
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this.loading().isLoading) {
|
||||||
|
switch (this.loading().action) {
|
||||||
|
case ActionType.UPDATE:
|
||||||
|
this.authService.updateUser();
|
||||||
|
message = `Votre photo de profile a bien été modifier !`;
|
||||||
|
this.customToast(ActionType.UPDATE, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onUserAvatarFormSubmit() {
|
onUserAvatarFormSubmit() {
|
||||||
if (this.file != null) {
|
if (this.file != null) {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('avatar', this.file); // "avatar" est le nom du champ dans PocketBase
|
formData.append('avatar', this.file); // "avatar" est le nom du champ dans PocketBase
|
||||||
|
|
||||||
this.userService.updateUser(this.user?.id!, formData).subscribe({
|
this.facade.update(this.user?.id!, formData as Partial<User>);
|
||||||
next: (value) => {
|
|
||||||
this.authService.updateUser();
|
|
||||||
|
|
||||||
this.toastrService.success(
|
|
||||||
`Votre photo de profile a bien été modifier !`,
|
|
||||||
`Mise à jour`,
|
|
||||||
{
|
|
||||||
closeButton: true,
|
|
||||||
progressAnimation: 'decreasing',
|
|
||||||
progressBar: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
this.toastrService.error(
|
|
||||||
`Une erreur est survenue lors de la mise à jour de votre photo de profile !`,
|
|
||||||
`Erreur`,
|
|
||||||
{
|
|
||||||
closeButton: true,
|
|
||||||
progressAnimation: 'decreasing',
|
|
||||||
progressBar: true,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this.onFormSubmitted.emit('');
|
this.onFormSubmitted.emit('');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,4 +73,29 @@ export class UserAvatarFormComponent {
|
|||||||
};
|
};
|
||||||
reader.readAsDataURL(file);
|
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,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { UserFormComponent } from './user-form.component';
|
import { UserFormComponent } from './user-form.component';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { AuthService } from '@app/core/services/authentication/auth.service';
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
import { FormBuilder } from '@angular/forms';
|
import { FormBuilder } from '@angular/forms';
|
||||||
|
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
|
||||||
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
|
||||||
describe('UserFormComponent', () => {
|
describe('UserFormComponent', () => {
|
||||||
let component: UserFormComponent;
|
let component: UserFormComponent;
|
||||||
@@ -13,7 +14,7 @@ describe('UserFormComponent', () => {
|
|||||||
|
|
||||||
let mockToastrService: Partial<ToastrService>;
|
let mockToastrService: Partial<ToastrService>;
|
||||||
let mockAuthService: Partial<AuthService>;
|
let mockAuthService: Partial<AuthService>;
|
||||||
let mockUserService: Partial<UserService>;
|
let mockUserRepo: UserRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
mockToastrService = {
|
mockToastrService = {
|
||||||
@@ -26,10 +27,9 @@ describe('UserFormComponent', () => {
|
|||||||
updateUser: jest.fn(),
|
updateUser: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mockUserService = {
|
mockUserRepo = {
|
||||||
updateUser: jest.fn().mockReturnValue({
|
update: jest.fn(),
|
||||||
subscribe: jest.fn(),
|
getUserById: jest.fn(),
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
@@ -37,9 +37,9 @@ describe('UserFormComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
provideRouter([]),
|
provideRouter([]),
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
|
{ provide: USER_REPOSITORY_TOKEN, useValue: mockUserRepo },
|
||||||
{ provide: ToastrService, useValue: mockToastrService },
|
{ provide: ToastrService, useValue: mockToastrService },
|
||||||
{ provide: AuthService, useValue: mockAuthService },
|
{ provide: AuthService, useValue: mockAuthService },
|
||||||
{ provide: UserService, useValue: mockUserService },
|
|
||||||
],
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Component, inject, Input, OnInit, output } from '@angular/core';
|
import { Component, effect, inject, Input, OnInit, output } from '@angular/core';
|
||||||
import {
|
import {
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
FormControl,
|
FormControl,
|
||||||
@@ -6,33 +6,51 @@ import {
|
|||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
Validators,
|
Validators,
|
||||||
} from '@angular/forms';
|
} from '@angular/forms';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { NgClass } from '@angular/common';
|
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
|
||||||
import { AuthService } from '@app/core/services/authentication/auth.service';
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
|
import { UserFacade } from '@app/ui/users/user.facade';
|
||||||
|
import { ActionType } from '@app/domain/action-type.util';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-user-form',
|
selector: 'app-user-form',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [ReactiveFormsModule, NgClass],
|
imports: [ReactiveFormsModule],
|
||||||
templateUrl: './user-form.component.html',
|
templateUrl: './user-form.component.html',
|
||||||
styleUrl: './user-form.component.scss',
|
styleUrl: './user-form.component.scss',
|
||||||
})
|
})
|
||||||
@UntilDestroy()
|
@UntilDestroy()
|
||||||
export class UserFormComponent implements OnInit {
|
export class UserFormComponent implements OnInit {
|
||||||
private readonly toastrService = inject(ToastrService);
|
private readonly toastrService = inject(ToastrService);
|
||||||
|
private readonly facade = inject(UserFacade);
|
||||||
|
|
||||||
@Input({ required: true }) user: User | undefined = undefined;
|
@Input({ required: true }) user: User | undefined = undefined;
|
||||||
onFormSubmitted = output<any>();
|
onFormSubmitted = output<any>();
|
||||||
|
|
||||||
private userService = inject(UserService);
|
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
|
|
||||||
private fb = inject(FormBuilder);
|
private fb = inject(FormBuilder);
|
||||||
protected userForm!: FormGroup;
|
protected userForm!: FormGroup;
|
||||||
|
|
||||||
|
protected readonly loading = this.facade.loading;
|
||||||
|
protected readonly error = this.facade.error;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
let message = '';
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this.loading().isLoading) {
|
||||||
|
switch (this.loading().action) {
|
||||||
|
case ActionType.UPDATE:
|
||||||
|
this.authService.updateUser();
|
||||||
|
message = `Vos informations personnelles ont bien été modifier !`;
|
||||||
|
this.customToast(ActionType.UPDATE, message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.userForm = this.fb.group({
|
this.userForm = this.fb.group({
|
||||||
firstname: new FormControl(this.user?.name?.split(' ').slice(0, -1).join(' ') ?? '', [
|
firstname: new FormControl(this.user?.name?.split(' ').slice(0, -1).join(' ') ?? '', [
|
||||||
@@ -53,20 +71,33 @@ export class UserFormComponent implements OnInit {
|
|||||||
name: this.userForm.getRawValue()!.firstname! + ' ' + this.userForm.getRawValue()!.name!,
|
name: this.userForm.getRawValue()!.firstname! + ' ' + this.userForm.getRawValue()!.name!,
|
||||||
} as User;
|
} as User;
|
||||||
|
|
||||||
this.userService.updateUser(this.user?.id!, data).subscribe((value) => {
|
this.facade.update(this.user?.id!, data);
|
||||||
this.authService.updateUser();
|
|
||||||
|
|
||||||
this.toastrService.success(
|
this.onFormSubmitted.emit(data);
|
||||||
`Vos informations personnelles ont bien été modifier !`,
|
}
|
||||||
`Mise à jour`,
|
|
||||||
|
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,
|
closeButton: true,
|
||||||
progressAnimation: 'decreasing',
|
progressAnimation: 'decreasing',
|
||||||
progressBar: true,
|
progressBar: true,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.onFormSubmitted.emit(data);
|
this.toastrService.success(
|
||||||
|
`${message}`,
|
||||||
|
`${action === ActionType.UPDATE ? 'Mise à jour' : ''}`,
|
||||||
|
{
|
||||||
|
closeButton: true,
|
||||||
|
progressAnimation: 'decreasing',
|
||||||
|
progressBar: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Component, inject, Input, output } from '@angular/core';
|
import { Component, inject, Input, output } from '@angular/core';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { FormBuilder, FormControl, Validators } from '@angular/forms';
|
import { FormBuilder, FormControl, Validators } from '@angular/forms';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
|
||||||
import { AuthService } from '@app/core/services/authentication/auth.service';
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@@ -15,7 +14,6 @@ export class UserPasswordFormComponent {
|
|||||||
@Input({ required: true }) user: User | undefined = undefined;
|
@Input({ required: true }) user: User | undefined = undefined;
|
||||||
onFormSubmitted = output<any>();
|
onFormSubmitted = output<any>();
|
||||||
|
|
||||||
private userService = inject(UserService);
|
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
|
|
||||||
private fb = inject(FormBuilder);
|
private fb = inject(FormBuilder);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@if (user != undefined) {
|
@if (user() != undefined) {
|
||||||
<a
|
<a
|
||||||
[routerLink]="[user.username ? user.username : user.id]"
|
[routerLink]="[user().username ? user().username : user().id]"
|
||||||
[state]="{ user, profile }"
|
[state]="{ user: user(), profile }"
|
||||||
class="block group"
|
class="block group"
|
||||||
>
|
>
|
||||||
<!-- Card du profil -->
|
<!-- Card du profil -->
|
||||||
@@ -33,18 +33,18 @@
|
|||||||
<!-- Avatar avec bordure gradient -->
|
<!-- Avatar avec bordure gradient -->
|
||||||
<div class="relative inline-block mb-4">
|
<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">
|
<div class="w-32 h-32 rounded-full bg-gradient-to-br from-indigo-500 to-purple-600 p-1">
|
||||||
@if (user.avatar) {
|
@if (user().avatar) {
|
||||||
<img
|
<img
|
||||||
class="w-full h-full rounded-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500 group-hover:scale-105"
|
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="{{ environment.baseUrl }}/api/files/users/{{ user().id }}/{{ user().avatar }}"
|
||||||
alt="{{ user.username }}"
|
alt="{{ user().username }}"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
} @else {
|
} @else {
|
||||||
<img
|
<img
|
||||||
class="w-full h-full rounded-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500 group-hover:scale-105"
|
class="w-full h-full rounded-full object-cover grayscale group-hover:grayscale-0 transition-all duration-500 group-hover:scale-105"
|
||||||
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user.username }}"
|
src="https://api.dicebear.com/9.x/adventurer/svg?seed={{ user().username }}"
|
||||||
alt="{{ user.username }}"
|
alt="{{ user().username }}"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
@@ -52,17 +52,17 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Nom -->
|
<!-- Nom -->
|
||||||
@if (user.name) {
|
@if (user().name) {
|
||||||
<h3
|
<h3
|
||||||
class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
||||||
>
|
>
|
||||||
{{ user.name }}
|
{{ user().name }}
|
||||||
</h3>
|
</h3>
|
||||||
} @else if (user.username) {
|
} @else if (user().username) {
|
||||||
<h3
|
<h3
|
||||||
class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
class="text-lg font-bold text-gray-900 dark:text-white mb-2 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition-colors"
|
||||||
>
|
>
|
||||||
{{ user.username }}
|
{{ user().username }}
|
||||||
</h3>
|
</h3>
|
||||||
} @else {
|
} @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 mentionné</h3>
|
||||||
|
|||||||
@@ -1,14 +1,40 @@
|
|||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { VerticalProfileItemComponent } from './vertical-profile-item.component';
|
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';
|
||||||
|
|
||||||
describe('VerticalProfileItemComponent', () => {
|
describe('VerticalProfileItemComponent', () => {
|
||||||
let component: VerticalProfileItemComponent;
|
let component: VerticalProfileItemComponent;
|
||||||
let fixture: ComponentFixture<VerticalProfileItemComponent>;
|
let fixture: ComponentFixture<VerticalProfileItemComponent>;
|
||||||
|
|
||||||
|
let mockUserRepo: UserRepository;
|
||||||
|
let mockSectorRepo: SectorRepository;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
mockUserRepo = {
|
||||||
|
update: jest.fn().mockReturnValue(of({} as User)),
|
||||||
|
getUserById: jest.fn().mockReturnValue(of({} as User)),
|
||||||
|
};
|
||||||
|
|
||||||
|
mockSectorRepo = {
|
||||||
|
list: jest.fn().mockReturnValue(of({} as Sector)),
|
||||||
|
getOne: jest.fn().mockReturnValue(of({} as Sector)),
|
||||||
|
};
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [VerticalProfileItemComponent],
|
imports: [VerticalProfileItemComponent],
|
||||||
|
providers: [
|
||||||
|
provideRouter([]),
|
||||||
|
{ provide: USER_REPOSITORY_TOKEN, useValue: mockUserRepo },
|
||||||
|
{ provide: SECTOR_REPOSITORY_TOKEN, useValue: mockSectorRepo },
|
||||||
|
],
|
||||||
}).compileComponents();
|
}).compileComponents();
|
||||||
|
|
||||||
fixture = TestBed.createComponent(VerticalProfileItemComponent);
|
fixture = TestBed.createComponent(VerticalProfileItemComponent);
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { Component, inject, Input, OnInit } from '@angular/core';
|
import { Component, inject, Input, OnInit } from '@angular/core';
|
||||||
import { Router, RouterLink } from '@angular/router';
|
import { Router, RouterLink } from '@angular/router';
|
||||||
import { UserService } from '@app/core/services/user/user.service';
|
|
||||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { User } from '@app/shared/models/user';
|
|
||||||
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
import { ChipsComponent } from '@app/shared/components/chips/chips.component';
|
||||||
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
import { ReseauxComponent } from '@app/shared/components/reseaux/reseaux.component';
|
||||||
import { environment } from '@env/environment';
|
import { environment } from '@env/environment';
|
||||||
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
|
import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
|
||||||
|
import { UserFacade } from '@app/ui/users/user.facade';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-vertical-profile-item',
|
selector: 'app-vertical-profile-item',
|
||||||
@@ -19,14 +18,14 @@ import { ProfileViewModel } from '@app/ui/profiles/profile.presenter.model';
|
|||||||
export class VerticalProfileItemComponent implements OnInit {
|
export class VerticalProfileItemComponent implements OnInit {
|
||||||
@Input({ required: true }) profile: ProfileViewModel = {} as ProfileViewModel;
|
@Input({ required: true }) profile: ProfileViewModel = {} as ProfileViewModel;
|
||||||
protected router = inject(Router);
|
protected router = inject(Router);
|
||||||
protected userService = inject(UserService);
|
private readonly facade = inject(UserFacade);
|
||||||
|
|
||||||
protected user: User | undefined = undefined;
|
protected user = this.facade.user;
|
||||||
|
protected readonly loading = this.facade.loading;
|
||||||
|
protected readonly error = this.facade.error;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.userService
|
this.facade.loadOne(this.profile.utilisateur);
|
||||||
.getUserById(this.profile.utilisateur)
|
|
||||||
.subscribe((value) => (this.user = value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected readonly environment = environment;
|
protected readonly environment = environment;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { FormBuilder, FormControl, ReactiveFormsModule, Validators } from '@angu
|
|||||||
import { AuthService } from '@app/core/services/authentication/auth.service';
|
import { AuthService } from '@app/core/services/authentication/auth.service';
|
||||||
import { LoginDto } from '@app/shared/models/login-dto';
|
import { LoginDto } from '@app/shared/models/login-dto';
|
||||||
import { UntilDestroy } from '@ngneat/until-destroy';
|
import { UntilDestroy } from '@ngneat/until-destroy';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { ProgressBarModule } from 'primeng/progressbar';
|
import { ProgressBarModule } from 'primeng/progressbar';
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
import { UserFormComponent } from '@app/shared/components/user-form/user-form.component';
|
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 { UserAvatarFormComponent } from '@app/shared/components/user-avatar-form/user-avatar-form.component';
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { User } from '@app/shared/models/user';
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
export interface Auth {
|
export interface Auth {
|
||||||
isValid: boolean;
|
isValid: boolean;
|
||||||
|
|||||||
63
src/app/ui/users/user.facade.ts
Normal file
63
src/app/ui/users/user.facade.ts
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import { inject, Injectable, signal } from '@angular/core';
|
||||||
|
import { USER_REPOSITORY_TOKEN } from '@app/infrastructure/users/user-repository.token';
|
||||||
|
import { GetUserUseCase } from '@app/usecase/users/get-user.usecase';
|
||||||
|
import { UpdateUserUseCase } from '@app/usecase/users/update-user.usecase';
|
||||||
|
import { LoaderAction } from '@app/domain/loader-action.util';
|
||||||
|
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';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class UserFacade {
|
||||||
|
private readonly userRepository = inject(USER_REPOSITORY_TOKEN);
|
||||||
|
|
||||||
|
private readonly getUseCase = new GetUserUseCase(this.userRepository);
|
||||||
|
private readonly updateUseCase = new UpdateUserUseCase(this.userRepository);
|
||||||
|
|
||||||
|
readonly user = signal<UserViewModel>({} as UserViewModel);
|
||||||
|
readonly users = signal<UserViewModel[]>([]);
|
||||||
|
readonly loading = signal<LoaderAction>({ isLoading: false, action: ActionType.NONE });
|
||||||
|
readonly error = signal<ErrorResponse>({
|
||||||
|
action: ActionType.NONE,
|
||||||
|
hasError: false,
|
||||||
|
message: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
loadOne(userId: string) {
|
||||||
|
this.handleError(ActionType.READ, false, null, true);
|
||||||
|
this.getUseCase.execute(userId).subscribe({
|
||||||
|
next: (user) => {
|
||||||
|
this.user.set(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(user);
|
||||||
|
this.handleError(ActionType.UPDATE, false, null, false);
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.handleError(ActionType.UPDATE, false, err, false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleError(
|
||||||
|
action: ActionType = ActionType.NONE,
|
||||||
|
hasError: boolean,
|
||||||
|
message: string | null = null,
|
||||||
|
isLoading = false
|
||||||
|
) {
|
||||||
|
this.error.set({ action, hasError, message });
|
||||||
|
this.loading.set({ action, isLoading });
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/app/ui/users/user.presenter.model.ts
Normal file
9
src/app/ui/users/user.presenter.model.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export interface UserViewModel {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
verified: boolean;
|
||||||
|
emailVisibility: boolean;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
}
|
||||||
22
src/app/ui/users/user.presenter.ts
Normal file
22
src/app/ui/users/user.presenter.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { UserViewModel } from '@app/ui/users/user.presenter.model';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
|
export class UserPresenter {
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
toViewModel(user: User): UserViewModel {
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
username: user.username,
|
||||||
|
verified: user.verified,
|
||||||
|
emailVisibility: user.emailVisibility,
|
||||||
|
email: user.email,
|
||||||
|
name: user.name,
|
||||||
|
avatar: user.avatar,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
toViewModels(users: User[]): UserViewModel[] {
|
||||||
|
return users.map(this.toViewModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app/usecase/users/get-user.usecase.ts
Normal file
8
src/app/usecase/users/get-user.usecase.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
|
||||||
|
export class GetUserUseCase {
|
||||||
|
constructor(private readonly repo: UserRepository) {}
|
||||||
|
execute(userId: string) {
|
||||||
|
return this.repo.getUserById(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/app/usecase/users/update-user.usecase.ts
Normal file
9
src/app/usecase/users/update-user.usecase.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { UserRepository } from '@app/domain/users/user.repository';
|
||||||
|
import { User } from '@app/domain/users/user.model';
|
||||||
|
|
||||||
|
export class UpdateUserUseCase {
|
||||||
|
constructor(private readonly repo: UserRepository) {}
|
||||||
|
execute(userId: string, user: Partial<User>) {
|
||||||
|
return this.repo.update(userId, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user