From 637590750aa921e625bbe77c879ba194131b62c0 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 16 Feb 2021 10:49:18 +0100 Subject: [PATCH 1/3] fix: map marker color + stroke-width --- src/app/map/components/map.component.ts | 4 ++-- src/assets/ico/sprite.svg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts index ad2bba78c..b5d66dd81 100644 --- a/src/app/map/components/map.component.ts +++ b/src/app/map/components/map.component.ts @@ -245,7 +245,7 @@ export class MapComponent implements OnChanges { type: brignais.features[0].geometry.type, coordinates: brignais.features[0].geometry.coordinates, } as any, - { style: () => ({ color: '#d50000', fillOpacity: 0 }) } + { style: () => ({ color: '#a00000', fillOpacity: 0, weight: 1 }) } ) ); } @@ -257,7 +257,7 @@ export class MapComponent implements OnChanges { type: metropole.features[0].geometry.type, coordinates: metropole.features[0].geometry.coordinates, } as any, - { style: () => ({ color: '#d50000', fillOpacity: 0 }) } + { style: () => ({ color: '#a00000', fillOpacity: 0, weight: 1 }) } ) ); } diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index b0ea45840..08676f739 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -1,6 +1,6 @@ <svg xmlns="http://www.w3.org/2000/svg"> <symbol id="map-marker" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M19.72 43.73l.706.66.683-.683c2.038-2.04 4.04-3.864 5.934-5.588l.179-.163c6.32-5.755 11.624-10.585 11.624-18.493C38.846 9.267 30.59 1 20.402 1 10.214 1 1.957 9.267 1.957 19.463c0 4.152 1.08 7.233 3.179 10.152 2.04 2.84 5.05 5.523 8.833 8.899l.078.07c1.717 1.531 3.607 3.217 5.672 5.147zm6.508-24.267a5.83 5.83 0 01-5.826 5.833 5.83 5.83 0 01-5.826-5.833 5.83 5.83 0 015.826-5.833 5.83 5.83 0 015.826 5.833z"/></symbol> -<symbol id="map-marker-locate" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M19.72 43.73l.706.66.683-.683c2.038-2.04 4.04-3.864 5.934-5.588l.179-.163c6.32-5.755 11.624-10.585 11.624-18.493C38.846 9.267 30.59 1 20.402 1 10.214 1 1.957 9.267 1.957 19.463c0 4.152 1.08 7.233 3.179 10.152 2.04 2.84 5.05 5.523 8.833 8.899l.078.07c1.717 1.531 3.607 3.217 5.672 5.147zm6.508-24.267a5.83 5.83 0 01-5.826 5.833 5.83 5.83 0 01-5.826-5.833 5.83 5.83 0 015.826-5.833 5.83 5.83 0 015.826 5.833z" fill="#117083" stroke="#fff" stroke-width="2"/></symbol> +<symbol id="map-marker-locate" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg"><path d="M19.72 43.73l.706.66.683-.683c2.038-2.04 4.04-3.864 5.934-5.588l.179-.163c6.32-5.755 11.624-10.585 11.624-18.493C38.846 9.267 30.59 1 20.402 1 10.214 1 1.957 9.267 1.957 19.463c0 4.152 1.08 7.233 3.179 10.152 2.04 2.84 5.05 5.523 8.833 8.899l.078.07c1.717 1.531 3.607 3.217 5.672 5.147zm6.508-24.267a5.83 5.83 0 01-5.826 5.833 5.83 5.83 0 01-5.826-5.833 5.83 5.83 0 015.826-5.833 5.83 5.83 0 015.826 5.833z" fill="#a00000" stroke="#fff" stroke-width="2"/></symbol> <symbol id="adress" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> <path fill-rule="evenodd" clip-rule="evenodd" d="M11 2C12.6055 2.0145 14.1397 2.68885 15.265 3.87463C16.3902 5.06042 17.0142 6.66048 16.9998 8.32269C16.9998 11.8208 12.1242 19 11 19C9.87584 19 5.00025 11.8208 5.00025 8.32269C4.98578 6.66048 5.60982 5.06042 6.73504 3.87463C7.86026 2.68885 9.39446 2.0145 11 2ZM10.9999 5.55695C12.0865 5.53677 13.0768 6.19906 13.5059 7.23274C13.9349 8.26643 13.7173 9.4661 12.9553 10.2683C12.1933 11.0704 11.0384 11.3157 10.0329 10.8888C9.02744 10.4619 8.37129 9.44779 8.37266 8.32272C8.36215 6.80858 9.53743 5.57133 10.9999 5.55695Z" fill="#333333"/> -- GitLab From 60fa0c8e41df9c82e5d6957dd752e4c210c4c9c8 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 16 Feb 2021 12:07:38 +0100 Subject: [PATCH 2/3] feat: add registration form from email --- src/app/app-routing.module.ts | 9 ++ src/app/app.module.ts | 2 + src/app/form/form.component.ts | 108 ++++++++++++++++++------ src/app/models/temp-user.model.ts | 7 ++ src/app/resolvers/temp-user.resolver.ts | 22 +++++ src/app/services/temp-user.service.ts | 16 ++++ 6 files changed, 137 insertions(+), 27 deletions(-) create mode 100644 src/app/models/temp-user.model.ts create mode 100644 src/app/resolvers/temp-user.resolver.ts create mode 100644 src/app/services/temp-user.service.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 993557771..9f29fd079 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -11,6 +11,7 @@ import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; import { ProfileComponent } from './profile/profile.component'; import { ResetEmailComponent } from './reset-email/reset-email.component'; import { ResetPasswordComponent } from './reset-password/reset-password.component'; +import { TempUserResolver } from './resolvers/temp-user.resolver'; import { StructureDetailsComponent } from './structure-list/components/structure-details/structure-details.component'; import { StructureListComponent } from './structure-list/structure-list.component'; import { UserVerificationComponent } from './user-verification/user-verification.component'; @@ -49,6 +50,14 @@ const routes: Routes = [ path: 'users/verify/:id', component: UserVerificationComponent, }, + { + path: 'register', + component: FormComponent, + canDeactivate: [DeactivateGuard], + resolve: { + user: TempUserResolver, + }, + }, { path: 'change-email/:id', component: ResetEmailComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 76b6da88f..25507d5d2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -30,6 +30,7 @@ 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'; +import { TempUserResolver } from './resolvers/temp-user.resolver'; @NgModule({ declarations: [ @@ -59,6 +60,7 @@ import { FooterFormComponent } from './form/footer-form/footer-form.component'; AuthGuard, AdminGuard, DeactivateGuard, + TempUserResolver, ], bootstrap: [AppComponent], }) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index a09015170..a5547fc23 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -18,6 +18,7 @@ import { AuthService } from '../services/auth.service'; import { first } from 'rxjs/operators'; import { Regex } from '../shared/enum/regex.enum'; import { PageTypeEnum } from './pageType.enum'; +import { TempUserService } from '../services/temp-user.service'; const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', @@ -42,6 +43,7 @@ export class FormComponent implements OnInit { public trainingCategories: { category: Category; openned: boolean }[] = []; public pageTypeEnum = PageTypeEnum; public claimStructure: Structure = null; + public linkedStructureId: Array<string> = null; // Page and progress var public currentPage = 0; @@ -67,6 +69,7 @@ export class FormComponent implements OnInit { public showMenu = false; public isEditMode = false; public isClaimMode = false; + public isAccountMode = false; public isLoading = false; public isWifiChoosen = false; @@ -75,7 +78,9 @@ export class FormComponent implements OnInit { private searchService: SearchService, private profileService: ProfileService, private authService: AuthService, - private router: Router + private tempUserService: TempUserService, + private router: Router, + private route: ActivatedRoute ) {} async ngOnInit(): Promise<void> { @@ -98,6 +103,16 @@ export class FormComponent implements OnInit { } else { this.initForm(new Structure()); } + // Handle account creation when pre-register + this.route.data.subscribe((data) => { + if (data.user) { + this.isAccountMode = true; + this.createAccountForm(data.user.email, data.user.name, data.user.surname); + this.linkedStructureId = data.user.pendingStructuresLink; + this.setValidationsForm(); + this.currentPage = PageTypeEnum.accountInfo; + } + }); } async setCategories(): Promise<void> { @@ -162,13 +177,16 @@ export class FormComponent implements OnInit { this.setValidationsForm(); } - private createAccountForm(): void { + private createAccountForm(email?: string, name?: string, surname?: string): void { this.accountForm = new FormGroup( { - email: new FormControl('', [Validators.required, Validators.pattern(Regex.email)]), //NOSONAR - name: new FormControl('', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), //NOSONAR - surname: new FormControl('', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), //NOSONAR - phone: new FormControl('', [Validators.required, Validators.pattern(Regex.phone)]), //NOSONAR + email: new FormControl(email ? email : '', [Validators.required, Validators.pattern(Regex.email)]), + name: new FormControl(name ? name : '', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), + surname: new FormControl(surname ? surname : '', [ + Validators.required, + Validators.pattern(Regex.textWithoutNumber), + ]), + phone: new FormControl('', [Validators.required, Validators.pattern(Regex.phone)]), password: new FormControl('', [ Validators.required, Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR @@ -192,19 +210,13 @@ export class FormComponent implements OnInit { street: new FormControl(structure.address.street, Validators.required), commune: new FormControl(structure.address.commune, Validators.required), }), - contactMail: new FormControl(structure.contactMail, [ - Validators.required, - Validators.pattern(Regex.email), //NOSONAR - ]), - contactPhone: new FormControl(structure.contactPhone, [ - Validators.required, - Validators.pattern(Regex.phone), //NOSONAR - ]), - website: new FormControl(structure.website, Validators.pattern(Regex.website)), //NOSONAR - facebook: new FormControl(structure.facebook, Validators.pattern(Regex.facebook)), //NOSONAR - twitter: new FormControl(structure.twitter, Validators.pattern(Regex.twitter)), //NOSONAR - instagram: new FormControl(structure.instagram, Validators.pattern(Regex.instagram)), //NOSONAR - linkedin: new FormControl(structure.linkedin, Validators.pattern(Regex.linkedIn)), //NOSONAR + contactMail: new FormControl(structure.contactMail, [Validators.required, Validators.pattern(Regex.email)]), + contactPhone: new FormControl(structure.contactPhone, [Validators.required, Validators.pattern(Regex.phone)]), + website: new FormControl(structure.website, Validators.pattern(Regex.website)), + facebook: new FormControl(structure.facebook, Validators.pattern(Regex.facebook)), + twitter: new FormControl(structure.twitter, Validators.pattern(Regex.twitter)), + instagram: new FormControl(structure.instagram, Validators.pattern(Regex.instagram)), + linkedin: new FormControl(structure.linkedin, Validators.pattern(Regex.linkedIn)), hours: new FormGroup({}), pmrAccess: new FormControl(structure.pmrAccess, Validators.required), exceptionalClosures: new FormControl(structure.exceptionalClosures), @@ -222,24 +234,24 @@ export class FormComponent implements OnInit { digitalCultureSecurity: this.loadArrayForCheckbox(structure.digitalCultureSecurity, false), nbComputers: new FormControl( structure.equipmentsAndServices.includes('ordinateurs') ? structure.nbComputers : 1, - [Validators.required, Validators.pattern(Regex.noNullNumber)] //NOSONAR + [Validators.required, Validators.pattern(Regex.noNullNumber)] ), nbPrinters: new FormControl(structure.equipmentsAndServices.includes('imprimantes') ? structure.nbPrinters : 1, [ Validators.required, - Validators.pattern(Regex.noNullNumber), //NOSONAR + Validators.pattern(Regex.noNullNumber), ]), nbTablets: new FormControl(structure.equipmentsAndServices.includes('tablettes') ? structure.nbTablets : 1, [ Validators.required, - Validators.pattern(Regex.noNullNumber), //NOSONAR + Validators.pattern(Regex.noNullNumber), ]), nbNumericTerminal: new FormControl( structure.equipmentsAndServices.includes('bornesNumeriques') ? structure.nbNumericTerminal : 1, - [Validators.required, Validators.pattern(Regex.noNullNumber)] //NOSONAR - ), - nbScanners: new FormControl( - structure.equipmentsAndServices.includes('scanners') ? structure.nbScanners : 1, - [Validators.required, Validators.pattern(Regex.noNullNumber)] //NOSONAR + [Validators.required, Validators.pattern(Regex.noNullNumber)] ), + nbScanners: new FormControl(structure.equipmentsAndServices.includes('scanners') ? structure.nbScanners : 1, [ + Validators.required, + Validators.pattern(Regex.noNullNumber), + ]), freeWorkShop: new FormControl(structure.freeWorkShop, Validators.required), }); return form; @@ -398,6 +410,21 @@ export class FormComponent implements OnInit { }; this.pagesValidation[PageTypeEnum.cgu] = { valid: this.userAcceptSavedDate }; this.updatePageValid(); + } else if (this.isAccountMode) { + this.pagesValidation[PageTypeEnum.accountInfo] = { + valid: + this.accountForm.get('surname').valid && + this.accountForm.get('name').valid && + this.accountForm.get('phone').valid, + }; + this.pagesValidation[PageTypeEnum.accountCredentials] = { + valid: + this.accountForm.get('email').valid && + this.accountForm.get('password').valid && + this.accountForm.get('confirmPassword').valid, + }; + this.pagesValidation[PageTypeEnum.cgu] = { valid: this.userAcceptSavedDate }; + this.updatePageValid(); } else { this.pagesValidation[PageTypeEnum.summary] = { valid: true }; this.pagesValidation[PageTypeEnum.info] = { valid: true }; @@ -544,6 +571,31 @@ export class FormComponent implements OnInit { this.progressStatus += 25; } + /** + * Page algo for create account case + */ + public nextPageAccount(): void { + if (this.currentPage == this.nbPagesForm - 1) { + const user = new User(this.accountForm.value); + // Create user with structure + user.structuresLink = this.linkedStructureId; + this.authService.register(user).subscribe(() => { + this.progressStatus = 100; + }); + } + + if (this.currentPage == PageTypeEnum.accountInfo) { + this.currentPage = PageTypeEnum.accountCredentials; + this.updatePageValid(); + } else if (this.currentPage == PageTypeEnum.accountCredentials) { + this.currentPage = PageTypeEnum.cgu; + this.updatePageValid(); + } else if (this.currentPage == PageTypeEnum.cgu) { + this.currentPage = this.nbPagesForm; + } + + this.progressStatus += 25; + } /** * Page algo for claim structure case @@ -566,6 +618,8 @@ export class FormComponent implements OnInit { public nextPage(): void { if (this.isClaimMode) { this.nextPageClaim(); + } else if (this.isAccountMode) { + this.nextPageAccount(); } else { // Check if user already connected to skip accountForm pages. if (this.currentPage == PageTypeEnum.info && this.profile) { diff --git a/src/app/models/temp-user.model.ts b/src/app/models/temp-user.model.ts new file mode 100644 index 000000000..5a487980f --- /dev/null +++ b/src/app/models/temp-user.model.ts @@ -0,0 +1,7 @@ +export class TempUser { + _id: string; + email: string; + name: string; + surname: string; + pendingStructuresLink: string[]; +} diff --git a/src/app/resolvers/temp-user.resolver.ts b/src/app/resolvers/temp-user.resolver.ts new file mode 100644 index 000000000..2ae604aa6 --- /dev/null +++ b/src/app/resolvers/temp-user.resolver.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, Resolve, Router } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map, catchError } from 'rxjs/operators'; +import { TempUser } from '../models/temp-user.model'; +import { TempUserService } from '../services/temp-user.service'; + +@Injectable() +export class TempUserResolver implements Resolve<TempUser> { + constructor(private tempUserService: TempUserService, private router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable<TempUser> { + const userId = route.queryParams.id; + return this.tempUserService.getUser(userId).pipe( + map((res) => res), + catchError(() => { + this.router.navigate(['/home']); + return new Observable<TempUser>(); + }) + ); + } +} diff --git a/src/app/services/temp-user.service.ts b/src/app/services/temp-user.service.ts new file mode 100644 index 000000000..4dd2b4524 --- /dev/null +++ b/src/app/services/temp-user.service.ts @@ -0,0 +1,16 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { TempUser } from '../models/temp-user.model'; + +@Injectable({ + providedIn: 'root', +}) +export class TempUserService { + private readonly baseUrl = 'api/temp-user'; + constructor(private http: HttpClient) {} + + public getUser(id: string): Observable<TempUser> { + return this.http.get<TempUser>(`${this.baseUrl}/${id}`); + } +} -- GitLab From f5abd5e5fea66c8e6d04aee335be93da9976b3e7 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 17 Feb 2021 10:58:41 +0100 Subject: [PATCH 3/3] feat: add connect redirection + handle structure join --- src/app/app-routing.module.ts | 6 ++++ src/app/app.module.ts | 2 ++ src/app/form/form.component.html | 4 +-- src/app/form/form.component.scss | 3 ++ src/app/form/form.component.ts | 35 +++++++++++++++++-- src/app/guards/auth.guard.ts | 5 +-- src/app/header/header.component.ts | 32 ++++++++++++++--- src/app/services/structure.service.ts | 8 +++++ .../structure-join.component.html | 6 ++++ .../structure-join.component.scss | 0 .../structure-join.component.spec.ts | 25 +++++++++++++ .../structure-join.component.ts | 33 +++++++++++++++++ .../structure-details.component.html | 9 +++++ .../structure-details.component.ts | 28 +++++++++++++++ 14 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 src/app/structure-join/structure-join.component.html create mode 100644 src/app/structure-join/structure-join.component.scss create mode 100644 src/app/structure-join/structure-join.component.spec.ts create mode 100644 src/app/structure-join/structure-join.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 9f29fd079..045be83e7 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -12,6 +12,7 @@ import { ProfileComponent } from './profile/profile.component'; import { ResetEmailComponent } from './reset-email/reset-email.component'; import { ResetPasswordComponent } from './reset-password/reset-password.component'; import { TempUserResolver } from './resolvers/temp-user.resolver'; +import { StructureJoinComponent } from './structure-join/structure-join.component'; import { StructureDetailsComponent } from './structure-list/components/structure-details/structure-details.component'; import { StructureListComponent } from './structure-list/structure-list.component'; import { UserVerificationComponent } from './user-verification/user-verification.component'; @@ -67,6 +68,11 @@ const routes: Routes = [ canActivate: [AuthGuard], component: ProfileComponent, }, + { + path: 'join', + canActivate: [AuthGuard], + component: StructureJoinComponent, + }, { path: 'reset-password', component: ResetPasswordComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 25507d5d2..8db542e8f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -31,6 +31,7 @@ import { AdminGuard } from './guards/admin.guard'; import { DeactivateGuard } from './guards/deactivate.guard'; import { FooterFormComponent } from './form/footer-form/footer-form.component'; import { TempUserResolver } from './resolvers/temp-user.resolver'; +import { StructureJoinComponent } from './structure-join/structure-join.component'; @NgModule({ declarations: [ @@ -51,6 +52,7 @@ import { TempUserResolver } from './resolvers/temp-user.resolver'; ResetPasswordComponent, FormComponent, FooterFormComponent, + StructureJoinComponent, ], imports: [BrowserModule, HttpClientModule, AppRoutingModule, SharedModule, MapModule, ProfileModule, AdminModule], providers: [ diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index f1b1929d9..c4b68351b 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -53,8 +53,8 @@ fxLayoutAlign="space-between center" fxLayoutAlign.lt-sm="center" > - <h2> - Revendiquer la structure <span>{{ claimStructure.structureName }}</span> + <h2 class="no-wrap"> + {{ isJoinMode ? 'Rejoindre' : 'Revendiquer' }} la structure <span>{{ claimStructure.structureName }}</span> </h2> <div> <p>Une fois réalisé cela vous permettra de devenir propriétaire de cette structure</p> diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 8ab5aa6f4..398ad100c 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -109,6 +109,9 @@ h3 { span { color: $secondary-color; } + &.no-wrap { + white-space: nowrap; + } } h3 { @include cn-bold-22; diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index a5547fc23..f1dfb68e2 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -70,6 +70,7 @@ export class FormComponent implements OnInit { public isEditMode = false; public isClaimMode = false; public isAccountMode = false; + public isJoinMode = false; public isLoading = false; public isWifiChoosen = false; @@ -97,6 +98,10 @@ export class FormComponent implements OnInit { this.initForm(new Structure(history.state.data)); } else if (history.state.newUser) { this.isClaimMode = true; + // Handle join strucutre, the case is very similar to claim + if (history.state.isJoin) { + this.isJoinMode = true; + } this.createAccountForm(); this.claimStructure = history.state.newUser; this.setValidationsForm(); @@ -550,9 +555,16 @@ export class FormComponent implements OnInit { const user = new User(this.accountForm.value); // Create user and claim structure this.authService.register(user).subscribe(() => { - this.structureService.claimStructureWithAccount(this.claimStructure._id, user).subscribe(() => { - this.progressStatus = 100; - }); + // If joinMode, send join request, if not send claim request; + if (this.isJoinMode) { + this.structureService.joinStructure(this.claimStructure._id, user.email).subscribe(() => { + this.progressStatus = 100; + }); + } else { + this.structureService.claimStructureWithAccount(this.claimStructure._id, user).subscribe(() => { + this.progressStatus = 100; + }); + } }); } @@ -615,6 +627,21 @@ export class FormComponent implements OnInit { this.progressStatus -= 25; } + /** + * Page algo for claim structure case + */ + public previousPageAccount(): void { + if (this.currentPage == PageTypeEnum.accountCredentials) { + this.currentPage = PageTypeEnum.accountInfo; + this.updatePageValid(); + } else if (this.currentPage == PageTypeEnum.cgu) { + this.currentPage = PageTypeEnum.accountCredentials; + this.updatePageValid(); + } + + this.progressStatus -= 25; + } + public nextPage(): void { if (this.isClaimMode) { this.nextPageClaim(); @@ -648,6 +675,8 @@ export class FormComponent implements OnInit { public previousPage(): void { if (this.isClaimMode) { this.previousPageClaim(); + } else if (this.isAccountMode) { + this.previousPageAccount(); } else { // Check if user already connected to skip accountForm pages. if (this.currentPage == PageTypeEnum.structureNameAndAddress && this.profile) { diff --git a/src/app/guards/auth.guard.ts b/src/app/guards/auth.guard.ts index 3d3205c2e..1c6867835 100644 --- a/src/app/guards/auth.guard.ts +++ b/src/app/guards/auth.guard.ts @@ -10,10 +10,11 @@ import { Observable } from 'rxjs'; export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) {} - canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): UrlTree | boolean { + canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { if (this.authService.isLoggedIn()) { return true; } - return this.router.parseUrl('/home'); + this.router.navigate(['/home'], { queryParams: { returnUrl: state.url } }); + return false; } } diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index aab75d532..ab1858df6 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit } from '@angular/core'; -import { NavigationEnd, NavigationStart, Router } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, NavigationStart, Router } from '@angular/router'; import { ProfileService } from '../profile/services/profile.service'; import { AuthService } from '../services/auth.service'; @@ -13,20 +13,39 @@ export class HeaderComponent implements OnInit { public isPopUpOpen = false; public displaySignUp = true; public currentRoute = ''; - public formeRoute = '/create-structure'; + public formRoute = '/create-structure'; + public returnUrl = null; - constructor(private authService: AuthService, private profileService: ProfileService, private router: Router) { + constructor( + private authService: AuthService, + private profileService: ProfileService, + private router: Router, + private route: ActivatedRoute + ) { this.router.events.subscribe((event) => { if (event instanceof NavigationEnd) { this.currentRoute = event.url; } }); } - ngOnInit(): void {} + ngOnInit(): void { + this.route.queryParams.subscribe((params) => { + if (params.verified || params.returnUrl) { + Promise.resolve().then(() => { + if (!this.isLoggedIn) { + this.isPopUpOpen = true; + this.displaySignUp = true; + } + }); + this.returnUrl = params.returnUrl; + } + }); + } public openMenu(): void { this.showMenu = true; } + public closeMenu(route: string): void { this.router.navigateByUrl(route); this.showMenu = false; @@ -47,6 +66,9 @@ export class HeaderComponent implements OnInit { } else { this.isPopUpOpen = false; } + if (this.returnUrl && this.isLoggedIn) { + this.router.navigateByUrl(this.returnUrl); + } } public get isAdmin(): boolean { @@ -58,6 +80,6 @@ export class HeaderComponent implements OnInit { } public displayLogo(): boolean { - return this.formeRoute !== this.currentRoute; + return this.formRoute !== this.currentRoute; } } diff --git a/src/app/services/structure.service.ts b/src/app/services/structure.service.ts index e370f3c66..7848ab3c4 100644 --- a/src/app/services/structure.service.ts +++ b/src/app/services/structure.service.ts @@ -50,6 +50,14 @@ export class StructureService { return this.http.get<Structure>(`${this.baseUrl}/${id}`); } + public validateStructureJoin(id: string, userId: string, state: boolean): Observable<Structure> { + return this.http.post<any>(`${this.baseUrl}/${id}/join/${userId}/${state}`, null); + } + + public joinStructure(id: string, email: string): Observable<Structure> { + return this.http.post<any>(`${this.baseUrl}/${id}/join`, { email }); + } + public delete(id: string): Observable<Structure> { return this.http.delete<Structure>(`${this.baseUrl}/${id}`); } diff --git a/src/app/structure-join/structure-join.component.html b/src/app/structure-join/structure-join.component.html new file mode 100644 index 000000000..4d51a54ef --- /dev/null +++ b/src/app/structure-join/structure-join.component.html @@ -0,0 +1,6 @@ +<div fxLayout="column" class="content-container full-screen"> + <div class="section-container" fxLayout="column" fxLayoutAlign="center center"> + <h1>Validation de la demande de rattachement</h1> + <p>Merci de patienter...</p> + </div> +</div> diff --git a/src/app/structure-join/structure-join.component.scss b/src/app/structure-join/structure-join.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/src/app/structure-join/structure-join.component.spec.ts b/src/app/structure-join/structure-join.component.spec.ts new file mode 100644 index 000000000..ab4d92278 --- /dev/null +++ b/src/app/structure-join/structure-join.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StructureJoinComponent } from './structure-join.component'; + +describe('StructureJoinComponent', () => { + let component: StructureJoinComponent; + let fixture: ComponentFixture<StructureJoinComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StructureJoinComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(StructureJoinComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/structure-join/structure-join.component.ts b/src/app/structure-join/structure-join.component.ts new file mode 100644 index 000000000..c38d14032 --- /dev/null +++ b/src/app/structure-join/structure-join.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; +import { StructureService } from '../services/structure.service'; + +@Component({ + selector: 'app-structure-join', + templateUrl: './structure-join.component.html', + styleUrls: ['./structure-join.component.scss'], +}) +export class StructureJoinComponent implements OnInit { + constructor(private router: Router, private route: ActivatedRoute, private structureService: StructureService) {} + + ngOnInit(): void { + this.route.queryParamMap.subscribe((params) => { + if ( + params.get('id') && + params.get('userId') && + (params.get('status') === 'true' || params.get('status') === 'false') + ) { + this.structureService + .validateStructureJoin(params.get('id'), params.get('userId'), params.get('status') === 'true' ? true : false) + .subscribe( + (res) => { + this.router.navigateByUrl('/home'); + }, + (err) => { + this.router.navigateByUrl('/home'); + } + ); + } + }); + } +} 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 e14724b24..803f77bdf 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 @@ -118,6 +118,7 @@ </div> <div fxLayout="row" fxLayoutAlign="center center" class="hide-on-print"> <a *ngIf="!isClaimed" (click)="handleClaim()" class="primary" tabindex="0">Revendiquer cette structure</a> + <a *ngIf="displayJoin()" (click)="handleJoin()" class="primary" tabindex="0">Rejoindre cette structure</a> <!-- temporary remove edit --> <a *ngIf="profileService.isLinkedToStructure(structure._id) || profileService.isAdmin()" @@ -326,3 +327,11 @@ " (closed)="claimStructure($event)" ></app-modal-confirmation> + +<app-modal-confirmation + [openned]="joinModalOpenned" + [content]=" + 'Voulez-vous vraiment rejoindre cette structure ? Une demande sera envoyée aux membres pour validation' + " + (closed)="joinStructure($event)" +></app-modal-confirmation> diff --git a/src/app/structure-list/components/structure-details/structure-details.component.ts b/src/app/structure-list/components/structure-details/structure-details.component.ts index ecbeb3a00..42eb58de7 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.ts +++ b/src/app/structure-list/components/structure-details/structure-details.component.ts @@ -36,6 +36,7 @@ export class StructureDetailsComponent implements OnInit { public currentProfile: User = null; public deleteModalOpenned = false; public claimModalOpenned = false; + public joinModalOpenned = false; constructor( private printService: PrintService, @@ -122,6 +123,10 @@ export class StructureDetailsComponent implements OnInit { this.claimModalOpenned = !this.claimModalOpenned; } + public toggleJoinModal(): void { + this.joinModalOpenned = !this.joinModalOpenned; + } + public handleClaim(): void { if (this.userIsLoggedIn()) { this.toggleClaimModal(); @@ -130,6 +135,14 @@ export class StructureDetailsComponent implements OnInit { } } + public handleJoin(): void { + if (this.userIsLoggedIn()) { + this.toggleJoinModal(); + } else { + this.router.navigate(['create-structure'], { state: { newUser: this.structure, isJoin: true } }); + } + } + public deleteStructure(shouldDelete: boolean): void { this.toggleDeleteModal(); if (shouldDelete) { @@ -156,6 +169,15 @@ export class StructureDetailsComponent implements OnInit { } } + public joinStructure(shouldClaim: boolean): void { + this.toggleJoinModal(); + if (shouldClaim) { + this.structureService.joinStructure(this.structure._id, this.authService.userValue.username).subscribe((res) => { + this.isClaimed = true; + }); + } + } + public getAccessLabel(accessModality: AccessModality): string { switch (accessModality) { case AccessModality.free: @@ -222,4 +244,10 @@ export class StructureDetailsComponent implements OnInit { ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners', 'wifiEnAccesLibre'].includes(eqpt) ); } + + public displayJoin(): boolean { + return ( + !(this.profileService.isLinkedToStructure(this.structure._id) || this.profileService.isAdmin()) && this.isClaimed + ); + } } -- GitLab