diff --git a/frontend/src/app/app.component.html b/frontend/src/app/app.component.html index a3f7082..33795f3 100644 --- a/frontend/src/app/app.component.html +++ b/frontend/src/app/app.component.html @@ -1,2 +1 @@ - - + diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index 18b4400..8208823 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -1,10 +1,12 @@ import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import { HomeComponent } from '@eps/shared/components/home/home.component'; +import { LoginComponent } from './components/auth/login/login.component'; +import { TranslateModule } from '@ngx-translate/core'; @Component({ selector: 'eps-root', standalone: true, - imports: [RouterOutlet], + imports: [HomeComponent, LoginComponent, TranslateModule], templateUrl: './app.component.html', styleUrl: './app.component.scss' }) diff --git a/frontend/src/app/app.config.ts b/frontend/src/app/app.config.ts index c131044..37cf2fe 100644 --- a/frontend/src/app/app.config.ts +++ b/frontend/src/app/app.config.ts @@ -1,10 +1,17 @@ -import { ApplicationConfig, LOCALE_ID} from '@angular/core'; +import { ApplicationConfig, importProvidersFrom } from '@angular/core'; import { provideRouter } from '@angular/router'; import { routes } from './app.routes'; import { provideClientHydration } from '@angular/platform-browser'; import { provideAnimations } from '@angular/platform-browser/animations'; -import { provideHttpClient, withFetch, withInterceptorsFromDi } from '@angular/common/http'; +import { HttpClient, provideHttpClient, withFetch, withInterceptorsFromDi } from '@angular/common/http'; +import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; + +function HttpLoaderFactory(http: HttpClient) { + return new TranslateHttpLoader(http, '../assets/i18n/', '.json'); +} + export const appConfig: ApplicationConfig = { providers: [ @@ -12,6 +19,12 @@ export const appConfig: ApplicationConfig = { provideClientHydration(), provideAnimations(), provideHttpClient(withInterceptorsFromDi(), withFetch()), - { provide: LOCALE_ID, useValue: 'fr-FR' }, + importProvidersFrom(TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: HttpLoaderFactory, + deps: [HttpClient] + } + })) ] }; diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 4781b2b..1191a82 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -1,46 +1,50 @@ import { Routes } from '@angular/router'; -// import { inject } from '@angular/core'; -// import { AuthentificationService } from '@eps/services/auth/authentification.service'; +// import { adminGuard } from './components/auth/admin.guard'; +import { authGuard } from './components/auth/auth.guard'; -import { HomeComponent } from '@eps/shared/components/home/home.component'; -import { NotFoundComponent } from './components/notFound/not-found.component'; +import { LoginComponent } from '@eps/components/auth/login/login.component'; +import { ListProspectsComponent } from './components/list-prospects/list-prospects.component'; +import { DashboardComponent } from './components/dashboard/dashboard.component'; import { UserComponent } from './components/user/user.component'; -import { ListProspectsComponent } from './components/prospects/list-prospects/list-prospects.component'; -// import { LoginComponent } from '@eps/components/auth/login/login.component'; -// import { AuthGuard } from './components/auth/auth.guard'; +import { NotFoundComponent } from './components/notFound/not-found.component'; export const routes: Routes = [ - // { - // path: 'login', - // component: LoginComponent - // }, { path: '', - component: HomeComponent, - // canActivate: [AuthGuard], + redirectTo: '/login', + pathMatch: 'full', + }, + { + path: 'login', + component: LoginComponent, + }, + { + path: 'prospect', + loadComponent: () => + import('./components/list-prospects/list-prospects.component').then(() => ListProspectsComponent) + }, + { + path: 'admin', + canActivate: [authGuard], children: [ - { - path: 'prospect', - component: ListProspectsComponent, - }, { path: 'user', - component: UserComponent, + loadComponent: () => + import('./components/user/user.component').then(() => UserComponent) }, - // { - // path: '', - // canMatch: [() => inject(AuthentificationService).isAuthenticated()], - // }, - - // { - // path: 'register', - // component: UserComponent, - // }, - ], + { + path: 'dashboard', + loadComponent: () => + import('./components/dashboard/dashboard.component').then(() => DashboardComponent) + } + ] }, { path: '**', - component: NotFoundComponent, + loadComponent: () => + import('./components/notFound/not-found.component').then( + () => NotFoundComponent + ), }, ]; diff --git a/frontend/src/app/components/auth/admin.guard.ts b/frontend/src/app/components/auth/admin.guard.ts new file mode 100644 index 0000000..74fd02f --- /dev/null +++ b/frontend/src/app/components/auth/admin.guard.ts @@ -0,0 +1,8 @@ +import type { ActivatedRouteSnapshot, CanActivateFn, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; + +export const adminGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Promise | Observable => { + console.log(route, state); + + return true; +}; diff --git a/frontend/src/app/components/auth/auth.guard.ts b/frontend/src/app/components/auth/auth.guard.ts index 330e7ac..eb84cf7 100644 --- a/frontend/src/app/components/auth/auth.guard.ts +++ b/frontend/src/app/components/auth/auth.guard.ts @@ -1,21 +1,15 @@ -import { inject, Injectable } from '@angular/core'; -// import { ActivatedRouteSnapshot, CanActivate, CanActivateFn, GuardResult, MaybeAsync, Router, RouterStateSnapshot } from '@angular/router'; -import { ActivatedRouteSnapshot, CanActivate, GuardResult, MaybeAsync, Router, RouterStateSnapshot } from '@angular/router'; +import { inject } from "@angular/core"; +import { Router } from "@angular/router"; +import { AuthService } from "@eps/services/auth/auth.service"; -import { AuthService } from '@eps/service/auth/auth.service'; -// import { Observable } from 'rxjs'; -@Injectable({ - providedIn: 'root', -}) -export class AuthGuard implements CanActivate { - canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): MaybeAsync { - console.log(route, state); +export const authGuard = () => { + const auth = inject(AuthService); + const router = inject(Router); - throw new Error('Method not implemented.'); + if (!auth.isAuthAdmin()) { + router.navigate(['/login']); + return false; } - - readonly authService = inject(AuthService); - readonly router = inject(Router); - + return true; } diff --git a/frontend/src/app/components/auth/login/login.component.html b/frontend/src/app/components/auth/login/login.component.html index bbef983..738cb99 100644 --- a/frontend/src/app/components/auth/login/login.component.html +++ b/frontend/src/app/components/auth/login/login.component.html @@ -1,50 +1,29 @@ -
- - - -
- - - - - - - - - - - - - - - - - - - -
- - -
-
- - -
-
- - -
-
-
-
+
+
+
+
+
+ + + + + + + + +
+
+
+
diff --git a/frontend/src/app/components/auth/login/login.component.scss b/frontend/src/app/components/auth/login/login.component.scss index e69de29..913f1ee 100644 --- a/frontend/src/app/components/auth/login/login.component.scss +++ b/frontend/src/app/components/auth/login/login.component.scss @@ -0,0 +1,7 @@ +:host ::ng-deep .pi-eye, +:host ::ng-deep .pi-eye-slash { + transform: scale(1.6); + margin-right: 1rem; + color: var(--primary-color) !important; +} + diff --git a/frontend/src/app/components/auth/login/login.component.ts b/frontend/src/app/components/auth/login/login.component.ts index de1db63..a2457bd 100644 --- a/frontend/src/app/components/auth/login/login.component.ts +++ b/frontend/src/app/components/auth/login/login.component.ts @@ -1,23 +1,51 @@ -import { Component } from "@angular/core"; +import { Component, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { Router } from '@angular/router'; -import { DialogModule } from 'primeng/dialog'; +import { AuthService } from '@eps/services/auth/auth.service'; +import { Role } from '@eps/shared/models/role.model'; +import { User } from '@eps/shared/models/user.model'; import { ButtonModule } from 'primeng/button'; +import { DialogModule } from 'primeng/dialog'; +import { InputTextModule } from 'primeng/inputtext'; +import { PasswordModule } from 'primeng/password'; + @Component({ selector: 'eps-login', templateUrl: 'login.component.html', styleUrl: 'login.component.scss', standalone: true, - imports: [DialogModule, ButtonModule], + imports: [DialogModule, FormsModule, InputTextModule, PasswordModule, ButtonModule], }) export class LoginComponent { - visible: boolean = false; + readonly auth = inject(AuthService); + readonly router = inject(Router); + + loginVisible: boolean = true; - showDialog() { - this.visible = true; + credatials = { + email: '', + password: '' } - closeDialog() { - this.visible = false; + login() { + this.auth.login(this.credatials.email, this.credatials.password).subscribe((user: User) => { + localStorage.setItem('userId', JSON.stringify(user._id)); + localStorage.setItem('firstName', JSON.stringify(user.firstName)); + localStorage.setItem('lastName', JSON.stringify(user.lastName)); + localStorage.setItem('email', JSON.stringify(user.email)); + + user.roles.forEach((role: Role) => { + localStorage.setItem('role', JSON.stringify(role.name)); + }); + + this.loginVisible = false; + console.log(this.auth.getUserId()); + + this.router.navigate(['/prospect']); + }, error => { + console.error(`Login error: ${error}`); + }); } } diff --git a/frontend/src/app/components/dashboard/dashboard.component.css b/frontend/src/app/components/dashboard/dashboard.component.css new file mode 100644 index 0000000..5d4e87f --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.component.css @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/frontend/src/app/components/dashboard/dashboard.component.html b/frontend/src/app/components/dashboard/dashboard.component.html new file mode 100644 index 0000000..3687f84 --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.component.html @@ -0,0 +1,73 @@ +
+
+
Sales Overview
+ +
+ +
+
+
Notifications
+
+ + + +
+
+ + TODAY +
    +
  • +
    + +
    + Richard Jones + has purchased a blue t-shirt for 79$ + +
  • +
  • +
    + +
    + Your request for withdrawal of 2500$ has been initiated. +
  • +
+ + YESTERDAY +
    +
  • +
    + +
    + Keyser Wick + has purchased a black jacket for 59$ + +
  • +
  • +
    + +
    + Jane Davis has posted a new questions about your + product. +
  • +
+
+ +
+
+
TAKE THE NEXT STEP
+
Try PrimeBlocks
+
+ +
diff --git a/frontend/src/app/components/dashboard/dashboard.component.ts b/frontend/src/app/components/dashboard/dashboard.component.ts new file mode 100644 index 0000000..d12f27e --- /dev/null +++ b/frontend/src/app/components/dashboard/dashboard.component.ts @@ -0,0 +1,26 @@ +import { CommonModule } from '@angular/common'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ButtonModule } from 'primeng/button'; +// import { ChartModule } from 'primeng/chart'; +import { MenuModule } from 'primeng/menu'; +import { TableModule } from 'primeng/table'; +import { StyleClassModule } from 'primeng/styleclass'; +import { PanelMenuModule } from 'primeng/panelmenu'; + +@Component({ + selector: 'eps-dashboard', + standalone: true, + imports: [ + CommonModule, + ButtonModule, + // ChartModule, + MenuModule, + TableModule, + StyleClassModule, + PanelMenuModule, +], + template: 'dashboard.component.html', + styleUrl: './dashboard.component.css', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DashboardComponent { } diff --git a/frontend/src/app/components/prospects/list-prospects/list-prospects.component.html b/frontend/src/app/components/list-prospects/list-prospects.component.html similarity index 93% rename from frontend/src/app/components/prospects/list-prospects/list-prospects.component.html rename to frontend/src/app/components/list-prospects/list-prospects.component.html index c72b815..798d922 100644 --- a/frontend/src/app/components/prospects/list-prospects/list-prospects.component.html +++ b/frontend/src/app/components/list-prospects/list-prospects.component.html @@ -1,5 +1,6 @@
- + + - Trigramme - Nom - Prénom - Profil - CV - Date de contact - Date d'entretien - Grille - GO / NOGO - BUM - PR - DC - Push / Qualif - - + Trigramme + Nom + Prénom + Profil + CV + Date de contact + Date d'entretien + Grille + GO / NOGO + BUM + PR + DC + Push / Qualif + + @@ -76,7 +77,7 @@ - + {{ prospect.dateContact | date : "dd-MM-yyyy" }} @@ -86,7 +87,7 @@ - + {{ prospect.dateEntretien | date : "dd-MM-yyyy" }} @@ -119,8 +120,8 @@ } @else { -
- +
@@ -341,7 +342,7 @@
+ (click)="onSave()" type="submit">
diff --git a/frontend/src/app/components/prospects/list-prospects/list-prospects.component.scss b/frontend/src/app/components/list-prospects/list-prospects.component.scss similarity index 100% rename from frontend/src/app/components/prospects/list-prospects/list-prospects.component.scss rename to frontend/src/app/components/list-prospects/list-prospects.component.scss diff --git a/frontend/src/app/components/prospects/list-prospects/list-prospects.component.spec.ts b/frontend/src/app/components/list-prospects/list-prospects.component.spec.ts similarity index 100% rename from frontend/src/app/components/prospects/list-prospects/list-prospects.component.spec.ts rename to frontend/src/app/components/list-prospects/list-prospects.component.spec.ts diff --git a/frontend/src/app/components/prospects/list-prospects/list-prospects.component.ts b/frontend/src/app/components/list-prospects/list-prospects.component.ts similarity index 66% rename from frontend/src/app/components/prospects/list-prospects/list-prospects.component.ts rename to frontend/src/app/components/list-prospects/list-prospects.component.ts index 797aa58..df3aad8 100644 --- a/frontend/src/app/components/prospects/list-prospects/list-prospects.component.ts +++ b/frontend/src/app/components/list-prospects/list-prospects.component.ts @@ -1,25 +1,24 @@ -import { PushQualif } from './../../../shared/models/prospect.model'; import { CommonModule, NgFor, NgIf } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, OnInit, - // signal, + ViewChild, } from '@angular/core'; import { FormsModule, NgForm } from '@angular/forms'; -import { Prospect } from '@eps/shared/models/prospect.model'; +import { newProspect, Prospect, PushQualif } from '@eps/shared/models/prospect.model'; import { User } from '@eps/shared/models/user.model'; import { FileResponse } from '@eps/shared/models/file-response'; -import { ProspectService } from '@eps/service/prospect/prospect.service'; -import { FileService } from '@eps/service/file/file.service'; -import { ConfirmationService, MessageService } from 'primeng/api'; +import { ProspectService } from '@eps/services/prospect/prospect.service'; +import { FileStorageService } from '@eps/services/file-storage/file-storage.service'; +import { ConfirmationService, MessageService, PrimeNGConfig, Translation } from 'primeng/api'; import { BaseIcon } from 'primeng/baseicon'; import { ButtonModule } from 'primeng/button'; -import { CalendarModule } from 'primeng/calendar'; +import { CalendarModule, LocaleSettings } from 'primeng/calendar'; import { ConfirmDialogModule } from 'primeng/confirmdialog'; import { DialogModule } from 'primeng/dialog'; import { FileSelectEvent, FileUploadModule } from 'primeng/fileupload'; @@ -30,11 +29,24 @@ import { ToastModule } from 'primeng/toast'; import { ToolbarModule } from 'primeng/toolbar'; import { DropdownModule } from 'primeng/dropdown'; import { InputTextModule } from 'primeng/inputtext'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; interface expandedRows { [key: string]: boolean; } +interface confirMessage { + target: EventTarget; + message: string; + header: string, + rejectButtonStyleClass: string; + acceptSeverity: string; + acceptSummary: string; + acceptDetail: string; + rejectSeverity: string; + rejectDetail: string; +} + @Component({ selector: 'eps-list-prospects', standalone: true, @@ -54,6 +66,7 @@ interface expandedRows { TagModule, ToolbarModule, ToastModule, + TranslateModule, NgIf, NgFor, ], @@ -66,8 +79,12 @@ export class ListProspectsComponent implements OnInit { // services readonly prospectService = inject(ProspectService); readonly messageService = inject(MessageService); - readonly fileService = inject(FileService); + readonly fileService = inject(FileStorageService); readonly confirmService = inject(ConfirmationService); + readonly translate = inject(TranslateService); + readonly primengConfig = inject(PrimeNGConfig); + + @ViewChild('formProspect') formProspect!: NgForm; // Table prospects: Prospect[] = []; @@ -95,10 +112,30 @@ export class ListProspectsComponent implements OnInit { fileName: string = ''; trigramExist: boolean = false; + locale!: LocaleSettings; + + constructor() { + this.translate.setDefaultLang('fr'); + this.translate.use('fr'); + + this.translate.get('primeng.calendar').subscribe(res => { + this.primengConfig.setTranslation(res as Translation); + this.locale = { + firstDayOfWeek: 1, + dayNames: res.dayNames, + dayNamesShort: res.dayNamesShort, + dayNamesMin: res.dayNamesMin, + monthNames: res.monthNames, + monthNamesShort: res.monthNamesShort, + today: res.today, + clear: res.clear + } + }); + } ngOnInit(): void { this.prospectService.getProspects().subscribe((prospects: Prospect[]) => { - console.warn(prospects); + // console.warn(prospects); this.prospects.push(...prospects); }); } @@ -107,9 +144,11 @@ export class ListProspectsComponent implements OnInit { this.visible = true; } - onSave(newProspect: NgForm) { - if(newProspect.valid) { - const newProspect: Prospect = { + onSave() { + const userId = localStorage.getItem('userId'); + const rhValue = userId ? userId : undefined; + if(this.formProspect.valid) { + const newProspect: newProspect = { firstName: this.firstName, lastName: this.lastName, trigramme: this.trigram, @@ -117,8 +156,12 @@ export class ListProspectsComponent implements OnInit { phone: this.phone.toString(), profil: this.profil, cv: this.cvFile, + rh: rhValue }; + console.log(newProspect); + + this.prospectService.saveProspect(newProspect).subscribe(() => { this.messageService.add({ severity:'success', summary: 'Success', detail: 'Le prospect est ajouté' }); this.visible = false; @@ -128,7 +171,7 @@ export class ListProspectsComponent implements OnInit { generateTrigram() { this.firstName && this.lastName ? - this.trigram = (this.firstName.substring(0,1) + this.lastName.substring(0,1) + this.lastName.substring(this.lastName.length - 1)).toLowerCase() : + this.trigram = (this.firstName.substring(0,1) + this.lastName.substring(0,1) + this.lastName.substring(this.lastName.length - 1)).toLowerCase(): this.trigram = ''; if(this.trigram) { this.checkTrigram(this.trigram); @@ -143,7 +186,6 @@ export class ListProspectsComponent implements OnInit { } fullNameBum(data: User) { - console.log(data); if(data ?? '') { return `${data.firstName} ${data.lastName}`; } else { @@ -152,23 +194,18 @@ export class ListProspectsComponent implements OnInit { } validatedBum(event: Event) { - console.log(event); - this.confirmService.confirm({ + const data: confirMessage = { target: event.target as EventTarget, - message: 'Are you sure that you want to proceed?', + message: 'Souhaitez-vous valider ce propect ?', header: 'Confirmation', - icon: 'pi pi-exclamation-triangle', - acceptIcon:"none", - rejectIcon:"none", rejectButtonStyleClass:"p-button-text", - accept: () => { - this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'You have accepted' }); - }, - reject: () => { - this.messageService.add({ severity: 'error', summary: 'Rejected', detail: 'You have rejected', life: 3000 }); - } - }); - + acceptSeverity: 'success', + acceptSummary: 'Confirmation', + acceptDetail: 'GO pour le prospect', + rejectSeverity: 'error', + rejectDetail: 'Action rejeté.' + }; + this.confirmMessage(data); } onRowEdit(prospect: Prospect) { @@ -195,23 +232,20 @@ export class ListProspectsComponent implements OnInit { archiveProspect(event: Event, prospect: Prospect) { this.editing = false; - console.log(event.target, prospect._id); - this.confirmService.confirm({ + console.log(prospect); + + const data: confirMessage = { target: event.target as EventTarget, message: 'Etès-vous sûre de vouloir archiver ce prospet ?', header: 'Confirmation', - icon: 'pi pi-exclamation-triangle', - acceptIcon:"none", - rejectIcon:"none", rejectButtonStyleClass:"p-button-text", - accept: () => { - this.messageService.add({ severity: 'info', summary: 'Confirmation', detail: 'Vous avez archivé le prospect.' }); - }, - reject: () => { - this.messageService.add({ severity: 'error', summary: 'Rejet', detail: 'You have rejected', life: 3000 }); - } - }); - + acceptSeverity: 'success', + acceptSummary: 'Confirmation', + acceptDetail: 'Vous avez archivé le prospect.', + rejectSeverity: 'error', + rejectDetail: 'Action rejeté' + }; + this.confirmMessage(data); } onUploadFile(event: FileSelectEvent, type: string) { @@ -240,6 +274,24 @@ export class ListProspectsComponent implements OnInit { } } + confirmMessage(data: confirMessage): void { + this.confirmService.confirm({ + target: data.target, + message: data.message, + header: data.header, + icon: 'pi pi-exclamation-triangle', + acceptIcon: 'none', + rejectIcon: 'none', + rejectButtonStyleClass: data.rejectButtonStyleClass, + accept: () => { + this.messageService.add({ severity: data.acceptSeverity, summary: data.acceptSummary, detail: data.acceptDetail }); + }, + reject: () => { + this.messageService.add({ severity: data.rejectSeverity, summary: '', detail: data.rejectDetail, life: 3000 }); + } + }); + } + expandAll() { if (!this.isExpanded) { this.prospects.forEach(prospect => prospect && prospect.pushQualif ? this.expandedRows[prospect.email] = true : ''); @@ -259,4 +311,12 @@ export class ListProspectsComponent implements OnInit { console.log(pushQualif, index); this.expandedEditing = false; } + + // Méthode pour initialiser le calendrier avec la bonne localisation + initializeCalendar(): void { + const calendarElement = document.querySelector('.p-calendar'); + if (calendarElement) { + calendarElement.setAttribute('locale', JSON.stringify(this.locale)); + } + } } diff --git a/frontend/src/app/components/notFound/not-found.component.html b/frontend/src/app/components/notFound/not-found.component.html index 630f05a..d1384c7 100644 --- a/frontend/src/app/components/notFound/not-found.component.html +++ b/frontend/src/app/components/notFound/not-found.component.html @@ -15,9 +15,9 @@

Page inexistante

Retour
- +
diff --git a/frontend/src/app/components/user/user.component.ts b/frontend/src/app/components/user/user.component.ts index fe9982f..6b5d8e5 100644 --- a/frontend/src/app/components/user/user.component.ts +++ b/frontend/src/app/components/user/user.component.ts @@ -1,7 +1,7 @@ import { User } from '@eps/shared/models/user.model'; import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, inject, OnInit } from '@angular/core'; -import { UserService } from '@eps/service/user/user.service'; +import { UserService } from '@eps/services/user/user.service'; import { TableModule } from 'primeng/table'; @Component({ diff --git a/frontend/src/app/directives/if-admin.directive.ts b/frontend/src/app/directives/if-admin.directive.ts new file mode 100644 index 0000000..7a02d44 --- /dev/null +++ b/frontend/src/app/directives/if-admin.directive.ts @@ -0,0 +1,21 @@ +import { NgIf } from '@angular/common'; +import { Directive, inject, OnInit } from '@angular/core'; +import { AuthService } from '@eps/services/auth/auth.service'; +import { IdentityService } from '@eps/services/auth/identity.service'; + +@Directive({ + selector: '[epsIfAdmin]', + standalone: true, + hostDirectives: [{ + directive: NgIf + }] +}) +export class IfAdminDirective implements OnInit { + private identityService = inject(IdentityService); + private authService = inject(AuthService); + private ngIfDirective = inject(NgIf); + + ngOnInit() { + this.ngIfDirective.ngIf = this.authService.isAuthAdmin() && this.identityService.isAdmin(); + } +} diff --git a/frontend/src/app/directives/if-bum.directive.ts b/frontend/src/app/directives/if-bum.directive.ts new file mode 100644 index 0000000..e0c7fd9 --- /dev/null +++ b/frontend/src/app/directives/if-bum.directive.ts @@ -0,0 +1,21 @@ +import { NgIf } from '@angular/common'; +import { Directive, inject, OnInit } from '@angular/core'; +import { AuthService } from '@eps/services/auth/auth.service'; +import { IdentityService } from '@eps/services/auth/identity.service'; + +@Directive({ + selector: '[epsIfBum]', + standalone: true, + hostDirectives: [{ + directive: NgIf + }] +}) +export class IfBumDirective implements OnInit { + private identityService = inject(IdentityService); + private authService = inject(AuthService); + private ngIfDirective = inject(NgIf); + + ngOnInit() { + this.ngIfDirective.ngIf = this.authService.isAuthBum() && this.identityService.isBum(); + } +} diff --git a/frontend/src/app/services/auth/auth.service.ts b/frontend/src/app/services/auth/auth.service.ts index e81e252..9287733 100644 --- a/frontend/src/app/services/auth/auth.service.ts +++ b/frontend/src/app/services/auth/auth.service.ts @@ -1,21 +1,93 @@ -import { Injectable } from "@angular/core"; -// import { Observable } from "rxjs"; +import { HttpClient } from "@angular/common/http"; +import { inject, Injectable } from "@angular/core"; -// import { User } from "@eps/shared/models/user.model"; +import { Observable } from "rxjs"; + +import { environment } from "@eps/env/environment.dev"; +import { User } from "@eps/shared/models/user.model"; @Injectable({ providedIn: 'root' }) export class AuthService { + readonly #http = inject(HttpClient); + // readonly jwtHelper = inject(JwtHelperService); + + apiUrl = `${environment.apiUrl}/user`; - // user$: User = new Observable(); + isAdmin: boolean = false; + isBum: boolean = false; + isRh: boolean = false; + + login(email: string, password: string): Observable { + const body = { email, password }; + return this.#http.post(`${this.apiUrl}/login`, body) + .pipe(); + } - // isAuthenticated(): boolean { - // if - // return true; - // } + logout(): void { + this.isAdmin = false; + this.isBum = false; + this.isRh = false; + localStorage.clear(); + } + + register(user: User): Observable { + const body = { + "firstName": user.firstName, + "lastName": user.lastName, + "email": user.email, + "roles": user.roles, + "password": user.password + }; + return this.#http.post(`${this.apiUrl}/register`, body); + } isLoggedIn(): boolean { - return !!localStorage.getItem('authToken'); + return !!localStorage.getItem('userId'); + } + + isAuthAdmin(): boolean { + const userRole = localStorage.getItem('role'); + userRole ? this.isAdmin = true : this.isAdmin = false; + return this.isAdmin; + } + + isAuthBum(): boolean { + const userRole = localStorage.getItem('role'); + userRole ? this.isBum = true : this.isBum = false; + return this.isBum; + } + + isAuthRh(): boolean { + const userRole = localStorage.getItem('role'); + userRole ? this.isRh = true : this.isRh = false; + return this.isRh; } + + // Récupération des données du localStorage + getUserId(): string | null { + return typeof window !== 'undefined' ? localStorage.getItem('userId') : null; + } + + getEmail(): string | null { + return typeof window !== 'undefined' ? localStorage.getItem('email') : null; + } + + getFirstName(): string | null { + return typeof window !== 'undefined' ? localStorage.getItem('firstName') : null; + } + + getLastName(): string | null { + return typeof window !== 'undefined' ? localStorage.getItem('lastName') : null; + } + + getRoles(): string | null { + return typeof window !== 'undefined' ? localStorage.getItem('roles') : null; + } + + getToken(): string | null { + return typeof window !== 'undefined' ? localStorage.getItem('token') : null; + } + } diff --git a/frontend/src/app/services/auth/identity.service.ts b/frontend/src/app/services/auth/identity.service.ts new file mode 100644 index 0000000..3846840 --- /dev/null +++ b/frontend/src/app/services/auth/identity.service.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { Roles } from '@eps/shared/enums/role'; + +@Injectable({ + providedIn: 'root' +}) +export class IdentityService { + + role = localStorage.getItem('roles'); + + isAdmin(){ + if (this.role !== undefined && this.role !== null) { + this.role === Roles.ADMIN ? true : false; + } + } + + isBum(){ + if (this.role!== undefined && this.role!== null) { + this.role === Roles.BUM ? true : false; + } + } +} diff --git a/frontend/src/app/services/auth/login.service.ts b/frontend/src/app/services/auth/login.service.ts deleted file mode 100644 index 00f49a8..0000000 --- a/frontend/src/app/services/auth/login.service.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { HttpClient } from "@angular/common/http"; -import { inject, Injectable } from "@angular/core"; -import { environment } from "@eps/env/environment.dev"; - -@Injectable({ - providedIn: 'root' -}) -export class LoginService { - readonly #http = inject(HttpClient); - apiUrl = `${environment.apiUrl}/prospect`; -} diff --git a/frontend/src/app/services/file/file.service.spec.ts b/frontend/src/app/services/file-storage/file-storage.service.spec.ts similarity index 68% rename from frontend/src/app/services/file/file.service.spec.ts rename to frontend/src/app/services/file-storage/file-storage.service.spec.ts index 445a925..f8e1afa 100644 --- a/frontend/src/app/services/file/file.service.spec.ts +++ b/frontend/src/app/services/file-storage/file-storage.service.spec.ts @@ -1,17 +1,17 @@ import { HttpClientTestingModule, HttpTestingController } from "@angular/common/http/testing"; import { TestBed } from "@angular/core/testing"; -import { FileService } from "./file.service"; +import { FileStorageService } from "./file-storage.service"; describe('ProspectService', () => { - let service: FileService; + let service: FileStorageService; let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ imports: [HttpClientTestingModule], - providers: [FileService] + providers: [FileStorageService] }); - service = TestBed.inject(FileService); + service = TestBed.inject(FileStorageService); httpMock = TestBed.inject(HttpTestingController); }); }); diff --git a/frontend/src/app/services/file/file.service.ts b/frontend/src/app/services/file-storage/file-storage.service.ts similarity index 96% rename from frontend/src/app/services/file/file.service.ts rename to frontend/src/app/services/file-storage/file-storage.service.ts index 11232e7..bdfa452 100644 --- a/frontend/src/app/services/file/file.service.ts +++ b/frontend/src/app/services/file-storage/file-storage.service.ts @@ -7,7 +7,7 @@ import { Observable } from "rxjs"; @Injectable({ providedIn: 'root' }) -export class FileService { +export class FileStorageService { readonly #http = inject(HttpClient); apiUrl = `${environment.apiUrl}/files`; diff --git a/frontend/src/app/services/prospect/prospect.service.ts b/frontend/src/app/services/prospect/prospect.service.ts index 7f9b5bf..59d089e 100644 --- a/frontend/src/app/services/prospect/prospect.service.ts +++ b/frontend/src/app/services/prospect/prospect.service.ts @@ -29,16 +29,18 @@ export class ProspectService { }); } + // PUT: **/id** + public updateProspect(prospect: Prospect) { + console.log(prospect, prospect._id); + + return this.#http.put(`${this.apiUrl}/${prospect._id}`, prospect); + } + // DELETE: **/delete** public deleteProspect(id: string) { return this.#http.delete(`${this.apiUrl}/delete/${id}`); } - // PUT: **/update** - updateProspect(prospect: Prospect) { - return this.#http.put(`${this.apiUrl}/edit/${prospect._id}`, prospect); - } - // GET: **/trigramme** checkTrigram(trigram: string): Observable { const params = new HttpParams().set('trigramme', trigram); diff --git a/frontend/src/app/shared/components/home/home.component.html b/frontend/src/app/shared/components/home/home.component.html index 038ec82..b039765 100644 --- a/frontend/src/app/shared/components/home/home.component.html +++ b/frontend/src/app/shared/components/home/home.component.html @@ -1,4 +1,3 @@ -
@@ -7,7 +6,7 @@
-->
- +
diff --git a/frontend/src/app/shared/components/home/home.component.ts b/frontend/src/app/shared/components/home/home.component.ts index 034616b..bc909ad 100644 --- a/frontend/src/app/shared/components/home/home.component.ts +++ b/frontend/src/app/shared/components/home/home.component.ts @@ -1,4 +1,3 @@ -import { LayoutService } from './../../../services/layout.service'; import { Component, inject, @@ -6,11 +5,13 @@ import { Renderer2, ViewChild, } from '@angular/core'; +import { LayoutService } from '@eps/services/layout.service'; import { TopbarComponent } from '../topbar/topbar.component'; -import { NavigationEnd, Router, RouterModule } from '@angular/router'; +import { NavigationEnd, Router, RouterModule, RouterOutlet } from '@angular/router'; import { FooterComponent } from '../footer/footer.component'; import { filter, Subscription } from 'rxjs'; import { CommonModule } from '@angular/common'; +import { LoginComponent } from '@eps/components/auth/login/login.component'; @Component({ selector: 'eps-home', @@ -18,10 +19,12 @@ import { CommonModule } from '@angular/common'; styleUrl: 'home.component.scss', standalone: true, imports: [ + RouterOutlet, CommonModule, TopbarComponent, RouterModule, FooterComponent, + LoginComponent ], }) export class HomeComponent implements OnDestroy { diff --git a/frontend/src/app/shared/components/topbar/topbar.component.html b/frontend/src/app/shared/components/topbar/topbar.component.html index 1f1e319..2773acf 100644 --- a/frontend/src/app/shared/components/topbar/topbar.component.html +++ b/frontend/src/app/shared/components/topbar/topbar.component.html @@ -1,39 +1,41 @@
- + - + --> - +
- - - - + pTooltip="Tableau de bord" + tooltipPosition="bottom" + [rounded]="true" + [text]="true" + severity="secondary " + *ifAdmin />
diff --git a/frontend/src/app/shared/components/topbar/topbar.component.ts b/frontend/src/app/shared/components/topbar/topbar.component.ts index b6a946a..91ea93d 100644 --- a/frontend/src/app/shared/components/topbar/topbar.component.ts +++ b/frontend/src/app/shared/components/topbar/topbar.component.ts @@ -1,7 +1,10 @@ import { CommonModule } from '@angular/common'; import { ChangeDetectionStrategy, Component, ElementRef, inject, ViewChild } from '@angular/core'; import { RouterModule } from '@angular/router'; -import { LayoutService } from '@eps/service/layout.service'; + +import { LayoutService } from '@eps/services/layout.service'; +import { IfAdminDirective } from '@eps/directives/if-admin.directive'; + import { MenuItem } from 'primeng/api'; import { ButtonModule } from 'primeng/button'; import { TooltipModule } from 'primeng/tooltip'; @@ -11,7 +14,7 @@ import { TooltipModule } from 'primeng/tooltip'; templateUrl: `topbar.component.html`, styleUrl: 'topbar.component.scss', standalone: true, - imports: [CommonModule, RouterModule, TooltipModule, ButtonModule], + imports: [CommonModule, RouterModule, TooltipModule, ButtonModule, IfAdminDirective], changeDetection: ChangeDetectionStrategy.OnPush, }) export class TopbarComponent { diff --git a/frontend/src/app/shared/enums/role.ts b/frontend/src/app/shared/enums/role.ts new file mode 100644 index 0000000..bffbd59 --- /dev/null +++ b/frontend/src/app/shared/enums/role.ts @@ -0,0 +1,5 @@ +export enum Roles { + ADMIN = "ADMIN", + BUM = "BUM", + RH = "RH", +} diff --git a/frontend/src/app/shared/models/fileStorage.ts b/frontend/src/app/shared/models/fileStorage.ts new file mode 100644 index 0000000..e025f57 --- /dev/null +++ b/frontend/src/app/shared/models/fileStorage.ts @@ -0,0 +1,6 @@ +export interface FileStorage { + _id: string; + filename: string; + contentType: string; + data: []; +} diff --git a/frontend/src/app/shared/models/prospect.model.ts b/frontend/src/app/shared/models/prospect.model.ts index 14863c0..b171d0c 100644 --- a/frontend/src/app/shared/models/prospect.model.ts +++ b/frontend/src/app/shared/models/prospect.model.ts @@ -1,3 +1,4 @@ +import { FileStorage } from "./fileStorage"; import { User } from "./user.model"; export interface Prospect { @@ -11,17 +12,18 @@ export interface Prospect { dateContact?: Date; dateEntretien?: Date statusProspect?: string; - bum?: User; - rh?: User; + bum?: User | string; + rh?: User | string; source?: string; prententionSalarial?: number; niveauEtude?: string; disponibilite?: string; mobiliteGeo?: string; - cv?: string; - grille?: string; + goNogo?: boolean; + cv?: FileStorage | string; + grille?: FileStorage | string; pr?: number; - dc?: string; + dc?: FileStorage | string; pushQualif?: PushQualif; } @@ -31,3 +33,14 @@ export interface PushQualif { datePushRetour: Date; dateQualif: Date; } + +export interface newProspect { + firstName: string; + lastName: string; + email: string; + phone: string; + profil: string; + rh: string | User | undefined; + trigramme: string; + cv: string; +} diff --git a/frontend/src/app/shared/models/role.model.ts b/frontend/src/app/shared/models/role.model.ts new file mode 100644 index 0000000..5b2086c --- /dev/null +++ b/frontend/src/app/shared/models/role.model.ts @@ -0,0 +1,4 @@ +export interface Role { + _id: string; + name: string; +} diff --git a/frontend/src/app/shared/models/user.model.ts b/frontend/src/app/shared/models/user.model.ts index 3d00ab0..430c47a 100644 --- a/frontend/src/app/shared/models/user.model.ts +++ b/frontend/src/app/shared/models/user.model.ts @@ -1,8 +1,12 @@ +import { Role } from "./role.model"; + export interface User { _id: string; firstName: string; lastName: string; email: string; password: string; - role: string; + roles: [Role]; + authorities?: []; + username?: string; } diff --git a/frontend/src/assets/i18n/fr.json b/frontend/src/assets/i18n/fr.json new file mode 100644 index 0000000..6884428 --- /dev/null +++ b/frontend/src/assets/i18n/fr.json @@ -0,0 +1,26 @@ +{ + "primeng": { + "global": { + "accept": "Accepter", + "cancel": "Annuler", + "choose": "Choisir", + "close": "Fermer", + "reject": "Rejeter", + "upload": "Télécharger", + "no": "Non", + "yes": "Oui" + }, + "calendar" : { + "dayNames": ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"], + "dayNamesShort": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"], + "dayNamesMin": ["D", "L", "M", "M", "J", "V", "S"], + "monthNames": ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"], + "monthNamesShort": ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Aoû", "Sep", "Oct", "Nov", "Déc"], + "ariaLabelPrev": "Mois précédent", + "ariaLabelNext": "Mois suivant", + "ariaLabel": "Fermer", + "today": "Aujourd'hui", + "clear": "Effacer" + } + } +} diff --git a/frontend/src/environments/environment.ts b/frontend/src/environments/environment.ts index 335a870..3c243da 100644 --- a/frontend/src/environments/environment.ts +++ b/frontend/src/environments/environment.ts @@ -1,4 +1,4 @@ export const environment = { production: true, - apiUrl: 'http://localhost:8008/api/v1' + apiUrl: 'http://localhost:8089/api/v1' }