diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 94e70d5fa2efc688b648558604b7d6eb036a67e7..d4fdc3edb27a07a4a389b134adedfa2c4afcedb0 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -17,6 +17,9 @@ import { UserVerificationComponent } from './user-verification/user-verification import { NewsletterSubscriptionComponent } from './newsletter-subscription/newsletter-subscription.component'; import { OrientationFormComponent } from './form/orientation-form/orientation-form.component'; import { StructureListPrintComponent } from './form/orientation-form/component/structure-list-print/structure-list-print.component'; +import { StructureResolver } from './resolvers/structure.resolver'; +import { RoleGuard } from './guards/role.guard'; +import { RouteRole } from './shared/enum/routeRole.enum'; const routes: Routes = [ { path: 'print', outlet: 'print', children: [{ path: 'structure', component: StructureDetailsComponent }] }, @@ -80,6 +83,16 @@ const routes: Routes = [ component: FormComponent, canDeactivate: [DeactivateGuard], }, + { + path: 'create-structure/:id', + component: FormComponent, + canDeactivate: [DeactivateGuard], + canActivate: [RoleGuard], + data: { allowedRoles: [RouteRole.structureAdmin] }, + resolve: { + structure: StructureResolver, + }, + }, { path: 'newsletter', component: NewsletterSubscriptionComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e10d31c7b1227d9cebcc0610f67ff941e21231d9..4111e2b27575bf8c920a36f80997213fcf1d0f24 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -23,10 +23,8 @@ import { FormComponent } from './form/structure-form/form.component'; import { UserVerificationComponent } from './user-verification/user-verification.component'; import { AuthGuard } from './guards/auth.guard'; import { CustomHttpInterceptor } from './config/http-interceptor'; -import { ProfileModule } from './profile/profile.module'; import { ResetEmailComponent } from './reset-email/reset-email.component'; import { ResetPasswordComponent } from './reset-password/reset-password.component'; -import { AdminModule } from './admin/admin.module'; import { AdminGuard } from './guards/admin.guard'; import { DeactivateGuard } from './guards/deactivate.guard'; import { FooterFormComponent } from './form/footer-form/footer-form.component'; @@ -38,6 +36,8 @@ import { OrientationFormComponent } from './form/orientation-form/orientation-fo import { StructureDetailPrintComponent } from './form/orientation-form/component/structure-detail-print/structure-detail-print.component'; import { StructureListPrintComponent } from './form/orientation-form/component/structure-list-print/structure-list-print.component'; import { StructurePrintHeaderComponent } from './form/orientation-form/component/structure-print-header/structure-print-header.component'; +import { StructureResolver } from './resolvers/structure.resolver'; +import { RoleGuard } from './guards/role.guard'; @NgModule({ declarations: [ @@ -72,8 +72,10 @@ import { StructurePrintHeaderComponent } from './form/orientation-form/component CustomBreakPointsProvider, AuthGuard, AdminGuard, + RoleGuard, DeactivateGuard, TempUserResolver, + StructureResolver, RouterListenerService, ], bootstrap: [AppComponent], diff --git a/src/app/form/structure-form/form.component.html b/src/app/form/structure-form/form.component.html index 455d10040dc4795f1d331caa5c9e63199f6c9677..00cffee51407c90db4902b2f50e22dba2723523e 100644 --- a/src/app/form/structure-form/form.component.html +++ b/src/app/form/structure-form/form.component.html @@ -88,13 +88,21 @@ [ngClass]="{ last: index == 22 }" fxLayout="row" fxLayoutAlign="space-between center" - *ngIf="page.name" + *ngIf="page.name && shouldDisplayPage(index)" (click)="goToSpecificPage(index, false)" > {{ page.name }} - <svg class="chevronRight" aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> - </svg> + <div fxLayout="row" fxLayoutAlign="space-between center"> + <app-svg-icon + *ngIf="!updatePageValid(index, page)" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + <svg class="chevronRight" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> + </svg> + </div> </div> </div> </div> diff --git a/src/app/form/structure-form/form.component.ts b/src/app/form/structure-form/form.component.ts index 93dbede129624581b65a8a18b2206058f72f54eb..c02e8fe48e2ccf34fd6b10f310557962a3286cd2 100644 --- a/src/app/form/structure-form/form.component.ts +++ b/src/app/form/structure-form/form.component.ts @@ -79,6 +79,9 @@ export class FormComponent implements OnInit { public isPopUpOpen = false; public displaySignUp = true; + // Structure id for edit mode + public structureId: string; + constructor( private structureService: StructureService, private searchService: SearchService, @@ -98,15 +101,7 @@ export class FormComponent implements OnInit { await this.setCategories(); // Check if it's a new structure or edit structure this.isLoading = false; - if (history.state.data) { - this.isEditMode = true; - this.isWifiChoosen = true; - const editStructure = new Structure(history.state.data); - this.initForm(editStructure); - this.structureService.getStructureWithOwners(editStructure._id, this.profile).subscribe((s) => { - this.structureWithOwners = s; - }); - } else if (history.state.newUser) { + if (history.state.newUser) { this.isClaimMode = true; // Handle join strucutre, the case is very similar to claim if (history.state.isJoin) { @@ -127,6 +122,15 @@ export class FormComponent implements OnInit { this.setValidationsForm(); this.currentPage = PageTypeEnum.accountInfo; } + if (data.structure) { + this.isEditMode = true; + this.isWifiChoosen = true; + const editStructure = new Structure(data.structure); + this.initForm(editStructure); + this.structureService.getStructureWithOwners(editStructure._id, this.profile).subscribe((s) => { + this.structureWithOwners = s; + }); + } }); } @@ -596,8 +600,16 @@ export class FormComponent implements OnInit { } } - private updatePageValid(): void { + /** + * Update valid page or return page validity of the given index + * @param {number} [index] - Page index + */ + private updatePageValid(index?: number): boolean { + if (index) { + return this.pagesValidation[index].valid; + } this.isPageValid = this.pagesValidation[this.currentPage].valid; + return this.isPageValid; } /** @@ -898,16 +910,18 @@ export class FormComponent implements OnInit { if (this.getStructureControl('freeWorkShop').value === null) { this.getStructureControl('freeWorkShop').setValue(false); } - if (this.structureForm.valid && this.hoursForm.valid) { - const structure: Structure = this.structureForm.value; - structure.hours = this.hoursForm.value; - let user: User; - if (this.isEditMode) { - this.structureService.editStructure(structure).subscribe((s: Structure) => { - this.createdStructure = this.structureService.updateOpeningStructure(s); - this.editForm = this.createStructureForm(s); - }); - } else { + const structure: Structure = this.structureForm.value; + structure.hours = this.hoursForm.value; + let user: User; + // If edit mode, update setbystep + if (this.isEditMode) { + this.structureService.editStructure(structure).subscribe((s: Structure) => { + this.createdStructure = this.structureService.updateOpeningStructure(s); + this.editForm = this.createStructureForm(s); + }); + } else { + if (this.structureForm.valid && this.hoursForm.valid) { + // For creation mode, check structure validity if (this.profile) { user = this.profile; structure.accountVerified = true; @@ -1003,4 +1017,13 @@ export class FormComponent implements OnInit { public structureDeleted(): void { this.router.navigateByUrl('acteurs'); } + + public shouldDisplayPage(index: number): boolean { + // handle OtherAccompaniment + if (index == this.pageTypeEnum.structureOtherAccompaniment) { + if (this.structureForm.value.proceduresAccompaniment.includes('autres')) return true; + else return false; + } + return true; + } } diff --git a/src/app/guards/role.guard.ts b/src/app/guards/role.guard.ts new file mode 100644 index 0000000000000000000000000000000000000000..86ebe58ea651ba0fdf797bde84f670a03f65ab2d --- /dev/null +++ b/src/app/guards/role.guard.ts @@ -0,0 +1,29 @@ +import { ActivatedRouteSnapshot, CanActivate, Router, UrlTree } from '@angular/router'; +import { Injectable } from '@angular/core'; +import { ProfileService } from '../profile/services/profile.service'; +import { AuthService } from '../services/auth.service'; +import { RouteRole } from '../shared/enum/routeRole.enum'; +/** + * Guard to assert that a user is authorized to access route. + * Admin can access everything + */ +@Injectable() +export class RoleGuard implements CanActivate { + constructor(private router: Router, private profileService: ProfileService, private authService: AuthService) {} + + canActivate(route: ActivatedRouteSnapshot): UrlTree | boolean { + const allowedRoles = route.data['allowedRoles']; + + if (this.authService.isLoggedIn()) { + if (this.profileService.isAdmin()) return true; + if (allowedRoles.includes(RouteRole.structureAdmin)) { + const structureId = route.params.id; + if (this.profileService.isLinkedToStructure(structureId)) { + return true; + } + } + return this.router.parseUrl('/home'); + } + return this.router.parseUrl('/home'); + } +} diff --git a/src/app/resolvers/structure.resolver.ts b/src/app/resolvers/structure.resolver.ts new file mode 100644 index 0000000000000000000000000000000000000000..3eb41ce252aca52ff2c6b7e2909e0f1f86e0ae78 --- /dev/null +++ b/src/app/resolvers/structure.resolver.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { Structure } from '../models/structure.model'; +import { StructureService } from '../services/structure.service'; + +@Injectable() +export class StructureResolver implements Resolve<Structure> { + constructor(private structureService: StructureService, private router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable<Structure> { + const structureId = route.params.id; + console.log(route); + console.info(`Resolver: ${structureId}`); + return this.structureService.getStructure(structureId).pipe( + map((res) => res), + catchError(() => { + this.router.navigate(['/home']); + return new Observable<Structure>(); + }) + ); + } +} diff --git a/src/app/shared/enum/routeRole.enum.ts b/src/app/shared/enum/routeRole.enum.ts new file mode 100644 index 0000000000000000000000000000000000000000..924eb2c4181c5beb9f81faea52072cb1c2673088 --- /dev/null +++ b/src/app/shared/enum/routeRole.enum.ts @@ -0,0 +1,3 @@ +export enum RouteRole { + structureAdmin = 'structureAdmin', +} diff --git a/src/app/structure-list/components/structure-details/structure-details.component.html b/src/app/structure-list/components/structure-details/structure-details.component.html index 14d8b2826cb1b8af9faea2342dbe3d3c02308bea..f4b137831ed91a16ad78e0f20ee05c7a0be4a62e 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.html +++ b/src/app/structure-list/components/structure-details/structure-details.component.html @@ -146,8 +146,7 @@ <!-- temporary remove edit --> <a *ngIf="profileService.isLinkedToStructure(structure._id) || profileService.isAdmin()" - routerLink="/create-structure" - [state]="{ data: structure }" + [routerLink]="['/create-structure', structure._id]" class="primary" tabindex="0" > @@ -187,7 +186,9 @@ <h4>{{ day.key | day }}</h4> <div class="opening-time" fxLayout="row" fxLayoutAlign="none flex-end"> <div *ngFor="let timeRange of day.value.time; let isFirst = first"> - <p *ngIf="isFirst && timeRange.opening">de {{ timeRange.formatOpeningDate() }} Ã {{ timeRange.formatClosingDate() }}</p> + <p *ngIf="isFirst && timeRange.opening"> + de {{ timeRange.formatOpeningDate() }} Ã {{ timeRange.formatClosingDate() }} + </p> <p *ngIf="!isFirst && timeRange.opening"> et de {{ timeRange.formatOpeningDate() }} Ã {{ timeRange.formatClosingDate() }} </p>