From 3585f68cee37c704f2e4da99d60e241df0ddd5b8 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Fri, 5 Feb 2021 16:11:27 +0100 Subject: [PATCH 01/72] fix(signUpModal) : redesign modal + export css modal --- .../modal-confirmation.component.html | 4 +- .../modal-confirmation.component.scss | 60 ++-------- .../signup-modal/signup-modal.component.html | 106 +++++++++++------- .../signup-modal/signup-modal.component.scss | 88 ++++++++++----- .../signup-modal/signup-modal.component.ts | 21 +++- src/assets/scss/_z-index.scss | 3 +- src/styles.scss | 50 +++++++++ 7 files changed, 199 insertions(+), 133 deletions(-) diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.html b/src/app/shared/components/modal-confirmation/modal-confirmation.component.html index fe2cf5816..d2bc29320 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.html +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.html @@ -1,10 +1,10 @@ -<div *ngIf="openned" class="modalExitContainer"> +<div *ngIf="openned" class="modalBackground"> <div class="modal"> <div class="contentModal" fxLayout="column" fxLayoutAlign="space-around center"> <h3>ATTENTION</h3> <p>{{ content }}</p> <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> - <button class="btn leave" (click)="closeModal(true)">Confirmer</button> + <button class="btn confirm" (click)="closeModal(true)">Confirmer</button> <button class="btn" (click)="closeModal(false)">Annuler</button> </div> </div> diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss index 5411fa484..5f111d0b5 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss @@ -3,56 +3,12 @@ @import '../../../../assets/scss/shapes'; @import '../../../../assets/scss/z-index'; -.modalExitContainer { - width: 100%; - height: 100%; - z-index: $modal-confirmation-z-index; - position: absolute; - content: ''; - top: 0; - background-color: $modal-background; - .modal { - .contentModal { - width: 100%; - background: $white; - padding: 35px 20px 18px 20px; - h3 { - @include cn-bold-18; - color: $orange-warning; - } - p { - @include cn-bold-16; - color: $grey-1; - text-align: center; - } - .footerModal { - width: 100%; - margin-top: 14px; - @include cn-bold-16; - .btn { - background: $secondary-color; - border-radius: 4px; - outline: none; - cursor: pointer; - border: 0; - color: $white; - height: 40px; - @include btn-bold; - width: 149px; - &.leave { - background: none; - color: $grey-1; - text-decoration: underline; - } - } - } - } - width: 350px; - margin: auto; - border-radius: 6px; - @include background-hash; - border: 1px solid $grey-4; - margin-top: 50vh; - transform: translateY(-50%); - } +h3 { + @include cn-bold-18; + color: $orange-warning; +} +p { + @include cn-bold-16; + color: $grey-1; + text-align: center; } diff --git a/src/app/shared/components/signup-modal/signup-modal.component.html b/src/app/shared/components/signup-modal/signup-modal.component.html index 537ae6c49..8c4387be2 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.html +++ b/src/app/shared/components/signup-modal/signup-modal.component.html @@ -1,47 +1,69 @@ -<div class="cModal" [ngClass]="openned ? 'oModal' : ''"> - <div (clickOutside)="closeModal()" fxLayout="column" fxLayoutAlign="center center"> - <div class="ico-close-wrapper"> - <div (click)="closeModal()" class="ico-close-details"></div> - </div> - <h4 class="card-header">Connexion</h4> - <div class="card-body"> - <form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> - <div class="form-group"> - <label for="username">Email</label> - <input - type="text" - formControlName="username" - autocomplete="on" - class="form-control" - [ngClass]="{ 'is-invalid': submitted && f.username.errors }" - /> - <div *ngIf="submitted && f.username.errors" class="invalid-feedback"> - <div *ngIf="f.username.errors.required">Identifiant requis</div> - </div> +<div *ngIf="openned" class="modalBackground"> + <div class="modal"> + <div (clickOutside)="closeModal(false)" class="contentModal" fxLayout="column" fxLayoutAlign="space-around start"> + <div class="ico-close" fxLayout="row" fxLayoutAlign="end center"> + <div class="ico-close-wrapper"> + <div (click)="closeModal()" class="ico-close-details"></div> </div> - <div class="form-group"> - <label for="password">Mot de passe</label> - <input - type="password" - formControlName="password" - autocomplete="on" - class="form-control" - [ngClass]="{ 'is-invalid': submitted && f.password.errors }" - /> - <div *ngIf="submitted && f.password.errors"> - <div *ngIf="f.password.errors.required">Mot de passe requis</div> + </div> + + <div class="form"> + <h3>Se connecter</h3> + <form [formGroup]="loginForm" (ngSubmit)="onSubmit()"> + <div class="form-group" fxLayout="column"> + <label for="email">Courriel personnel</label> + <div fxLayout="row" fxLayoutGap="13px"> + <input type="text" autocomplete="on" formControlName="email" class="form-input" /> + <img + *ngIf="f.email.invalid && f.email.value" + src="../../assets/form/notvalidate.svg" + alt="logo invalid" + /> + </div> </div> - </div> - <div *ngIf="authFailed">Identifiant ou mot de passe invalide</div> - <div> - <button [disabled]="loading"> - <span *ngIf="loading"></span> - Login - </button> - <button (click)="sendSwitchToSignIn()">Inscription</button> - </div> - <a (click)="swithToResetPassword()">Mot de passe oublié</a> - </form> + <div class="form-group password" fxLayout="column"> + <label for="password">Mot de passe</label> + <p [ngClass]="{ invalid: f.password.invalid && f.password.value }"> + Le mot de passe doit contenir au minimum : 8 caractères dont un caractère spécial, un caractère en + majuscule et un chiffre. + </p> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowPassword ? 'text' : 'password'" + autocomplete="on" + formControlName="password" + class="form-input" + /> + <img + (click)="toggleShowPassword()" + class="eyePassword" + src="../../assets/form/eyePassword.svg" + alt="logo eyePassword" + /> + <img + *ngIf="f.password.invalid && f.password.value" + src="../../assets/form/notvalidate.svg" + alt="logo invalid" + /> + </div> + </div> + <div class="invalid" *ngIf="authFailed">Identifiant ou mot de passe invalide</div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> + <button class="btn confirm" (click)="swithToResetPassword()">Mot de passe oublié</button> + <button + type="submit" + class="btn" + [disabled]="loginForm.invalid || loading" + [ngClass]="{ invalid: loginForm.invalid || loading }" + > + Connexion + </button> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> + <button class="btn register" (click)="sendSwitchToSignIn()">Je n’ai pas encore de compte</button> + </div> + </form> + </div> </div> </div> </div> diff --git a/src/app/shared/components/signup-modal/signup-modal.component.scss b/src/app/shared/components/signup-modal/signup-modal.component.scss index a73c0f1c2..0caa3caa4 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.scss +++ b/src/app/shared/components/signup-modal/signup-modal.component.scss @@ -3,38 +3,66 @@ @import '../../../../assets/scss/buttons'; @import '../../../../assets/scss/z-index'; @import '../../../../assets/scss/hyperlink'; -.cModal { - position: fixed; - z-index: $modal-z-index; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: $modal-background; - display: none; + +.ico-close { + width: 100%; } -.cModal > div { - max-width: 450px; - position: relative; - top: 40%; - left: 50%; - transform: translate(-50%, -50%); - background: $white; - text-align: center; - border-radius: 4px; - @include cn-bold-16; - p { - padding: 30px 48px 0 40px; - } +.invalid { + color: $orange-warning; +} +h3 { + @include cn-bold-24; + color: $black; + margin-top: 0; } -.oModal { - display: block; +.form { + max-width: 391px; + margin: auto; } -.ico-close-wrapper { - position: absolute; - top: -10px; - right: -10px; + +.form-group { + margin-bottom: 26px; + label { + margin-bottom: 4px; + @include cn-regular-14; + color: $grey-2; + } + input { + width: 100%; + margin-right: 39px; + } + &.password { + p { + @include cn-regular-14; + color: $grey-3; + margin-top: 0; + margin-bottom: 4px; + padding-right: 39px; + &.invalid { + color: $orange-warning; + } + } + .eyePassword { + cursor: pointer; + margin-right: 39px; + } + } } -.ico-close-details { - opacity: 1; +.footerModal { + padding-right: 39px; + button { + width: 100% !important; + &.invalid { + opacity: 0.4; + } + &.confirm { + font-size: 14px; + } + &.register { + background: $white; + border: 1px solid $grey-4; + color: $secondary-color; + height: 38px; + } + } } diff --git a/src/app/shared/components/signup-modal/signup-modal.component.ts b/src/app/shared/components/signup-modal/signup-modal.component.ts index 3d70b98ae..2f8a77bad 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.ts +++ b/src/app/shared/components/signup-modal/signup-modal.component.ts @@ -3,6 +3,7 @@ import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/fo import { ActivatedRoute, Router } from '@angular/router'; import { first } from 'rxjs/operators'; import { AuthService } from '../../../services/auth.service'; +import { Regex } from '../../enum/regex.enum'; @Component({ selector: 'app-signup-modal', @@ -15,7 +16,7 @@ export class SignUpModalComponent implements OnInit { public submitted = false; public authFailed = false; public returnUrl: string; - + public isShowPassword = false; constructor( private formBuilder: FormBuilder, private route: ActivatedRoute, @@ -28,8 +29,14 @@ export class SignUpModalComponent implements OnInit { ngOnInit(): void { this.loginForm = this.formBuilder.group({ - username: ['', Validators.required], - password: ['', Validators.required], + email: ['', [Validators.required, Validators.pattern(Regex.email)]], + password: [ + '', + [ + Validators.required, + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR + ], + ], }); // get return url from route parameters or default to '/' this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; @@ -41,6 +48,7 @@ export class SignUpModalComponent implements OnInit { } public closeModal(): void { + console.log('ok'); this.closed.emit(true); } @@ -60,10 +68,9 @@ export class SignUpModalComponent implements OnInit { if (this.loginForm.invalid) { return; } - this.loading = true; this.authService - .login(this.f.username.value, this.f.password.value) + .login(this.f.email.value, this.f.password.value) .pipe(first()) .subscribe( () => { @@ -76,4 +83,8 @@ export class SignUpModalComponent implements OnInit { } ); } + + toggleShowPassword(): void { + this.isShowPassword = !this.isShowPassword; + } } diff --git a/src/assets/scss/_z-index.scss b/src/assets/scss/_z-index.scss index 18c12fc2c..fc1ca81b6 100644 --- a/src/assets/scss/_z-index.scss +++ b/src/assets/scss/_z-index.scss @@ -6,5 +6,4 @@ $menu-phone-z-index: 1003; $structure-details-z-index: 1001; // Modals (filters/confirmationPopup/authen/...) -$modal-z-index: 1002; -$modal-confirmation-z-index: 1004; +$modal-z-index: 1004; diff --git a/src/styles.scss b/src/styles.scss index 25c9dd89e..2efc432c2 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -1,6 +1,7 @@ /* You can add global styles to this file, and also import other style files */ @import 'assets/scss/typography'; +@import 'assets/scss/z-index'; @import 'assets/scss/color'; @import 'assets/scss/breakpoint'; @import 'assets/scss/icons'; @@ -182,3 +183,52 @@ button { display: none !important; } } + +// Modal + +.modalBackground { + width: 100%; + height: 100%; + z-index: $modal-z-index; + position: absolute; + content: ''; + top: 0; + background-color: $modal-background; + .modal { + .contentModal { + width: 100%; + background: $white; + padding: 35px 20px 18px 20px; + } + .footerModal { + width: 100%; + margin-top: 14px; + @include cn-bold-16; + .btn { + background: $secondary-color; + border-radius: 4px; + outline: none; + cursor: pointer; + border: 0; + color: $white; + height: 40px; + @include btn-bold; + width: 149px; + &.confirm { + background: none; + color: $grey-1; + text-decoration: underline; + } + } + } + min-width: 350px; + width: 80%; + max-width: 560px; + margin: auto; + border-radius: 6px; + @include background-hash; + border: 1px solid $grey-4; + margin-top: 50vh; + transform: translateY(-50%); + } +} -- GitLab From 6ee98b2e5194bc6d3af633ddea524f375e6f5345 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Fri, 5 Feb 2021 16:43:30 +0100 Subject: [PATCH 02/72] fix(signIn) : fix design modal register --- .../create-account-form.component.html | 106 +++++++++++++----- .../create-account-form.component.scss | 35 ++++++ .../create-account-form.component.ts | 20 +++- .../signin-modal/signin-modal.component.html | 36 +++--- .../signin-modal/signin-modal.component.scss | 49 +++----- .../signup-modal/signup-modal.component.ts | 2 +- src/styles.scss | 2 + 7 files changed, 168 insertions(+), 82 deletions(-) diff --git a/src/app/shared/components/create-account-form/create-account-form.component.html b/src/app/shared/components/create-account-form/create-account-form.component.html index 06831aa70..6b547d7fd 100644 --- a/src/app/shared/components/create-account-form/create-account-form.component.html +++ b/src/app/shared/components/create-account-form/create-account-form.component.html @@ -1,44 +1,90 @@ <form [formGroup]="accountForm" *ngIf="accountForm" (ngSubmit)="onSubmit(accountForm)"> - <div class="form-group"> - <label for="email">Email</label> - <input type="email" autocomplete="on" formControlName="email" class="form-control" /> - <app-validator-form *ngIf="submitted" [control]="getAccountControl('email')"></app-validator-form> + <div class="form-group" fxLayout="column"> + <label for="email">Courriel personnel</label> + <div fxLayout="row" fxLayoutGap="13px"> + <input type="text" autocomplete="on" formControlName="email" class="form-input" /> + <img *ngIf="f.email.invalid && f.email.value" src="../../assets/form/notvalidate.svg" alt="logo invalid" /> + </div> </div> - <div class="form-group"> + <div class="form-group" fxLayout="column"> <label for="surname">Nom</label> - <input type="text" formControlName="surname" class="form-control" /> - <app-validator-form *ngIf="submitted" [control]="getAccountControl('surname')"></app-validator-form> + <div fxLayout="row" fxLayoutGap="13px"> + <input type="text" autocomplete="on" formControlName="surname" class="form-input" /> + <img *ngIf="f.surname.invalid && f.surname.value" src="../../assets/form/notvalidate.svg" alt="logo invalid" /> + </div> </div> - <div class="form-group"> + <div class="form-group" fxLayout="column"> <label for="name">Prénom</label> - <input type="text" formControlName="name" class="form-control" /> - <app-validator-form *ngIf="submitted" [control]="getAccountControl('name')"></app-validator-form> + <div fxLayout="row" fxLayoutGap="13px"> + <input type="text" autocomplete="on" formControlName="name" class="form-input" /> + <img *ngIf="f.name.invalid && f.name.value" src="../../assets/form/notvalidate.svg" alt="logo invalid" /> + </div> </div> - <div class="form-group"> + <div class="form-group" fxLayout="column"> <label for="phone">Téléphone</label> - <input type="text" formControlName="phone" class="form-control" (input)="modifyPhoneInput($event.target.value)" /> - <app-validator-form *ngIf="submitted" [control]="getAccountControl('phone')"></app-validator-form> + <div fxLayout="row" fxLayoutGap="13px"> + <input + type="text" + autocomplete="on" + (input)="modifyPhoneInput($event.target.value)" + formControlName="phone" + class="form-input" + /> + <img *ngIf="f.phone.invalid && f.phone.value" src="../../assets/form/notvalidate.svg" alt="logo invalid" /> + </div> </div> - <div class="form-group"> + <div class="form-group password" fxLayout="column"> <label for="password">Mot de passe</label> - <input type="password" autocomplete="on" formControlName="password" class="form-control" /> - <app-validator-form - *ngIf="submitted" - [nameControl]="'password'" - [control]="getAccountControl('password')" - ></app-validator-form> + <p [ngClass]="{ invalid: f.password.invalid && f.password.value }"> + Le mot de passe doit contenir au minimum : 8 caractères dont un caractère spécial, un caractère en majuscule et un + chiffre. + </p> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowPassword ? 'text' : 'password'" + autocomplete="on" + formControlName="password" + class="form-input" + /> + <img + (click)="toggleShowPassword()" + class="eyePassword" + src="../../assets/form/eyePassword.svg" + alt="logo eyePassword" + /> + <img *ngIf="f.password.invalid && f.password.value" src="../../assets/form/notvalidate.svg" alt="logo invalid" /> + </div> </div> - <div class="form-group"> + <div class="form-group password" fxLayout="column"> <label for="confirmPassword">Confirmation du mot de passe</label> - <input type="password" autocomplete="on" formControlName="confirmPassword" class="form-control" /> - <app-validator-form - *ngIf="submitted" - [nameControl]="'password'" - [control]="getAccountControl('confirmPassword')" - ></app-validator-form> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowConfirmPassword ? 'text' : 'password'" + autocomplete="on" + formControlName="confirmPassword" + class="form-input" + /> + <img + (click)="toggleShowConfirmPassword()" + class="eyePassword" + src="../../assets/form/eyePassword.svg" + alt="logo eyePassword" + /> + <img + *ngIf="f.confirmPassword.invalid && f.confirmPassword.value" + src="../../assets/form/notvalidate.svg" + alt="logo invalid" + /> + </div> </div> - - <div class="form-group"> - <button class="btn btn-primary" type="submit">Valider</button> + <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> + <button + type="submit" + class="btn" + [disabled]="accountForm.invalid || loading" + [ngClass]="{ invalid: accountForm.invalid || loading }" + > + Valider + </button> </div> </form> diff --git a/src/app/shared/components/create-account-form/create-account-form.component.scss b/src/app/shared/components/create-account-form/create-account-form.component.scss index e69de29bb..0beea76df 100644 --- a/src/app/shared/components/create-account-form/create-account-form.component.scss +++ b/src/app/shared/components/create-account-form/create-account-form.component.scss @@ -0,0 +1,35 @@ +@import '../../../../assets/scss/typography'; +@import '../../../../assets/scss/color'; +.form-group { + margin-bottom: 26px; + label { + margin-bottom: 4px; + @include cn-regular-14; + color: $grey-2; + } + input { + width: 100%; + margin-right: 39px; + } + &.password { + p { + @include cn-regular-14; + color: $grey-3; + margin-top: 0; + margin-bottom: 4px; + padding-right: 39px; + &.invalid { + color: $orange-warning; + } + } + .eyePassword { + cursor: pointer; + margin-right: 39px; + } + } +} +button { + &.invalid { + opacity: 0.4; + } +} diff --git a/src/app/shared/components/create-account-form/create-account-form.component.ts b/src/app/shared/components/create-account-form/create-account-form.component.ts index 4363e6774..702924838 100644 --- a/src/app/shared/components/create-account-form/create-account-form.component.ts +++ b/src/app/shared/components/create-account-form/create-account-form.component.ts @@ -1,5 +1,6 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Regex } from '../../enum/regex.enum'; import { MustMatch } from '../../validator/form'; @Component({ @@ -11,14 +12,16 @@ export class CreateAccountFormComponent implements OnInit { constructor() {} public accountForm: FormGroup; public submitted: boolean = false; + public isShowConfirmPassword = false; + public isShowPassword = false; @Output() public submitForm = new EventEmitter<FormGroup>(); ngOnInit(): void { this.accountForm = new FormGroup( { - email: new FormControl('', Validators.required), - name: new FormControl('', Validators.required), - surname: new FormControl('', Validators.required), + email: new FormControl('', [Validators.required, Validators.pattern(Regex.email)]), + name: new FormControl('', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), + surname: new FormControl('', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), phone: new FormControl('', [Validators.required, Validators.pattern('([0-9]{2} ){4}[0-9]{2}')]), //NOSONAR password: new FormControl('', [ Validators.required, @@ -30,6 +33,11 @@ export class CreateAccountFormComponent implements OnInit { ); } + // getter for form fields + get f(): { [key: string]: AbstractControl } { + return this.accountForm.controls; + } + public onSubmit(accountForm: FormGroup) { this.submitted = true; if (accountForm.valid) { @@ -50,4 +58,10 @@ export class CreateAccountFormComponent implements OnInit { this.accountForm.get('phone').setValue(phoneNoSpace.replace(/(?!^)(?=(?:\d{2})+$)/g, ' ')); //NOSONAR } } + public toggleShowConfirmPassword(): void { + this.isShowConfirmPassword = !this.isShowConfirmPassword; + } + public toggleShowPassword(): void { + this.isShowPassword = !this.isShowPassword; + } } diff --git a/src/app/shared/components/signin-modal/signin-modal.component.html b/src/app/shared/components/signin-modal/signin-modal.component.html index d8d32764a..a0d11538c 100644 --- a/src/app/shared/components/signin-modal/signin-modal.component.html +++ b/src/app/shared/components/signin-modal/signin-modal.component.html @@ -1,17 +1,25 @@ -<div class="cModal" [ngClass]="openned ? 'oModal' : ''"> - <div (clickOutside)="closeModal()" fxLayout="column" fxLayoutAlign="center center"> - <div class="ico-close-wrapper"> - <div (click)="closeModal()" class="ico-close-details"></div> - </div> - <h4>Inscription</h4> - <div *ngIf="!success"> - <app-create-account-form (submitForm)="onSubmit($event)"></app-create-account-form> - </div> - <div *ngIf="userAlreadyExist">Cette addresse mail est déjà utilisée</div> - <div *ngIf="success"> - <p> - Un mail de confirmation vous a été envoyé. Merci de valider votre addresse mail avant de pouvoir vous connecter. - </p> +<div *ngIf="openned" class="modalBackground"> + <div class="modal"> + <div (clickOutside)="closeModal(false)" class="contentModal" fxLayout="column" fxLayoutAlign="space-around start"> + <div class="ico-close" fxLayout="row" fxLayoutAlign="end center"> + <div class="ico-close-wrapper"> + <div (click)="closeModal()" class="ico-close-details"></div> + </div> + </div> + + <div class="form"> + <h3 *ngIf="!success">S'inscrire</h3> + <div class="emailAlreadyUse" *ngIf="userAlreadyExist && !success">Cette addresse mail est déjà utilisée</div> + <div *ngIf="!success"> + <app-create-account-form (submitForm)="onSubmit($event)"></app-create-account-form> + </div> + <div *ngIf="success"> + <p> + Un mail de confirmation vous a été envoyé. Merci de valider votre addresse mail avant de pouvoir vous + connecter. + </p> + </div> + </div> </div> </div> </div> diff --git a/src/app/shared/components/signin-modal/signin-modal.component.scss b/src/app/shared/components/signin-modal/signin-modal.component.scss index a73c0f1c2..529354b01 100644 --- a/src/app/shared/components/signin-modal/signin-modal.component.scss +++ b/src/app/shared/components/signin-modal/signin-modal.component.scss @@ -1,40 +1,21 @@ @import '../../../../assets/scss/typography'; @import '../../../../assets/scss/color'; -@import '../../../../assets/scss/buttons'; -@import '../../../../assets/scss/z-index'; -@import '../../../../assets/scss/hyperlink'; -.cModal { - position: fixed; - z-index: $modal-z-index; - top: 0; - right: 0; - bottom: 0; - left: 0; - background-color: $modal-background; - display: none; + +.ico-close { + width: 100%; } -.cModal > div { - max-width: 450px; - position: relative; - top: 40%; - left: 50%; - transform: translate(-50%, -50%); - background: $white; - text-align: center; - border-radius: 4px; - @include cn-bold-16; - p { - padding: 30px 48px 0 40px; - } -} -.oModal { - display: block; +h3 { + @include cn-bold-24; + color: $black; + margin-top: 0; } -.ico-close-wrapper { - position: absolute; - top: -10px; - right: -10px; +.form { + max-width: 391px; + margin: auto; + width: 100%; } -.ico-close-details { - opacity: 1; +.emailAlreadyUse { + color: $orange-warning; + text-align: center; + margin-bottom: 10px; } diff --git a/src/app/shared/components/signup-modal/signup-modal.component.ts b/src/app/shared/components/signup-modal/signup-modal.component.ts index 2f8a77bad..5d53eceaa 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.ts +++ b/src/app/shared/components/signup-modal/signup-modal.component.ts @@ -84,7 +84,7 @@ export class SignUpModalComponent implements OnInit { ); } - toggleShowPassword(): void { + public toggleShowPassword(): void { this.isShowPassword = !this.isShowPassword; } } diff --git a/src/styles.scss b/src/styles.scss index 2efc432c2..40a51cd1c 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -195,6 +195,8 @@ button { top: 0; background-color: $modal-background; .modal { + max-height: 90%; + overflow: auto; .contentModal { width: 100%; background: $white; -- GitLab From 4abc885932771e89f53b497743ebb882cbe3c369 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Fri, 5 Feb 2021 18:08:56 +0100 Subject: [PATCH 03/72] fix: structure type display --- src/app/models/structure.model.ts | 4 ++++ src/app/structure-list/components/card/card.component.html | 2 +- .../structure-details/structure-details.component.html | 2 +- src/app/user-verification/user-verification.component.html | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/models/structure.model.ts b/src/app/models/structure.model.ts index e9468d7c5..7fee546ca 100644 --- a/src/app/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -172,4 +172,8 @@ export class Structure { return null; } } + + public getLabelTypeStructure(): string { + return typeStructureEnum[this.structureType]; + } } diff --git a/src/app/structure-list/components/card/card.component.html b/src/app/structure-list/components/card/card.component.html index 67e4642b4..b9bc6cc92 100644 --- a/src/app/structure-list/components/card/card.component.html +++ b/src/app/structure-list/components/card/card.component.html @@ -13,7 +13,7 @@ </div> </div> </div> - <span class="typeStructure">{{ structure.structureType }}</span> + <span class="typeStructure">{{ structure.getLabelTypeStructure() }}</span> <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="7px" *ngIf="structure.hasEquipments()"> <app-svg-icon *ngFor="let equipement of structure.equipmentsAndServices" 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 6d346e76d..58a2e2d98 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 @@ -18,7 +18,7 @@ </div> <div fxLayout="row" fxLayoutAlign="space-between center"> <div class="typeInformationHeader" fxLayout="column"> - <h3>{{ structure.structureType }}</h3> + <h3>{{ structure.getLabelTypeStructure() }}</h3> <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="7px" *ngIf="structure.hasEquipments()"> <app-svg-icon *ngFor="let equipement of structure.equipmentsAndServices" diff --git a/src/app/user-verification/user-verification.component.html b/src/app/user-verification/user-verification.component.html index 86db3d2c5..4d2ba4981 100644 --- a/src/app/user-verification/user-verification.component.html +++ b/src/app/user-verification/user-verification.component.html @@ -15,7 +15,7 @@ <div *ngIf="structure" class="structureInfoBlock" fxLayout="row" fxLayoutAlign=" center"> <div class="structureInfoContent" fxLayout="column"> {{ structure.structureName }} - <span>{{ structure.structureType }}</span> + <span>{{ structure.getLabelTypeStructure() }}</span> </div> <div class="validateSvg"> <svg class="validate" aria-hidden="true"> -- GitLab From 438ae1269650e27ee75e0fe52e1e6d779a09f73d Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Fri, 5 Feb 2021 18:11:03 +0100 Subject: [PATCH 04/72] fix(signup) : fix error position --- .../shared/components/signup-modal/signup-modal.component.html | 2 +- .../shared/components/signup-modal/signup-modal.component.scss | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/shared/components/signup-modal/signup-modal.component.html b/src/app/shared/components/signup-modal/signup-modal.component.html index 8c4387be2..1069f993c 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.html +++ b/src/app/shared/components/signup-modal/signup-modal.component.html @@ -46,8 +46,8 @@ alt="logo invalid" /> </div> + <div class="invalid" *ngIf="authFailed">Identifiant ou mot de passe invalide</div> </div> - <div class="invalid" *ngIf="authFailed">Identifiant ou mot de passe invalide</div> <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> <button class="btn confirm" (click)="swithToResetPassword()">Mot de passe oublié</button> <button diff --git a/src/app/shared/components/signup-modal/signup-modal.component.scss b/src/app/shared/components/signup-modal/signup-modal.component.scss index 0caa3caa4..bdee93df1 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.scss +++ b/src/app/shared/components/signup-modal/signup-modal.component.scss @@ -21,7 +21,7 @@ h3 { } .form-group { - margin-bottom: 26px; + margin-top: 26px; label { margin-bottom: 4px; @include cn-regular-14; @@ -32,6 +32,7 @@ h3 { margin-right: 39px; } &.password { + margin-bottom: 39px; p { @include cn-regular-14; color: $grey-3; -- GitLab From 667bc2d54d58f3c3d1b016852bce616b54a06e87 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Fri, 5 Feb 2021 18:43:26 +0100 Subject: [PATCH 05/72] fix: structure type for map --- src/app/map/components/map.component.ts | 2 +- src/app/models/structure.model.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts index 01f19e751..ad2bba78c 100644 --- a/src/app/map/components/map.component.ts +++ b/src/app/map/components/map.component.ts @@ -159,7 +159,7 @@ export class MapComponent implements OnChanges { structure.structureName + '</h1>' + '<p>' + - structure.structureType + + structure.getLabelTypeStructure() + '</p><div>' + '<span class="ico-dot-' + cssAvailabilityClass + diff --git a/src/app/models/structure.model.ts b/src/app/models/structure.model.ts index 7fee546ca..3c4569dc4 100644 --- a/src/app/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -174,6 +174,6 @@ export class Structure { } public getLabelTypeStructure(): string { - return typeStructureEnum[this.structureType]; + return typeStructureEnum[this.structureType] ? typeStructureEnum[this.structureType] : ''; } } -- GitLab From c89ce891642f559fd46a7c327911e08610eb389b Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Mon, 8 Feb 2021 15:13:52 +0100 Subject: [PATCH 06/72] fix(form) : add edit logic to inputs default value --- src/app/form/form.component.ts | 158 +++++++++++++----- .../structure-type-picker.component.ts | 6 +- .../structure-details.component.html | 15 +- .../structure-details.component.ts | 19 --- 4 files changed, 121 insertions(+), 77 deletions(-) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 6832ffc8a..41f05124b 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -13,7 +13,7 @@ import { MustMatch } from '../shared/validator/form'; import { Address } from '../models/address.model'; import { Module } from '../structure-list/models/module.model'; import { Equipment } from '../structure-list/enum/equipment.enum'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { first } from 'rxjs/operators'; import { Regex } from '../shared/enum/regex.enum'; @@ -23,8 +23,6 @@ import { Regex } from '../shared/enum/regex.enum'; styleUrls: ['./form.component.scss'], }) export class FormComponent implements OnInit { - @Input() public idStructure?: string; - @Input() public isEditMode: boolean = true; public profile: User; public createdStructure: Structure; @@ -62,6 +60,7 @@ export class FormComponent implements OnInit { public isShowPassword = false; public userAcceptSavedDate = false; public showMenu = false; + public isEditMode = false; constructor( private structureService: StructureService, @@ -70,59 +69,54 @@ export class FormComponent implements OnInit { private authService: AuthService ) {} - ngOnInit(): void { + async ngOnInit(): Promise<void> { this.profileService.getProfile().then((user: User) => { this.profile = user; }); - + await this.setCategories(); // Check if it's a new structure or edit structure - if (this.idStructure) { - this.structureService.getStructure(this.idStructure).subscribe((structure) => { - this.initForm(structure); - this.idStructure = structure._id; - }); + if (history.state.data) { + this.isEditMode = true; + this.initForm(new Structure(history.state.data)); } else { this.initForm(new Structure()); } - this.setCategories(); } - private setCategories(): void { + async setCategories(): Promise<void> { this.searchService.getCategoriesAccompaniment().subscribe((categories: Category[]) => { this.proceduresAccompaniment = categories[0]; }); - this.searchService.getCategoriesMoreFilters().subscribe((categories: Category[]) => { - categories.forEach((categ) => { - switch (categ.id) { - case CategoryEnum.accessModality: { - this.accessModality = categ; - break; - } - case CategoryEnum.equipmentsAndServices: { - categ.modules.forEach((c) => { - this.equipmentsAndServices.push({ module: c, openned: false }); - }); - break; - } - case CategoryEnum.labelsQualifications: { - this.labelsQualifications = categ; - break; - } - case CategoryEnum.publics: { - this.publics = categ; - break; - } - case CategoryEnum.publicsAccompaniment: { - this.publicsAccompaniment = categ; - break; - } + const equipmentsCategs = await this.searchService.getCategoriesMoreFilters().toPromise(); + equipmentsCategs.forEach((categ) => { + switch (categ.id) { + case CategoryEnum.accessModality: { + this.accessModality = categ; + break; + } + case CategoryEnum.equipmentsAndServices: { + categ.modules.forEach((c) => { + this.equipmentsAndServices.push({ module: c, openned: false }); + }); + break; + } + case CategoryEnum.labelsQualifications: { + this.labelsQualifications = categ; + break; } - }); + case CategoryEnum.publics: { + this.publics = categ; + break; + } + case CategoryEnum.publicsAccompaniment: { + this.publicsAccompaniment = categ; + break; + } + } }); - this.searchService.getCategoriesTraining().subscribe((categories: Category[]) => { - categories.forEach((categ) => { - this.trainingCategories.push({ category: categ, openned: false }); - }); + let categs = await this.searchService.getCategoriesTraining().toPromise(); + categs.forEach((categ) => { + this.trainingCategories.push({ category: categ, openned: false }); }); } @@ -218,8 +212,84 @@ export class FormComponent implements OnInit { saturday: this.createDay(structure.hours.saturday), sunday: this.createDay(structure.hours.sunday), }); + if (this.isEditMode) { + this.showCollapse(structure); + } + this.setValidationsForm(); } + private showCollapse(s: Structure): void { + if (s.website) { + this.showWebsite = true; + } + if (s.facebook || s.twitter || s.instagram || s.linkedin) { + this.showSocialNetwork = true; + } + if (s.publicsAccompaniment.length) { + this.showPublicsAccompaniment = true; + } + if (s.proceduresAccompaniment.length) { + this.showProceduresAccompaniment = true; + } + this.trainingCategories.forEach((categ: { category: Category; openned: boolean }) => { + switch (categ.category.id) { + case 'accessRight': + if (s.accessRight.length) { + categ.openned = true; + } + break; + case 'socialAndProfessional': + if (s.socialAndProfessional.length) { + categ.openned = true; + } + break; + case 'baseSkills': + if (s.baseSkills.length) { + categ.openned = true; + } + break; + case 'parentingHelp': + if (s.parentingHelp.length) { + categ.openned = true; + } + break; + case 'digitalCultureSecurity': + if (s.digitalCultureSecurity.length) { + categ.openned = true; + } + break; + } + }); + this.equipmentsAndServices.forEach((equipment: { module: Module; openned: boolean }) => { + switch (equipment.module.id) { + case 'ordinateurs': + if (s.equipmentsAndServices.includes('ordinateurs')) { + equipment.openned = true; + } + break; + case 'tablettes': + if (s.equipmentsAndServices.includes('tablettes')) { + equipment.openned = true; + } + break; + case 'bornesNumeriques': + if (s.equipmentsAndServices.includes('bornesNumeriques')) { + equipment.openned = true; + } + break; + case 'imprimantes': + if (s.equipmentsAndServices.includes('imprimantes')) { + equipment.openned = true; + } + break; + case 'scanners': + if (s.equipmentsAndServices.includes('scanners')) { + equipment.openned = true; + } + break; + } + }); + } private loadArrayForCheckbox(array: string[], isRequired: boolean): FormArray { return new FormArray( @@ -254,8 +324,8 @@ export class FormComponent implements OnInit { } private createTime(time: Time): FormGroup { return new FormGroup({ - openning: new FormControl(time.openning, Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$')), //NOSONAR - closing: new FormControl(time.closing, Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$')), //NOSONAR + openning: new FormControl(time.openning), //NOSONAR + closing: new FormControl(time.closing), //NOSONAR }); } diff --git a/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts b/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts index 7de312486..63b880768 100644 --- a/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts +++ b/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts @@ -22,11 +22,11 @@ export class StructureTypePickerComponent implements OnInit { constructor(private structureTypeService: StructureTypeService) {} ngOnInit() { - if (this.pickedChoice) { - this.pickedType = this.getType(this.pickedChoice); - } this.structureTypeService.getStructureTypes().subscribe((types) => { this.structureTypes = types; + if (this.pickedChoice) { + this.pickedType = this.getType(this.pickedChoice); + } }); } 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 58a2e2d98..7a84db49a 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 @@ -1,11 +1,3 @@ -<app-structureForm - *ngIf="showForm" - [idStructure]="structure._id" - [isEditMode]="isEditMode" - [profile]="currentProfile" - (closeEvent)="updateStructure($event)" - (clickOutside)="displayForm()" -></app-structureForm> <div class="structrue-details-container" *ngIf="structure && !isLoading"> <!-- Header info --> <div fxLayout="row" fxLayoutAlign="end center"> @@ -87,14 +79,15 @@ >Revendiquer cette structure</a > <!-- temporary remove edit --> - <!-- <a + <a *ngIf="profileService.isLinkedToStructure(structure._id) || profileService.isAdmin()" - (click)="editStructure()" + routerLink="/create-structure" + [state]="{ data: structure }" class="primary" tabindex="0" > Modifier cette structure - </a> --> + </a> <a *ngIf="profileService.isAdmin()" (click)="toggleDeleteModal()" class="primary" tabindex="0"> Supprimer cette structure </a> 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 4395765f1..c94ea5747 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 @@ -23,7 +23,6 @@ import { PublicCategorie } from '../../enum/public.enum'; export class StructureDetailsComponent implements OnInit { @Input() public structure: Structure; @Output() public closeDetails: EventEmitter<boolean> = new EventEmitter<boolean>(); - @Output() public updatedStructure: EventEmitter<Structure> = new EventEmitter<Structure>(); public accessModality = AccessModality; public baseSkillssReferentiel: Category; @@ -33,10 +32,8 @@ export class StructureDetailsComponent implements OnInit { public tclStopPoints: TclStopPoint[] = []; public printMode = false; public isOtherSection = false; - public showForm = false; public isClaimed: boolean = null; public isLoading: boolean = false; - public isEditMode: boolean = false; public currentProfile: User = null; public deleteModalOpenned = false; public claimModalOpenned = false; @@ -119,11 +116,6 @@ export class StructureDetailsComponent implements OnInit { this.printService.printDocument('structure', this.structure); } - public editStructure(): void { - this.isEditMode = true; - this.displayForm(); - } - public toggleDeleteModal(): void { this.deleteModalOpenned = !this.deleteModalOpenned; } @@ -157,17 +149,6 @@ export class StructureDetailsComponent implements OnInit { }); } } - // Show/hide form structure - public displayForm(): void { - this.showForm = !this.showForm; - } - - public updateStructure(s: Structure): void { - this.structure = new Structure({ ...this.structure, ...s }); - this.updatedStructure.emit(this.structure); - this.displayForm(); - this.ngOnInit(); - } public getAccessLabel(accessModality: AccessModality): string { switch (accessModality) { -- GitLab From b894bc03eda5837354128cbedff4f1177cbedbde Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Mon, 8 Feb 2021 16:18:34 +0100 Subject: [PATCH 07/72] fix(form) : fix footer form when edit + init summary --- src/app/form/form.component.html | 31 ++++++++++-- src/app/form/form.component.scss | 19 +++++++- src/app/form/form.component.ts | 84 ++++++++++++++++++++++---------- src/assets/scss/_layout.scss | 3 +- 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 526e3a3ed..a09a877e4 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -7,7 +7,7 @@ <div class="content"> <div class="progressBar" - *ngIf="currentPage != 0" + *ngIf="currentPage != 0 && !isEditMode" fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="20px" @@ -22,7 +22,7 @@ [value]="progressStatus" ></progress> </div> - <div *ngIf="currentPage == 0" class="home page" fxLayout="column" fxLayoutAlign="space-between"> + <div *ngIf="currentPage == 0 && !isEditMode" class="home page" fxLayout="column" fxLayoutAlign="space-between"> <h2>Ajouter votre structure</h2> <img src="../../assets/form/schedule.svg" alt="logo schedule" /> <div> @@ -33,6 +33,16 @@ <button class="btn start" (click)="nextPage()">C'est Parti</button> </div> </div> + <div *ngIf="currentPage == 0 && isEditMode" class="home page" fxLayout="column" fxLayoutAlign="space-between"> + <h2>Modification de la structure</h2> + <div> + <div class="summary" *ngFor="let page of pagesValidation; let index = index"> + <div class="itemSummary" *ngIf="page.name" (click)="goToSpecificPage(index)"> + {{ page.name }} + </div> + </div> + </div> + </div> <div *ngIf="currentPage == 1" class="informations page" fxLayout="column" fxLayoutGap="28px"> <h3>De quelles informations faut-il vous munir ?</h3> <img src="../../assets/form/factures.svg" alt="logo factures" /> @@ -171,7 +181,7 @@ </div> </div> </form> - <form [formGroup]="structureForm" *ngIf="structureForm"> + <form [formGroup]="structureForm" *ngIf="structureForm" [ngClass]="{ editMode: isEditMode }"> <div *ngIf="currentPage == 4" class="page"> <div class="title"> <h3>Quelle structure voulez-vous réferencer ?</h3> @@ -974,13 +984,16 @@ </div> </form> <div *ngIf="currentPage != 0" class="footer desktop"> - <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm"> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form (previousPage)="previousPage()" (nextPage)="nextPage()" [isValid]="isPageValid" ></app-footer-form> </div> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> + <button class="btn" (click)="goToSpecificPage(0)">Validé</button> + </div> <button *ngIf="currentPage == nbPagesForm && !profile" class="btn validate unique" @@ -998,15 +1011,23 @@ Voir ma structure </button> </div> + <div *ngIf="isEditMode" class="footerEditMode"> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode && currentPage == 0"> + <button class="btn unique" (click)="validateForm()">Terminé</button> + </div> + </div> </div> <div *ngIf="currentPage != 0" class="footer phone"> - <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm"> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form (previousPage)="previousPage()" (nextPage)="nextPage()" [isValid]="isPageValid" ></app-footer-form> </div> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> + <button class="btn unique" (click)="goToSpecificPage(0)">Validé</button> + </div> <button *ngIf="currentPage == nbPagesForm && !profile" class="btn validate unique" diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 203a87dac..2569f0c24 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -5,10 +5,18 @@ @import '../../assets/scss/shapes'; @import '../../assets/scss/z-index'; -$progressBar-height: 50px; h3 { margin: 0; } +form { + @media #{$tablet} { + &.editMode { + .page { + height: calc(100vh - #{$header-height-phone} - #{$footer-height-phone} - 1px); // -1px because of header border + } + } + } +} .form { background: white; width: 100vw; @@ -449,3 +457,12 @@ img { } } } +.footerEditMode { + width: 100%; + position: fixed; + bottom: 56px; + margin: 20px 0; + @media #{$tablet} { + bottom: 0; + } +} diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 41f05124b..ce8f865fc 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -367,13 +367,20 @@ export class FormComponent implements OnInit { }; this.pagesValidation[4] = { valid: this.getStructureControl('structureName').valid && this.getStructureControl('address').valid, + name: 'Nom et adresse', + }; + this.pagesValidation[5] = { valid: this.getStructureControl('contactPhone').valid, name: 'Téléphone' }; + this.pagesValidation[6] = { valid: this.getStructureControl('structureType').valid, name: 'Type de structure' }; + this.pagesValidation[7] = { valid: this.getStructureControl('accessModality').valid, name: "Modalités d'accueil " }; + this.pagesValidation[8] = { valid: this.hoursForm.valid, name: "Horaires d'ouverture" }; + this.pagesValidation[9] = { + valid: this.getStructureControl('exceptionalClosures').valid, + name: 'Précisions sur les horaires', + }; + this.pagesValidation[10] = { + valid: this.getStructureControl('pmrAccess').valid, + name: 'Accessibilité pour les personnes à mobilité réduite', }; - this.pagesValidation[5] = { valid: this.getStructureControl('contactPhone').valid }; - this.pagesValidation[6] = { valid: this.getStructureControl('structureType').valid }; - this.pagesValidation[7] = { valid: this.getStructureControl('accessModality').valid }; - this.pagesValidation[8] = { valid: this.hoursForm.valid }; - this.pagesValidation[9] = { valid: this.getStructureControl('exceptionalClosures').valid }; - this.pagesValidation[10] = { valid: this.getStructureControl('pmrAccess').valid }; this.pagesValidation[11] = { valid: this.getStructureControl('contactMail').valid && @@ -382,15 +389,18 @@ export class FormComponent implements OnInit { this.getStructureControl('twitter').valid && this.getStructureControl('instagram').valid) || !this.showSocialNetwork), + name: 'Présence sur internet', }; - this.pagesValidation[12] = { valid: this.getStructureControl('publics').valid }; + this.pagesValidation[12] = { valid: this.getStructureControl('publics').valid, name: 'Public admis' }; this.pagesValidation[13] = { valid: this.getStructureControl('publicsAccompaniment').valid && this.getStructureControl('proceduresAccompaniment').valid, + name: 'Accompagnements proposés', }; this.pagesValidation[14] = { valid: this.getStructureControl('otherDescription').value, + name: 'Autres démarches proposés', }; this.pagesValidation[15] = { valid: @@ -399,9 +409,10 @@ export class FormComponent implements OnInit { this.getStructureControl('baseSkills').valid && this.getStructureControl('parentingHelp').valid && this.getStructureControl('digitalCultureSecurity').valid, + name: 'Ateliers au numérique proposés', }; - this.pagesValidation[16] = { valid: this.getStructureControl('freeWorkShop').valid }; - this.pagesValidation[17] = { valid: this.getStructureControl('freeWifi').valid }; + this.pagesValidation[16] = { valid: this.getStructureControl('freeWorkShop').valid, name: 'Gratuité des ateliers' }; + this.pagesValidation[17] = { valid: this.getStructureControl('freeWifi').valid, name: 'Gratuité du wifi' }; this.pagesValidation[18] = { valid: this.getStructureControl('equipmentsAndServices').valid && @@ -410,11 +421,24 @@ export class FormComponent implements OnInit { this.getStructureControl('nbTablets').valid && this.getStructureControl('nbNumericTerminal').valid && this.getStructureControl('nbScanners').valid, + name: 'Matériels mis à disposition', + }; + this.pagesValidation[19] = { + valid: this.getStructureControl('labelsQualifications').valid, + name: 'Labélisations proposées', + }; + this.pagesValidation[20] = { + valid: this.getStructureControl('equipmentsAndServices').valid, + name: 'Autres services proposés', + }; + this.pagesValidation[21] = { + valid: this.getStructureControl('description').valid, + name: 'Présentation de la structure', + }; + this.pagesValidation[22] = { + valid: this.getStructureControl('lockdownActivity').valid, + name: 'Informations spécifiques à la période COVID', }; - this.pagesValidation[19] = { valid: this.getStructureControl('labelsQualifications').valid }; - this.pagesValidation[20] = { valid: this.getStructureControl('equipmentsAndServices').valid }; - this.pagesValidation[21] = { valid: this.getStructureControl('description').valid }; - this.pagesValidation[22] = { valid: this.getStructureControl('lockdownActivity').valid }; this.pagesValidation[23] = { valid: this.userAcceptSavedDate }; //this.pagesValidation[24] = { valid: true }; this.updatePageValid(); @@ -577,19 +601,23 @@ export class FormComponent implements OnInit { let structure: Structure = this.structureForm.value; structure.hours = this.hoursForm.value; let user: User; - if (this.profile) { - user = this.profile; - structure.accountVerified = true; - this.createStructure(structure, user); + if (this.isEditMode) { + console.log('ok'); } else { - if (this.accountForm.valid) { - user = new User(this.accountForm.value); - this.authService - .register(user) - .pipe(first()) - .subscribe(() => { - this.createStructure(structure, user); - }); + if (this.profile) { + user = this.profile; + structure.accountVerified = true; + this.createStructure(structure, user); + } else { + if (this.accountForm.valid) { + user = new User(this.accountForm.value); + this.authService + .register(user) + .pipe(first()) + .subscribe(() => { + this.createStructure(structure, user); + }); + } } } } @@ -627,4 +655,10 @@ export class FormComponent implements OnInit { this.resolve(hasAccept); this.showConfirmationModal = false; } + + // Function for editMode only + + public goToSpecificPage(numPage: number): void { + this.currentPage = numPage; + } } diff --git a/src/assets/scss/_layout.scss b/src/assets/scss/_layout.scss index b80043aa2..94ebedd43 100644 --- a/src/assets/scss/_layout.scss +++ b/src/assets/scss/_layout.scss @@ -1,4 +1,5 @@ $header-height: 70px; $footer-height: 56px; -$header-height-phone: 50px; +$header-height-phone: 70px; $footer-height-phone: 75px; +$progressBar-height: 50px; -- GitLab From e2285fab985a2eb166c5445784ad652e8ecb6e76 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Mon, 8 Feb 2021 18:55:28 +0100 Subject: [PATCH 08/72] fix(form) : fix api + add logic to cancel + validate form --- src/app/form/form.component.html | 50 ++++++++++++++++--- src/app/form/form.component.scss | 72 +++++++++++++++++++++++---- src/app/form/form.component.ts | 58 +++++++++++++-------- src/app/services/structure.service.ts | 3 +- 4 files changed, 144 insertions(+), 39 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index a09a877e4..1af4e1fcb 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -4,7 +4,17 @@ [content]="'Il vous faudra de nouveau remplir le formulaire si vous quittez'" (closed)="hasRedirectionAccepted($event)" ></app-modal-confirmation> - <div class="content"> + <div class="content" [ngClass]="{ editMode: isEditMode }"> + <div class="returnBtnSection" *ngIf="isEditMode && currentPage != 0"> + <button class="btn previous" (click)="goToSpecificPage(0, false)"> + <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> + <svg class="chevronLeft" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> + </svg> + Retour + </div> + </button> + </div> <div class="progressBar" *ngIf="currentPage != 0 && !isEditMode" @@ -33,12 +43,22 @@ <button class="btn start" (click)="nextPage()">C'est Parti</button> </div> </div> - <div *ngIf="currentPage == 0 && isEditMode" class="home page" fxLayout="column" fxLayoutAlign="space-between"> + <div *ngIf="currentPage == 0 && isEditMode" class="editHome page" fxLayout="column" fxLayoutAlign="space-between"> <h2>Modification de la structure</h2> <div> <div class="summary" *ngFor="let page of pagesValidation; let index = index"> - <div class="itemSummary" *ngIf="page.name" (click)="goToSpecificPage(index)"> + <div + class="itemSummary" + [ngClass]="{ last: index == 22 }" + fxLayout="row" + fxLayoutAlign="space-between center" + *ngIf="page.name" + (click)="goToSpecificPage(index, false)" + > {{ page.name }} + <svg class="chevronRight" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> + </svg> </div> </div> </div> @@ -181,7 +201,7 @@ </div> </div> </form> - <form [formGroup]="structureForm" *ngIf="structureForm" [ngClass]="{ editMode: isEditMode }"> + <form [formGroup]="structureForm" *ngIf="structureForm"> <div *ngIf="currentPage == 4" class="page"> <div class="title"> <h3>Quelle structure voulez-vous réferencer ?</h3> @@ -992,7 +1012,14 @@ ></app-footer-form> </div> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> - <button class="btn" (click)="goToSpecificPage(0)">Validé</button> + <button + class="btn" + [ngClass]="{ invalid: !isPageValid }" + [disabled]="!isPageValid" + (click)="goToSpecificPage(0, true)" + > + Validé + </button> </div> <button *ngIf="currentPage == nbPagesForm && !profile" @@ -1011,8 +1038,8 @@ Voir ma structure </button> </div> - <div *ngIf="isEditMode" class="footerEditMode"> - <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode && currentPage == 0"> + <div *ngIf="isEditMode && currentPage == 0" class="footerEditMode"> + <div fxLayout="row" fxLayoutAlign="center center"> <button class="btn unique" (click)="validateForm()">Terminé</button> </div> </div> @@ -1026,7 +1053,14 @@ ></app-footer-form> </div> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> - <button class="btn unique" (click)="goToSpecificPage(0)">Validé</button> + <button + class="btn" + [ngClass]="{ invalid: !isPageValid }" + [disabled]="!isPageValid" + (click)="goToSpecificPage(0, true)" + > + Validé + </button> </div> <button *ngIf="currentPage == nbPagesForm && !profile" diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 2569f0c24..514a150a0 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -8,15 +8,6 @@ h3 { margin: 0; } -form { - @media #{$tablet} { - &.editMode { - .page { - height: calc(100vh - #{$header-height-phone} - #{$footer-height-phone} - 1px); // -1px because of header border - } - } - } -} .form { background: white; width: 100vw; @@ -43,6 +34,9 @@ form { &.unique { width: 240px; } + &.invalid { + opacity: 0.4; + } } &.desktop { @media #{$tablet} { @@ -98,6 +92,23 @@ form { } } .content { + .editHome { + height: calc( + 100vh - #{$header-height} - #{$footer-height} - 81px - 1px + ) !important; // -1px because of header border + } + @media #{$tablet} { + &.editMode { + .page { + height: calc( + 100vh - #{$header-height-phone} - #{$footer-height-phone} - 87px - 1px + ); // -1px because of header border + } + .editHome { + height: calc(100vh - #{$header-height-phone} - 87px - 1px) !important; // -1px because of header border + } + } + } padding: 0 16px; display: block; overflow-y: auto; @@ -208,10 +219,29 @@ form { width: 192px; @include btn-bold; + &.previous { + background-color: initial; + color: $grey-2; + width: 120px; + border-radius: 6px; + border: 1px solid $grey-4; + } &.start { margin-bottom: 26px; } } +.chevronRight { + height: 24px; + width: 24px; + stroke: $grey-2; + margin-left: 10px; +} +.chevronLeft { + height: 24px; + width: 24px; + stroke: $black; + margin-right: 10px; +} .progressBar { height: #{$progressBar-height}; max-width: 960px; @@ -276,6 +306,7 @@ img { border: 1px solid $grey-4; border-radius: 4px; margin-bottom: 13px; + //margin-right: 40px; @media #{$tablet} { width: 296px; } @@ -461,8 +492,29 @@ img { width: 100%; position: fixed; bottom: 56px; - margin: 20px 0; + margin: 0; + background: $white; + left: 0; + border-top: 1px solid $grey-4; + padding: 20px 0; @media #{$tablet} { bottom: 0; + left: 0; + } +} +.returnBtnSection { + max-width: 960px; + margin: 24px auto; +} +.itemSummary { + height: 60px; + border-bottom: 1px solid $grey-4; + @include cn-bold-20; + cursor: pointer; + &:hover { + background: #0000000d; + } + &.last { + border: 0; } } diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index ce8f865fc..41603d55c 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -30,6 +30,7 @@ export class FormComponent implements OnInit { public structureForm: FormGroup; public accountForm: FormGroup; public hoursForm: FormGroup; + public editForm: FormGroup; public labelsQualifications: Category; public publics: Category; public accessModality: Category; @@ -66,7 +67,8 @@ export class FormComponent implements OnInit { private structureService: StructureService, private searchService: SearchService, private profileService: ProfileService, - private authService: AuthService + private authService: AuthService, + private router: Router ) {} async ngOnInit(): Promise<void> { @@ -138,7 +140,27 @@ export class FormComponent implements OnInit { ); // Init form - this.structureForm = new FormGroup({ + this.structureForm = this.createStructureForm(structure); + this.editForm = this.createStructureForm(structure); + + // Init hours form + this.hoursForm = new FormGroup({ + monday: this.createDay(structure.hours.monday), + tuesday: this.createDay(structure.hours.tuesday), + wednesday: this.createDay(structure.hours.wednesday), + thursday: this.createDay(structure.hours.thursday), + friday: this.createDay(structure.hours.friday), + saturday: this.createDay(structure.hours.saturday), + sunday: this.createDay(structure.hours.sunday), + }); + if (this.isEditMode) { + this.showCollapse(structure); + } + + this.setValidationsForm(); + } + private createStructureForm(structure): FormGroup { + const form = new FormGroup({ _id: new FormControl(structure._id), coord: new FormControl(structure.coord), structureType: new FormControl(structure.structureType, Validators.required), @@ -201,22 +223,7 @@ export class FormComponent implements OnInit { freeWorkShop: new FormControl(structure.freeWorkShop, Validators.required), freeWifi: new FormControl(structure.freeWifi, Validators.required), }); - - // Init hours form - this.hoursForm = new FormGroup({ - monday: this.createDay(structure.hours.monday), - tuesday: this.createDay(structure.hours.tuesday), - wednesday: this.createDay(structure.hours.wednesday), - thursday: this.createDay(structure.hours.thursday), - friday: this.createDay(structure.hours.friday), - saturday: this.createDay(structure.hours.saturday), - sunday: this.createDay(structure.hours.sunday), - }); - if (this.isEditMode) { - this.showCollapse(structure); - } - - this.setValidationsForm(); + return form; } private showCollapse(s: Structure): void { if (s.website) { @@ -232,6 +239,7 @@ export class FormComponent implements OnInit { this.showProceduresAccompaniment = true; } this.trainingCategories.forEach((categ: { category: Category; openned: boolean }) => { + categ.openned = false; switch (categ.category.id) { case 'accessRight': if (s.accessRight.length) { @@ -261,6 +269,7 @@ export class FormComponent implements OnInit { } }); this.equipmentsAndServices.forEach((equipment: { module: Module; openned: boolean }) => { + equipment.openned = false; switch (equipment.module.id) { case 'ordinateurs': if (s.equipmentsAndServices.includes('ordinateurs')) { @@ -602,7 +611,9 @@ export class FormComponent implements OnInit { structure.hours = this.hoursForm.value; let user: User; if (this.isEditMode) { - console.log('ok'); + this.structureService.editStructure(structure).subscribe((s: Structure) => { + this.router.navigateByUrl('home', { state: { data: s } }); + }); } else { if (this.profile) { user = this.profile; @@ -658,7 +669,14 @@ export class FormComponent implements OnInit { // Function for editMode only - public goToSpecificPage(numPage: number): void { + public goToSpecificPage(numPage: number, isSave: boolean): void { + if (isSave) { + this.editForm = this.createStructureForm(new Structure(this.structureForm.value)); + } else { + const structure = new Structure(this.editForm.value); + this.structureForm = this.createStructureForm(structure); + this.showCollapse(structure); + } this.currentPage = numPage; } } diff --git a/src/app/services/structure.service.ts b/src/app/services/structure.service.ts index 41a02af5a..e370f3c66 100644 --- a/src/app/services/structure.service.ts +++ b/src/app/services/structure.service.ts @@ -31,8 +31,9 @@ export class StructureService { return this.http.post(`${this.baseUrl}`, { structure, idUser }).pipe(map((item: Structure) => new Structure(item))); } - public editStructure(id: string, structure: Structure): Observable<Structure> { + public editStructure(structure: Structure): Observable<Structure> { structure.updatedAt = new Date().toString(); + const id = structure._id; delete structure._id; // id should not be provided for update return this.http.put(`${this.baseUrl}/${id}`, structure).pipe(map((item: Structure) => new Structure(item))); } -- GitLab From 0f3da6c5e657c550ce044be706619b362dc1f303 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Mon, 8 Feb 2021 19:03:21 +0100 Subject: [PATCH 09/72] fix(form) : fix deactivate condition --- src/app/form/form.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 41603d55c..534201db7 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -651,7 +651,7 @@ export class FormComponent implements OnInit { public canExit(): Promise<boolean> { // Avoid confirmation when user submit form and leave. - if (this.currentPage == this.nbPagesForm) { + if (this.currentPage == this.nbPagesForm || this.isEditMode) { return new Promise((resolve) => resolve(true)); } else { return new Promise((resolve) => this.showModal(resolve)); -- GitLab From 3a6753a0ad7bedee5ed6bf54625a8912fec3a622 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Mon, 8 Feb 2021 19:14:36 +0100 Subject: [PATCH 10/72] fix(detailsStructure) : hide services on equipments tab --- src/app/structure-list/components/card/card.component.html | 2 +- src/app/structure-list/components/card/card.component.ts | 5 +++++ .../structure-details/structure-details.component.html | 5 +++-- .../structure-details/structure-details.component.ts | 5 +++++ 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/structure-list/components/card/card.component.html b/src/app/structure-list/components/card/card.component.html index b9bc6cc92..2a02fd3a6 100644 --- a/src/app/structure-list/components/card/card.component.html +++ b/src/app/structure-list/components/card/card.component.html @@ -16,7 +16,7 @@ <span class="typeStructure">{{ structure.getLabelTypeStructure() }}</span> <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="7px" *ngIf="structure.hasEquipments()"> <app-svg-icon - *ngFor="let equipement of structure.equipmentsAndServices" + *ngFor="let equipement of filterOnlyEquipments(structure.equipmentsAndServices)" [type]="'ico'" [iconColor]="'grey'" [icon]="structure.getEquipmentsIcon(equipement)" diff --git a/src/app/structure-list/components/card/card.component.ts b/src/app/structure-list/components/card/card.component.ts index 58d7f395d..3f3b6d5af 100644 --- a/src/app/structure-list/components/card/card.component.ts +++ b/src/app/structure-list/components/card/card.component.ts @@ -35,4 +35,9 @@ export class CardComponent implements OnInit { public cardHover(): void { this.hover.emit(this.structure); } + public filterOnlyEquipments(equipmentsAndServices: string[]): string[] { + return equipmentsAndServices.filter((eqpt) => + ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners'].includes(eqpt) + ); + } } 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 7a84db49a..effd118fd 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 @@ -12,8 +12,9 @@ <div class="typeInformationHeader" fxLayout="column"> <h3>{{ structure.getLabelTypeStructure() }}</h3> <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="7px" *ngIf="structure.hasEquipments()"> + <div></div> <app-svg-icon - *ngFor="let equipement of structure.equipmentsAndServices" + *ngFor="let equipement of filterOnlyEquipments(structure.equipmentsAndServices)" [type]="'ico'" [iconColor]="'currentColor'" [icon]="structure.getEquipmentsIcon(equipement)" @@ -215,7 +216,7 @@ <h2>Équipements</h2> </div> <div fxLayout="column"> - <p *ngFor="let equipement of structure.equipmentsAndServices" class="no-margin-bottom"> + <p *ngFor="let equipement of filterOnlyEquipments(structure.equipmentsAndServices)" class="no-margin-bottom"> {{ getEquipmentsLabel(equipement) }} <span *ngIf="equipement == 'ordinateurs' && structure.nbComputers"> : {{ structure.nbComputers }}</span> <span *ngIf="equipement == 'tablettes' && structure.nbTablets"> : {{ structure.nbTablets }}</span> 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 c94ea5747..9029f08a0 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 @@ -203,4 +203,9 @@ export class StructureDetailsComponent implements OnInit { this.tclStopPoints = res; }); } + public filterOnlyEquipments(equipmentsAndServices: string[]): string[] { + return equipmentsAndServices.filter((eqpt) => + ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners'].includes(eqpt) + ); + } } -- GitLab From 5cffa1b2dc5282a63e936c2cf0c3f5d19fdc90ab Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 9 Feb 2021 12:24:33 +0100 Subject: [PATCH 11/72] fix(form) : fix description/covid --- src/app/form/form.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 534201db7..03290090e 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -166,7 +166,7 @@ export class FormComponent implements OnInit { structureType: new FormControl(structure.structureType, Validators.required), structureName: new FormControl(structure.structureName, Validators.required), description: new FormControl(structure.description), - lockdownActivity: new FormControl(structure.description), + lockdownActivity: new FormControl(structure.lockdownActivity), address: new FormGroup({ numero: new FormControl(structure.address.numero), street: new FormControl(structure.address.street, Validators.required), -- GitLab From 68b2eb8fb8e127ee2d27ac090b668131e05c5d2e Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 9 Feb 2021 12:28:40 +0100 Subject: [PATCH 12/72] fix(structureDetails) : add a forgot category --- .../components/structure-details/structure-details.component.ts | 2 ++ src/app/structure-list/enum/public.enum.ts | 1 + 2 files changed, 3 insertions(+) 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 9029f08a0..49633ddf7 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 @@ -175,6 +175,8 @@ export class StructureDetailsComponent implements OnInit { return 'Séniors (+ de 65 ans)'; case PublicCategorie.all: return 'Tout public'; + case PublicCategorie.under16Years: + return 'Moins de 16 ans'; default: return null; } diff --git a/src/app/structure-list/enum/public.enum.ts b/src/app/structure-list/enum/public.enum.ts index 1f8371303..272561297 100644 --- a/src/app/structure-list/enum/public.enum.ts +++ b/src/app/structure-list/enum/public.enum.ts @@ -1,4 +1,5 @@ export enum PublicCategorie { + under16Years = 'moinsDe16Ans', young = 'jeunes1625Ans', adult = 'adultes', elderly = 'seniorsPlusDe65Ans', -- GitLab From 09ad13b03f5da9ccda9f178cd2768ed3ca01a798 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 9 Feb 2021 16:20:06 +0100 Subject: [PATCH 13/72] fix(form) : fix regex to accept space on surname --- src/app/shared/enum/regex.enum.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/enum/regex.enum.ts b/src/app/shared/enum/regex.enum.ts index 7a564960f..4f83475e6 100644 --- a/src/app/shared/enum/regex.enum.ts +++ b/src/app/shared/enum/regex.enum.ts @@ -1,6 +1,6 @@ export enum Regex { email = '[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}', - textWithoutNumber = '[A-Za-zÀ-ÖØ-öø-ÿ-]{1,}', + textWithoutNumber = '[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}', phone = '([0-9]{2} ){4}[0-9]{2}', website = '(www[.])[a-z0-9.-]*[.][a-z]{2,3}', linkedIn = '(linkedin.com/in/[a-z0-9A-Z.-]{1,})', -- GitLab From 23639cde03635327347209ffc1c268b55a379216 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 9 Feb 2021 16:21:52 +0100 Subject: [PATCH 14/72] fix(form) : fix text --- src/app/form/form.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 1af4e1fcb..37549e72e 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -277,7 +277,7 @@ </div> <div *ngIf="currentPage == 7" class="page"> <div class="title"> - <h3>Quels sont les modalités d'accueil ?</h3> + <h3>Quelles sont les modalités d'accueil ?</h3> <p>Plusieurs choix possibles</p> </div> <div *ngIf="accessModality" fxLayout="row wrap" fxLayoutGap="16px" fxLayoutAlign="flex-start"> -- GitLab From 1e210a0b974199990ac3b5d48a34e0c6a8bb4419 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 9 Feb 2021 17:24:19 +0100 Subject: [PATCH 15/72] fix: header issue on add structure navigation --- src/app/header/header.component.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 2c2af037e..aab75d532 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 { NavigationStart, Router } from '@angular/router'; +import { NavigationEnd, NavigationStart, Router } from '@angular/router'; import { ProfileService } from '../profile/services/profile.service'; import { AuthService } from '../services/auth.service'; @@ -17,8 +17,7 @@ export class HeaderComponent implements OnInit { constructor(private authService: AuthService, private profileService: ProfileService, private router: Router) { this.router.events.subscribe((event) => { - if (event instanceof NavigationStart) { - // Show loading indicator.curr + if (event instanceof NavigationEnd) { this.currentRoute = event.url; } }); -- GitLab From 0f4045503d6df24002ac21d174dd6b92462318a4 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 9 Feb 2021 17:44:54 +0100 Subject: [PATCH 16/72] fix(buttonForm) : export logic to buttons.scss --- .../footer-form/footer-form.component.html | 9 +++++-- .../footer-form/footer-form.component.scss | 11 +-------- src/app/form/form.component.html | 18 +++++++------- src/app/form/form.component.scss | 24 +------------------ .../modal-confirmation.component.html | 4 ++-- .../modal-confirmation.component.scss | 19 ++++----------- src/assets/scss/_buttons.scss | 15 ++++++++++++ 7 files changed, 39 insertions(+), 61 deletions(-) diff --git a/src/app/footer-form/footer-form.component.html b/src/app/footer-form/footer-form.component.html index b4c50c392..c2e91330f 100644 --- a/src/app/footer-form/footer-form.component.html +++ b/src/app/footer-form/footer-form.component.html @@ -1,5 +1,5 @@ <div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"> - <button class="btn previous" (click)="goToPreviousPage()"> + <button class="btn-primary small previous" (click)="goToPreviousPage()"> <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> <svg class="chevronLeft" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> @@ -7,7 +7,12 @@ Précédent </div> </button> - <button class="btn next" (click)="goToNextPage()" [disabled]="!isValid" [ngClass]="{ invalid: !isValid }"> + <button + class="btn-primary small next" + (click)="goToNextPage()" + [disabled]="!isValid" + [ngClass]="{ invalid: !isValid }" + > <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> Suivant<svg class="chevronRight" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> diff --git a/src/app/footer-form/footer-form.component.scss b/src/app/footer-form/footer-form.component.scss index dfecb8334..7df6b3dd1 100644 --- a/src/app/footer-form/footer-form.component.scss +++ b/src/app/footer-form/footer-form.component.scss @@ -1,16 +1,7 @@ @import '../../assets/scss/color'; @import '../../assets/scss/typography'; -.btn { - background: $secondary-color; - border-radius: 4px; - outline: none; - cursor: pointer; - border: 0; - color: $white; - height: 40px; - width: 149px; - @include btn-bold; +.btn-primary { &.previous { background-color: initial; color: $grey-2; diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 37549e72e..176b86f8f 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -6,7 +6,7 @@ ></app-modal-confirmation> <div class="content" [ngClass]="{ editMode: isEditMode }"> <div class="returnBtnSection" *ngIf="isEditMode && currentPage != 0"> - <button class="btn previous" (click)="goToSpecificPage(0, false)"> + <button class="btn-primary previous" (click)="goToSpecificPage(0, false)"> <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> <svg class="chevronLeft" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> @@ -40,7 +40,7 @@ <p>Une fois réalisé cela vous permettra d'être référencé sur la platefome</p> </div> <div class="btnStart"> - <button class="btn start" (click)="nextPage()">C'est Parti</button> + <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> </div> </div> <div *ngIf="currentPage == 0 && isEditMode" class="editHome page" fxLayout="column" fxLayoutAlign="space-between"> @@ -1013,7 +1013,7 @@ </div> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> <button - class="btn" + class="btn-primary" [ngClass]="{ invalid: !isPageValid }" [disabled]="!isPageValid" (click)="goToSpecificPage(0, true)" @@ -1023,7 +1023,7 @@ </div> <button *ngIf="currentPage == nbPagesForm && !profile" - class="btn validate unique" + class="btn-primary validate unique" routerLink="/home" [routerLinkActive]="'active'" > @@ -1031,7 +1031,7 @@ </button> <button *ngIf="currentPage == nbPagesForm && profile" - class="btn unique" + class="btn-primary unique" routerLink="/home" [state]="{ data: createdStructure }" > @@ -1040,7 +1040,7 @@ </div> <div *ngIf="isEditMode && currentPage == 0" class="footerEditMode"> <div fxLayout="row" fxLayoutAlign="center center"> - <button class="btn unique" (click)="validateForm()">Terminé</button> + <button class="btn-primary unique" (click)="validateForm()">Terminé</button> </div> </div> </div> @@ -1054,7 +1054,7 @@ </div> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> <button - class="btn" + class="btn-primary" [ngClass]="{ invalid: !isPageValid }" [disabled]="!isPageValid" (click)="goToSpecificPage(0, true)" @@ -1064,7 +1064,7 @@ </div> <button *ngIf="currentPage == nbPagesForm && !profile" - class="btn validate unique" + class="btn-primary validate unique" routerLink="/home" [routerLinkActive]="'active'" > @@ -1072,7 +1072,7 @@ </button> <button *ngIf="currentPage == nbPagesForm && profile" - class="btn unique" + class="btn-primary unique" routerLink="/home" [state]="{ data: createdStructure }" > diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 514a150a0..5766a9a3c 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -26,18 +26,6 @@ h3 { max-width: 960px; margin: 20px auto; text-align: center; - .btn { - width: 149px; - &.validate { - background-color: $green-1; - } - &.unique { - width: 240px; - } - &.invalid { - opacity: 0.4; - } - } &.desktop { @media #{$tablet} { display: none; @@ -208,17 +196,7 @@ h3 { } } -.btn { - background: $secondary-color; - border-radius: 4px; - outline: none; - cursor: pointer; - border: 0; - color: $white; - height: 40px; - width: 192px; - @include btn-bold; - +.btn-primary { &.previous { background-color: initial; color: $grey-2; diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.html b/src/app/shared/components/modal-confirmation/modal-confirmation.component.html index fe2cf5816..603c74f41 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.html +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.html @@ -4,8 +4,8 @@ <h3>ATTENTION</h3> <p>{{ content }}</p> <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> - <button class="btn leave" (click)="closeModal(true)">Confirmer</button> - <button class="btn" (click)="closeModal(false)">Annuler</button> + <button class="btn-primary small leave" (click)="closeModal(true)">Confirmer</button> + <button class="btn-primary small" (click)="closeModal(false)">Annuler</button> </div> </div> </div> diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss index 5411fa484..7e11f5659 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss @@ -29,21 +29,10 @@ width: 100%; margin-top: 14px; @include cn-bold-16; - .btn { - background: $secondary-color; - border-radius: 4px; - outline: none; - cursor: pointer; - border: 0; - color: $white; - height: 40px; - @include btn-bold; - width: 149px; - &.leave { - background: none; - color: $grey-1; - text-decoration: underline; - } + .leave { + background: none; + color: $grey-1; + text-decoration: underline; } } } diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss index 6f8458fcf..76f220fca 100644 --- a/src/assets/scss/_buttons.scss +++ b/src/assets/scss/_buttons.scss @@ -62,3 +62,18 @@ line-height: 18px; padding: 8px 15px; } + +.btn-primary { + background: $secondary-color; + border-radius: 4px; + outline: none; + cursor: pointer; + border: 0; + color: $white; + height: 40px; + width: 192px; + @include btn-bold; + &.small { + width: 149px; + } +} -- GitLab From c68f9baf3e433db7f34d06446d205e17d5ed48cd Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 9 Feb 2021 17:59:41 +0100 Subject: [PATCH 17/72] fix(editForm): fix logic validate/close form --- src/app/form/form.component.html | 2 +- src/app/form/form.component.ts | 8 ++++++-- src/assets/scss/_buttons.scss | 3 +++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 176b86f8f..fcac27064 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -1040,7 +1040,7 @@ </div> <div *ngIf="isEditMode && currentPage == 0" class="footerEditMode"> <div fxLayout="row" fxLayoutAlign="center center"> - <button class="btn-primary unique" (click)="validateForm()">Terminé</button> + <button class="btn-primary unique" (click)="closeEditMode()">Terminé</button> </div> </div> </div> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 03290090e..55847505a 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -612,7 +612,7 @@ export class FormComponent implements OnInit { let user: User; if (this.isEditMode) { this.structureService.editStructure(structure).subscribe((s: Structure) => { - this.router.navigateByUrl('home', { state: { data: s } }); + this.editForm = this.createStructureForm(s); }); } else { if (this.profile) { @@ -671,7 +671,7 @@ export class FormComponent implements OnInit { public goToSpecificPage(numPage: number, isSave: boolean): void { if (isSave) { - this.editForm = this.createStructureForm(new Structure(this.structureForm.value)); + this.validateForm(); } else { const structure = new Structure(this.editForm.value); this.structureForm = this.createStructureForm(structure); @@ -679,4 +679,8 @@ export class FormComponent implements OnInit { } this.currentPage = numPage; } + + public closeEditMode(): void { + this.router.navigateByUrl('home', { state: { data: this.editForm.value } }); + } } diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss index 76f220fca..ec08d9b85 100644 --- a/src/assets/scss/_buttons.scss +++ b/src/assets/scss/_buttons.scss @@ -76,4 +76,7 @@ &.small { width: 149px; } + &.invalid { + opacity: 0.4; + } } -- GitLab From a05708495671d20610c102091f6c2893e8214bcb Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 9 Feb 2021 19:06:53 +0100 Subject: [PATCH 18/72] feat: add social network on structure details --- src/app/models/structure.model.ts | 4 ++ .../structure-details.component.html | 47 ++++++++++++++++-- .../structure-details.component.ts | 2 - src/assets/ico/sprite.svg | 49 +++++++++++++++++++ 4 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/app/models/structure.model.ts b/src/app/models/structure.model.ts index 3c4569dc4..cbf1e4cb2 100644 --- a/src/app/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -176,4 +176,8 @@ export class Structure { public getLabelTypeStructure(): string { return typeStructureEnum[this.structureType] ? typeStructureEnum[this.structureType] : ''; } + + public hasSocialNetwork(): boolean { + return this.facebook !== null || this.instagram !== null || this.twitter !== null || this.linkedin !== null; + } } 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 58a2e2d98..6b15486ef 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 @@ -62,6 +62,45 @@ >{{ structure.website | url }}</a > </div> + <div *ngIf="structure.hasSocialNetwork()" fxLayout="row" fxLayoutAlign="none baseline" fxLayoutGap="13px"> + <app-svg-icon [type]="'ico'" [icon]="'network'"></app-svg-icon> + <a + *ngIf="structure.facebook" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.facebook" + > + <app-svg-icon [type]="'ico'" [icon]="'facebook'" [title]="Facebook"></app-svg-icon + ></a> + <a + *ngIf="structure.twitter" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.twitter" + > + <app-svg-icon [type]="'ico'" [icon]="'twitter'" [title]="Twitter"></app-svg-icon + ></a> + <a + *ngIf="structure.instagram" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.instagram" + > + <app-svg-icon [type]="'ico'" [icon]="'instagram'" [title]="Instagram"></app-svg-icon + ></a> + <a + *ngIf="structure.linkedin" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.linkedin" + > + <app-svg-icon [type]="'ico'" [icon]="'linkedin'" [title]="Linkedin"></app-svg-icon + ></a> + </div> <div *ngIf="structure.contactPhone" fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="13px"> <app-svg-icon [type]="'ico'" [icon]="'tel'"></app-svg-icon> <p>{{ structure.contactPhone | phone }}</p> @@ -163,7 +202,7 @@ </div> <!-- Démarches en ligne --> <div - *ngIf="structure.proceduresAccompaniment.length" + *ngIf="structure.proceduresAccompaniment.length || structure.otherDescription" fxLayout="column" class="structure-details-block" fxLayoutAlign="baseline baseline" @@ -179,10 +218,8 @@ <app-logo-card *ngIf="accompagnement != 'autres'" [name]="accompagnement"></app-logo-card> </div> </div> - <p *ngIf="isOtherSection"> - Tout ce qui est en lien avec la création d'entreprise ex : consultation de sites de références (BPI, Je créé - dans ma région, ...), inscription à des newsletters, aide pour trouver des sites (CMA, CCI, Urssaf,...), - recherches d'infos sur moteur de recherche... + <p *ngIf="structure.otherDescription"> + {{ structure.otherDescription }} </p> </div> </div> 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 4395765f1..bff893ebc 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 @@ -32,7 +32,6 @@ export class StructureDetailsComponent implements OnInit { public accessRights: Module[]; public tclStopPoints: TclStopPoint[] = []; public printMode = false; - public isOtherSection = false; public showForm = false; public isClaimed: boolean = null; public isLoading: boolean = false; @@ -84,7 +83,6 @@ export class StructureDetailsComponent implements OnInit { const index = this.structure.proceduresAccompaniment.indexOf('autres'); if (index > -1) { this.structure.proceduresAccompaniment.splice(index, 1); - this.isOtherSection = true; } } diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 7242edba1..b0ea45840 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -92,6 +92,55 @@ <path fill-rule="evenodd" clip-rule="evenodd" d="M6 5C6 4.44772 6.44772 4 7 4H13V6C13 6.55228 13.4477 7 14 7H16V17C16 17.5523 15.5523 18 15 18H7C6.44772 18 6 17.5523 6 17V5ZM8.5 8C8.22386 8 8 8.22386 8 8.5C8 8.77614 8.22386 9 8.5 9H11.5C11.7761 9 12 8.77614 12 8.5C12 8.22386 11.7761 8 11.5 8H8.5ZM8 11.5C8 11.2239 8.22386 11 8.5 11H13.5C13.7761 11 14 11.2239 14 11.5C14 11.7761 13.7761 12 13.5 12H8.5C8.22386 12 8 11.7761 8 11.5ZM8.5 14C8.22386 14 8 14.2239 8 14.5C8 14.7761 8.22386 15 8.5 15H13.5C13.7761 15 14 14.7761 14 14.5C14 14.2239 13.7761 14 13.5 14H8.5Z" stroke="none"/> </symbol> +<symbol id="network" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0)"> +<circle cx="11" cy="11" r="2.5" stroke="#333333"/> +<circle cx="11" cy="3.43506" r="1.5" stroke="#333333"/> +<path d="M11 4.93506V8.93506" stroke="#333333"/> +<circle r="1.5" transform="matrix(1 0 0 -1 11 18.4351)" stroke="#333333"/> +<path d="M11 16.9351V12.9351" stroke="#333333"/> +<circle cx="18.5" cy="10.9351" r="1.5" transform="rotate(90 18.5 10.9351)" stroke="#333333"/> +<path d="M17 10.9351L13 10.9351" stroke="#333333"/> +<circle r="1.5" transform="matrix(4.37114e-08 1 1 -4.37114e-08 3.5 10.9351)" stroke="#333333"/> +<path d="M5 10.9351L9 10.9351" stroke="#333333"/> +<circle cx="16.3047" cy="5.63171" r="1.5" transform="rotate(45 16.3047 5.63171)" stroke="#333333"/> +<path d="M15.2422 6.69238L12.4138 9.52081" stroke="#333333"/> +<circle r="1.5" transform="matrix(0.707107 0.707107 0.707107 -0.707107 5.69561 16.2383)" stroke="#333333"/> +<path d="M6.75781 15.1777L9.58624 12.3492" stroke="#333333"/> +<circle cx="16.3044" cy="16.2383" r="1.5" transform="rotate(135 16.3044 16.2383)" stroke="#333333"/> +<path d="M15.2422 15.1777L12.4138 12.3492" stroke="#333333"/> +<circle r="1.5" transform="matrix(-0.707107 0.707107 0.707107 0.707107 5.69531 5.63171)" stroke="#333333"/> +<path d="M6.75781 6.69238L9.58624 9.52081" stroke="#333333"/> +</g> +<defs> +<clipPath id="clip0"> +<rect width="22" height="22" fill="white"/> +</clipPath> +</defs> +</symbol> + +<symbol id="facebook" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="0.5" y="0.5" width="29" height="29" rx="3.5" fill="white" stroke="#BDBDBD"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M13.0938 24.4152H16.9241V16.9509H19.7723L20.3125 13.4152H16.9241V11.1071C16.9241 9.8631 17.5789 9.24107 18.8884 9.24107H20.4107V6.24554C19.494 6.08185 18.5938 6 17.7098 6C16.7604 6 15.942 6.18006 15.2545 6.54018C14.5997 6.9003 14.0759 7.44048 13.683 8.16071C13.2902 8.88095 13.0938 9.73214 13.0938 10.7143V13.4152H10V16.9509H13.0938V24.4152Z" fill="#333333"/> +</symbol> + +<symbol id="twitter" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="0.5" y="0.5" width="29" height="29" rx="3.5" fill="white" stroke="#BDBDBD"/> +<path d="M22.7143 10.5223C22.256 11.1771 21.7158 11.7336 21.0938 12.192V12.5848C21.0938 14.058 20.75 15.4821 20.0625 16.8571C19.4077 18.2321 18.3601 19.3943 16.9196 20.3438C15.4792 21.2932 13.8259 21.7679 11.9598 21.7679C10.1592 21.7679 8.50595 21.2768 7 20.2946C7.22917 20.3274 7.49107 20.3438 7.78571 20.3438C9.25893 20.3438 10.5848 19.8854 11.7634 18.9688C11.0759 18.9688 10.4539 18.7723 9.89732 18.3795C9.34077 17.9539 8.96429 17.4137 8.76786 16.7589C9.29167 16.8244 9.78274 16.808 10.2411 16.7098C9.4881 16.5461 8.86607 16.1696 8.375 15.5804C7.88393 14.9911 7.63839 14.3036 7.63839 13.5179V13.4688C8.09673 13.7307 8.5878 13.878 9.11161 13.9107C8.8497 13.7143 8.60417 13.4688 8.375 13.1741C8.14583 12.8795 7.96577 12.5685 7.83482 12.2411C7.70387 11.881 7.63839 11.5372 7.63839 11.2098C7.63839 10.6205 7.78571 10.0804 8.08036 9.58929C9.81548 11.6845 12.0417 12.814 14.7589 12.9777C14.5298 11.8973 14.7426 10.9643 15.3973 10.1786C16.0521 9.39286 16.8869 9 17.9018 9C18.8185 9 19.5878 9.34375 20.2098 10.0312C20.9301 9.9003 21.6176 9.63839 22.2723 9.24554C22.0104 9.99851 21.5357 10.5878 20.8482 11.0134C21.4702 10.9479 22.0923 10.7842 22.7143 10.5223Z" fill="#333333"/> +</symbol> + +<symbol id="instagram" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="0.5" y="0.5" width="29" height="29" rx="3.5" fill="white" stroke="#BDBDBD"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9511 10.8691C13.8152 10.8691 12.845 11.2714 12.0404 12.0759C11.2595 12.8568 10.8691 13.8152 10.8691 14.9511C10.8691 16.087 11.2595 17.0572 12.0404 17.8618C12.845 18.6427 13.8152 19.0331 14.9511 19.0331C16.087 19.0331 17.0453 18.6427 17.8263 17.8618C18.6308 17.0572 19.0331 16.087 19.0331 14.9511C19.0331 13.8152 18.6308 12.8568 17.8263 12.0759C17.0453 11.2714 16.087 10.8691 14.9511 10.8691ZM14.9511 17.6133C14.2175 17.6133 13.5904 17.353 13.0698 16.8324C12.5492 16.3118 12.2889 15.6847 12.2889 14.9511C12.2889 14.2175 12.5492 13.5904 13.0698 13.0698C13.5904 12.5492 14.2175 12.2889 14.9511 12.2889C15.6847 12.2889 16.3118 12.5492 16.8324 13.0698C17.353 13.5904 17.6133 14.2175 17.6133 14.9511C17.6133 15.6847 17.353 16.3118 16.8324 16.8324C16.3118 17.353 15.6847 17.6133 14.9511 17.6133Z" fill="#333333"/> +<path d="M20.1335 10.6916C20.1335 10.4313 20.0388 10.2183 19.8495 10.0526C19.6839 9.86332 19.4709 9.76868 19.2106 9.76868C18.9503 9.76868 18.7255 9.86332 18.5362 10.0526C18.3469 10.2183 18.2522 10.4313 18.2522 10.6916C18.2522 10.9519 18.3469 11.1767 18.5362 11.366C18.7255 11.5553 18.9503 11.65 19.2106 11.65C19.4709 11.65 19.6839 11.5553 19.8495 11.366C20.0388 11.1767 20.1335 10.9519 20.1335 10.6916Z" fill="#333333"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M22.8667 11.6855C22.8903 12.3244 22.9022 13.4129 22.9022 14.9511C22.9022 16.4893 22.8903 17.5778 22.8667 18.2167C22.7957 19.6602 22.3579 20.7843 21.5533 21.5888C20.7724 22.3697 19.6602 22.7838 18.2167 22.8312C17.5778 22.8785 16.4892 22.9022 14.9511 22.9022C13.4129 22.9022 12.3244 22.8785 11.6855 22.8312C10.242 22.7602 9.12976 22.3342 8.34885 21.5533C8.04122 21.2694 7.79275 20.938 7.60343 20.5594C7.41411 20.1808 7.27213 19.814 7.17748 19.4591C7.10649 19.1041 7.07099 18.69 7.07099 18.2167C7.02366 17.5778 7 16.4893 7 14.9511C7 13.4129 7.02366 12.3125 7.07099 11.65C7.14198 10.2301 7.56793 9.12976 8.34885 8.34885C9.12976 7.54429 10.242 7.10649 11.6855 7.0355C12.3244 7.01185 13.4129 7 14.9511 7C16.4892 7 17.5778 7.01185 18.2167 7.0355C19.6602 7.10649 20.7724 7.54429 21.5533 8.34885C22.3579 9.12976 22.7957 10.242 22.8667 11.6855ZM21.3404 18.9266C21.293 19.2106 21.2339 19.4472 21.1629 19.6365C20.8789 20.3465 20.3701 20.8553 19.6365 21.1629C19.4472 21.2339 19.1988 21.293 18.8911 21.3404C18.6072 21.3877 18.2522 21.4232 17.8263 21.4468C17.424 21.4705 17.0927 21.4823 16.8324 21.4823H13.0343C12.7977 21.4823 12.4664 21.4705 12.0404 21.4468C11.6381 21.4232 11.2832 21.3877 10.9755 21.3404C10.6916 21.293 10.4549 21.2339 10.2656 21.1629C9.55571 20.8789 9.04693 20.3701 8.7393 19.6365C8.69197 19.4709 8.64465 19.2579 8.59732 18.9976C8.54998 18.7373 8.51449 18.4888 8.49083 18.2522C8.46717 17.9919 8.4435 17.6843 8.41984 17.3293V13.0698C8.41984 12.8095 8.43167 12.4782 8.45533 12.0759C8.47899 11.65 8.51449 11.295 8.56182 11.011C8.60915 10.7034 8.66831 10.455 8.7393 10.2656C9.02327 9.53206 9.53205 9.02327 10.2656 8.7393C10.4549 8.66831 10.6916 8.60917 10.9755 8.56182C11.2832 8.51447 11.6381 8.47898 12.0404 8.45533C12.4664 8.43169 12.8095 8.41984 13.0698 8.41984H16.8324C17.0927 8.41984 17.424 8.43169 17.8263 8.45533C18.2522 8.47898 18.6072 8.51447 18.8911 8.56182C19.1988 8.60917 19.4472 8.66831 19.6365 8.7393C20.3701 9.02327 20.8789 9.53206 21.1629 10.2656C21.2339 10.455 21.293 10.7034 21.3404 11.011C21.3877 11.295 21.4232 11.65 21.4468 12.0759C21.4705 12.4782 21.4823 12.8095 21.4823 13.0698V16.8324C21.4823 17.0927 21.4705 17.4358 21.4468 17.8618C21.4232 18.2641 21.3877 18.619 21.3404 18.9266Z" fill="#333333"/> +</symbol> + +<symbol id="linkedin" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="0.5" y="0.5" width="29" height="29" rx="3.5" fill="white" stroke="#BDBDBD"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5357 22.7143H10.4866H7.24554V12.2054H10.5357V22.7143ZM8.86607 10.7812C8.34226 10.7812 7.9003 10.6012 7.54018 10.2411C7.18006 9.84821 7 9.40625 7 8.91518C7 8.39137 7.18006 7.9494 7.54018 7.58929C7.9003 7.19643 8.34226 7 8.86607 7C9.38988 7 9.83185 7.19643 10.192 7.58929C10.5848 7.9494 10.7812 8.39137 10.7812 8.91518C10.7812 9.40625 10.5848 9.84821 10.192 10.2411C9.83185 10.6012 9.38988 10.7812 8.86607 10.7812ZM22.7143 16.9688V22.7143H19.4732V17.6071C19.4732 17.2143 19.4568 16.9033 19.4241 16.6741C19.3914 16.4122 19.3259 16.1176 19.2277 15.7902C19.1622 15.4628 18.9985 15.2173 18.7366 15.0536C18.4747 14.8899 18.1473 14.808 17.7545 14.808C17.0015 14.808 16.494 15.0536 16.2321 15.5446C15.9702 16.0357 15.8393 16.6905 15.8393 17.5089V22.7143H12.5491V12.2054H15.692V13.6295H15.7411C15.9702 13.1711 16.3467 12.7783 16.8705 12.4509C17.4271 12.1235 18.0818 11.9598 18.8348 11.9598C19.6205 11.9598 20.2753 12.0908 20.7991 12.3527C21.3557 12.5818 21.7649 12.942 22.0268 13.433C22.2887 13.8914 22.4688 14.3988 22.567 14.9554C22.6652 15.5119 22.7143 16.183 22.7143 16.9688Z" fill="#333333"/> +</symbol> + <symbol id="public" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> <path d="M13.0474 10.437C14.2168 9.73893 15 8.46093 15 7C15 4.79086 13.2091 3 11 3C8.79086 3 7 4.79086 7 7C7 8.46093 7.7832 9.73893 8.95263 10.437C7.21207 11.2192 6 12.9681 6 15V18H16V15C16 12.9681 14.7879 11.2192 13.0474 10.437Z" fill="#333333"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M16.917 14H21V11.8C21 10.3099 20.1516 9.02743 18.9332 8.45382C19.7518 7.94188 20.3 7.00468 20.3 5.93333C20.3 4.3133 19.0464 3 17.5 3C16.542 3 15.6963 3.50407 15.1915 4.27286C15.7028 5.05718 16 5.99389 16 7C16 7.44599 15.9416 7.87827 15.832 8.28963C15.9075 8.34834 15.9858 8.40316 16.0668 8.45382C15.9493 8.50916 15.8352 8.57108 15.725 8.63916C15.5088 9.26223 15.173 9.82915 14.7453 10.3124C15.8722 11.214 16.6677 12.514 16.917 14ZM14.9929 7.24086C14.9976 7.16118 15 7.08087 15 7C15 6.48461 14.9025 5.99199 14.725 5.53957C14.7085 5.66836 14.7 5.79981 14.7 5.93333C14.7 6.40316 14.8054 6.84718 14.9929 7.24086ZM15.9 14H14V11.8C14 11.5447 14.0249 11.2955 14.0723 11.055C14.9949 11.7745 15.6585 12.8106 15.9 14Z" fill="#333333"/> -- GitLab From 3c19cd27b7aa9207b8461969ef6a641db4d6075e Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 09:37:49 +0100 Subject: [PATCH 19/72] fix(signup) : fix svg sprite --- .../signup-modal/signup-modal.component.html | 25 +++++++------------ .../signup-modal/signup-modal.component.scss | 8 ++++++ .../signup-modal/signup-modal.component.ts | 1 - src/assets/form/sprite.svg | 12 +++++++++ 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/app/shared/components/signup-modal/signup-modal.component.html b/src/app/shared/components/signup-modal/signup-modal.component.html index 1069f993c..91900cf36 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.html +++ b/src/app/shared/components/signup-modal/signup-modal.component.html @@ -14,11 +14,9 @@ <label for="email">Courriel personnel</label> <div fxLayout="row" fxLayoutGap="13px"> <input type="text" autocomplete="on" formControlName="email" class="form-input" /> - <img - *ngIf="f.email.invalid && f.email.value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + <svg *ngIf="f.email.invalid && f.email.value" class="notValidate" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#notValidate'"></use> + </svg> </div> </div> <div class="form-group password" fxLayout="column"> @@ -34,17 +32,12 @@ formControlName="password" class="form-input" /> - <img - (click)="toggleShowPassword()" - class="eyePassword" - src="../../assets/form/eyePassword.svg" - alt="logo eyePassword" - /> - <img - *ngIf="f.password.invalid && f.password.value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + <svg (click)="toggleShowPassword()" class="eyePassword" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#eyePassword'"></use> + </svg> + <svg *ngIf="f.password.invalid && f.password.value" class="notValidate" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#notValidate'"></use> + </svg> </div> <div class="invalid" *ngIf="authFailed">Identifiant ou mot de passe invalide</div> </div> diff --git a/src/app/shared/components/signup-modal/signup-modal.component.scss b/src/app/shared/components/signup-modal/signup-modal.component.scss index bdee93df1..3403cb7ca 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.scss +++ b/src/app/shared/components/signup-modal/signup-modal.component.scss @@ -22,6 +22,10 @@ h3 { .form-group { margin-top: 26px; + .notValidate { + width: 32px; + height: 40px; + } label { margin-bottom: 4px; @include cn-regular-14; @@ -46,6 +50,10 @@ h3 { .eyePassword { cursor: pointer; margin-right: 39px; + width: 24px; + height: 40px; + stroke: $grey-3; + fill: $grey-3; } } } diff --git a/src/app/shared/components/signup-modal/signup-modal.component.ts b/src/app/shared/components/signup-modal/signup-modal.component.ts index 5d53eceaa..eb03ae892 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.ts +++ b/src/app/shared/components/signup-modal/signup-modal.component.ts @@ -48,7 +48,6 @@ export class SignUpModalComponent implements OnInit { } public closeModal(): void { - console.log('ok'); this.closed.emit(true); } diff --git a/src/assets/form/sprite.svg b/src/assets/form/sprite.svg index 4b583444a..e81f41f7b 100644 --- a/src/assets/form/sprite.svg +++ b/src/assets/form/sprite.svg @@ -324,4 +324,16 @@ <path d="M16 6L8 12.5L16 19" fill="none" stroke-linecap="round" stroke-linejoin="round"/> </symbol> +<symbol id="eyePassword" viewBox="0 0 22 16" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99519 3.00001C8.83101 2.37183 9.87111 2 11 2C13.7667 2 16 4.23333 16 7C16 9.76667 13.7667 12 11 12C8.23333 12 6 9.76667 6 7C6 6.5747 6.05278 6.162 6.15215 5.76809C6.45257 6.49223 7.16566 7 8 7C9.10667 7 10 6.10667 10 5C10 3.89333 9.10667 3 8 3C7.9984 3 7.9968 3 7.99519 3.00001Z" stroke="none"/> +<path d="M1 8C2.57273 3.90267 6.45455 1 11 1C15.5455 1 19.4273 3.90267 21 8" fill="none" stroke-width="1.5" stroke-linecap="round"/> +<path d="M1 8C2.57273 12.0973 6.45455 15 11 15C15.5455 15 19.4273 12.0973 21 8" fill="none" stroke-width="1.5" stroke-linecap="round"/> +</symbol> + +<symbol id="notValidate" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="13" cy="13" r="13" fill="#DA6C2E"/> +<path d="M13.25 14.5L13.25 6.00001" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> +<path d="M13.25 20.6066L13.25 20" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> +</symbol> + </svg> \ No newline at end of file -- GitLab From c111eb5da34487218e80ec94d0d640529fd3296b Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 11:18:05 +0100 Subject: [PATCH 20/72] fix(signup) : fix size --- .../shared/components/signup-modal/signup-modal.component.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/shared/components/signup-modal/signup-modal.component.scss b/src/app/shared/components/signup-modal/signup-modal.component.scss index 3403cb7ca..e5ae20ec9 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.scss +++ b/src/app/shared/components/signup-modal/signup-modal.component.scss @@ -23,7 +23,8 @@ h3 { .form-group { margin-top: 26px; .notValidate { - width: 32px; + min-width: 26px; + width: 26px; height: 40px; } label { -- GitLab From dfa65ec175cb18593408166212ae0140bf6546b8 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 14:54:08 +0100 Subject: [PATCH 21/72] fix(form): bug homePage blink on editForm --- src/app/form/form.component.html | 2 +- src/app/form/form.component.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index fcac27064..d495e9ef1 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -4,7 +4,7 @@ [content]="'Il vous faudra de nouveau remplir le formulaire si vous quittez'" (closed)="hasRedirectionAccepted($event)" ></app-modal-confirmation> - <div class="content" [ngClass]="{ editMode: isEditMode }"> + <div class="content" *ngIf="!isLoading" [ngClass]="{ editMode: isEditMode }"> <div class="returnBtnSection" *ngIf="isEditMode && currentPage != 0"> <button class="btn-primary previous" (click)="goToSpecificPage(0, false)"> <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 55847505a..94fc88cb6 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -62,6 +62,7 @@ export class FormComponent implements OnInit { public userAcceptSavedDate = false; public showMenu = false; public isEditMode = false; + public isLoading = false; constructor( private structureService: StructureService, @@ -72,11 +73,13 @@ export class FormComponent implements OnInit { ) {} async ngOnInit(): Promise<void> { + this.isLoading = true; this.profileService.getProfile().then((user: User) => { this.profile = user; }); await this.setCategories(); // Check if it's a new structure or edit structure + this.isLoading = false; if (history.state.data) { this.isEditMode = true; this.initForm(new Structure(history.state.data)); -- GitLab From c0ca979379ad5b6eefb6d9b3ff26c217b3e488d2 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 15:05:52 +0100 Subject: [PATCH 22/72] fix(form) : fix modifiedAt on detail structure after edit --- src/app/form/form.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 94fc88cb6..233f6af6f 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -615,6 +615,7 @@ export class FormComponent implements OnInit { let user: User; if (this.isEditMode) { this.structureService.editStructure(structure).subscribe((s: Structure) => { + this.createdStructure = s; this.editForm = this.createStructureForm(s); }); } else { @@ -684,6 +685,6 @@ export class FormComponent implements OnInit { } public closeEditMode(): void { - this.router.navigateByUrl('home', { state: { data: this.editForm.value } }); + this.router.navigateByUrl('home', { state: { data: this.createdStructure } }); } } -- GitLab From 6c4d27004f7f8b73bb030d68763cc9edceaac1a7 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 15:20:39 +0100 Subject: [PATCH 23/72] fix(structureDetails) : update isOpen on edit hours structure --- src/app/form/form.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 233f6af6f..29618ad4b 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -17,6 +17,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { first } from 'rxjs/operators'; import { Regex } from '../shared/enum/regex.enum'; +const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', templateUrl: './form.component.html', @@ -615,7 +616,7 @@ export class FormComponent implements OnInit { let user: User; if (this.isEditMode) { this.structureService.editStructure(structure).subscribe((s: Structure) => { - this.createdStructure = s; + this.createdStructure = this.structureService.updateOpeningStructure(s, DateTime.local()); this.editForm = this.createStructureForm(s); }); } else { -- GitLab From d203667ace6afd1e48e1dfd3776bb01e77823478 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 16:35:45 +0100 Subject: [PATCH 24/72] fix(form) : fix text + css --- src/app/form/form.component.html | 6 +++--- src/app/form/form.component.scss | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index d495e9ef1..040e4cc7c 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -1018,7 +1018,7 @@ [disabled]="!isPageValid" (click)="goToSpecificPage(0, true)" > - Validé + Valider </button> </div> <button @@ -1040,7 +1040,7 @@ </div> <div *ngIf="isEditMode && currentPage == 0" class="footerEditMode"> <div fxLayout="row" fxLayoutAlign="center center"> - <button class="btn-primary unique" (click)="closeEditMode()">Terminé</button> + <button class="btn-primary unique" (click)="closeEditMode()">Terminer</button> </div> </div> </div> @@ -1059,7 +1059,7 @@ [disabled]="!isPageValid" (click)="goToSpecificPage(0, true)" > - Validé + Valider </button> </div> <button diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 5766a9a3c..cec676cd2 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -284,7 +284,6 @@ img { border: 1px solid $grey-4; border-radius: 4px; margin-bottom: 13px; - //margin-right: 40px; @media #{$tablet} { width: 296px; } @@ -490,7 +489,7 @@ img { @include cn-bold-20; cursor: pointer; &:hover { - background: #0000000d; + background: $grey-6; } &.last { border: 0; -- GitLab From 273b9e0d614d1471e22b00ff62f21569ec869f3d Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 17:04:25 +0100 Subject: [PATCH 25/72] fix(editForm) : add header name structure on page too --- src/app/form/form.component.html | 4 +++- src/app/form/form.component.scss | 13 +++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 040e4cc7c..58d7bafd0 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -5,6 +5,9 @@ (closed)="hasRedirectionAccepted($event)" ></app-modal-confirmation> <div class="content" *ngIf="!isLoading" [ngClass]="{ editMode: isEditMode }"> + <div class="headerEditMode" *ngIf="isEditMode"> + <h2>Modification de {{ editForm.get('structureName').value }}</h2> + </div> <div class="returnBtnSection" *ngIf="isEditMode && currentPage != 0"> <button class="btn-primary previous" (click)="goToSpecificPage(0, false)"> <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> @@ -44,7 +47,6 @@ </div> </div> <div *ngIf="currentPage == 0 && isEditMode" class="editHome page" fxLayout="column" fxLayoutAlign="space-between"> - <h2>Modification de la structure</h2> <div> <div class="summary" *ngFor="let page of pagesValidation; let index = index"> <div diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index cec676cd2..edf259514 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -82,7 +82,7 @@ h3 { .content { .editHome { height: calc( - 100vh - #{$header-height} - #{$footer-height} - 81px - 1px + 100vh - #{$header-height} - #{$footer-height} - 81px - 1px - 55px ) !important; // -1px because of header border } @media #{$tablet} { @@ -93,7 +93,7 @@ h3 { ); // -1px because of header border } .editHome { - height: calc(100vh - #{$header-height-phone} - 87px - 1px) !important; // -1px because of header border + height: calc(100vh - #{$header-height-phone} - 87px - 1px - 55px) !important; // -1px because of header border } } } @@ -495,3 +495,12 @@ img { border: 0; } } +.headerEditMode { + max-width: 960px; + margin: auto; + h2 { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } +} -- GitLab From 70eed32d5a3edd34e608a6e68dbd4203e2fa61ff Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 10 Feb 2021 17:54:48 +0100 Subject: [PATCH 26/72] fix(form) : check if email already used --- src/app/form/form.component.html | 13 +++++++++++-- src/app/form/form.component.scss | 2 +- src/app/form/form.component.ts | 11 +++++++++++ src/app/profile/services/profile.service.ts | 4 ++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 526e3a3ed..1ab1982ba 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -100,8 +100,17 @@ </div> <div class="form-group" fxLayout="column"> <label for="email">Courriel personnel</label> + <p class="special invalid" *ngIf="this.accountForm.get('email').hasError('alreadyExist')"> + L'email est déja utilisé. + </p> <div fxLayout="row" fxLayoutGap="13px"> - <input type="text" (input)="setValidationsForm()" formControlName="email" class="form-input" /> + <input + type="text" + (input)="setValidationsForm()" + (keyup)="verifyUserExist($event.target.value)" + formControlName="email" + class="form-input" + /> <img *ngIf="accountForm.get('email').valid" src="../../assets/form/validate.svg" alt="logo valid" /> <img *ngIf="accountForm.get('email').invalid && accountForm.get('email').value" @@ -113,7 +122,7 @@ <div class="form-group" fxLayout="column"> <label for="password">Création de mot de passe</label> <p - class="password" + class="special" [ngClass]="{ invalid: accountForm.get('password').invalid && accountForm.get('password').value }" > Le mot de passe doit contenir au minimum : 8 caractères dont un caractère spécial, un caractère en majuscule diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 203a87dac..7169d27ac 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -151,7 +151,7 @@ h3 { margin-top: 10px; margin-bottom: 0; @include cn-regular-18; - &.password { + &.special { @include cn-regular-14; color: $grey-3; diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 6832ffc8a..5da4fa9a6 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -557,4 +557,15 @@ export class FormComponent implements OnInit { this.resolve(hasAccept); this.showConfirmationModal = false; } + + public verifyUserExist(inputEmail): void { + if (this.accountForm.get('email').valid) { + this.profileService.isEmailAlreadyUsed(inputEmail).subscribe((isExist) => { + if (isExist) { + this.accountForm.get('email').setErrors({ alreadyExist: true }); + this.setValidationsForm(); + } + }); + } + } } diff --git a/src/app/profile/services/profile.service.ts b/src/app/profile/services/profile.service.ts index a196203fd..849d148b4 100644 --- a/src/app/profile/services/profile.service.ts +++ b/src/app/profile/services/profile.service.ts @@ -67,4 +67,8 @@ export class ProfileService { public changeEmail(newEmail: string, oldEmail: string): Observable<User> { return this.http.post<any>(`${this.baseUrl}/change-email`, { newEmail, oldEmail }); } + + public isEmailAlreadyUsed(newMail: string): Observable<Boolean> { + return this.http.post<Boolean>(`${this.baseUrl}/verify-exist-user`, { newMail }); + } } -- GitLab From 12b8fe0ec470876f506bb64c69644cb1d1a628d7 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 10 Feb 2021 18:48:41 +0100 Subject: [PATCH 27/72] fix: firefox issue on hour picker --- .../structure-details.component.html | 74 ++++++++++--------- src/assets/scss/_inputs.scss | 2 +- 2 files changed, 39 insertions(+), 37 deletions(-) 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 6b15486ef..5f2d23285 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 @@ -64,42 +64,44 @@ </div> <div *ngIf="structure.hasSocialNetwork()" fxLayout="row" fxLayoutAlign="none baseline" fxLayoutGap="13px"> <app-svg-icon [type]="'ico'" [icon]="'network'"></app-svg-icon> - <a - *ngIf="structure.facebook" - target="_blank" - class="custom-link" - rel="noopener noreferrer" - [href]="'http://' + structure.facebook" - > - <app-svg-icon [type]="'ico'" [icon]="'facebook'" [title]="Facebook"></app-svg-icon - ></a> - <a - *ngIf="structure.twitter" - target="_blank" - class="custom-link" - rel="noopener noreferrer" - [href]="'http://' + structure.twitter" - > - <app-svg-icon [type]="'ico'" [icon]="'twitter'" [title]="Twitter"></app-svg-icon - ></a> - <a - *ngIf="structure.instagram" - target="_blank" - class="custom-link" - rel="noopener noreferrer" - [href]="'http://' + structure.instagram" - > - <app-svg-icon [type]="'ico'" [icon]="'instagram'" [title]="Instagram"></app-svg-icon - ></a> - <a - *ngIf="structure.linkedin" - target="_blank" - class="custom-link" - rel="noopener noreferrer" - [href]="'http://' + structure.linkedin" - > - <app-svg-icon [type]="'ico'" [icon]="'linkedin'" [title]="Linkedin"></app-svg-icon - ></a> + <div fxLayout="row" fxLayoutAlign="none baseline" fxLayoutGap="8px"> + <a + *ngIf="structure.facebook" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.facebook" + > + <app-svg-icon [type]="'ico'" [icon]="'facebook'" [title]="Facebook"></app-svg-icon + ></a> + <a + *ngIf="structure.twitter" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.twitter" + > + <app-svg-icon [type]="'ico'" [icon]="'twitter'" [title]="Twitter"></app-svg-icon + ></a> + <a + *ngIf="structure.instagram" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.instagram" + > + <app-svg-icon [type]="'ico'" [icon]="'instagram'" [title]="Instagram"></app-svg-icon + ></a> + <a + *ngIf="structure.linkedin" + target="_blank" + class="custom-link" + rel="noopener noreferrer" + [href]="'http://' + structure.linkedin" + > + <app-svg-icon [type]="'ico'" [icon]="'linkedin'" [title]="Linkedin"></app-svg-icon + ></a> + </div> </div> <div *ngIf="structure.contactPhone" fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="13px"> <app-svg-icon [type]="'ico'" [icon]="'tel'"></app-svg-icon> diff --git a/src/assets/scss/_inputs.scss b/src/assets/scss/_inputs.scss index c9fbb4a9f..477d2319b 100644 --- a/src/assets/scss/_inputs.scss +++ b/src/assets/scss/_inputs.scss @@ -31,7 +31,7 @@ .switch { position: relative; display: inline-block; - width: 60px; + margin-left: -55px; height: 34px; } -- GitLab From 8a186897bc4c713cacd8fd11e17f89dbfd40ce51 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 10 Feb 2021 21:39:04 +0100 Subject: [PATCH 28/72] fix: update modale position --- src/styles.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/styles.scss b/src/styles.scss index 40a51cd1c..c8e77507a 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -230,7 +230,9 @@ button { border-radius: 6px; @include background-hash; border: 1px solid $grey-4; - margin-top: 50vh; - transform: translateY(-50%); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); } } -- GitLab From 7cf77c3272902d895e8db5a65401447d2bcfaf9f Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Thu, 11 Feb 2021 10:47:12 +0100 Subject: [PATCH 29/72] fix(form) : fix wifi equipments --- src/app/form/form.component.html | 4 ++-- src/app/form/form.component.ts | 6 ++++-- src/app/models/structure.model.ts | 1 - src/app/structure-list/components/card/card.component.ts | 2 +- .../structure-details/structure-details.component.ts | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 58d7bafd0..22cf64f49 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -706,8 +706,8 @@ <h3>Proposez-vous le wifi en accès libre ?</h3> </div> <app-radio-form - [selectedOption]="getStructureControl('freeWifi').value" - (selectedEvent)="onRadioBtnChange('freeWifi', $event)" + [selectedOption]="isInArray('wifiEnAccesLibre', 'equipmentsAndServices')" + (selectedEvent)="onCheckChange($event, 'equipmentsAndServices', 'wifiEnAccesLibre')" > </app-radio-form> </div> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 29618ad4b..58d8f0d11 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -225,7 +225,6 @@ export class FormComponent implements OnInit { [Validators.required, Validators.pattern(Regex.noNullNumber)] //NOSONAR ), freeWorkShop: new FormControl(structure.freeWorkShop, Validators.required), - freeWifi: new FormControl(structure.freeWifi, Validators.required), }); return form; } @@ -425,7 +424,10 @@ export class FormComponent implements OnInit { name: 'Ateliers au numérique proposés', }; this.pagesValidation[16] = { valid: this.getStructureControl('freeWorkShop').valid, name: 'Gratuité des ateliers' }; - this.pagesValidation[17] = { valid: this.getStructureControl('freeWifi').valid, name: 'Gratuité du wifi' }; + this.pagesValidation[17] = { + valid: this.getStructureControl('equipmentsAndServices').valid, + name: 'Gratuité du wifi', + }; this.pagesValidation[18] = { valid: this.getStructureControl('equipmentsAndServices').valid && diff --git a/src/app/models/structure.model.ts b/src/app/models/structure.model.ts index 3c4569dc4..8ff53a5ee 100644 --- a/src/app/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -37,7 +37,6 @@ export class Structure { public equipmentsAndServices: string[] = []; public hours: Week; public freeWorkShop: boolean = null; - public freeWifi: boolean = null; public otherDescription: string = null; public isOpen: boolean = false; diff --git a/src/app/structure-list/components/card/card.component.ts b/src/app/structure-list/components/card/card.component.ts index 3f3b6d5af..8a64274b6 100644 --- a/src/app/structure-list/components/card/card.component.ts +++ b/src/app/structure-list/components/card/card.component.ts @@ -37,7 +37,7 @@ export class CardComponent implements OnInit { } public filterOnlyEquipments(equipmentsAndServices: string[]): string[] { return equipmentsAndServices.filter((eqpt) => - ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners'].includes(eqpt) + ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners', 'wifiEnAccesLibre'].includes(eqpt) ); } } 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 49633ddf7..2b4bb09e5 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 @@ -207,7 +207,7 @@ export class StructureDetailsComponent implements OnInit { } public filterOnlyEquipments(equipmentsAndServices: string[]): string[] { return equipmentsAndServices.filter((eqpt) => - ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners'].includes(eqpt) + ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners', 'wifiEnAccesLibre'].includes(eqpt) ); } } -- GitLab From 3c037a3f441d9569ba6f47ef9ff047bab6181630 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 11 Feb 2021 11:37:33 +0100 Subject: [PATCH 30/72] fix: add page type --- src/app/form/form.component.html | 53 ++++++++++--------- src/app/form/form.component.ts | 88 ++++++++++++++++++++------------ src/app/form/pageType.enum.ts | 27 ++++++++++ 3 files changed, 111 insertions(+), 57 deletions(-) create mode 100644 src/app/form/pageType.enum.ts diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 22cf64f49..9a6d244c7 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -46,7 +46,12 @@ <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> </div> </div> - <div *ngIf="currentPage == 0 && isEditMode" class="editHome page" fxLayout="column" fxLayoutAlign="space-between"> + <div + *ngIf="currentPage == pageTypeEnum.summary && isEditMode" + class="editHome page" + fxLayout="column" + fxLayoutAlign="space-between" + > <div> <div class="summary" *ngFor="let page of pagesValidation; let index = index"> <div @@ -65,7 +70,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 1" class="informations page" fxLayout="column" fxLayoutGap="28px"> + <div *ngIf="currentPage == pageTypeEnum.info" class="informations page" fxLayout="column" fxLayoutGap="28px"> <h3>De quelles informations faut-il vous munir ?</h3> <img src="../../assets/form/factures.svg" alt="logo factures" /> <div> @@ -79,7 +84,7 @@ </div> </div> <form [formGroup]="accountForm" *ngIf="accountForm && !profile"> - <div *ngIf="currentPage == 2" class="page"> + <div *ngIf="currentPage == pageTypeEnum.accountInfo" class="page"> <div class="title"> <h3>Qui êtes-vous ?</h3> <p>Ces informations ne seront pas visibles sur la plateforme</p> @@ -126,7 +131,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 3" class="page"> + <div *ngIf="currentPage == pageTypeEnum.accountCredentials" class="page"> <div class="title"> <h3>Quels identifiants utiliserez-vous pour vous connecter ?</h3> </div> @@ -204,7 +209,7 @@ </div> </form> <form [formGroup]="structureForm" *ngIf="structureForm"> - <div *ngIf="currentPage == 4" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureNameAndAddress" class="page"> <div class="title"> <h3>Quelle structure voulez-vous réferencer ?</h3> </div> @@ -237,7 +242,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 5" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structurePhone" class="page"> <div class="title"> <h3>Quel numéro appelé pour joindre votre structure ?</h3> </div> @@ -263,7 +268,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 6" class="page" fxLayout="column"> + <div *ngIf="currentPage == pageTypeEnum.structureType" class="page" fxLayout="column"> <div class="title"> <h3>Quel type de structure ?</h3> <p>1 seul choix possible</p> @@ -277,7 +282,7 @@ ></app-structure-type-picker> </div> </div> - <div *ngIf="currentPage == 7" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureAccessModality" class="page"> <div class="title"> <h3>Quelles sont les modalités d'accueil ?</h3> <p>Plusieurs choix possibles</p> @@ -293,7 +298,7 @@ </app-checkbox-form> </div> </div> - <div *ngIf="currentPage == 8" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureHours" class="page"> <div class="title"> <h3>Quels sont les horaires d'ouverture ?</h3> <p class="notRequired">facultatif</p> @@ -306,7 +311,7 @@ [isEditMode]="!isEditMode" ></app-hour-picker> </div> - <div *ngIf="currentPage == 9" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureHoursDetails" class="page"> <div class="title"> <h3>Avez-vous des précisions à apporter sur les horaires ?</h3> <p class="notRequired">facultatif</p> @@ -327,7 +332,7 @@ </p> </div> </div> - <div *ngIf="currentPage == 10" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structurePmr" class="page"> <div class="title"> <h3>Est-ce accessible pour les personnes à mobilité réduite ?</h3> </div> @@ -337,7 +342,7 @@ > </app-radio-form> </div> - <div *ngIf="currentPage == 11" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureWebAndSocialNetwork" class="page"> <div class="title"> <h3>Comment vous trouver sur internet ?</h3> </div> @@ -527,7 +532,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 12" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structurePublicTarget" class="page"> <div class="title"> <h3>Quel public peut venir vous consulter ?</h3> <p>Plusieurs choix possibles</p> @@ -549,7 +554,7 @@ </button> </div> </div> - <div *ngIf="currentPage == 13" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureAccompaniment" class="page"> <div class="title"> <h3>Quel(s) accompagnement(s) proposez-vous ?</h3> <p class="notRequired">facultatif</p> @@ -629,7 +634,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 14" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureOtherAccompaniment" class="page"> <div class="title"> <h3>Quelles sont les autres démarches ?</h3> </div> @@ -648,7 +653,7 @@ </p> </div> </div> - <div *ngIf="currentPage == 15" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureWorkshop" class="page"> <div class="title"> <h3>Quel(s) atelier(s) au numérique proposez-vous ?</h3> <p class="notRequired">facultatif</p> @@ -691,7 +696,7 @@ </div> </div> </div> - <div *ngIf="currentPage == 16" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureWorkshopPrice" class="page"> <div class="title"> <h3>Ces ateliers sont-ils gratuits ?</h3> </div> @@ -701,7 +706,7 @@ > </app-radio-form> </div> - <div *ngIf="currentPage == 17" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureWifi" class="page"> <div class="title"> <h3>Proposez-vous le wifi en accès libre ?</h3> </div> @@ -711,7 +716,7 @@ > </app-radio-form> </div> - <div *ngIf="currentPage == 18" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureEquipments" class="page"> <div class="title"> <h3>Quel matériel mettez-vous à disposition ?</h3> <p class="notRequired">facultatif</p> @@ -852,7 +857,7 @@ </div> </ng-container> </div> - <div *ngIf="currentPage == 19" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureLabels" class="page"> <div class="title"> <h3>Quelle(s) labelisation proposez-vous ?</h3> <p class="notRequired">facultatif</p> @@ -869,7 +874,7 @@ </app-checkbox-form> </div> </div> - <div *ngIf="currentPage == 20" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureOtherServices" class="page"> <div class="title"> <h3>Quels autres services proposez-vous ?</h3> <p class="notRequired">facultatif</p> @@ -891,7 +896,7 @@ </ng-container> </div> </div> - <div *ngIf="currentPage == 21" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureDescription" class="page"> <div class="title"> <h3>Pouvez vous présentez votre structure ?</h3> <p class="notRequired">facultatif</p> @@ -908,7 +913,7 @@ </p> </div> </div> - <div *ngIf="currentPage == 22" class="page"> + <div *ngIf="currentPage == pageTypeEnum.structureCovidInfo" class="page"> <div class="title"> <h3>Y a-t-il des informations spécifique à la période COVID ?</h3> <p class="notRequired">facultatif</p> @@ -927,7 +932,7 @@ </p> </div> </div> - <div *ngIf="currentPage == 23" class="page"> + <div *ngIf="currentPage == pageTypeEnum.cgu" class="page"> <div class="title"> <h3> Acceptez-vous que les informations saisies soient enregistrées par la Métropole de Lyon<span diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 58d8f0d11..dfc550c6d 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -17,6 +17,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { first } from 'rxjs/operators'; import { Regex } from '../shared/enum/regex.enum'; +import { PageTypeEnum } from './pageType.enum'; const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', @@ -39,6 +40,7 @@ export class FormComponent implements OnInit { public proceduresAccompaniment: Category; public equipmentsAndServices: { module: Module; openned: boolean }[] = []; public trainingCategories: { category: Category; openned: boolean }[] = []; + public pageTypeEnum = PageTypeEnum; // Page and progress var public currentPage = 0; @@ -84,6 +86,8 @@ export class FormComponent implements OnInit { if (history.state.data) { this.isEditMode = true; this.initForm(new Structure(history.state.data)); + } else if (history.state.new) { + console.log('Create user only'); } else { this.initForm(new Structure()); } @@ -336,8 +340,8 @@ export class FormComponent implements OnInit { } private createTime(time: Time): FormGroup { return new FormGroup({ - openning: new FormControl(time.openning), //NOSONAR - closing: new FormControl(time.closing), //NOSONAR + openning: new FormControl(time.openning), + closing: new FormControl(time.closing), }); } @@ -363,37 +367,46 @@ export class FormComponent implements OnInit { } public setValidationsForm(): void { - this.pagesValidation[0] = { valid: true }; - this.pagesValidation[1] = { valid: true }; - this.pagesValidation[2] = { + this.pagesValidation[PageTypeEnum.summary] = { valid: true }; + this.pagesValidation[PageTypeEnum.info] = { valid: true }; + this.pagesValidation[PageTypeEnum.accountInfo] = { valid: this.accountForm.get('surname').valid && this.accountForm.get('name').valid && this.accountForm.get('phone').valid, }; - this.pagesValidation[3] = { + this.pagesValidation[PageTypeEnum.accountCredentials] = { valid: this.accountForm.get('email').valid && this.accountForm.get('password').valid && this.accountForm.get('confirmPassword').valid, }; - this.pagesValidation[4] = { + this.pagesValidation[PageTypeEnum.structureNameAndAddress] = { valid: this.getStructureControl('structureName').valid && this.getStructureControl('address').valid, name: 'Nom et adresse', }; - this.pagesValidation[5] = { valid: this.getStructureControl('contactPhone').valid, name: 'Téléphone' }; - this.pagesValidation[6] = { valid: this.getStructureControl('structureType').valid, name: 'Type de structure' }; - this.pagesValidation[7] = { valid: this.getStructureControl('accessModality').valid, name: "Modalités d'accueil " }; - this.pagesValidation[8] = { valid: this.hoursForm.valid, name: "Horaires d'ouverture" }; - this.pagesValidation[9] = { + this.pagesValidation[PageTypeEnum.structurePhone] = { + valid: this.getStructureControl('contactPhone').valid, + name: 'Téléphone', + }; + this.pagesValidation[PageTypeEnum.structureType] = { + valid: this.getStructureControl('structureType').valid, + name: 'Type de structure', + }; + this.pagesValidation[PageTypeEnum.structureAccessModality] = { + valid: this.getStructureControl('accessModality').valid, + name: "Modalités d'accueil ", + }; + this.pagesValidation[PageTypeEnum.structureHours] = { valid: this.hoursForm.valid, name: "Horaires d'ouverture" }; + this.pagesValidation[PageTypeEnum.structureHoursDetails] = { valid: this.getStructureControl('exceptionalClosures').valid, name: 'Précisions sur les horaires', }; - this.pagesValidation[10] = { + this.pagesValidation[PageTypeEnum.structurePmr] = { valid: this.getStructureControl('pmrAccess').valid, name: 'Accessibilité pour les personnes à mobilité réduite', }; - this.pagesValidation[11] = { + this.pagesValidation[PageTypeEnum.structureWebAndSocialNetwork] = { valid: this.getStructureControl('contactMail').valid && (this.getStructureControl('website').valid || !this.showWebsite) && @@ -403,18 +416,21 @@ export class FormComponent implements OnInit { !this.showSocialNetwork), name: 'Présence sur internet', }; - this.pagesValidation[12] = { valid: this.getStructureControl('publics').valid, name: 'Public admis' }; - this.pagesValidation[13] = { + this.pagesValidation[PageTypeEnum.structurePublicTarget] = { + valid: this.getStructureControl('publics').valid, + name: 'Public admis', + }; + this.pagesValidation[PageTypeEnum.structureAccompaniment] = { valid: this.getStructureControl('publicsAccompaniment').valid && this.getStructureControl('proceduresAccompaniment').valid, name: 'Accompagnements proposés', }; - this.pagesValidation[14] = { + this.pagesValidation[PageTypeEnum.structureOtherAccompaniment] = { valid: this.getStructureControl('otherDescription').value, name: 'Autres démarches proposés', }; - this.pagesValidation[15] = { + this.pagesValidation[PageTypeEnum.structureWorkshop] = { valid: this.getStructureControl('accessRight').valid && this.getStructureControl('socialAndProfessional').valid && @@ -423,12 +439,15 @@ export class FormComponent implements OnInit { this.getStructureControl('digitalCultureSecurity').valid, name: 'Ateliers au numérique proposés', }; - this.pagesValidation[16] = { valid: this.getStructureControl('freeWorkShop').valid, name: 'Gratuité des ateliers' }; - this.pagesValidation[17] = { + this.pagesValidation[PageTypeEnum.structureWorkshopPrice] = { + valid: this.getStructureControl('freeWorkShop').valid, + name: 'Gratuité des ateliers', + }; + this.pagesValidation[PageTypeEnum.structureWifi] = { valid: this.getStructureControl('equipmentsAndServices').valid, name: 'Gratuité du wifi', }; - this.pagesValidation[18] = { + this.pagesValidation[PageTypeEnum.structureEquipments] = { valid: this.getStructureControl('equipmentsAndServices').valid && this.getStructureControl('nbComputers').valid && @@ -438,24 +457,24 @@ export class FormComponent implements OnInit { this.getStructureControl('nbScanners').valid, name: 'Matériels mis à disposition', }; - this.pagesValidation[19] = { + this.pagesValidation[PageTypeEnum.structureLabels] = { valid: this.getStructureControl('labelsQualifications').valid, name: 'Labélisations proposées', }; - this.pagesValidation[20] = { + this.pagesValidation[PageTypeEnum.structureOtherServices] = { valid: this.getStructureControl('equipmentsAndServices').valid, name: 'Autres services proposés', }; - this.pagesValidation[21] = { + this.pagesValidation[PageTypeEnum.structureDescription] = { valid: this.getStructureControl('description').valid, name: 'Présentation de la structure', }; - this.pagesValidation[22] = { + this.pagesValidation[PageTypeEnum.structureCovidInfo] = { valid: this.getStructureControl('lockdownActivity').valid, name: 'Informations spécifiques à la période COVID', }; - this.pagesValidation[23] = { valid: this.userAcceptSavedDate }; - //this.pagesValidation[24] = { valid: true }; + this.pagesValidation[PageTypeEnum.cgu] = { valid: this.userAcceptSavedDate }; + //this.pagesValidation[PageTypeEnum.addUserToStructure] = { valid: true }; this.updatePageValid(); } @@ -464,13 +483,16 @@ export class FormComponent implements OnInit { } public nextPage(): void { // Check if user already connected to skip accountForm pages. - if (this.currentPage == 1 && this.profile) { - this.currentPage += 2; // Skip 2 pages from AccountForm + if (this.currentPage == PageTypeEnum.info && this.profile) { + this.currentPage += 2; // Skip accountInfo pages from AccountForm this.progressStatus += 2 * (100 / this.nbPagesForm); } // Check if "other" isn't check to hide "other description" page - if (this.currentPage == 13 && !this.isInArray('autres', 'proceduresAccompaniment')) { - this.currentPage++; // page 14 skip and go to page 15 + if ( + this.currentPage == PageTypeEnum.structureAccompaniment && + !this.isInArray('autres', 'proceduresAccompaniment') + ) { + this.currentPage++; // page structureOtherAccompaniment skip and go to page structureWorkshop this.progressStatus += 100 / this.nbPagesForm; } @@ -485,13 +507,13 @@ export class FormComponent implements OnInit { } public previousPage(): void { // Check if user already connected to skip accountForm pages. - if (this.currentPage == 4 && this.profile) { + if (this.currentPage == PageTypeEnum.structureNameAndAddress && this.profile) { this.currentPage -= 2; // Skip 2 pages from AccountForm this.progressStatus -= 2 * (100 / this.nbPagesForm); } // Check if "other" isn't check to hide "other description" page - if (this.currentPage == 15 && !this.isInArray('autres', 'proceduresAccompaniment')) { + if (this.currentPage == PageTypeEnum.structureWorkshop && !this.isInArray('autres', 'proceduresAccompaniment')) { this.currentPage--; // page 14 skip and go to page 13 this.progressStatus -= 100 / this.nbPagesForm; } diff --git a/src/app/form/pageType.enum.ts b/src/app/form/pageType.enum.ts new file mode 100644 index 000000000..4742e76b2 --- /dev/null +++ b/src/app/form/pageType.enum.ts @@ -0,0 +1,27 @@ +export enum PageTypeEnum { + summary = 0, + info = 1, + accountInfo = 2, + accountCredentials = 3, + structureNameAndAddress = 4, + structurePhone = 5, + structureType = 6, + structureAccessModality = 7, + structureHours = 8, + structureHoursDetails = 9, + structurePmr = 10, + structureWebAndSocialNetwork = 11, + structurePublicTarget = 12, + structureAccompaniment = 13, + structureOtherAccompaniment = 14, + structureWorkshop = 15, + structureWorkshopPrice = 16, + structureWifi = 17, + structureEquipments = 18, + structureLabels = 19, + structureOtherServices = 20, + structureDescription = 21, + structureCovidInfo = 22, + cgu = 23, + addUserToStructure = 24, +} -- GitLab From 2f2e7baa422b788d590c5dafced0cb69a2c35f83 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 11 Feb 2021 11:39:29 +0100 Subject: [PATCH 31/72] fix: move footer-form --- src/app/app.module.ts | 2 +- src/app/{ => form}/footer-form/footer-form.component.html | 0 src/app/{ => form}/footer-form/footer-form.component.scss | 0 src/app/{ => form}/footer-form/footer-form.component.spec.ts | 0 src/app/{ => form}/footer-form/footer-form.component.ts | 0 5 files changed, 1 insertion(+), 1 deletion(-) rename src/app/{ => form}/footer-form/footer-form.component.html (100%) rename src/app/{ => form}/footer-form/footer-form.component.scss (100%) rename src/app/{ => form}/footer-form/footer-form.component.spec.ts (100%) rename src/app/{ => form}/footer-form/footer-form.component.ts (100%) diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8bcb3953a..76b6da88f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -29,7 +29,7 @@ import { ResetPasswordComponent } from './reset-password/reset-password.componen import { AdminModule } from './admin/admin.module'; import { AdminGuard } from './guards/admin.guard'; import { DeactivateGuard } from './guards/deactivate.guard'; -import { FooterFormComponent } from './footer-form/footer-form.component'; +import { FooterFormComponent } from './form/footer-form/footer-form.component'; @NgModule({ declarations: [ diff --git a/src/app/footer-form/footer-form.component.html b/src/app/form/footer-form/footer-form.component.html similarity index 100% rename from src/app/footer-form/footer-form.component.html rename to src/app/form/footer-form/footer-form.component.html diff --git a/src/app/footer-form/footer-form.component.scss b/src/app/form/footer-form/footer-form.component.scss similarity index 100% rename from src/app/footer-form/footer-form.component.scss rename to src/app/form/footer-form/footer-form.component.scss diff --git a/src/app/footer-form/footer-form.component.spec.ts b/src/app/form/footer-form/footer-form.component.spec.ts similarity index 100% rename from src/app/footer-form/footer-form.component.spec.ts rename to src/app/form/footer-form/footer-form.component.spec.ts diff --git a/src/app/footer-form/footer-form.component.ts b/src/app/form/footer-form/footer-form.component.ts similarity index 100% rename from src/app/footer-form/footer-form.component.ts rename to src/app/form/footer-form/footer-form.component.ts -- GitLab From cc61eedb495879f173ac91f2ae1d716f60906de7 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 11 Feb 2021 11:39:46 +0100 Subject: [PATCH 32/72] fix: upadte import --- src/app/form/footer-form/footer-form.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/form/footer-form/footer-form.component.scss b/src/app/form/footer-form/footer-form.component.scss index 7df6b3dd1..31dbd2f3b 100644 --- a/src/app/form/footer-form/footer-form.component.scss +++ b/src/app/form/footer-form/footer-form.component.scss @@ -1,5 +1,5 @@ -@import '../../assets/scss/color'; -@import '../../assets/scss/typography'; +@import '../../../assets/scss/color'; +@import '../../../assets/scss/typography'; .btn-primary { &.previous { -- GitLab From 0bbb3f0e1b55ff2461cdbf48d0380774aebaea4c Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 11 Feb 2021 14:50:19 +0100 Subject: [PATCH 33/72] feat: add registration for claim when not connected --- src/app/form/form.component.html | 112 +++--- src/app/form/form.component.ts | 351 +++++++++++------- .../structure-details.component.html | 4 +- .../structure-details.component.ts | 8 + 4 files changed, 275 insertions(+), 200 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 9a6d244c7..8f4e22987 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -35,7 +35,12 @@ [value]="progressStatus" ></progress> </div> - <div *ngIf="currentPage == 0 && !isEditMode" class="home page" fxLayout="column" fxLayoutAlign="space-between"> + <div + *ngIf="currentPage == pageTypeEnum.summary && !isEditMode" + class="home page" + fxLayout="column" + fxLayoutAlign="space-between" + > <h2>Ajouter votre structure</h2> <img src="../../assets/form/schedule.svg" alt="logo schedule" /> <div> @@ -932,36 +937,6 @@ </p> </div> </div> - <div *ngIf="currentPage == pageTypeEnum.cgu" class="page"> - <div class="title"> - <h3> - Acceptez-vous que les informations saisies soient enregistrées par la Métropole de Lyon<span - class="asterisk" - >*</span - > - ? - </h3> - </div> - <app-checkbox-form - [isChecked]="userAcceptSavedDate" - [text]="'J\'accepte'" - (checkEvent)="acceptDataBeSaved($event)" - > - </app-checkbox-form> - <p class="informationEndForm"> - <span class="asterisk">*</span> Les informations recueillies sont enregistrées dans un fichier par la - Métropole de Lyon en vue de l'animation du réseau des acteurs de la médiation numérique. Elles sont conservées - pendant 24 mois et sont destinées aux seuls intervenants habilités de la Métropole de Lyon. Vos données - personnelles sont traitées dans ce cadre aux fins de Ârecensement des actions de médiation numérique sur le - territoire de la métropole. Conformément à la loi 78-17 du 6 janvier 1978 modifiée relative à l'information, - aux fichiers et aux libertés, et au Règlement Général européen à la Protection des Données, vous avez la - possibilité d’exercer vos droits d’accès, de rectification, d’effacement, d’opposition, de limitation du - traitement et de révocation de votre consentement. Afin d'exercer vos droits, vous pouvez vous adresser : par - courrier postal à : Métropole de Lyon - Direction des Affaires Juridiques et de la Commande Publique - 20, rue - du Lac - BP 33569 - 69505 Lyon Cedex par courrier électronique en remplissant le formulaire dédié sur Toodego, - le site des services et démarches en ligne dans la Métropole de Lyon - </p> - </div> <div *ngIf="false" class="page"> <div class="title"> <h3>Voulez-vous inviter d’autres personnes dans cette structure ?</h3> @@ -983,33 +958,62 @@ </div> </div> </div> - <div *ngIf="currentPage == nbPagesForm && !profile" class="page" fxLayout="column" fxLayoutGap="69px"> - <svg aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#emailVerification'"></use> - </svg> - <h3>Un courriel vous a été envoyé afin de finaliser votre inscription</h3> + </form> + <div *ngIf="currentPage == pageTypeEnum.cgu" class="page"> + <div class="title"> + <h3> + Acceptez-vous que les informations saisies soient enregistrées par la Métropole de Lyon<span class="asterisk" + >*</span + > + ? + </h3> + </div> + <app-checkbox-form + [isChecked]="userAcceptSavedDate" + [text]="'J\'accepte'" + (checkEvent)="acceptDataBeSaved($event)" + > + </app-checkbox-form> + <p class="informationEndForm"> + <span class="asterisk">*</span> Les informations recueillies sont enregistrées dans un fichier par la Métropole + de Lyon en vue de l'animation du réseau des acteurs de la médiation numérique. Elles sont conservées pendant 24 + mois et sont destinées aux seuls intervenants habilités de la Métropole de Lyon. Vos données personnelles sont + traitées dans ce cadre aux fins de Ârecensement des actions de médiation numérique sur le territoire de la + métropole. Conformément à la loi 78-17 du 6 janvier 1978 modifiée relative à l'information, aux fichiers et aux + libertés, et au Règlement Général européen à la Protection des Données, vous avez la possibilité d’exercer vos + droits d’accès, de rectification, d’effacement, d’opposition, de limitation du traitement et de révocation de + votre consentement. Afin d'exercer vos droits, vous pouvez vous adresser : par courrier postal à : Métropole de + Lyon - Direction des Affaires Juridiques et de la Commande Publique - 20, rue du Lac - BP 33569 - 69505 Lyon + Cedex par courrier électronique en remplissant le formulaire dédié sur Toodego, le site des services et + démarches en ligne dans la Métropole de Lyon + </p> + </div> + <div *ngIf="currentPage == nbPagesForm && !profile" class="page" fxLayout="column" fxLayoutGap="69px"> + <svg aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#emailVerification'"></use> + </svg> + <h3>Un courriel vous a été envoyé afin de finaliser votre inscription</h3> + </div> + <div *ngIf="currentPage == nbPagesForm && profile" class="page"> + <div class="title"> + <h3> + Bravo !<br /> + Votre structure a bien été référencée. + </h3> </div> - <div *ngIf="currentPage == nbPagesForm && profile" class="page"> - <div class="title"> - <h3> - Bravo !<br /> - Votre structure a bien été référencée. - </h3> - </div> - <div class="structureInfoBlock" fxLayout="row" fxLayoutAlign=" center"> - <div class="structureInfoContent" fxLayout="column"> - {{ getStructureControl('structureName').value }} - <span>{{ getStructureControl('structureType').value }}</span> - </div> - <div class="validateSvg"> - <svg class="validate" aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#checkVector'"></use> - </svg> - </div> + <div class="structureInfoBlock" fxLayout="row" fxLayoutAlign=" center"> + <div class="structureInfoContent" fxLayout="column"> + {{ getStructureControl('structureName').value }} + <span>{{ getStructureControl('structureType').value }}</span> + </div> + <div class="validateSvg"> + <svg class="validate" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#checkVector'"></use> + </svg> </div> </div> - </form> + </div> <div *ngIf="currentPage != 0" class="footer desktop"> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index dfc550c6d..a3b3c84dc 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -41,6 +41,7 @@ export class FormComponent implements OnInit { public equipmentsAndServices: { module: Module; openned: boolean }[] = []; public trainingCategories: { category: Category; openned: boolean }[] = []; public pageTypeEnum = PageTypeEnum; + public claimStructureId = null; // Page and progress var public currentPage = 0; @@ -65,6 +66,7 @@ export class FormComponent implements OnInit { public userAcceptSavedDate = false; public showMenu = false; public isEditMode = false; + public isClaimMode = false; public isLoading = false; constructor( @@ -86,8 +88,11 @@ export class FormComponent implements OnInit { if (history.state.data) { this.isEditMode = true; this.initForm(new Structure(history.state.data)); - } else if (history.state.new) { - console.log('Create user only'); + } else if (history.state.newUser) { + this.isClaimMode = true; + this.createAccountForm(); + this.claimStructureId = history.state.newUser; + this.setValidationsForm(); } else { this.initForm(new Structure()); } @@ -132,20 +137,7 @@ export class FormComponent implements OnInit { private initForm(structure: Structure): void { // Init account Form - 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 - password: new FormControl('', [ - Validators.required, - Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR - ]), - confirmPassword: new FormControl(''), - }, - [MustMatch('password', 'confirmPassword')] - ); + this.createAccountForm(); // Init form this.structureForm = this.createStructureForm(structure); @@ -167,6 +159,24 @@ export class FormComponent implements OnInit { this.setValidationsForm(); } + + private createAccountForm(): 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 + password: new FormControl('', [ + Validators.required, + Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR + ]), + confirmPassword: new FormControl(''), + }, + [MustMatch('password', 'confirmPassword')] + ); + } + private createStructureForm(structure): FormGroup { const form = new FormGroup({ _id: new FormControl(structure._id), @@ -367,142 +377,197 @@ export class FormComponent implements OnInit { } public setValidationsForm(): void { - this.pagesValidation[PageTypeEnum.summary] = { valid: true }; - this.pagesValidation[PageTypeEnum.info] = { valid: true }; - 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.structureNameAndAddress] = { - valid: this.getStructureControl('structureName').valid && this.getStructureControl('address').valid, - name: 'Nom et adresse', - }; - this.pagesValidation[PageTypeEnum.structurePhone] = { - valid: this.getStructureControl('contactPhone').valid, - name: 'Téléphone', - }; - this.pagesValidation[PageTypeEnum.structureType] = { - valid: this.getStructureControl('structureType').valid, - name: 'Type de structure', - }; - this.pagesValidation[PageTypeEnum.structureAccessModality] = { - valid: this.getStructureControl('accessModality').valid, - name: "Modalités d'accueil ", - }; - this.pagesValidation[PageTypeEnum.structureHours] = { valid: this.hoursForm.valid, name: "Horaires d'ouverture" }; - this.pagesValidation[PageTypeEnum.structureHoursDetails] = { - valid: this.getStructureControl('exceptionalClosures').valid, - name: 'Précisions sur les horaires', - }; - this.pagesValidation[PageTypeEnum.structurePmr] = { - valid: this.getStructureControl('pmrAccess').valid, - name: 'Accessibilité pour les personnes à mobilité réduite', - }; - this.pagesValidation[PageTypeEnum.structureWebAndSocialNetwork] = { - valid: - this.getStructureControl('contactMail').valid && - (this.getStructureControl('website').valid || !this.showWebsite) && - ((this.getStructureControl('facebook').valid && - this.getStructureControl('twitter').valid && - this.getStructureControl('instagram').valid) || - !this.showSocialNetwork), - name: 'Présence sur internet', - }; - this.pagesValidation[PageTypeEnum.structurePublicTarget] = { - valid: this.getStructureControl('publics').valid, - name: 'Public admis', - }; - this.pagesValidation[PageTypeEnum.structureAccompaniment] = { - valid: - this.getStructureControl('publicsAccompaniment').valid && - this.getStructureControl('proceduresAccompaniment').valid, - name: 'Accompagnements proposés', - }; - this.pagesValidation[PageTypeEnum.structureOtherAccompaniment] = { - valid: this.getStructureControl('otherDescription').value, - name: 'Autres démarches proposés', - }; - this.pagesValidation[PageTypeEnum.structureWorkshop] = { - valid: - this.getStructureControl('accessRight').valid && - this.getStructureControl('socialAndProfessional').valid && - this.getStructureControl('baseSkills').valid && - this.getStructureControl('parentingHelp').valid && - this.getStructureControl('digitalCultureSecurity').valid, - name: 'Ateliers au numérique proposés', - }; - this.pagesValidation[PageTypeEnum.structureWorkshopPrice] = { - valid: this.getStructureControl('freeWorkShop').valid, - name: 'Gratuité des ateliers', - }; - this.pagesValidation[PageTypeEnum.structureWifi] = { - valid: this.getStructureControl('equipmentsAndServices').valid, - name: 'Gratuité du wifi', - }; - this.pagesValidation[PageTypeEnum.structureEquipments] = { - valid: - this.getStructureControl('equipmentsAndServices').valid && - this.getStructureControl('nbComputers').valid && - this.getStructureControl('nbPrinters').valid && - this.getStructureControl('nbTablets').valid && - this.getStructureControl('nbNumericTerminal').valid && - this.getStructureControl('nbScanners').valid, - name: 'Matériels mis à disposition', - }; - this.pagesValidation[PageTypeEnum.structureLabels] = { - valid: this.getStructureControl('labelsQualifications').valid, - name: 'Labélisations proposées', - }; - this.pagesValidation[PageTypeEnum.structureOtherServices] = { - valid: this.getStructureControl('equipmentsAndServices').valid, - name: 'Autres services proposés', - }; - this.pagesValidation[PageTypeEnum.structureDescription] = { - valid: this.getStructureControl('description').valid, - name: 'Présentation de la structure', - }; - this.pagesValidation[PageTypeEnum.structureCovidInfo] = { - valid: this.getStructureControl('lockdownActivity').valid, - name: 'Informations spécifiques à la période COVID', - }; - this.pagesValidation[PageTypeEnum.cgu] = { valid: this.userAcceptSavedDate }; - //this.pagesValidation[PageTypeEnum.addUserToStructure] = { valid: true }; - this.updatePageValid(); + if (this.isClaimMode) { + this.pagesValidation[PageTypeEnum.summary] = { valid: true }; + 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 }; + 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.structureNameAndAddress] = { + valid: this.getStructureControl('structureName').valid && this.getStructureControl('address').valid, + name: 'Nom et adresse', + }; + this.pagesValidation[PageTypeEnum.structurePhone] = { + valid: this.getStructureControl('contactPhone').valid, + name: 'Téléphone', + }; + this.pagesValidation[PageTypeEnum.structureType] = { + valid: this.getStructureControl('structureType').valid, + name: 'Type de structure', + }; + this.pagesValidation[PageTypeEnum.structureAccessModality] = { + valid: this.getStructureControl('accessModality').valid, + name: "Modalités d'accueil ", + }; + this.pagesValidation[PageTypeEnum.structureHours] = { valid: this.hoursForm.valid, name: "Horaires d'ouverture" }; + this.pagesValidation[PageTypeEnum.structureHoursDetails] = { + valid: this.getStructureControl('exceptionalClosures').valid, + name: 'Précisions sur les horaires', + }; + this.pagesValidation[PageTypeEnum.structurePmr] = { + valid: this.getStructureControl('pmrAccess').valid, + name: 'Accessibilité pour les personnes à mobilité réduite', + }; + this.pagesValidation[PageTypeEnum.structureWebAndSocialNetwork] = { + valid: + this.getStructureControl('contactMail').valid && + (this.getStructureControl('website').valid || !this.showWebsite) && + ((this.getStructureControl('facebook').valid && + this.getStructureControl('twitter').valid && + this.getStructureControl('instagram').valid) || + !this.showSocialNetwork), + name: 'Présence sur internet', + }; + this.pagesValidation[PageTypeEnum.structurePublicTarget] = { + valid: this.getStructureControl('publics').valid, + name: 'Public admis', + }; + this.pagesValidation[PageTypeEnum.structureAccompaniment] = { + valid: + this.getStructureControl('publicsAccompaniment').valid && + this.getStructureControl('proceduresAccompaniment').valid, + name: 'Accompagnements proposés', + }; + this.pagesValidation[PageTypeEnum.structureOtherAccompaniment] = { + valid: this.getStructureControl('otherDescription').value, + name: 'Autres démarches proposés', + }; + this.pagesValidation[PageTypeEnum.structureWorkshop] = { + valid: + this.getStructureControl('accessRight').valid && + this.getStructureControl('socialAndProfessional').valid && + this.getStructureControl('baseSkills').valid && + this.getStructureControl('parentingHelp').valid && + this.getStructureControl('digitalCultureSecurity').valid, + name: 'Ateliers au numérique proposés', + }; + this.pagesValidation[PageTypeEnum.structureWorkshopPrice] = { + valid: this.getStructureControl('freeWorkShop').valid, + name: 'Gratuité des ateliers', + }; + this.pagesValidation[PageTypeEnum.structureWifi] = { + valid: this.getStructureControl('equipmentsAndServices').valid, + name: 'Gratuité du wifi', + }; + this.pagesValidation[PageTypeEnum.structureEquipments] = { + valid: + this.getStructureControl('equipmentsAndServices').valid && + this.getStructureControl('nbComputers').valid && + this.getStructureControl('nbPrinters').valid && + this.getStructureControl('nbTablets').valid && + this.getStructureControl('nbNumericTerminal').valid && + this.getStructureControl('nbScanners').valid, + name: 'Matériels mis à disposition', + }; + this.pagesValidation[PageTypeEnum.structureLabels] = { + valid: this.getStructureControl('labelsQualifications').valid, + name: 'Labélisations proposées', + }; + this.pagesValidation[PageTypeEnum.structureOtherServices] = { + valid: this.getStructureControl('equipmentsAndServices').valid, + name: 'Autres services proposés', + }; + this.pagesValidation[PageTypeEnum.structureDescription] = { + valid: this.getStructureControl('description').valid, + name: 'Présentation de la structure', + }; + this.pagesValidation[PageTypeEnum.structureCovidInfo] = { + valid: this.getStructureControl('lockdownActivity').valid, + name: 'Informations spécifiques à la période COVID', + }; + this.pagesValidation[PageTypeEnum.cgu] = { valid: this.userAcceptSavedDate }; + //this.pagesValidation[PageTypeEnum.addUserToStructure] = { valid: true }; + this.updatePageValid(); + } } private updatePageValid(): void { this.isPageValid = this.pagesValidation[this.currentPage].valid; } - public nextPage(): void { - // Check if user already connected to skip accountForm pages. - if (this.currentPage == PageTypeEnum.info && this.profile) { - this.currentPage += 2; // Skip accountInfo pages from AccountForm - this.progressStatus += 2 * (100 / this.nbPagesForm); + + /** + * Pgae algo for claim structure case + */ + public nextPageClaim(): void { + if (this.currentPage == this.nbPagesForm - 1) { + const user = new User(this.accountForm.value); + // Create user and claim structure + this.authService.register(user).subscribe(() => { + this.structureService.claimStructureWithAccount(this.claimStructureId, user).subscribe(() => { + this.progressStatus = 100; + }); + }); } - // Check if "other" isn't check to hide "other description" page - if ( - this.currentPage == PageTypeEnum.structureAccompaniment && - !this.isInArray('autres', 'proceduresAccompaniment') - ) { - this.currentPage++; // page structureOtherAccompaniment skip and go to page structureWorkshop - this.progressStatus += 100 / this.nbPagesForm; + + if (this.currentPage == PageTypeEnum.summary) { + this.currentPage = PageTypeEnum.accountInfo; + this.updatePageValid(); + } else 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; } - // Check if going to the last page to submit form and send email verification. - if (this.currentPage == this.nbPagesForm - 1) { - this.validateForm(); + if (this.currentPage !== this.nbPagesForm - 1) { + this.progressStatus += 25; + } + } + + public nextPage(): void { + if (this.isClaimMode) { + this.nextPageClaim(); } else { - this.currentPage++; - this.progressStatus += 100 / this.nbPagesForm; - this.updatePageValid(); + // Check if user already connected to skip accountForm pages. + if (this.currentPage == PageTypeEnum.info && this.profile) { + this.currentPage += 2; // Skip accountInfo pages from AccountForm + this.progressStatus += 2 * (100 / this.nbPagesForm); + } + // Check if "other" isn't check to hide "other description" page + if ( + this.currentPage == PageTypeEnum.structureAccompaniment && + !this.isInArray('autres', 'proceduresAccompaniment') + ) { + this.currentPage++; // page structureOtherAccompaniment skip and go to page structureWorkshop + this.progressStatus += 100 / this.nbPagesForm; + } + + // Check if going to the last page to submit form and send email verification. + if (this.currentPage == this.nbPagesForm - 1) { + this.validateForm(); + } else { + this.currentPage++; + this.progressStatus += 100 / this.nbPagesForm; + this.updatePageValid(); + } } } public previousPage(): void { 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 effd118fd..12b27e22e 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 @@ -76,9 +76,7 @@ </div> </div> <div fxLayout="row" fxLayoutAlign="center center" class="hide-on-print"> - <a *ngIf="!isClaimed && userIsLoggedIn()" (click)="toggleClaimModal()" class="primary" tabindex="0" - >Revendiquer cette structure</a - > + <a *ngIf="!isClaimed" (click)="handleClaim()" class="primary" tabindex="0">Revendiquer cette structure</a> <!-- temporary remove edit --> <a *ngIf="profileService.isLinkedToStructure(structure._id) || profileService.isAdmin()" 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 2b4bb09e5..adcac606a 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 @@ -124,6 +124,14 @@ export class StructureDetailsComponent implements OnInit { this.claimModalOpenned = !this.claimModalOpenned; } + public handleClaim(): void { + if (this.userIsLoggedIn()) { + this.toggleClaimModal(); + } else { + this.router.navigate(['create-structure'], { state: { newUser: this.structure._id } }); + } + } + public deleteStructure(shouldDelete: boolean): void { this.toggleDeleteModal(); if (shouldDelete) { -- GitLab From f85d2c4e938941eef70ce6a36f12b8c507b2524b Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 11 Feb 2021 16:08:25 +0100 Subject: [PATCH 34/72] feat: add user delete structure --- .../structure-details/structure-details.component.html | 2 +- .../structure-details/structure-details.component.ts | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) 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 5f2d23285..2cbb04476 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 @@ -136,7 +136,7 @@ > Modifier cette structure </a> --> - <a *ngIf="profileService.isAdmin()" (click)="toggleDeleteModal()" class="primary" tabindex="0"> + <a *ngIf="canDelete()" (click)="toggleDeleteModal()" class="primary" tabindex="0"> Supprimer cette structure </a> </div> 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 bff893ebc..df9de7418 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 @@ -220,4 +220,11 @@ export class StructureDetailsComponent implements OnInit { this.tclStopPoints = res; }); } + + public canDelete(): boolean { + if (this.profileService.isAdmin() || this.profileService.isLinkedToStructure(this.structure._id)) { + return true; + } + return false; + } } -- GitLab From d93cde74722118af0a061aecc4fa03168d90954d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20BRISON?= <ext.sopra.jbrison@grandlyon.com> Date: Thu, 11 Feb 2021 16:57:14 +0100 Subject: [PATCH 35/72] Feat/edit structure --- .../footer-form/footer-form.component.html | 9 +- .../footer-form/footer-form.component.scss | 11 +- src/app/form/form.component.html | 84 ++++- src/app/form/form.component.scss | 97 ++++-- src/app/form/form.component.ts | 309 +++++++++++++----- src/app/models/structure.model.ts | 1 - src/app/services/structure.service.ts | 3 +- .../hour-picker/hour-picker.component.html | 7 +- .../hour-picker/hour-picker.component.ts | 1 - .../modal-confirmation.component.html | 4 +- .../modal-confirmation.component.scss | 49 ++- .../structure-type-picker.component.html | 2 +- .../structure-type-picker.component.ts | 10 +- src/app/shared/enum/regex.enum.ts | 12 +- src/app/shared/enum/typeStructure.enum.ts | 35 +- .../components/card/card.component.html | 2 +- .../components/card/card.component.ts | 5 + .../structure-details.component.html | 20 +- .../structure-details.component.ts | 26 +- src/app/structure-list/enum/public.enum.ts | 1 + src/assets/scss/_buttons.scss | 18 + src/assets/scss/_layout.scss | 3 +- 22 files changed, 505 insertions(+), 204 deletions(-) diff --git a/src/app/footer-form/footer-form.component.html b/src/app/footer-form/footer-form.component.html index b4c50c392..c2e91330f 100644 --- a/src/app/footer-form/footer-form.component.html +++ b/src/app/footer-form/footer-form.component.html @@ -1,5 +1,5 @@ <div fxLayout="row" fxLayoutGap="10px" fxLayoutAlign="center center"> - <button class="btn previous" (click)="goToPreviousPage()"> + <button class="btn-primary small previous" (click)="goToPreviousPage()"> <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> <svg class="chevronLeft" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> @@ -7,7 +7,12 @@ Précédent </div> </button> - <button class="btn next" (click)="goToNextPage()" [disabled]="!isValid" [ngClass]="{ invalid: !isValid }"> + <button + class="btn-primary small next" + (click)="goToNextPage()" + [disabled]="!isValid" + [ngClass]="{ invalid: !isValid }" + > <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> Suivant<svg class="chevronRight" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> diff --git a/src/app/footer-form/footer-form.component.scss b/src/app/footer-form/footer-form.component.scss index dfecb8334..7df6b3dd1 100644 --- a/src/app/footer-form/footer-form.component.scss +++ b/src/app/footer-form/footer-form.component.scss @@ -1,16 +1,7 @@ @import '../../assets/scss/color'; @import '../../assets/scss/typography'; -.btn { - background: $secondary-color; - border-radius: 4px; - outline: none; - cursor: pointer; - border: 0; - color: $white; - height: 40px; - width: 149px; - @include btn-bold; +.btn-primary { &.previous { background-color: initial; color: $grey-2; diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 1ab1982ba..8a89c5a16 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -4,10 +4,23 @@ [content]="'Il vous faudra de nouveau remplir le formulaire si vous quittez'" (closed)="hasRedirectionAccepted($event)" ></app-modal-confirmation> - <div class="content"> + <div class="content" *ngIf="!isLoading" [ngClass]="{ editMode: isEditMode }"> + <div class="headerEditMode" *ngIf="isEditMode"> + <h2>Modification de {{ editForm.get('structureName').value }}</h2> + </div> + <div class="returnBtnSection" *ngIf="isEditMode && currentPage != 0"> + <button class="btn-primary previous" (click)="goToSpecificPage(0, false)"> + <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> + <svg class="chevronLeft" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> + </svg> + Retour + </div> + </button> + </div> <div class="progressBar" - *ngIf="currentPage != 0" + *ngIf="currentPage != 0 && !isEditMode" fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="20px" @@ -22,7 +35,7 @@ [value]="progressStatus" ></progress> </div> - <div *ngIf="currentPage == 0" class="home page" fxLayout="column" fxLayoutAlign="space-between"> + <div *ngIf="currentPage == 0 && !isEditMode" class="home page" fxLayout="column" fxLayoutAlign="space-between"> <h2>Ajouter votre structure</h2> <img src="../../assets/form/schedule.svg" alt="logo schedule" /> <div> @@ -30,7 +43,26 @@ <p>Une fois réalisé cela vous permettra d'être référencé sur la platefome</p> </div> <div class="btnStart"> - <button class="btn start" (click)="nextPage()">C'est Parti</button> + <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> + </div> + </div> + <div *ngIf="currentPage == 0 && isEditMode" class="editHome page" fxLayout="column" fxLayoutAlign="space-between"> + <div> + <div class="summary" *ngFor="let page of pagesValidation; let index = index"> + <div + class="itemSummary" + [ngClass]="{ last: index == 22 }" + fxLayout="row" + fxLayoutAlign="space-between center" + *ngIf="page.name" + (click)="goToSpecificPage(index, false)" + > + {{ page.name }} + <svg class="chevronRight" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> + </svg> + </div> + </div> </div> </div> <div *ngIf="currentPage == 1" class="informations page" fxLayout="column" fxLayoutGap="28px"> @@ -256,7 +288,7 @@ </div> <div *ngIf="currentPage == 7" class="page"> <div class="title"> - <h3>Quels sont les modalités d'accueil ?</h3> + <h3>Quelles sont les modalités d'accueil ?</h3> <p>Plusieurs choix possibles</p> </div> <div *ngIf="accessModality" fxLayout="row wrap" fxLayoutGap="16px" fxLayoutAlign="flex-start"> @@ -280,7 +312,6 @@ (updateForm)="updateHours($event)" (updateFormError)="setHoursError()" [structureInput]="hoursForm" - [isEditMode]="!isEditMode" ></app-hour-picker> </div> <div *ngIf="currentPage == 9" class="page"> @@ -683,8 +714,8 @@ <h3>Proposez-vous le wifi en accès libre ?</h3> </div> <app-radio-form - [selectedOption]="getStructureControl('freeWifi').value" - (selectedEvent)="onRadioBtnChange('freeWifi', $event)" + [selectedOption]="isEditMode ? isInArray('wifiEnAccesLibre', 'equipmentsAndServices') : null" + (selectedEvent)="onCheckChange($event, 'equipmentsAndServices', 'wifiEnAccesLibre')" > </app-radio-form> </div> @@ -983,16 +1014,26 @@ </div> </form> <div *ngIf="currentPage != 0" class="footer desktop"> - <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm"> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form (previousPage)="previousPage()" (nextPage)="nextPage()" [isValid]="isPageValid" ></app-footer-form> </div> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> + <button + class="btn-primary" + [ngClass]="{ invalid: !isPageValid }" + [disabled]="!isPageValid" + (click)="goToSpecificPage(0, true)" + > + Valider + </button> + </div> <button *ngIf="currentPage == nbPagesForm && !profile" - class="btn validate unique" + class="btn-primary validate unique" routerLink="/home" [routerLinkActive]="'active'" > @@ -1000,25 +1041,40 @@ </button> <button *ngIf="currentPage == nbPagesForm && profile" - class="btn unique" + class="btn-primary unique" routerLink="/home" [state]="{ data: createdStructure }" > Voir ma structure </button> </div> + <div *ngIf="isEditMode && currentPage == 0" class="footerEditMode"> + <div fxLayout="row" fxLayoutAlign="center center"> + <button class="btn-primary unique" (click)="closeEditMode()">Terminer</button> + </div> + </div> </div> <div *ngIf="currentPage != 0" class="footer phone"> - <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm"> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form (previousPage)="previousPage()" (nextPage)="nextPage()" [isValid]="isPageValid" ></app-footer-form> </div> + <div fxLayout="row" fxLayoutAlign="center center" *ngIf="isEditMode"> + <button + class="btn-primary" + [ngClass]="{ invalid: !isPageValid }" + [disabled]="!isPageValid" + (click)="goToSpecificPage(0, true)" + > + Valider + </button> + </div> <button *ngIf="currentPage == nbPagesForm && !profile" - class="btn validate unique" + class="btn-primary validate unique" routerLink="/home" [routerLinkActive]="'active'" > @@ -1026,7 +1082,7 @@ </button> <button *ngIf="currentPage == nbPagesForm && profile" - class="btn unique" + class="btn-primary unique" routerLink="/home" [state]="{ data: createdStructure }" > diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 7169d27ac..17ced3d6b 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -5,7 +5,6 @@ @import '../../assets/scss/shapes'; @import '../../assets/scss/z-index'; -$progressBar-height: 50px; h3 { margin: 0; } @@ -27,15 +26,6 @@ h3 { max-width: 960px; margin: 20px auto; text-align: center; - .btn { - width: 149px; - &.validate { - background-color: $green-1; - } - &.unique { - width: 240px; - } - } &.desktop { @media #{$tablet} { display: none; @@ -90,6 +80,23 @@ h3 { } } .content { + .editHome { + height: calc( + 100vh - #{$header-height} - #{$footer-height} - 81px - 1px - 55px + ) !important; // -1px because of header border + } + @media #{$tablet} { + &.editMode { + .page { + height: calc( + 100vh - #{$header-height-phone} - #{$footer-height-phone} - 87px - 1px + ); // -1px because of header border + } + .editHome { + height: calc(100vh - #{$header-height-phone} - 87px - 1px - 55px) !important; // -1px because of header border + } + } + } padding: 0 16px; display: block; overflow-y: auto; @@ -189,21 +196,30 @@ h3 { } } -.btn { - background: $secondary-color; - border-radius: 4px; - outline: none; - cursor: pointer; - border: 0; - color: $white; - height: 40px; - width: 192px; - @include btn-bold; - +.btn-primary { + &.previous { + background-color: initial; + color: $grey-2; + width: 120px; + border-radius: 6px; + border: 1px solid $grey-4; + } &.start { margin-bottom: 26px; } } +.chevronRight { + height: 24px; + width: 24px; + stroke: $grey-2; + margin-left: 10px; +} +.chevronLeft { + height: 24px; + width: 24px; + stroke: $black; + margin-right: 10px; +} .progressBar { height: #{$progressBar-height}; max-width: 960px; @@ -449,3 +465,42 @@ img { } } } +.footerEditMode { + width: 100%; + position: fixed; + bottom: 56px; + margin: 0; + background: $white; + left: 0; + border-top: 1px solid $grey-4; + padding: 20px 0; + @media #{$tablet} { + bottom: 0; + left: 0; + } +} +.returnBtnSection { + max-width: 960px; + margin: 24px auto; +} +.itemSummary { + height: 60px; + border-bottom: 1px solid $grey-4; + @include cn-bold-20; + cursor: pointer; + &:hover { + background: $grey-6; + } + &.last { + border: 0; + } +} +.headerEditMode { + max-width: 960px; + margin: auto; + h2 { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + } +} diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 5da4fa9a6..91a10097f 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -13,18 +13,17 @@ import { MustMatch } from '../shared/validator/form'; import { Address } from '../models/address.model'; import { Module } from '../structure-list/models/module.model'; import { Equipment } from '../structure-list/enum/equipment.enum'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { first } from 'rxjs/operators'; import { Regex } from '../shared/enum/regex.enum'; +const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', templateUrl: './form.component.html', styleUrls: ['./form.component.scss'], }) export class FormComponent implements OnInit { - @Input() public idStructure?: string; - @Input() public isEditMode: boolean = true; public profile: User; public createdStructure: Structure; @@ -32,6 +31,7 @@ export class FormComponent implements OnInit { public structureForm: FormGroup; public accountForm: FormGroup; public hoursForm: FormGroup; + public editForm: FormGroup; public labelsQualifications: Category; public publics: Category; public accessModality: Category; @@ -62,67 +62,69 @@ export class FormComponent implements OnInit { public isShowPassword = false; public userAcceptSavedDate = false; public showMenu = false; + public isEditMode = false; + public isLoading = false; + public isWifiChoosen = false; constructor( private structureService: StructureService, private searchService: SearchService, private profileService: ProfileService, - private authService: AuthService + private authService: AuthService, + private router: Router ) {} - ngOnInit(): void { + async ngOnInit(): Promise<void> { + this.isLoading = true; this.profileService.getProfile().then((user: User) => { this.profile = user; }); - + await this.setCategories(); // Check if it's a new structure or edit structure - if (this.idStructure) { - this.structureService.getStructure(this.idStructure).subscribe((structure) => { - this.initForm(structure); - this.idStructure = structure._id; - }); + this.isLoading = false; + if (history.state.data) { + this.isEditMode = true; + this.isWifiChoosen = true; + this.initForm(new Structure(history.state.data)); } else { this.initForm(new Structure()); } - this.setCategories(); } - private setCategories(): void { + async setCategories(): Promise<void> { this.searchService.getCategoriesAccompaniment().subscribe((categories: Category[]) => { this.proceduresAccompaniment = categories[0]; }); - this.searchService.getCategoriesMoreFilters().subscribe((categories: Category[]) => { - categories.forEach((categ) => { - switch (categ.id) { - case CategoryEnum.accessModality: { - this.accessModality = categ; - break; - } - case CategoryEnum.equipmentsAndServices: { - categ.modules.forEach((c) => { - this.equipmentsAndServices.push({ module: c, openned: false }); - }); - break; - } - case CategoryEnum.labelsQualifications: { - this.labelsQualifications = categ; - break; - } - case CategoryEnum.publics: { - this.publics = categ; - break; - } - case CategoryEnum.publicsAccompaniment: { - this.publicsAccompaniment = categ; - break; - } + const equipmentsCategs = await this.searchService.getCategoriesMoreFilters().toPromise(); + equipmentsCategs.forEach((categ) => { + switch (categ.id) { + case CategoryEnum.accessModality: { + this.accessModality = categ; + break; } - }); + case CategoryEnum.equipmentsAndServices: { + categ.modules.forEach((c) => { + this.equipmentsAndServices.push({ module: c, openned: false }); + }); + break; + } + case CategoryEnum.labelsQualifications: { + this.labelsQualifications = categ; + break; + } + case CategoryEnum.publics: { + this.publics = categ; + break; + } + case CategoryEnum.publicsAccompaniment: { + this.publicsAccompaniment = categ; + break; + } + } }); - this.searchService.getCategoriesTraining().subscribe((categories: Category[]) => { - categories.forEach((categ) => { - this.trainingCategories.push({ category: categ, openned: false }); - }); + let categs = await this.searchService.getCategoriesTraining().toPromise(); + categs.forEach((categ) => { + this.trainingCategories.push({ category: categ, openned: false }); }); } @@ -144,13 +146,33 @@ export class FormComponent implements OnInit { ); // Init form - this.structureForm = new FormGroup({ + this.structureForm = this.createStructureForm(structure); + this.editForm = this.createStructureForm(structure); + + // Init hours form + this.hoursForm = new FormGroup({ + monday: this.createDay(structure.hours.monday), + tuesday: this.createDay(structure.hours.tuesday), + wednesday: this.createDay(structure.hours.wednesday), + thursday: this.createDay(structure.hours.thursday), + friday: this.createDay(structure.hours.friday), + saturday: this.createDay(structure.hours.saturday), + sunday: this.createDay(structure.hours.sunday), + }); + if (this.isEditMode) { + this.showCollapse(structure); + } + + this.setValidationsForm(); + } + private createStructureForm(structure): FormGroup { + const form = new FormGroup({ _id: new FormControl(structure._id), coord: new FormControl(structure.coord), structureType: new FormControl(structure.structureType, Validators.required), structureName: new FormControl(structure.structureName, Validators.required), description: new FormControl(structure.description), - lockdownActivity: new FormControl(structure.description), + lockdownActivity: new FormControl(structure.lockdownActivity), address: new FormGroup({ numero: new FormControl(structure.address.numero), street: new FormControl(structure.address.street, Validators.required), @@ -205,20 +227,82 @@ export class FormComponent implements OnInit { [Validators.required, Validators.pattern(Regex.noNullNumber)] //NOSONAR ), freeWorkShop: new FormControl(structure.freeWorkShop, Validators.required), - freeWifi: new FormControl(structure.freeWifi, Validators.required), }); - - // Init hours form - this.hoursForm = new FormGroup({ - monday: this.createDay(structure.hours.monday), - tuesday: this.createDay(structure.hours.tuesday), - wednesday: this.createDay(structure.hours.wednesday), - thursday: this.createDay(structure.hours.thursday), - friday: this.createDay(structure.hours.friday), - saturday: this.createDay(structure.hours.saturday), - sunday: this.createDay(structure.hours.sunday), + return form; + } + private showCollapse(s: Structure): void { + if (s.website) { + this.showWebsite = true; + } + if (s.facebook || s.twitter || s.instagram || s.linkedin) { + this.showSocialNetwork = true; + } + if (s.publicsAccompaniment.length) { + this.showPublicsAccompaniment = true; + } + if (s.proceduresAccompaniment.length) { + this.showProceduresAccompaniment = true; + } + this.trainingCategories.forEach((categ: { category: Category; openned: boolean }) => { + categ.openned = false; + switch (categ.category.id) { + case 'accessRight': + if (s.accessRight.length) { + categ.openned = true; + } + break; + case 'socialAndProfessional': + if (s.socialAndProfessional.length) { + categ.openned = true; + } + break; + case 'baseSkills': + if (s.baseSkills.length) { + categ.openned = true; + } + break; + case 'parentingHelp': + if (s.parentingHelp.length) { + categ.openned = true; + } + break; + case 'digitalCultureSecurity': + if (s.digitalCultureSecurity.length) { + categ.openned = true; + } + break; + } + }); + this.equipmentsAndServices.forEach((equipment: { module: Module; openned: boolean }) => { + equipment.openned = false; + switch (equipment.module.id) { + case 'ordinateurs': + if (s.equipmentsAndServices.includes('ordinateurs')) { + equipment.openned = true; + } + break; + case 'tablettes': + if (s.equipmentsAndServices.includes('tablettes')) { + equipment.openned = true; + } + break; + case 'bornesNumeriques': + if (s.equipmentsAndServices.includes('bornesNumeriques')) { + equipment.openned = true; + } + break; + case 'imprimantes': + if (s.equipmentsAndServices.includes('imprimantes')) { + equipment.openned = true; + } + break; + case 'scanners': + if (s.equipmentsAndServices.includes('scanners')) { + equipment.openned = true; + } + break; + } }); - this.setValidationsForm(); } private loadArrayForCheckbox(array: string[], isRequired: boolean): FormArray { @@ -254,12 +338,15 @@ export class FormComponent implements OnInit { } private createTime(time: Time): FormGroup { return new FormGroup({ - openning: new FormControl(time.openning, Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$')), //NOSONAR - closing: new FormControl(time.closing, Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$')), //NOSONAR + openning: new FormControl(time.openning), //NOSONAR + closing: new FormControl(time.closing), //NOSONAR }); } public onCheckChange(event: boolean, formControlName: string, value: string): void { + if (value == 'wifiEnAccesLibre') { + this.isWifiChoosen = true; + } const formArray: FormArray = this.structureForm.get(formControlName) as FormArray; if (event) { // Add a new control in the arrayForm @@ -297,13 +384,20 @@ export class FormComponent implements OnInit { }; this.pagesValidation[4] = { valid: this.getStructureControl('structureName').valid && this.getStructureControl('address').valid, + name: 'Nom et adresse', + }; + this.pagesValidation[5] = { valid: this.getStructureControl('contactPhone').valid, name: 'Téléphone' }; + this.pagesValidation[6] = { valid: this.getStructureControl('structureType').valid, name: 'Type de structure' }; + this.pagesValidation[7] = { valid: this.getStructureControl('accessModality').valid, name: "Modalités d'accueil " }; + this.pagesValidation[8] = { valid: this.hoursForm.valid, name: "Horaires d'ouverture" }; + this.pagesValidation[9] = { + valid: this.getStructureControl('exceptionalClosures').valid, + name: 'Précisions sur les horaires', + }; + this.pagesValidation[10] = { + valid: this.getStructureControl('pmrAccess').valid, + name: 'Accessibilité pour les personnes à mobilité réduite', }; - this.pagesValidation[5] = { valid: this.getStructureControl('contactPhone').valid }; - this.pagesValidation[6] = { valid: this.getStructureControl('structureType').valid }; - this.pagesValidation[7] = { valid: this.getStructureControl('accessModality').valid }; - this.pagesValidation[8] = { valid: this.hoursForm.valid }; - this.pagesValidation[9] = { valid: this.getStructureControl('exceptionalClosures').valid }; - this.pagesValidation[10] = { valid: this.getStructureControl('pmrAccess').valid }; this.pagesValidation[11] = { valid: this.getStructureControl('contactMail').valid && @@ -312,15 +406,18 @@ export class FormComponent implements OnInit { this.getStructureControl('twitter').valid && this.getStructureControl('instagram').valid) || !this.showSocialNetwork), + name: 'Présence sur internet', }; - this.pagesValidation[12] = { valid: this.getStructureControl('publics').valid }; + this.pagesValidation[12] = { valid: this.getStructureControl('publics').valid, name: 'Public admis' }; this.pagesValidation[13] = { valid: this.getStructureControl('publicsAccompaniment').valid && this.getStructureControl('proceduresAccompaniment').valid, + name: 'Accompagnements proposés', }; this.pagesValidation[14] = { valid: this.getStructureControl('otherDescription').value, + name: 'Autres démarches proposés', }; this.pagesValidation[15] = { valid: @@ -329,9 +426,13 @@ export class FormComponent implements OnInit { this.getStructureControl('baseSkills').valid && this.getStructureControl('parentingHelp').valid && this.getStructureControl('digitalCultureSecurity').valid, + name: 'Ateliers au numérique proposés', + }; + this.pagesValidation[16] = { valid: this.getStructureControl('freeWorkShop').valid, name: 'Gratuité des ateliers' }; + this.pagesValidation[17] = { + valid: this.getStructureControl('equipmentsAndServices').valid && this.isWifiChoosen, + name: 'Gratuité du wifi', }; - this.pagesValidation[16] = { valid: this.getStructureControl('freeWorkShop').valid }; - this.pagesValidation[17] = { valid: this.getStructureControl('freeWifi').valid }; this.pagesValidation[18] = { valid: this.getStructureControl('equipmentsAndServices').valid && @@ -340,11 +441,24 @@ export class FormComponent implements OnInit { this.getStructureControl('nbTablets').valid && this.getStructureControl('nbNumericTerminal').valid && this.getStructureControl('nbScanners').valid, + name: 'Matériels mis à disposition', + }; + this.pagesValidation[19] = { + valid: this.getStructureControl('labelsQualifications').valid, + name: 'Labélisations proposées', + }; + this.pagesValidation[20] = { + valid: this.getStructureControl('equipmentsAndServices').valid, + name: 'Autres services proposés', + }; + this.pagesValidation[21] = { + valid: this.getStructureControl('description').valid, + name: 'Présentation de la structure', + }; + this.pagesValidation[22] = { + valid: this.getStructureControl('lockdownActivity').valid, + name: 'Informations spécifiques à la période COVID', }; - this.pagesValidation[19] = { valid: this.getStructureControl('labelsQualifications').valid }; - this.pagesValidation[20] = { valid: this.getStructureControl('equipmentsAndServices').valid }; - this.pagesValidation[21] = { valid: this.getStructureControl('description').valid }; - this.pagesValidation[22] = { valid: this.getStructureControl('lockdownActivity').valid }; this.pagesValidation[23] = { valid: this.userAcceptSavedDate }; //this.pagesValidation[24] = { valid: true }; this.updatePageValid(); @@ -507,19 +621,26 @@ export class FormComponent implements OnInit { let structure: Structure = this.structureForm.value; structure.hours = this.hoursForm.value; let user: User; - if (this.profile) { - user = this.profile; - structure.accountVerified = true; - this.createStructure(structure, user); + if (this.isEditMode) { + this.structureService.editStructure(structure).subscribe((s: Structure) => { + this.createdStructure = this.structureService.updateOpeningStructure(s, DateTime.local()); + this.editForm = this.createStructureForm(s); + }); } else { - if (this.accountForm.valid) { - user = new User(this.accountForm.value); - this.authService - .register(user) - .pipe(first()) - .subscribe(() => { - this.createStructure(structure, user); - }); + if (this.profile) { + user = this.profile; + structure.accountVerified = true; + this.createStructure(structure, user); + } else { + if (this.accountForm.valid) { + user = new User(this.accountForm.value); + this.authService + .register(user) + .pipe(first()) + .subscribe(() => { + this.createStructure(structure, user); + }); + } } } } @@ -542,7 +663,7 @@ export class FormComponent implements OnInit { public canExit(): Promise<boolean> { // Avoid confirmation when user submit form and leave. - if (this.currentPage == this.nbPagesForm) { + if (this.currentPage == this.nbPagesForm || this.currentPage < 3 || this.isEditMode) { return new Promise((resolve) => resolve(true)); } else { return new Promise((resolve) => this.showModal(resolve)); @@ -558,6 +679,24 @@ export class FormComponent implements OnInit { this.showConfirmationModal = false; } + // Function for editMode only + + public goToSpecificPage(numPage: number, isSave: boolean): void { + if (isSave) { + this.validateForm(); + } else { + const structure = new Structure(this.editForm.value); + this.structureForm = this.createStructureForm(structure); + this.showCollapse(structure); + } + this.currentPage = numPage; + this.updatePageValid(); + } + + public closeEditMode(): void { + this.router.navigateByUrl('home', { state: { data: this.createdStructure } }); + } + public verifyUserExist(inputEmail): void { if (this.accountForm.get('email').valid) { this.profileService.isEmailAlreadyUsed(inputEmail).subscribe((isExist) => { diff --git a/src/app/models/structure.model.ts b/src/app/models/structure.model.ts index cbf1e4cb2..0e107ba8a 100644 --- a/src/app/models/structure.model.ts +++ b/src/app/models/structure.model.ts @@ -37,7 +37,6 @@ export class Structure { public equipmentsAndServices: string[] = []; public hours: Week; public freeWorkShop: boolean = null; - public freeWifi: boolean = null; public otherDescription: string = null; public isOpen: boolean = false; diff --git a/src/app/services/structure.service.ts b/src/app/services/structure.service.ts index 41a02af5a..e370f3c66 100644 --- a/src/app/services/structure.service.ts +++ b/src/app/services/structure.service.ts @@ -31,8 +31,9 @@ export class StructureService { return this.http.post(`${this.baseUrl}`, { structure, idUser }).pipe(map((item: Structure) => new Structure(item))); } - public editStructure(id: string, structure: Structure): Observable<Structure> { + public editStructure(structure: Structure): Observable<Structure> { structure.updatedAt = new Date().toString(); + const id = structure._id; delete structure._id; // id should not be provided for update return this.http.put(`${this.baseUrl}/${id}`, structure).pipe(map((item: Structure) => new Structure(item))); } diff --git a/src/app/shared/components/hour-picker/hour-picker.component.html b/src/app/shared/components/hour-picker/hour-picker.component.html index 0a509ad52..ff785893b 100644 --- a/src/app/shared/components/hour-picker/hour-picker.component.html +++ b/src/app/shared/components/hour-picker/hour-picker.component.html @@ -19,7 +19,6 @@ id="{{ day.name }}" (click)="toggleOpenDay(day, $event.target.checked)" [checked]="day.open" - [disabled]="isEditMode" /> <span class="slider"></span> </label> @@ -52,13 +51,13 @@ <div>de</div> <div class="input-container"> - <input type="time" [(ngModel)]="hour.start" (change)="submitForm()" [disabled]="isEditMode" /> + <input type="time" [(ngModel)]="hour.start" (change)="submitForm()" /> </div> <div>à</div> <div class="input-container"> - <input type="time" [(ngModel)]="hour.end" (change)="submitForm()" [disabled]="isEditMode" /> + <input type="time" [(ngModel)]="hour.end" (change)="submitForm()" /> </div> <div> @@ -70,7 +69,7 @@ </div> </div> </div> - <div class="add" *ngIf="day.hours.length === 1 && !isEditMode"> + <div class="add" *ngIf="day.hours.length === 1"> <div (click)="addHours(day)" fxLayout="row" diff --git a/src/app/shared/components/hour-picker/hour-picker.component.ts b/src/app/shared/components/hour-picker/hour-picker.component.ts index 6114ce012..63f42f543 100644 --- a/src/app/shared/components/hour-picker/hour-picker.component.ts +++ b/src/app/shared/components/hour-picker/hour-picker.component.ts @@ -14,7 +14,6 @@ import { CheckHours } from '../../validator/form'; export class HourPickerComponent implements OnChanges, OnDestroy { @Input() modifiedFields: any; @Input() structureInput: FormGroup; - @Input() isEditMode: boolean; @Output() updateFormError = new EventEmitter<any>(); @Output() updateForm = new EventEmitter<FormGroup>(); diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.html b/src/app/shared/components/modal-confirmation/modal-confirmation.component.html index d2bc29320..018d67fad 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.html +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.html @@ -4,8 +4,8 @@ <h3>ATTENTION</h3> <p>{{ content }}</p> <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> - <button class="btn confirm" (click)="closeModal(true)">Confirmer</button> - <button class="btn" (click)="closeModal(false)">Annuler</button> + <button class="btn-primary small leave" (click)="closeModal(true)">Confirmer</button> + <button class="btn-primary small" (click)="closeModal(false)">Annuler</button> </div> </div> </div> diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss index 5f111d0b5..8b50ba8f3 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss @@ -3,12 +3,45 @@ @import '../../../../assets/scss/shapes'; @import '../../../../assets/scss/z-index'; -h3 { - @include cn-bold-18; - color: $orange-warning; -} -p { - @include cn-bold-16; - color: $grey-1; - text-align: center; +.modalExitContainer { + width: 100%; + height: 100%; + z-index: $modal-z-index; + position: absolute; + content: ''; + top: 0; + background-color: $modal-background; + .modal { + .contentModal { + width: 100%; + background: $white; + padding: 35px 20px 18px 20px; + h3 { + @include cn-bold-18; + color: $orange-warning; + } + p { + @include cn-bold-16; + color: $grey-1; + text-align: center; + } + .footerModal { + width: 100%; + margin-top: 14px; + @include cn-bold-16; + .leave { + background: none; + color: $grey-1; + text-decoration: underline; + } + } + } + width: 350px; + margin: auto; + border-radius: 6px; + @include background-hash; + border: 1px solid $grey-4; + margin-top: 50vh; + transform: translateY(-50%); + } } diff --git a/src/app/shared/components/structure-type-picker/structure-type-picker.component.html b/src/app/shared/components/structure-type-picker/structure-type-picker.component.html index 3c6fa1bb6..3613f3efb 100644 --- a/src/app/shared/components/structure-type-picker/structure-type-picker.component.html +++ b/src/app/shared/components/structure-type-picker/structure-type-picker.component.html @@ -26,7 +26,7 @@ <svg *ngIf="choice == pickedChoice" class="validate" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#checkVector'"></use> </svg> - {{ choice }} + {{ getStructureTypeName(choice) }} </button> </div> </div> diff --git a/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts b/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts index 7de312486..b64a4718f 100644 --- a/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts +++ b/src/app/shared/components/structure-type-picker/structure-type-picker.component.ts @@ -1,6 +1,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { StructureType } from '../../../models/structure-type.model'; import { StructureTypeService } from '../../../services/structure-type.service'; +import { typeStructureEnum } from '../../enum/typeStructure.enum'; export enum structureTypes { public = 'Publique', @@ -22,11 +23,11 @@ export class StructureTypePickerComponent implements OnInit { constructor(private structureTypeService: StructureTypeService) {} ngOnInit() { - if (this.pickedChoice) { - this.pickedType = this.getType(this.pickedChoice); - } this.structureTypeService.getStructureTypes().subscribe((types) => { this.structureTypes = types; + if (this.pickedChoice) { + this.pickedType = this.getType(this.pickedChoice); + } }); } @@ -67,4 +68,7 @@ export class StructureTypePickerComponent implements OnInit { throw new Error('Structure type not handle'); } } + public getStructureTypeName(type: string): string { + return typeStructureEnum[type]; + } } diff --git a/src/app/shared/enum/regex.enum.ts b/src/app/shared/enum/regex.enum.ts index 7a564960f..6011651df 100644 --- a/src/app/shared/enum/regex.enum.ts +++ b/src/app/shared/enum/regex.enum.ts @@ -1,11 +1,11 @@ export enum Regex { email = '[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}', - textWithoutNumber = '[A-Za-zÀ-ÖØ-öø-ÿ-]{1,}', + textWithoutNumber = '[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}', phone = '([0-9]{2} ){4}[0-9]{2}', - website = '(www[.])[a-z0-9.-]*[.][a-z]{2,3}', - linkedIn = '(linkedin.com/in/[a-z0-9A-Z.-]{1,})', - facebook = '(facebook.com/[a-z0-9A-Z.-]{1,})', - twitter = '(twitter.com/[a-z0-9A-Z.-]{1,})', - instagram = '(instagram.com/[a-z0-9A-Z.-]{1,})', + website = '(www[.])?(https://)?(http://)?[a-zA-Z0-9.-]*[.][a-z]{2,3}((/)[a-zA-Z0-9-/]*)?', + linkedIn = '(linkedin.com/in/.{1,})', + facebook = '(facebook.com/.{1,})', + twitter = '(twitter.com/.{1,})', + instagram = '(instagram.com/.{1,})', noNullNumber = '[1-9]{1}[0-9]*', } diff --git a/src/app/shared/enum/typeStructure.enum.ts b/src/app/shared/enum/typeStructure.enum.ts index e21c7cb40..e23ed6665 100644 --- a/src/app/shared/enum/typeStructure.enum.ts +++ b/src/app/shared/enum/typeStructure.enum.ts @@ -1,19 +1,32 @@ export enum typeStructureEnum { - associationCaritative = 'Association caritative', - centreSocio = 'Centre socio-culturel', - cyber = 'Cyberbase / Cybercentre', - coworking = 'Espace de coworking', fablab = 'Fablab', + // A supprimer ? + + //A remplacer par Association ? + associationQuartier = 'Structure associative de quartier', + associationCaritative = 'Association caritative', + + // En attente de suppression remplacer par CAF CARSAT, Pole Emploi et CCAS grandOrganismePublic = 'Grand organisme public (CAF, CARSAT, Pôle emploi...)', + + mdm = 'Maison de la métropole', mairie = 'Mairie', - mdm = 'Maison de la Métropole (MDM)', - mediatheque = 'Médiathèque / Bibliothèque', + CAF = 'CAF', + CCAS = 'CCAS', + CARSAT = 'CARSAT', + poleEmploi = 'Pole Emploi', + mediatheque = 'Médiathèque/Bibliothèque', + prefecture = 'Préfecture', + bijPij = 'BIJ/PIJ', + logement = 'Logement', + + association = 'Association', + centreSocio = 'Centre socio-culturel', + mjc = 'MJC / Cyberbase', + pimms = 'PIMMS', + sij = 'Structure information jeunesse (SIJ)', missionsLocales = 'Missions locales', - mjc = 'MJC', - pimms = 'Pimms', - ressourcerie = 'Ressourcerie (matériel moindre coût / recyclé)', - associationQuartier = 'Structure associative de quartier', + formation = 'Structure de formation', insertion = "Structure d'insertion", - sij = 'Structure information jeunesse (SIJ)', } diff --git a/src/app/structure-list/components/card/card.component.html b/src/app/structure-list/components/card/card.component.html index b9bc6cc92..2a02fd3a6 100644 --- a/src/app/structure-list/components/card/card.component.html +++ b/src/app/structure-list/components/card/card.component.html @@ -16,7 +16,7 @@ <span class="typeStructure">{{ structure.getLabelTypeStructure() }}</span> <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="7px" *ngIf="structure.hasEquipments()"> <app-svg-icon - *ngFor="let equipement of structure.equipmentsAndServices" + *ngFor="let equipement of filterOnlyEquipments(structure.equipmentsAndServices)" [type]="'ico'" [iconColor]="'grey'" [icon]="structure.getEquipmentsIcon(equipement)" diff --git a/src/app/structure-list/components/card/card.component.ts b/src/app/structure-list/components/card/card.component.ts index 58d7f395d..8a64274b6 100644 --- a/src/app/structure-list/components/card/card.component.ts +++ b/src/app/structure-list/components/card/card.component.ts @@ -35,4 +35,9 @@ export class CardComponent implements OnInit { public cardHover(): void { this.hover.emit(this.structure); } + public filterOnlyEquipments(equipmentsAndServices: string[]): string[] { + return equipmentsAndServices.filter((eqpt) => + ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners', 'wifiEnAccesLibre'].includes(eqpt) + ); + } } 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 5f2d23285..08b0db06c 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 @@ -1,11 +1,3 @@ -<app-structureForm - *ngIf="showForm" - [idStructure]="structure._id" - [isEditMode]="isEditMode" - [profile]="currentProfile" - (closeEvent)="updateStructure($event)" - (clickOutside)="displayForm()" -></app-structureForm> <div class="structrue-details-container" *ngIf="structure && !isLoading"> <!-- Header info --> <div fxLayout="row" fxLayoutAlign="end center"> @@ -20,8 +12,9 @@ <div class="typeInformationHeader" fxLayout="column"> <h3>{{ structure.getLabelTypeStructure() }}</h3> <div fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="7px" *ngIf="structure.hasEquipments()"> + <div></div> <app-svg-icon - *ngFor="let equipement of structure.equipmentsAndServices" + *ngFor="let equipement of filterOnlyEquipments(structure.equipmentsAndServices)" [type]="'ico'" [iconColor]="'currentColor'" [icon]="structure.getEquipmentsIcon(equipement)" @@ -128,14 +121,15 @@ >Revendiquer cette structure</a > <!-- temporary remove edit --> - <!-- <a + <a *ngIf="profileService.isLinkedToStructure(structure._id) || profileService.isAdmin()" - (click)="editStructure()" + routerLink="/create-structure" + [state]="{ data: structure }" class="primary" tabindex="0" > Modifier cette structure - </a> --> + </a> <a *ngIf="profileService.isAdmin()" (click)="toggleDeleteModal()" class="primary" tabindex="0"> Supprimer cette structure </a> @@ -261,7 +255,7 @@ <h2>Équipements</h2> </div> <div fxLayout="column"> - <p *ngFor="let equipement of structure.equipmentsAndServices" class="no-margin-bottom"> + <p *ngFor="let equipement of filterOnlyEquipments(structure.equipmentsAndServices)" class="no-margin-bottom"> {{ getEquipmentsLabel(equipement) }} <span *ngIf="equipement == 'ordinateurs' && structure.nbComputers"> : {{ structure.nbComputers }}</span> <span *ngIf="equipement == 'tablettes' && structure.nbTablets"> : {{ structure.nbTablets }}</span> 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 bff893ebc..dc84ff30b 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 @@ -23,7 +23,6 @@ import { PublicCategorie } from '../../enum/public.enum'; export class StructureDetailsComponent implements OnInit { @Input() public structure: Structure; @Output() public closeDetails: EventEmitter<boolean> = new EventEmitter<boolean>(); - @Output() public updatedStructure: EventEmitter<Structure> = new EventEmitter<Structure>(); public accessModality = AccessModality; public baseSkillssReferentiel: Category; @@ -32,10 +31,8 @@ export class StructureDetailsComponent implements OnInit { public accessRights: Module[]; public tclStopPoints: TclStopPoint[] = []; public printMode = false; - public showForm = false; public isClaimed: boolean = null; public isLoading: boolean = false; - public isEditMode: boolean = false; public currentProfile: User = null; public deleteModalOpenned = false; public claimModalOpenned = false; @@ -117,11 +114,6 @@ export class StructureDetailsComponent implements OnInit { this.printService.printDocument('structure', this.structure); } - public editStructure(): void { - this.isEditMode = true; - this.displayForm(); - } - public toggleDeleteModal(): void { this.deleteModalOpenned = !this.deleteModalOpenned; } @@ -155,17 +147,6 @@ export class StructureDetailsComponent implements OnInit { }); } } - // Show/hide form structure - public displayForm(): void { - this.showForm = !this.showForm; - } - - public updateStructure(s: Structure): void { - this.structure = new Structure({ ...this.structure, ...s }); - this.updatedStructure.emit(this.structure); - this.displayForm(); - this.ngOnInit(); - } public getAccessLabel(accessModality: AccessModality): string { switch (accessModality) { @@ -192,6 +173,8 @@ export class StructureDetailsComponent implements OnInit { return 'Séniors (+ de 65 ans)'; case PublicCategorie.all: return 'Tout public'; + case PublicCategorie.under16Years: + return 'Moins de 16 ans'; default: return null; } @@ -220,4 +203,9 @@ export class StructureDetailsComponent implements OnInit { this.tclStopPoints = res; }); } + public filterOnlyEquipments(equipmentsAndServices: string[]): string[] { + return equipmentsAndServices.filter((eqpt) => + ['ordinateurs', 'tablettes', 'bornesNumeriques', 'imprimantes', 'scanners', 'wifiEnAccesLibre'].includes(eqpt) + ); + } } diff --git a/src/app/structure-list/enum/public.enum.ts b/src/app/structure-list/enum/public.enum.ts index 1f8371303..272561297 100644 --- a/src/app/structure-list/enum/public.enum.ts +++ b/src/app/structure-list/enum/public.enum.ts @@ -1,4 +1,5 @@ export enum PublicCategorie { + under16Years = 'moinsDe16Ans', young = 'jeunes1625Ans', adult = 'adultes', elderly = 'seniorsPlusDe65Ans', diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss index 6f8458fcf..ec08d9b85 100644 --- a/src/assets/scss/_buttons.scss +++ b/src/assets/scss/_buttons.scss @@ -62,3 +62,21 @@ line-height: 18px; padding: 8px 15px; } + +.btn-primary { + background: $secondary-color; + border-radius: 4px; + outline: none; + cursor: pointer; + border: 0; + color: $white; + height: 40px; + width: 192px; + @include btn-bold; + &.small { + width: 149px; + } + &.invalid { + opacity: 0.4; + } +} diff --git a/src/assets/scss/_layout.scss b/src/assets/scss/_layout.scss index b80043aa2..94ebedd43 100644 --- a/src/assets/scss/_layout.scss +++ b/src/assets/scss/_layout.scss @@ -1,4 +1,5 @@ $header-height: 70px; $footer-height: 56px; -$header-height-phone: 50px; +$header-height-phone: 70px; $footer-height-phone: 75px; +$progressBar-height: 50px; -- GitLab From ee7b65a8a1f48c218796b145d2b4ed51db2690b9 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 11 Feb 2021 18:26:25 +0100 Subject: [PATCH 36/72] fix: add previous page handling for claim --- src/app/form/form.component.ts | 48 ++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index fe17f84f1..716865200 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -516,7 +516,7 @@ export class FormComponent implements OnInit { } /** - * Pgae algo for claim structure case + * Page algo for claim structure case */ public nextPageClaim(): void { if (this.currentPage == this.nbPagesForm - 1) { @@ -542,9 +542,25 @@ export class FormComponent implements OnInit { this.currentPage = this.nbPagesForm; } - if (this.currentPage !== this.nbPagesForm - 1) { - this.progressStatus += 25; + this.progressStatus += 25; + } + + /** + * Page algo for claim structure case + */ + public previousPageClaim(): void { + if (this.currentPage == PageTypeEnum.accountInfo) { + this.currentPage = PageTypeEnum.summary; + this.updatePageValid(); + } else 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 { @@ -576,20 +592,24 @@ export class FormComponent implements OnInit { } } public previousPage(): void { - // Check if user already connected to skip accountForm pages. - if (this.currentPage == PageTypeEnum.structureNameAndAddress && this.profile) { - this.currentPage -= 2; // Skip 2 pages from AccountForm - this.progressStatus -= 2 * (100 / this.nbPagesForm); - } + if (this.isClaimMode) { + this.previousPageClaim(); + } else { + // Check if user already connected to skip accountForm pages. + if (this.currentPage == PageTypeEnum.structureNameAndAddress && this.profile) { + this.currentPage -= 2; // Skip 2 pages from AccountForm + this.progressStatus -= 2 * (100 / this.nbPagesForm); + } - // Check if "other" isn't check to hide "other description" page - if (this.currentPage == PageTypeEnum.structureWorkshop && !this.isInArray('autres', 'proceduresAccompaniment')) { - this.currentPage--; // page 14 skip and go to page 13 + // Check if "other" isn't check to hide "other description" page + if (this.currentPage == PageTypeEnum.structureWorkshop && !this.isInArray('autres', 'proceduresAccompaniment')) { + this.currentPage--; // page 14 skip and go to page 13 + this.progressStatus -= 100 / this.nbPagesForm; + } + this.currentPage--; this.progressStatus -= 100 / this.nbPagesForm; + this.updatePageValid(); } - this.currentPage--; - this.progressStatus -= 100 / this.nbPagesForm; - this.updatePageValid(); } public showPassword(): void { this.isShowPassword = !this.isShowPassword; -- GitLab From b097e360061545d77f4429be04365f61d91b921a Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Fri, 12 Feb 2021 09:58:14 +0100 Subject: [PATCH 37/72] fix(structure) : remove char --- .../structure-details/structure-details.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 99d78b725..a22713344 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 @@ -129,7 +129,7 @@ tabindex="0" > Modifier cette structure - </a> --> + </a> <a *ngIf="canDelete()" (click)="toggleDeleteModal()" class="primary" tabindex="0"> Supprimer cette structure </a> -- GitLab From 41cfbb801537b662fe08ed4133e803a15d914088 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Fri, 12 Feb 2021 16:10:41 +0100 Subject: [PATCH 38/72] fix: add claim structure message --- src/app/form/form.component.html | 24 ++++++++++++++----- src/app/form/form.component.scss | 8 +++++-- src/app/form/form.component.ts | 14 ++++++++--- .../structure-details.component.ts | 2 +- 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 1599d7889..f1b1929d9 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -35,12 +35,7 @@ [value]="progressStatus" ></progress> </div> - <div - *ngIf="currentPage == pageTypeEnum.summary && !isEditMode" - class="home page" - fxLayout="column" - fxLayoutAlign="space-between" - > + <div *ngIf="displayAddStructure()" class="home page" fxLayout="column" fxLayoutAlign="space-between"> <h2>Ajouter votre structure</h2> <img src="../../assets/form/schedule.svg" alt="logo schedule" /> <div> @@ -51,6 +46,23 @@ <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> </div> </div> + <div + *ngIf="displayClaimStructure()" + class="home page" + fxLayout="column" + fxLayoutAlign="space-between center" + fxLayoutAlign.lt-sm="center" + > + <h2> + 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> + </div> + <div class="btnStart"> + <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> + </div> + </div> <div *ngIf="currentPage == pageTypeEnum.summary && isEditMode" class="editHome page" diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 17ced3d6b..8ab5aa6f4 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -86,6 +86,7 @@ h3 { ) !important; // -1px because of header border } @media #{$tablet} { + height: 100%; &.editMode { .page { height: calc( @@ -103,8 +104,11 @@ h3 { height: auto; h2 { @include cn-bold-28; - color: $secondary-color; + color: $black; margin-bottom: 0; + span { + color: $secondary-color; + } } h3 { @include cn-bold-22; @@ -163,7 +167,7 @@ h3 { color: $grey-3; margin-top: 4px; - width: 256px; + width: 280px; &.invalid { color: $orange-warning; } diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 716865200..a09015170 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -41,7 +41,7 @@ export class FormComponent implements OnInit { public equipmentsAndServices: { module: Module; openned: boolean }[] = []; public trainingCategories: { category: Category; openned: boolean }[] = []; public pageTypeEnum = PageTypeEnum; - public claimStructureId = null; + public claimStructure: Structure = null; // Page and progress var public currentPage = 0; @@ -93,7 +93,7 @@ export class FormComponent implements OnInit { } else if (history.state.newUser) { this.isClaimMode = true; this.createAccountForm(); - this.claimStructureId = history.state.newUser; + this.claimStructure = history.state.newUser; this.setValidationsForm(); } else { this.initForm(new Structure()); @@ -523,7 +523,7 @@ 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.claimStructureId, user).subscribe(() => { + this.structureService.claimStructureWithAccount(this.claimStructure._id, user).subscribe(() => { this.progressStatus = 100; }); }); @@ -814,4 +814,12 @@ export class FormComponent implements OnInit { }); } } + + public displayAddStructure(): boolean { + return this.currentPage == this.pageTypeEnum.summary && !this.isEditMode && !this.isClaimMode; + } + + public displayClaimStructure(): boolean { + return this.currentPage == this.pageTypeEnum.summary && !this.isEditMode && this.isClaimMode; + } } 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 a46e89ef8..a3c33e731 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 @@ -126,7 +126,7 @@ export class StructureDetailsComponent implements OnInit { if (this.userIsLoggedIn()) { this.toggleClaimModal(); } else { - this.router.navigate(['create-structure'], { state: { newUser: this.structure._id } }); + this.router.navigate(['create-structure'], { state: { newUser: this.structure } }); } } -- GitLab From ff8adb8c40679fbd539ce644c00eb2cec109f531 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Fri, 12 Feb 2021 16:50:18 +0100 Subject: [PATCH 39/72] feat: add favicon --- src/favicon.ico | Bin 948 -> 10462 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/favicon.ico b/src/favicon.ico index 997406ad22c29aae95893fb3d666c30258a09537..a4f55c1a6dbfdbebed67dc58155936c95d4bfdb0 100644 GIT binary patch literal 10462 zcmZQzU}RuqFfszs3JfPS7#PGD7#K7d7#NHg7#LC*AmR)R3=9J53=GP85DelY<NyEv zGyeboKlK0q|2zKw|NrX$-~}MJZU6uOe+bCUXnx@P|NsBC!RvIKF5CM5|9?J^UqF6> z#sN;ngIogS7f@OXALQ;N#(ALpu#*^tgIo<LPrMoAZY0KepuGQ&7=?pe&2aF;AP-{N zIPdS@zyB94TJ%3RH~0UnS+oBC{{5Ra7SY($<mBZ43=9l#?C<YSV;iY&{PX9};qFKF z%jL_Lsc#t#O+0bp1Xh1++O&y=wo>2d<HwI<^~dJTo2hRZ4NV*_{=k*4wrtr#!_cF? z(Zk&zTepIm%+wDzTA6tA<Vmdge5m;YSGwA^4b)twRp?RQTwLYVQ1i#JW5=+jtL@uC z-F@nZ8?8({dh{q(f9%)+8d{;1t<*R7$dMz%+8;Z24y~^J;lqcq#>Y_e$Du=qu=-=y zu3gkm#56SV;K75#!XF0?9Kh<2-Me?wF!ZQz^#1+(hqXWU?Ab&8K%=3F8#Zjf8XtuG z0UA>Q<*mJY_x}I>{X1S4wzPctZ)y1!1>4)d`=65X@&C4MKmPxt!bn?WWF%I9tXsDZ zr`tY!`0(G@*ch%)US9tH?c29;s>kLJWPgF=De=pU88iMfGBU#5gFIf6nVE^xU4?~( z7<xiOLBrzMwSe4->~3uAEn9wI*NjK>)2C1W-QC?W+=A>678Vxpm>g(~4rUaHJ!Q(2 z|IEzH7<vs24PnYKXkz@4l=KlpA0dH~k`fGeAo~HCt*);AKQ1l~jMdcCaO$h5sK9A0 zG5!GM7n}y*kZ^Hv!Ra1kKM>27m6iPuients{hu`HD@q>6maaTJ-r>@YQ#>Oh1H&IY zJUm#_3n9Po^78%%^(k>$_W%F?BS(G_^hb5|XM9Frm3#d7@qa}{MYvyVY;6ADyLazD zsEiU25P*vl@&l-x0`<eNT7o3__wWDz>FFPF`URAx?%er<q=R_ow{PG6gT|;p^*Jd2 z!gPZCn3R<CpM!$~r+<Wmg#K4oSO5R`k@&p%?%iLkesOhu_y5?jUoh*bPQQEi?mwuG ztf{H_4@!qQIXVAZTU-Bw>PJHL9xlg#;sun(K>4M+`^*0|Yrg;g@PP`IATA3>#c34+ z!y(c@et1i(0HUM0umAu5KeReS*aHVN02P5<YDkHO{r~@;>Hq)#Lu&vBHHblj%#6@A zKcIC>L(wmw^aNhF1o8(6L*oUM#z2EPgL%yfC{KgJ9JEde91kG(9{>>y2N)W_v;&wN NCFmCduyBXP0|0zfNn8K` literal 948 zcmeAS@N?(olHy`uVBq!ia0y~yV31*8V36ZrV_;ygKUz1Rfq{Xuz$3Dlfq`2Hgc&d0 zt^32kz?|mk;uzx5IW^qdM>tSq@8_3ird$#$b)PtmGj%avUXLD+Sz&S#OH+=Bed~^k zE(t5$l#lUxmp*pmH4IGd;gaZCq}bXq{|D!ekIpR10+!S`*{d|ox^icu@gBap=gxSl z)qcKL`~J+{;`cM7wI-iz2<>oODaSCydtP5<MW1wf<TuBx!dGjq8vAe_U9d%#;n2;~ ztIzqnEH{y!wal35e7{x1Hsg%~d-G=p@OUvc9hsl6?CH^zIAcH4dH&5h)pmyZ`;AmY znu9nUgJNH3mcPBjX#coaKr*U4j!%Z~oA~*{^O0@2@w29<wr*Hd6m9W4?iJTNwSxEO zbZ7LQcdSjGd{tNB!=E*W4}Mcz_;n3qgHgODkLx_m$6c$kW=vUJwv9RWv=qawDF4jI zecb79^1?aT*Gg5}<XPMZSbg{U^>^))gz^vCs#x@S-}tn_vPR|kz1LqIe!rYt))!zS zvoY(Akln64y_z>0QjMl1s!cRVJi24!LcTlil2T>5&o<0i>s<dc?er26S5@8W8`pM; z?OJ_(o%8XhesAw(MP$yZOFrqWQ!;;jh~)Cir!V|3V%SyvTVbY;X7SZaVH{WAn5iz& z=um#RWTxWF*#F&(9!oDx@K6yA5`DY&SzM6FU&o1Nh8mJ$_K`0-W?itG(RWrOO6tS( z!$-ac=AQN8Fk0<BB}&XQ`TaF6*~v@SoLSx?;oQ+;r1+?{g<-$Og0AfnQ<NAsxaYUD z+I=d#@j<}$!(6siDyny?5-zS^_E<WE|Fc@(z6)mqJUBACq7-f4Ff?>mExzc#e(SB@ zpT5qXq@xuU7Pi&!lysUFld}wm*K3YgSJ&p#-Wvt7zg$RJsLW@&^@+eF`2?fN5XBd} zYR&gAzj?o-u2HA8y}zpX#FqnS-c)`HnSE9~tZm`$gD*b2i2bTRB<E}-y2QP;#X<GV zM02Ca4I=mURp)zkC^<~)h~voUs^i%;FYU;h>57S0T%Iz2GuuD?dAA3DV$B=lZOvyH z6r$};vfMbhvpd#2)ab(g!yh-Ek7Z~$GM#H>zhpq@2A(^5;i*}LzgRN!Zg8aEKBmo3 zU~_+0pvT;%56#ba?9GZ^(LVK@^z27^d%wKo4BR!RsX!tAv-hD-_1uOUt()YuJC_P2 zIa#nij44Xwx_+jyrA#kp&fiDVG#=mia(wT_^268u`LO+C{8`>(vCFyVAp-*ggQu&X J%Q~loCIA*?vmyWh -- GitLab 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 40/72] 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 41/72] 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 42/72] 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 From d0d134d0753584d31919034449672e379a5e72f0 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 18 Feb 2021 16:09:58 +0100 Subject: [PATCH 43/72] fix: update typeform update --- .../user-verification.component.html | 45 ++++--------------- .../user-verification.component.scss | 6 +++ src/index.html | 1 + 3 files changed, 16 insertions(+), 36 deletions(-) diff --git a/src/app/user-verification/user-verification.component.html b/src/app/user-verification/user-verification.component.html index 4d2ba4981..ed99a6f36 100644 --- a/src/app/user-verification/user-verification.component.html +++ b/src/app/user-verification/user-verification.component.html @@ -11,7 +11,6 @@ Votre compte a bien été créé. </h3> </div> - <div *ngIf="structure" class="structureInfoBlock" fxLayout="row" fxLayoutAlign=" center"> <div class="structureInfoContent" fxLayout="column"> {{ structure.structureName }} @@ -23,43 +22,17 @@ </svg> </div> </div> - <div class="btnSection" fxLayout="row" fxLayoutAlign="space-around center"> - <button *ngIf="structure" class="btn" routerLink="/home" [state]="{ data: structure }">Voir ma structure</button> - <button class="btn tmp-form-link" routerLink="/home"> - <a - class="typeform-share button" - href="https://form.typeform.com/to/vfbLqfKe?typeform-medium=embed-snippet" - data-mode="popup" - data-size="100" - target="_blank" - rel="noopener noreferrer" - >Donnez nous votre avis ! - </a> - </button> - </div> </div> <p *ngIf="verificationIssue"> Une erreur est survenue lors de la validation de votre email... Veuillez envoyer un mail au support. </p> - <script> - (function () { - var qs, - js, - q, - s, - d = document, - gi = d.getElementById, - ce = d.createElement, - gt = d.getElementsByTagName, - id = 'typef_orm_share', - b = 'https://embed.typeform.com/'; - if (!gi.call(d, id)) { - js = ce.call(d, 'script'); - js.id = id; - js.src = b + 'embed.js'; - q = gt.call(d, 'script')[0]; - q.parentNode.insertBefore(js, q); - } - })(); - </script> + <div + class="typeform-widget test" + data-url="https://form.typeform.com/to/m7DV3CdW?typeform-medium=embed-snippet" + ></div> + <div class="btnSection" fxLayout="row" fxLayoutAlign="space-around center"> + <button *ngIf="structure && verificationSuccess" class="btn" routerLink="/home" [state]="{ data: structure }"> + Voir ma structure + </button> + </div> </div> diff --git a/src/app/user-verification/user-verification.component.scss b/src/app/user-verification/user-verification.component.scss index e48bfc961..b6e482712 100644 --- a/src/app/user-verification/user-verification.component.scss +++ b/src/app/user-verification/user-verification.component.scss @@ -57,3 +57,9 @@ color: $white !important; } } + +.test { + width: 100%; + height: 300px; + transform: scale(0.75); +} diff --git a/src/index.html b/src/index.html index 034e83f36..1dfbff37e 100644 --- a/src/index.html +++ b/src/index.html @@ -12,6 +12,7 @@ href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol@0.72.0/dist/L.Control.Locate.min.css" /> <script src="https://openlayers.org/en/v4.6.5/build/ol.js" type="text/javascript"></script> + <script src="https://embed.typeform.com/embed.js"></script> </head> <body> <app-root></app-root> -- GitLab From 14220950f13df2b5e0b7863d4796d61072ef45be Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Fri, 19 Feb 2021 18:00:15 +0100 Subject: [PATCH 44/72] fix(structures): update strucutes list design --- src/app/structure-list/components/card/card.component.html | 2 -- src/app/structure-list/components/card/card.component.scss | 5 ++++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/structure-list/components/card/card.component.html b/src/app/structure-list/components/card/card.component.html index 2a02fd3a6..111164435 100644 --- a/src/app/structure-list/components/card/card.component.html +++ b/src/app/structure-list/components/card/card.component.html @@ -23,6 +23,4 @@ [title]="structure.getEquipmentsTitle(equipement)" ></app-svg-icon> </div> - - <br /> </div> diff --git a/src/app/structure-list/components/card/card.component.scss b/src/app/structure-list/components/card/card.component.scss index ac49fb154..bf275ebec 100644 --- a/src/app/structure-list/components/card/card.component.scss +++ b/src/app/structure-list/components/card/card.component.scss @@ -6,13 +6,16 @@ .structure { padding: 12px 0 12px 0; border-bottom: 1px dashed $grey !important; + height: 110px; cursor: pointer; + @media #{$large-phone} { + height: unset; + } .typeStructure { color: $grey-3; @include cn-regular-16; } .structure-name { - padding-top: 13px; color: $grey-1; @include cn-bold-20; padding-bottom: 5px; -- GitLab From 8c829ba2b118414c6cb536b6033b6f1faf1a864c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20BRISON?= <ext.sopra.jbrison@grandlyon.com> Date: Mon, 22 Feb 2021 13:52:14 +0100 Subject: [PATCH 45/72] Fix: design profile --- src/app/form/form.component.html | 2 + src/app/form/form.component.scss | 5 + src/app/form/form.component.ts | 51 +-- src/app/header/header.component.html | 2 +- src/app/header/header.component.ts | 4 +- src/app/home/home.component.ts | 5 +- src/app/map/components/map.component.scss | 4 +- src/app/models/owner.model.ts | 4 + src/app/models/structureWithOwners.model.ts | 7 + src/app/models/temp-user.model.ts | 2 - src/app/profile/enum/TypeModalProfile.enum.ts | 6 + .../enum/functionTypeModalOptions.enum.ts | 9 + .../modal-options.component.html | 46 +++ .../modal-options.component.scss | 36 ++ .../modal-options.component.spec.ts | 24 ++ .../modal-options/modal-options.component.ts | 21 + src/app/profile/profile.component.html | 363 ++++++++++++++---- src/app/profile/profile.component.scss | 166 ++++++++ src/app/profile/profile.component.ts | 229 +++++++++-- src/app/profile/profile.module.ts | 3 +- src/app/profile/services/profile.service.ts | 5 +- .../reset-password.component.ts | 6 +- src/app/services/structure.service.ts | 13 + .../components/button/button.component.html | 12 +- .../components/button/button.component.scss | 3 +- .../create-account-form.component.spec.ts | 3 +- .../create-account-form.component.ts | 17 +- .../modal-confirmation.component.scss | 2 +- .../signup-modal/signup-modal.component.ts | 12 +- .../structure-type-picker.component.scss | 2 +- .../svg-icon/svg-icon.component.scss | 5 + src/app/shared/enum/regex.enum.ts | 11 - .../modal-filter/modal-filter.component.scss | 2 +- src/app/utils/CustomRegExp.ts | 22 ++ src/assets/form/sprite.svg | 5 + src/assets/ico/more_vert_24px.svg | 3 + src/assets/ico/sprite.svg | 40 +- src/assets/scss/_shapes.scss | 10 +- src/styles.scss | 2 +- 39 files changed, 954 insertions(+), 210 deletions(-) create mode 100644 src/app/models/owner.model.ts create mode 100644 src/app/models/structureWithOwners.model.ts create mode 100644 src/app/profile/enum/TypeModalProfile.enum.ts create mode 100644 src/app/profile/enum/functionTypeModalOptions.enum.ts create mode 100644 src/app/profile/modal-options/modal-options.component.html create mode 100644 src/app/profile/modal-options/modal-options.component.scss create mode 100644 src/app/profile/modal-options/modal-options.component.spec.ts create mode 100644 src/app/profile/modal-options/modal-options.component.ts delete mode 100644 src/app/shared/enum/regex.enum.ts create mode 100644 src/app/utils/CustomRegExp.ts create mode 100644 src/assets/ico/more_vert_24px.svg diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index c4b68351b..5667595b7 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -164,6 +164,8 @@ (keyup)="verifyUserExist($event.target.value)" formControlName="email" class="form-input" + [readonly]="isAccountMode" + [ngClass]="{ disabled: isAccountMode }" /> <img *ngIf="accountForm.get('email').valid" src="../../assets/form/validate.svg" alt="logo valid" /> <img diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 398ad100c..fb6d475f8 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -5,6 +5,11 @@ @import '../../assets/scss/shapes'; @import '../../assets/scss/z-index'; +.disabled { + opacity: 0.4; + cursor: not-allowed; +} + h3 { margin: 0; } diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index f1dfb68e2..837866f33 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; import { Structure } from '../models/structure.model'; import { Time } from '../models/time.model'; @@ -16,9 +16,9 @@ import { Equipment } from '../structure-list/enum/equipment.enum'; import { ActivatedRoute, Router } from '@angular/router'; 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'; +import { CustomRegExp } from '../utils/CustomRegExp'; const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', @@ -112,7 +112,7 @@ export class FormComponent implements OnInit { this.route.data.subscribe((data) => { if (data.user) { this.isAccountMode = true; - this.createAccountForm(data.user.email, data.user.name, data.user.surname); + this.createAccountForm(data.user.email); this.linkedStructureId = data.user.pendingStructuresLink; this.setValidationsForm(); this.currentPage = PageTypeEnum.accountInfo; @@ -182,19 +182,16 @@ export class FormComponent implements OnInit { this.setValidationsForm(); } - private createAccountForm(email?: string, name?: string, surname?: string): void { + private createAccountForm(email?: string): void { this.accountForm = new FormGroup( { - 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)]), + email: new FormControl(email ? email : '', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]), + name: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)]), + surname: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)]), + phone: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.PHONE)]), password: new FormControl('', [ Validators.required, - Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR + Validators.pattern(CustomRegExp.PASSWORD), //NOSONAR ]), confirmPassword: new FormControl(''), }, @@ -215,13 +212,19 @@ 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)]), - 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)), + contactMail: new FormControl(structure.contactMail, [ + Validators.required, + Validators.pattern(CustomRegExp.EMAIL), + ]), + contactPhone: new FormControl(structure.contactPhone, [ + Validators.required, + Validators.pattern(CustomRegExp.PHONE), + ]), + website: new FormControl(structure.website, Validators.pattern(CustomRegExp.WEBSITE)), + facebook: new FormControl(structure.facebook, Validators.pattern(CustomRegExp.FACEBOOK)), + twitter: new FormControl(structure.twitter, Validators.pattern(CustomRegExp.TWITTER)), + instagram: new FormControl(structure.instagram, Validators.pattern(CustomRegExp.INSTAGRAM)), + linkedin: new FormControl(structure.linkedin, Validators.pattern(CustomRegExp.LINKEDIN)), hours: new FormGroup({}), pmrAccess: new FormControl(structure.pmrAccess, Validators.required), exceptionalClosures: new FormControl(structure.exceptionalClosures), @@ -239,23 +242,23 @@ 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)] + [Validators.required, Validators.pattern(CustomRegExp.NO_NULL_NUMBER)] ), nbPrinters: new FormControl(structure.equipmentsAndServices.includes('imprimantes') ? structure.nbPrinters : 1, [ Validators.required, - Validators.pattern(Regex.noNullNumber), + Validators.pattern(CustomRegExp.NO_NULL_NUMBER), ]), nbTablets: new FormControl(structure.equipmentsAndServices.includes('tablettes') ? structure.nbTablets : 1, [ Validators.required, - Validators.pattern(Regex.noNullNumber), + Validators.pattern(CustomRegExp.NO_NULL_NUMBER), ]), nbNumericTerminal: new FormControl( structure.equipmentsAndServices.includes('bornesNumeriques') ? structure.nbNumericTerminal : 1, - [Validators.required, Validators.pattern(Regex.noNullNumber)] + [Validators.required, Validators.pattern(CustomRegExp.NO_NULL_NUMBER)] ), nbScanners: new FormControl(structure.equipmentsAndServices.includes('scanners') ? structure.nbScanners : 1, [ Validators.required, - Validators.pattern(Regex.noNullNumber), + Validators.pattern(CustomRegExp.NO_NULL_NUMBER), ]), freeWorkShop: new FormControl(structure.freeWorkShop, Validators.required), }); diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 713873855..c26ce85cc 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -32,7 +32,7 @@ </div> <div fxLayout="column" class="right-header" fxLayoutAlign="none baseline" fxLayoutGap="5vw"> <a routerLink="/home" [routerLinkActive]="'active'" (click)="closeMenu()" i18n>Les acteurs</a> - <a *ngIf="isAdmin" routerLink="/admin" [routerLinkActive]="'active'">Administration</a> + <a *ngIf="isAdmin" routerLink="/admin" [routerLinkActive]="'active'" (click)="closeMenu()">Administration</a> </div> </div> <div> diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index ab1858df6..1fff90f72 100644 --- a/src/app/header/header.component.ts +++ b/src/app/header/header.component.ts @@ -45,9 +45,7 @@ export class HeaderComponent implements OnInit { public openMenu(): void { this.showMenu = true; } - - public closeMenu(route: string): void { - this.router.navigateByUrl(route); + public closeMenu(): void { this.showMenu = false; } diff --git a/src/app/home/home.component.ts b/src/app/home/home.component.ts index b2b2b512f..5a803cdb4 100644 --- a/src/app/home/home.component.ts +++ b/src/app/home/home.component.ts @@ -7,7 +7,7 @@ import { StructureService } from '../services/structure.service'; import { Filter } from '../structure-list/models/filter.model'; import { GeoJson } from '../map/models/geojson.model'; import { GeojsonService } from '../services/geojson.service'; -import { ActivatedRoute } from '@angular/router'; +import { CustomRegExp } from '../utils/CustomRegExp'; @Component({ selector: 'app-home', @@ -107,8 +107,7 @@ export class HomeComponent implements OnInit { * @param value string */ private isLocationRequest(value: string): boolean { - const regex = /^\d+\s[A-z]+\s[A-z]+/g; //NOSONAR - if (value.match(regex)) { + if (value.match(CustomRegExp.LOCATION)) { return true; } return false; diff --git a/src/app/map/components/map.component.scss b/src/app/map/components/map.component.scss index de50ff0b1..817150907 100644 --- a/src/app/map/components/map.component.scss +++ b/src/app/map/components/map.component.scss @@ -8,7 +8,7 @@ .map-wrapper { border-radius: 6px; - @include background-hash; + @include background-hash($grey-2); border: 1px solid $grey-4; } @@ -90,7 +90,7 @@ ::ng-deep .leaflet-popup { border-radius: 6px; - @include background-hash; + @include background-hash($grey-2); border: 1px solid $grey-4; padding: 0 0 4px 4px; bottom: -15px !important; diff --git a/src/app/models/owner.model.ts b/src/app/models/owner.model.ts new file mode 100644 index 000000000..fb682b346 --- /dev/null +++ b/src/app/models/owner.model.ts @@ -0,0 +1,4 @@ +export class Owner { + email: string; + id: string; +} diff --git a/src/app/models/structureWithOwners.model.ts b/src/app/models/structureWithOwners.model.ts new file mode 100644 index 000000000..e3d2a6340 --- /dev/null +++ b/src/app/models/structureWithOwners.model.ts @@ -0,0 +1,7 @@ +import { Owner } from './owner.model'; +import { Structure } from './structure.model'; + +export class StructureWithOwners { + structure: Structure; + owners: Owner[]; +} diff --git a/src/app/models/temp-user.model.ts b/src/app/models/temp-user.model.ts index 5a487980f..8540ce74e 100644 --- a/src/app/models/temp-user.model.ts +++ b/src/app/models/temp-user.model.ts @@ -1,7 +1,5 @@ export class TempUser { _id: string; email: string; - name: string; - surname: string; pendingStructuresLink: string[]; } diff --git a/src/app/profile/enum/TypeModalProfile.enum.ts b/src/app/profile/enum/TypeModalProfile.enum.ts new file mode 100644 index 000000000..4608f9bf5 --- /dev/null +++ b/src/app/profile/enum/TypeModalProfile.enum.ts @@ -0,0 +1,6 @@ +export enum TypeModalProfile { + password = 'password', + email = 'email', + deleteAccount = 'deleteAccount', + addAccount = 'addAccount', +} diff --git a/src/app/profile/enum/functionTypeModalOptions.enum.ts b/src/app/profile/enum/functionTypeModalOptions.enum.ts new file mode 100644 index 000000000..2ec4677af --- /dev/null +++ b/src/app/profile/enum/functionTypeModalOptions.enum.ts @@ -0,0 +1,9 @@ +export enum FunctionTypeModalOptions { + changeEmail = 1, + changePassword, + deleteAccount, + addUser, + removeUser, + editStructure, + removeStructure, +} diff --git a/src/app/profile/modal-options/modal-options.component.html b/src/app/profile/modal-options/modal-options.component.html new file mode 100644 index 000000000..6d8a62025 --- /dev/null +++ b/src/app/profile/modal-options/modal-options.component.html @@ -0,0 +1,46 @@ +<div class="modalOptions" (clickOutside)="closeModal(0)"> + <div *ngIf="isModalProfileOpts" class="modalContent" fxLayout="column" fxLayoutGap="10px"> + <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="9px"> + <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'email'"></app-svg-icon> + <p (click)="closeModal(functionType.changeEmail)">Changer mon courriel</p> + </div> + <div fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="9px"> + <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'password'"></app-svg-icon> + <p (click)="closeModal(functionType.changePassword)">Changer mon mot de passe</p> + </div> + <div fxLayout="row" class="deleteItem" fxLayoutAlign="start center" fxLayoutGap="9px"> + <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'cancel'"></app-svg-icon> + <p (click)="closeModal(functionType.deleteAccount)">Supprimer mon compte</p> + </div> + </div> + <div *ngIf="!isModalProfileOpts" class="modalContent" fxLayout="column" fxLayoutGap="10px"> + <div (click)="closeModal(functionType.addUser)" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="9px"> + <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'add'"></app-svg-icon> + <p>Ajouter un compte</p> + </div> + <div + (click)="hasOwners ? closeModal(functionType.removeUser) : null" + fxLayout="row" + fxLayoutAlign="start center" + fxLayoutGap="9px" + [ngClass]="{ invalid: !hasOwners }" + > + <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'remove'"></app-svg-icon> + <p>Supprimer un compte</p> + </div> + <div (click)="closeModal(functionType.editStructure)" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="9px"> + <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'edit'"></app-svg-icon> + <p>Modifier la structure</p> + </div> + <div + (click)="closeModal(functionType.removeStructure)" + fxLayout="row" + class="deleteItem" + fxLayoutAlign="start center" + fxLayoutGap="9px" + > + <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'cancel'"></app-svg-icon> + <p>Supprimer la structure</p> + </div> + </div> +</div> diff --git a/src/app/profile/modal-options/modal-options.component.scss b/src/app/profile/modal-options/modal-options.component.scss new file mode 100644 index 000000000..907e3f944 --- /dev/null +++ b/src/app/profile/modal-options/modal-options.component.scss @@ -0,0 +1,36 @@ +@import '../../../assets/scss/color'; +@import '../../../assets/scss/typography'; +@import '../../../assets/scss/shapes'; +@import '../../../assets/scss/z-index'; + +.modalOptions { + width: 300px; + background-color: $white; + position: absolute; + z-index: $modal-z-index; + list-style-type: none; + @include background-hash($secondary-color); + border: 1px solid $secondary-color; + border-radius: 6px; + .modalContent { + background: $white; + border-radius: 6px; + padding: 25px 22px 18px 22px; + div { + cursor: pointer; + &.invalid { + opacity: 0.4; + cursor: default; + } + } + p { + @include cn-bold-16; + white-space: nowrap; + margin: 0; + } + .deleteItem { + color: $red-default; + fill: $red-default; + } + } +} diff --git a/src/app/profile/modal-options/modal-options.component.spec.ts b/src/app/profile/modal-options/modal-options.component.spec.ts new file mode 100644 index 000000000..b1b9a866a --- /dev/null +++ b/src/app/profile/modal-options/modal-options.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ModalOptionsComponent } from './modal-options.component'; + +describe('ModalConfirmationComponent', () => { + let component: ModalOptionsComponent; + let fixture: ComponentFixture<ModalOptionsComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ModalOptionsComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ModalOptionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/profile/modal-options/modal-options.component.ts b/src/app/profile/modal-options/modal-options.component.ts new file mode 100644 index 000000000..e9eb32e28 --- /dev/null +++ b/src/app/profile/modal-options/modal-options.component.ts @@ -0,0 +1,21 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FunctionTypeModalOptions } from '../enum/functionTypeModalOptions.enum'; + +@Component({ + selector: 'app-modal-options', + templateUrl: './modal-options.component.html', + styleUrls: ['./modal-options.component.scss'], +}) +export class ModalOptionsComponent implements OnInit { + functionType = FunctionTypeModalOptions; + + constructor() {} + @Input() isModalProfileOpts = false; + @Input() hasOwners = true; + @Output() closed = new EventEmitter<number>(); + ngOnInit(): void {} + + public closeModal(value: number): void { + this.closed.emit(value); + } +} diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index a26b3822a..3ef2c4743 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -1,100 +1,311 @@ -<div fxLayout="column" class="content-container full-screen"> - <div class="section-container" fxLayout="column" fxLayoutAlign="center center"> - <h1>Profil</h1> +<div fxLayout="column" class="content-container full-screen" *ngIf="userProfile"> + <div class="profileSection"> + <div class="section-container" fxLayout="row"> + <svg class="cameraProfile" aria-hidden="true"> + <use [attr.xlink:href]="'assets/ico/sprite.svg#camera'"></use> + </svg> + <div class="profileInformation" fxLayoutGap="18px" fxLayout="column"> + <div fxLayout="row" fxLayoutAlign="space-between center"> + <p class="profileName">{{ userProfile.surname | titlecase }} {{ userProfile.name | titlecase }}</p> + <button class="btn-primary" (click)="logout()">Déconnexion</button> + </div> + <div class="profileEmail" fxLayout="column"> + <span>Identifiant</span> + <div fxLayout="row" fxLayoutAlign="space-between center"> + <p>{{ userProfile.email }}</p> + <nav aria-label="modalOption"> + <ul> + <li> + <button + [ngClass]="{ active: isModalOptsProfile }" + (click)="openModalOptsProfile()" + class="btn-primary transparent" + > + <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'moreOpts'"></app-svg-icon> + </button> + <ul *ngIf="isModalOptsProfile" class="dropdown"> + <app-modal-options + [isModalProfileOpts]="true" + [hasOwners]="false" + (closed)="closeModalOpts($event)" + ></app-modal-options> + </ul> + </li> + </ul> + </nav> + </div> + </div> + </div> + </div> + </div> + <div class="structureSection"> + <div class="section-container" fxLayoutGap="18px" fxLayout="column"> + <ng-container *ngIf="userProfile.structuresLink.length > 0 && structures"> + <div class="structureCard" *ngFor="let s of structures; let i = index"> + <div class="structureInfo" fxLayout="column" fxLayoutGap="14px"> + <div fxLayout="row" fxLayoutAlign="space-between start" fxLayoutGap="20px"> + <a class="structureName" routerLink="/home" [state]="{ data: s.structure }">{{ + s.structure.structureName + }}</a> + <nav aria-label="modalOption"> + <ul> + <li> + <button + [ngClass]="{ active: modalOptsStructureIndex == i }" + (click)="openModalOptsStructure(i, s)" + class="btn-primary transparent" + > + <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'moreOpts'"></app-svg-icon> + </button> + <ul *ngIf="modalOptsStructureIndex == i" class="dropdown"> + <app-modal-options + [isModalProfileOpts]="false" + [hasOwners]="currentStructureOwners.owners.length > 0" + (closed)="closeModalOpts($event)" + ></app-modal-options> + </ul> + </li> + </ul> + </nav> + </div> + <div fxLayout="column" fxLayoutGap="14px"> + <p class="ownerName" *ngFor="let owner of s.owners">{{ owner.email }}</p> + </div> + </div> + </div> + </ng-container> + + <div class="addSection" fxLayout="row" fxLayoutAlign="center center"> + <app-button + class="hide-on-print" + [type]="'button'" + [style]="'buttonWithHash'" + [text]="'Ajouter une structure'" + [iconBtn]="'add'" + (action)="addStructure()" + ></app-button> + </div> + </div> + </div> + + <div> <div *ngIf="userProfile" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="10px"> - <p>Id: {{ userProfile._id }}</p> - <p>Email: {{ userProfile.email }}</p> - <button (click)="logout()">Se déconnecter</button> - <p fxLayout="column" *ngIf="userProfile.structuresLink.length > 0"> - Mes structures : - <span *ngFor="let structureId of userProfile.structuresLink"> - <strong>{{ structureId }}</strong> - </span> - </p> <p fxLayout="column" *ngIf="userProfile.pendingStructuresLink.length > 0"> Mes structures en attente de validation: <span *ngFor="let structureId of userProfile.pendingStructuresLink"> <strong>{{ structureId }}</strong> </span> </p> - <button (click)="toogleAddStructure()">Ajouter une structure</button> - <button (click)="toogleChangeEmail()">Changer d'email</button> - <form - *ngIf="changeEmail" - fxLayout="column" - fxLayoutGap="10px" - [formGroup]="formEmail" - (ngSubmit)="onSubmitEmail()" - > - <div class="form-group"> - <label for="email">Email</label> - <input type="email" autocomplete="on" formControlName="email" class="form-control" /> - <div *ngIf="submitted" class="invalid-feedback"> - <app-validator-form [control]="formEmail.controls.email"></app-validator-form> - </div> - </div> - <div class="form-group"> - <button type="submit" [disabled]="loading" class="btn btn-primary">Appliquer</button> + </div> + </div> +</div> +<div *ngIf="editModal" class="modalBackground"> + <div class="modal" (clickOutside)="closeModalOptsProfile()"> + <form + *ngIf="editModal == typeModalProfile.password" + [formGroup]="formPassword" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + fxLayoutGap="20px" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Changer de mot de passe</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div class="form-group" fxLayout="column" fxLayoutGap="4px"> + <label for="oldPassword">Ancien mot de passe</label> + <p *ngIf="passwordError" class="special invalid">Votre ancien mot de passe est incorrect.</p> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowOldPassword ? 'text' : 'password'" + formControlName="oldPassword" + class="form-input password" + autocomplete="on" + /> + <app-svg-icon + (click)="showOldPassword()" + [type]="'form'" + [iconClass]="'grey'" + [icon]="'eyePassword'" + ></app-svg-icon> + <app-svg-icon *ngIf="passwordError" [type]="'form'" [icon]="'notValidate'"></app-svg-icon> </div> - </form> - <button (click)="toogleChangePassword()">Changer de mot de passe</button> - - <form *ngIf="changePassword" fxLayout="column" fxLayoutGap="10px" [formGroup]="form" (ngSubmit)="onSubmit()"> - <div class="form-group"> - <label for="oldPassword">Ancien mot de passe</label> + </div> + <div class="form-group" fxLayout="column"> + <label for="password">Nouveau mot de passe</label> + <p class="special" [ngClass]="{ invalid: fpass.password.invalid && fpass.password.value }"> + Le mot de passe doit contenir au minimum : 8 caractères dont un caractère spécial, un caractère en majuscule + et un chiffre. + </p> + <div fxLayout="row" fxLayoutGap="13px"> <input - type="password" + [type]="isShowPassword ? 'text' : 'password'" + formControlName="password" + class="form-input password" autocomplete="on" - formControlName="oldPassword" - class="form-control" - [ngClass]="{ 'is-invalid': submitted && f.oldPassword.errors }" /> - <div *ngIf="submitted && f.oldPassword.errors" class="invalid-feedback"> - <div *ngIf="f.oldPassword.errors.required">L'Ancien mot de passe est obligatoire</div> - </div> + <app-svg-icon + (click)="showPassword()" + [type]="'form'" + [iconClass]="'grey'" + [icon]="'eyePassword'" + ></app-svg-icon> + <app-svg-icon *ngIf="fpass.password.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> + <app-svg-icon + *ngIf="fpass.password.invalid && fpass.password.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> - <div class="form-group"> - <label for="password">Mot de passe</label> + </div> + <div class="form-group" fxLayout="column"> + <label for="confirmPassword">Confirmation du mot de passe</label> + <div fxLayout="row" fxLayoutGap="13px"> <input - type="password" + [type]="isShowConfirmPassword ? 'text' : 'password'" + formControlName="confirmPassword" + class="form-input password" autocomplete="on" - formControlName="password" - class="form-control" - [ngClass]="{ 'is-invalid': submitted && f.password.errors }" /> - <div *ngIf="submitted && f.password.errors" class="invalid-feedback"> - <div *ngIf="f.password.errors.required">Le mot de passe est obligatoire</div> - <div *ngIf="f.password.errors.pattern"> - Le mot de passe doit avoir au minimun 8 caractères, une majuscule, une minuscule, un chiffre et un - caractère spécial. - </div> - </div> + <app-svg-icon + (click)="showConfirmPassword()" + [type]="'form'" + [iconClass]="'grey'" + [icon]="'eyePassword'" + ></app-svg-icon> + <app-svg-icon + *ngIf="fpass.confirmPassword.valid && fpass.password.value" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="fpass.confirmPassword.invalid && fpass.confirmPassword.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> - <div class="form-group"> - <label for="confirmPassword">Confirmation du mot de passe</label> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button + type="submit" + [ngClass]="{ invalid: formPassword.invalid }" + class="btn-primary small leave" + (click)="submitPassword()" + > + Valider + </button> + </div> + </form> + <form + *ngIf="editModal == typeModalProfile.email" + [formGroup]="formEmail" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Changer de courriel</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div class="form-group" fxLayout="column"> + <label for="email">Nouveau courriel</label> + <p class="special invalid" *ngIf="this.fmail.email.hasError('alreadyExist')">L'email est déja utilisé.</p> + <div fxLayout="row" fxLayoutGap="13px"> <input - type="password" + type="text" + formControlName="email" + class="form-input" autocomplete="on" - formControlName="confirmPassword" - class="form-control" - [ngClass]="{ 'is-invalid': submitted && f.confirmPassword.errors }" + (keyup)="verifyEmailAlreadyUsed($event.target.value, this.fmail.email)" /> - <div *ngIf="submitted && f.confirmPassword.errors" class="invalid-feedback"> - <div *ngIf="f.confirmPassword.errors.required">La confirmation du mot de passe est obligatoire</div> - <div *ngIf="f.confirmPassword.errors.mustMatch">Les mot de passe ne sont pas les mêmes</div> - </div> + <app-svg-icon *ngIf="fmail.email.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> + <app-svg-icon + *ngIf="fmail.email.invalid && fmail.email.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> - - <div class="form-group"> - <button type="submit" [disabled]="loading" class="btn btn-primary">Appliquer</button> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button + type="submit" + [ngClass]="{ invalid: formEmail.invalid }" + class="btn-primary small leave" + (click)="submitEmail()" + > + Valider + </button> + </div> + </form> + <div + *ngIf="editModal == typeModalProfile.deleteAccount" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + fxLayoutGap="30px" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Supprimer un compte</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div fxLayout="column" fxLayoutGap="16px"> + <div class="row removeOwner" *ngFor="let owner of currentStructureOwners.owners" fxLayoutGap="16px"> + <button class="btn-primary small" (click)="removeOwner(owner.id)">X</button> + <span> + {{ owner.email }} + </span> </div> - </form> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button type="button" class="btn-primary small leave" (click)="closeModalOptsProfile()">Terminer</button> + </div> </div> + <form + *ngIf="editModal == typeModalProfile.addAccount" + [formGroup]="formAddAccount" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + fxLayoutGap="30px" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Ajouter un compte</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div class="form-group" fxLayout="column"> + <label for="email">Courriel du compte à ajouter</label> + <p *ngIf="ownerAlreadyLinked" class="special invalid">L'email est déjà rattaché à la structure.</p> + <div fxLayout="row" fxLayoutGap="13px"> + <input type="text" formControlName="email" class="form-input" autocomplete="on" /> + <app-svg-icon *ngIf="fAddAccount.email.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> + <app-svg-icon + *ngIf="fAddAccount.email.invalid && fAddAccount.email.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button + type="submit" + [ngClass]="{ invalid: formAddAccount.invalid }" + class="btn-primary small leave" + (click)="addOwner()" + > + Envoyer + </button> + </div> + </form> </div> - <app-structureForm - *ngIf="addStructure" - [profile]="userProfile" - (closeEvent)="toogleAddStructure($event)" - (clickOutside)="toogleAddStructure()" - ></app-structureForm> </div> +<app-modal-confirmation + [openned]="deleteModalStructureOpenned" + [content]="'Voulez-vous vraiment supprimer cette structure ?'" + (closed)="deleteStructure($event)" +></app-modal-confirmation> +<app-modal-confirmation + [openned]="deleteModalAccountOpenned" + [content]="'Voulez-vous vraiment supprimer votre compte ?'" + (closed)="deleteAccount($event)" +></app-modal-confirmation> diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss index e69de29bb..c7b151d23 100644 --- a/src/app/profile/profile.component.scss +++ b/src/app/profile/profile.component.scss @@ -0,0 +1,166 @@ +@import '../../assets/scss/color'; +@import '../../assets/scss/typography'; +@import '../../assets/scss/hyperlink'; +@import '../../assets/scss/shapes'; +@import '../../assets/scss/breakpoint'; + +.content-container { + padding-top: 0; +} +.section-container { + width: 50%; + padding: 20px 0; + .profileInformation { + width: 100%; + } + .cameraProfile { + width: 128px; + margin-right: 16px; + height: 113px; + } + @media #{$tablet} { + .cameraProfile { + display: none; + } + width: 70%; + } + @media #{$large-phone} { + width: 90%; + } +} +.profileSection { + background: $white; + border-bottom: 1px solid $grey-4; + .profileName { + @include cn-bold-24; + margin: 0 !important; + } + .profileEmail { + span { + @include cn-regular-16; + color: $grey-2; + } + p { + margin: 0; + @include cn-regular-18; + } + } +} +button { + &.transparent { + background: none; + border: 1px solid $grey-4; + border-radius: 6px; + } +} +.structureSection { + margin-bottom: 108px; + .structureCard { + border: 1px solid $grey-4; + border-radius: 6px; + .structureInfo { + border-radius: 6px; + background: $white; + min-height: 100px; + padding: 33px 55px; + @media #{$large-phone} { + padding: 33px 25px; + } + a { + margin: 0; + &.structureName { + @include cn-bold-24; + color: $secondary-color; + text-decoration: underline; + } + } + .ownerName { + @include cn-regular-18; + color: $grey-2; + } + } + @include background-hash($grey-2); + } +} +.addSection { + button { + background: $red-default; + color: $white; + } +} +.contentModal { + padding: 35px 34px 18px 54px !important; + .headerModal { + width: 100%; + } + p { + &.special { + margin: 8px 0; + @include cn-regular-14; + color: $grey-3; + &.invalid { + color: $orange-warning; + } + } + } + .removeOwner { + button { + width: 40px; + background-color: $red-default; + } + span { + @include cn-bold-18; + } + } + button { + &.invalid { + opacity: 0.4; + } + } + .form-group { + width: 100%; + padding-right: 40px; + input { + width: 100%; + } + } + .ico-close-details { + min-width: 40px; + } +} + +ul { + list-style: none; + margin: 0; + padding-left: 0; +} + +li { + fill: $secondary-color; + color: $black; + display: block; + float: left; + position: relative; + text-decoration: none; + button { + width: 40px; + fill: $secondary-color; + &.active { + background-color: $secondary-color; + fill: $white; + border-color: $secondary-color; + } + &:hover { + background-color: $secondary-color; + fill: $white; + border-color: $secondary-color; + } + } +} + +ul li ul { + position: absolute; + display: block; + margin-left: -268px; + margin-top: 7px; +} diff --git a/src/app/profile/profile.component.ts b/src/app/profile/profile.component.ts index 5f3b49a73..67cb92574 100644 --- a/src/app/profile/profile.component.ts +++ b/src/app/profile/profile.component.ts @@ -1,8 +1,16 @@ import { Component, OnInit } from '@angular/core'; -import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { Structure } from '../models/structure.model'; +import { StructureWithOwners } from '../models/structureWithOwners.model'; +import { TempUser } from '../models/temp-user.model'; import { User } from '../models/user.model'; import { AuthService } from '../services/auth.service'; +import { StructureService } from '../services/structure.service'; import { MustMatch } from '../shared/validator/form'; +import { CustomRegExp } from '../utils/CustomRegExp'; +import { FunctionTypeModalOptions } from './enum/functionTypeModalOptions.enum'; +import { TypeModalProfile } from './enum/TypeModalProfile.enum'; import { ProfileService } from './services/profile.service'; @Component({ @@ -11,73 +19,136 @@ import { ProfileService } from './services/profile.service'; styleUrls: ['./profile.component.scss'], }) export class ProfileComponent implements OnInit { - public form: FormGroup; - public userProfile: User; - public submitted = false; + // Password profile + public formPassword: FormGroup; + public isShowOldPassword = false; + public isShowPassword = false; + public isShowConfirmPassword = false; public changePassword = false; - public loading = false; - public changeEmail = false; + public passwordError = false; + + // Email profile public formEmail: FormGroup; - public addStructure = false; + public changeEmail = false; + + // formAddAccount + public formAddAccount: FormGroup; + public ownerAlreadyLinked = false; + + // Global var + public userProfile: User; + public loading = false; + public structures: StructureWithOwners[] = []; + public editModal: TypeModalProfile; + public typeModalProfile = TypeModalProfile; + + // Modal options + public modalOptsStructureIndex: number; + public isModalOptsProfile = false; + public currentStructureOwners: StructureWithOwners; + public deleteModalStructureOpenned = false; + public deleteModalAccountOpenned = false; constructor( private authService: AuthService, private formBuilder: FormBuilder, - private profileService: ProfileService + private profileService: ProfileService, + private structureService: StructureService, + private router: Router ) {} ngOnInit(): void { this.profileService.getProfile().then((profile) => { this.userProfile = profile; + this.structures = []; + profile.structuresLink.forEach((structureId) => { + this.structureService.getStructureWithOwners(structureId, profile).subscribe((s) => { + this.structures.push(s); + }); + }); }); this.initForm(); } - public initForm(): void { - this.form = this.formBuilder.group( + this.formPassword = this.formBuilder.group( { - oldPassword: [ - '', - [Validators.required, Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/)], //NOSONAR - ], - password: [ - '', - [Validators.required, Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/)], //NOSONAR - ], + oldPassword: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], + password: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], confirmPassword: [''], }, { validator: MustMatch('password', 'confirmPassword') } ); + this.formEmail = this.formBuilder.group({ - email: ['', [Validators.required, Validators.pattern('[a-z0-9._%+-]+@[a-z0-9.-]+.[a-z]{2,3}$')]], + email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], }); - } + this.formAddAccount = this.formBuilder.group({ + email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + }); + } // getter for form fields - get f(): { [key: string]: AbstractControl } { - return this.form.controls; + get fpass(): { [key: string]: AbstractControl } { + return this.formPassword.controls; } - public toogleChangePassword(): void { - this.changePassword = !this.changePassword; + // getter for form fields + get fmail(): { [key: string]: AbstractControl } { + return this.formEmail.controls; } - public toogleAddStructure(): void { - this.addStructure = !this.addStructure; + + get fAddAccount(): { [key: string]: AbstractControl } { + return this.formAddAccount.controls; } - public toogleChangeEmail(): void { - this.changeEmail = !this.changeEmail; + public closeModalOpts(functionType: number): void { + switch (functionType) { + case FunctionTypeModalOptions.changeEmail: + this.editModal = TypeModalProfile.email; + break; + case FunctionTypeModalOptions.changePassword: + this.editModal = TypeModalProfile.password; + break; + case FunctionTypeModalOptions.deleteAccount: + this.toggleDeleteAccountModal(); + break; + case FunctionTypeModalOptions.addUser: + this.editModal = TypeModalProfile.addAccount; + this.ownerAlreadyLinked = false; + break; + case FunctionTypeModalOptions.removeUser: + this.editModal = TypeModalProfile.deleteAccount; + break; + case FunctionTypeModalOptions.editStructure: + this.router.navigateByUrl('/create-structure', { state: { data: this.currentStructureOwners.structure } }); + break; + case FunctionTypeModalOptions.removeStructure: + this.toggleDeleteStructureModal(); + break; + default: + break; + } + this.isModalOptsProfile = false; + this.modalOptsStructureIndex = null; } - public onSubmitEmail(): void { - this.submitted = true; + // Profile Section + public closeModalOptsProfile(): void { + this.editModal = null; + this.formAddAccount.reset(); + this.formEmail.reset(); + this.formPassword.reset(); + } + public submitEmail(): void { + // stop here if form is invalid if (this.formEmail.invalid) { return; } this.loading = true; this.profileService.changeEmail(this.formEmail.value.email, this.userProfile.email).subscribe( () => { - this.toogleChangeEmail(); + this.closeModalOptsProfile(); + this.formEmail.reset(); this.loading = false; }, (err) => { @@ -85,25 +156,109 @@ export class ProfileComponent implements OnInit { } ); } - public onSubmit(): void { - this.submitted = true; + public submitPassword(): void { // stop here if form is invalid - if (this.form.invalid) { + if (this.formPassword.invalid) { return; } this.loading = true; - this.profileService.changePassword(this.form.value.password, this.form.value.oldPassword).subscribe( + this.profileService.changePassword(this.formPassword.value.password, this.formPassword.value.oldPassword).subscribe( () => { - this.toogleChangePassword(); + this.closeModalOptsProfile(); + this.formPassword.reset(); + this.loading = false; + this.passwordError = false; }, (error) => { + this.passwordError = true; this.loading = false; } ); } - + public openModalOptsProfile(): void { + this.isModalOptsProfile = true; + } + public showOldPassword(): void { + this.isShowOldPassword = !this.isShowOldPassword; + } + public showPassword(): void { + this.isShowPassword = !this.isShowPassword; + } + public showConfirmPassword(): void { + this.isShowConfirmPassword = !this.isShowConfirmPassword; + } public logout(): void { this.authService.logout(); } + public deleteAccount(shouldDelete: boolean): void { + this.toggleDeleteAccountModal(); + if (shouldDelete) { + this.profileService.deleteProfile().subscribe(() => { + this.logout(); + }); + } + } + + // Structure section + public openModalOptsStructure(index: number, s: StructureWithOwners): void { + this.modalOptsStructureIndex = index; + this.currentStructureOwners = s; + } + public addStructure(): void { + this.router.navigateByUrl('/create-structure'); + } + private toggleDeleteStructureModal(): void { + this.deleteModalStructureOpenned = !this.deleteModalStructureOpenned; + } + private toggleDeleteAccountModal(): void { + this.deleteModalAccountOpenned = !this.deleteModalAccountOpenned; + } + + public deleteStructure(shouldDelete: boolean): void { + this.toggleDeleteStructureModal(); + if (shouldDelete) { + this.structureService.delete(this.currentStructureOwners.structure._id).subscribe((structure: Structure) => { + this.ngOnInit(); + }); + } + } + + public verifyEmailAlreadyUsed(inputEmail, formControl: FormControl): void { + if (formControl.valid) { + this.profileService.isEmailAlreadyUsed(inputEmail).subscribe((isExist) => { + if (isExist) { + formControl.setErrors({ alreadyExist: true }); + } + }); + } + } + public removeOwner(owner: string): void { + this.structureService.removeOwnerFromStructure(owner, this.currentStructureOwners.structure._id).subscribe(() => { + this.currentStructureOwners.owners = this.currentStructureOwners.owners.filter((o) => o.id !== owner); + if (this.currentStructureOwners.owners.length == 0) { + this.closeModalOptsProfile(); + } + }); + } + public addOwner(): void { + // stop here if form is invalid + if (this.formAddAccount.invalid) { + return; + } + this.loading = true; + const user = new TempUser(); + user.email = this.fAddAccount.email.value; + this.structureService.addOwnerToStructure(user, this.currentStructureOwners.structure._id).subscribe( + () => { + this.closeModalOptsProfile(); + this.formAddAccount.reset(); + this.loading = false; + }, + (err) => { + this.ownerAlreadyLinked = true; + this.loading = false; + } + ); + } } diff --git a/src/app/profile/profile.module.ts b/src/app/profile/profile.module.ts index a9427003f..adf5fb74c 100644 --- a/src/app/profile/profile.module.ts +++ b/src/app/profile/profile.module.ts @@ -3,10 +3,11 @@ import { ProfileComponent } from './profile.component'; import { SharedModule } from '../shared/shared.module'; import { CommonModule } from '@angular/common'; import { BrowserModule } from '@angular/platform-browser'; +import { ModalOptionsComponent } from './modal-options/modal-options.component'; @NgModule({ imports: [CommonModule, BrowserModule, SharedModule], - declarations: [ProfileComponent], + declarations: [ProfileComponent, ModalOptionsComponent], exports: [ProfileComponent], }) export class ProfileModule {} diff --git a/src/app/profile/services/profile.service.ts b/src/app/profile/services/profile.service.ts index 849d148b4..6b7515fd4 100644 --- a/src/app/profile/services/profile.service.ts +++ b/src/app/profile/services/profile.service.ts @@ -15,7 +15,7 @@ export class ProfileService { constructor(private http: HttpClient, private authService: AuthService) {} public async getProfile(): Promise<User> { - if (this.authService.isLoggedIn() && !this.currentProfile) { + if (this.authService.isLoggedIn()) { const profile = await this.http.get<User>(`${this.baseUrl}/profile`).toPromise(); this.currentProfile = profile; } @@ -25,6 +25,9 @@ export class ProfileService { public setProfile(profile: User): void { this.currentProfile = profile; } + public deleteProfile(): Observable<User> { + return this.http.delete<User>(`${this.baseUrl}`); + } public isLinkedToStructure(idStructure: string): boolean { if (!this.currentProfile) { diff --git a/src/app/reset-password/reset-password.component.ts b/src/app/reset-password/reset-password.component.ts index 3f3187b99..153f2dc8c 100644 --- a/src/app/reset-password/reset-password.component.ts +++ b/src/app/reset-password/reset-password.component.ts @@ -3,6 +3,7 @@ import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/fo import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { MustMatch } from '../shared/validator/form'; +import { CustomRegExp } from '../utils/CustomRegExp'; @Component({ selector: 'app-reset-password', @@ -36,10 +37,7 @@ export class ResetPasswordComponent implements OnInit { private initPasswordForm(): void { this.resetFormChangePassword = this.formBuilder.group( { - password: [ - '', - [Validators.required, Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/)], //NOSONAR - ], + password: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], confirmPassword: [''], }, { validator: MustMatch('password', 'confirmPassword') } diff --git a/src/app/services/structure.service.ts b/src/app/services/structure.service.ts index 7848ab3c4..f65757bff 100644 --- a/src/app/services/structure.service.ts +++ b/src/app/services/structure.service.ts @@ -12,6 +12,9 @@ import { Weekday } from '../structure-list/enum/weekday.enum'; import { Time } from '../models/time.model'; import { Filter } from '../structure-list/models/filter.model'; import { User } from '../models/user.model'; +import { StructureWithOwners } from '../models/structureWithOwners.model'; +import { Owner } from '../models/owner.model'; +import { TempUser } from '../models/temp-user.model'; @Injectable({ providedIn: 'root', @@ -62,6 +65,13 @@ export class StructureService { return this.http.delete<Structure>(`${this.baseUrl}/${id}`); } + public removeOwnerFromStructure(idOwner: string, idStructure: string): Observable<any> { + return this.http.delete<any>(`${this.baseUrl}/${idStructure}/owner/${idOwner}`); + } + public addOwnerToStructure(user: TempUser, idStructure: string): Observable<any> { + return this.http.post<any>(`${this.baseUrl}/${idStructure}/addOwner`, user); + } + public getStructures(filters: Filter[]): Observable<Structure[]> { if (filters && filters.length > 0) { let requestUrl = `${this.baseUrl}/search`; @@ -181,4 +191,7 @@ export class StructureService { return tabNum[0] + 'h' + tabNum[1]; } } + public getStructureWithOwners(structureId: string, profile: User): Observable<StructureWithOwners> { + return this.http.post<any>(`${this.baseUrl}/${structureId}/withOwners`, { emailUser: profile.email }); + } } diff --git a/src/app/shared/components/button/button.component.html b/src/app/shared/components/button/button.component.html index 099145ec9..dcfb2a0f7 100644 --- a/src/app/shared/components/button/button.component.html +++ b/src/app/shared/components/button/button.component.html @@ -6,16 +6,10 @@ fxLayout="row center" class="searchButton withIcon" fxLayoutAlign="space-between center" - fxLayoutGap="13px" + fxLayoutGap="5px" > - <app-svg-icon - style="height: 100%" - [type]="'ico'" - [iconClass]="'icon-32'" - [icon]="iconBtn" - [iconColor]="'currentColor'" - ></app-svg-icon> - {{ text }} + <app-svg-icon style="height: 100%" [type]="'ico'" [icon]="iconBtn" [iconColor]="'currentColor'"></app-svg-icon> + <span>{{ text }}</span> </div> </button> </ng-container> diff --git a/src/app/shared/components/button/button.component.scss b/src/app/shared/components/button/button.component.scss index 6aecd56b1..55ae3ca67 100644 --- a/src/app/shared/components/button/button.component.scss +++ b/src/app/shared/components/button/button.component.scss @@ -22,7 +22,7 @@ button { border: 1px solid; } .btnSearch { - @include background-hash; + @include background-hash($grey-2); border-color: $grey-4; padding: 0 0 4px 5px; &:hover { @@ -45,6 +45,7 @@ button { padding: 3px 16px 3px 16px; display: table-cell; vertical-align: middle; + border-radius: 4px; @include btn-bold; &.withIcon { color: $black; diff --git a/src/app/shared/components/create-account-form/create-account-form.component.spec.ts b/src/app/shared/components/create-account-form/create-account-form.component.spec.ts index 79e2eb5be..1a3aa14de 100644 --- a/src/app/shared/components/create-account-form/create-account-form.component.spec.ts +++ b/src/app/shared/components/create-account-form/create-account-form.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { CustomRegExp } from '../../../utils/CustomRegExp'; import { MustMatch } from '../../validator/form'; import { CreateAccountFormComponent } from './create-account-form.component'; @@ -12,7 +13,7 @@ describe('CreateAccountFormComponent', () => { email: new FormControl('test@test.fr', Validators.required), password: new FormControl('Testaze123!', [ Validators.required, - Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR + Validators.pattern(CustomRegExp.PASSWORD), //NOSONAR ]), confirmPassword: new FormControl('Testaze123!'), }, diff --git a/src/app/shared/components/create-account-form/create-account-form.component.ts b/src/app/shared/components/create-account-form/create-account-form.component.ts index 702924838..5d3d73098 100644 --- a/src/app/shared/components/create-account-form/create-account-form.component.ts +++ b/src/app/shared/components/create-account-form/create-account-form.component.ts @@ -1,6 +1,6 @@ -import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { Component, EventEmitter, OnInit, Output } from '@angular/core'; import { AbstractControl, FormControl, FormGroup, Validators } from '@angular/forms'; -import { Regex } from '../../enum/regex.enum'; +import { CustomRegExp } from '../../../utils/CustomRegExp'; import { MustMatch } from '../../validator/form'; @Component({ @@ -19,14 +19,11 @@ export class CreateAccountFormComponent implements OnInit { ngOnInit(): void { this.accountForm = new FormGroup( { - email: new FormControl('', [Validators.required, Validators.pattern(Regex.email)]), - name: new FormControl('', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), - surname: new FormControl('', [Validators.required, Validators.pattern(Regex.textWithoutNumber)]), - phone: new FormControl('', [Validators.required, Validators.pattern('([0-9]{2} ){4}[0-9]{2}')]), //NOSONAR - password: new FormControl('', [ - Validators.required, - Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR - ]), + email: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]), + name: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)]), + surname: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)]), + phone: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.PHONE)]), + password: new FormControl('', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]), confirmPassword: new FormControl(''), }, [MustMatch('password', 'confirmPassword')] diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss index 8b50ba8f3..21be72c13 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.scss @@ -39,7 +39,7 @@ width: 350px; margin: auto; border-radius: 6px; - @include background-hash; + @include background-hash($grey-2); border: 1px solid $grey-4; margin-top: 50vh; transform: translateY(-50%); diff --git a/src/app/shared/components/signup-modal/signup-modal.component.ts b/src/app/shared/components/signup-modal/signup-modal.component.ts index eb03ae892..2c13f60c1 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.ts +++ b/src/app/shared/components/signup-modal/signup-modal.component.ts @@ -3,7 +3,7 @@ import { FormGroup, FormBuilder, Validators, AbstractControl } from '@angular/fo import { ActivatedRoute, Router } from '@angular/router'; import { first } from 'rxjs/operators'; import { AuthService } from '../../../services/auth.service'; -import { Regex } from '../../enum/regex.enum'; +import { CustomRegExp } from '../../../utils/CustomRegExp'; @Component({ selector: 'app-signup-modal', @@ -29,14 +29,8 @@ export class SignUpModalComponent implements OnInit { ngOnInit(): void { this.loginForm = this.formBuilder.group({ - email: ['', [Validators.required, Validators.pattern(Regex.email)]], - password: [ - '', - [ - Validators.required, - Validators.pattern(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/), //NOSONAR - ], - ], + email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + password: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], }); // get return url from route parameters or default to '/' this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; diff --git a/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss b/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss index bac3648ad..9772d6210 100644 --- a/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss +++ b/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss @@ -59,7 +59,7 @@ button { } } .containerBtn { - @include background-hash; + @include background-hash($grey-2); padding: 0 0 4px 5px; border-radius: 4px; cursor: pointer; diff --git a/src/app/shared/components/svg-icon/svg-icon.component.scss b/src/app/shared/components/svg-icon/svg-icon.component.scss index c1377ef46..30dac9094 100644 --- a/src/app/shared/components/svg-icon/svg-icon.component.scss +++ b/src/app/shared/components/svg-icon/svg-icon.component.scss @@ -1,3 +1,4 @@ +@import '../../../../assets/scss/color'; .icon { display: inline-block; height: 2em; @@ -8,6 +9,10 @@ &.icon-75 { width: 4.688em; } + &.grey { + fill: $grey-3; + stroke: $grey-3; + } } svg { diff --git a/src/app/shared/enum/regex.enum.ts b/src/app/shared/enum/regex.enum.ts deleted file mode 100644 index 6011651df..000000000 --- a/src/app/shared/enum/regex.enum.ts +++ /dev/null @@ -1,11 +0,0 @@ -export enum Regex { - email = '[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}', - textWithoutNumber = '[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}', - phone = '([0-9]{2} ){4}[0-9]{2}', - website = '(www[.])?(https://)?(http://)?[a-zA-Z0-9.-]*[.][a-z]{2,3}((/)[a-zA-Z0-9-/]*)?', - linkedIn = '(linkedin.com/in/.{1,})', - facebook = '(facebook.com/.{1,})', - twitter = '(twitter.com/.{1,})', - instagram = '(instagram.com/.{1,})', - noNullNumber = '[1-9]{1}[0-9]*', -} diff --git a/src/app/structure-list/components/modal-filter/modal-filter.component.scss b/src/app/structure-list/components/modal-filter/modal-filter.component.scss index 247ab7361..7c31c30f9 100644 --- a/src/app/structure-list/components/modal-filter/modal-filter.component.scss +++ b/src/app/structure-list/components/modal-filter/modal-filter.component.scss @@ -37,7 +37,7 @@ border: none; padding: 0; } - @include background-hash; + @include background-hash($grey-2); border: 1px solid $grey-4; ::-webkit-scrollbar { width: 16px; diff --git a/src/app/utils/CustomRegExp.ts b/src/app/utils/CustomRegExp.ts new file mode 100644 index 000000000..94ead16e4 --- /dev/null +++ b/src/app/utils/CustomRegExp.ts @@ -0,0 +1,22 @@ +export class CustomRegExp { + /** + * Validate a password (at least 8 characters, 1 uppercase letter, 1 lowercase letter, 1 number, and 1 special character) + */ + public static readonly PASSWORD: RegExp = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/; + /** + * Validate an email + */ + public static readonly EMAIL: RegExp = /^[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}/; + public static readonly TEXT_WITHOUT_NUMBER: RegExp = /^[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}/; + public static readonly PHONE: RegExp = /^([0-9]{2} ){4}[0-9]{2}/; + public static readonly WEBSITE: RegExp = /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/; + public static readonly LINKEDIN: string = '(linkedin.com/in/.{1,})'; + public static readonly FACEBOOK: string = '(facebook.com/.{1,})'; + public static readonly TWITTER: string = '(twitter.com/.{1,})'; + public static readonly INSTAGRAM: string = '(instagram.com/.{1,})'; + public static readonly NO_NULL_NUMBER: string = '[1-9]{1}[0-9]'; + /** + * Validate a location request in search bar + */ + public static readonly LOCATION: RegExp = /^\d+\s[A-z]+\s[A-z]+/g; +} diff --git a/src/assets/form/sprite.svg b/src/assets/form/sprite.svg index e81f41f7b..37f14d8e9 100644 --- a/src/assets/form/sprite.svg +++ b/src/assets/form/sprite.svg @@ -336,4 +336,9 @@ <path d="M13.25 20.6066L13.25 20" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> </symbol> +<symbol id="validate" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg"> +<circle cx="13" cy="13" r="13" fill="#47C562"/> +<path d="M8 13.8182L11.8889 17L18 10" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> +</symbol> + </svg> \ No newline at end of file diff --git a/src/assets/ico/more_vert_24px.svg b/src/assets/ico/more_vert_24px.svg new file mode 100644 index 000000000..4e88a7515 --- /dev/null +++ b/src/assets/ico/more_vert_24px.svg @@ -0,0 +1,3 @@ +<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11 7.66675C12.1 7.66675 13 6.76675 13 5.66675C13 4.56675 12.1 3.66675 11 3.66675C9.9 3.66675 9 4.56675 9 5.66675C9 6.76675 9.9 7.66675 11 7.66675ZM11 9.66675C9.9 9.66675 9 10.5667 9 11.6667C9 12.7667 9.9 13.6667 11 13.6667C12.1 13.6667 13 12.7667 13 11.6667C13 10.5667 12.1 9.66675 11 9.66675ZM9 17.6667C9 16.5667 9.9 15.6667 11 15.6667C12.1 15.6667 13 16.5667 13 17.6667C13 18.7667 12.1 19.6667 11 19.6667C9.9 19.6667 9 18.7667 9 17.6667Z" fill="#348899"/> +</svg> diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 08676f739..02b135476 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -24,9 +24,9 @@ <path fill-rule="evenodd" clip-rule="evenodd" d="M4 4C4 3.44771 4.44772 3 5 3H14C14.5523 3 15 3.44772 15 4V6H9C8.44772 6 8 6.44772 8 7V18H5C4.44772 18 4 17.5523 4 17V4ZM10 7C9.44772 7 9 7.44772 9 8V20C9 20.5523 9.44771 21 10 21H19C19.5523 21 20 20.5523 20 20V8C20 7.44772 19.5523 7 19 7H10Z" fill="#32383D"/> </symbol> -<symbol id="cancel" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M16.9498 5.36385C17.3403 4.97332 17.9734 4.97332 18.364 5.36385C18.7545 5.75437 18.7545 6.38753 18.364 6.77806L7.05026 18.0918C6.65973 18.4823 6.02657 18.4823 5.63605 18.0918C5.24552 17.7012 5.24552 17.0681 5.63605 16.6776L16.9498 5.36385Z" fill="black"/> -<path d="M18.364 16.6777C18.7545 17.0682 18.7545 17.7013 18.364 18.0919C17.9734 18.4824 17.3403 18.4824 16.9498 18.0919L5.63605 6.77816C5.24552 6.38764 5.24552 5.75447 5.63605 5.36395C6.02657 4.97343 6.65974 4.97343 7.05026 5.36395L18.364 16.6777Z" fill="black"/> +<symbol id="cancel" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="M16.9498 5.36385C17.3403 4.97332 17.9734 4.97332 18.364 5.36385C18.7545 5.75437 18.7545 6.38753 18.364 6.77806L7.05026 18.0918C6.65973 18.4823 6.02657 18.4823 5.63605 18.0918C5.24552 17.7012 5.24552 17.0681 5.63605 16.6776L16.9498 5.36385Z" stroke="none"/> +<path d="M18.364 16.6777C18.7545 17.0682 18.7545 17.7013 18.364 18.0919C17.9734 18.4824 17.3403 18.4824 16.9498 18.0919L5.63605 6.77816C5.24552 6.38764 5.24552 5.75447 5.63605 5.36395C6.02657 4.97343 6.65974 4.97343 7.05026 5.36395L18.364 16.6777Z" stroke="none"/> </symbol> <symbol id="nok" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg"> @@ -40,10 +40,17 @@ <path d="M11 16.8182L14.8889 20L21 13" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> </symbol> -<symbol id="add" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M12 5C11.4477 5 11 5.44772 11 6V11H6C5.44772 11 5 11.4477 5 12C5 12.5523 5.44772 13 6 13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V13H18C18.5523 13 19 12.5523 19 12C19 11.4477 18.5523 11 18 11H13V6C13 5.44772 12.5523 5 12 5Z" fill="#333333"/> +<symbol id="add" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="M12 5C11.4477 5 11 5.44772 11 6V11H6C5.44772 11 5 11.4477 5 12C5 12.5523 5.44772 13 6 13H11V18C11 18.5523 11.4477 19 12 19C12.5523 19 13 18.5523 13 18V13H18C18.5523 13 19 12.5523 19 12C19 11.4477 18.5523 11 18 11H13V6C13 5.44772 12.5523 5 12 5Z" stroke="none"/> </symbol> +<symbol id="remove" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"> +<path d="M6.97363 12.9062C6.34733 12.9062 5.89876 12.7877 5.62793 12.5507C5.36556 12.3053 5.23438 11.9879 5.23438 11.5986C5.23438 11.2092 5.36556 10.8961 5.62793 10.6591C5.89876 10.4137 6.34733 10.2952 6.97363 10.3036C14.44 10.3036 10.571 10.3036 17.0156 10.3036C17.6419 10.3036 18.0863 10.4221 18.3486 10.6591C18.6195 10.8961 18.7549 11.2092 18.7549 11.5986C18.7549 11.9879 18.6195 12.3053 18.3486 12.5507C18.0863 12.7877 17.6419 12.9062 17.0156 12.9062C9.63742 12.9062 13.3678 12.9062 6.97363 12.9062Z" stroke="none"/> +</symbol> + +<symbol id="edit" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3745 2.62547C17.72 2.28003 18.3461 2.34613 18.7731 2.7731L20.3193 4.3193C20.7463 4.74627 20.8124 5.37243 20.4669 5.71787L18.6115 7.57331L15.5191 4.48091L17.3745 2.62547ZM17.993 8.1918L14.9006 5.0994L5.62344 14.3766L8.71584 17.469L17.993 8.1918ZM8.09736 18.0874L5.00496 14.995L4.74469 15.2553C4.60266 15.3973 4.52461 15.5949 4.52337 15.8155L3.77535 18.7134C3.7736 19.0246 4.0678 19.3188 4.37904 19.3171L7.27695 18.569C7.49751 18.5678 7.69506 18.4897 7.83709 18.3477L8.09736 18.0874Z" stroke="none"/> +</symbol> <symbol id="liste" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"> <rect x="10" y="9" width="16" height="2" rx="1" /> @@ -73,7 +80,12 @@ </symbol> <symbol id ="email" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4H19.5C20.3284 4 21 4.67157 21 5.5V16.0714C21 16.8998 20.3284 17.5714 19.5 17.5714H3.5C2.67157 17.5714 2 16.8998 2 16.0714V5.5C2 4.67157 2.67157 4 3.5 4ZM2.91716 6.02444C3.04832 5.78143 3.35163 5.69075 3.59464 5.8219L11.2431 9.94966C11.5474 10.1138 11.9148 10.1093 12.2149 9.93753L19.3945 5.82797C19.6341 5.69079 19.9396 5.77387 20.0768 6.01353C20.214 6.25318 20.1309 6.55867 19.8913 6.69585L12.7116 10.8054C12.1116 11.1489 11.3767 11.1581 10.7682 10.8297L3.11971 6.70192C2.8767 6.57077 2.78602 6.26745 2.91716 6.02444Z" fill="#333333"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4H19.5C20.3284 4 21 4.67157 21 5.5V16.0714C21 16.8998 20.3284 17.5714 19.5 17.5714H3.5C2.67157 17.5714 2 16.8998 2 16.0714V5.5C2 4.67157 2.67157 4 3.5 4ZM2.91716 6.02444C3.04832 5.78143 3.35163 5.69075 3.59464 5.8219L11.2431 9.94966C11.5474 10.1138 11.9148 10.1093 12.2149 9.93753L19.3945 5.82797C19.6341 5.69079 19.9396 5.77387 20.0768 6.01353C20.214 6.25318 20.1309 6.55867 19.8913 6.69585L12.7116 10.8054C12.1116 11.1489 11.3767 11.1581 10.7682 10.8297L3.11971 6.70192C2.8767 6.57077 2.78602 6.26745 2.91716 6.02444Z" stroke="none"/> +</symbol> + +<symbol id="password" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M4.89603 10C4.40117 10 4.00001 10.4012 4.00001 10.896L4 18.2042C4 18.699 4.40116 19.1002 4.89602 19.1002H17.1043C17.5992 19.1002 18.0003 18.699 18.0003 18.2042L18.0003 10.896C18.0003 10.4012 17.5992 10 17.1043 10H4.89603ZM12.3442 13.4441C12.3442 13.9365 12.0794 14.367 11.6845 14.6011L12.3442 17.0002H9.65611L10.3158 14.6011C9.92088 14.367 9.65611 13.9365 9.65611 13.4441C9.65611 12.7018 10.2579 12.1 11.0001 12.1C11.7424 12.1 12.3442 12.7018 12.3442 13.4441Z" stroke="none"/> +<path d="M13.8017 10.0002V7.90011C13.8017 6.35368 12.5481 5.10005 11.0017 5.10005C9.45524 5.10005 8.20161 6.35368 8.20161 7.90011V10.0002H6.10156V7.90011C6.10156 5.19386 8.29542 3 11.0017 3C13.7079 3 15.9018 5.19385 15.9018 7.90011V10.0002H13.8017Z" stroke="none"/> </symbol> <symbol id="pass" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> @@ -237,4 +249,20 @@ <path d="M15.227 14.2886C14.1412 13.2028 12.6412 12.5312 10.9844 12.5312C9.32752 12.5312 7.82752 13.2028 6.74173 14.2886L8.15595 15.7028C8.8798 14.979 9.8798 14.5312 10.9844 14.5312C12.0889 14.5312 13.0889 14.979 13.8128 15.7028L15.227 14.2886Z" stroke="none"/> <path d="M12.3986 17.117C12.0367 16.7551 11.5367 16.5312 10.9844 16.5312C10.4321 16.5312 9.93209 16.7551 9.57016 17.117L10.9844 18.5312L12.3986 17.117Z" stroke="none"/> </symbol> + +<symbol id="moreOpts" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M11 7.66675C12.1 7.66675 13 6.76675 13 5.66675C13 4.56675 12.1 3.66675 11 3.66675C9.9 3.66675 9 4.56675 9 5.66675C9 6.76675 9.9 7.66675 11 7.66675ZM11 9.66675C9.9 9.66675 9 10.5667 9 11.6667C9 12.7667 9.9 13.6667 11 13.6667C12.1 13.6667 13 12.7667 13 11.6667C13 10.5667 12.1 9.66675 11 9.66675ZM9 17.6667C9 16.5667 9.9 15.6667 11 15.6667C12.1 15.6667 13 16.5667 13 17.6667C13 18.7667 12.1 19.6667 11 19.6667C9.9 19.6667 9 18.7667 9 17.6667Z" stroke="none"/> +</symbol> + +<symbol id="camera" width="100" height="113" viewBox="0 0 100 113" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect width="100" height="113" rx="12" fill="#F8F8F8"/> +<path d="M27 11H15C13.8954 11 13 11.8954 13 13V25" stroke="#BDBDBD" stroke-width="2" stroke-linecap="round"/> +<path d="M27 100H15C13.8954 100 13 99.1046 13 98V86" stroke="#BDBDBD" stroke-width="2" stroke-linecap="round"/> +<path d="M73 11H85C86.1046 11 87 11.8954 87 13V25" stroke="#BDBDBD" stroke-width="2" stroke-linecap="round"/> +<path d="M73 100H85C86.1046 100 87 99.1046 87 98V86" stroke="#BDBDBD" stroke-width="2" stroke-linecap="round"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M61.1175 39.1429H72.7941C75.6692 39.1429 78 41.5002 78 44.4082V70.7347C78 73.6426 75.6692 76 72.7941 76H24.2059C21.3308 76 19 73.6426 19 70.7347V44.4082C19 41.5002 21.3308 39.1429 24.2059 39.1429H36.7502C37.8558 35.5825 41.1444 33 45.0294 33H52.8382C56.7232 33 60.0119 35.5825 61.1175 39.1429ZM49.5 70C56.9558 70 63 63.9558 63 56.5C63 49.0442 56.9558 43 49.5 43C42.0442 43 36 49.0442 36 56.5C36 63.9558 42.0442 70 49.5 70Z" fill="#C4C4C4"/> +<circle cx="49.5" cy="56.5" r="10.5" fill="#BDBDBD"/> +</symbol> + + </svg> diff --git a/src/assets/scss/_shapes.scss b/src/assets/scss/_shapes.scss index 9eb400742..a5497d14f 100644 --- a/src/assets/scss/_shapes.scss +++ b/src/assets/scss/_shapes.scss @@ -33,17 +33,17 @@ $mat-tab-shadow: 0px 2px 7px rgba(0, 0, 0, 0.25); background-position-y: 12px; } -@mixin background-hash { +@mixin background-hash($color) { background: linear-gradient( -45deg, - $grey-2 2.5%, + $color 2.5%, $white 2.5%, $white 47.5%, - $grey-2 47.5%, - $grey-2 52.5%, + $color 47.5%, + $color 52.5%, $white 52.5%, $white 97.5%, - $grey-2 97.5% + $color 97.5% ); background-size: 5px 5px; background-position: 25px 25px; diff --git a/src/styles.scss b/src/styles.scss index c8e77507a..b49b24295 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -228,7 +228,7 @@ button { max-width: 560px; margin: auto; border-radius: 6px; - @include background-hash; + @include background-hash($grey-2); border: 1px solid $grey-4; position: absolute; top: 50%; -- GitLab From e886673882248a5884f5ff57229463257b23b8ab Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Mon, 22 Feb 2021 18:39:43 +0100 Subject: [PATCH 46/72] fix: regex issue on phone number and text without number --- src/app/utils/CustomRegExp.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/app/utils/CustomRegExp.ts b/src/app/utils/CustomRegExp.ts index 94ead16e4..e268d8229 100644 --- a/src/app/utils/CustomRegExp.ts +++ b/src/app/utils/CustomRegExp.ts @@ -7,8 +7,11 @@ export class CustomRegExp { * Validate an email */ public static readonly EMAIL: RegExp = /^[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}/; - public static readonly TEXT_WITHOUT_NUMBER: RegExp = /^[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}/; - public static readonly PHONE: RegExp = /^([0-9]{2} ){4}[0-9]{2}/; + public static readonly TEXT_WITHOUT_NUMBER: RegExp = /^[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}$/; + /** + * Validate a password (at least 8 characters, 1 uppercase letter, 1 lowercase letter, 1 number, and 1 special character) + */ + public static readonly PHONE: RegExp = /^(?:(?:\+|00)|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/; public static readonly WEBSITE: RegExp = /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/; public static readonly LINKEDIN: string = '(linkedin.com/in/.{1,})'; public static readonly FACEBOOK: string = '(facebook.com/.{1,})'; -- GitLab From 34e3aebd188f3984dcd5bfb0028bfd20793924da Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 23 Feb 2021 10:56:45 +0100 Subject: [PATCH 47/72] feat(about): add europe and region logo + update style --- src/app/about/about.component.html | 55 ++++++++++++++---------- src/app/about/about.component.scss | 12 ++++++ src/assets/form/sprite.svg | 10 +++-- src/assets/img/about_illustration.png | Bin 74099 -> 0 bytes src/assets/img/about_illustration_1.jpg | Bin 0 -> 45744 bytes src/assets/img/about_illustration_2.jpg | Bin 0 -> 59980 bytes src/assets/logos/logo_europe.png | Bin 0 -> 21823 bytes src/assets/logos/logo_region.png | Bin 0 -> 18352 bytes 8 files changed, 51 insertions(+), 26 deletions(-) delete mode 100644 src/assets/img/about_illustration.png create mode 100644 src/assets/img/about_illustration_1.jpg create mode 100644 src/assets/img/about_illustration_2.jpg create mode 100644 src/assets/logos/logo_europe.png create mode 100644 src/assets/logos/logo_region.png diff --git a/src/app/about/about.component.html b/src/app/about/about.component.html index 0b50d93e3..fcbc5f03c 100644 --- a/src/app/about/about.component.html +++ b/src/app/about/about.component.html @@ -1,26 +1,37 @@ <div fxLayout="column" class="content-container full-screen"> <div class="section-container"> - <h1>Qui sommes-nous ?</h1> - <p> - La numérisation accélérée des différents services privés et publics ainsi que la crise sanitaire que nous - traversons a renforcé une fracture numérique déjà forte pour un nombre important de citoyens. - </p> - <p> - Au printemps 2019, la Métropole de Lyon s'est saisie des enjeux autour de l'inclusion numérique en initiant la - structuration d'un réseau des acteurs de la médiation numérique sur son territoire. Son objectif est de mettre en - relation les acteurs qui œuvrent au quotidien pour limiter cette fracture numérique, nombreux sur le territoire de - la Métropole : associations, centres sociaux, structures informations jeunesses, grands opérateurs de services - publics, collectivités... - </p> - <p> - Des ateliers de travail ont été organisés en 2019 pour identifier les besoins de ces acteurs et 9 offres de - services ont été identifiées : - </p> - <img src="/assets/img/about_illustration.png" alt="illustration des besoins" /> - <p> - Cet espace vise à centraliser et mettre en commun les ressources développées dans le cadre du réseau par ses - acteurs. - </p> - <p>N'hésitez pas à contribuer à cet espace en partageant vos ressources</p> + <div fxLayout="row" fxLayout.lt-sm="column" fxLayoutAlign="left" fxLayoutGap="5%"> + <div class="about-container"> + <h1>Qui sommes-nous ?</h1> + <p> + La numérisation accélérée des différents services privés et publics ainsi que la crise sanitaire que nous + traversons a renforcé une fracture numérique déjà forte pour un nombre important de citoyens. + </p> + <p> + Au printemps 2019, la Métropole de Lyon s'est saisie des enjeux autour de l'inclusion numérique en initiant la + structuration d'un réseau des acteurs de la médiation numérique sur son territoire. Son objectif est de mettre + en relation les acteurs qui œuvrent au quotidien pour limiter cette fracture numérique, nombreux sur le + territoire de la Métropole : associations, centres sociaux, structures informations jeunesses, grands + opérateurs de services publics, collectivités... + </p> + <p> + Des ateliers de travail ont été organisés en 2019 pour identifier les besoins de ces acteurs et 9 offres de + services ont été identifiées : + </p> + <h2>Recenser et partager des ressources existantes (optimisation)</h2> + <img src="/assets/img/about_illustration_1.jpg" alt="illustration des besoins" /> + <h2>Co-construire de nouvelles ressources (développement)</h2> + <img src="/assets/img/about_illustration_2.jpg" alt="illustration des besoins" /> + <p> + Cet espace vise à centraliser et mettre en commun les ressources développées dans le cadre du réseau par ses + acteurs. + </p> + <p>N'hésitez pas à contribuer à cet espace en partageant vos ressources</p> + </div> + <div fxLayout="column" fxLayoutAlign="center center" fxLayoutGap="20px"> + <img src="/assets/logos/logo_europe.png" alt="logo de l'union européenne" /> + <img src="/assets/logos/logo_region.png" alt="logo de la région Auverge-Rhône-Alpes" /> + </div> + </div> </div> </div> diff --git a/src/app/about/about.component.scss b/src/app/about/about.component.scss index e077716b4..08f1b70f1 100644 --- a/src/app/about/about.component.scss +++ b/src/app/about/about.component.scss @@ -1,3 +1,15 @@ +@import '../../assets/scss/color'; +@import '../../assets/scss/typography'; + .content-container { overflow: auto; } + +h2 { + @include cn-bold-16; + color: $red-default; +} + +.about-container { + max-width: 760px; +} diff --git a/src/assets/form/sprite.svg b/src/assets/form/sprite.svg index 37f14d8e9..1008b4e01 100644 --- a/src/assets/form/sprite.svg +++ b/src/assets/form/sprite.svg @@ -302,13 +302,15 @@ <g opacity="0.35" filter="url(#filter0_f)"> <rect x="74.145" y="49.134" width="94.7677" height="60.8093" transform="rotate(-19.503 74.145 49.134)" fill="#348899"/> </g> -<path d="M72.012 33.98C69.9322 34.3896 68.691 36.5394 69.3763 38.5454L95.4069 114.744C96.0855 116.73 98.3483 117.677 100.239 116.765L182.813 76.9455C184.422 76.1698 185.188 74.3077 184.592 72.6244L165.466 18.6229C164.886 16.9861 163.194 16.0218 161.49 16.3573L72.012 33.98Z" fill="#C9ECF3" stroke="#83B6C1" stroke-width="3"/> -<path d="M72.2571 38.0604C70.0496 37.0427 70.4459 33.7921 72.8333 33.3347L161.294 16.3859C163.287 16.0041 164.88 18.0294 164.04 19.8761M72.2571 38.0604L164.04 19.8761M72.2571 38.0604L132.625 65.8898C137.408 68.0943 143.071 65.9885 145.25 61.195L164.04 19.8761M72.2571 38.0604L164.04 19.8761" fill="#EAF8FB" stroke="#83B6C1" stroke-width="3"/> +<path d="M70.7961 38.0606C70.4045 36.9143 71.1137 35.6858 72.3022 35.4517L161.781 17.8291C162.754 17.6374 163.721 18.1884 164.052 19.1237L183.178 73.1252C183.519 74.0871 183.081 75.1512 182.162 75.5944L99.588 115.414C98.5074 115.935 97.2145 115.394 96.8267 114.259L70.7961 38.0606Z" fill="#C9ECF3"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M71.7226 32.5083L161.201 14.8857C163.635 14.4063 166.052 15.7839 166.88 18.1221L186.006 72.1237C186.858 74.5283 185.763 77.1886 183.465 78.2967L100.891 118.116C98.1898 119.419 94.9573 118.067 93.9878 115.229L67.9572 39.0304C66.9782 36.1647 68.7513 33.0935 71.7226 32.5083ZM72.3023 35.4517C71.1138 35.6858 70.4045 36.9143 70.7961 38.0606L96.8267 114.259C97.2145 115.394 98.5075 115.935 99.588 115.414L182.162 75.5944C183.081 75.1512 183.519 74.0871 183.178 73.1252L164.052 19.1237C163.721 18.1884 162.754 17.6374 161.781 17.8291L72.3023 35.4517Z" fill="#83B6C1"/> +<path d="M72.8854 36.6982C72.0024 36.2911 72.1609 34.9909 73.1159 34.8079L161.577 17.8591C162.374 17.7064 163.011 18.5165 162.675 19.2552L143.885 60.5741C142.05 64.6107 137.281 66.384 133.254 64.5276L72.8854 36.6982Z" fill="#EAF8FB"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M72.8854 36.6982C72.0024 36.2911 72.1609 34.9909 73.1159 34.8079L161.577 17.8591C162.374 17.7064 163.011 18.5165 162.675 19.2552L143.885 60.5741C142.05 64.6107 137.281 66.384 133.254 64.5276L72.8854 36.6982ZM72.5513 31.8615L161.012 14.9127C164.2 14.3019 166.75 17.5423 165.406 20.4971L146.616 61.816C144.092 67.3663 137.535 69.8047 131.998 67.252L71.6294 39.4227C68.0974 37.7944 68.7316 32.5934 72.5513 31.8615Z" fill="#83B6C1"/> <path d="M63.4997 63L2.12107 78.1767" stroke="#DC2A59" stroke-width="3" stroke-linecap="round"/> <path d="M77.0343 97L36.1785 116.5" stroke="#DC2A59" stroke-width="3" stroke-linecap="round"/> <path d="M70.0344 80L48.5 88" stroke="#DC2A59" stroke-width="3" stroke-linecap="round"/> <defs> -<filter id="filter0_f" x="62.145" y="5.49524" width="133.632" height="112.959" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<filter id="filter0_f" x="62.145" y="5.49527" width="133.632" height="112.959" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> <feFlood flood-opacity="0" result="BackgroundImageFix"/> <feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> <feGaussianBlur stdDeviation="6" result="effect1_foregroundBlur"/> @@ -341,4 +343,4 @@ <path d="M8 13.8182L11.8889 17L18 10" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> </symbol> -</svg> \ No newline at end of file +</svg> diff --git a/src/assets/img/about_illustration.png b/src/assets/img/about_illustration.png deleted file mode 100644 index 6e9cfe47aa986fbfa52afa5308c2d0e92b532be3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 74099 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�cEgs%5hOb3Snl1^Yw*L^>oxHqjdR{<S<jj(a;HT|uD{o4=Yp)73&HY!hYE#<U zOV5StLw0+&t-9x1ttZMhr6N>pU$^Dv&u1U6&^fZXdUy6B{Rmanqs#v6xYTf0MS4>B zqtB9y#9sX^J*{<B=k8Wl;~BM06C5|jUR<28Kb<dw-|qU2Ak8Dv8DYYG`(H<io{wD7 z8}D;@$?W-IcQR|Q{(Zst>&vsiSEf7mKVthHcF3^V<Zqiguf!8UPqn8Lv<`K2yC=5B zZIpW0_TYj5tJt)Y&lT+^o)&hgsO3tvnYl5JuPlRebCCH`+b9+trgkeP(THR5H)7mR zMeXqF{Z(q%Dxw>)@J80huEOtKKSg839t3NeHv5>YUU<-a-g^K1TBrMcRiVdiYTL6) zGM8x<_W9W?oZsaCQr|azf!|pv?~fNhZCkTYmwVa@&e_+@m%e^puD;@Hr2Fc?-Opxy ze``~kn(ukq>Xu>kM62@r^gYb3jCQTbe=hb;P@SJ_qhRw(e#*@CFZz0~ersBM<y*I8 z)&F<tQ?~1dmfkkKI;r#i>gz|{y=$$rUw_%T>gvL!p7PH=o_Os1`1AAa`PwEPTW6aF zeOo2MyYIV@rbfum)z`E(RkNRb9vTxdBkReOsrgxaU%lBSKYQ%FeQD|YXP$d+WgIQP zEPD3Xw+jhte?0Im^<1@D?E1XCZ<<qCEV~XnY<zFCBIM!j6D<3kot&QV9#|TrmMWTk z?1ui8y|K}zJKva`-Tl?_DNCiOM*p6-ud~{NS6rR%6WHC|AAahHCf7OLu$q<OoxReh zge4dKvc0w9+uG+R%+q%=O%h(}cJfrr)F8i<n*CCW+p||+U%j%l)%Db$7tyLQ1#7nX z{kW&|SoN2T>-L-_@=3N*SJrP9`uNW`e%Z9ItGr7epH=(uLv5?b=VNOxJ=uAy>~wHf zf^!9{kb8Hx;)LBk6`#w_es%Wg+%^9~aPE2gs9C#Qvclq5iLJeK_4d>&D_2g^lhLXD z)$L~gp=Xm>ZeY&gSw&Am-@5CCzTH!EcGrixr#exq4Zh`>`CR&>Ul;QJ@~@ds??}e~ zTQI?@lWWbq6&J2bPkpW(bYPXp%Bm)_^AT%;u9XWa^)G((LSf_e%PkC=3u}c__2Sjn z-c)OCdv!c;{mQ2wYp-m}<?ikN<FtHE#gvkg^3*eP*4ydMl5m<5H)XBVU#k`OmO3wa zuAX)0wS3m^XSu6(cOOwzQ_<YwSh_OK^lRO#u*|P+&MTOs%?<dieWbt6a(bn<%0w;1 zTdvAIw`$9@NlKzmK1)9G`|4y=6uQ_`_o_v1gGuhndpqAL=HB1s>v?dlh^*kcfR6Nq z;hFL^%NLxLd}Z{^@9mteK9-G3SM6S1>*{DXwV>|@yT?L1_j$3OP8_yq-7tNTk*msa z?G<s8oK~s7+>o9(`C5Khoae+vx6BRs|EaXJuDT|C-CH_%YSF{JxAtUi*7EzgNGjy| zw$j<B3wik`ZVy{iwkB%*tgVyJHnXe_Ula1X+b^V}=2TL)Yt_OKW0#++uW9cqE7i1e zSy*wm`P9>?&Y2FD(Wm*ou2gVo+Lq2@dFsYj9-&rM(O**{-UK&X5A^h0Iq~9qxms7| z`THWzR&5R696#;#>*ev2qqc^X`D_qslg)lL<M+M(Q;#d#9r~YGaFuCrOHK?@+WYF& z#H48R3tf?)uPzEsF3x`U>FKJg`!+H$F>f(`biMGXiqVYrCYz@$>lF0;A6^wM+rCsr z<oA8>yo2D6-yShleV4-52mg=OH>p@Rb7w%I#>&`Jm*t<_G0?OYN{-pL-=o=T#mPg; zrkRhPXZ?7Xo>h4+;Humu=g0G_e2@C?Su^Q~hpFYKg0K4@DwXbi$m3h_#5LFURg&v3 zrjSbtfqN^Jax?TTH>bV4tNkvh<NV@9Hw?D!{-d(h{%eFNOSNdn=~IW}W;iTuovx{J zE3msMsrKKO)3^2|ER~t>|4_B>(QWgPxps4xZV0&gBXO7A-oKfmCswbKnwV98Y2vHG zkC|TGpEZp4+~IzFU+>)SxRP0F_r9!%vwvG<oXF?dbM?Z*>l&+^iz}n9TsY%tyyf$y zvr^xVJ+3~pBJ$tYmbT1Cr}MSHe_PAd+k5T6Z2bwZj;$=)z3f-&DVe`U6*Fe3U%7BD zV`YK+v_siyTDp5euI|}gFhwMCeH+)R)>nrgo?mf(>he{A*Ta52T|P7F(sQM&>%zUQ zA2UzgpSya}?$VIO2k&luG_^D8&!>v5xfdcN3lE;+;|@vMoRwAm&hS^-n{=(2KV~{5 zDTS`C<>r+tT>Qx<wXH$ix-U}gOMd;=#=BDtD%$qdd}eOl_oXlE%DS~vBIcL1{1Caj zYTK+ib`HYF{X&kcPJYVC9ac2IOse3xXx00b&qHmhdk=M;5|I&Dvf<j+sRd^e|3%LJ zeCGngQnxork8Yea<GqsR5nuc37CTqXC(}yd#ZyXL#CdnUs_~WkI_Xk)PC%rce<GXq z&SK-M?=Qw_tN9!L)}H%l(x>Y}$J}2W>DHfiXz9Q0bJy#?tB86(zhLQ}B9S0#Pv`f0 ze7bmpzpl%DvQ}{INB%tvmNXQoUR%4a_n31_`|0JrfvH(Be^$5ecztzw)~Bt?U%%Zn zm&^|c32G8jV)L|}rCsF_KDj^t$L3T0JR$wY{lT9n_g{I!J}>;@t=uk6jo^fY2}-+7 z%xB*zXBL!S+BbdvqWSXmu`B1E4+>0~;C1WjRm=W6tERrMU0Aw0{N<PB?}DmUgd4A~ zifQ!Uw{jYP{K_Vdl?~z1B4RI(i{HC+EM81&!w#Rm^MPBJZN2(vg<P7L;1!if`?Ir` zwqC!p_;H`iilv*=vd?ISuZ&BXGv|p$?%j#&c2&G|I{k#(;h&;L<1N9cRRwn<`a*V> zm7eY4zVurB{<>hf{gJin?P4dp_g#GEm%hTpCgSMX$)I6`jHfO6CT|bxpPjUK`<c}* zenga7Ki_fJx70sN=ANUm`u`<;I^Jy6SN?Vdn|^$0ekJc;QSQB6v5LHtMX#@2y2Z>@ zRq)`iZ|4HL-d+kh{apWgWjMcavd%(B-r8SJRb+pAD_4dryL6!_e46={#g`U*DvVhn zB-;As(~nA-Tz=g^$A<8oa`TNYgq8}2?5^9ScIVfMsOzg&vr3z<I{VVy>+I{<%U`G8 z`?jT~Gmz`{j^Jn}?y0lquYNJ>;;nZgnfI!NuV!9gSnAF$b?E-bGs&iPM>a=IpS<db z%=%56im!g&boos7%%En)IlH6eB^JocIoHO)mUVg6)~*+~PP87A15MogXJC>${zmKn z>*%Oyv)4DVt`@z#xq0T?=Vzl=uE_crboc(d|5`V9`2L(7Bzk{s?9Xh06>)JB&GaG` z9azC}_2q?AZ&rTRja&I=PE6|+kD!dpRlDO<6@CU8hi*v>{;5_I$N1o#xAr%!<cCG9 zqEj`t<>{_W{P}OmmZhsj!$MZgIJcHZAkujH32EtNE;b6OaiY4h%MTr@Im*JOy&=@j z!euFJEkLkl(e+JgZOQ+NQpKNXZ;JC}eS3>}+SaUQu3+QP%Uix)pZPg&it*<yrAM`# zi-cyIO<Z+!%KcNPf<HD*)#~XCiCHeTdvWon)4u+vwkLI0?W@}U>3Uv3N>G!bfLc)E z%Cd8jO8yla9?dcjKX+*<*P3YWiB~62u}t5%=y9^hojniN|6F@>&7QNjMUSqI6`B-c zP`<E<iCOg0rkO_5^uu%Z-s0-&4vWm1R<<Nf%R<4y(=~2y<!2Axy`P1S%HLmYv)**} z)NQG|m6DW2uP$3^sCU%bNOVWW-X<$E?fS2$*Dswm|D)ODpWUKCF{?g2^PRe@W@*xF zvw(=TD#=eyhb>>BQM>$*va;xnU4fP-zk9ZJhp#Zu_E&tkuq*4TPuH!lT5S9Oa9_^} z$~;we^vm0zcP}5v`5jlyKINI)Tk(;LbC0vZmP4u9XNzhpQd$m0nQGM^WzF2SL_7Iy zRV9;+SH_hCIVbMN`ZR`Ha*J`Ydz74hR&5tKch=H1qS+$Tr=)z#&%L?Fb?fe}Q@y;l zrXDo9x+2uMkvHq@IWNhbT2Zr=;xkt&MVaW;x3ZR>uK9E}dfk^lOh>P*U%J6<cU<Me zubIF1q;j3o4vGm`(o}JmeLd$&iJ(hUcUR4|2^8$gx_-rG=Z@TWT(07kqE|Ps?Todm z?cCJ7r|b2#xJvJ^l{=P%T(zG1{_E-JEpDonm7iGHwDzTL{uCZHLCe-%^7Qo+(*@RM zt)1=VeN9_e+xet!l-KQTnY)9RNxhBIDE=AMDk`SEDEMc^srfs79497n<z7DVX8V;% zytg(QJAago($FxA^70Hm*rr>g<Rp5sPCu^G`TE+s>(*=u`B`J9YMQH~`d&)?*}HZ1 zeP8~5cz*Tgmt+xdd7&WVshjeem6VmI+*syXS-<|)3eN9)7fg1GS)FTrIb?Gpb7y$D zKv(T8yZVVr*(#b=8ZNFoTe-3$<14wtv^HhdR#q^iS1;?jv{}SWx`O%apQl~fQT|IK ze`@LK1d04QuKx5)yrTMkhkaEiyYe?=`)8Ex<9fb+lDS<>pzf?0>hqJMOhY^}R&Y&Q z8=e-}n6)))Rc?3KDzkev+Y(crWnP_iwd*sB#!lzkpPs&aB(zjFcJY$3vmyc_DsN8& zottKIbW46(%A3FJ^RLYmpRavY|9bYaMc?MT9#AUqQ2wxe;tpr2qx%;YPR_3n`nlTt zb<0NoDf>6S($_Q2zg*_K|52f;DBCI>n}C&()>DPwUA+0;PE+;w-V1N^*R`L$r}Uwk z<B4v>Liy!BmkTSdX+7MsL_cb)W4_&|J$ls-AKKm*{ZT#hzxTW6XJXe$7C(Ej;`F({ z)34kJyR4KT_`_IWgUY+}4j=PRD3`vH`MK$H$mbdRjPe%jU+9y2Z{?Rv?vR+MH4jdB zsD6L<>$UfU|9ziwCB+*5x+v71OHzHv`>p=v_WE0Mm~>}ahUfm;Z1QHE`!Dw&<@3|e zL@jc;H&s0NU)<UH^6Z?qpz`jo>w!r-{<@wzwi&S!=zvl{tH6!ZkDNtUEnM4$v=&E< zvHL(i$Z_TqZg8Wl-dSbGH0?;}U%O4FTaeB9AkCuF`I}eeTNAP=jjQ?(?0;mqwyB9p zH*QaaaoU*xPtQ&vA)zaq)BQ~|FS$6jKbV{r8lRKHi?q7v$8y0HpZu5m&9{qP?A{-C zdYbOljmhpuj~&x8HV(dD_j_$uS69jBv*xQEo7w)G^lhs_T}GtM6gulj?Z2PTAHAPf z{VwqBt=;#c_H)bGnKYKa`}h0(^%E17ukI*(ykFEJdIpMP>xCM%ydMPx1(iIXTmEWl zcwC?h%lrlFY;taG@hrbzyFIS%XKHKfO?}RgzDavQgH939rB4gn9H!Q8czCz`{?)1B zaXV)psN5S@`~7Zt)yJdarp3?B-2EG{Jui0i8LQk|Tkc+e(}`lMvuvZ5=OZs~@2!c4 z*|z52uQUIbzxV63Wqxy4ndRPkahTu!%JTVjyMEuB=q|T1f8Wov@A=h9$SbTK1h_Bo zvYUIVnOkS!Fa5=JACHQ+?O|nOTjku&x9jV*Xw#Ax0hjGdUtI~z%Uk#1TH$>*l<;b| zVhX(^93B>y7AO1YV@26xc}dBYd-m9Tw$|14J$Ufo?)Qa$F>+-aP!^FLsAmMZS|MkT z9gETPHgEmCA>uIwjqi57*1KDFJ9pQcO{ecfpE`Nckl*x=WSqsc9De&h1-kM3YRdoB zU-NWxYpcApt@``BSJ&3gwmkm2!XWYKsi{@3RxW??`6r*+&-wGxK0iDA>h|{a5#QG7 zT!?=@uX^3bX<=pc@z*mRf4v^R{{8;{b@3PfO{zYj_}d`z(fU;n&zj%A;%k0)%kHX* zrSVlSm)agX|892uBhSRdg?-lVES4@?mi6YwMk%|R9VcrKHnINtbXxy=q|p01n{PLg zw<aI&t376YVfLHey^p%I|9#pf@3HsV`uP3lw`~$-`MC|^a~Tb$&?3vJ<p)0o-Jfe+ zzA9>K)-vBccc!O?oM7TN{-M8BFM6Ag&dOia@AyPGIu5Og++6aoRXpqVw%kJ}{>)7L z{q3#QuNRBArkoVIthT4E-{w=tGM|}Aaf^jyWosRd&Mmzb+11r`Y3ud4YKNEqR-HI{ z^k~$sl9zG4pBfmMede>V$o$yDxW=I6=FOY+p<lnx(r0=4@woi;4T*=pNj;yxr*Y1| zFPHt(oE}_bUZXItzI5)*eH)UFa$R1pKCfa?pZ&jz;MVecmB;r?_&p=JuW<F3*~*X0 z|NQvK#moEm>|Xn#M$WstN?)&<68>-R_j|&V1Hz|==G;Hf$b5Bu{Qj=#Z}Y(^=EH6d z7QXW3=l4s0ec#O>`M7_1;K#0=iYh8Do}Ql7I!hlZRz91V-ZNd#&hB1s)q&&v_WvT( z=htlVWN&Oab9Ghd>xR!aqnq00syyb`eCl-Tl~SEz^@O|GxcXbp*H>3h3tVpH7Jqe+ zUH*!6{+>j+HxF2kKYaL5?H{-Po&{G|hj%~TQ~CMJ*X!}plTI@$G_&)Eg@sLvtA1<h zY*FE8RJuNH@2-`6Pi;OP5#DX<b>`3hU$3;kzPx<A!bh(9jp0WxahvKr`TUo@d^pTM z`}($ku*=JQMa9LJ$JhOIRd(w!xM%-KxxbB9TFcV%=9SblGYqrt?%JxjY|do=)nRLW zWDkfJ@yu*CNIsz1Jl*W`Q{G=wpMF}se%~ztvrEnucmF+46!Wl|k+7uFUcus`*}9KU z`8GJriLd`#8XR_eTkiJ>qFS3uUxzg-8X0F^Qb~@ODI%cY{QX|_`bv*Bn~xly`<y;3 zc~>Fz`SWMqY3FUf&snMdQ9<R4W=q?*%XYsE)zsW3gq=IcmoHj-c4D^uzaNWVE}vzZ zJ?qomdLOS3OB}l<itlL@P}_4M;oeejapwn_)}I`9&s1qJo$+|;;`=+!b8o6-<=@1r z=Ppy&61FxfwcrtF!7=}CG2OE3!a*H2A07y7a=5?i#+~x}wX-Y=7gatq`+Lq};k=3y z6O)f=UteK7FDGS!&As~nwZ(6n`RxpT1p1#^I`51AOAY>+Wqap6;3%{I^P%~r@RRm% z4n=KI_3Q_IHZ@-^x~KWSnPr-NM5TRC27f8*;oJH9?<TnH-@%w~zr(1m;=W3Cer2Ua za(kiE++AgFvlca*3UhusT<mtw=g0>eCFd>DGhL>=zP>(xPvZkud*$*6j`7Z$=GXno zRQ`QJp(^)K3+FR!lPCQD9-KPGwBC<NS#3toOgY=C8N2rUdbK*u>Gqz=;?JLok1f`o z{Gj_Dxa_j{B4|;t{lxti&N%HUpWo|dFZv^Gnw9eJpT|`N0SQr&U-M-RL@MqC>=2Ky z*{CQcp~Ay(+Fm1V%6G~1ISVa|pGhzupJceC>wtH!VusG_#1KO^jf#H;9y-~MR)*IE z`sdgGi#+qr@QK9#)$4Yhn!wg0f9$ZTz@5VjChN0tJ!Te6v&_1)qwv^{1PKp*%LCkA zcV4gCeNG}f?#P+Lssb|R2{k8#54Wjz6f?a|w-jmm{OxwW^kM#!Gwkd4wed(Mb#SmS zD0gjqDtGT{m-myGFJG#~&x?7MEi1rKprrek{UM7rha}qtdx7V(vey|-S4eM{Et_HZ zO~BZLgXPnk&F9Vh9UfaYy38rNmDzTniSyXB>-QR1i=3=3UDz++cS5|;>=L_x_LHuv z4=XLEP2PEKl5uHDq%0H5!4Ld8hL_Y*Sa>Sql~@`#DcVoq{nb~O@N&wo0P{yqX^I9F zwI^opQn3E=*Ht_=<yFbQpU<U}9PaLXE|m9*t<(1Fm0;hye)fM$8qJNW4)dD7IAeU? zB=^BWrGoGL+1J;ZZsPvQbp7Ao-_k)&(Te$3rtWu_ue~xM{T8F5YuQA>CzI^<{`qv; zw|YYCo&^(BZ#mWn=%rk-S5Uvx{-C&I`sbI*9sDfQ9pnuYSG+jO>i$Hise6v|u@~6| z$(s&TlrS4Ts`{z$NWiDS@!A2de5tqlo=%JQ+3ygiSh{8QM-S;0{4Em;Srt`ft7~Rz z1WcFC-*YjcPsjO|NB{KMo?`Q5OPC*X-^mdFTAF%kE=S>AP@VbUg8Kq5z2!{S$7bI& zmD3i<Nj=?rCS&Hoq?Ugf2P%@cC~DT19Z*QfiTL;B|26H+spZFb6dN8SYIL68{UN5= zu{Pkt7GZrJF6m7y4-d_5bxh?KvY1<Z&hl8m_jh-1OZcRHoviL})-^BHaSywI?0<(! zf4iScBHtb5c(Atd#lwjVk9(!frIH<`9!*j|)?O58ba-|{cs~Q<kNf}srQ4WoVO6@f zCUW!Lqk-Sk=ht3)!+9jQiSh7(i~SObOi$SKC)$VQu~oP$l)KB8PEk~wV8mn3CUK~- zvE@L*nZNRNincE7Oq|zru3i>9cd+Fo!&h08j0qpbCW$pMvdwWmpqQD!*mpwxh~&=1 z`bYbmdZkQxB$vcVly!)!wEaz~sMoIcG<~|O;(=(>f{WkP=M*%3G)O$4_?_S8L&HjL z7QvvirZ%qYu5;NaOc1c%SLJlS<lUW{SEQ5JKen^P6`fRF#wlPOchk8c;PUYaBDcDZ zPiMHNGjr4W%}EE6y{(0Xh0ji`+IXcarcpTJlD{bPj`<~*Jf9iO(=Sky|JHNKy&_aG z@XNyb1E*ahuk?R&coVDiT>ky{bBb|W*!iW5nta}98{{42dDPiH?W3=>!n=yfpJyvl zot)<V5#-NT{Bddj@;OCa7qfecnKGZBo11-1*yyke^P(N=9qK<HDz+&+q2h9Pswc-* zUQm;GVXwnf)xraP$2pbTr*oWV^}5Adp|2pUGk>SciM{0p$Gha_H=j(-&s1oaFqg|Y zP_f%h`f2ZUrt=(e279t36nQ+3vdgWA-&^%nOSh@!0E4psfft)yj+OGL{Y+#z$nPI} z)PG8#eGgCLQb!w>3Qk2!iMU?}dEX=+Vh~i|o{(L?n8$I643Bnc+m@f~zj*n%<t+Yx z`%xXUGfuKmS^iaXIRBxiY^R!J99&kK&(puvVa~kh30sGO3Y(Ivl#SaIXW7VihfWBc z;9k#paKHSAwhbZe6HAyYCcNLr^mFIigT<cvAA~mFaA;CIBT)1Iz$R&nT|X}7=RLSJ zO*gtH{P=#oDZ!DkFWBD+epvQP-?Payx8(BA(`-d7dQ+b6JJ)XWBH1P|@9v$d(6S?w zTYZAt{pMQDTzccwqo3~!pV}2Q&DqGyc<D?^)4xQ^`}_O8aDICEip?f-O6h`wy7Oxe zFs*;Pk11u6;cK@4{~k(JDqPla{QX*;L$~oukM)0soy%*s>rHrfN^SkWOoiP!n?Fo% z{yWk0zs;MSt_PGpymwvTHQ7Q$adW**(V0Vk)BjbRG2l9L?f;nuq0;~V*3=k2(Am6$ zh55Y_n@m#Icb4<Q_l!K)t5@GR?sEH1)erfV!uu6#-j&(}SM(Z4O)%f1FTmPkde6#3 z`AIXw!pCxL7kuB#?d)ftkQ!@aQ}MN;Nx;8Dn*DLx!xn~(Jzmxkk4oPctGsz@eyrOn z&`yr~`h>!>_H%!3d_BKDZi4ilx{ByKn>yTnfAlDj-E<~SZQ8C0^3xq0J$CRj9<=WG zcsSb0?u6izNeq^j@ArJ>`Popj`)TqPMa~NMJ5}#jEvjr^7%hK7-(vU6JIaM0pKYu; zW2D$JMfvN&$xqnK_syRFd-m^lmiJ5x(ls|IWkznvxcIIjuf|-zL-K7I^F8^LPMeO$ z|F>v7;Zt&y`gMo*LCnVUcZ)h6&udmT>VM7`$z*)2v3mcbi)Zie^_%&{{GNk{%ssVf z$v;Kp3poUAr+MCW_<QjF*~7i<cQ)y~{8#okyyyA<+qUJ8cd4A-dv4-`f8Xrc{ZFW# zeq=M{seA<2TAkk8{8uI)xTK$Qt@rh`<F?!NPUyPqF`D^B|MI+O&@`VKQ?(!K|A%Iq z`kSAci`aX6>KuJwnz*yw*704P*far!(mTgrA2Q<kcr*RbJ((##4zls||8|#?u;gH` z4*lnmaI-&kk@dSBhjsED*&>`$A}5`FCsF$^Lg?s=i(ws{xrMI}U*w&@-D8--?R0dy z&WCpnJdu}<Fg#*Bc=%YH?1sdAzSD&j=l*;yIe$``*~#8PM?qo&-=1~}$47zkpIQ|j zu1&hx{xraxzprKQ-(x!-R;HX6KE0|UGGf{%^?*FNyy(B54{dG}=ikJwdH6S5n*htV z@<#bMbsjm1-;;GYRD_v1bX~;s`?EDZcI@O=zo367z1bp;_tf#`r&W_a%=tK*J@kN> zP2~Z`M;`Mh$Vq-(zx}q_txg?>hL86O=2rb`?|OOX{=XfMB9986nk2oXD>cpRz-0Y- z^1mh)*Z)+s^{#tUyLjCL2F;{D4Z=?ytQ4kiX;k*E*ZJKmXM5`bpJUCxpU-=H8aGCp zfA^5Am~?=psejI=2DZl>pIX!VJR7C&|9_G6Aa3#g16_M_E3_s$Ke;*o%!9dyrCskE zb~^e`n0G+Pg5PBWfA7=yf};1KI;Z!(n|4;PUzvZ&h3NvW-`IZLUq7SG=I-w~i|2ok zF={)|z{wiTf5$@fvhg&9niHzIPW(<wI^E-q({2c#5HecQ`}&2*p3)^NH74gtzr6^W z$0-q9VdTGj(V7UU);Wn5H*O!alba&q#;DJ6&s>fvVX?XNhMBES4F->FzMfbk>fAW- z`0l4w34BX_8`Ml-KEdamC*f=+vOW8H-uKCg?33n8i#MOWWFV*RA+$ODn|xx6;Vuc8 z>)vh4Ig^g;ld?(FC^>iVaBP?B!{nBk6ED}j@~*BtFFVh2PfS8{VOqUR3Tv`cOpj!j zyz=27$={dm@cyZYTcKUO&S1tD=I=Mv-K}ie6Z#F8`zs^~NNhNM*kPuC*Pcd!BDsKt zYws}o-*UcR(084`Mv+s(qR03z|FtVO?LRzV_}#CdKSA__RGjIC!@=C^o^bzcxzTL+ zn^p5TuK?pKriDj$H<xoPa;U7RJSY;x!<e>m-u);1ADdJ*@be!!{Lgt>>O|2;$q65; zCiT6pSofp%O_1Farca)cHZ9H#HjX?W4t&hwt50%@4_vm}oc9rP<iX#q(<j;&uo~O! zc%$1=(!^Zka=%IZ2%lw>!v>kIHy6??stz`^hezB?zSJ<sp=5@D!MPXNa%K$)Mx6_N zZ2}zUew3Ij-!fy@MYC5tD;NC#D70yP&u@X+4w3yQyLZH-zns5s+1oAbIWO#)O5K;e z$hTQ0;H_3HAoI=6Y0cdOmY)tVGrY-mnRcRkUy{GX683!U%7-r>Ssl5=dquK{W#0?o z4y9krF$#q~OBB|g5WW#0Z_pN@bozzbo|ZjgoqOAS$`oq9@IRhm*vl^X=;)87dnZI( zGAK+qW3*3UPmke;Vs(pz12dk=c3)|=TjcNIZ*xrjZPN^6y_@RXk!7iuau?fz*9_<} z$+7E(u`;=_s>WYjuru`V;s@ul6lE%AYjI0($n8Ha{dm7iXyl#$hfI3}X0KM1l92L{ zSCKf}EB?lT$xU8gDvsg*(znbf4<B2*i^GBIV~wL<&X4DU2}#^HTMeA{<w7>gi3+YT zs(v)%u9J|0^qbe8=H~`yrY)3^NV`)pSBqOfEbX9<_{S|Xd5z|&r2VL<`*7_+??L9H zn_f4v{bK3w+3oqy;qvVtq0)?dW{Mt<y}It>!dUA)n<tpp>%U!iH|e%jcZ2H#2f-Ca z^DMN^ZQ}Q~)X{xl+H}G3T?AX=%|mxzw}khl@=w!>C`&reecE2<^ajCMzAxWxy4uK; z^cYm0!UyGkm@5P<^_asgQEjJ?I>C64`<v|tPqP&^AK3Vu^}NgxzW0nRy?+(oJbyF` z)P!k(k3I2~vaC|+w7%lF{VmFf;0JfX6+!DIelG6a_!4PQZUe`kaL|C@{!5p)&4pXO zfkP&UBSNU$qjY(058P-SrvIX#;lTA@w&X5H5^Wb@3Vmi<SnXW;{M_7U#pZiv&NF}X zS(V`)$NA_dmA|d>^~(1rZP;$}rW8Eh0Nb?KF2xl3?PW-u@uBSoKWjwZ*FUZ;+`}>> zyz9g4@>3sw3EK4@5$E`Ehi|?I>P|=KAk~kKzoOwWiTk#n+%Ep}nEHL4FZIQi@!XGh zH`T1`ejoMeq{6=Fm;cwE`R~Mj%M6sdpuYQX-(|t6i57Mb-qi8betB{6+2Tw8o~%tW zcYGF+^)z}x&BMn2gwKrS($HO%kd2Hor#M11g^r6$`dPhH`Mk94yxngfUMZ6s%+>p@ z1%!lzybPE8aEe#T#N)V3anHQ^f0e&$&)ZJeY>#A!#T9`SLH^7AW*8i-+4FB|c-&9k zM&=*W<ElLG?k>L`9$)))c3sBRRa1AKzx3+L%HaCq`*D!;3UzmZhGIafhm8E@=ZbBL z&TSHl!UR*+Utg`i|4)$jG#$g!%SGRRp1nNu_xJbt-%dW<x*0OO1s}`oa+s=En0;-{ z#YL{&XP9}XzHQ{@;kmLRaIs0z6OX$Ehk2)db1x{^pts`zlk>NVrmqf%w&mU~`FK=Z z+tTu;jmtr2A%Wxe+IPxs=RUitu}7|Tzg6k08MQT)m6hx<EZ)b%<0_X%)csRGTz<bc zecJxhr%wF~FkR+9f8FQv_Vx2C#Kpw2Zfr=*y0vBHy~^iv?L+SUQ`6JbytugEPU~A{ z(^pVuyz=w2xA~u%`=-yDwQBXcU0M7#H{uHZd_3O0d|SP~fn-v@#&6fvNePF_zTYkH zeteLvgpuF+ZpGtX_Sr`nm;e57nE&fxe)}0E{I*{v+}Qj(y#4Fz>#si?<`+M{-^%9C zhr>ytDfP_1CK~>{>~H@zf6{)Pdpimre|fw8{;WTeo7dd>bD7cRZ?5R$UGe+t-0}+D z{zNnVlC7RRZQ8f|m7nTY&+`-I1dZWbUJ<CQZf(8$lGNIWjfX1VY&<^axP1Mcn;R0H zmlp2WQT8@!dDPadbD+V7>lRb*TKxTTdG3)TF3(pkpI4-}|4)&+iOH3jHkC!Gf&8y- zT@8<a%fiZP8M~{bu<FZ;hbB2UE^u;i6v$ORnaF(Z{gTOkMX#=`d@Ld^E?;oV-uPg6 zeC<=2$|n>3JUvg&pP9R~a&6SsQ<+y+O`SY>a`4N)`yVB_xwUB)|NZr~-u2tmXD>hh zoU{AguG2q1o!0-pqyGKg?^fO7`u85+x>qM6Dr$K>zJBlK+}qpsy8d)upMP`H(Usxr z=S`kHyE@>jhPHO`uClkUd}f=yU2sW0XVK$+`+GkhH1qSB{d5F}_1y3G>;Esf{9jyX zUd^YI=d9oF2~M5Wm2<!3vhTS*S?jXvFE^jJD`w-BdNO6&w0HZy<y+1)O6@8>YkJ+{ zg}U@AF{U%uR=e!@{ciW%dA8M75(`cpGCptfxWY#6^Y=&H`e9;6W13ma^6%ZLeAp`f zO=|fjX;81(^YPazS&rn0h#8;1T=owa`?_GAA%l>R(3~q*LMGQeJvG(O$ET<GPT}#H zjT<vADzPiwTpMlve9z}|#nW`7(;n6Sdb#|Zpu5b&JC)Dpo_p9X|1PHRsHocf2@?d0 zYhPbmd+yH8;<>?omPeQDeX*!phJ&ARlKXCt@~ffYmW$o{%XXE&f45}$^4Y(I)%{A? z_+(yud3*bM<@dYgXH7rYe?DV8_xJn#_g~)Ky<PF>vt#S(jnmJS7^E??@f0YCfqLhG zUaYLFZ3)f+{uU2e+8zBB_N&jUI5egDjNx$$f&E>H*VaTXtDA7}cHZu@PmC)*BrK2L zUw7`u$H#Neobf4^6BJDJNREn{_4%y%{TaotI&3cFPbs|?nQqB%_hZ3IYYC@{iF4!j zR#{GuDe44`yfzolx2u(zqQ%Gqnp<#QxAWhv?DaFvVyoY7^{c4Zaj^99vEFk>#pB=T z#O%1R<mb!f^XDw?x4ZSEdrm<4KfN6fnA$lxOWPm(`|-G6&F;zPW77F1l^4T5KR<7N zUHf12g9+=s3q0K86~s>*pJQ1(XU`s+=YKw*zkk<!f|QyZs5qLup!(g;<42aui|a%r zbj>iyoK*aDYPi&#liYLeZgiWOaJckQ&aEvcmsI|KyM6BOce~GT;ryfKGvmRYPp7om z)E~XSv-9#1=E=eSwpU}^)g+p~u+LDcKfzH^^XKF7-G)h%SnMQr&aeM>^MvB#`Xe2J zYPqg4f9gRK1bmw=Ec2DVJl}h|p6LylvKxtfRVv!rr(NXjYIYp#+ZAjkymES6)k%q< zW3mx(<#$W@UQP()5t-2=WqQhyZ-?LS&!0cf4UezA+S%V<KFcKYQO*Cq-<QRLW^Odo z&(1PEXZd_i@`uG|C)6K0F;UsKdV=GthL}d~qNci}m?qxR)jA8>Uxfc+m3?q(b^V?H zf8W<H^L=OftK+0zW6i<2`>)5>-`&z*c<RSeFC|$C*jj-26~f7)I~5Dwo;AO3^0rmv zZqaF7wY9g;t4+FZ|Ag<4!sQn-$K?L{*S7OYKQr*_Y<`!>v&i^@`9CWL*(0ST7eJ%x zuYP<wt#7^MH18H>o!D5XnC<IdO26vK%e}K><IIX+vA3#gqPL&3{C=m{cdkVJx_5^q z8)^*1CR&#$*D@bml(Rkmg8gr<E&MyHUMy_)nf3DW^4TXZr%sPeJN0D7q)AD~s!vbT z^|igI{Dt}Hg8dU3*YE##OQc!P>i3(?vX^*et*$tl7oD52>?~+lXrlcwzLtWYpPs78 zzFnk$Ku)&BM@XXkhTOXOWw$bS|Gss{rlJ19qxgG_Dq92pC$X&J>2$szkjCbk%Bb3? z;8irQ{@>42i)Li=^j%vUE#121y2t)vW>pCZiHZaF_SB}Hc4!wcus&>H;Go%Dws?+J z`MaDcoL$1qrYR=`7Ka$fRlK>iJ^wwZuwUj^u_P%uc`j(=@#enT-I@Aby;~+&_t>&= zCX2|(<V=w`)?hNFbJ2m*60)*y7pw`?nmBP{n?Ymz=CrdtA8+od+-zu(w>#j}73S9F zBA1Ih=UGhQKGpWiXRg)NmseI!{y3xV-_Pf5Q+8ZeoVadc>F1?-8s!BC9we3o)M!-L zOcRjeVg1{+>Cv9p-DPci9;rI#8JxQ!n-fqWAS`0jHhq_)m_~Dz;{rpTied%NFT8F{ zkAI)m-)~X+`kJYWoYCb)uH4RdxVX7}|6efUFn{W7w?e&5VCKSqCtgUV&q)-qY+0Us zyw7dfOYUC{M#uVOvqhBL7AIA_-}}8sy!Z8;{SVuuc_bIz6}Y}F_qNn0m$>tGzt?#3 zbuPTSMP7*Q;c}Th7iFzV47y|{@rh0FGGJ!su`6}6bT;s4W^8pSnrmjLxM*($+u}uw zcn&Vpe_7t6chBw#!%PP|1w#qpDRwCf>;M026G`HE>0OYb(`;DlvU`H2n$i>2Y1PW6 zdSO?DBOUlW{No=eF7B7~KVkedd5@&~3G2)i#-IHDDsH^GI=sB@$3ymHj!4#sy;WNc z|M@JMk#q5XkM{|wuW3SuDq31Dx}~(eK2rE`_up@~&;D3kqZaeUK85EJ|H%Z=#^RJY zA6X}+JaU%(%lu=0#iPz+%<P@No=o=d`M!6N`mBE~917Y!m9ISR<nRA`ZA#;sV6|kH zoyj_?>gv{4CjZ;@xX+qLZOPH<Zoh>*VX6wVPMCk!;@&dxq*&3hB`iN1Y<B+T*vP1T zOm@jkvq!ql%RIbgCpz1lN#HMZ^E=4i^m5zF8_E5qyBg{m9$cvZ!n>sH(*ds9Qx%JC zN_0|-=Nj@kF8;6hV}jWu1@#lM!nVguYMDPeE?dF=cAk0uyqD7DLBW!2dA}9bZkb*5 zqS-L{*o@*QtZxp^{iS&S!D-9u7Z>~JtmNL+zOi%OzZZ-9eWYia7~f1c@{bSl|6(oS zFk@oqD+b48(aukvr)RkQU-<h)xZ0r?V!sv|?G28+trh3I-eE%SiYShdnYM+XWE7v2 zARm<4xHlknciGxE*W|9>NI(2<<&q^!Y}dU%&;L!}W65jV?{^m4)&82X|F7rv{e>&8 z3;WwF6jXMb(QaJz?aj*Qyq!niY~S_w+wHIae!qXc>-D<ezrVh2O+L=|(|(R&&5we+ zg~w%|iE7pCRI;+V^=5lVN5_TQ?{}A%->(h79#_3K>-xIA)^Cq=3YXqUY@b=Hr@!yV zB0t-&SMG-XebCG=A|jIVO=M&LgzG!^{Cqb1nO(I|r+3^N<C4QuG=qI?<P;vxt$A0+ zANSiSl9`RiV12t{{5-K|Qq_{?xwn?Q-~T`E>gsUu-}jS}lke(i+io%!tbCw2b;1OJ zr%#?}Xlb2#;u<y2?-{r3T&q%}uRFh9i#|F};#dFv%YN3rTeGejWhN&jmHc|ST)Ohb zjg5=@Y`;ZpR&U*M^ytxN{kAEfVeXrge?IQFm-@MI{Q)xt1&1dGFY?CM|J~YW^U34$ zN%L2p@^zn1sxQlV6jS^4>N9VpH;JduoiqD=<3P*$9-rA}XKi*EXI@%j`>EvfJk#uH zKdm_T-@NWwd&B+kyueqDIUgGMD}&V@&d%Rw81b=l7igw(Z}s<Uudc2>+r9qaobMV! zKU`w(@2llao0ETMM<BPjUPx8duBdG}nU^l)zp0JfnAGZNT=2kQYwqo9J+}M$n6^FP ztgB(`-y@m*g*!eV@lJqz`F?5r*J=ONe=2Z4I9M$+(eTz`({Fbgjz2p)`|OX!`rkw+ zux^h(z-_rS?#kn5v-7Xr*;#z{`OgWgzjmjco%PIH|4HS&%I7nGE%we3zwyab;@hE3 z-zRQ7P`5pFb=b_ZC(oXB)vm6&)xXi<?%(4_=2#Z@%-OJf^4@iCE;3htyCHPL-Sgkw zU8TlGjMuKr{r%=)lFVll+tM%m3l}c@^<r`Vm1VxtXXn{Q8>gKySRd|`_LtfJb@R(l zPfz>SUt-_#^6mEfW;2w9w{(Z=FH}Cj^5@0$tgEYDKAG%)ZJKU$-RAw%_Ay#N*}cj3 z)W6b#^EVfJ)SWna(OrJ;aXY)Xdw$Y8&;1K3aIHJoYi5>t$pzHFJ=QOupVDi1{$6}a z&-DF^qbpuoS1*#}J|R0lK}CMUB7ceX|7x3M7TD|UdZBdL{Dt<1g?m|2*ml0Zw6uGo z;`aOl-TM^nf3f|%8_p2C{q7V0``hyF-a5fpJAdZPl~c9D->s9cJvUoo1%K6)PZzh( zum87GlWoq0+Ps}N?BeDh72L~~*5mkweV3?>#QG=AZ?3KmKmWwI_?ge*$C8g<$R1Ox z&i-3^bKkAdk8)+{2b)-Te~mf%``dT>W{0Ui%e1%{9Nk~OdHXq1L->1senIZtT~{Sk zKUrUBn3T+%-p(g`W?AZ#yYoe379V|<u}`Zp@WJo(8eC=^i!IJsK9?~rFkDnvSh)N4 zsRho4yAB?0P>x9KiLZFbI$7+GlxbE9o0Lk!wjKAUJ+;cb^C_T7qS!ouk87HR#{RCx z-klQG<^k#_I5Pvb2{`{3*Iir?u+Qeo!NVLA)}EVdEuCYikoO^<|G7pg({q;Xe(&-> zByQd=9Z<GqYFVg{|7}ALmUlZN3eL%M{aCqt-lZEGlX-W(zPPwuk@?-<F85ChYkFo& z2ker#bn29P{knM2ERl}clV(x<EucK*`+xV(f+G_g^f&|*dH9~(I?A%MK__9)#L_2h zLF~sS@UGkY@tE}PUdg|%Qm@`TbG)~OJLYBZzX|f@2@MB2E%NLO{o+4AU-o=fwo8Ti zVrd1PJelo2kAxI*PDt+mxj3)Ky5r00_4~3scqJSseHXj4MJ7}H?|Dl`=98Af+<pI^ ze!jimRy4Kox4;)ruSvqELo0d8YnDFI^D}c4pRY?yu?be=Um$ROf6XU`;$QC?+2su0 zE?e{M?d{pUKmHjVO_|myt0Jhx@UrChk&v|O_Mg&w3=cL5Og8_-r?@Qt!pgh_wkMQJ zOHcBZy4cM$*!A}D3;E-|@gHBv&oBP8gpZ}}GMjmyJ>L~Z%LVUO?3RAv{y{}=$$Ybk z>cWA)M3auXOtVXLVq2+LzkN^F^B&b>%Wj*$@cQ@ds(DYnhmFz6lzp+g%bs~V_{`Jd z7QO90`^RT_f1e(W>%HE;70XWaN@rNRbRUrTd|a^lV7_^+>rPeuQ%@54#F-5a)fp~S z(dDmR;9c?iaB{!xwrKucPv4)PSm${DX24c~6-Fw2u5ATI0W)>OdCnA6R83_(5InIW z=?`bC^n6KMx4whr3XPKD^K@Pv<e6y0F{S;1Q&zwQcIgxC2U|WRianM8pp)2QxbI9z z;Ef~ee>`92=vmBv^z<1nf0;O&wEQz!JC7&0c|HhS@Gs$X&%Ej@%uA1yXUvRTn4rM( zr2A6I>C)51{b!E+P-63%Z)28oBjMaZHq{n)E>#bQz)9kAEGOh<EXvW_{l@6A>WBJO z7j)16_mKOy|8DU*<I5@2-m#@EtVmulx!&gc9OZ+5-`4k~H|Kw8k~eBzl)UZzv9%Q{ zx=$a6K9;k1tl1#*W_8UkiNi;NmuuWv=-e))FD)nZ-(mjFI2{hdO-29zexLoGO_={Y zb1mn#Giwz61<H9$j<Dv7_#bt=@%OQF(8~4Fcghl<d|1>gX`E*M&>_xXR-VkI9nbUR z-sRiukl;y~{!LkWg6FGu$5zg`xA&YNQ|HH~1B%8KA(cg<kNxVVvi|?_g*DZg%b;!o z15@(V-sAb-=Pdr8|G)T{uw3>N34Pwe{XZUY7k}x0Uf*|T;cSKUFPy^4dv?5&SQ{a? z|D!;l{T{zBvT~BQ`nTWZUH4sTy;@aYp_laCKX)o*K0aE&eBd=``M`FG`x6Sw=O{kU ztNK*%x$yk=vM>6}AN|~SDWt6b%e&HZcMIQC#eGzBN>Pm6!YRbMtVfb5pXWey`L)RO znfDW%>sbER+%5jqw!G@@KF4(vH`adT^`Gw?Ho^JVr)9JAj3%FBm#?`nCHUj~=xNdX z$Ao`=ezM7X+ntXl_5W&u{Y%)a1lP~ZUVb6*!N%>|eoTKn?P*-{nP9ae7h|6unB3WO zvF~x8^|>`446a=;?=dgA*vPiQ`TS0okD#H}dS0ge>jeLr{(N7)PJ%g&^UNV_J;@22 zr>4x5WOVxQ`MmvqwfwX=o-fAdZHm7${PJD0H^za}C_~X?g5{nh0jHS96U0S+$BV`^ zALkKx>}P!-A>aEnkBW`IPNzXc;5mt^56KG>U3MI+l}k;3`Sysg{~c!IX=(w#CM520 znV`?{a352l!37=8#~UmDd_3M8<n(eQ&ruyoA-;>v0ZA1Bhsy6&E|2t!-BYpgql}}) zKaT~v8U;4>GBZ;*r4$*QVm-{Cxcse-Ym&tQu}v(Nu7Zzw3p+)>x(OEXemc;?#Cl%n z_`Xxi?F~6}3U{2^W0Y^`ts)^LVq#pdFQI9o`!~g;cSagt4_sKd<D-|;2}7O}{NA5> z`1kA+u-#)+&F!Q0;md@!rFoy6<0QC$3xCjX@_Nb4VO@Lnl4MF=exqSq%m$HDf=s>Q zj7dBP4^D2+aZ33R^-lXiC6o3Z?kn==#YztyE??02&zXPY8drPAHbt=>LngLKC!4)R zcDCs>+eWK+G=Jwg^Rjy{>n+gWk<{NqKbFj`Sn;>n+hhgzdeg)j<8_kvMVarF@tCx* z{!}zrrr4%)(#WxO%BSB6Ov&>$?p|+aJEP|2vj(5dTRO~VzE3dj<JiJEXQsl=)vRB5 z%NXX!S3VI`)))QF7Foh9T*P6p>+W0WV_o~%&-A{QyLj|S`F+o>KNrnUNYwHx-VSe= z^3d~mn?nByQx&dF@8tFNd^mKsO5~^e{6*LK)E-Psnaz?iec!R-`Xhqw5`Qj43sfGH zs$#8u?)TwG?>uFJ^ZWZZR!{Kxf1q|+4yOhCpW}`7Hr4;WD+yTKssHu;`?SBaFU`1U z|M$z~V-kxv{xW;<E^gc4Bw(=QU)L;0w$1|r3wUDuXVuG9Tes^Z9=IugP@#;Od6F$R zhe;dj(V`8ENp0oLRnAp^m^b^UC2C}R+x(z2<y*(6rk;=PR@;5w$RB*obh5$5kximS z$I&sN^ZY)IDH7{y*rY2iiLn{a<J)lT_Q4{VwkbDtyXM(^bPMe7I(_m;GTU(<zNR*% zMf?UbJb8~E$2OQa*eKMVX$#yZz%@motL@)sbsN2xOZI9c?&(}|*!a*Vg|99@7dU9j zAN853QNL%ifPl@6mafv{4wKl7*esRfJuD?Is^3|>R>G@8(&E8E1v!bt9Um86{Qrf2 z3FDuG!p%-nCycmem<7h)>P<ZUqEWw^<0bd417{>^3?6OSoV1|-=6RRu8J@v%KPUD5 zatgZhH0;%hAgd*u{jwhof==*#G?bYTl)~Wb^M9j>tAhR>Qvr)_w>PYB`xfBN!7Rtx zA(Pzq<qK=>Lj4#W!?p<yPxzWy+fGQh@I@-vC3jTx_-|m$-;g-R@qpxcA3KZm`zpyi zHxqjjJZ5faj6cfI_J#55;+h^4VUACSZg<NldvED4+f~~<XHwg~BafNm?kL-DNSpIe zbfK<*iHFI@4E2i5;@!$`lg}u5KR8~oVBZ%0$|W_&WH<Hixls3oBXvSs)kXI=;g4do z1^mlz_HS$Ak1*J|AYa0~$24W~uixyyrkWYl#R#>h&-qpV=i~8X70ujLtws{cSA>l^ z-#ME}Os+VUDZHe?LV)p+LrR3ELVwq;ZaX&VCt^+x^-9j`)H(KaG7Bc%*_AxYj#>Jh z^OFm_0tv4i7x-S8`fW#UQY%x^!6TeGGa4@C-SVt{DV@S=dpiG%Wi9iuFTAD2si&vb z-|{LbwW)Qs`XVt|SFQaG?~;2HtiLc;9kictEmywOE$-tvqj?-YpH@u&FR|qRO6A=4 z24^qH^mTDba}=F@{$1$*vi5fQg99#dbNy?MHLgBA(?NK?(Lr(9W;v&Dqkj>{miL}e zE}WolQ~Bll>-GEdPBB)0dt{{PJYDd_<Xm+hmyJgSTptCt@&4Cg3Vn5ZrNhA@wq{AC zw(T4T|L$$LSoQ5@dhf@FsZ-B<$k7OwJ994Z3{Tm&$_I)KEerqTteC~|;9-l)+}dw9 zLpL|o2d|wmA<;v@((F$$hl8EL1D{EX8UL>y5YkNHn)E%M+h>`=*3c8rKgYjr+PiJL zSLLG>s>V`#e9lzOIBPh0#S6ht`MjH5_DH?qJ=B~a`fvRUgXEOTuBE1FZD&>~aEPXV z+P`c=dMG2KO|H)3hrV~`d-{Z~_#7X|eu7!zO>X|3_`qdnKX2!Fz<bF1$^K6tZ%4F$ z6SWDiSP=2`$9ms}LWU35*yMZ;TW2_5by{$$@=*i(`CHNXdsC<NNm$q{>-V0WDp>cJ zt9{Cbbl&$uZG1(Kc3odBc4V6wbUG#hyjJ^P4QRHlc=kytyVI-RNGH^Hzn+>9c0`WV zL)Lrw8_%Rzv{l*vMOjvvbQW7T_Rh39dh}Vc+4)T}C)_Un-&`*CAW$||;@=}q&c+At zjxIeK<u&orG^r`Oe#e)Wmwyjh?!>DdrSb08qj!9cOr9O5pVi;<REych$iyOb%kuo@ zn|G_-&(##Coik5=mS?fx<+dmtuXlG99|#n1Sg^=2@iqPzVmi*+`CGrVBYU;;0VIh- z3I!kB?YV=yydT39GqyK4A5bjt5iC%7H{;M-tzw875a9q54hx}h6N_CzGE96BA&@OH z!7N>O_02yCFz@#+-jP)Q@^``K-8D!GELi&3S-M0!AGcTkeE#^}fBqBaJ7u~)tdITC z&;O1+m!H*i`ZXkHJ?3pp`dXs(|MKMe%tQOz9_=@Cc<@8M^3Q&&`i=WVpZ^zD{&U`1 zsdD?BqV9UJ??^snY(KD{tuaZo^RfAZAL*z5REzIkddmLS*Z<2;3t0RMX8yltW_`dL z(SHjK|L0%qL<x#|W2U3;4zm68*Rp?d+CKMrs6gZGz17>d<=#$PRHE$Gb7OfuU+M4C z`DfmBqv(6g(wOA_h_7XK?}5Mne!sVRzvuIpZ@2ST`^~jtKlSLHB`5-5A@w6%DPWC9 zjDtf%#asVhUtTV@EPnQ)iCgc&mdwjv?i8P2>DbKn+q=%=x?+cM`Z*K+us=T@_y4wu zDfD0!oiJll_4jvc!{cj11p>5P1b2S>cwBz=+QjPG`uBhS-`JXc{oVVrh3ojGOg!e- zew+F8{$^?Oyerx3_b!{N9WJKzexeFfbA0{Z(A8mUttKUXJ9&EY8T&sUn$ynD+k5`2 z^Uq1MW@UZw@bt8tlH+cxTd_87@2xMvfg#8Cula0e5E3vm;p5BY^SjE`TiqM>ffn(H zt%(Ty;p0BTNK?}@zUE`=>($CfE~b`q$h_wekrq1sb6ZOb%i|r>bfeY&U;p&<bm-Qs z(Ae_3rEPoemR$CIrr5FQRl~o(zh9p*J}<)Z?%nS9*LcnEB*?AP==!869bfx(YDEF_ zqmxVCfByO9lDGcMY|t3|rOTK3Bd#85<-WQp)!R9af5ry|bHzjN_y6B#Su7zgzPyo{ zJ?r(gwd^NLpKL$);P?Ce=6|c6EPXny!PkN3^|iItH(c4+*j6PS?RvD=;^ZvT>??b# zzyI01(0!5g<5lkca$UW>wh4w4_LSeNbU)gAK`!8BQo-bnXOwbZURrwGM_TTMR$p25 z8HNH;P?WMYCVfsgY<|CH@q-5my;7#D4rIinr7hbTw?5AHZsqg2?nhtR{d%#u>dnUE zUmkVq&kEJ^TOYPI$~8^PCUtk}X|cC=c3yV5*U>OlC(>yDAJ=ZNnI59iM;9(!ILoef zm*Td$*5%heJUo2X(qU2S1j}<==jPkTr_U*DTeNub+2^INudR&Uo|n|*@8fgjNT)Ds z-rDHxX3x1lYoGcZur|u{a-4x{+1p!7Pfk{szS~h?mV3)YK3&Q3O6<BA%eiH@GMDkq z`8UHbIb?rb?c(yMr>0(A6}r0iSAWL+`St%aquZ}C9(Okp?Kv5|yDYbud(-jKb2es~ zms0k-%q<Ii_w)Ju{975PpUyRN2)Oup{r-PO5{_LR1|PQ=JIP*M9d6!dU}JOVh0hcL zre?k+?{92eT(N=8F<x%Q!v7UNjtRLhYA`f3eDu8Vz+&&|dT!+<+1J+0lrqc7__X-_ zjg84?4=sq@UA8Rl6pMX<n}6No34a7m6+S*TbKmp2>+52B;~pRHH|J*fq7}TXNAvoB z{tCajR##uR{NAYi<=C@-KOXag=7YO>dhV#-zO%E~x4P2kj`joBzdBY{rLU&^pJE|# zQB2M*>;AsIKO*JV7tH;AL)@(H+s$-oThN}3F9+nZudT`4)L{CzweIX}bMtLSc-Q&Q zGC6re<$7hpNiM<lm7kx@{N!)>RHRw_NB+G%kqO-wmI<kPN$g(C+mq}e_E=R(>ChEP zdHMVg+x+)e=bpc>c5sTvkH(xyW&Lutw{%XtP(G$=mFNu`ey`Gv*l?gn_Eh4!#kpT> zKQm-zOy6qTt(|-L+y?Pmr<111a%XQpBYj8N>Uh(eE5ZJv$$}4mP3$oa?)VKVrbVJz zx_o~$o}9-nS+K?@h;{FbJ)H+4k7op?E=pp0TDqg?X6p2r8j^kM;`i4bEBq9!?yBi? z#lmb`OQoUU;|xI=)xb@l$*DA>XSdkpYZkQ2*WIuvUKC+56EwGzbMn!Q4l$>70^3A? zMv5v`S-8x(YI;2;S!ue!^rmexon>*APp2O1beL7J&c*5?<MO}ncE6vsNl_&C_BK<N zh8Gv!Zoi-RN|8TPaqbi4SugMM9Z$TuHotB+|E10g{-8Ocd)4o4gTG%5kIz+VO8D^U zw0`#qm0v;6^7sEu`&9AYAwYlcm!QW_ZWh_RUbA^i%1NPS#fxX3&#%8HFf)7o-mvd? z%jYXPG_&)YO>{W5P)^CuBJI?!|NnmT9{XZDW8+cZ*@_`9zYoY3ozItu+WSJgg+p6{ zPulFvmgkGQ^)78bZ<p=C!~&WjUuM64*DEby)0Pc$s^9HA7Aai2Eaugbi<`Fg>RoA9 zUal`GK0&P@<+p5-^L;12KdeRt4_n2hKK;L2e*fjC)B5J6&P#rtlsm>>TE+VJV8*pI zk)YXyPupV@55746&Ax@F=jJK(|3}5+*C;x-aRjyL>B#MIitP#h)%fkx+!toiTdy?7 zZD}q1eI?lc<zaq%lO0Wq_k!kHUr+Y8Gjy5zML@14*Ly?Dn+-RrJ{)BCtq<b=mh$v= z{(e)dpzpEecbBTquetPsds>h1v1=zLDwm#AojzmVaW&0u4O5G%T%p;A;&Sc_@742& ztN&ZNIQ{%Q+p{-RcvJrMn%_I&|L^m8``JHRw_SES#$Vgt7amjCy6?{?@5Sa-8#Nk4 zK1tqr(SF(A{_cc8v6vU#f0McsTucgHTv%xJd`|Hhr$gIbCOm%g>-Bp5f;msM-`as1 z7e~b!lY~2!t6WUj9<@x~ddS74h3|Re<qwlsE^P>Hxtwxn^Zm#~<B1z@*@`TTn<FM{ zuQF*@cTmJf;kgF|AFt><AaK-jhQQxPcVtpHmv>)I-*kVz!Nbj7?Kd2p6yhcbE#m$y zyvW8yN}*Z6Vvfqoi^5E9DlAGaRx&buSKQ8bggJ8jov(82r{gt+*<XZJ1P^X>)UcS} zKF`Isz+-~isrDNe>Royjmi#*;rZrJ?jy==G=0A>2H~5zS{o}*CWYQ)+*B8pC9M*@& zRUUOoQEZ-&w`gIE`lrUrs;@c)N)MhCwmdrXlH4zzB4;}pU5STxHpa{7rUvsfbvFGG z<XqD4$(**Odxf!!z#$&S2Pbo7tW^$Z-_T3l_u&vXoAoc=O-;7WOb5EE_Iz`zcb>AM z{e;rv?xQ@1c6$D5R%YOMzFxtC-}cJ{#o`-^+><zk)!rD#`6iuonSJ6k6Vq*FCl1EV z6CFK718t5mRXO_|m^ojvDZ}xU!e^J|JI<LFq$D=#-4L3nzR2&(@q;YZ3SF-C26`Ke ze+lj0e9<sRNcwo6r+f5TUn8~-jd$nOR?aVUl)L2>yXC2a(|pHA3P*E#oqAp-|C*Lz z&26%%?FQ%L{(ng^LV-(a)*FOv)PJVa*J>bla8~)Pl;e_*FK#<&eXi`yjfcADYRfn* zSo(xnx_CR2wL3lU3Ne33zLXx+WUyf?^T7j#Rh=@ErTAWNyPbEseU4|}ic<?GS)4j* zxzXvkqi}6fY0Q*EW&D9cEbK?ScCBDlV&GJ*a%|~XyQi7+oB&g^@}a~BE;lwMuNGO( zuBjx&a*#EDTUc)VMZS8b_U5K34pD-2R?7+>AA4qeyV@dd8>b+LRf@1};SCF^ZOs<P zLk0f1=qdP2P-kI3-~B@L|4Qaq!8a%WA75~`m-&z7&nJ_64=Z!Buoy8fJRliWdvWr6 zfnXM+zJ$Xse4_;o81=ujo^a?B%$eER*4uS+ZQ7?mv+sME5|4UrUVCnuy2Q=gxY>P$ zN!zOxH**MDY<sfip4r;k<As|Ynj93iFw4HME=iL<?{*+Vd&i~|PW&AV(eHYE4L*x= zu(*gVT(96dfxCr6`S00nhTK9*yIme^)L&GX^F66uKxWbBnB{Vd9_})D2wMx#{NmZs zZo8%yS&t2j(xJ=a-dxDjtp9S+T}}I<R?egj<wyCGT|5*oya<}$JL!1dM&}tB(kh=F z4%H+-*Z7dsEh)VyujH7RM7K`cjOPdBo#H2am}2+zW0JHalcz}e$45th{(XO6&H9|q zJJzYM*MS-_N2ME+oFDy3O!A%_YW1W?^zmhX``mNNg1iZfHXT|ueXXjRty%@k^CLT# zIty+qRA5@uHPfdsZ%XWxrsgL+JQI~#j4K4()*D=Onv~Jqt5+m{{6Vhy^@2M-UpmZn z1Fhs1DBNoNtXx!Va4_<bhC=NG#t+Y}C(67!+UR&qKxVG^{l9{$GD22S8-G3C@TP3- zUN<QZ<#V%Fh%8|j@;B+-T>E6>p>(BCi%RVm4VRg$wZAOfUQ<1{JX`x-cFnemTj!@R zN;V4!@qV3_-YJsFr}DykcgMZRgD0zRr>qzM;up0@$3^v}{I<6W*K)$69Q8dcCTewT z7nRwyo&Qbgy^GQ^dm?UpTPQuzx?_vzugT(p?i`}q7&(<bo!I)9Gu2V0bH~k3p51vl zyPfVRbbm1qbX-*ZMR$gw-3Hf1o;eeh?rEs}b^X~Ry}si1lf8MKllHpBK5&qn5Pa+5 zRl(UU8AkdW=633_-dv*lKH>eps<-)XZWJj*O$giOW7&7mu;~z2uGSfg#lC-*OaCtn zyHOQm)!yLzV7~K$8*@58J2`CW4YkoO*>>{Ap#&$TPtD3-_ub%2RP8iQy!~RLyPV;Y z_X&MhqvhvY7Q5A2c%FM_uu~{eA<0R$?Dc8;`wMH1TN)?|%O0F5;QB!Jp&TExQs?1A z3k45uaC*{UlJj%JS*Iu^DT^QujVk2{66a0}o=u#3{-@ljFO#IBmwli6+0TWeGorw( zYmbFThkBrR!Gk}+-D0{vA}bcW=-zHqlKPu-c~7&dl~)OSBahHbMJ`Vfj!kDG?H94< zEdHdTcYBgR;JV6A(VrKa%bH~)CdnP|Iwv4kuz2qr!A&e|doq+~1j<i5($um-`g`Ys zh1P+-+kSO?+hDQ4?q27OTbsDGj#u;6Dk>*9)p5+Q4Sw$X{O=pp2$$f3V>zu(YZUBy z1oagaYG<3}`q;YUScIJ4X1t@BQ(yn*LcI>_iQe194t8&j`!m6;%IV5R=WU-AR)3K# zV+&Nv>W(cq*ZVfEK6cOB2gbXa&2KOKV*c&+>TvzfvN;;UhmL*IHapn;dW!dziL5d# z(<~Vmc}<a3Ty@OSbkXfCGmAEse%j2S<B{{^oL$GEN>echmr~8s_tiR=B_4Nq*>Rw- zWU<0*ofUfPF7n&tILP)Wb_6B1{#)w%hGRqN*&FP~n|o8E?Q)amIPTy#IhQm0Pv-{R z<9n@HWSEYMH!`;i{A1i3DJaDG$cBHi)kU^B#!Fg?9bIM?cvwiOS@LbW-8t7dY)#C} zCwutT8JvA!U_Dt@J8ur_WNW{PR`oxhiaWa$Xt*m);8V)V*=?}!@rAwVQxp~p@Sjk* zJt1e|P3AXG5_fY4dEb7&^SRvT02X0`?-!;T`%dtmV>4e-VNScUQ8Tl}ga<Pe+!FXQ zA3YQ_IB40#`c=^P;L67dijnM-tKaXvF5sjj^JU`QxW%#)1Q%saX)l=8VKT?oiQR+q zZOfF~YxSe$Ej~_Q`Q@M{z_;qLL8r?^cA1Wth=&L4RYZ>;yLS0sor=`$hmBkwGPMjl z7w|1w_NUu?wSI1LVxYuBN47<wU)rxumtSQ!VS}zWi@@yW2TpmK?b%zp?HJ|PCrID0 zU&47eLs_C(en#*#+jv*ITZeu;ddQ>{AW*S;g@Sy_<b7)PWv6#qZ3|E|w)t|v^wa^r zKcydEwaOaLJ2+{R#B7;Y$G3HC`uu_KtI@ib)gH<fn_Dy2CqGX(epvNR^6c3~9{dLv zzCY>yf79Qb`RBg8I+$(%S}WjN()(V=ZG-<S%@b;x(>u&uF5Oc3`up~hdfRW-EIqB# zRe}%OO~k_vOkovGZj$A=b%cqd$UEgn!l6B$vm;odcc(hOvOjlVu3>WS=Y*%be)BQ5 zJA8ChP<S42^IYI1S@%OvPR-<>W9vWfVM2i&hf3dCmTlpIl19hF6j#S6@z^{)nKgH< z^BmTT5^Bqw1!c_B>kAqzB-8>=EsK#66aMv(OS|S&^A^Fk3u4qt!hUV(H7lO~e$R}6 zO!bM%-gR$m=UDi))Vw&oO@y!Qw}5ktRYYX4d?3RqB`Ma*mL(5ARJ8Ay#8%`Gdq-XO zYqff5H?OeWjR#*8Y@Qr<tl*q1u3X|E{$;6M$x^*n3-n$+nI#^5%jLF)czf`K2Kmy@ zee>d$dmU;2v!_GJX@N$&25*XJ?8R<7SMgud_a&SbD&jc!$nN@^vg`l8EY*9{x+z2c z{@TUMYGa<a&bz@dqw?$0ybnwM=geNC+rV7)VQ%#Uk1zdSrbJE<x#GWMbFRDjt4~~I z&AknRKW_ZW{eP+K*Kr}n+hQe({d@CHtL!~0Qth}SEqBtsPq(-&7H;1!`{un>E@OKA z?+v-jYf6^9*AR}~P@5mWi2Lp5{dGIcJS*dFf7^DyPy3y6PjOCzNssN9MZP!sx0b() z+<o9&?*7SaI+@DfT<R~CcgU{)w@W5(w$H+M{musz1J(#?n5pzl)l=yUHEBO4_};(s z!Gun}4@ac>8g=~_G1jDU_lT<bKXYWW3^*?)s9mG}KbGZ(ulb3+s)g*%P3oqKg*j8L z3Sr}A4u$RuZcMP?5Wj6{_UZ}0jP}Lz_W$q9@o(DkaKX)Yhdei*Jsc)_NNZ|_oOb9o z+c|&A1r@qjH6MFMOSf7INZk+#<ekS+@(4C8*Z8RKz`oyF{}*h_G2CFAv|Z*;=s`v2 z3l@vrqUNl%{eGxyTDkY}-#7QmeE++@nW5}p<iSH+{tpnYIq<Q`VWVQ9%%P`+_0{VR z%R8KF`Y=VJy+*Nk&TGZBwMu7yn@FX*TAX=(1ThW=8ch>zOxis6lwxw-t;X+7EiYz& ze`%R7<ssd^d2PXqdu}h^n0|i;b0TPz>%sjNkTw6+0%D_4hX`|7x}Ns`m3!;9`;#on z*x`E)3!(Bo-xplIkFdG%zY0^|k-zG@@0#915`8S&nDn_s%i)u;((mdG4+9#W9c23- zztn!pAHB8C$m5!i`arJu^EtE5P@d_Se}~Mv4<g$3JZI|jAIR5O!=nf^()l6ZWx)-b zxu@(mJ)3X!S$|T;3E6cYZWsP}tyfn&zy9AFwD1rFcXxhm%e!m!ng8zF{TDv5&yoN0 z)coO(>oKf<rBxRd+^_$?_ow=YC8hAVg9J<$$Vp-ks=mHj8NA%@+v>{abM9=(ynJPQ ze*Ds9%e2(g+|2LSY@TJFulKKiRrs+!*=m>D8}DtplMWt`1r4);`&*)*He1J{o6}jj zKbfVT5~=<9bo#2ey}RV<ek6XLFA=srZf*X)pKh9(n!BFQtG=?xwY$bB{q%#oXrrCl zOuySU{yP}9X>L~h3d8JcI<a*>pGsL4ExA|mnD_Ie`+0Xuug6Ah$(VR+YxZ)h(pN7) zJ?N?7aVwX4PnUX|E4fF$??b?m;`6r4`R)H~IH6Q!H{bvLp3i-G`~Q~J{(igtXV%8A zr%nIK=Tucy{rlQvcR}vf+1DOVUa#MO4b%^x#}v0UYwEilkNI>XHazg?(g}$(`0-+K z|CWM>hn$w`zjDc0??2b->K5rw=OxeBR)5Pl(jh2ikl-*ebbIypyvy_FoYe97_v^Jj z=tvmXv}o3=5BDD1zj*Qf%<Mv;W0Krknr(ac)%+~l`}^H)cHi<1+Y;*!xA7W(J`Or$ zWWy8JqWOl_`S)y&9zA;U*8fLGyStaXSsZ!d*4FH=FBbQMPKmL6u_q+LRwDiH@9+9K zpOpQ!r<@e3{cw=|nd0{IcE8We*=qO5DVCA1F{uL-tN(5oylic2n{_%fGxO*6pDzW} z>f&<N3Kibokl0*!>}O}+gTo6Po4>qTy*}&5hlgp8Y?~H++I(r6qI27fjz`t+_okn6 z{oil@??%k;m&@n#{W4(pe_!_UlI!Q^=g;eZFR<Hi_s(*E`DXbeX~+9yUCjdJJ>(4% zl;Zc-{cW5iyS3USUvbT~b-PMmFZ=MJz<B5R8HS5byh=SiUwzH$kd;9RGhY~HU0IRr z_UO=|L!iz4v$u(@xL{u*xTxTE?snNJa~#{+`DCjul(1*Bm4QYfW-peKIcy<zD5=r) z*0r_KXKjuePJH~Z-fI8PXS37tY`@<r?$s4%4eOOQzvihvH$=o$bX8BUv^m$Nb2g_d zqBp1cF8s99g|87bhQE7m)hW&8D*_k0?fP(tdu!%pwW*rH$8zR6)f6!7`CQ=K%*OlW z$z=a74)!9I+bd_A=U)r<w@sB<qq|(g>dG`%=?a77+uL&6EH3tKN<A$#cV)YWp#SrG zMW=O-y?nH%?A@JArAZT*7uifW4Law<rBi5@L1NRoozJ9#+2%(+eQctz>&nO5`THe{ zIDTedTXXaNht9sfYb%$}%i8c%EoFw4;#uA3ZC5gv&z)x9xp`Bn(Gkvz>Jyq<T3G)6 zc-)^>)D|No`S#Y<?7C`=$tM(#T(t7r;84LNnRGhk_Riw;xfPE()#R_U^ULL|n_u(k zq?`Th<0)r;JKs%cS|_mS*4FIVq2=%I?d`3bGuy|{@7j6$|7*0@?KmV5E@OFLf8UR! z#o|^s_bt3}<cLeLyzDu<-#N+A_pE;Ud=W|KQPd4xHRYxB@7XO6#HFQIuL@nQWoH++ z)O)(w&5Kjb9A|Bej8?y>!SUqGOk+_=$;>Zmw|Z{;Yvu`C6LHZ(dEPy>UCi@bDqaNZ z-*Z{p5mUUUft7E@2V?6Sthuc*m*l@BgIdrf?^=ZY3|fSf>aT`|Ybq%@3Hbh+tYN-O z=;^Y(FNzMbic8#fcF%d#Z~xDLU+U|Ni-#X1PWk@FG5*c0GPBG}M_zdEercT}{d?!T zU9UgA?O8KHKBswO(?2o&cWYyIUb1-p>FMcit4b+Z&~imd=`WnuC9}g0%zZ5Wg|nOE z1m{FgCFeb#Wn~I}Jh-qS(b;#N&CMGpXBZ}*3I4p;z2D5;Y6G{yGsQLDdOMGNG391H zrd#<U-orSV@yv|ieZSvX8@mhsOgpnsdYfgz1BYhDT8@(oWl9#stNYGMxw8MuCGWF8 zo^cn;JeK*rsP_!xzfaHS*U#G7%AxbkLB2`REF)Jespjq0>$6J!{`zWM@!3G~GuPA0 z@oS^D8vSapeS63)_y0w=U!t-r`K_Jhk1<|X_nVVZvUJb8ySrz<?%_VBbv;A9LTGCH z4MY1t{^#><PIL%==2ntax4ri09Fx2|7R|cfH>4lArMM$Nen-UYkNYN1`xLptdCl8B z9#ZQr%g3ZoZ{OMe+E0e*s4S=&N&awqI%}`vsYnYM_2|`qzDZAxIzP|06tv=~T+LNY zRE_P(w1dAFatK>!2h7p14@^33ImPJP^lQ^v53h{eylkex%n6+)PE+30$~^jf-acO~ z^wgn)8+p5flbaUz`pn6xe<X5K=*Ugw3Hl!z8yu<>59aUvI_+NF?_B2zGv)|Ji@*1o z(r^21M&Tz5)j9uvzu%uPBj?h)Lh)t64$xq3MKPP8P>XQVY>wKQ2X1P)>c2nYKG%Y~ zrMNd|VzAugpP=~~wW1RWwI|K=YY*&n54$jdb6ZK(*H@{Zs{dcuSA5R$_?mT!+5+4k z4n|&%J0aC%T=Dno^|LHzkMzxTn0Df@nC-cPj}lKGd|AYs)RFv+>qqdtuh--AZ}l5D zxHMb-<<$FP$j5!WXWo^<o2k<!FSRyT{Cqmy_ve<cFPG0ZtC+46dC6iCpR5(fbMB3; z969&*#e!xBB$A|G91XKC^O7^Df3dK=C)VMWf~bY^hCN@eMf=X3;vQYOe9h|h`))lr zFip>0xM0Zxmb@>TOfQ?iEKtt<13GgfM|3uG_M3_OT52xVU+LZV;DP9ur7=pkA2itt z*D|~RYHpwOf`7fkhXW$9FDJXpRjTaoSD1gLv)9pR<>Xf0+y@@4*A?}qExB2=Gw-!C zf5Fk4llf;atXB}d!n-WTelojle9cE!;e(H@yIoH$%j?i9ZDPG$dP6HB!0buG?;8eY z?sHFs$JZ`RJv~iw?#tDntr%4q!YoXCKnuVwyZxHD(a)-&rQvSv_q)sPONO6mkNGbC z?rzCt-?WPk^XHddi*$=Wrj<NhH}%#TiAIh_@yCLj*nckKebMvo%>FZqH}<@>7wdfR z>9qcSi8&jbOVS^9Zs<sodt~*Q?P7jQD|@ksm9Svy>d7Wk_}5MG{Qv2I)NZ~n+7ZV3 zj{7zizv-}?Z2A1MzrE?xA`zx&_xxuJ&y?P@RsD&-VZM!Zoq*pP`PnSrL4C#-9wqbU zFH73^{D@+KjY2?<QXy;OQHSJ)hXoQfF84d86vp*7@*aHn+-*ifq@dAB*>xKZC5xVE zN$EWPaN5@WH9k=;E*9KJmK^Q6eZNz5%Jiw$=UP0)+j+z$Ec(lNkxMDZXNm<c2mcR& zkGJMOSWtM#LRrRivd!wW56^RWE~=}gs(Eq9oon*YytE}V*qHJ5)QxMIolda$s_-Z= zY;;-C-TAFuzRsYJX>Y+hi{K80$wrkInqSP=FObM$J%`&VgzdIQ$A`lCo$~DtE-jvN zNl*1Hc$G3Oe1CBWxrHoOsC>}K&Xc%UrbIo+HCpj%Lh@YpzIAnffBign!R^hnGc%LB zmhN?tI?(&?;J*va-vaf{aanJ4kqz=#!trMV=bMHOsdF65*%*&CUha1h`qL_Zxou1G z{TF<O_7fabA`A@gtzeH{P@@$1g*z}(Ec69a=hylmnM+=?J@V&k*nd7_?0co>UqXzt z+@3OLOP8*?zhA>2&yX~>2q|#jpHp|dPxfrN2j>xnCdZ#IoURB7J<t`tqgXS?ed*g5 zE?3%qGS6Kdx;pL6@o2qY7vtVFYj^9Ouw26Q$ya3JHUoageTtzjah+U&cDV~|l-|C0 zx>fa=ln47`VcSloMch_OI#~*34~|>3)i}s>3M3ipH2e{lxL2nln=)n7M%NBevnIYX z^S8*pSvj4Rk!9*W75-hqJDdL)u2b+lv;IkI!ra1Rl4`TKJCCb%+*_pmc-pQF$#1$| zC-^>@{yOYHzun=sgWVt7)<$nS{v5P9E8%>M^Os46hmvGtJAWnS>*&oe&R|cTuOa*Q zC=++>!Rzrlm52K_aQ@<zPV#d&4_bG4Lz&y+nYub(<6}l}sj9S5pIhdfxH{9t#79Zq zU5&?1P511aubh}*yzxT;gYaJ6=I@8N{Q6J!c1p1K8GPJeq|f#770bj%hK;XObNVNL zy0?A)%IkIC>c8)Q{$KVGYbKMm44-!HsSp0ob;a9Vu1pTqh?cPs^4@0Q&-5|HnpM58 z_;}#0KWn46-#eZe#Q#I^Wipdn#LdShCz=;8`#XjGq@q^SXXbnkp(PXFs>*D;dRt`D z_jZqosx2{18w3Is_FwIM{jE6hUgrU!)CP-7b=zh}Os#G$+TdBet^EaGAK$0rWx_Kq z%H8t0!a7%>B4#rS-zQGP2d+z4j-L%P>SK)&{IKvZ$IAoLcPSRW=oEHV(6@+@Q9dGI zBe;6;_Pbx$|7LWWS9-qpJ)C|duGwY*cgrlZ3DY|ia+4Ap7Q2}L>fdzns`iVcX6k-( z|4gX;)u-zl-M;qnv&A{bww4}xqG;sug`;OCy9W0nrh^K$-}ovItKE2)@-XS+wx?CE z4_{MI2#epww=p!^UQ{c@;O2>D-UdS^%_Q%cHG6jCKbPs?sdW?Ss{4BF`Tv=}6lJGn zTyo=(&Q;M>;bvssXI$6$Ya(0pL*pa!s{b7_y>w&KOXCiKs!PwyE_U#J>DFbo>5z-! z*^&3V-)`Gqce`Kwmsn38nx|NPgRAo0p3i>9+jgEy>hVy${<eG18RKs&7@y4Er_AIh z_2H>(&B1%m(&ue)nNi4oGD-1}4U6ZtZ=NRnvp0OLyzj|<aPfSP{Ql4Y@y~q4s-Q8P zPnLxoGW;AOuRUZKj0z%S4Fhh5sct%)HRY=1Ij+Q`uSMAwlz)A`H~d1nTEVuKf(VP? zmd<`rDZkeqtg#h_otsimYR!6|!|&kpw)Wo4IWx}8Ea^OTquM9qURj~+r;{xG1$7<S zPnvHyHQWeOZc13}ykzs!pZkR`rkI{G6I-1bw!`O24STv;>b6H~F7QPk`=@537N?dN zt^aFM`|Am+zb<RP@Y|VFyzx@I$Hn(wx~B&u3cEP0*&ypN)8|;2`n#LI-fU&w>%2hW zqtCNB%}QC#&Pqxtx83VXw&XrCF1@hcLU~c)gSVne%Picrr>(fS{feu-;8*2S$(@?H zjT{k*uUzk5tNeKLRg2!0^Q#xwY&$!})r2{{yFj)*_x<hicae7#{4GKk$q8t$-;jIa zntbX8SNAV0mmKRO8+R0&H16AyeDz*)M&;`**AHI2%~#_1BKm~rM30<{+Ac>svKl#e z30z&2A5jpovGn7+eESF7J3nrhzoaCV9nE(AP5P_GKgrrIZj$@YxPPm2e1C=Cag)>h zg!>P2FIT^QAoW9#^Y}H(yoR?9EgdnHty{MB?>v|OrqitAv>p%J3zz?f|NeLya*J~a z3c7ErIykGi=Zk#w@(D)g-ruWu-23zPiYxtd7YWb5$oXf+;SIU#>%MLNzgt{9d;XQv zk}m57{(e2r&G&=vnb~@QgUu_P0u+}$;5m3$J)m8<-J#Ge;r`Ok_A4}*XP@k3JehUv zf9vMtubxYI*gxFLUjJ8@x3Z(?VTr_yR?)DwH4h5z?TO$tUa_)yvkFg*(>GVeIi1PB z*_Iz%b>2;8#)Xbe1`7o<Q%q0Hl+-R`J!`s1a!IRzgP8(XPN0HM*93(*^1^n}nif@a z-Zru%c}|(!!Bw@9_ee|kWA$AJOG{F}dbe<%;Zxbtt|1&f<BESsKc~>cGZX%vbKwqL zl9$tP)w=9}Vx5QRxn{G<);|I^9sHFF!a4sRlw6p-DbxP;%&-ohfWG?3Z+0#XZ?op` zSAD)YNpf~u`MQk_59Yp5`RZ(Qvo6_NxTsS(U0cM-amox11{TRjZuOD23a?+Bw*Kp~ zq_u95jsZjVoV|ZJMU-Z89FgC6dzOos!chU4oTe2r(PeQ9?QiT_R9DirLYUWegYGNU zEVjlZYw?BuJ0%+R%T78qdgwlVyhBlc0^<o=tB9s7BX6}&ToWI^%s%yejeMZA!7}?7 z)&Kt~tUAWjwL;qBQGw*?8;nmn-oNP2EspDR{*g1;QS3$iwvR9P>tomW?QyO-FnxtU z>4qB%<2>#><VY2eRkc5EsKC5s`ui7W)7M|%%b8c$B70F?;0LoH+neUK1s<lir%cfP zB&IS!TWr}4rQ`+=&A;zO4G#3Z;6A2a%(3m&(S{DA<ax!(TNodu{pP=P<|5w-;Xn_i zOD_)ZIcn+QVShbRCh2}y$ATtt7XF3uC!X*3*zl773!kR)s~21Sx4A3EvvR&Y{A^=w zPQT^+&1GBUe<!|Mo<HOL!A28A3qL4*^%IBw2gZfQ4`!~BJiy9%P9~k@r`DPcTsD8d zT>d=SIVR$(v7>{GBCF55c5R;pTl%k>>@v1AI%ssz@Mm$q$sGPeoCcOgD<7Yk`C_O0 zkNFz>eC==9x4rW?zfJbGq08+V0)<jCmr}p_NG+O?BOb_is5V+&Sk&qx-%N#Fhh|#H zDBRY5!6@cpW+8rLrt=1+TTPFb>uY4!pKIrwT9aeH$X=yo*NhFC(zc4c6Vj9{zi3|c zoFl5A!>HstH!ktDxchl)KDk4zoG$$5mI^LCFmnTM#%->bEdd4hSb5%-e){mpuJWLW z`e8GX^miAobNzLQl2O^lI8m+0Ti!|I#-#LauH)>Q3-&8mCNwxZztH}mn#9PNl-*Ny zxqgc0O_yg1UN;!dO?+$fhE2xeJNv{%^CJX4Fff${h+Wrg-rRGIG5Yr7xyCLon>Mn) znQ2x$*KV19LCO{O#%1vvM1OVex-i>L-Rf-mFX1Au`;++QDt5*-vNV~NdF)@LZsu|~ z!9j)d5Rd2Ay!DONzaGR`3LlGDCci<j&-a4cuSs?7DM~$o8`Nc0=C7C$zQ-a!Ey5vh zL*g@@Q(H50=HGt5NBLUVkDT`tr|tP-`2N9x?z$sv2PW-X+WWwzN^H@R?fh;ID=Hqe z8s$pm%f`6Jzma;gk@by8&?EgNtlrOp#CRV#{GDb|EWgn2(gt0=laFMjzrAw!pP2uJ z)m`R=z1`*dEuwQ3-rr!|cKDF^>=eagKNRhk@Jlx;G|FG!5P9oR_s#~(9>WLy>yI+c zeN*9DZtz6z`i;^9cAe*p><+ZLYNS_rKF;O8zESbZv27dbZ>2t$y>&z)$u8-f@_tb9 z<*uNxe1?v>MgM^=x#z(XHV8d&x3?5O;?j2c*8CguUnNW`cqE{q#nK`5P^aUF;9JWm zCPS5|17*g1VoH7?mv)D~j5qI_?0kQM$u@RHcbSmwD#z_@a~mgY<UMw6;^caECYOU9 z6_>vIfmW5)+cO<aTX}HRq^laM&os51U|4MtmYuUzK<s<}g$eyr#WO2sUG8*#P~p;W zf-`!lOG$^7!?_!Jwi61}x-~d&uUK=s<B^>2qDqC>JGRgvP75;*k=Y(HeeE1g|9=W{ zGe;ZclyAIyc%}v4{7<tZSvjLyT29QhVCj-#<SR;U&Uq^1`;bTK_U)XPBHQfFUD()q ztW2*`p@CiKRC;2_g`^h_Kkw{))ZH-G`K|Doqv65JXMzU#MZi`Fnw*QNXx;2NNA~f= zLc8L!_RS`9ran4g?*F%NT_Y#=@ux==3$_SaOxirLeUY?a=I_g5t!IxPdKw_6|82Pm zSM)yb@IQhHec<8wKji`%W)m&yPgrg~teA1{rkv`k1Bq!$Wde5d0v<~}>1|$NGxJob zI?8I;f7~Ef%(1^Q!E~Nia%$%Jla=*mN9v0A$ivnG#3lcps;lDt?$7DM`zYh(f20L9 zwoSA+@a&|`wqSl0m-|0<vdL8xpbj?gZ*thER``FaU%}fF4$!bM)b@fE95O#XZgti@ zyR;lx{DFXl8fX~aC!uUAlJW-v8c{4Vo!U`0v6)a4Amh+|oJ@U3WQAwMs-g&Yi7~oA z>Jsx0pM^3SpWM`-TzH%%EgW?WdXejai8~e@y*%|9$_&B`g$EvY3>$BTqIhXRhG2op zx{Qw{OF=6a;I`7%*#|bN7e+tcx+>jXQ}?h*!O@0`xAtXRy)Qj`-rAnd&s$&YoxkP7 z-g{p24Z=@-_-h+mxO>ve;9GA}gOWZaW^PM*6Vm(ZmAdBd4V(VkUS8*N_U+GZq2uR& z{@(le!KrPElefJ1yf3J%Z<G1^3v>AECQMsn&u3!q7jovzwM`p?m(H0}E9&QWx#vBb zhh}E=`sHaE&-$6C?n}FTFT4Gdrfu-$eed*Dm&tjpdo<I`*861lb*a0PgoL+NzPj^h z<?_p?6dwru2v;sR<9X-W&F-z=&L0hOn6M?gf7ySF2vLze`X@9cXZ^mDE>(0~`_`|Q z0&D+Q?O?O})Awxu*SS+7_jJa*c~JQ4*Te9ocQt=+TRGwWrN`ejQg8d6JilDb==c4} zsw&wBAAG!JSN!q*ds+5yGvD|XtcTSkgr;xJ-7fae#A&AVb~*N0^72~eB+a6{{3bIo zeG9cv^{wxdP+$5?Z`%$@`}&!a!tz2Q&Sw2yru$}lkIq>+Q%5hWf1Y>d?L2MrwE4a2 zd=XL6wPzQndvsqD72}+KP7##uL^zp_&pDDid!vh}UzSQMt7dKO9If}$Lbe{V>;3q3 z{(`NCm#_G=^KI#|58tnD{U5z5=sioWgn_5^p1|ExH%47f-%|G9(D~Nu>~ET<>uwt7 zuDtfo!b!6JPp1CUeak(q-OgQ_Ww&_BR->}_dw-;;evwqp^}ckhvv|wHcKao{a!K24 zUAx(Pum0MXyu?0y#z`AhJI~Y0^#ZQu-i-SFTjZ@*PxzrVs+SgIdd=VLRsYW}&g;yD zWe<Zz?W<k$Vylfrub0ocG(FGc{_BwSE6dJCpZaj?ZRahcf<;>@HechDSp0+ccG2fG z_m+wNb=NMt)wK6lmHkrh-EL<u{rZ=@bVtcl{|QYBXDc^l-ZNS&YY^7?yU=Q%@9nKC z?|WocH<|slbIojC?&bNLX;1MxuiW>>VVuEYUXj~UGGCvVdCTbG`a9f_mv$|jJoU@< zqSXAp%T~?L=iRB!w9A#UajgCqGw-+U(dAnYt7RX4z<FuiUJG_DkH(ujS(ML+uP8cs z&}*a3J@vP9_IYI2%c`zDcew3R&82mopSQgHeSd4-6_Hf6zqVSlcH}fq<Da+UiR<gF z|DI3uy0i84i-|v<bFGy!aJxP2t@PW|v$i_Tu9sPPwprcNdQws8v-jI<qihq_O#3Rm zH1>1Y%w3=Jmw9-z_gy_V-`#U|(dp-L&$h9w{hPby(l+%OOKz@C61|?@Q<E@f+S-=7 z$4f0&`^0Vx7N2GEQgdaLeaWPx-@T!~R_Xo>71-!Ich7+y=CwCBe&6NqC28%xy88dZ zt;uO#yT8A+pKPEPDZT7m@L#><Po8`K&7Nt-E2W<6ANF?cq^3_|5t@;=wq`A!b#=YI z>QlGp*K{NFbicYhy>>kF{FOaNosxDvYV!#@G(j_HPwz9<)grO8)4L;gPu^Pif<={Q zrpnu!(pC5V{8q^<-t(w__JS*&pFfx0wk>bnB%T{~YDwiSvy??!PG4RolX?4u<gGjP zhwLKWOPRYzMn4k$eVk{lr0wM|{@*6`M%~PxVsx$ggV(7Gn-m{!ZDBpU&Zf3DDCz#k zOKo$b%yXKA{>@NLJ@w^cSLSEYeR^@b)y__2TFNF~diBdc<<F(1^58kw0~;L<=(Chf zS$$@bnQrXb%e&&TwYs8rMTtsZnXu^a_pmv07fU8D*WMBoxwZbGpQe@1!bw)&YMi2W zdS->4UlRM-PyX!B#2HSjr-omfC(>GTEB>y1rQ52=s{F{BQxmt@{aAYKpIxZt*3+5i z&RK-Mxb7fpSLJhSqq}l`-MO5%X-8Z#e(kn@y~lf(=ChC9Qw5}#uhjoO?fvbCZK6}| z&RSJEpZ9WR^1ZOEs9kMy%J*lrxMf$nY&f>;=<2(<(^p>9)SYUR+WG3;w6O28%gvV0 zT{73+|H`L@##`68tvK{5G;H?dsI05I|B7B)*m-N~D*dCXS$Ep^uezE$eTCoRS5bL$ z&ho@ol~re6P4lh(DBU`}E2wNH$5su=rE`m?WnWu;Ut^Vz=G0qrmb{gny}I%|`?qbD zn%=>A)xNi<uL~>qS`@WYJJv8dSVa6maIWX<_@AMl>;A=F6g`#k``6v9qVq?)s(u{W z`?GV0PW1AR`8CTo-R}u#(RauWdm9ke{_)kN*P5Ev0hi-{dqubVg*q&~)OCAn^jhtZ z>z#Y&O+VG5@vU3%R;*!6sMWS<Mw?6@Pqxb{s){z<y|DdQ-9`6wwbYf*{+|oHbB6WT z+urphhku`!SY-3=T6jp&RX<jj*33W;`MKYp8V5~Y_37sl0Y?Ysr40_L#f)vQC%f5y zoVdyTOkmd3pr|i-@}-qO*j(do26D^)cG6Zq8?tjo5Z8@e(Yseo2|ayJ-{YzGv+c$S zXKIetSw?s-VhUaCV|g{_qL22y-)`CMK7naZGg9-t13p*lg};qHceyLP?#eXz;~Fc& zdYXiSOjq8?-}T`1bJ3X-))m^y1^c|bbSu&*z<QhN%CmdJHU4bdyo7bO$<JlsAw~uv zn->*tdHi^O>A|((+RvV?I~BJlE?e%uU+R91^&j-kZY_MqboS4O@Y3wdArk{9&idpi zuj<CKBFS&Ys<Qm$Ua49c$G)D|);zJi;evo`u7B9<?eBwJzHgH%|8MOzdsBp#_}Y{6 zzXkq2o*%UL^R3tG+wCvD<17}9+PForcXjsE>oe{5C`{!!ecYjV+DfmTpSXIuOdh9r zpI4l<F=yket@fVW@vl~%*`g!Nc=ehJ@2<zJrWV`xM)LfrI4r#D+ur9QJFD9N&9aPJ zdw<8_DVDD%$A|2THEzwnw^n{$owx4otzn%xE6>NCe{t;k{D6#^60U+Rr#UBt9e=re zd0hBky<E{X3~P73&5Zr{>v7Om3E%%a_8&Uk8}_x>|LUWE!B;9S#mxQjrZ>v?pMI{S zx%*RVlVG=q32C*zcc1(9N3+#^+Jz^_?Sh_aiHZhly6wOJb=NMNip5HP`HOzmx@l_Z z`Sza6Sz0QbroH*OhwHV~Yc@?yGTHe2^edw;xvWc5vTnq#fAOVHUE5a0<M8^bpyyv} zyjDr9y%^6I@NLP<tUC*rKI@&iWEXe%>YIDRHPmf`Ex7`M8baKHR=SmL{rWcU(l3jZ zX*c&u?DppaFI&)1D#-D?^Uc=F^Ty&!TeW7Z9X%JdVpFb^r(-p9)4qnBqwoC!P8t~o zMVz>|Y&!4sQ_qBUoDq%Q`ek-vR^?aARiD3@dc|)FG|sCJ*0z7UT=?Fu(tJ5jPi^z; zcP)}xb;ns(eLJ8!%Q|b)sdrxHZ}MKI*NRS@wf0-Q-QopqSGWB5D!*mNb%VLr7B@R> zDhg3j_joFOEp&4EyKAfXe`g&JSDw1JWO7;V&1IYR32fc>-Ke!RcX`>yXwkUsT_SR8 zg!8Q8jqTqq&fZ_^w<<Pn<HLhpy;JmRe;ZECi1lxoC{!c4N@&jYXEO7hL>Fw>W9+GF zTr48zvzvLUq`-nT4;-$(y5r?L|Ns0cbCzb_l@I<bcJ|7b$Lv!Lf9Zd%`du6vy&)=f z)^*dj=VomV(w?pp{JnZz@J{LTSKrwQoBTeSAEK$@;`w@e_QMH<uL=&kYJI(MH0t+J zhhB*v?RvWxEe_!gpQ8Ox(r)UV$xDUv-YpT%+wUR$?!Sxi^YhamO<Jldz2*6J&sSyl zuX64E9COq^e%bzaRqm_z$uFtmk6qT1F#ptp6`64XeXG>jwTv{qS4DW`M$cKXc5&LO zXIr0V9G_viN=S5ZkgxxRYxk75RJ~Ta`|EafQ0t{%f4_;FyqkXT)u&UjT>huSeBAWH zG8==cJQ|poYP_O1xLLcM4f=jOKkVe{T^U!C<Fk(WA1*m|b9u(=9@eZ2OD4{;shle^ zMVGbJ<LRQD&u4xgJHe29!Y}Ce@%Mpq<(7M{-j?}m$;(%TuUoZh-T9a9zY;B6)gSQo zv$aOqwWO&TVczQdE2k?tH7`BmvofittWR?7oD&A7b-$!qOEWds-c`D~GEHf+_r3q2 zyHB^R{L8QZ`pBh=QNNC)M}2zfu+{!`3M&hXmW9TN3F1>Y&rfqQ+B|jl6waWKvI$L# z7F~II-Tdg${c*3B_6R9j{h$4u$Bx_Xm+Q;W(;CIUj9Po6SLylR(}=z4**))vkM@4a zRaNTSubukYeRW%E@YUKsQJNRjS=rxvKTy$~w>iKx>-w3asnt!-`erTawy$+tCARii zsdUDL#rd<VH`@0FT+d5id1P(+?Dr3!CYx-0?@+qyBbV<hQ-`&WFD;oMur_+9Tdw?` zIPZP3i{xU@hwNN^-r%M4X}9b@4+O1#FI(*!y>rK&_a1Z0qtvJF-@DsWc$;--VYAw* zH+#jkO8nb)*;e*mS-CK(<mCd-?R!)@Qm(433^^<u^8TXq5{As5>+-i|{wQ&suD{r5 zsrSlxFY{MsT|PGJ!e{l;cUN2@6&qJoR;Ihg-iv(tI`qn`dCXV8_C_DwrI33pXX)BI zYfK;i581vh-DCUQr5>Kor|K_nz0VuC`FMVSgU1BD9e<@-%~!8nw)Is;#`*bIbu0qb z`rEl~7F!qI>!;iMZ;i?7u(hl1c86&!OWS=X``vnQ7ti7phfJ`LxOIM~*VOKbT2oDL z3l(x-TXkw``n$_V4jfRJ`k7Tb$$#1u=PRn$8-EK0Pu-g{*(qw?f+eomA_5{ytZc1j z-%j*hy*laet*G5eTeI@|ub)u23;6l9lS?N)>gzcLDS;I$Ci@u(fA@S|c4mG4)uz-{ z*SE}Tn)P(v$CXuq>bbA212sQ~Yc41Zb(Zid$;)H1SB<`M^^1^Xmcsm1+ig8xEC0&4 z;~lU0;PpSQ^`V|~^Y=StMTe_0H9g_xHS%lgDl*zURa9(+kngJ}*QMtOFA!KHSUG9p z<J-HHXU<(bVGp}SQH>?n$uBFTJ_{EmdfZt2Mb~<(ma?bz`r4R@)<L0)cHX<6uRWz+ zc-t&5G$LcgsZy(xFJv$8TWNG^T7XBughOU;LRQMIoMl}y$L#F<g_mYeUUEc5HSflv z4E|2xOy=g0e^ZVv>zR{gI(0`uA4m7vn4MuS1rKuVT`)H@`DL)Q)}kubqq=d6tL#pS z%dFtoHfMsuN*A#u+6P|TUy$tGTk|S7^QT(wJQM%5ySGeT)HHQx*5oV|)~pl4`I@>0 z?uM(&v=w#Fy4(&@ZkqD)^xgGmxh5F(TK?El{Z(zM_q4P+V~>}A-88Dx7nS|EwB&AQ zR>{4Uv#wX~n4}-GXi1f!R&iqW+=^U&PNr$<`(!lLj03jLx@sP7wmx$GrjIkDuI2AB zYWKf(ZH>rLZQst@Nuh5K@y+`D&i_{3&tNWH`L(Z}ZwX#JTR&*$<>*Y``Hr`KJzC!N z&%{l1b$H-m_H_X(qo!^xzRDJAzkDi_w(IOqtLNMf%U1up^vPA}EyhY7r;}>3gp{Tx zlm{|$@$d##IrMpHI7nLFTy<;1HpPOJzUXa!51%etTJ_X7^6dPb_x7y+mD&@=zFh0? z?)xIYP8+c1_=Y&7?W&Ymc<JZcEq_i2-um@iXz7){z_WR>S01#zGfK>zZ0){r_O?}S zo7tYtE_)v+r1;*bGG%*UDr-}C-<G7OS-Q73#aTXfTjl1~5hOHOG-~gj5KXVt?2yn~ zho*X+I}y}-YwHrNi>D&?I8Hw97wWM8q1NnKi?1v(Ni|=S^hRBiXZQEa)2p^-6<ikG zS?aBIe^ci*-gzrF*=ZWsxMWQ!f48n=3X}4oC$~k{d&D<vin_8nxam-K=#;QyT8Ytf z#LmuGdv&_b<!J@;Eh7b|OjvVH^25uVsVf7IUJ;fPS#xQrm-Ox@P9<d%lrC@HQub6! zH+Ey-$;a+mhBubCNnaKX*u7|0>hCF29|u?5oDpN|>?|^2rfO=M_H4t~ku|eEdAVuV z-R66`^xWCU|EyR*LwSok4#cf56Vcx2di-3>5-DG)CsvbGqo>TBy7p4(>f5&~-!rZX z-QPEjXQswEMb`41oO8S1{>xh8R(C!8tzDHz=waop>PMEptriM#3|(51>F+kH^F?bK zyN0!L^u|QH)V3w7t3qaMx^!7|qxO3PQ<l!dw=O>X<l3&Ss%ZG#CU%9$R$IrFj5ob{ zIKw*+Fle4$-m#~1*A}L!f+{l~M^05Ly>*0tYvQ-0RG+vtU!9k&I)8fpi>~<l;aMjQ zj&4q#TK<mx-y~<rYRlP*R*o`i?m?HP&YYVnV-bJ3dC7x4?dDBIOd=;Y&OdPHbB?4U zW0$V%5-Z;9)qifT4OO=G$h;ZpWnp<<(A%q*C(O-b!4$VG69ZCqHC@^yGU;h>Q9#~L z-YC=1qkdmQ4V42NS88&t-}rv!kuwaM))oN|D_tf(n{ujI-&RfO`b~8e&#PY2Z#GQ| z78QuSXc4|`*(Ih=^G<o33`xrJvMc9$TQW&$_0;Qo`s+i3oEBxxEngLAsB97vxzO=d zL7sT)G`)o<LgsIqdP22sWuxUOo^!1INoTzqPjPaGgk>z4RhG3>Xsb4-Y`H6o=hMX@ ze^+LQ{M=PM#inN0HQ(9GSI++$xN_6d(B1BNDIH$kX<kc>nLbbB2<mJQa7|%d@vf71 zYN?lPDDTumOhqE0TNf?zw{a{zm$kk(_*us7Cp)8l-c)dPb6IgL_ioAShW@Fhd5zo7 zL@tuu?|XGey4^+B`}>!@`H;YRL`_1;OI5MZlj}39hN+Q@rKR0eU*3R|Q)0Hw-lra! z`SRr{w)YJ>O9gXVPAFIg1!QzIZC8If&o<@AJD-V*yibUWtngr69k)EpYhCz77TMLw z+Dny6ejUE<ZF}zWB+bntrd9jQ_WjztDqLf+knOGK)4W*>l<o+4=q+hw*D}%ZIn`#+ zKC3uBQgEjgOMC0fZU678#9lG8U;p>Qn$stv%q!PzQ$MykbGO*DqN0cfy{adCFZbMe z|4dPKSAqXB5ur6YPc;^Yv}_8VeAaE}$tkmz&3WNr>{oLoTYkldR83<I_m_!^w<KmR zezPUz=5eh<#!F-q8@u@U!e1BfUhz(O`tv^H^UjWqH-o|)`Ye;AW=>t4bIfgC%WT70 zi$8oQh%I>ke~Db~x3lx-EIsf*A@%pB&tG^%p3Ha2bll*3et{VO`E*Ss88zryVi{?c zZ|5@HckknwFC@M$@N3x47Ug&AUAtC>`#wzd{=4=W*Y=X&psSN!ef?uCReU^Ks`&Wn zUyH-#L-T{;x8B|ska3$$OIU2BYX1KfM>cYAd3kuB*R0J^vFGiBSH*aV|NF5?Pv&2v z-s``sPR+X>_c3(QmM6;}e(IgKekuRgD<7oe1CG7QT~+*T<*i*=dpFrjsPslGSSrsO z;I=d))N1WaMb?Qkjnb<E40NJbO;9<S;(c<pS@yBxwIW*$<Gx0jhE}}vc<SP)ylIN0 z=rV`+zDa8O6Sk>pX$5O#pDzs#C|Gc+efpJoGqs;eTgKPdSw|>VUlN^E9wBc(Pev)V z&9k~`>6Zje#rHE=&Y6eiUv#j1{U}6z*_us{gST2MdOp>DtD5*#>U#LgHwkMizNTkh zNz#35=`hhcSo8&Vz{zD_r-bcqx|Y)&+ROEHtD#w#=29h*sKobST3SJK`T1r)UmT!o zHPs+`!7Q`<MMW<gqrODtXFf7oTeo-n7Sp)3=T4}mOg()h<N30PrF?Ui{ha?LI4G)X z)0(MMIm1tW^WB<xDQ4=bSl_IW(_d;|`l)9{drbSLqONJQY>HUk{Ur(Kyqwf)WDcJW zRWWhrI@jpIzQSv^@YcO43u-oUYK5=eBy=q=xWr+Gh{U2D`u_ro-WPV|1o?_aUkmQ+ zk(t_`usCOZfS(%g+3e&qp1o6|ETdk3dlU5ZlxS$uO|wuh&Y%RBma?ximz3^x*Vy95 zdNt!}gyT)iuCAVtH=9al{k*B)b>1WFX_+ri<*J6KEv>6^o{L2p=k{&;YY?Tm|NONr z@7Y(oob}SWzGmSmx9LGQzcA!VS^0WCexChq!bdGpfmz-npT3-|HvV<hVsCHR+pg=| z`s;&lov3_Oe^WW?%fIuh3ca{pQ=VyL6fb%ezG}^xs~fp=Bo$fNwBD^2?2@$zTq+$F z?swbjmW1xlMV-GRIRkpFOhtBEeovhD;gqOo=%SV~-+AdL9<8ere%gIISxkBBt?d`q zFfLvayHoI@?|$D)f9#jdYC5$od)-vy=h?3hg=$?kEYCJvmaW|@ZRgeHZ=S0A+T5-9 z_}LSwOJ+uLg>6{1HEWS(vbyI<J59yl;`ZL~wZZ--6Yn2cqH?r<b}q-8cQ3oA2+1w# z(0V#WP-4Lj&tla+=QmzgFTJc^dB}44ieoF)wer5?WFGx?=a_HAimUQpjOQCWH%}3q z(w*jUPw9Y?z!c@BZ0xU&gsEqKJ75?T^Nv|tbHC4pzJ0>JMi$a9*F3pZ)K`<J$n~UV z+4F_-bZ;kzZu47r_tqEasVXwQEPatGdfC}?9&LXca;0;pO5WEss;lm-O8j^2gk5h= zjPG5$`mo7%{~~WDi_dmGQ+m$U_3gXeaa}xy>-*;0EOO2Nzv3hR-qjC+CTjhE(t2rY z>^IFe{e?>pU46Os_m&r}aZ*+Pqo1vbJ-d6C_LR4iPTdumIAvX<vzzwj{7J92WUb2k zeqU`WM`%P@-=(JFEt)#+y{d2dCT(ge5|N*>_`?d0*7Y}5&5Y*@5As`f%6#o|?e}~A zx_H7CAM4$+Fs0JB=5_Sk2urWki;K4`E9tX}UN56%x@YI?e>H2T@2h?nTl%7AmDV)P z#R<ae9-JxfQ`%ItJ!G+QvB=zMi(kCi8~0?(@w{tS!uMY~-@jjds?I{L^zC7#N*ZCW z!h^1>=UtrfegEYj>G9#a`Ic*KOw&|lOV!e_3Q4yspM7lKMQ`D}8ehk$Ng@@+;h}t6 zb+oq&>=P{IIcvTAxLCSVCqruc50Q}lldSd??_XB?dv@EsPm2`Ia_8^&OuTz*#+lGO zTmNkN_bU_iYnDBmduvt9+f{7ltM75%wbI;oKjo_B;@+EHPpfw7t-bPhnfKS{-w&JU zW-d5%d)|Ug>CY!Q{mGxj$?;QUflMmv(~0Z50(bp6%5L&*Zk*JwBPF5Q;f~AA)+n~J zXj&dwep6Uo^YL^>wMVyJ?RwYsYC?ILrvF6Sg_gG@<}METVCL2Gx<~x|g%$mE7yQ5P zS#^Rd^~#rH+#&i}E-N20Mj4m2UHfkoAbQAa)w8MTD}26Rp0dT-?_;)EcFlVGDPj}W zUOM}F<!@&5S-BUUmB%mrFu~yLzdiGff-i{_r)h89{XVLAeahigTDSj-oRu;T)qFi| zRrG1IS>`p9oHkCJBlDA~XiDbdnUh|{m-anM-k$xf+*xGSthKLXXRkijI#)Dq@tRxv zD*MywJ}eG;KX28FSyMxnRpx3cMXWxwd5Tp^)24kA3wkz9w0e4sTeLK*_%`pey}vZo zGq0>~4(YGs<GvKhAG1DCzGnT8^}k*msZw82#IXF!rqHL8^lnM+j%CiP@=;u<=(R3B zaQfZa;Jiptuaz-hySVfPHABzT7A*M~8as8frQhHDDzAqJ9lO>=JIl)NGc?~T$!=%5 z^GA;Fjjh48c@-fSUovK0SUmaE+|o@yYmZF$5THAEdqB0G#L@+ymR|XKGJ1)S<Ek06 z)?WDH;JfRu>RY}~E1ycf4Q+HyY!E5_{`1%0mF30TbF|%G_xvt8e>T+PoZxYx3AT-v zUf&Ij7Tud^X&1U<&E~M<xt9+_&kN27*x(hmc7fUX`)Qw+9RBwC*fOT#DO|!TlcHB| zdL;Zk<Am}1*G<>urj)%44D#yoGP_%yb4aD69x{j?=%S#Ye*Ay9U4K&Uni%HK9L!gL z{WlAB44u7P@Mh4R=^0PdpV~c@S)=pHxb)rbt(m7be*g3O!z=GYP5X-P?yt+Qym$Vm z*K@1v#r&mG`zN%w*H`_w%9Ui#-o9Pt|CVoY4vY~Mr?*$!`0~v<^Ns2MZL_QIoPV8t zN%7vloAI-`v@XYOF8%#{>-WE3Z`}VHll$#e{^_s#_wE1W@q9k}T!H^KF`KJ?JbyTE zd;FfapW@6}pa0$X^~dzo*B{o~|Nm9@%hFrw%PaO>A7-6iQWaXPy|Hq|;nkC`PXFHY z>bcd|>ZJ;`{9$R^{O8A(^}nn6Z@IE+>7Kuz?-Whn|8wuDX+2>(ck6w+w(0xkt9QGZ zukWc!><jaKpYEmUai96w{p{PP+4}?iin^rYY7fTM-cbin-4{#|yuoj5<+Zg+z5e}? zss*o3JU;t;{<P{=&2AI5we^gZ=aw(9`m<j2%iSrNd!6ELZqwUWWnaDeTb#P}eQvM! z@ym4=FQ|HJ`@iVV<y*V{Uip@GpS^hYz1auiHdnnbKL7Z|&iC`v>T|VL_*9>r_J02L zd*9b5*I&=I{I>6J-u<^<|4w}#Z~t@0vD5ba-*YOr)x6(r-oHKOed+%%_e#FaTYGr_ zzdx_d7T&MkQ1a~QUDLcTJNBCA$gTTv{b>3B>?O+m^>@wo*Z*qT_r6%yp;NJv|CfPv z<*BD@V&`=BybUd%?;lp~FPC38`C9hpd*^>&z8)=>xu<f+@ofJtv2_s(+n)ceRZo!r zdq*4`5CKOxWNv%J2x`CYDh8dFAk%ne?rzaT!H<u+)NI@>aw78Pggu8BgstCk<W74{ z(l^`t8TtQ{-%CcG^79N0c)54K^N;?QkF?4-ES|79Si{x=u>1?=un_t_|5e`g2(T>= zz!T>GbU9>hzr9k=owZIDJXj0c0rxMFMW*w2yY+$4_h5zK^xWY5pg^EN<y}EQaV1m$ zqW;fekm3C04Xf{gXIY{1{|6p|=HVYMc=ib@0NO0~K;TEaphepMjSCIlo;h`D()oW6 z(h`l{sQU&4OsKF#+gInh;Kqa<KR-Wz{rS9octXO0z9;iNSXfwQ{PDN_I%QSp=Kos1 z@87Cj7mMWfKjDHJX8VP|zuA0#mG5k`rZcH2DKcgCQ*<JOOtY?B=##ZBdB69&T$!cb zV({=&0b~(F<D(vjjh#mXYj5wV+#D1XRPyiVbMX$zCwhj4flIxohZPhYTvh-7@Ap+9 zD<>Hl8*}R%jzw7(@gd(~L4w*B{w-(n7G7H!?7n<%*)2OMhkI)N^VZna{?aft4K*}0 z-1Tf$_LW_wulLWbmA0+ga?@|?)3?DBem_D9<9P5kOV)=o=gwU#sO<LQ`TY8@zrVhI zeX+QIRm{$!TU#<GSABn%d+q}0q6e$5SAvg5TA-~B(dH1DJzt`+<)-)p&;dj_tMy`c zg~-=@XpF0P$U0R&K5oLc%*)GOwg~$P1nvf%it~I<ai68PyIu6Lg_Er2b=}=nx*Bv| zZk)7vUPx6{)x?{Ow-0Q~xp&jb&)Zvj!=Jaew`-f3ee00>(9zv}d75rCm(G5R*gIRZ zr%#$RscW*=@u_;(+%~TN@uMQy_=cf>mH5;*H#fWA-j;hizwpNEe>s)=XZAg4%Y7{< zQg~(4t$!cZH{4}8W|LmpKXrq2;(768=?7tVSpP&UF|HS#`sU8g%WoctgjTX^AHSvk z!&r>d&7zP)Mwg|FxAX9Wx8294X@z=$j%7cR{`S_^G=&eF6+>5r80w$fTUghZZFA8o z?Tke8MBV1^&Vp*!r|CwoN;^C2nd{y9e?Olu6e&EgMZWGwV%^Ovf!p(9o$da<zOlD@ z`%S-pkGl2yUOW$zt9ZZ|+&A|K|B5^I6`w&jYnR`z{m!3N@F(o=+wJ#XU5~Ff<(06j z*-`iNsrchHpi6Dn#_!+fA$80`iD}<{yGL(cf(|MYi**f3ROFI0%b8&r71*)lxXH<W z`+pj7tej={_EwjFeRFg3g4^y-Z@J6Ys=Q%(d42u-HXg~MkXM?koFYB$*Z=>!>%$@L ztE<D;D``$Mvj6uZ`R~{3@vcE}iCl^6>Op&ABHrBDx%p#a>&fSrELiy18<Td=y|u`d zd++CS)}Tw*K|4fizuipV_4QixSI|W+p9_7axjd3G%UPj5ufoYV{hZC5Z_l4AUUCR| zaaw=>nfvovPlzsUX>EPkrM)h|#ib>3YQKzS5NLNP=s@&soh!Sq{>t5cH%-m>dvv*L z=fC~yb#nFvE_Pd%7#>%-v@$(0c#4zAy2{VbcD-CSyKB=8Wi9SY5i+UQ)<mv~+gsJR z%6G2SRe>GBewIrQ{C0n=WGyyF-ff0;_wEe}Gi<B3DHhy_3FP4Z1G)uCqpK*@f~8B9 z>8P4e_ysc!m1Q|MH&uN;Yks!u)qzH4&>c3J=jK>ez2E!&%b(BZrJassown<Z3b1T- ztUJ~tnWkO#@zK#XleV_DRW6-EttX>an7j~`lw7&}e%<XkPm@D?{&`Q+xp-q^vag8H zrmB;w(`RgRdZ#e$g{$2neh&MChjuzBmA1URnYAuv=cbu!I%nL-)z#;739iW6?m1bF zi=RJyx?XHcu;Y!Qr|OaKcD>d!&A#Sy@#4j2#{3f$(&yK1J7@jg=5tq9m(l(3wNX>& zi+?&$dQOK=*6PZZ<gc%;szq@zF8}{--|xKQxjrsz@>MSsP4n(VfDUTY;Eol%<t5U& z_EX;7U77dxR6g7F{rsn=r>}0w40hJ?Xb)y_Zh3L}>Gb%#l83+E@Bg0%I=Jt|gRIk) zpH8ZOJtmzm@#ezOQ`h6`b5CqOD!Qk__Q!+fpF1{HfbJ|<$-L*+_xJgm?7!bBuKId4 z{A~KC#r7(5MJ=ro=YP4$DjpN?z|8jc_XN&9_4zeLbyr&I-d|kY9vXP%^QY7Lv#UA} z1wQw;|GNZqSwp|=w;OiHId42$;#2TQQQ^{0ZV5p}r{h&$U(Nhdrp?`6b<tJ)<=^l3 zuOAhU&ymsj*(p}s)5v;d>GU|QZL426-{M#Y+A3DME&KYpFPfDbI1Lof%a-3sJhA-l zH`O4fa{@t1C#H2cb-i$Zw=ux_<&w!PD{Ee_-F~M0)Letarl_r1sX0v_kDM3TX#4+^ z_Iimm9*6%plpOC<Ja@CGZ<}K?+p@da*VZV`jPz-=y?);IdkpB-t(fos7Ou(P_w$%Q zbk2`&xAV=dc;#$v%(?7puCydBQAa;w!vUQK7HWqMhbK74H?NlUFTYp0yfUG?NOJw1 z!`}`F-|5)0iDOOd?rnKH9<pst^^0#%-{$fEqF-N9!s#u-vlC+M{(iZ9OzdL!#`A}h zZ?NuXIWSSQ<tMZCiz_Q9M=IF=`EdAH*h~InoK_C&B0R*m#DaEEc5Za7pYZK6Xm^y6 z+X{ARm+)T`HhDJRJCk^Z`+98oTrs_vjAI-VH-j?&{JKMJ0>5N-Z}__<=cZ9{?VTNk zX>SVVT4p~zHFeqhOyOdN&kDO1uxG9~pI4sr{Kc_6p4}=v>MeC|FDz`HY0n{LAm3!U zDdVD2>DyaZA5=V>nV$9Q%gblG%HM6BKU4TaWSQT>zh|z#a5*x??m6qtZQt^_itA;V z*d0Mf5%k>Lx8|Ipq9W)3Jw;O=DGLKOUSVODc3mztCbnBgI)%^9<d0^1->$?{p_D4% zvaN5$ald!%LFfKm`t|zggJTOHi>#0?I>eB6NbQwH>M4<r%}05v)aKl;`+fESvth!A z#}$QMOBB7DE+=sx@|$z-*Nv>SrbC4fI0V$p)LbtW^0l7q?QFe&ZSfp`yPrqyBp0P# zN_q5H>h^x07cU&_H{3eTY5ehVgvMmEK(+4|+KV#voo>#lOq{gL=2joy9OXpq*0KWL z=N-YGetu>@-^ia*i|YJ0!F-OMpD5GE6UzM(>U`5)xK!TY6kehFb4s)B$0iq}kBhaR ze)?j3j%~Sn$rQT<dwo{rG%r;C@&ChN{$mF>A8L1LY}K9oDU*Lkpjw}#&k5%gNu#E@ zm92XcI-8FuUi_cnX8nD+^%Ksfw&G`=xzYQ#-&d^sBg0?eq~q2rm3r#)HQ%3GKF_Ou zXXw|-%H${Ie%kchqDk^PFZtJ9PU|y%P_T1f-d(H9X&xrIM}J;opA(vz&G|-hcS85$ z!-12;VlQ&eX35{sCh+LWi|?Q-uqHSiQ;BdocOv;V*G(7qU$aamgng>KeB+-3`z@}> zW%m`XOc0-=pWI>B)=<=MmtfeM_wSHR^IU^v&(6)w?yGgUqi|~i)31y6S)H{`Hxz^> zh?S%nvb*U-DXhDpVAlL@N_XvIV>9-bNs26T`g^}j+Q}bw!~R#u+Ni5B%+aS3*FAV+ z;39H&hPF=Hl;bxx{q?fdc@VK>V%x_<Niz!%hUxtEjMb4lD8bZaJK>!6i{5<#`Ik0W zB%GPRe<xDLxGX|o!|j8QCKg2=;FB;oaD(Two<*X)j*sHgKMXd1St^?p7O8hvz3Hq= zk_nK%aj{;fU<aS(<6T#|Wfh)2I3B^EeZnC1mdOo1Zcn*chx0Ti*GzH0`9yI8gWZzU zoX5=vm}4b3e~Zydt&0DlQJ2`bZ^_*+%(=}jpZx4Ez1O)>S^h{sLx<^Tne|k*577k{ zc3~DgF&wfJE!1q^8D?&Jd&+CtLDw&D3RsyAEO@d<YO?4>twpI<O1H2~>N5CvtFt3i zL0EKk);h&C>k6$bEIOoDzvvRxp5-~s%(%0ey-#%R-ixZAj4eOi*FGS$+)wuH?d|5N zhMY=k7Ul2x=w@7K*!k$dj41*gpN!AjB!5T^dX|}}mKgTy!TBd=&4k__kSyk7am?>% z3(pX~$g*gTOPoRw$7J=Cj+1iFkFazderlsK(R}@ev%V^<e<Hpv)?b}`f@QLP%ft?z zBa=HX3wh*A)}%c>y@Ye&lZBEV!mIN*^zY|R;XQLeHoBqW(E<ez3rn@ixZlE`9^Kfe z+rj8^D_%%QsE7A)!Oee-PXsa*vWitMCcI&CYI)qWCV`zbvC+>(T(HAJg*lx$^HAfX zBQKI=`1rWnnfV^{>&^phXV*9DE<VZ@$?$N_%j6ScwO+qB1a~lR>u?a;(9R{QHRJt* zmbDu-8g@iRHysIB7p^h+gyFUZ2fqt%9hfp5*q@2y@C9`RFI_9Z{!B8bZ$+YG^kRXU zXNq%-oc=EmU$VmBkeG$b(SJQcx$3Wu-EiB%ce0&9VS;nPv1f`ubzbaXzsVMUOyc{# zU$3;CgJ$eju=y>#==K@evO9urlP?}P#MM}F*>2J6GwN3yV<%Xi&`Whb^ybdfcz*7{ zPoEDhi(m-i`^*wp*<#ti%4mP)T=&lQx`S?OZ`3X7KfrUzIb}xqFV5(sJ(vC8s2H%V zY(HVX>(;-|eZFGaH>_NwcK-YI`s|F9b&fN*XZp_+-yrzPX@+-4l+u$_nGFpK&#&N) zJ|5<I{?AkWFXBgLS+;KO<oL|=nd31pBmag}LGEvUGEA+m3vRePT60KZPps*uzToD9 z4Y$%#IXx4mb_yp{xBm9&5)fvaH}_9YWovDEl5z~6WXh*B1w)5!uY!5sQcoXoEaH81 zqUAu!A(oR4X;aU+aBfUo)OKUzRQ87p{ys@=N_9P<HSq-p-^rxsYr9Xjvb)YO5LAme z_ta4?$4X7K#j3E!BDdvt0^>FgVMAeMzy24j!rH$!)gHDj`J{h2<;?HqzW?d(^%kgD z=KXKnKL70z8S69{u(LKac0L0FGRDTn%l^M#T)$=iw~Ft1-}BzSnd|-j?Kz+F##h=? zmjs2LSCQ8%ym#tx;O>e|v#y%8A80i3j5PY*UX$jK=>F}j&UC|U?Vl&hJ{d<APuirl ziDeF7%oV{2RbSiLZ;Bdi>UhADaAIX@^v_u~ruLV$e-!eq+b*=G_h49g(EN(<bvNDj z<?k!Hz+@gN{D-?D+F0Y%<nl=irk$IZ94z;x=U$=9J?-`ck9W5=re^eA%0AeZaHDk7 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Ì=Es@r;s(9Le%1wz?XCi|oBVey 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 Ofx*+&&t;ucLK6Vm9mG`t diff --git a/src/assets/img/about_illustration_1.jpg b/src/assets/img/about_illustration_1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..33a703590c620c7311e236d1cfd60324a270beaa GIT binary patch literal 45744 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 E0KaSV;{X5v literal 0 HcmV?d00001 diff --git a/src/assets/img/about_illustration_2.jpg b/src/assets/img/about_illustration_2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..79faca4a6331a11cc93e8de5339dc754cd1a9e0e GIT binary patch literal 59980 zcmex=<NrejCD)3~GzJD=Uj{7(1_llWMn)k9W(EcZMg~R(F!=vgL?s|2za+mnBfmhw zSkHjL!pYN@MHMV5049+Lu$&c0fKifxfdM2BF^9XLC^N6bzqF*Fw1k0yffb}AptvMB zDL+34tim&|BsDd!G#4ZRVuNVsoFWjPAFMYxvjU{TDYFEm2P6$KGquPsF*h~DH6nz8 zfkBMHgCT%Hfx(r*k0Fd9l0kvNl);R_gu#G8fuWcom7$0slOdI%m_dQT2xI~S#OQ#c z{QR`w)RNKy|D>#B1_lN}1~-O$h9ZVshD3%E1_g!`26cv)3<V5D45<vI3}p<dV9i_w zaDCtqV_;x#$|=c%`hq8^G&83pGY@Py0|NsmShcfnq&Fl4twClm>7s;^5ZI@XQ1UM* z$;{6yW?*0t2dQ)}D9M9)oWVIKsR+&qN-y?>bDWFvoDrP75(LL5DW@1N<Ck7i2Isiu z=D5H)o_Qq*qn(no)1g5EjwA+#Aa^Hc1_p2vVF*f5P)NxyO>)S80CFK1yBFnQh&kn8 zs>z2?@<BaZ?V;0i_49UJ&7wlcIo4Jg7LQ1S}p<k>d~qQVGUfWvR&}`9&^?C5a#( zfE^i-9#9O80|pQqq%a^o0OV{2pVTynF=*C?<QHJIHaIy4A|6nb><|eu35*4ji}DM? zGg9*cK&d)2FCAn#NQMt25tN!-;*?)flAoKCpO+32W?*38fyscJfF#Zf6A#Kv&nQ8X z;D$+r<QKq&LHUdME!ao_1`lwmV}j8jITkgr93Pkj6ZQ=Zko3k2=CUv_Fhqe>g7PN= z1BfpN;xm5+%X5NBu)!cvbx;mwVBmt41`JFfQRc5;RUQobX=x0M3^{3O3~XQ#MySZb znX}*`pp3@I5XHa%7cm7J4@&t^U0^eRfK4c1VEB&}FuwrFW0m?(i5$poP#`ccFfcJN zf_Y3Jo4_Fh2{{8WpNSD73vxHeFQBvr4h<F#uskTF7#SFt93U7P>T+N?umxbVr9gaU zUwCMMxC{(a85o!vK#7DQm?4oN4_rW%Fk~{6fJ+=*219U&LDV9XAU8nyVEt2}95{uf z04@e~D2Sg|nv(<eI0FM)QhsS(N-<h)Pc8vj4R$^#i@?(%0|PiKA@iLO=^gBhAb4^H zJB_V4Co?&<I4s8pRHQMY*ux5zV_;wqVqjok2Xj1Kkj!RJFUl`1KoMfgFUm~MgqO9h z!64K9!D0*y47`b@CHd~Dd8tK-C8;SOpN3Qxz^WH6h+>ckNX9caT>%swH1<DpX;BWe zl7dDGDDY85e2ddz6&XYgTVhU0NMbsQD!$~@oScx<iW1Laj}TuUP{{*Q%mEWdQOuc< zUsUColbH?+DSn9OJz&D108dFxODqK!!<=QQMI~62hrxtVm2)ShJLl)*7lDEjTH*>q z%y)8!OMt@IFFy}dWpb9}7x<T!6sIDWb6h#8X`qGyswhtqxB$ga&0Pd4Ceeh!IZFd% z5d#k>Iop2#vmv=x1k45{VMuNTsbJu`3e8dT85kH;85kHJFfh0#FfefKWRPHB{D0&B zTLvD-l>DUBcxPV*#{cga_`p1d6i_Z@{C}IFg@K2IgM*WUhm(^>keiEJP?VpChhJ1e zSXfk8SVE8o44@R4jX^N-a&z<Y@e1(q35f9V@ri&4J`so(K?ubIr7-k@1^ypk5aeJ` zWth**sKme|$jB_n`2Q{g3j^2xBaHS842-OdU;y?$sK#bwVqs-t=iubxW<-}{WME>3 zNeVD9vM@3;u`sc+vvII8^B6NSFbOiVC<+;}ItC^RD-|{xv58DvxY0>jMKtJOlCeoq z(?w@?)k(o09)3(NHWgd6saefsa!88np~WR;;+H}nrGDDna`<xT<IisDQ<k)DnR?{P zlP^oR9=-bXYgk%Y+q7ldj$M29&D=day}W(;^6kg3KmRVsz{JGJ#KHo06FVELDcA{$ zhC(ckfr*7m!mLJ(6GcESI2d&CgRx4|!=k21&cUL|#U>wB*+33pS2I0y$z^iLV(}E$ zP_vTMM{p<Lc7Zy=3IA^~@Gyhz6lAbxc)P25*TGk7Q;)rO+xROnIdM|N%bWS1!_2SV ze6#we{Mmogwyy3?tDZUWTi1;1n~wJsU4C0z|3Kz%+%x;XN=IMpkPTbE=B~fF?YUEn zl?x7@%*<T1%W+L@hw*i@(A@m=mu88-%J%Kn->iIf^Tp_jsp*E>x9dH-6Pw$&@NM4R ztrnAg_4A+an)ch)|HDenbE}PC$1k$=o86adU|zdm^MteKOO<widwcIc!-efy_n+-w zqHgQ{TJP=Jm!k8Z%50GSx%nUCxj+9I?)+z9n`WneZPv?Vy;l<7Z!Xw$JixKx+WVU? zZyd`noxT5H{k4l3$A2BWe))CUW2<cfdb|q{p3k`bF8goo*4yd(%U}F8nKE&wY|7q; zH%i&MZauTU_UwH6%J9$e&Uf=))<4)k`>#^+^lQ6Iex0{@!uX;8qNv#8um2f*&(@_% zpIcL$X(jN@K6`uQ);FbbYvX4ueev~u`Rj;36V6}Q?f7$D`OAIN-`B7Evh?=Ut7lWs z9Z0Asc1T~xv!(f!b@}q2eZS;i+CR7wJ@vY)zy86M*G_!7b$j8m^89;`&ZnJS`)}Wu zx&7Ds9^5&_F73Y{t@_<x<5|mIRh={wdlF#I#&vwzi@fIRm4DTq?>v9^rOJ}{+}8^q zu6uc=pzz|`Jr06DFP%O4`rPY2hIzBDy=#8Ahl6Fp+mF$I&!+G7I<@P?%4KVM%45v) z_|~3HZY+QCW8=H@<>7yK{Ac)**=8RnV0Y8;!buaW^1A#V`HO#_)SP(YwTgG4R*unj z$8DaKroX<{vc3Is^L15OeM?pMr(GX<cZkev@?U@8&$IswCilyqt=u+m^DFO7nKxgw zP3!9Ub@9)Kzv`dsmj6(dcV1uZf9nOi!+(YgJNo{;`p@9KbKSq)^Z&L?*?()-vE%bM zY}k?e<-i`t>ACeQYV9w+S^s7JLy@|x*PZA7YV}Kh=K7)Hddbz_{~5jr&$4=Un&I>k z%L7&EmyE9Gv0i@v{rbz_k#9VI{4Mibuh(B3!5!<VV8PZs=j=Aa+i?$`8}{FuG^J?Q z6ORL{YWQ5r<v*w&73X@lJh{8LaHa?E#jiK_#TtGwGf#W=``_#fbKPT&x9iI|d^@XW zRD0k5%i*8$t`>{e@qV7_z1{fcFD0{mk?Sv)z54cR-~CrA%N<pHE?>FZb4+HMv4YLc zYexICXY1XJU-sQT|A&3x@Ba*Mw<x;Dnojt$OFBVUS7*sv!~Iv2Z~c~kTleZ)PWk2} zuacn1d+l+0EKe`62t{d@wSRXsuVq=idVQ|i?T@GT8cAK-cp(4Q+jZ4<p1;dq`SM?C zrsn?(Z>j>%rH5umPwjY9^g4UH)auw2wzE@hd$J`?eJ-!}`WpM^`j`3z+ddY)S`nkQ z^}N`P?|Y`_=~j2%yY}zd`Gp(POYZfz_s*|K`?>1#lw<Rs9e%t1&F`77>|Wb?t+*S$ z`QrN2{b#jfElS$1&MJtJj=!a6*<yJ(_w#O@9ee)u-tK#!<dqfrweYamuE=Y~XLFOC z<!(prlDcr~{;MkY@_)Vm8Mt3*GcTI9=~&&xGio;&&u#HeytnYn-ud(YGX%c*mz&BH zzoO)V?7meI+7lypow}#ZyZ+|4@`%&>XRVrl;aC0H)jZ2zB}=@CdULEl@Fv@`Tb}wE zUv;XT<M;lSc)#F$vQTjCUA-F#mOmBO@G;*}+4=nT-rKdm?GG+X_o&t`j=y4bCnxQ= z%?(DjsP{qp(_h?ud-H9muYIU|bZ+Susl(CEGWphJ+rM_bmS1xF?cG-zzv@G3t+UK7 zNTwKFd^LgZdezx`Z~rsQ`p<CA(|6;=HM%<Wfu~iPRBU+pzXfk!Vqr4ziqW08;~RF| z;J?h5v2lTMc6{91t8YWrtbWz~hQIcz<^9DxN|yz${W3M(E&nCz)PIIAi*!Y5BbL5B zvGl8OPn48v>!D4LUmE+qyS?-6&G&!4Ou3!!r+f12zq=DE)E0ev^KJVjyR-9e*Z)xx ze^Y*t=T&4egIP}Y0=|0V;_Z2VzOZe6HJ9(W?snOBk@=hFhr3O$n|Ocz*8dFq-v?UQ zhrCXX-#6>lv)}Qr1$ELdwM*Nzzxplz*JaAxzmwCKXY<ts?~Rx)zD4Yy-e0*}w>Ou) zop;5&#OMLzE6LrnObWIz8*VS_xiy>dFFW7u$j-?DrAx%abXd%j)w82_-~44}cQvK7 zZz^y3t(S}sc5eFCnW2~V?VY`IZT|Nq%b1tmkPY5<d-5-3!yVNRpWWDf`+fco`5#OA zJ=qoF-mIB!5|Q)8cVf)s-8rUf>z7^6xpDVD!!J{xuXWSR|84jDa_qI@TkB}W-FMk< zojv!<^L*&b#)Z%I<}PV#&(F!z{c84Z{?eDfF9qq}4CDG%yIW(@+oMk(m-FTDr(N5A z|BKnOzkPq#=imNxqN34^GskLsZ{N)?``tJH+nK(iqw2Z`8(*#zr~Q(=Ci9Aauk7sd z)B-Dx?VK+4xFWfE+q<i?xBdJ6Y{{}2FW&PieC0W;GV`mn!kKOM?%QwvXIL#d$HqLV zojoObf~-KE;t`kcjQjVjSu$zLBu|y)|1Z8bs5|{T?!Et8xo!KF{hc@e`MI<E{xjV9 zTK1peZhZ8Y)j{(&=WV`lb9dp_GM9IgcVD^v^Y`@Ao+`^<ZmT==rG5FgcsFV3THB5P z`1|v}iroAypZ?<C*-f(2WiIY7EB?8E?tUgRDfr9JztcCIU$}Yyh5kkLU*g`sQD6Tp z?cmE_a&t1br?~L*Khd9l$<zPx^KT)WYB_JenLFS8r~FC(f4;ZsXWZVB_e*e7?c_hb z{}~)>_D%AtTkxMj_04|<#!a#EPUhLK>a$<I53GMP*ZlANJ$~o!mA$KfSpJ{ky7(DQ zmE|w*)Zdhw@!S07UrXy++ljmH?=t&W{(1kGD|3HqxxL?f?&7wxTFDakPv%wK&sZh} ze`T&u%dxiQoo#z@lbL`058IdZlWmRv9ACcY{Ds^5Z2vQSJv-kQ?1p*ro6Pp--~9HU zp`~}?->o+KH-F21<M&RPd#~@t%(A!l=70LWe)=R&|Er$*Q-k2t|C^8&uOhg~7Y=IS zf_eu042}#b4EYR645<tX3;_%o4EYQt4EYSjU{(PG1A_<w6`;{F0|p~#Hx%63hV<Oo z89;qsMn;DJ4|%_5l#~=$>Fehe>m`C(&U(rDx%w4}1^R}12Ko#(_7w$*$=RtT3Q4Ky znR&KK?|1K4QpilPRSGxtHSjHPPR+>ls47YguJQ{>uF6ifOi{A8Q?RM9s>m(KO)W`O zsL0L9E4HezRRSAsl~-&964qBz04piUwp9YR&=n$l0~DO|i&7QL^bB;94Ghc_%q{f{ z%}mTpEOZo%42%r*4Gi@SjdTr7txS!qj4TzPK*>(QrYI%ND#*nRYFAN8nynJlQ|0C5 zdgaD?`9<mahL)C=`UXb&Mn<|tDQUXJm3bwJ6}oxF$}kgLQj3#|GQp#F3Lp~`lk!VT zY?YLt##ZDOz>UQkR!9nceeo;J%>@MjIAn@TT+2%Et1brh)AWN<i}Op1l2cvFQu9ib zVV0#N!}S)F7Uh6_nv$%anv<HFnpaY+Z>VPox2ga(91Pcj;t#kqIOahX6j(VIr6!i- z7lq{K=fENgF{o^0q-Ug{;hUJ8nFktX)<n{h>XKNJYO4f_H(diGT_d9qLo+KQ6Dt#Q zZ39Cq0|SUs-_(-Cl*E!m;uPnnWTsUTqtw^e$}_LHBrz{J)zigRsUo*PFEca6$|%`5 z+0roCSl1{q)k4?A(854BDb>_OH!USG+1$v~GT9)_2<$FjUn`gV<Wf)og7iZYZc2V~ zW{MR&jI0bzlTFN0lT&n)jnm9^%~MUxbuCO16Lk%fO_P$6EsZUc6HQ^JLB=-{bC691 zxhf?y#VXO*ASKbl*hn|Y)HGSwBss-WH__P0P&X~rz{otwFg4A>$V5pYCD|%1zbH4c z#8xRYH!(d`KdT@$T}c7q9B{YYH$NrSR>>Gy+|$`PpeR2rGbhzn$tAP6ASbaBl-?_H z3w(X8JTi+*@{1~=IVd=_5X#EQQ~)PStHfl`ShlTFGAJ3OrYJ#$GqHp&asGf9Tb_}c zhc|IRlojWvm6RtIr8=gk=9LhU{Pb}-zc{fBzvGEZ8W2~}$nnsiP0mQnOHWO)Rnk`i zc}E{q5J4hFA3e^%a!7@ioq`QmD9FVP!~~bGpt9BuT2hWuqaiRF0;3@?8UmvsFd70Q zDFhImxYWE9TcvU(d%ORScq2TWo#O+7{M|f#Tp1V{85npy5(^4)m;@LYa`Q@xg4~@H zA|j&{*q=fdl`t|eCngscI0gjxfI6x$8pMWTMh1p&*C12QmvupALB)xsgi=zAlNlHo zV;C41l2VG3a~T*I4=^w=zDh1ADq&z?Uc<n^pj=*30AgPN^?!>ZBBK}>n4f^y=@2$( zI$9(N%w_>i8Hp5y1UWM>uqZGvaPTB17Ns*V@aQlwFesEIr<X7=@PPbcke8B~$H2f- z!@$5`mz<H9!oa|@hk=1XCpR}A#0E`aYm_CY7cemJf@WbYl90?yM`9-?Cc({5OiYKf zA^rjf0|&Gf4(3pk$jvDQEr9_U30h3RlTqXWT5rL@z`%GTJKr15&P($12QL|7V7!qD zo?`_K1~A^pDD?=1i6<93gC_pLdQ%czykX*bIewt<0*j|*x_QFHGfO-}VC>Xl*I*dC zC_e~pS6Y#?BTPK82;@u#F3<#CX?7@FJR{W;Zhlop2xuq-Y*$%km>*1Uadt4=JyjXb zaPgwjAh`Qe^W2?a>eGtc;C{~q&0&L<OEECs$jJ1B>n+I$0WHb^+m)P%aDQr^Bf`w$ z2)Nr)Qe9kO=BMU`!u?xN;sm#=AP1aJz-FfAxWmPZ%Yxy0ONv6^;sqrE@G#6u^a_BP z&ydOB%aF*BtiTUrF));*R)AOEIOi8sf+toL9199^QWZS&lJy{?B@7IpybBIxhR-*^ zd6H3#10hy?g@M6Jje&tN4<Q!U#=x-4oPk03EkaDChJk^5F$2TiOUb20Wf1c~bFvJq z3|tKS3?dAY3~~(03>pl&3`PuQ4Au+|46Y1b4E_uu43P|R49N@`47m(N4CM?p42=wJ z4BZS97^X7JVwlgcm|+FOT82#w+ZpyS9Ar4gaGK!)!&Qdc3=bHdGQ4K^!0?UXHzOk> zJ0mZnFry@+JfkY3Hlq=v1*09KE29r%5Mv}`0%JO39%CtE4P!H7H{&G6S&R!AS2Av7 z+{t*5@g(C##+!@}8DBDfV*JI#%*4Ya!X(S2#-z_=!Q{x~%@o2E%aqPk$W+DD!qmq! zgJ}`d8m4Vb2boSYU1fT}^qT1_GXpa>vlz1?vo5m*voo_la};wLa}jeLa~Jb8=0(iw zn0GNBXTHq*fcXvcPZl;7VHO1zT^4H=50+4tWR^mfI+h-mSu879wy_*xxy<sA<paw< zR$f*aR&7=*R!`Ol)^yf#);88@tjk!pvL0o<%KDV`8yg#&7@InqIhzMtBwH3+HCqqc zT(<RW2iPvMJ!bpL&dx5$uFY=C?$4gcUc%nSK9hY7`+oLI>`&Q$aBy?Ta~N^BaYS+C zaWrvE<5<P9kK+=@bB^Df0-UOxR-FEvDV&v@{hUiUcX6KQe9HNoOOQ*Q%a$vID~qd< zYX;XkuA^LcxIS}pb1QLMatCr}ayM|#;NHM}g8L!&PaYv2Egol{7@ks|KAz<~2Y7Ds zeCFllRpWKwjp8lg?c-g^dzkkg?+-p<K0Q89z7)QCzS(@+_%89i<LBg8<#*(d<FDeM z#=n{WJpUU3P60ImXMse4I)T{&I|Z%_d=(THG!*m~%oXerTqSr?@P!b&keZOIP>N8C z&|;y(LXU-+g_VV!g_DJwg_j5)6@DthCZaClDUv19EwWbRoX7`JAyE_2FwqLp*`j+z zABZuFsfl@t<%so*Z4$dC_Dft=+)+GLyi<Ig_$Bf064DY55~&hh66+<dO8k;kkaU&I zmYgWLUGlCJi<FjBpj5fkJgK8nZ=^+}t)-KtyQDWs-;!aH(UJ+0sg_wRb4KQ?temX7 zY=P`d*~7AL<izA0<g(<Z$nBGRDK9K<C!Z-lMSj2hD+N&nM}-`P845=fJ}Am4dMK7E zE>t|H_*+R`DO9ORX}!{2WiDk4<uv6f%7>IcsK~4Ms??~gR=K6hscNB`p*mglxaxN` zb+vG{cD3zlFVrQ}J=Lq!SF7LA;MK6#DAZV_aYd6&(?T;_bFStEEha5ftqiT%TIaQy zw9T|LwdZPI)M3@J)XCFXq;p-DN7qreOn0^JLp?D)AH62M?RxL@RrI6uC+Z*9|7T!o zkYljK;I^TVp_gHk;V#2ZMp{P6Mzf8s81ovt8P^-{F#c$wZIWs-&*X-wu&J+UyXhg* zzh>rUMP}>FUYe_zCz#JMziuI75n$0{al(?-(#f*ka*yRtD>JJSt4&t#t#z%ltyfvU zuu->3wOL~G*jC9l(RQKj13Lw~1iJ-x59}4~6YUq-KXOoZNO4%^@Z3?;G0Sm{<2xq< zry{4VPT!raoNJvAxG=l8yL7pna}{z8b)DsU&rQiK-EFno2X|BVYWD*ktRCJT6Fshb z%6cYyuJruiW$sn$b;O&;JJ@@c_ah%IpCX@KzD&MezLS0L_^JBk`EB=S@b~ne?0+{v zJ)kgPcOY9}K;Z1aXF-NRH9^ONg@faRR|S6yaSG`VxgDwzS{iyNj6W<UY-QN@aF_5& z;SVD8BWfc~M@mIzM(&7Wj|z)g7WFmSHF|3F(-_m3wwP<N>ai8EC*!2za^m*J^T#K~ zZ%$xM2v1m*@F&qfaZ%#eB+sNdNgtA(lcy)YPH{+?lJYXuHg!_!^EBJENog<AZPO>G zzs#`Dn3nM-(>Zfi=Ep3Ltod2rv;DJ|XaCO$&sm?#k(-daGfyZlJMT!oTz*CV#RBbu zwt@$RR)tdwKNNWvEh}a$jxF9%B2rRNa;8+Hw6*kMnO)iJvLEGP<(n%6EAlGNRBBar zRz9zCt6EykQk`6Vs7A4-vF2f|W9`B^#=4}sL-oq_E%i?u+#6Ojay4c*o^3K{n$+~A zIlOsSi)>3n%i~t}*41r%ZAER@+pXK@cd&G1cAV=p?wr~AuPdeNRJTF*wC+DW$vvlf z4SQ$w{_jicJJ)a4KW_rtguDsYCfZM2F-dSz)uhLheJ5|5qA;av%9p9}Q%_DanKpkq z*Ywiq4`%qx*fCRO=7gDlW@XH}I@@XX`Z+RlI_G?!n=<#(JcoJf=F86Sp8sn>=7Jjw z-4||Kq_$|<Vz$L)i=QnCTXKA<#nP3_q?UCr`@1}E`GXZfD~_x*U%7IX%&Pv?Osh** zzg!cu=E7Q+wL8}7u3NZXY<>3zh7F|~UTsX+cx{vSrbC-8Hm}>Fx@FE*;jLZU7`Ih! z`>;J@`-2^kJ1*|@+IeV~^{%bE^>#1cqqt}GUeUe%`?&VC?EAmJYX8>*1qa?7Oh5Se zQ2e3Wha(POITCo}+)?kNCyu!tJAB;n`2G{NC-$7QI=S<d#i{M5%}#GSV|r%mS<|yy z&zYUucHaE_jtiC-c3rf&xbKqvrGuAUE+4z%dFAv~zpEFog<iXHJ?8p@8!0zl+|0T8 z@mATb-?tm?u-@sqD{yz(J(+up?rYv(|G@0Q-iIy^&pZl#bo+7g<JV7$pZs~+@{IS{ zwCD2ASH3WMvFD}R%L}ifUp;+Y@cQ?gwzq<B=e*N+x8=RV`!gRRK0N(c^zr|vp3jn> zmwz$&a_DQo*L&Y`zy1E+^+WQ<ik}ugkN*n)_564FAND^p{%Zc+_0Rj?o&UN2{}&_{ zC4$=mpe5%_X=w}$pO!N)@J2E)2wz}e;EIN{t-&hb3(_HN5U?0%nK}f6xQq-8koGr- z!(hh1u>1f61E|^M-N3-GfPsMl+?-`#U<hzvU<h$wU@|s_W3UzmhT=41&~jA<21f4f z3=FK({{R0R&cMK~$-waU`v3pGxBvhDcNqf%^DPF31K%KRdXPpo0S1P3%pu?U?auo( zEd+@m<1%o2AG&55<VToaVKf8C6(%4pOvc6t8fnp)186D_yyy(PqKi0!2?3bl07)xo zi4_xEf>;J*(b-g{24+S<<V9xz|Bo;(0xhFtVg}7Qf_wv7E6Bmd%EHOa#l*<K&B(yO z#>@!nA3`PBSXr3anK&3Z8MwFw7?>EDSs0m_K`vrq<1A%lVrCI!6=G9l7gl26Fcfh# zQWoV5bP`KcF%}OhY?MfvsOr3M;|Dd9A}+~;7fsb42Aef~oV3U#q}bKX+&x(%G%Q?d z)1ga`KBY95Om0bCy!r6u$C?&tpG&nYtvo!vA|j*GGcvQv$}1|XTHD$?I=iM!oi=^O z%vnp8E?d50<*F@Pw{73CbJvlh$Bv&kdFsm5Yu9hwy!GViv*#~fzWVa@+xH(oe+hz| z3|ged%EH3J#>Se<$iytjqA0{_$i|=~>=?*ylqgcj;nb)+Q8Z{_l9(~)MwNpXor^w* zKWs8lofIsQ?DBEZCN)#Z5Hr^lb*|#($!;Ye_lBmHhMBjtf*t%>Q)<eREui&sQy~lH zG~C0}EZ}SA5DVr!A~GsEW~|t8;>L>~mY$KBm7OzJ?mT((<xeduuc)l5u34*gox1hv z7wWpUyVGm_9Q(2UTJ>zz&DPBOf345RTjT6D(}%6P{L#jrR-9@*-ZG^om;Kg@w3)Gc zKB>!nf46!`{~rtaL-xN<<xTp}aN#rOe}=1ef3NSF-~IFchs)-Z794(L=kw*Bx8{^J zOOHQ(k!ik9E^F4VTQ?ssTe_^xWQFRId5c0_|1)GIzQ0?&IEf=c(jzs2YlA`)#}0)) z4(0p1zQ_Ll9{2j*-|M@+?=O9~_x;`K_0^*SB!<WT3%h$)ZZ0#6-ey0eJn7xP#`?*U zc`NVEzHPd7*D;$`Q#N;n{u`Aui#|TgHnLiwu}W;JRHp2dX?;eEU)la%UlLWGV(UNk zwzT}7GB)ew#Rhpgxn*ICSC<_-vgqg=*>0X4cl3g@60FrZcdsaty}GBk`e@Nvf6=g8 z^R)bRwXHIGugoufH*5bTcfXpJAH6N_bT@w#^Y4;fx~+QA{EW0rmVa5bH@9nTJD!?c zzo&xp!rGAPe9M`)g=f52&A4hwRf3Anqkn&|@4EG$AyN8G>@?4v*8X|Y^H=_7DELw{ z`#(c`-RryJ{WJF)Tg`rOJTUOPeE#F_=gN7f-=A&1=G(g)uVc6E-d&TI`*MF#vha3; z8#>J4k6BZ;nwDQXmf|r%EIGgK^<9qpa=*9Df1)4$@VC?SHNW#$W^N9Rl&UbxFU#fl zX7^`#w|vN#lM_}SI6CEwtDL#o>ZeO)X33_tHgm^;;z7Fh_xh4K_pFzTZ`{`R^4`w# zJ0)TlZZD2rtpBY*YS#XTGk9|S!uG$-Y*t*c@^_T`h3Qha4*mK4J?_=CI@|g~&2}6$ zZEyNkyzb)RKUud@_T}u@tyg4Q@9q`eVfA}eHGAQCoeRkyqU-BJu3dYib}~)9aZ<0m z)44|n_x@gAnkn>W%IlV&tl=N`NoKz`-zUH3{?_eoVKYCh-)h!&=bw6D_p&8-ZnB-x za~7SwHY;tOh1_KKTTx*XH0AzY-zAZFcXrs)4W0fw%l^FIxiW3-?QXtwS?xS7t%{&U zReO|{ddsZU5&5LOXF{c5V6zb8{{5xz8sz@{tiL#4*3Q1{_{rS6yOQt9reC`8c+&mJ zrE6yV{XFw{>xqkx?se&;Dec|*<ngL?4i{`D?&_Xo@%`Q23!8i2_U$<lkdt+Dd)fU% zSH9T^3(miJ!}svaqYt<9Wwo0+`%hTxC1aYKQo6O&Nop#;jJ9I<u79BP_jKx;y1fTe zl@FzxKRa_cZRzTz3%UiT-j%Q^3-WsPqUB3S;p11Y7p|__bt1whu;sea>=odG<LPec z^#ztLhtkdOojIJAx^n5FZoz4HC49<)yiPrd)L1Fhv}u>r_LXaT54%eE7HP6NvftnJ zJ^1zZzKchnZ=4;izH8ev>*8k@pB~OV_2!w;WZUU|woaagGrGD~d=h;zi}y_64l(mz z)!*Y@*+_p*4$Wh`RdV}$R!H8GsulNh4(`|}oX1)v>b}nHuzSQ!(JGUPJ`0Xl9A>(< z0~8jXH|A^XDqi!Oe~r~<%bf}9@7u25m;Cw-+x^>@A8)p_iK~hU4NKBA_p_U~C&fTw zr>3UVRiUGcBSZgP0O>y5ey`fT;K`>SF>J4IN1i#x6wg*I+<xxSr%D@@UuWuM_OgnG ze2utS(-rP>qU{kVjrPud!`r@XlgW<S<9oODG3!Qsxp?u>=jzG4T_>)dj8=Abo3bVJ z>V}YWUyBw-G-(<cDCW8EFMYS<>U-Py{dY{Hz4n*BU8Q?{YRi?RZ)fAm#eH`a1?X+M z9FUl{%4cedmU8x%!VOaXHu80^@21W!dmB}i9BKFL_J<(dTsPBI%g#Jryi8Kcd)AuD zxv#<$qkTkvD(#=BdjgaO&#OJYeNokB((?@)kB9fKICbfyT2QOda&dQo6{}AMZ}t<* zsnubRnXLQR<!Oh^@9%Lhr_J8@>(ln!EBR8kKR0WAYgL)#8oq1gb~nD-?cO@BVG*y4 zCx)9DNh<3xvrQ53*nb@qqaUM>XQXEqn%=Ozy6v+`<og{KR(ryVB_}RDTf6pb_=j0t zRx1~s_`8Aq<U{?%pF!n8a_q-V_pDA%E`M9rE${ME>t}dDQk0cUN%5>m?bkA)Q<p93 zTOx60T_|&}%KiSj*LQ8Nrfj@oBK$Oa^@|lBORlVs4xY4XRnxqtd3_dt)`#1sTvD|O zSt)QOC5=OT_U-$-zWeN0`+l?Wjz4+voV&LjOBS8E#^Q~gzv%p<DTY5|nx^wQ_pV*C zde*F68+~$k0*x&$RXo1G>${`be}<oqmuj>3TU}rC-R#GqqkDJf?Jj%t&5m!jcE_aU znv-r?toSVXO48=01gox($YmuZ^Oa8%w#nSSTfHo{|I_J}-`D+m!?(Ng({Ja3()#$X z_fy_yZr^HW&)Rr6_uTnacSWjGymv%v26j|EG`qM;)UdPUV8vgTz3sccyYY+fFtFU? zV%^L=`$*~sQv=gbyL|CUi`E|r30k1KD)D5Hudd7njUeyUQ^kIPVrf11elxSD)79DT z+^Y_@w-Ym&Zp<fMm}Io+QPRZ7OFnZ-OegPU$y;$m(us-j-@L!qckx**e_|i`o$cy9 z>*v|e6qeoGtzD5<JA2ZdpEZ4K^Erb<w`51lPdgL%WW{IEFU}0v%B5|KG8I3q61ybQ zRC|3_!t3+XVx@0R-FuYn{=FHya^l_JXYn@g`5u?8+;J@D<>sBbd%N#kS<blg=}9|v zeTN%w_IRa6id}B&yB_@hZuP>p)f=|)@(GLY%-(+N{Nmd)cP2J-&tG-tMpTVjTvcy; zKA*m}X;#4X({^naHhMf=lUrT=J?{0YcSm2piM8WeufA_vnbq_Xj}32E&3b#jWy$h8 zL3_8jHqBbJ%i_}~)1%YoobdhodwuC_!~JG<cTZkUG-vyKYlhC*RKqokkEEH4DN9aX zwOE*|IMmlMI+8Wy%b|r$*&*6%Uhe(z{oUS6Rqfl#YG%Ay7G4m3`AtlndD+!BCtiKu z=Fi%+>{RN@S*9&XuToBX`v=TqR{v>}b28wU|65Rs)YjadcXe&+D~<1F_g63Pm)1-3 ze7V`;=EHtL=93~f!>4IYb2)Zc#b>RhoocGW)`Q>Q?Y$DU_v<FZpE7cC`zzi?>Fj&n zt9CnL*WSY&%TBzLNKtUt<7>-0vhuXslLo1|DQut$`J~*}o0sz<%Tl7Z=bYZ7u>AMQ zZ%ZoQ1iy>O<l<xv*`d11(&Ocy6tm3&(bD%oWzfl;=5?2EZYg+TUiaem?x3b+%dE7j z-kzL&?7@oXYo`6v^2jQhYO>*Uu<l;N)#CXFKm}c@?<egQtA5|g@4a$+x1@cZ<Ml0b z7pmRt+t#@J-}CEFq9xrY^tz_sIrY_eWvNT&dX7)=zMbFWUNY=As>-XMbbI-uv#eq9 ziu<enGb9C;iKnK&yv(zke_cUk#Ou_<2lte-x@AdzjI>!&@zbYJDlJ6Hr*GD^f3NTI zvv2zKAR)6n@D1-Iv!lf)Ma3RluV*nZo#f;(%PQvh{r?R7D=In^n>@U3&#!xZ*M0KV zUArWgFPpxpj3-oNi>am843&G<rE1HM1)6o8>G^2pVq;>`m#cGzrEs1ED00$Hd@GMm z{AkhhFMh)|JAKw291=3u+{+R&3N1HzFeDk7ntlv?(y3#AX7^uE(?TF^UsTCC_08MY z`&>$Id-PVeb>p|=N$F*AT+6xb65i(CotEP!x2;X<&R(vo)@OQ^*IsvFusyew{ciQb zU9#^tFFSv9%l^IAWw!VA>}(d?GD<geE>W}dsCy8)k0o@QPq2>NVuhaL3BGU7?=O9~ z;ZZer!t+g2BW&b;+>)GgRn&6z+QW~^^~9B@ELjq~bk(YjUrINJUcTJ+VE2OPf3?5Y zmuly|x1GO7Z0ELVuWv^$EG*S^os_$K?$Kx0Wh1updd3t^4ON=7)NA%}wdrD@y7D!v z+us{kX5NZjAG+t;T(hU^R#sW*?hf3s;luN6t;fA;lcr4<R!zEoCFD3y*PBksgf*OE zpt?Np>6^aYjhk26{k>VR?|Nfaxsms-g2HWgmu0$W2hI&&$1ce1<HWw=<I3dh`}cQ! zcVI8Ax$rV>^4XHOn<uV6*?#Z&PuX2sW!r-uSY7`i_hS8>{JzCGZON8`IZs&)Z_lVw zNuRe$Q}rt^$C@Yl`%B;T`03Za>AAD>RE^%JTRSVio%WbLNha>$k?_;o=LsrKEuMHO z%<IRr4yRu&kBoNt{%gO#>wCb<-TLda&7P)xTzvnx+UktQPcpK<`<lHv=)YYf&CRu9 zwQ*tiHltZCnIfW<Ax(#Wfzp9%|0c7qan(7AA9a47se64e^*f(=Sz^{lN!}G(B+8e2 z`s}nQN`0C&f5#qQmS7hrv*pp;zt@)<HE*(dui3Ts++*{1r%yaI+`3qH;+cTFIkItD z%?dGY8rnr7UoQm(U7K!I<M--2xb{CTpI*9Uhv?nFGO^>6b4-1<Wbb%<cZT?Zttl#< zE5!}AK9|c%(kgX-a%I~SP|0Q*pY-)B+wCRyN^ieUyO{H*`KT=0>(zIDc;#-7`fSt5 z6_lQ~&L`IU)JKbeNgI=v@Gt!SZtumEcl~qk3SN}nwm<EC<o8rRfB9`zkIPR6Z#Vm# z_Ayk#@A8{7>>;^cT{<nInY_W?yUJA!XXpzCiOqV#EnoVs*CqOLy8lk~n;Y`ZZZ&(q zbh&7$b(qdOJ>Qf59w8R1=Ce(8*W|TyuFVyQF0QY8eYdpv-fr{aYflSzXP3R5dwAkO z|8F;U@yJz+PFs8U3Y$jer!8AVONAdlzqTY}X5czqP^GR3YB0aAdwsY1{;uzF-{0-M zPNu;Bi~Ho3-k;lkxn6l+)>AFB^T+pWyMBM~%6;E1C0biQ*(bcByynKYJ3FIxzSb^V zmY?-RE0}lsqV-C%DxRj!)3(~$zrXZdH{0{Gy}R%44cuOK);}<JPM&aXYQb!^oboer z%kF&r8@hE<r>t$-(VKs-&wIV<?$J#KmRdDc3zx6XI`yie>u<}K1+#yzFL`?}+FHEx zHP12orMvDwt~)bXeebq)FYbPsv45`Mw{7M2Y{KHZ3PK{@F5jY6_SZEw?a`W(@3UrY zmbLWM&PuIK*~}*Q`fkqqjgKbrPTl-)nq|^7*|pmrP19VQv8>PIqe;id)54L9r%DK% zs$_}2zw3MG!9Uk_eO_7q-skI?zjr@e-tsT=>$MAS=kPs0^Jd=M^C^b6Zzr3|+WM62 z?RIQD`c8Ls?C#6#CeyMsRwqu#zP22c;Kl8qMoxMEzGtrF6<PB;a}Do5=e+m#_x+!C zla9P|`}ggco#CJAm?O8lS8uysm8a8JuRh_(X*;R4i3{7$N2;*y;W-Ryl&%j3HHP%- z9&L)e*Y&PPO|Gw|FW&H~Y<u_7$1kVH`j)xv&v^P$W5vtMy~S6qoLbTR>5T2~^`*<_ zhXvN`{F(h>_0P)x3_3jS2l^+)dhWEd|90Os@7{}_s&}mG*|%C<UjOa8SAN#si_x=h zMhZ_`b0+;$<y%fq7K4M*QztZ;fXaLGn&^LV^}6Rj>#kV&lezC*PT0d`KLhXWzPzDY zpZ7w^&c74y%FCIbk<78P&&k)HXeztc>#J?`PP4Bi0hb<}yFdMh%f;3R&#%?r<6eZm z^nd#N^J|s+M!#Nd&wKW**Z-h@ckSbk>OZsITfJ$z=(<n&-P!Xy%;$f<%sA1gx%cFw zs_Q?@t}k5~EVyi%@oNU@>h&c7ztkV-pUMe8#Q#jvvE%i=UkBgG-v97??XSs(AOCHC zoNMm)HuvM*-PPr}ulDSFdGN{Nw`afVCztW51c$4ZrN7B!3)5;ew%Fib_xi5n`l<T` zcdypoZx@?nt^Lrvx~A*B-j`SMeHQvp58khqxiok4$K}dftxk(~o=(&%*M4`#G~6fU zo7^}5M>D;4tW?Qa68KVAK?UTqy*0~T>fHPNv;EJBUDIo8Lf__3|IeVc`k&e#wHvmv zx8}zCx=&p6>i(qUHHHPj@AvF;T3+}d^~NUWNv>=D+4GnRNvymj#I${X>AS^$CcU_G z@AJ>XKS!*}H|*0YJAV2<!<0{djDA>NpIdy>Hfzhx8CAQVOne<<%m4km;jGnnBDB`d zR`tu95w7>3O{%k{#9xw2etzk@`M-DntoZZq^q&`9>vyKVTeI%b9nBAy>L&kWy!uy} zzclR4<g34(Q!ka3Sv7B7wYxaUJ}@Xa?@yf9VsrVMSJuV#?Yz5ZNz3<ld#^sP*NJcI z-e{NepTR_alI`}(_Nlj|H$SZn`f8q+*Q2H9ZhC!dG&{fJtkXG`Y12xNCYFnZv1jey zE;#cJYtb!6P+@7d?8noa**EK@w$-X1<2!!5X8-H^lVrc|tWJM==6AfL+?}&Q@BVJx zmHBPj?^9(amXe=-ug-m^wsujsq-(&s9m~2NS#aI>y}rbtUdO+8Z~vz1)r*X!@8^FP zJNIVas@JyPx9$EEX%(N9W4mzI_p-FESu<~M+8r!EZ`GOdGPm{mzPe??$|j+~49CJN zwr0q^zRUHW!Q`K!?;Ux?f3@H2f)|Bv*fgm|?j;YqTi%?u`%6z>x%AEL*1frN=dI{o zpY`hMlf&<MTz#ep|Glc&w#wiI%dCjzcY7~z?%bun`SsH`xf}P?nV0!}|Ji-B_VCB$ z8}`}#ndYO)nqpgd?RLHR<<03U+cQI+JUUzYRqVig#vS|n5*G<B^Id%;&8pE*``z9v zJ?SCe&c?lcUEfoE`tkA1r?>h4oZ4-^<+pOJ@3xHFtFo;6YXegDx93+FA6AIkExqI3 zuG37H*KF93sI|1g?)6>PpZ@Lt8BELP2h_~Gt-INLD_ig*J>i@`u0NjWv;185<Hvu7 z?Y{-fr~SRZ+f8$mNWit!@7Ipa(h+vFx$>W(oL94T_WbZ&-~HBKivG`#_qFf$9oh0L z*56CMn*DqCudls2_wDtM^?7go_TGqy-QIn!zS25-{Xe6(e=j?C-3z_)+1IuA)Y=lZ zw#)#ZFQu+Fpq|a)kN&HzcR#*YzqZ=Hq=dgd<m>jxKTm%O2Rd$Be8W0ecF%^|^4qb# zn=;M(ULO9s?edyqxxITge7Zer(n6uxQX7vuef3avcor`A`Y!YSH*3r1eR|#B5$XD4 z`>h>aeY4KJ&pZ9Idh4n<+r3lY&TF`1uM}SVu+A?0V$jJzwvT5YpK{){bdiyfshXLW zZdDeC-RrwK?0=4w$$fmYzWYW_etgc}gL}*V>2Iz|uK4C2`gXo7--G*i<+p8*GuFK^ zx4!@A-cVLMr<#2?Iv3^SOgb*>rR?O}+rR63sC)R;>dWjmLhh?nRX<iw%hB(g?f>k% z<F~w9FJt&l9(zzO%Ez5wn*ZH>Q+0CsS6MzMF5S)Bmfbn~cwXO(;Ip9$Rr@*h&Vt5{ z`)<7OeLUxGtmi%3#*fbBt}^p=_T{InRJuF2y6J3w@BFT}z1_U}VefcaSYN-|@Z-+W zSmi0RBqM@OJuu_}wT-)u{PBMK^VjN)hPywnu%0(vRNkpZV)@K(b<Fwibb9U5*IP61 z`ZRk+>aYI{CC7tX-tz9;y7+Y9`8fy9h_2E|*OfG41&u;%sc(;%ckVz9(@)-(H+k!B z+a|j@2jBO<eknJ0dhAt;v%fcL8-#{N^!^nOx}K+dX>Wo#_esT=6w$z7_vJAmS56(| z-}OD@=(YRu*3rA|<iG57Kk%>Njeh^_-JBnsi{GyMcK7RynzC~%-~ZY7;`qbaS#SQ7 zJzR7~YTuE&x)Yi<>v~UFTWWams)}94*B|Y>zB?aNe$4m%M6T}3eP#Ml<#EL;UYajI zwx+-GPi_CgZ}qNk^{&3&x9@D;t$jysOXsgOxcQ%9)4k|>bJ7eirK`?WYS;93P&Nvg zy77D5%Qm~^cSA0e*M=Sa5xMi%`^WX^f4=y>*&@Au@yFfwY8P$_URo&8Q}+GVe0jIJ z$r)zc#d$O4O=4NIeC^_6TMjw9t#NjLxA#Kn#b__So<*-@H-ENY{F}MDw9Q)db5(m% zP4)fV<td?ge|PUFl764|)XY4|{rc%iN8haBQ;J=vn#*RiTXLC=%+yl?PRyrlukR}T zaq&vmeK9Lx%iHxr)zVMoF1(3<`f+0!|IssP(s{|-r@9AzOaG{I=4*AS+Ub;+v)66k z=C-XWXHN7aw$5vlgvIYhmdKaBYu-PxqRV&c)&1&Lo1f|(T>Jh%!wi$^_J?-L8RceD zI%<zrzFxB6_FQeTV6NNqwrl(DG&>ZO$7R!7Tc$OMQ`khS)xWPyW%;h}?xt1jWpCa+ zI9C>aBEDDp`|f>KNAKDjF7VrQG}k8Y<GlX5Z^B<Mf8C$fJAd-JrmlrXGyd4QHA~M9 zxcqR{G9kB%l|0?=_Fl~Yak%>p@5GPB`FUs8fBe4F{MqgI^Pf)q#J-^Fx$K4wTcYRf zXTDibc0E<ke*O2eE;p-+JJ$6zZ9BGN%SVq)wXd67`QPomyzlXkiTA(WKCIjJws-R# zfv4vC!)g+b|Eaz6xAxNR=#AUW4)`wLqN(v+?DnnL-Z|$UYgf4!<?Q(>lyZ;RUp6P< z%vnPzP*%t+Yxn;4Jf_}|C+yixTh*QAwP_E2O4=oF+;n&QwTe@3o?U&N^H==Vv)|2Y z?SgaXd}CHOyCAS6$#&wb#iy18dtCXriiNNC`Ywt3S>ZlqdEb7DUYdR1bo+O)??3MM zy|O!U%k)Q#ynn*(N0W2E?MvG=&!;R}_4>>|t+FPPvd7yrJ$uf$P4ezrx!i0LquS<o zd#}gy{xf^BEBu+d>$~39S8v}po_Oky<boR(>t4O_?cYAXF1cLo{{5Lgcf&5+ukIE% z^IDwuY~$@<ZC_V^_Pbtdy}1rQ$psC^I@X_#@3dZS`f2_%`@Fa3`+9e8TbHi;LEL^{ zaCUBtsNS1*>(<|to^3njJ@5RM)BhPJgue5YcvV$dEU`07%w~_Fs?8z?C(w|Ta{G_f z!MEoAydFK7_hY&KmO82D`?BBG{@wU1ydr0gpM1{SJAC(FOg->k_G9?fiJqa2x2pBa z%UE(vOn8}GgVo=8`fUgG88+T$FbuWcys}&`_^~dpTiE`eYs&ea-jC!uyRv`gt2(<B z?d>HIt9V{i+|>G2t?Ml+yiF?L$Ex-VpMp0Ffnq7LeED&^cipQNzxeZ?;h5~X-TdNB zKPUIR>^j5m_4eHDI~UWx+h^xjt(|ynx3TEnqW2qjWeXTw4h%~!dMx&tEB!Ldg$gJ0 zUEjT;_ILlCEqCff=(B%jd9Tjhev|+7`$zwjfPAjC)4NaR{bzW8JnUPj#v9wUJGWP< zf3(Uy>ew40<yy`e%Hyz5V*!8ZyY?u(#y4B<9Z%bT-1pagBlfd*!V6qKZT`K#H^%Bu zcDr%++EqPe=RYdWYEJjwq`B-M?;?={TKA7XxO5EERCIdT-zq(M{kH3e=DfT4<LjK; z)xR~x&*k{nrf*woDtGaX%|7|Mw`UgT_Qbusx6e}B>&Yjc<!oEm+v<us`U|;nf(Cbf z{#o+t`sQELH=8fJ<~Mug!LKs5wwnw^W2f%fRn@#?cU*hj+gHDCRaTwfFg@q_tFrRa zyc$!Lv!-cpmL7Thd}XEMq^e#A^<Cdxoi~~Bac{Qs`_Ew5J8^TJd4BEtPsO&Mrayk` z@BRImeE#ECAO0q*-Cgi{&bexB&;9DBf}dEuEL$fuX;IClU=`mP%O@rzT72m*eK)<r zzWsK8dZ||#-_IY9WB0#M{Fk_Q=8bsGvUS=Sk2cNzy?a;UJ+IoGQ`Rk5Ta`8U=k*!9 zcQlO`vj-Yq+N)~Z@$$x`weR*`%CwU%ll#x`rf%7NY3|3<f7#r;T;6}?@9mG<#rKuj z-Rs)Dux#1O7oM4Ba%no*>%8o?9(;H2l<vfX$(plNkNF$za%cYiJ?@3=kDnj8H@@EW z-Lm6!j{Kw@XMZ;Sn7r5O{BE;($6mdicxkHi{o2&{JgzRA;~DGX)&8EmQL<AfHZ@OO zHQGO9>jrNnUn8!j=DXF)ZFgU&T|E28=Ucz!)h||Wn!LR1efF2UpEvJLz4o6$&TM;B zUhT=LM-spL8JC^B{hvXk=;oU5ao?`7C*5kYx&GmojaS;z6~^w;wi;#M<6Z^*sr%Wx zf7kD%$7S!`|1&JUv+n+j{WE{dr<}b$yIk?gj=7gNZ|AE|eDZD2kG~5i+>P#9yuA4C z)~;?vQ8is7ZeE$@lM;>5Ucc9u+Ha~e4lC!Cdu+@5QM#_=`|&yJ*dMVSd^2zTi8rTY z8^sg8ZM*dT?e4kvX77_-*mA<8bW_gTX751f8Rs@Fxq9x%Om!B}Y*cIS%Ma5Q`A?2s zy#AxS%*%y8&iem3ZD;UpnRxPJvw3FIS5D3ds7X5%n1A-D@0OL1`t$3{RPK8loO1kj zKQ}NYa%u04NwwE^F?=sS>i7DVWbH(;;5FOD>>s(u)-?QV?pyj=Cf_HadU{T-#rA97 zIVo1Vi-JqmWv!dd8NBIf>Ph$hl}(cNE&k?jj!R70^m~1&x$Q^oeIKozvmX{#i|;mX zv+G~_&m{Td*6RNZoR02$G8P~H^Z3T^@5<Xg&G?fStnJxr9iI{G8!D@G<@C3T6O)YG z`#rwLz4W|fFIs)7|H(Q#xl2dC^T>YOzR&xcctYB5?etQy>&GwW^X)m|9jj(mbN!^H zas2UBR)<U8-V_&?Q(QQ8#VfukOS%sJ{k^`#yk7g>;<tV?f6AEs(_Xw;;@;-}3~trk zoHwL@Z{D?C`ug1S*LP3qw>I-;-S~Zadalox=;SA7{gT$XPqMkQBV~Qpe4q7Uw|boK zR<G?KbCd*Qq~-smySMjRMRnJ`+50(s<F@Jk2k%<i?hW+aTz)?J>oMkv3y(j4bW%QY z<@uu3$Nl3c9l!MQ^5ZMF!e4NR*}c9ScrN;9*q@u{606<WC*HLeOKIQ!y-d9N-K`%R zCLWx+?A+TMWq-}2e{T;L_gj>(@k_|<w27=Xg;NUT;}$CmzuS9tcTLQX$2-cx4lI;c z-ge$0pYQg9JE2p*NH1T%>u_G;gX4*hqLiB(uJ5~h<m$EO8LJehMQR7lo3XK!(b3oa zZuR1&?}=S@@WS?BseSa@-oJZyd*{EoBd5O6T5dM0`@ZkBq6*#SyE4m;DlWMFA@$W( z;b1wno*xgzq{KlZBc;u+>bBpuZQNTO&bZkiV&2g=RW@w)wfRz7j$-*G7jv`H7vH{e zvueSSD@WUxPF&m|V4|sff7f>>dCJyAyy6R%>_3~F{o!YnNA|~=URHDJ+}f6!UcMF> z=Je%j$mQkV9?g@KjHq+`{%-H(va7dlmnXm7BX|CmMV0RUt>4b+^>a5Ie4O+l`J=_0 z8cknUM|a-yU9+aKaCFy+fQF3xLDK}FWgCtAZvXgxY41lqDY<*Ed$;fV&#?QdP{>2| zGeIY`H?Fm6S=BaO+0|6HD&X1zrbV+)WWHixa!Fv{U;3`q$4_0n@mN4&!HicjZt|j@ ztZc^Oy-VA=<@*;c>rGm|Wbu+^9uJl+Z&Kyz=n{3<0BX&%Z5Kc0*Kl|5?A~{=C$HQ# z*T1=Ava71vyVXZmr^qdql<XFdj_0xpNZn+y!mrC~){eA!QyC)WGB8xHFR>EfQ25KS z@jpWZsL7zc$;|A+>)7=@H>3IUlg-v$IrR7ahc$e1jJ|yf{;5t}eLDQ_9)=0?ODC<L zmHvFDO7c(8gw1-=M;BGLeLH;fw!i-F;@kOoj}3Ot{W-nac2ALsKt)!$SlX;MnH))- z*-dqiR%vmSbePMvGT-0z-RnLTMlvz-C}DyBm+v22wQ3gVjIM2qq`a&w)K;xtwQ{P$ z@@31G_b#2csB%eapG0COx3wYHDo!=fw9jSjr_cXrPJbevTQm92eS!Ux^^a!#XP8>O zKjYf<N8j1=eOu1v)rfrEF>Bl6lPP8E?j6<&GP}5Rg<H>4qf(I%XM#f%7Bj!UTfI8( zpU>T|f9_ZBDBtv-!Q@=qZCOpZYm?KCUitLM>`ryw{*v?4Vy@htvej14U(++#v8`#+ z#F|-G|7>N=eF+}50gY^f+yhFF<SrwMiM#&%R~=~DgK$XEPes+YCm%pVoMhTqT38(u z|0{m}H&E!j$D4d#|EmY(FpMDle`VMCA0hfD)_wi){LkL3;#lcV(d(<2wpXT>vz@vp zU0o7%>GVG9#o1|QZ#IPG)U8;%V)=^ck~?2ca$h+wqQv{#tcKLR$9H{?O0L(CyZ(ND z_kH<Kw>Ljt{yFVmd_B*nxqIiYUVHCv<=q`?+}yW6$lV=mCNDO#r_DbrEo-I0)PUgH zyQ((99c%42?V15vEcRmIpC#t$C*ltN$lPkp|D^s@-IAZ3yUVvdziYX6>s?vpjs-`) ze)CRo4dv4o?>M&2UcS3qIlSvsa&}PV84d5b@;5+ji#4F710nK%e*C_?_t)?AKbkMj zb$4&jT5T8oXVdMyrh5!yL-!}WsylR7I@iFG=jYAn;;m_4N*}-5b;@MfY&DLGMLXjB zK~qW7KNY^c6Q5e+Y$3d}`mpwHtNQKL(aW~K<9-tN=1Bg#b2q9lr{wLr<+D8gYMGjn z#me2>{M(XKy5*<l244xv_%!JiD79sSqK5Z(zh->PE58e8lD3!eC!`kE*6zvvIQw-m zL)o)4Coa8x({janxs8OVMGk9_YN+o)If>c#cYTk_uGc^N{nO>g(@**JFa2qFuU=oX z+WSe~-M4GlPQB6neaAZgN&cr_KU;5_N~as|*cR${WX0`b*`wFE`<8jE)=X}2v&npz z44QM{iRaosGyk*2?fWA8r@v!Ym3Js_FaGo9XXz*BRc7ygzpa^{cAV|vt=ZDbQ&0HM zyqhjLxof%7k2OzCQ%z-6`C24)tbELTdJU*?`>)LYbM8;xYqhDLS6fee9XI)8etk#1 zUY%u4%8|qF6YrVqPA@-tJ7jLr?)A12Q%#p^`$jrWxVyFfuwrjRMq>Xd$*mw$LSMa~ zci-}#cTM{{mLJbA-Isn<r%_^lGV-6{`#t?>@9&=9_WQK@tiumBR?1%9zwN7zaPMM1 ze*V~5%e&@_2Xd}I;lAd`^^KsSp|nQyUrN2zH?@PeXUcs%RhnOPx%~U*#c%lACGM;X zTU~o}=Qh7HtL|)m{5jykQP;v_YTEfHU1h4P`dt^doc=B~W9{DSyNqNH|9QACBw@kZ z-u+i%%`aBBZ`fdec9#Cr<JZ2-K5V&X#kPCy`A-tJSy$)1T9ca<8k{Gg<^R(q<Y?xW zZml*@cgXfm{rTlj{5}6{y%R69DOU4pY$N~iyALnTP5h)ZIU&)yZhvL|+^09U?8yA? zR=;>j*y;ms?=Ia`->a+>-Qyh^D^nzKyAf1QYW(p19Q?HZwEwjG2HQf@j=rc}`7!&^ z`J!i4X_E_sujbF1_u{_f;jP>5Jv($wm*wj5?8Fx<R(|Hw_L-P9VUv!7Xi@F;T{?ST ze`bEFo)z}xeAe5FGP6TJw)g)0y?s~LDjmM~^1$-8Z1zo`u9jHF^gYpzGn<=t_s&f# z%LO?$C687nUR2)98}>c!W$?xQXUq5huD9O0-Aw-R7whe-tFw7;-L%_(`0d)cH>~SR za@uc3+&P<e=0AgnZ1=7r`4{&KFTJ02$N%O`L&HOw%clsx1PuVVb^olcx&J19^3T7Y zH(R&vnw|HJ@5XKE>ax(U^KKvfW_Iw++n$|Wr%rLb(ffK+cCu}5np?MgsGkteGqdvJ zp*yt}2)@5tz3Swxy}v)nuiWSVXXe{=XV2!oJGXN7Q~T<#&ki36&YySZ*xSF|+p3Zu z2bG0!bGvU^wdw7a*^(3Lxvsou@wr-b<#b)r_FdoI?kCk7zMpJeFS>1$y|UT$W4Y_) z^ZzKHsM-GN&9ob5-|tDgfAc4goAY{utJcPwjjX%2?ebX@DRu9}qDj3Uy+tmY<ohvB zow)b+`VxT)^=9!?*S5XAyJ~Onm7Q@{Z`m&Va603S%)9i=viISo@1ESguWf#o@6zg% z*WdnqIBn&<&3f0{HlOv3WIpN}#r_hs#^%L=fInY<CdcnxwNLxtrhoSKr|y4C{Lb%x zF`wygqqr0A+U>vQP2QPR?i;7LDR)0#Ozy51FaBM!nyVWn+4!-$`g`0<{k!|m9DdRs z_ipdhOA&D!UcLYFvG=9S_Tq16BIcAQe)o^R_Md_OUiI|kq_VyPYhQc4@|mWsytC-H z+tOJvn@{Z6Z&PrES*J+&-QG+7o9kz>pONzMPk;Y!`-^=G54^cI^X2-k*r|KA#TQ)7 z@2O8q`L2JW_u<r=l5;2a=Eyn8sBvyv7u2cRwQiGTP{xed%Ex!BSKjUZbIH!7l<ni4 z`Eus>j=a+P&oIGM_V`9Kvom*o3+I<jd-+<*cj5l3%_ZNiRQev={L+YhgRymUev;W# zSDp`#pLr}_F;UGyDUAJY_43;7e~#?SD4SQkbl2vDFW&=m>`vahT+_Ae?(W&U3~CoP z-@ID!Z}O5!+w=2vySHQ-ZMl8QhV4H?r_P4(Q&&UGRli0ZzgxZFbXv{BpPe`7Ufoc= zJ@b)SUUb^Edfh$$849oe$vS#HZ=zlGjzZhnGj2^~&wsKid(olYXD2V5nE7PImP?Np z_X*r9+rPi`-5Tbf)qhUjlh@4nEzEz@GH$8#e}=?uw^p6Xwe4<sW81!G+f=)UOID>H zEfbF}tJ!Kjojull;-YC1*-Epbv@Y}{D=Y@hrY#GoH}!jVckbJnhrhn}4bGc4>rl-^ zdFfS#Q)26HPTkvaX_>$Hb|cy4c0ZP__1k(pcaB`S-lnUMQ`_HPmSyRa4p!e^`mRmB zq4xAAJ^R+${|vWfpYGl^JuN?X;?4gID%oZ0oMr#M=xfU{J8?FxzWHI%q~}`8*NB81 zYfSD4o*L3{qpE#>>AR+RPyS`fp1)qJxp}ud>$Td<$#<%k|EznmFRI5aSb3+yG57rm zrxOaNSjVa~EzN2>Kl{hI<um>>Oy%Y_-(UJ}LD~AxmOmvwZpu6Vdfms@_vWAb_9y$} zvA6|mUUk_WKAAI9{z+PVMWW%3x1ZZiq-dG@mik>>b>r$KzaJ-)MK_u_DXckuf7f^S z$q$yF;=B4&wDYY^dj8iJw`R{?v%OSIti9~vx3-Trt+h9Pu*^xi9++-+j=4VMYTlZX zEvAP4{^6qDvyB$3F9of`ijmFy{QT*;*Gq2CR$o}g?~oj&*Ys2OlX&ry-Sq|EmOYD& zb-XJwTdqGRG3{7t;>l@uLQLOlds}Y_nZzKHAqHyVch}XM)KA{Mddcs}`|ZrX*1MZs z`|<R`U&~vvbGPlTR^6L)$9i^g*|nR??(w(Z_{qC9*0cL~;<sb0OLTRsPU^-y4AlXR ze_iq}|Ic8kcjexH23-yFxFfG?PwukX{`h>ZzU$Mf*LiQ(U0iu5`ndL9g#!<_pHJAA z%|D@e&64)i@UuCkVsYYgH6Kq}$dL)^m|f`IvVX?(Q+xKR9lk%e`}$u_cDL{LC*&vn zXOJx0XP%x?%_ev8*wbB0cXyi2`*J#E^(Ld-uSe5f?pl}edgaPTUMp1RrnDY-P`$o{ zGogNVeyQ5EBM+?Vmv8v_>fVYMXB%(s?cMfz+xfh2amBT%-|wb<Sh?gzZSBqvks1$g zmtJ^mwx-d@y8kT8SK(#zZ}%Sj9`_>ZP5r$849e!Q^0j%MRnONL#A;uj`}jg_u4~iU zGvCz2cYLapJ9zqS)UH>1&bhNsZs+W^PTX{sXG*tZ)bWlsE2-mmtC!!K^)E~I`0cw- zw`i38XYg^J`6Bn@Cw|)tQ!?9c)J`z}c4^V!r?0>LiCw>|{A+6FYs+otKgv{lhC8xw z$NF-wnv}u|+Ds7onfXb4{iEMT@xA*cH_S~jKQdo#H}~%Hyti*>^<EZJYR-KuwL0&? zhTUGeOQZYO{rj4-S>W7%hEnUTx<c;KRzeTtOW&<pzwAH5Eb|j{=U=wXytZ3A?ee$y zgs<ku<}SW{cW>w(v;Aq`;vT>0J-qkx@1HNM{xhVdf3vN%6}x|OjkH@;#iyGab!9er zat1E4)CUhjZ2FmaE7tp?`nunzSAG+_Z9Dnq?e~xKjvsk*?)G#;*S6cHd*9!%woZK_ zTr9iSMAA5TYjCm0nGHLHyjk~qg655d@7+JE{bbsOw{?eaRLom^tF~ACxb4LaRiAf$ zlonpMyI6P5o7ji5mn>R+@9JFNdHou<v(q1bdvtZ8C*K3%^C2tZBZKz+y}nCk<9?lg z0pI!9x9#TU-CC8N`zK+#_NVaW8%*o(-FYYcM329qRL{CR_eS|1@7Rd4dDTCby}WIj z#^cJOIcfRwh^IQY;_vVJ?(&~usr`)q3@7D#>}9OXbw3s7m%dNTUHkMydQtiI*k{}F z+$&S8jh5S9+qyS>+hpNer9WT2T9cNxyfXA#F3a2SK963@zJv!8IKRiejJN+B_%pZt zweIiu_)F2u>idG?`#zVhd$;PQ?e^<;9WH+lFUUE!;mn)6*UIz!a!*gZW1S@XST@!+ z%<|f#O%^dX7|-we?sh)6X4%iVJ9f8M<gCfN^yBoToBtW)%+mu)zn}1#Tlesuw6X54 za{k1C;HTwb#Vb~A{;63Q5o30G(Q4Oai_fZjjqGfUNC^u1_xi4Ies8^Loo~rjrFVSS ze7kSR%WZ#u_Wg{z{(Y-go_a6SU!5>HC%^ae)LX&F<0E6$&g3rb>t|MFDbZ#+@9+Ca z>lzDabp3p*{F&9e?YzIW2M4~Zo4=%a#qZC_?`%JA`ObIkhV0blHLp&rSov{h?#hlg zx)*n^PQSH#U+(p`_1U3`%6$@Ai3x6Ppp6ZR{~2bmpX#4#y*{Pp!tMF8FL%AqD7}A7 zXMRx5+rPVvE9c(scqj9&zU&?E)@f(2e%<EG8|%A$L&P6GaYd$SC)}08f2#EvaexL4 z0={kk9Q4lSKSP$Dd{U|W_xsDu>T7cQdw2e+H9PobPJ3o*djIc@9e3w$l>4`At>1>D z4?cv>{1~ohw2C<{GqE80#0${$h0=cplU46Nt$*L&EG?b%z9#SXtGNd!|K#6Ot)08< z$s6Crv+fI*+WS4(zE58;aMq2PTAcQ+uHSAgY>QsFCgD<64`@`#^>B8*vHWy_Qr5b^ zH*AwH1b!`dt;zVY^!D!CvU`f}&Yp9B_aeI|M-pz^Z!6oEXtbIobO%Rv_vP>>*UDDq zDg{Jr2Gxk!@A#kf-@03We@6`8<=e5z`*-F3e!c2fcyUnf-0e3j-`?F8yeaYa$9>_& z-=>P@$cAn;=Tq8U;1<nqBYZ`|>CUSOncw4Hi5>ozX0Mj<de`<%yC<*vWjFuve}=4Y z_fP6AJr+><*X`&0Z3dr8;;z0~`>^wF>E&le!ns|$Y)?C14+=i(Dx+;{b9ptJc7g5n zT_LIVGx|@-E&B0%XPr;^_K1?uu(NmfhHm}-pTQ%+>Ue3MRPR&Qo#p3VExa9_o%BtO z*)TddeD;#I`I@Y`am50fFQztM)~e(JRg)90{AW0`|LLuVYPVirmaU%jX3qTbKe?}C zr|;Wrt-amq`10>;>iddk=CFlL{aC$U)$H@XZ*RQjxNP=*Jo&9w=1s0{&<dsU{(q_Q zy5FR?y?@W!`0-KQh8y>F%-fHgJ7t?~p7fug`g3IM-s{%$H)mR_Cmo-E(!cL`f}eK& zT9qAVn!KZ~^1NHOatWw`D80FU_W2WQo*lgVcXi;!2%deX&(@T^t9$d>^JBI0ma=>H z$*E>+S5M}>ow)sXdPSbq&jRbovU>_k7}HiC)QQgzxWynY_C4;U^{4P>&(E{{cz;h? zUpw@|-t0S~`^9!uKY1Vhe%`veGck2{Z~Z=B@wRsN9<#jSAM^Zj*6d!?6?1h(cE(-J zl$LE<LCvV1GyBhQ@B7An>h1Ikv3q-8+U>q^b?3fSH$`@@uXyy^SoGhFBJ0iB7sB&= z{xgJrbH8`>>t8)@@wqc!Cfwec@%ZGKLq|^wfYNWyzlbui%H4+_sUM%CR;;^C!#caP z%sx54=9t)`Ws`R8Pby)%b0p7Z-<iHeQ`THOzH9Y%Q}qc8`I47tSd@VZVBVMV&nA9S z-?#nUW#5UHqpQ}h+udFDZGH6D*ZU5?)zJQRTYdBXeKXE3-CrHPL2bRXu<fSL)y)|k zuUBsP=o;%awPV5SK4(zf<o;*Nzrt_R&%H6vdq4O5^=R{TW$~f!Evp<?e2hGG=Gv39 z<#q?=ZI-_yX|eUpl?kQmKMH%5eL3pu8)D><Qf4?A)X$SRwCrolyVto#1GlB#_7Cs1 zR?@LG3zE5O%dwDIZ}+0CTb&tMS@Ttk{W610A}k7`K?x(9Yx6hr^t69TL0{wU{rtPr zE+Tnf;<saWU#`1!Yo_0!ML*7NNjUILmB&IS&AWJ8sTr%-t=l&Q1LfTM7WH0OAsDfl z$@cm#E9=QW=a#2k{Hb`owql0dk~eni?#x&E5HD?R=bZHR+^KczZd<qQ&wrYgU2GY5 zf6^|#WlPTlJ^wJ-Sxr>)$%-~Jt+P*h`}dc=+x*r3v(Zofo!`YPb7S9Mn;YHvR(IjG zzbAM9y8Jj~%Ixnc-|dR6ukVa{WxKp@@u|F1zj`O$+Ff6s^ipW$>u+B^{D>;)_fUI$ zw|cSjpFRK5-s?~KvbVqe&7Ju{&ilV9zwPR2JA30s-k}?}uV)^gd)(HyPI*Js>&>Yv z?^w**6XwEIp}k;Ml+@Rtj}yPxy}lcq=l|sU3AvWn=bv%2r+@qQ?$lTR6L#-^X1D*i ze53T|tR0(bMQ`8Dz43l~W|dRBk&H|ncjZGRueW>79QG}kHqDL!G>$OsN&O7@j5qsM z{|ukeo}P1OZ}~C3M?a2Vu1>32TUN5~?a^=NPP~-4_Tb#RnaOjkp08L{t+c{HbIXxO z65UOa>)-9YkgtCJ{#?nu-#2gk9lYMoAn47$>pvTB?HBmQvb$;hre}9{)U+MG{kyyP z@ow|dzinYWsbxaTnsY*e&-W}544u{P=E)Ry@AX}=eJB2<+upsu+dKKoHvWu9X4`WU zKJ2W!@Gk4^^wxJa|8Cu})LZ`MS4^CI;v3I0KELVZ4>!m*hV}(Vhfbd6p|mV*=?r;w zyVrMlYA60VQs<lhZ|hIjogePnU#}Kl{VV)Yf9SV=&u-rSt$8E5e%F$_wre;3XV~MO zYFc;w<fV(bGd_qG3VhY#b?m(A$iJe$^j%Br!9R!h1^u$NUT1ZE$*O%;kL*Hn^gDmr zl)X9mKJ@MPb?Z(9<i34*!{q9gog8HcjwTgPwh0Lla`-wmpmpl(EBf#DUOMxi!RYVz z&X;<v?|hGMC@Tv5rhh7alCAmP7lx&gJLRRX-xbua^W`hg?tFedc-N*K{}wF&blh%L z=&WS{3mB$-k9#TpG5GoQvOh=e{4QTtem?R_?!C1?Rj=H>Y`<mue}<@>f9G!gJhwpT zan$O(i|_V6-L3uXV7jqo46Ea;S^GIQy0ST$Mn`m4uP>>qWBji6{Jng)ZTTMaGbcm$ z8RpKr@%yl%&_uRi+l`;6-Y#0M$!xwV^n|ms%$n!ZCe8q5x%&TC@3cQ(f8c!oe}*&q z3%<L%f4_F}a9)MhhTSbWzK16)`}EZ6bH&qw&&eBq@6WrkQYi3AhMM7K(f<sVQd8_f zZGf7acE*Xj)PHLInfdm7SNyr+pXJHb_tzTzp8Rf`efhyH^VO#<T-qo%ufO0;)TvWF zb=sA=vZuS2dES=Wy5Z`@5TS^F3u>?Lx_9`e?SBT-`A_$M&X?U)XY`*z|Ln>=R*y6O z<lekpuj;t*yWHWWH!7-pH}5RE8LFHeaQV5hzfDNwMD@FsTn3XCn6EGe^`0u?{$Agu zE3dUrrKJDNzOC)6e;?0(QZ%C`?a`5Z*|qa-SN_>|{@!ot$^<pzhvjJ}??qRwYPa6D z`NmJqi?%kWB0a-+ctIl|qNU-Vp8vRbDa4k$_cc%4_gh!$<-Ujgxo@4S`J2D=s`sAl z^HYzASHA1Jv1;v!D<R+FvJC@QmCe@pJmY!M$>lFWMd;1<cdM7p`Nz(^tDti3<{clW zMDE?cqcD<3t(bRGqt?csF>YbgqGYC7?V1&E@k&tG#d9A|^l+pk@$WBvw`%+2<)6I% z#mkFZFXz3tH~L!i`rp?#`#rEtZoMZvf4}*<Bd_A$`<R;CJpUzo({{O{{jA%hR?S-H zadC0yta)ZzTsc^(*OzR3b^fXBhwIDD`6J%RPTXGp)c%+F``WLc)K`6&nRooI<=wp- zesgc!$j-a#a&A)5)~z8w_HLcfdMp0n#mwcRdY5kO76vVVdtXtnrr&g5VRwIBd3!>x z+#%`po6XG*-miYnxxG5Q_U*m>clI`43x1S(bM|lD4Td{AwrwqYbHuf^Np#hunVlU5 z+m@%<y}qlvo&VFKAB8*id7S*z7Q1nO!R>eZYaUd47rd=qf9v*thMcc%Z{PB6WHH&H zcld5?V%xckZ*}gyTCi-n%kR$26H^pEX&2X*zMCYy`ls0+#k;$!x0mhByY;4~Onm$9 zTkTKwhU(paJNI*Q-v0MoR@>Ii-cU6yrYwrn>b2#RHOZ<mGd@eMuwVT`_ubyh+m8MV z*w0n<Iyd+1>TTQVKK+y4W*6|FE-B#3b8GIBJe#NKHE&-P`g`u)T-|IQ8$IKkslR@} z)L`%D6J8a8M$4!D?f<jspWe6d6IXwFhkM_OHs7*(^|x)|mH!zQ^F4YKxBb@KnNw~G z9_PDs;+xy$Tatgm<jcYyynN`fcv8hz8!aK<?1I|syX1G7KYAbdr~J;|*SoEb3&!`H z`P1{(YTe^EKUWt`jg<H_``KpR-h=+JuA<&X$(gfdJ6mfEBbhbrL3_a#DBJ(pvQO`w z-tFVxS<}>O9j6>E+iG5!{4JM1>D{TgjmzGyNRH6jzN_@p6TjR4)F&@<n9BL^gQaQ{ zTN0>!@o`iA^#0?z?K_NS>X_;$gyd$Oihb_d^{QrlYQ9c;;9IY}gKg`0@3P&vA)MOB zFYP;_Z&y4^l#~|Jlgm23Nw(K_g_ZCh%A5M*<*wJ;ubs1gU6{|gt<HX(VRZM#@9&=8 z-|4&Yy2PB8pD}62ZcmkN+qM2j8Q*l}pii@zZPvI?a%14Hdwtg=eIIj8;hjp`<I_&% zp4qc?_UaA4*|!w)&HVM_TJGJOcZ=mi-<{gxTA;Li^({di!z24<EctsBv>N@D-nV~o z{~08H3o~!pcJJ-2$ecd)^;KevA5Xi|r+aexf|W;0ugr{{ICtsqs#{VWT9XTtHpiBC zu3mD2UHG7_K|-a%@AW0^x9^|&{pcOL;oLi41Iof)-`%~w@XNY4cdJ)dm!-YFTY2~3 zydS@JuGk(QcJ*3VTK1|QgSUU?axwZdba5)B1<hNgez$t*e};q_)*oMQ?9~0aqiDw6 zn{D@`zi%qIyES<4w`m{G<@Vm+zWttXmh+~}Prrk$J60}Rd&o6%TY|{el8+mgukQKx z$Z(_F>$_^j^G_!~mUHI3zN2@0-~GGUq1Ua`jW>PVy?Cv)dik5b59i&Ao~~|}U;NN~ z-qT|#msNKQ+s;|DV%@B&>(iEMXQj@w6!MgqU;1v^&G$|9a^_;v8#hSG?w&PS@>aF; zvNF5(z1#KTs%G6`cX}>eSbE1hK0G)u_WP8`aHEhbHvP7VDyPnPf=)qbyz!rbvCjHi z-Gh6#Pg{4~`EmF2UUTvIypQ|J_PslsyY}wZa{q|8ch+6Lb#w3BIY+Ks4ok=q&re&m z;??8c9@8zKt$g~{fHpnMFS9?iZt{)Fb9?Vf{MbG%zvSyS>y0Z6ZqF`%pL=+>@lE!( z8E2|*ZMU3qF6>%pN>))+XOCFK%JyX{uAr@>9v1e~{xcjsmE}9JuRS+U<Kt}QRolzG zf*&W{y?H{@cyIQeO`qS%wB5Zk@$yxdqD<!<T2kBklESL8npet1MBlAmX#V*95xylq z9?rgNy*b@`?{>9s_bhUEZ~a{Tc;okN5AIj5%3ZugYVWsoSK2k_tywu~HfQr>&47=` z8jMdH+w{E2v%S8{%khKxA`R=N{W}WY-k4ptS$|*hTbXBvZ~vXT$KY=CoHr{TG|hSM zv+mG>Q_jt+PKCI8bNfp=`r2$*GRfuK0?=%a_bdM%+u%#T^EYoxJA3?Td|KJQ4|iu@ zFUpO1mbTrzb?M3chUX=L2`^VKneb@CO-t2sla1SZBTq(yhALc*`Ts2bC-=xO@{e}X zZf(<^bERtjixijKxx0Lixr}xBicgWdVh`+A&R!O;7-mqhOQuyOjh}Z(NWfB1JGFh^ zr5~mrAMZ4-{#^Ke@8&7%&y|Utc)hlB>ZuEPGTWrpI~TmFd6!(2qjT)qiN{)+tGC^I zcKc_~TbGUDmrhKc@JnKf&hop}3yl};Vo(3V#%|hn>*w6>J6C<z%iPZP=+4~kz1!J- z-I<xQaA{Mg<fq7Aw>CSwvg#VGDcN$G*Gkq$IM~qc^<9>~wIA#sZ;uQ&bI!^=vsKr- zG(Y9b$E=>OwL$+Gimc<yFTbDr)JZH%`CWE7tI<iB`|DJ`tOw21u9-jiKf}rA<z>sF zwXfZc+`D17{^NaLU#;8l%4V-np6q{y<~zTO^S;j6^4>pq&hfWr-m-CT%YN$Nv7ly4 z)k^(`Q%?KYq~EPxt+Db4?}ba>uI%04Uw7norO&)ax2w;W=E|<TDZM+l^xT(wv;Ero zWn1q`FHU(>H04IjG}nw5icwQ9YzUW_x=P4R-0t;V%i|xyA76<!UjI!h_sxded(&^< z{?D+-Y_8?G>yj(2nK#|8H+y};-)?Vd$kg;rn|3MWyTpZD{v{6D>+?4K)7c+q4rlkU z)Xv<<#+;J)>$c&){@v~sZ??VR?cDzQ^(PD8-8Xo*e=1Qga_ye288RWnbJ;2jt<(<B zg(pB$YtyIvu>bg<LBIXY9NxIQr)1Kz%luBht-a5bu6^a|d%k@uj)uHlwW`vmSJl?k zWJiY0L?)MOR_j-Q)_^R}{KNAixA)fjTekzg>GAe`IaTIkTipL<?_=L9TjsXTu2egd zcdKr1x7H5P^(R7ozVZvZU0JluZE6zV5s-H8Pu35&`_wF4efwtK;r_~p-{o!}erLPU zc~{l@nmpge9k0yVJ~T<yUfyC<X}dkvRnI>^ZSiua4?gn8UavFiiP*mDyTk2z9sceq z6}6o$x2F7^eSOPq>Gg(tJ+8`D-<o|n{Ym-!$i&y#t3O&h*QXfl^zvGJ*}HqTdXVNT z_53}pT4^CuI3+;a>&!F$)W7z7aKk!!t@$#&O{+6=?%usteVf_Uck-I$^JZILnLV3# zw^_)gYd_^yt<sV?#y0b5q-4*lC6-p89MHJ;{RjI+<>&LB?w+>rw{UKr?DYw^E%Oc@ z%E>!<_qTO=nO}Iiiox4G)4tt~kXb5cuOI2`&*ZEeY4=W)(|!V|QZifr>C=xhd#BHP z^Yr%a>F<lD?!Ij;cd_>Qj;F8Z@O^$$<LW0|{r>LOjcV&A=RGW+R@byHUNy&{t*<g= zmcgVGZ)&gas$B6y^5d%g+}Eu04L2=2`}W_`7k58?yq)>*)6S?@vrJ#TJG}hXmEY5a zC&VjxhQ}Mdxy9DBIq1q|Z~jT@;hXtO-%b1x|Md9B&yrh=W@~H5-?qNZKk;m7S`_pC z-SL%qzj{<;4fhyEKVENeLwM%Nki%Wal8k3WrOiIlly#*;=e=s{-s`)<%h(UR@7p{5 z&dGbyiy3#M+_@TAJAKD@J9ha!=3>T)@Asa(_Ex&|--CO%^}6=S$UQvyR;{OY^I5?# zu0^2}xIyPcl!_mG-#^7-?$YM|oa??_H)|j7`RAONk+tb+sL6kZm-6*bjU#K%henGm zUA*?l_0T-7h2Qdcw54A%9RTUp_o$J6>EFNm(Co6(ukopC&G_Z#9LedOJpawxr#q^C zuQ!yQv`XsCKJ&CUZ{~&7n3+xTTzN6`u)E@-MwgIh7Jot2kylN_znFTdI?s1|5AUw# zYOcE&lAo2koIUYjS$y)lyK}ex7W^jXS#(=(eO2xmHLv-?bJpJ8y6B^~O4O2Qou!9X z$i2R6<M@Gp!HVjjxGRU}9ld?|dfpxXgy&^yci!xM*!Y_F<P8fCjRlMD%{<%Lb1FDd zyV#hggg=anLwjkf+#*XZP(hyc*1o?s^Umz$>$Xq(c`PhE-D=zXjhbaGUv6@BS*&>U zR(EdTM)QIdZDO1u*=ApZk4xr%_AAu~xt{UEe}>bWAFFY%`@TQ?@wRh$vP*B+W?zf- z-KiN}_#|%G;<T@_x~y`i7B{V(f7(CTnaxU3<@D<*P0_DGE$y-g|HAHzY*%|Vzv$ZI z-*WT%{4U(8z3IQH@b2P0FRXsGZ`)QfBkkz>UA}Yur}$_+UDnO)H<ja}sm<c(io?4> zLvyWX!Vli>kLZaHO-#7;b*sd>Id9JXy=|@6wp}+o+*j<~n>}uC59Ylrn&=a0G~-X3 zdDZn}norqGJ~6R_M$=xH-j+Yj{YdWCq4#fh>Yct5y_j{sna$k9KjxMBd++W{x>=id zvv7yv&DwG^Z(ZH6Ge<6_AG+pnrAXB$c#-G?7SIr`*V*+Sl$VsfvlsQAbn4r(zv~W{ zO^^Eamalx{ZtK$bHb?DVzOvL>a--mKNZRUG%8L#s`KGjaMF^R!n%I4I?OD*W&;_f0 zIDY)JPf~OBx4o&Qs_rMY{qziX4WGJd#p1PZJI(IaU7ojX-7KezA&FWAm6i)9H8wUL zo()=`$ZlQzGrK0|c17;Tt(nqWR-2i9zES;st>&)(42HLU-`Cw_7I*f|j#Wx`Z_J&% z?e(Y2j|	%vuta$>pia<NJ7d#^W!b(y(y99mk)tH?ws9vc0-JvucOAx<hiE$@N*e z2PZ9>Ww-Hg%Ey^Kp{62VJ}ucYa~01tCC=ovPHH@J?tptrt?{28{&+a~-VER8v-&q0 zr%O(`Q9WIEYf<gQU9Zf{KHb}!>FnKq^Y-nET>D4d9cSLj)QA*rId^eQ=%hs_17-U3 zK?gllKjC+K^4q+qq&(%#o27F68>_T8+V8dSy7gr0%FWj9_6`Men>T4KZEcydW%go; zEpJj8yH2}HZ0!V9BAy-pV*WG8n>{NoeDdvTeopQgy~6Kz)9e!ytCOn(zP>wmt}Ik7 zY<fkW?A$F%r~Wf=9(roTKYR5&r4<*0LPI=FIAwZQgO+UWcdl`Ialcn`&$sMlW@mmI zmfKyp@!Q_Mec_2KGJHXD`<Aa?dNBRfMxVN?+f!XEE&u&zP|e?Y!lw0kdIG5L+U}!w zt(|+$-N+lCZk~E+9vWx0E~n00)ih3D;KnQcjS&SQL3Q_xxR(AsIn`oPeBkr@yS{t8 zTlUho_0op?S-I_wp^oB7WoB>R+Md}OdimPlbMNon6hD>zTETeow<}*o^Sq91bCCg+ z*dkTupH_d2+FpAhf9A%!oy*zp<lq0DoT8uhUj9q&HtVc)S-<8Sdv_!!?Bq5^t<6&< zy=H1DPtsK64>s%#IPv{^+{@G4e>VTqPTKTyO=}r*<-0klW!&{|_wL^EJwGX*=i9Z{ zd+ak#2Ach9UT+vWGc9&);EZXj{AFEuzl(T0)x4()YPLFUKfb@c*74(R)1BpUXKG*W z_`dA*;fH3&f~&rneLbJQ_RMbgoTlC7nHQdH-dd{b*mP8r#Z@5Kq)TP$+ylyYtCt*4 zs$u+b^wwSOoBO59-yNG=P@0}o=J(|7-HY9;PKEKb$hPnNp8x&c_M!)yPCVMq9q+y4 zKSR~b1*ry1p|vWYRIm73{%7)^$hWeuXWqL#S$zM>SLSB!Yd8O1o%f(xzVyMjyjOSk zy3MevZQlKz@7lr>?j0wef7tPNr@L3mBwOQyTx+_HfL1Optd{?9dC|wxYQAm#tdl)| zJ>I)L{>0ri(WX(ay>CgM=bw8py=d}k(^VPEkH6De8kP2UOBzcNXz^>4P5-B}KhE0P z@w|PpTY3HG2)BDz4wsdlPkpy{`mRm4>o*!NE_!!%_3SebR=-p~m5{wzWAb5{?Ny$F zK6)+1@Ah73p87-m<9)+zzt?A8eS7ERp_+!*Yo}kG+s}DtXXUD*Xy=3kwp+JLwp{&c zDttZo+T};bW@`ELnw)=KrURaAO#SKnXPwpOZN|TY>%YeN+iv(iPkxV8^A6(*=e^~r z_7TsE{$`unn(Xe|oe&tl?%v9~Un8PC7X5B(P}#J6nrkMvC1^7E_<shS`CT{OojX!i z6Jh%@FnHT<<Fc}6H*T8#ZQNyKo4nn8dqQ&Yjk~{NyI#M#_4C^ulidf+b#u0EdB)bc zZ0=2_*9o=Pch$T;-(1c7_(^qtVToRFt>f;Dvb49i=eln%|Id(?pL_jUY_8g)Phm$r zn{G+!to1XSEX<bRc8aISmsM+4MV!Ss&^R35e+H>v&fFhQFR}W*Y(r_#6U}dF0zubP zS6Qw)oA!ED`l<AyuX=7fRo9;mJM&<pj=-wP$M=`M+yDRC-QS!31=du(ynC|umh9Zy zx7E|~VwM#6^GO>{>$5uLWi?$eIAyaZ*O}eb!hxFHvayxRmX$rQ1l3moW&XY;?I(1% zx_7<p+wwZ@;jOI#s%b{NueWY{_w{Inc2Zx&>y1KBGhRPx2z3aZR#g`C!jT8m+nYRB z_hb0u_~Xix>)Kw-TlGrwv&Y8N*Jf-d4&?6oGl%1m%kp(6k~0>rTeZxSAyN25G7lpY zXr;A0XsIBXi$Ezo6k#}7`~Uil?Csn9^w;g4zVY^sUF$cOPkV9x`@?s(y}Qc%{jQhm zNk0*fODQ}2_Lg4z-AbNP(Hhx8wrif^^1@G}ruC_IgelZUfoj_fv*`t4H3C1TU-)gk z`P;eZ^0IZ6n<a9WUin>Llv|-)XwtMARDtf=8hyDe?W485WW%euOCx>zSDg$CKkmh9 zm05Oe`}epP*K0z4x?a1lU)3#t`)^{<+m7jm({EeuN%>lFrZ?>F-3ZR)ZsVp^Cmw1W zS@`r!nO8A4OXHRLvRRse7f;w!uP;fl{k^^<U|(9<`xEmfy;iw@Uw+r}wYxn(=*~F( zP3v2EI^WuZZ+$1X9=e*U^_{1(x$D&0l#QOgOBZQ1sNVVmny>YUf4ch9_04q!fBbIT zSFN^QD!wRdx6o$u$SvO%<#BG8sqCHTIdN8Z-g~dCQ*+9+R@v0OTGntiSY?Cqta7(s z2H)T9z4#rpY{t7jU#31TC-(XNO;>hu-HAJKYj%5i%8HLBXQQXAUT}NHtUX6#&CDk1 zTn)W^`QoO-5>KBf)PYj8+x*j;f1dvK{9N7DTM}>D58at|^2guX5pQo#c^&um=G?;@ ztCJRIyKepWa?ygHteecXy6ikR`_!|4*N^)1RyA$q5qx_RG&$4vb^mACAMUsJ9-a5X zZ^uvZ>$5j+HT!s8)$P{NCr>7%s^3}^yi3#UT<8NIyH{&(*NOdpvGMYilfFw{g9p_t zgX<0WJ4J7Mul#<@dWXSH*`4<)`;Onf>l^LvI(_Q)W92K8&Ya%AF`~onVA}699JjwF z9$94YR*)m=5okRDf1mt$^9T3K|HRvCZlCtAdC9hxyUTa|+SHu?zM@FzT~Awb-jh!v zSN0z^thmKBYt56HJbsp>#Q0dwG>@In%QS6oVPN!+*~I!}~HvKJK00k)N8^f7Hn5 zeEnOW@pYQpv}M~({r7D%)7vzwU471N-PT3xEj>P770pubGe~P;(_5e%eYbjr>HRaz zt5#K4KmK>`mh}F$+wJ|nFLx+QKVcJ}d?~MMXHjUh{`$g|Q)ioQGuSfYR#(nB!HjOx zn6)-Rm#1ner|kf>fmVOK|D65k{;9jA%gXYd%I>k(m)<+!I=wi*%EBn(?Z)1Yr2V}i zUN`6L6=fB<7?9jpHLdq%=BZ65LK~UyRxkR`u*H6s`tjc1_ao13y!CrFzkXb<UdQS0 zb?@HloVlsH-L?Jbg?B!6{4yUNf1Ytm?Bv_G2Yq9oFF5INo;6WQ;gc|E5i)PZe}?(; zBA;SQcUs%YU0T|lpR03DI6vKRCy(5tWAD9Uo;}}rYsQLgvSnJl55uE(D+R0RDlICG z7trV1%F<u@u1&Z9{$2MQe<MThN?TVx{&csvZ^_xKsh<yN?Fu`%?UrzT#f#z@w{}$+ zRn)9wefh*|S5!yxipC4=4xrK9xNYA*Yy9xvWmfn5{*AtGw~KQ5cHA`iJ9TIA){w|O zwUV!2bsagj?#sbhZ&Gb$PtSFqT<EJB%XGzA)cx&7UQl&1$7uf<`$uwI^HZ;;{Z5-L zDEE7z<Q%Uj+rGV6JX><R>e=(#Gq$I_YsxV?yV-17u(8jFi%&Lnd%K4m`si`s{{3Cw z{VMZk$y#nwd26-r(EZ;=lf^e$GxJ{m(WSfWcYpD_JNxp@x4kr1730ZWrK5a3&0ICi zs%%x?e1WYmGNzx3V7T@@?uEtK{|rX;{UxzyKQ3SO!P4vO<&K}4TYrm}J=iVJ-*+<P z&EM_4uUDzh*7e`{Nz*0i;1h+l&b%R}ZR`s#oizvmB}BWD^3#3|Kcy$%k%(J(|H|(9 zy4zm7+;sKEOT(Exy0Kw**RPz`FT2}d!?PEL@8{jJwAvwOl`~g<R#$qEm=h=vd(Zs& z^kZxDxw3b+SHF2BJmbX)t^JpBJo`?o+2^Jy^v!(vZE09pmBfbh@>IKDjGn!sY^xTZ zYZYh%Ee3X+AOD}h@Lv0h7qv_8>^;Bx+w-vNd40!Dl*a7Vjw@H+Q4rhla>kjr=XRZb zD9z7SH@hyitj|vCu+x<VdnU8AoZu4s9{0li)So{;W_G;pU3jaw?4Hr~?Du>6!UeZ! ze)zHHU3O{sQ?n0ccYgdVi_@15-Sr~t+akAUt;rXU6~5B9YjCuCeb@B;`)B$e&aYi| zyD%^B!|hh{v_r?fEfZSJ?m6Sm72~h-g4JeJ)&6G)jNSh6rc{*dQZX+_k*}Ax6kOi= zXK7CEyS*2RKWqP-St5UwcjB(sncv0a>&xP#xi@|KQQO(O@`Nd$%los38;|7n)CHG5 zF1)Fuckg7Ll+5+Jk27B_>8pCN_^Oc61kh#xnWsOuf1JekV9AS<yX|G=6T<T{zM1N{ zOV9lG)_4EXg*kJgS<RP!_|Ncec7A4t;L@1*d$kWk?YDyxVe=XB&ze6fcmHSD8}eIu z>&oqA!8vd3j^CY_Y-pi-^vRuzr8YCYa=*9T5;e70q4C}9(e14>W+ts#xOh_53J;zW zBF&)Qw0-D*22=g6-H(^-^51D*_j=81mv?KIn|)qzJGwjno!P0}+@0Ptazx`E-{bc0 znEZR|mIWuLH2>IIuzW#fjMgd8Tu)zq;di|gFZ(u~`Ih$PPDGBM+^jy6gIAuU&$;w` z^G}b+vUB`$Gk$1me=^<q>sz0O;?l0nr`KP&*;Idzdtv=!_NV&Hnx(IJ>XVXu)r5;4 zrRMIx6|Lme+2+6Y`(0KYQOn-VbC<1eYFF)_WL4GiYJq5hqt2A#qsmIJHhhnJu~+_C z?gh*HljbI0kgeYFVfMD^<%z%BEq9k6*;=~8hwa9ZQc+cvnK{?gH774vemHLpPwClU z|L|zfpf6X7f(^>|9{3*jBKfww?LPIqyF2%nhv{$Mu;oYG_M^`8?_7TNVzYEr@pf&k zRyO^j2P=vzeo5UuyM1#ddsLOwotjdUuai$qJaXpUg5T>)^h)N}e|0~RyFGT7)#4Jp zhYOCRsvb(uSXFq_N31?SP04k}<8rpU=Po|pT#<EB^KPokiY8+fAIUskQ0pQ(`#;0X z>xX2@iuJcWf6o)LbB~*U#RL2Hix0NDmfW0s`T9@JkF(FG=k{+obY}Pbinp)2(zAME z8Ru<Pj92e>2X#fgbg%w7{7*jT@7B%bb>TZcno5_~<W9M_W6m^w(fM}*lfPxInJ;Vi z=B{ST(d*t@%--_3O{%JzwP(_$!~V85E0wj~9#pR{onQQ)!JxnWw|)J=YnAt29teIn zQ}u@7m8GU~>56Sfj^8SD*N?fDUarb!wV1Kxc)p`acHfGXEBd6SPYjiq#_)T6seNJ1 zmmhPtS_f7oTi=nevO6%j;^D4WJQ+()UrAn7&2O#c?z&;+c2-lC8=Bvzcq^R@?u}{= z_2#^8q;v-~rcl}OGx$Q}sk~ilk9~6!%Cft0B0nbX;OfF~f8Tj)Uhm54I{RYc`9Ob> zl9dfhmrQ(K*^?p4$pX^Iz2#qJjqklF)$f&C-`Vb!Ghg??_}$rq6VEtLJ`ix_Er(Vw z=iT3Vt4&><`QF^R9Da*q^~Yt)W<|~wjM==x?)6>HyRx5*SJ=5F7E7L6dN=;~!`nMe zj^yd&mZ=+VFK1&`G_|@Gba2+~-EHlXvyT1!D!k^(TOF>+M@khz`!*f)oS&a}ep9=0 z#+}&NH-2mNRa8rF+@{7ayV>o<ovEen>w3~{^}OW_3{y4w?({hnizbO33wRZj#bFL= zNb#?%pLzfI+KV4QKVDv1w(jY%(rLj@_#+?f_L`m*^q)ceZsFV5l~dPFmKS?=r_#nw zE9JnOixI2&Kjt)Dx+po@ZLdRr-RryDyQlwbsByhzyD;$b&Ua4_TztQZ=iP<dGrL@u zoDNC+AnAAD@P+4PPI<CteI51g9K4jbr_M{*F)%n(M%2inS7tA0dz9;chP?j_CUr~h zM)uuaxP0;2TT||;ermV>c)57l*8}-lGV4Cg&nsQ$Tpt!vx>Qu`{>qqlPgmx|dO164 zJGIT4xh#`mlEA;$ch&4<|1+4xZvMo-_Pcs<*^;^0mw)q3&rGrA=4_p>zxVO|FV+h0 zewNL<dm`^l+{;_i)goJecdat@swmtlx-V<xB#zVj7T8|jB~`WlXZx3Xk6u_<Z{G4b zV&3lcwGV?gXaBm@TfDn@gF$rDvPG5KzFj_<^D4*cUFY{UcSfo3y}G6{E5E&oGcO4` ze7AaWV8i>m)pz$g+E?es-tE03o9e&4==aR;vfNW!GYkSkPET#h$O_|pX%u1<c#>z! zWu0Pos~I<)L1Viwc3=MU?Pc%EYqg>Ktq+@eU%lyf|91Wj>EetvlD%hV9{swt)cI<{ z_Ia<DAA0m^mDxSsvahZ>Kc#MMX=`h%lrx+D4b(`zFj@V~{$9>q!YO?>Zf}0~s`uu` zZ)w(fL9=%4*_Up5Q$lL(#f|q_-F<y^cCN8|e0z(i=$5Zv4n?UgUpDp60yj|gehzf5 z^zMCTe|{aSY5Zw>?>~e5{vA`Tlew>7exCZu@8Ep3sTTj99C^cg)jqDQwoY%#L5^j) z8~44O#WBrN?RS@v`j_?lOW*B!b$@=vxsyNF-m(|9-?3F<?#itXP9#;`-Y%`CWjJ@~ z{%>a5g45sWF7G<N)~YM{x?vud$FkX{{zV>How)e^uJ5k98ULKv=iIk_^-tS9R@y5y z>vRLl@1-xzR=Zt0f2Wb`U1xvKd#1L$y?2A&>P$O#?5)?1=XV})b-s9Ta<5a6S0uA2 zsQ0}2-^=&!R{r?8{p0TLt>#V-GB?k3J(T+<SI$~}-ksUg^%5qn^U1q?+d|8xuiP@+ z*O_Iiu&$|1n}l+v+$qrd`V0Jf{xi%tez>-?_hw|w^OD46OW*iTj=izl(ja2bzb9+X z=B#mx(ow6o=jCG;ov^yAKljPfk5Be=^g1q`RnQpGU;3_n?alkPsavYn%icA9mR&9$ z^Gu^)rjK5A(u3mJA2&ywJuVZRxXEHp-lgMDKUH#R-AH$jRBD}~%;W)@dDAzsKhyv4 zp8Hyj-*Mer<7(GVymz~K-N!Afx0LsP->$xU+mynyyA3PePAG6LyUx2-WZ@=bSGNm+ z4K?eweSf$2%H6Mjj@e0--g?_<E?=Jeyt^VN<F_@l9b3Qg&IK!9FOInJYwt&!+x4;& zyP9*7v;`NhUp?ozD7QA}sZ6u)aW7N%*5&+3c>VU~wz9YvZ|CsJC6^|bz02y$ir6G+ zBd5>5hb4E5(eBlS$tzxMnC&gQ>&WUqRyk9?oQ&XotYZnX{*?Yqc^M<yb9Y(YZ&sZA zt^H%F8<$R1U@D)`^5RQDYfIBEF1|ZI_3w>UcdwM>s7#lbnj0zgH<=w&u&~;z)m4?A zKN|PzrQEW&9MZDeem5tV9eW#l+m|oiaPsl6(kanZOCGH#+s7|C>EzRCySfccK6^{u znx&O=#^g?a>AMBae-2ya@m+nhcK?iXZ%@h1y(!CmIre_rJE@q%Pd@(M$M@*Y?c=!* zKUmbnit1byo%%L#>*J`31x8mw{DU6}wnu}Ou6pW!miSTayQy&Zx9!_A3xCX6pBVPl z)J$J-`*v5c5B-~07;LR>^KFgZw94S(yIH)K13MN@%FuYW#50SvC9#{K_WG`pGxY}Y z9Y6Fo-jv-Ndq<-EW^CugGf8Dju1MT@=4}~2@wU}}hP_)Rzs=&wb$fi=d$X9CWn-YX z_=lrGSvJX_BS_!H|6EX0b#uSs_WizPe9sTP-MhN^#$3<+W$V*|uA~(2;62>@w)A4( znJ2F{O1#=rck8xcm@kjulU2*TJX5CdW>tXt%@gj(8~!uTt-HDSrOo~Hhbz8+jV;sW zSJiJ;wBr}wVSMFfc*AYWyVw0a^==-_%lWF)sVd4E8xk6-sU+egng4F@72ds{)fet& z-cWAt`*nFjqPcobl4iMyMUHBSWl!0?J3HU5l|DY>NW%7<9q%7h{wyf{m2^_iE^B2* z+oD%o6W`yhUV8s{-p7M~+F#A#Pkp#T%V6@gkW_=+lXJpP%FMJ~zPs#e&5yN*w|$yt z&9e5`^~>`(SdN`I!VNm&+8MNCXfMHI6ZZeVu>0-Zd-3xBjFSI(e%}|oc3XYkpUyKi zC*R0zJdwC*_ol_7dlFvHnfA`&aimem)v3+QO$R1jSW>99DS{bvs;BUOhH1C$^WVn( zdb97tkMNJ5m;Ii<>YvS*?}6ph-rkIy^S18g>zBo|k6Z6~vAxVncVgqJlX+72j;}c$ za3RFiR>-_j)8wS2P{i-`C5HbQ^#3zFyL_!ywBzmC-cS0=%*|uo^&Y(a)wX^2_Vm!V z+fRJ;`MrPBi*37(Za3R@X}QOCzRSnn*6!G`VwK%bk-eTXduM>AiMDkgzaIS5@QbCD znZD=5#mm+$Eeq=T^0i>83(twlpo<=y?4adqtG@rsn#+CfKZ8b{P}%(B_xp{LSAJif zb1j#5;k*?m(-m%CbiKCq_hN~^y$?@Zi82f~yV|x$rTewsrp2JitcB0Do$u5}?x-^< zt#>L-k9%Tg|LCQ*S%1#cPq$~q?w#|Rcjne_*X-I8dt0|>J~a6~E$h^-V*fzlDFRN7 zM?p2s#o6EA?Y(5Z(ct#t!W`MR4L7AH$<E!gx!S+r(YASSR~`*c&)jzI$%&*@FAHYA z>d{SoX>{esoGBa6eYsSn9-?svbkKX&^pDqndj9!y;?J>Hao6AOTlk~-qmtfg*>3+k z^VRGW-qjv%ym#Lu(>(9miMJ~j-LtiIx$@gP<3EGwn$<B=lRc$9CW}X$kYWH0690Jm zXZ63Vho9qFeo1dLn|{2v^^*MQ<6F$<CqJljE!`GZweR##PPw4=!ej%BuDKg79G!G) z^E0LMKg(sdUb4EjMN1jfH}rhpv;VaB&(5#kGyXH^`|Wzu+kSCEP5BSePt{NEC+jCa z*#2|<y}9!@SD#y5^W?+Lz%5$JA09kCd~)aF<tr8^rA@olu)+L$+>2eQ{~0E||IFOI za-Z3Sqyp=F`CWCA-}j%|HFtT_Yq@`UE6;w<53A|BYx=vdy)roeO|Hu6jpcgR{CQQb zn0B1CRoSdGvGD!f>c!7L{%82<@y$Ny+w?;=hfCj`X<qls_+k60y0!N#cn{tP{?_Vu z_3gTEXOGWVa5TYKdY7cp&o|oQQ~bl1xC&dhGDd*9ITkPNpW1%9oS1vRDPqkv@gzs{ z<NKd}{_Op@?oZ$3^2f`&x;Iwu<GE5>@7$(&v`i>4R$|49{%<eauY*$9YQrBVKO|S? z)IU|e9?w?Z^!9A?%`f#*FRc!jh395!uKG~2`)=VnEv={PkGhM0%;H)zfoWChp2<${ zlfa|@{BP}_e*SF#RO4*&_x;gduOHu)yYeG@(igwWr{CN=wRp{${Jo#A*-qb7e#lqN zJKJUI?cn6RQ?V;PBy+c|J2LaD&yoiIUEjUK{>9JTzq{)F*6($Xf4tn0@+SA>&Z??a zygP5+Hz{XN`W}`qd9C=`iK5y3J2UI=FT8qtcD`ry`kbrWyN;=NU1&VSU;3_3KBzSO zgxs1R+@G|wzyHhEo^!`i$Gq~{H#gzJ=NoVTXQ<2d+kfWar=YjKi*Ho_XJBRtiS6I3 zp}1r1DW@f&o4bWTBkHU989$tUT7IU!b*|Rey^hyw?E|mhXW6*d@!E}5ws$+`l#3;A zwBKPmRnljV$cH0kapw-a-MeM;GHVUH-#^xTx?A!YwBnxOr|QT2Cp~j#eu}G``&w?t zo4t!a>o54$?)l94_WYtpW>@_kQ#M%aJ-+Sr=BpK}Ui^Dp_GZhGJpH{37aVB}7Yk#0 zV`2a*vcis?{!zcb`gr;Ka~EI89^O-}E%<TrlXv17hkdW#-57tda^8dGpsh=0`$O{H zEIaX5GRTUj?D?(Vv)V36E&euX!HVXSYe0SQ#rrqbv3=K%zy2rtj&<>hZ?7&Lp8PZP z-}G;8-`-{2ySVmtaZUGuSrO)bAMaH!6<WPvndpUMJ7>u_M65af=Gbh|RJ_~2*!^O4 zeqZ=A(^K!>*mFGmi=OrB46{)2pm%x?7rfUAx&5oXFf>f>@k^iO9qGFcuTVO4LTUN! zo$RK9XRd3%1Z~V{?tAqwX7A0LvZ0?Q3wEqreDK}b+n;ym{@MI`mG92IPjBv&{kv^j z+uhqgw%n@pecRr-dRv*l>9?(%yw2?+myQZsTmX$QyH4L+#k_NuwfaZrO)u<h(u`tv z--tispOEKS^?sY3TX^}hFL!R--FRW{L6@DjcW+s0Jil=}xq#o~vAnLO$%LuPqdLJ& z@Z+WT5AUC5D|!3mXJyVSx4Ad(7T@^Y{KTDkKYm=>{hPgE@#`$PUuScC?#V6Ae!u5T zde)OqyVGKuMJ%^>hJ+uEHoqP7xAyw3+*kQe7v8hYY<x4v%}=&@$LsIy2XYTz{BpG< zwB$qLirvYjWo*F}*K&Pi<^0Uo9MAFbF?}r~?7<`!lw+}d*LTNx{~3NTf7Z`0DX*xh zxe*(EEq1=+r=sS^VNdVKcJAEGEZVl_)tvXiZ{MxIwflaSx8sC9pSCBfCe2Pf8M^)T ziWxzdx18A=aqHjfyS8Qg2jgeoZT_U6n)ml+d+tNe>-J{f?$=)WCg;EB;lfwH=FL7j z=dPZ4Zt4BwzdRntuD<a@CoGI3`%~$4(X+nF$DW)`e7E<)&1>}xzc=T6U4Bq@VSM+^ z9edWhezQNFd+?TMNu*9qr*Hea6}wmIt`2tAHOV$Bikfy`;`!=MPYvg+oqo30cj-!= zZ@6!`|HAv@HD$M6nce(*`&zB&tQTE*r(Ww%_~AQy&TUDZvhOR-JSg7V?ebFX=G%Sk z=P!H8mfX&EeyXt2smC^4?)BZM_S5f{|FqrrPd6ua`nBlpAMZ0Cn7fBQ`5jo+_F%!Q zwFhrX2j`uZd=n!conQ4Mb<u0x^E*1O_np`g=*B264jOmn{lNTic|pm)OFwUK|D7#h zCaZr+?&|uxzZD#B8kf!ca5#7G;Y+u@*Ds%4k#E&2D%tzF_f+DuNvoFB9r;}K(9Q_7 z=~4Y={?n(Q*|%=ARlZ=oI<a(m=-2m-HDy1QFKqeyef##cZ2krLtNxuh81S%6@AZ+l zdrC5QY_RZpQMl22{_@rK<*G58ZNh^<dle5S*6Yc=IQ{k6k410nI#!;1ZzF#E?aoUT z%=hN&Zx1iLkpJZN?ddt`t5@!NHD6%j<6pD$!na+InqwPU@K3Yrq*R*rqb{e~>$^6} zKfQffUg=kL{<kBt_v3eKlr4LFSGsER3aihXcB}K>ja|CIpyKXsIqRaJe7%Q<4vU(7 zU4Htx@cEuFU0q%QLCdS}?^Z8*d!?RvcYfM?{;tS9*KXZ-?Rh0$(=NZ9?eiq5H=!XP zA5=dsdj6@JUERe-_4Q{_lSqkwFC9Mx-}jaS4dl9)AOCdw=i|tWJM+4HOr+Om=jBa! zzqWU4*}LoSv+CB}*v-7bT6pX3`DLzi-^t`zsh;}IcKuxL*{AIyAtGl^_N-n$#iKEH zM)mrVw2%K8PG5f_^u_<w-07j)%e?aK?!3O=^gBI$<GG|~eLnri=hz9$@BQ~#`EmY+ zroP2e@12hPd!XzJURtL2<Awi(`SM9G-z{GMYVG3Pzlw|gGnm*-em7&E<FdNG-4)4? z-^@F7Wan+8H#Z*5E_C0yVP?U_@Mp4ZRx$Bh&L2T-)KK$JKYzCFYWFUYdwA=%dCFIN zk39YvBJ1x}^!{gXj-Pc;BJR_HCmFwf+iy3^Rhx6|-m2qQ!Z|)&xO&Uq+eB`X?eF!a z%`57sO)u3ua`J85>6iJBXU|@5XPmZ6Iw$S-;irA?-u`X85h}@9*7YRw*YCtf7sc0X zt9#g;zSKp3$~I@=uq*dOFN>_X_xi5<`==K#&R4NY_|EuFa(f%|+V8w>C;S$_x_9nv zjC)O4+_CALeW!!oOZBWTNK))9{vB^>@<qFC;fy^$URAk;n}g;zM0zKF*#5NN;alF5 z_r5c){;j@adG_M`pP8GMzs~vg<M4wOA9tJYEkAeh-fi=ZnV)W&OqO`<9=vtmZPT6& zYiIS9MdaN9H5@kOoPV19Y4O@^<$?8Lx1tws%>K@P#BbLNzDuvyt^64tec^U^LcZ=? z)1<I{NmUW|_zu3BvsN{ua^(xhlUyBrEtd{XzgxXHcE|qd>iL0R%FoNKzxlg3#X0h> z<$s1w<z?Z&PhGv(yZ7R4?xoxAm2v$&Jm2kn@P7v0wKsk;x~mq~UU{8vmg)QV`mTaI z@|<-ZPpc<aoMk(A+c{<XXUTOxi<|Fl%I8tA+}pgX&fs~~vi(B83-}-1_<L~s_u>O8 zLjUgih{}2VXK)5pP}|?tKehdoTwYps<92^+$L*PKyD!9Qb}VVmd;ikxt=seabA9iB zja@n6=EQe3TBc?9x~&_yMa5!67cE&=QvPKjsOadQ@Gm}g``p*P_xEYr*}OacX72Gj z(&9Ct56r{Q+T45jQ!mpj?DC3RU!qs1{AtOxmQQ%E6MS`|tBmT(SB6;^u9{B;b!7Vb zPX4(1NniEP&fU(B|1*^4{pvZN^X~2LTLrnN8slAO+}UBfcgD%<Ps`ZO-nCvWEF)rl zxH8$Wn^(-+kgH|=%Csplpcb>h-QOS0ckKSppnNr6?bqY1GP_@gzwsZoo%*qU!>{_Z zn*NhD>8oDveZHYcx~spq_qMgmhJ4$JYT^3P-6t<xzOB&h{-iO+0(7s3b<TeV{dvD` z$s6snsabaD4d3cT2Wzr!N{c^syYu?U9sA>-q?=c0Z`<Z;em|?ecDwARPw(FL@7N^r zrMgJ!<M-9=eLFdM!$3ndVGs8g%if;xLhta2H@5AL+kc;0oSx5B+q}7)@73X}kHyQl zs(#t7)(b9r9Qkp=#n7`4R~B&^Dre*efJSIm)Z~9U@NR!kgn!4})gSk7-p#+gdjI3S zZsGhp&G@s*vfQulZqE94PHI{A_WCBCNvdLc+a?<aX}@ST?u@hl>H=ERsCV^WSnbWL zv9%wIw{L6D&HsDz)8ysVi`w^@KaYJUcX~?6vtrBJkzGCQCu^5)U%9+9vv9|oo99D3 zy$#EFGLo3Ta#yb}N&F}ApFziN-D}&`e;>VHxBc)tzgzA5tXFT%aIJg1_O5kOlke3# z()%mk#C(+#T;<2cbCr)-+w1YS$d#)~<*(`4Uf&f^Z|L7yyZY|$*N^*4zk1%co~(GW z()Ydp+}D14-_88RU6Ge#_wyi6RO+|&$+MF_-kif)BWN-qY)ad*HqXUjLPnsZD)Zyz zpZ&k(*Hr5>?|HS2?cQw(uK68*|8Cf{BW8Wt^>6p;g>ToMyj^np<$@D=e=l;LNL#+{ z=&}1pR=vDx_^A3g$4AgHH4BaWmi%0K@jt_<%S-C)O4P30{&|1W@9>qocCR$|@2`G) zM<zO4(z9dP;rzSDH*c4F$ew>aPsf!dNKgN4M$fxV^$DQ7uXXD`gS?$f={5hy=0&CM zhjYU2%zoT==Iy^}(;u&U|L)(G(|NJ?+uoMlyt(rCs{YL%(`pyYUbVdF?a3$SRZCZI zQd;NUf5idRJ9J;Rf7<?v(rxlvGKx!nb6ZZhzn`W0@bb*13sx`d7wvf4ckjOS=4$sX z1s}o}Uk%LVakyg`Y#Mo4q+i%}%Lci>*LN{*l0S9%iI~3S&J8zn*Y5dUoL+uCqQvgn zJ(CFjX_5D)->@y8{(A1J-|?jnt(DUshJLD@y5*@+=f_Ej{&(FvF7A+O<}ZEMGf(_s z`N_AFKO2kgvs#|txAKPZ_d4SZzr`za-`(#vy;M8>no_UAxwn6|AMg2M^!#J&*Qv`~ zm(^bNI_>vvUzm9^s4u4c>H8`>ch67TAIG1x>v_4i|L%=_%U=EadE@uujJ({JEjKOq zKa!p4yEXat*4g#yI|M(j_T~-a{m;<R{?%47rT-czIIdPD)lb`g=HKF5w>Rl+Ih&;Q z$#UM3mojD#mfo^{)SsFcd)&@^&+qx$s)N^B-7kNtG`H%NkE?!a(51xUVhu-GO=jO8 z-{W37emwlMxMu3jzt6YqR<F)kBPO2}^yf+bq)>^!CvQ#oHt*ZQo2L6^%kRf9?=H8k zI&pjXh8N~#NfWMwI94q2w3)DN26!oA<JNzYf7ZR|yL_ki^Tu7f*Zr>7pYkq#R>_sv z$XWLyWcbXQ*W0<2sZ`%i`X<X;7iQeLeAR*q7RfVlQ&-QHdn;mI4C;p1<lS$mSNuFX z_~}_bcj>E<wKwnF7p)Rs`R!`no#jX0`mPS&SH|S5_k7{e2lLc&mv7P3y1MU}`KFf) zH$bBtjX&(CxtAqXx7Q|`vt7S0yZM^z_B(%5?;5{5|1D>`dhFMAXQyX9Hsd()(EPd2 z!>OCCFBd#8j@Es8G&H$$>B>G2p@mbGCz$X0?#%D?$N#75#r@isH*VaqV^{FPZO^{G z-TU`W@#EiV^Q%hY)8EP-?LHr#{Hp5w;!gtJJ9i~-sPDcS9G<RywN*E8=^QudGoZC{ zed(Vb-i+?fE9v`n?{~j+eB<=}|D4M0PuRHKKIN)v=biiZ+?hMwkxfU!gS|voF8AUR zo$cB>4Seb7e+GiXRN#TE{};EvpFjWf<LCaT&%M1nQ|evU%ay-2v+mx`>K^;VOg{T@ z)%>!29@i44$-+CVg-tt;IPWf!+@&ERzjfBy7hju7q(Niz?nm>x>(B0g_)Wa_q1ED* zyWBji#fu-A`#oB9wv0b+*3GO9(ay}93b(Xn=`Q75wY*<&d*Z}<xj`pGbuMido7nk1 z?!~ru_n$`oIl6sccI`W!>)~(H?#Rx+@iG7J`3e0`-prW8Dq33R&#&hZ_~wtC*<^M# zyJ`K>CCfkD3qBkNS|fIQan9pchSvG{k5+xDy=KhbdHl+cb1VKm>T8-eD_=Sy%U>n! z*d5R?t{s`@B`#uQ5#X>9sAT^O8U|U<Ai}VKfwh6@0OJ=1KF}`d>veyx?|LxtxWHv8 zl`PMs6|0(qr8h@9h$nxw2QAYp{{9YeF;7Wo)X^zI%Y!B{UdnMj`mYUi0#GdIgg?*> z_;=EeK7Fxj>sF0yfkO|Z1-~f$XK(_w{O%KN%(@0Hodt|VISfos?##1S0lBuExRgU& zP=Z5F>4=;o`zww=pbq}J1KXJ!EEwuQ&LnPqUH$({cb4z0pV5CvzQ20W`qb~{C;u(H zF?(BEMds?Vea^)Pqjwm7H9r2dZN7Tv;bm^Sr2}<p))XI?7O(?dRiXQH|8wIHn-#;o z8(#2y`~9<e-|Hhu{p<G`e|`7(e&pLy&Q-meB6E7p?%%Sos#@K&K5}BqCRs0u+S=>8 z+~3&Goo}*fzJZ<9SN@_u0XOF|Ua?o)AiH$JZIhe#PXGJ)y;j#hF5uB8x!lXm2QCEV z)~=TIu{7Ol74k&xPbKTl?{TjlgC@1--i_X$`Nl^!@A<voi}T*x-P#+H)A#Uh<et2H z2QTRKZ9H~$XU<+<Syru+%B(@3bZ<0(XDi;Q6?5*~Xz}OYoij<_*|>FmW%IO6TTTR} z7Ft?ax;|Qc{K*Qnd3~E^IsQuNz3Knp_xHG$m!aoeUWyjpSLRo3KkwEI-N#!uM=#FW zqP_FmJ@tY|MV5beTvht=dfwrSSI<s7yyfu}QNiOYYC-GBmx9(WfXf8rnB5R^<#J$f zNLW~iqo=zk%U09SP^06lrlux>TQ*+_@z+)fILPA>dAB1pz|-veyS>+B^1NkdELeQ% zk@kyCJNERNn9le!&1@CdE3Q{wEkcf=?vdUqTfT-|K6Y^8#hH=52SM}3!JzeUu%zH4 z8ofR{;Jm+g>`nubQq#&)wHv+tLuVWhJ|Da@>&1OV@9ire&04B5+cMZu*k4wuIpD*} zf>uXR!>t}UlbMA)`*0;vCrxUzZqJf4X_L13m}xEBH;rdnq~XM)dafU*7@e9L`m(>P zB=d~dBh^yf1kiZeoWH-<mx3H(`p9@j>_M-qH)Qtdo4ZUkHSv$^I1!YxGH6p@SktD7 zt2Ca@5?#4;rkAIr)x<^~(B(JRC5&%at=U_4f7zKwS;jYZRYmQYC&be$bF-#YHE;c< zRqKu&Hx3I5zWTyWeWGZ}#!J4S1E^2S&pGsTmC=?NYxe1@hc>PMCpvrC@h6(vSq3-l zw=G}$Pj+hCtVosP*Z<Cq3Hp)7sooWmotfaj>$@K)T_cwiuT*7g`5aXyHEo-;%OZW+ ztVoSliYq-<C0L~`pZKsmWS-c=2{)(w1$l9?_v}N5r!T$zIrU><`sx+0icBIrvtKHo zzLxyrNtf2HUoqLIw6!!|rp^`f4CGR_YEuBsmn;XJjc@w+^Ng4+KdoZI;>9PgJap|p z1Miv3k3We_-TbIXPUDr`VRc@UO`eX?52nf=)o{OX;MNgP@1V-(o|w*Fv-{KaySr^8 zT>~dAIpjHEiO=3Jt4SUUSFK-mY|El;Ra%+LT_%M(OBt_F5dswpFN|kI&DmPUG+V!W z#kof>GhP=}L>P%odb;sSq_ENBSFbl-Jau)-l&xod71}%ngMy_q)jhZG`tFy~$~vPp zlx4FJ0|WOY&j$>k;ehLH=N_8N+39S%cP44`jh*u{9-d6xBr?;dW>fH~3nA{JsXUi2 zpZMU@)5k2dhQkb$8vQ`yD&L7NKi4B?7L43OJAwa~jqG*4FF(e+bH-2W?biJE7k}69 zKL5Gtw)M3A()&_wN}M}7fA#FNyw+}G{f|E`7lgeD)a<Fu`=W7#du0OuuJ7*Q|Agyz z{W`Dj`pKw0?Okklbg0^;A9L5WXQvwY2CH1Lm-|*~mic7q(OsuLT}pj4O>0uflofpI zUxP+y7Uusv|DgJM@t<S=!YkhQEG|8K=k<PTwcS~%-}WVowl6%GoRR(O)#9qB=BJ;| zO+1;Cxaslwu1$Rkm(QgHG=b{t8~b`fbyYRiR-~*E`M|YP^Xeq_Re~BTDnw7Fwl3Wn zP{|Ey%4I+Q&%nj!6PLg4AO9PzuHL0$#npe#-<VQeHhou-(W8I5sab{*bC!AS7XK8q zPV!;m##b+uw`$#r4Lw*3Iw@_b`6to0eyeZqXDbVjee(U;jYIG5Y(MdSa<zHx+gH7} z`}#BA^Pb&qt!*8<qtIS*!O162PP|*U=s?i9W`Qkr$*f!}Qym;YOKiV`W?s2({_Wgm z)xGxn_pJ9`FFww8Ui&TY>3!?@ipDH`{j6IHzRujU<jvUwh8Hhd%3lEu->j9~KlMMu z3EhX6_n#47{4cFcJ-B>-OwfM@)7bO(rLFh;cHeCFCgR@wJG<vs<z;SPeq`E>n~D>s zE?l(aXtLshNs*71?KOE5BERr^+=~x?!v7`QH~A2+SH9i%&-9<_-KV~lXXWNkTzj!3 zI&1gh-@*@<mQ^QQy!O=WTJrCg8_i3@lS{+&bAl_vb{gOK$a6+AWtugp52g1a|5@y3 z<-9NF&+|6kiC3`ZE~?piv!2U*`iZse{#^@R@|}74#y9TD+N_wqU$64czOp^JQ@3SS z<ndb%ji&_qKk!YQvl7$;k~5$G+2>u(zwjN4U-3`;p?lZ!`u!8zr9bs;-PY@O^-js{ z@+X@wCoWle=cd)1n$W_a^oy%hJ|{m-*f4GLp=F2f@A~c_f0BQ;{F&-SW#XUyzIx?9 zskVLJ_xRKN{j*m;`S;^pj-BwrZDvlB%agx-<J+G9Rqx)aw{tIV+V!7-f8$1Hm29Iz z)0~@LCzO+=R)Cg*7gqT{S^k;jr|)O`2h)Auzxy!x@l>^!KluB8%6{TbD=pQJf8P`K z@BR+HW79cOZcA^Mjoe`{<L%zvD<=B9bqo&|Sgxv+{R?z_N9lit8T~2W_#VCWyYWKw zCv)J(`D?%RAGmk){Vv<n?tkwro4k4ZR@b~Aw@iOW>xb{0`e^N~YJ1ME<!jb0TKw_K zshb=(PBrNMy}oNL-^QQOpPv5uoLKtaFYMji&3!*Eez^VU-kTe~U3dTLZ>&0BlWLxK zx^36ylJD%lLhe}1xmUc-cya(wuGOP6+zzi*=0&P#OMZ`g@$~4Y`%m~T|7>qQ`<*}S z-733f=gQ*_zC2SRYaMu9`ai=GgUZ;CTfV)uRS*7_cYNK6N4xVmZxrR*eD)T5acr5h z*cCNRlZxgY-{W57|4aSPps-s!tBjXD<g0ypS>L%kyQ?4n`|*CxQ?`RAO6v|BO5AX} zT>p=>?7s5<45?+IC6}(f)$&;(vQ<LI&urPcf_q`p3_va2iy^P#XYIXz@3(e(IeTi& z=EHw7;@14w{V{yUePP$FD~hVUcbV(wKmNFN_UXdB+ASNd=jEQ>{CZQUf75~mhfRzw zrv#i*DdOTUeK)aW_UBDj{M+~Wzq`Bq#_Y$Nilm!=9?iYGqw0698Go_U?Am?ePpXBw zxwmF$ZP1={TaxFQyR>U>)2bQA1Ex&bcwCYv)u?)X$-;9rdw)t_ziYg0cYXe!#dUY? z{%26NQ~R^xt?${5^=)rg_2eJF`|!GK@ecF2n|Et#`;G;7-QB%?*MEkBOGmeVno{Y$ zHtI}gxW059sLedZGVk-x<|`4t*Eg)#7vQ;XyMO8*jTiqJCfumKd;3D}+KqDO-|*ht z_pR*M-QVTO&ov4!CY9Z_d%NIH(0>LFNt1cAWhZ~!vsvPOe(AgZ@&ok~ejl$al|NCt zf7fT*-8+9LKfaf@uG@O^@Ac9fwYERIv-(ud#y#Jz-CJ|NHg@`{H|I_)Jr!(q+CScF z%jV-Et3@uX26b72zm-36&wEt9|B(Gm(Y<f(zP&wr{-gA>|6Wagyk*PpvMjlk_u}u| z+wFBm@A}ciUb|$`#+yaP0pXXP$a$)+IO=m&Cv|4<-Rjj+|6*cqe_CGsZJDtA&6{t& zJ%25C`p56Cx35<9oqMA<@q9i{*`E`k4`xZ7+i?4C^o}CSD!nD=gVMB`oF^_%JATre zr@m16#DVJdB~0)4PyWxK@S%3%r|+9L&euA9{ZHA?qt|8Cug8n+JDhE&Uz`*Bchc)K zcK&&JS;7K$CFks`n_L)rB|&S!B(VjP*wZ4f{i?mbOZ&I~6ZxcEy(uS3mj{*Er@x84 zxLxIE<^9`}#Wxnkrfw+MUDV8ddnZqwb@?~@q;H`)m!6c(v({9<alYyBWA)W@Hcoxk zcM4Qh3zp@dm!CLys(ird3HN{a?VbFjKB`{-PI}_EcXw~k&KI|j?E7>y?N#}`=Ler@ zuT1HitT=u3!oJxOKQ*&5UZ_laEvdp^_xi5;kMDb}nR)NazW#Wc{nKVu$8FZjf3}rv zldt)+;n2RT&6TOM*V<W><lHV_v*G8@mD$_s%r_XXRlam3Y^}+%^g~x(H2U<ce7E<) zj{gj2jQ9P@UsG0I_I~eQ|C_ee;gf!<f0WODo>5Rz@}uwC`?EVES@Vze9c%N_c+w?y z?`p)XyIUhJdSqsO@{twgykvWQm-GAiQ|#?F-kpETnq7Ry_mb-SkLDeFA-VF|seHZu z8?upmzuPC}Ufj7oE`P7)Zg$(oJCU_pC$FFKSR>`|T9Xnv;bfiTp5NnMo!Q5c*Z1`8 zzDqx2Z_J&0`}T6v_M3A*?)^18<*TVpg@>8k<pU3vEM4(gWy_BDk1F@<x^~6NIpV64 zp@*o6<yX+?U-P9uJAW?T|IfS3uR6!<$?b@^?LW)ekI(of_T>It)h)YXf8EpiQSdc; z&cQt2gWk7FYZveOSQ>x4;OYnGbY~fZOTO~!@9+BV#{RSP$@X&J8j+vX`#)NruPgWa zc(eB4XXA?8J8FMU9h`GQFFEzu_B4OStr?FOEL^m9`SLSYlOM5(UwOLpOQsm8##vkY z`;+jtUFDmr9zRVJeq#RHICkySf01*K-oEhW)v4OGvA$PtPcO~y+gr1>POO<*mn$I0 zce$>&!xc+TPyX(ruVVVk?^dsl`4=0z{8N5m*}SheYd6k1|ITjn>X&b)-rQZSc&oPi z((nCSUwmKp=)_XHFrGZ?6`yA^`d_W|DXzU{<@KuK7N>n;>hx)#<xAGLXMeBJKV$dp z$KUO{i`74V&nQ2BcHi~s)n(uA*XWA9YKq(S<L9-?w|h61Ez2&td@aFXXYS3&us5Gh zvwZgS_c<w&GEMnz^@8f`vhd_TeLr<SZr0yx7oqR<{M+{9{d0C(_3ta&BAw0}c}Ie8 z={wooZMSb9YW;jVx=G^1%Yuuc&&B4-YR~FQ>s-|PRejfYkF(S7#U8w2+xp4-sh#_i zu0NG;dzb&&S;fDzewW3s*q!dqcWy6ENUwe;yM4ylN1tEYhL*?WYi#(yc06Fll8K)S zW-Ncm{Cj;#<-?zipW>G9{APZ%X4<p$2fl4TIrseKxqq)t?eAFf>FxY}-i<4FGe7aW z-G18Z!cw_&YmRxgE}T|dT>1Ktv3MY8((d8ayEi{A-_7c$%>F63<jX(d_4|0s*-y;h z{Ce-gO&V-Z-qg<iq+gdicduTQ`k}u|D#Q=wzIeI%Wn!n)Mw$6aws(H7FYVs^FaOV^ z`+tS++*efoX*FMFd%WA%^M_a6op|j_#ocwgv$w9YKA-!w?Df6q!@Aw)Rd)6qH7nQL z{dz-1qti=Sku5^<cXJ(UukX6KpE1xhj+;9?;LKOih&iP$Z?5rN_I2l(w3&h7N^B>n zn|c{^2`lXy=M?q{_W!?f)A{F~ujXFQ!XNUzyFWMgx}?gAe~I_SH{3p5ny0(Eaa#DT zNS||AvlV;qM82Bkx*~_S_{XJ?6;lpXfEEq8dN2OZFn#)A{|WOg|1-=?)tQ^vecR-3 z_5NM@{MW1hGc4I`$+zQ9Z2k1v%f4N6ws+l9&US9~(nZIg*mwA;t>`(b5g9Wruo~24 zeEW6zr<?EO_Wks~xcBz1x{Pn-RbR`U9#+dsZ`}2J#|H_U`w5B9-bnG(nJs?zJFj=) znY=D3FP{>xmElZQFT~WI-{18;?2}#d-^71k-`}lX@P5m$?$y8dr>9u`-u{j6%A2{9 zry82p-MnWJCG$+FQn66QxOQzx(3(&|i(MyX#n!Wfj)YvY&-`(^!^8IAb;`@D-iz%O zD)POY-GA%GD%02QXD+!rPM@5<*48Gwb+)Fb&{F3kqRW`CdF}+wk==fOw|dF3FE^`$ zH<rzNcw_f##-6*;)@4bnK2%T7eYSGacELG^uOCTRd+f<dl|7{<r3D*3m9tlCDu9~r zOY$DQ=$mzSw|#o)x2LalXV1E6TRY))Wp3}*PZCy^A8z}rZa$=UeeHojlRBMLKh|WX znTxoR0uzse3M=O~``hP>{(4@Sx9`x6{qk1Rv-4v=`%heQclTu8O$8-0ZQkm|&fIk6 z>SR^5`D_6jf44ZSwotJE4ce~RoA{Cc(R;6hdorsQFZi`S;qo_LQ(wNX-MKk$>}<`= z>^7!(t;vgU_o;g>8L*X4ZpOdES$3c{g27w<=6e3B{|u5l3T`VuPWR7^iF<P^vbS~T ziw)Jvy#4L=n?8RPyqZ?FFCzHz!gYsIvXsIn&2zdqX))+>g{Ae$HBmpF-rg;qQ#w8U z%kt>=dmA@hm)q^0m6yA==Twr}rKFdW`kHK1L=6m&yZU=d9T8Gmb+KU$huHUbdoL~L zzGUmT-8_!D=Iy_2H+T5n-CE`no4sv2pFXpr^Q41099pa~*7l;dCWf<g_sa%+;>tL5 zOnNqGnoK;Wgx#xri$qo4qu0E%Z+yu8`6|oc<gO1}cKSbjAMJN#>q=&yT}N$mtwm-D z`2=-m1%tw9$+@RLW`A5>adw0Hoxk$eC3jkFmv`)V{W*5q3RU0r?MFG>jQRUJ+%}pi z%Rg{t)mh}Br1Yv`a((H$d0BSmUrfU@E|tf+2ffLx4r`kw73KTkhL->1g5&<2%a#>t zIC6&uUH+vscfwgI2GIjd_g>%idUL;juJWCmzFTL%javsgt#0#^6_ayQ%=N^ktA=(( zZPgC-=h?LO>7o^E@BP(wNbb}GZSE{N8Tc{z@zhxVsL(4V>tjy`UY;`H@vnoobCch0 z+W6({y2<h_Jub@4^6vWUI3!F0_OBELpJQt4`SJadwELoer|XBmxqg4A@6qks>)cDt z<mNnGZ60>{SXg4&y~p#aBR^N<xH)BIXyrbOP|;lN4@z_ChiamKyxjJBx4df8%ep;B z!^_^;Yj(Vu`C{8s7Tw(&Eq5$yH|;*TqW}0GbG@nj-wj-pyUKF?)`0h#_)mZFZr=H$ zp?vvkx0OXn-EEs2A1>z1?iw!hw_0RFi1QX%wa)85WJE<ao|Lioc+wl0a{x55<NfO0 zyjTCG+}7S)H9zU?-PUccE?aXuZeJ0+c=>Fo`Oi~b%fDU;J>$N_WZe@DE*%DtD_!57 z@0OP->-&4MtncPaznzE5#A3HIr<mum-8}9*V}<lOiIv|?>Iww~4$kiq6)jw}rc`YA zN|nj)_Fi5dR#Wt1-o*8HEaR5E>YaT3r|wnD73Z(NU8AbY8t2-x$@gL9)hG{*BbPRF zRdhK>+)+IMDgo55$un2UulqfJ>+Wy6a~^&_o>3N`lV%;&U-B+S%qnNy$+f2+Zj?wn zoS@ZEs46QGF;Ug#(#m<_6So+F%7BH|1vOqj&hPrquzUYKyS~>oCvH{7yvxt$db`z8 z<9Nz;Tj#Jm$qN++<{F3QrLOFXR|z^%YBUX0<oP*2mOs96o6&EblIQX3uAI-UF*|ko zfq349b!NLW-(Gmk*f?#Dn`q$=-}JYFC4YB5Gg^Ls*LR=fhwqPGygsvTxnJk&j9t&| zXD_>Rvv(=q?Zxfl@jEp&G(Lv>ZcFV9DxA|V+utU?J6x1kDq`o({ky(<?7R6Re{*%T z;)Up{<(lr>c1bTX_tUNm`eMhNp8Lb>*~@#ookG^Up0hTup!LL~!r%Iut2Sn=TzoM) zbdhJ=3;lO{FKpajXZA%s<Gt<98*@9aREt;U=PpV5_x@eXKc>F@>P}leb9GhBiVRqr zBXW7&j>jt^gUp^fsDN6CZnvYgGjpDw+jsT$j=B4O&$o=;S<ZMg@TmKrWc!I)*_*!p zQw_8&4Gdi8d=gaSHutPQwy){jx2gPPX;$4yDZAsPHgMT~4{umJ+g4=C#u=GfvldQ1 zZd)s(?qVvW>@UP1c!hy|*LQE;xmV|wUcWl~*#4B9JABtyzmfU#dwJE#_Jw`D<!X;s zwrajAmb6&>cjoF<i9I^~OT@R{dJdY%V2`$c)P8Jj<4)t-`_oFwqu$;<y-Q<r#o2>1 zj=jyv39XC?dpb9(_{Xe87xv_KoW7z_`O>MYQhL&Kb<kC0d`o|PzhukLm&P~u)!(_- zJ$Gd7`)qPMU67UKr)Hz7*s>+;E2=6y?JP<=lJ_qJ4XrN9*YBOnc_YI2@^<s}L2r6b zw%)rviTB~I6-5zVnZf3}R<6u^vEWYVq!nKMj}0#d9p_l#9%<tx^eYcEPI-3EKGmAE zH!^Nl54`v5eVu!_{l<^^53KFw<F{q-+TA`hbK%PAq8$;7J1!=_N?-0aOGQy#`8#vW z{kzp`Zm)kd{n**hI}L6hF8|UW{f_tS_J_AaZseSE&q-dkX2GJH_lwH*S&0d1$;5G7 z^T=Eo88y%7*~u@UvOAjdW4WySDVz7Fa$oLVcJKDG`RmnR<*hv6yRBe{Pkiv#f8pAm zbMM^PQ<};-ZFj*_bH1oHwMn~{99Jnh{UG!LXy2>GLo4NF+ir)NF|Rc%Q(V1mntS5a zSv<O`mMd@0tXZ2@uD&hxLUWwdZo_z6pAGWPXSEH*L7^M^{@#CvQ}HXU&u@KUKFz)I zdcv;<4|jdfe`YmHcJB6~2|<Zgr`Ah`Z`*J;f6uBV?S|JZD&@Jov^8HUu8cgAyX5#2 z&;Vi7n?QG#Ku0c}g2Pg-A{jaxk4v?<1pWvOVE7N--kyRyAu#BUg7|;+-r;Rl&1<T! zC#C;7zQfGz-W%KIoiC?Nw%%IqW+yXEZJ)*cliYiRx{n6?>aWsF*4FVowE;BGp|RI_ z?|+8r`iKASo%nLz;(N8cmn(Aj?^r#%;BAguwRqn=-rFjjz7ysb``fxqohscFsT@+a zdg?L-tv(Y_TS30d>Xgr1QQcaekD(WyXeO*#ysEYNaIj~nkiUN*cb}By!U;z6Pk=0% zO3it|dw;L*8vLW`6gJ$k_bC2fyAL|}C+gLA@z~P3@<sKt?Y{UnZ~82_{`B3c!LGZM zZ{1kam{lWdeq5{eda&zsFUi=IA0?%Hny&Bh3zJM<Av7V(yLx@;hG%*)IX~`{{K(W; zdc@b+Q%@@<+v&&8!cD=)z8D4A8k)Ry`Lfi<&EC<;<Rd71_<=^Rz3a{VXU^UGQ9t8P z{(+Kj`D=e&*8b$Y@1w2h?4O)>Do*V=9`;2~Y%_2CjOpchJ+*eug8nu)Emury>j=4e z*iTDUs?!WqbBFzB(Bz-}d;at7N4oNE-7kErY5woRn|Ehlo_#v|hTzr18@Km`ui5UK zcdxzXtyR^lu2s8y^xhvo$Ximdr~i@BRCe&@vCZe}Uf*SxJ$iFS-1Jv|=il)jXZ7`C zUTyWCp`uJKYgXT}b<4#!m;G}txE>sINA!2+%Z+b33)~igW`tbk3;c7dx7MF9Wxve6 z#M7JqDLuDS|I>T^k<m=~eu-^&c2{rxHGR={RhRRPZyaw*^0dYo7r(bK>=kt_(|@~1 z>8bL@U)AeNGPD0P%;i5er@r8NP9$ij(~_5a+^gI_cAJ0tq}%OPXO>j`%V)-+{|t*y zt-W(|>FLeRvl3nP95tlcw6+OunGW8j8n}O+`thu~XRBYWdN=c@yyv!p``Z5*ZW`SA z`=U(D&TrQ3+Sjq_?YVEe{vI)2J!#^0k(P>iTIPK!rFw@0Kq0Af-|RobJl(Xb^V)O% z_`aHV{q*C4`If(uABDa7sk_ta)wIoz=lK^uvliyw_S=8`2kR+m{~0>>Jvo|OWaKUy zxhN_zSXiUhf%)Cui~r95XE6QOc>8zx=Iy#+H{L{MRIPt@?dA{lxqULWhAURR^}Rg* zaPBRhd$zoxOKus4J$sa?#mOfa@e(wMk$a}zf_MH!p_&<|zV(Wyl?UaVf5!UnWPNOo z-HKN$GRu3_-keF&e7)`4rQ=6K=L_cd>%0p{*?Ngna!sHEXodV#>FYmV{E2;SckNiI zeahG8r8#^0<QA_nsC~Yp=tu67-PMole!P>)vg+;LSnYYayk4~CZQ==sslJ^{PXu4w zviiwnQ2i{eKmESN#(KHnqMY9T>#~#Y%-!mIS^EBt^h2e~*qUwGj@{f{*R+hM?Bm@} z;ZF@Gth3r}u~2j6LnGg?4gO1b8O(QmcMVRfw~*U#+t&S><+^Lf(#rMTH7_v}FZ$Db z_AO^wo-+59+tsy~pJ*3W%B)i_&YPnauNd^>O=xJ$m53KwsmqrwE6V$ooL~B`=ih~Y z$+Dq0>J_wp=_lmOJA5x>#-4B7S<kBVS-rO}zqO<1+oQvWXCJf8Q_J}xefalgRaGbL z)XQ>ToYF2ytk4Cm*R?-YKhHM$V|r<B-@Y>szO(&)<9mARr{Dh>e$U<d$=X>ww*B=T zUY9AAx_2k9z8Bq}7bq2}y<pP3NgQ&Sud<X<HZe&2USA>vnjt#hw=?(UmEG)liNBc( z-`P*7__zJ+7Zr&)TvsoBo5x-2J-yw2%Hb5v=kuDT=`GLUjsL#DTWwC+jKBWRynnAR zxq3bR?EJ;s*X)S7_vCfovsd$$-1*(Uy{zo|d%eTAMI`oZIrr||l-X5wpN<BHUrt^1 zso=6_#|_D-TT&}Er)lk2@g#8R6y|q(FX(TqpZz>K*!PooeOcz6Db>b;*QV}$dQn<j zy65K9-Th@H_s(=_$^7$c$o>0thxTjpd3l?*X<h7JBhn;gw6yXH`>yX!dBs2H=HBg| zerM*|zt?1+HcgtGS+cj3-*exYL!s}Nu6dXH*Y3p4uUpo;Ps@0%z3FOWRFvBsu8LZh zS1h0@x6`@xW`65WzT0KHd3TlZ6aL6c0pI<9>&JHPHeYw`(52kCO>bwdk-5E`cl&oa zw|?7DQ&+L@TzA>@d%-Rr4$HbY{`GPA9{0M9|L6Xi>@!!MJ)JJN$FREan}2Tpyv=t- zL#x#`P2TpxDw%WI`FD5nZXJ2JJ<h-2vC-EXdsKb>^t66?u;!ZhDjH4@_`SYFyzKnv z;?)a&Jija3e(k=Db$#Bqeb;|n?fhweWw!Np(9wmSH|D0!S$F8s?DETM>IME<7jwJr ze6>6gv}dB9;DvPyj_{YhYs)+RFJm_U;{7`|etRGIt?%B_GYP-ut*(9-Fx!eJ?$YUR zOWCZlysT_o^IkO<=go={O|3dUX;#yukR>YIC-T^LeRqsI`Y&bocEg)<XKuHyPEV_9 z&dQlT+xhXivv&d#HH$3P-rlmZOf~Gyij{Y!hDvhn{OG#k#l3A&D*{WLPIOHNW!l!L z{AU|4e|S(-y}$IUdqs}!+=;JZ|K7dpyvsc8%JGYjt52`5x;I^R!X4AyJVB|iwS=|p z)Qw$Zon6BwoDO#mmsy=XHNN!S?3MMi{e1)8#2vm@+kR#5<Ey6D>PcVqQxdmr`+hs3 z%I4X>n}2W4T727@Uw6C8@^j~VmbQMEtueVNwr<`mo2(NmQ@6Bj-}T+S{PFhcOMhB! z1ijBNQ~UJ#<ciNTWq8xrE8W|sRi+=W=s1<|S-Mzv*1<g4-4pr}u3Vk+l{IBE(<*nn z*LTC~1L{q9ug*IDZL4`&P3;@b-CWb3J};^%e9^b*^_^JRx+ib$gopWjjf}Zy$#d&c zpx)(!3uc=~>TEnLZSc{}5p-6@@vp}}3%y)-=G>dPYddcL{w=u6JnYiDzei`>uI}gL zGdA6@=|6+~CX<T3LpOKto0zQ?)?X0(MC;I{j(Z-fl9*g`bwG`F;o$!aW_+tp^R)V1 ze|hiKogclsKOS2DRPE~>i8FGV1-JFrWL)3S`gy9Uq{mXpSG)RluI}UVNS?U#lj05r z(6Tu<=ARqCOuTh_epy+2)OUTii%Gxo)60)tSn}rW{+1)xzop0LpIN7z`*vknM{|L{ zn{nyw_4dxazr9YbYzhfI7Ui(C>-YK+$*=C8V_x6UkIBEe``WHeexD9}ixXz{yi!s2 z`d7@<kl3wJ>91;cZ!0Q^zOTIP^@_w}o-(F4rS7z-NnUeS*ue06eaW6x|MK>-{=ITA zEb&|0&-35HqBobT2c}rH%g1l{&#*_jyy~s)_Wpa8RXp3CPcG~25nG@2bY*toBAagC ziC!%ckNH=I->qJ}dRLuCZtVPztMxxs+io}S)6PuT#<I6{>aCkLJ|Wfj<sJPQW-S+v z>=X^Ye0;|8?8QoFK28%4PgR&-`mTF@_J0PWb3HF_`5p}4wPE)o$L5#+mc9M)`1qq2 zPyQ)8-nE`Cy`{{J%c^VFD=#UTo}Fj5a7LQ?Yz(@1C@Q>GtM>Q$QmggPzX?rVw&Cu2 zb=U7Kl|0uzpLtVJmK%Aa#6CZ|c|pxwzen?{L-QrR8Rx8eWpVyyO7hy`a81EAj{W;f z-!=QBe@=Y4_x|;2`RdaA(DMA8=jLJTo65tg*M8Hxb0}r|^!T^)7M}~xjr}_NE~naq zc7ZD<x7SPCSv9`YN{{JvNpW-7X!rWA!L#}~<;T{P-MI04yX^MzXT{<>tg3UqU;ExA zX%;7}e$_U0x77L{VZmj|=5DVKUJFe<YG=1-Q5Kizyo{9#($$`d?!CS%=$-$wotI_^ z-u-#YZ_nGgC$Gu!vmJ@OczcJAo?dL{v{PkE+0LzhT@!Y9{pwSfe8b9RsvmWn4mNW1 zc9fP>pP;UMw|a5o$L~+<jQ3f+*Sukz(|iA#!GldZed3~}_t`g|>J`(Pr?u1ghR?TK zr=J^0H|JapI=xQnR8_ZG8xJ^Z3H-31{oLo>pR|YlYroaKJo(I=Kjz!MuRmT!PCSvb zvoQ8;_|@In$~%feEOg40%fjB-7>5J~)?VS9xNLD%mxJ%)Nhd)|lVvZ+>uzWJ{DbqV zyn%K2+U;ukMeq8i-HM&KtLk(5<Ail*uB2?9aZB=^?s~__sa^aJZ(3$uTQ=p&)#Y7% z4wrUlfmTF1h}wUyeUWmjcH*QnZ|V*ndf&J0{@v>9qJOJZ<4&A7JLP=YQZujhDIo#x zZQ@?8x+$`w>qo)XrbWxvZDU<BiK&O%i0yma%hG-JGv~+W<nFvYHT16Kouc>eUNl!; zd;4zntjXy~<+gVA>z6HGe&=fZQKQ}Kre~!-nUr<H%}7e?<+GEW&MphsF2CD*>3!LM z27|a|H*e%ms9dw}#v87Re-<Tmr{DK}>|c4v)qC3XoctP9w{qEzMGLE@{b#6`6*V<> z4`w!-@$`W)AE<{c`A+`X;>*#|`gc#xd42E7!kd42L$`i=_wtQYPa5CkoM*eaYjXdH zJ-T@}f6Ma|X)F4IOVTs8iyAGTa{02$llCdm_ji5weAWN#{$<159k(X0vG%vU^>}yH ze};vxtgN&CGZcLf`{{f0mUOl4&a$wmX`2~$6-no<&U#w-yKHIHnybeyoEPc1_)D+$ z`Ywrmfq$0WiIv=KCmniCrtH*>y<7L2m&jiH`)ax8>auyKZ_7&W{cW!PWaaWq<;80c zhP^%dd#3U=E|-US9H7B8#g2c4_0p?1RNuFrdb|2{-qG#97F*ukZPc}EaZ0LD?ELG} zb(>~QSDbvi#5%n!T`R+ArEE&6eQL}G?{9r7dk<@Tk9!gIUjLc<mARrb?(8nt-T2$Q zJpJ1{{l!Iw56#l}rmIgr?|c7uOYYps-G>})OI1oOMNVw^JvH66*K#Mw{+oB?wZDZs zl<=AFv+iczu`TN9lG{6K>SM3&lX&lS_wBA{zjNe{EHkzAlHojmHH3}jWTdE-O5V}G zW_GXdimd8?_VR+sZ{1DqS?|r?+}K?%cqz2jRxCKtclN@2GvDrv++A?hboVyin`XzB z+?Mp=WjW7Uvh~HK5XA=9^`OC^w?F@!{dGIV_wtYWFZOpMYv10kt)2H)qw1rfbU6PW zvv+52OYZXBJA2Zye)C-`GKzn<7MZTdEZniHYv*gt^HVuMr#HOHKf(Vj`s#j}?bGZT z_gCHDl632>eSC2G_V2fIZR+&7)l*DWDvG@uPra+My}4u2;nNYF)2g&I#k#y#d;;I{ zB^Oq2+&h2sW}&xQwas_#-ri#_pZ90l&FEy-joa$vxYpFFAFoI_op*olwDp>fUOTLK zLcO}Oj;GC9xyB?5)SVCbTK_rdRo;cBZbjG2^$Od*8JB)pmc94as@S8OHkOqBUA_6+ z+A@9N-7m|IS(>vfn{B+ksB%@xG^>*doeQU*eZm1cr+2~I`e(;4&2ARIHKjDR`sR&) zmtON8zq&ins#;#|;@#QXYtyZlJgvWY%|7LNZtnRhw{LpwdhsE2Q@GpLKr<_?NZGFp z`tSB$@z_5*-G@gm%VvE<*}3a)ZQ=``SgB23y1iHL@~zp|b@y+WofP|Yhuy8+mOEE$ zI})AGyX`^MO6^<QT{^b)%ANq-9wa-z=RbqWujfz0Bb;y9W}7BGPPh|2IrW{6neWF< zso&OpySdY{;P&e5r}OzIoO-l$-Qkpn88=_9N^5i7tPBp!1W=^`TIxyZxX}OWcg{Zx z&(FKtySZ$ASgFc}{|w9bPn7Qbb$S)g#gjJHC1<_ATOZ$46yRdBw)yqk-OAx1M!%-H zwKs8sdV_1+elG3*vHX+vhW`xHOx+K~ow{c)U$y?xs&9GDdsNQlF4}u{>fNtDYTTYL zTyZ=vv+NzyZsUFBcCXfW?ON5dC+Pg7zEZ(oi$NyY{Ab`XcgtV#J?-g_=>1DhyZzSB z4=Rg4uG97;<)7ueAAfgfg$k#ostTQ&TD^Sn=dIqY>pvPFm*x5KLH1|zyS-O?K*PCH z{=ENj`sRGCU1`(T{aU~M+x@_IT)MZL@01$n-&=S~_W8Bj$0fUG&Rd=Ne&5b}+@faD zf{}$vp1#4J!7LwFubBhd?Xy7pk@}}E_wBi={xdv&9$s?2F6h#mv&(P9ZoDP0uBmNx zTTte{LV0{v$iB4t2brD|`ev`*C|z9;;_;<qOLx#}4<YHB`Jip--k|Zx_?n!vJ8#_9 zFZ=rK!*TcNx2m7#rknLWJL+|2<C6{2{gS~Qr!VYY$k!d}7^x)0du%mmd@JC(_kRY} z`xEvi@3!(@XZ)Yx%<LW8(+}$R{{Ak%Z`bt4V%8Z+-|}8m<?f8Ob54E3`}2+k&k@PH z*Eek2^LdJw+w$I32~3s|4WRbj&k6s$rFZQvOt+f-ch;*bKUjCxh3r3lXaBLX*H&!z zy>A%3m;cfnyvgwHF>|4_-q%c{kA`i0DJ`xVd);FeXuK;dZU5wB-n!Rs|4vz6=jw0v z<zI$${m&lf$<~6YJ}DAbdvXI_ZT2WWr4_aKNA3QutRU0+8KBwe3&uD8Go1L(uw%QJ zyXAFh?q^G?%^%shKRb5q=h?oOJng;#&&z$;las&y)2`2}U1^@O?9K7Bp__^=y)2h5 zYkngZmDL&Ay=$S{_qdmjH~(jtG(GRV-TgOrmfiR{UF_<Qw>Rr8cHMv8=DqZlRaL+C zytuPBZ{9JBH{JI14fF2ICzCUt{%2@uTi%-y6jjx<?#!#D(s!#Ds$2e<vq`e=z@6O> zt-orv)&E?QYyZq@#(#!)T0564yKV8~g@5qh;~$@XvW@hOKU&hX-ygIp`jYUz{S(t} zy|tfXegEmaTW|O_ExYyi^d774wcpuezRmZXclM0uj%~bpcI{`c{oa#5cc<K?Wp_$` z?2uclzHseDtJgjK%lDVQ>z{t|ew*yno7KNHZ^_G5FK51FeY)~XSoP!eNp{a(AAghM zm%M%xZ*=hXPZm}xcW!6-sJ*%Qb*I<%C3EL3;tX4`W}`s>|E}+@^74P?WpAqu6WgBp z&D`s`_UFCM*JPt_&y0KX<NCJU#hMe>y_PWAVlw;v8nx5US3JH^dFClw-s1O~&!T>r z&rO=3Isvp8Gva>yY2)Je^^>fPcW7Bp&U*Z9-^n+&%{MHx&e#V%{&v3RQN}ilCB;`m zYIknlygP5Y`jwlVnl`>Yrydolae2mxrip>JulIjuf55X}arf%vKdm=@-dlRK%<blD zpZ$JmhJT-4+A;6*jhVge6VG1Fi_$FoU9LL);OXYO5<Xu`E*}n;IkRbT#}lVjpu)I$ z)%vHCcV_+WUR-^)`c}X2-a5mJIX>*J7v8ShdNg<K^lfEp`eUm9Gw@EhCE>G_&upFE zyM84hN6wbyX-)l(Wqodw?^Z85`Z4=wR(XET7rv9P^JnaSS~%Bq%WnRseot@x6?WXT z=+OH=K65wjs$QJ=@OIHR6YJ^ft}_n>z3Dr)YTd#vnG&HbTMH#)IzZ>(zX&=|uc>?5 z_wwBS%a;32zo^oQOJ7=KtG}T<Kc*(;)@<gwz>A@t{{Egjck`D7K3`oVy<Ok8ZMn!L zcdsh~S9TihJ$V__bmYEmfBM+!&AU6kttwmf@WO?f_(lJ?%646y{io#qrTom4-}fI) znE(Cj>c2ar>`&&dzy9U>yS<mge*BC2@P5Z{t#;j8EQ!DMpRK$7Gk(gV{@hz01{L%6 zbtj}+J+f+?!hUw@5+Sdh3XCm#_vmJW3Ow1@_n&G{x-r|n{A-N#_r=Pu4_}SFc+IwW z{=u4*TQl_DF6`b?_O3oRVUzXmEsxKJT+2(p8FBZZit}5|kDtSjOLeBihpjJt*E;XY zzo^M&Y4?{O+@Dv^WwQVDW5o|`QcG`jhW~r2WfX0!>3*}w>1M^&%LfCuIMl=~d=lC5 zx!2t8^<A-lPyYqPZoKm~`qgs#Pui=Ota;OAH|s>Yxn2C@t7gycONSSJ)iW1$R8wEQ zT`s%&N`1ku*Clp3-gZ`#1Os}t=kF-90&Os{eJB5P`n0>Y6L%N=T=(GkmC~|jKg~OT zD(p6sopd2Ds_LZGrPb@o^RCS=tCyXm)n&6r)NNT`p<A!VW6wxC&>}jI(0c84=k8gn zvtOIL@>8(*u5b3fU-A=6@{j$}aS#8`Q2uFlxb)tS61tlmckR|zxV>3b?&lrr;H<Z+ zytD#+#mqQ4XNAWGMO3dZ(aP_C`grGdF>k}Uw`;GvZr}8Ezxb}zkN28H=50NY@4Gwh zZsxpWX{)u(^KRbRd@boa-{pH-etcVaBAM%`QLx`Lttk_<`%B-o_BZY4)840$yYAHS zC*|{`ZNqQQz9f5J?edN4a@|c;-3ha5Dvr;Ik9rex?8V*Bxm7kZ_qyg3hOV`lDrR_C zK+Nv-T?V0gy}qlrt1sK%v`zfPobz_?$EKgAxBoLJ?F!p%d-Zbq$K84Q6C{=%{dVur z{VxW2e`0nY7oXJW{^8ZC%)-xs40<x4(@~Z@nSWY%i`kvq;`LvbpVV=a-<M>sxc}0> zKX+>*|312H{pC;p%7tt0Y<~N8q37<6Lb+1Ci+<clJe;*=jqCCrokz|^wbyr5z4Cur zJL$FEws*Fz;dhr8UM)L+@J;^VpT(Qrym+zXc=&~Rw=#E&WqEx1<R135sH|?;0$!<= zOU))-s;a%d>+IeC45zlU-}}+Kx%qDG458+%8rK{0vW6SyE_R>T_<ecAH?{+BdKcgL z$tCscr^cs<8JmBz35K>#^(i$tbs|lEe(Af3k>{V<Pq-gjsjK$=uDwjgZtwhWc8A~8 z?K<)G{88J*n|Cd1+n#(_#`xiH_I3Xm`ogb#lT$BWWqyCjPYxEx{RxUvprz5v@6?}; zuS>5go1V1p$B(OQX}dQR-@aUxbLGeM%`fY0^SW~OvGwOCYaWkHHr`^rT6kw!uPN90 zzO$Fp7q9cYdstYkCBF1s&$jbF0-YOvD29KSY<V+!e(|N0s@s)0Uh|Hfm^Ew8S(D$} z_M1*?S}yN9^<-++G5PGU)JyYa-2)@7m)T9ykV<mfU-$a1$QkiZmw$5F%x#^2|Gsuv zS$#!bT=T2Gw%eV(FJvTdm{eD<GUv5RvnZc-{O;_@rI+6G#Qk2>xZ-ojv#c;f4`1yA zr<PQ&FX=e^pFzX+>Gp!?s2ux}udAOG>^-`}+HLd7&vz}S-<dD;XxSZUt^H|s;(1@f z(l@@kEUag``drY=`--+Aks22tFX_++<=wT{KlDHKKe#G)vg5~#<>FcIY|mc5uUf|U z{_Wh!S9XW%_IFF)k)621+Bhr6a#x@4e}+vjwsj?ZSF1VcId|#06-yl}mMr4fo?rT| zt#0GLfZvaWH(C`J**$%!#dq;^P1dV@i+?h9z1!Dv+x9z8<=wgN%Kkg8J}XZ+lcadN zdj8=_v-*~73ccW$(0L?9^Xj|37w<Jch|A~Q_%HHp?DE@FBDCI5{JlRVZ;fuMd(_*S zclT~>xvih|mUpw-<vZ5uI$NdlOW)dF^xZm(>z7Mghpue$amLPxGS%x#zJJ*L;BigD z&A*qoZ`XTw;2lTxZnK#>%TBzWyLH;@fAel!apnwfUVZ%QzN4q|=H6Gk7W2il-^?*# zl~<;Eh~Z(0X!+82-S?08KTQn2xbfnf?e}7pC%$?+f1@@3`fsdt_kY~n^6h-pl_c|Z z>WXd0{cqIn+nM$1?D6>RnMSNVSEfu63ioI@lUoYv?;Vf*5MBGce!AW5AJ%i;o$@db zyY%|){Odo?UA?{AX)|xf$;VayR!hw;<2}CV;hp=X6OKML*wK+$^yT7{hoZBdC{Bz9 z&B=GC-TW64JF%hviC&c5%a>9%*K2$)mzK`vXZ@I*9#Fz+V;d><V#&H&k@wcDI(vOx zeP-SYb1r{n)l{{hdAsJOdd&qduPVxaYP@HKl|AE?8|R+A_W3Ik{dm*%Y1PSP>0ZA3 ztKxb7b{NKIdS8*?nfFV!{i;!paMyo^g7c0$c?xx7own3%z4&hL72cnJmh29z+j{qI z=+$^#)dLCD*^lm>{c-(Hb@Rqu?-p<TzU^UbyR566a7FH&ziyw7dA6@!aU|@tdoXX* z6K>aqEwe6w7Txg7zW+1kp*8cGe;#jS58wD0a^<^yZfSYRn>|az&Mi8XT&DK<Rqp(4 zX7}z%?%g?Y{j}z)hp$$fz4i_D)(&2?F+}1WR~u+j+;;h&9ouDX{)W%H8-0IiLG<)> z|AI=_pYZDP>;Cb%@#Ww7QxAPTcIlpV(frgoVb+$DPM(dPvgYomtNWZ*tt)N!>v$jz zn(|nz`04Y*d*V;e#`|5$t37Wgob~lt`Nmb-{iLQf_($&E_Mai8l<(`SnssvhDp|@B zUMn=dcVAiR-d7*uSW;a0Ztq2<oAsysm7TBr?Y=C1e8Vn3-Ho$eoO$o@#`gTxc*(N< z=x=UT^LAORG`PLHKA)@RoY(g9G{cBjC2bj*{~6j3M%<d`Eex9IwCI1@eOLN?)sx@K zJOBCSuD!n7NV31%wBYcTs@`vX_l_m)Gk!1s-p1mW71!LSb5>`~eJa5jXnZbgUQ(3F z#T2>Mcje?~+Y1-e8(GE4Z9J8xvF%@SUjEvJx2(l2r{A7peLVkh`L$!K=dETpdwTPy zX6B1>xBFAI)!J5dc_s-hna;-r>UD_d^nbel#%8{MYFY97{XIX<UZ4A>i}&#kvuFH| z%uXDysk>{PFWa>G@a4C%r>`y6S9`E}_3~Bgs$SLXkz7@ED{BHvnbU#a>q{i^;-B*G z(f(z;&MK&RpIP3M!*6@{?<?5ZRd?f+<oBn1H%`2dxoQ?SyX1>q&ikJH)URgIX|9Y* zj~?6Iw2ig(Z^G1oeXsAT{Z9XB{66+SgWA`_kN0`z-f3g52>SA8<?i3f%U)Xh?=idQ zymIA>#F)5iZ`K@p-B|YR(w$Eir>|bVWKq|WO5W$?e?jxKl7DCa+5YO=^0dHj!ey7j z*6MxxS-<<7My@0OgqmfCE+r){{Js9sCBeUEO!hhb^1l~%tsiuR--7)$^_qM3y?wQB z;_F@V6C!!H$6t;Y|IPgPigma2*3{nbtZAR_?q)7^p0{q<vO6ZBf)DoJ@5@;Iu`|H# zTi+&^&kDbOuP=$JcK(_5eY(rVGXE3n_O7@YJN=Gi?CX#9C2x5<FUYEH`?Wf^G|zeQ z{PKiP(#!L{{@J}F=$oura9)1b(e=l!K6&x#dGE|gOLi>+?WxxHuh+Bt{*$%y?tKO8 z)5=@Fy{|s~X?xXo`-q_Oe6Iv^zf&i3Z2p~`y#Cq2{CyLpj~n{-9KHT%wd%@aM~pXz zUkp+5R^AF~-M)HdXYgfexWiXpwnHbw-t_b(ZnnI8c;?F2^L8Hk8g@of=4;K=%fSg| zVSG`uYSQd_4El_9zKR;LURCJ<jb^%muKbGwWj)ZoPYT8z;{RW`z4?jW;dguYeyUEd z>AJDkt<L$al-$H~Us>iB_uRA2PJQ>bcgN=HX`78Fzv10E;a(+=8&k%1J+_QO(}h~e zt36guS&@+Y{%-ZE9dG+vZD;QM{8xMLFYAZ<Pu}m{+kbhdj+vhC=k0cmCH#ru@88W^ zc3;x((4D=SyA5}L-ttLe*W8V}%hf`kPFlNo(z?k`93ET@-{W4LTKO?v+4?`jlknsB zPTsjMvudAtqLsDcUZd*v(!6&EZ&)8M&5z-GecRN+)9Nv!`}Li3-h}+{X-m17@_Omo p3Ww?93_XS_{iW~LfY!lLBa}wC8S(!wf>)f3@L-_2_v`=P1OU)~PoV$+ literal 0 HcmV?d00001 diff --git a/src/assets/logos/logo_europe.png b/src/assets/logos/logo_europe.png new file mode 100644 index 0000000000000000000000000000000000000000..89d98cf3c5a0eb7480472af3d10bb04d82407894 GIT binary patch literal 21823 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 IbxsLQ00xL>EdT%j literal 0 HcmV?d00001 diff --git a/src/assets/logos/logo_region.png b/src/assets/logos/logo_region.png new file mode 100644 index 0000000000000000000000000000000000000000..5bf6812f6ae13d983d0566daa05153cbbdf65209 GIT binary patch literal 18352 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 Z-ieK8PJVTYWME)m@O1TaS?83{1OTi}{oDWm literal 0 HcmV?d00001 -- GitLab From 4eea4a7c6bb290de1110834d5a3e8ff1a7ab3aa5 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 23 Feb 2021 11:19:03 +0100 Subject: [PATCH 48/72] fix(form) : remove "add user" --- src/app/form/form.component.html | 21 --------------------- src/app/form/form.component.ts | 5 ----- src/app/form/pageType.enum.ts | 1 - src/app/utils/CustomRegExp.ts | 2 +- 4 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 5667595b7..517892876 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -959,27 +959,6 @@ </p> </div> </div> - <div *ngIf="false" class="page"> - <div class="title"> - <h3>Voulez-vous inviter d’autres personnes dans cette structure ?</h3> - <p class="notRequired">facultatif</p> - </div> - <div class="collapse" [ngClass]="{ notCollapsed: true }"> - <div fxLayout="column"> - <div class="collapseHeader" fxLayout="row" fxLayoutAlign=" center"> - <div class="titleCollapse">J’ajoute d'autres personnes dans cette structure</div> - <div class="logo"> - <svg class="show" aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#show'"></use> - </svg> - <svg class="hide" aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#hide'"></use> - </svg> - </div> - </div> - </div> - </div> - </div> </form> <div *ngIf="currentPage == pageTypeEnum.cgu" class="page"> <div class="title"> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 837866f33..2b9b3f25a 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -17,7 +17,6 @@ import { ActivatedRoute, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { first } from 'rxjs/operators'; import { PageTypeEnum } from './pageType.enum'; -import { TempUserService } from '../services/temp-user.service'; import { CustomRegExp } from '../utils/CustomRegExp'; const { DateTime } = require('luxon'); @Component({ @@ -28,7 +27,6 @@ const { DateTime } = require('luxon'); export class FormComponent implements OnInit { public profile: User; public createdStructure: Structure; - // Form var public structureForm: FormGroup; public accountForm: FormGroup; @@ -79,7 +77,6 @@ export class FormComponent implements OnInit { private searchService: SearchService, private profileService: ProfileService, private authService: AuthService, - private tempUserService: TempUserService, private router: Router, private route: ActivatedRoute ) {} @@ -541,7 +538,6 @@ export class FormComponent implements OnInit { name: 'Informations spécifiques à la période COVID', }; this.pagesValidation[PageTypeEnum.cgu] = { valid: this.userAcceptSavedDate }; - //this.pagesValidation[PageTypeEnum.addUserToStructure] = { valid: true }; this.updatePageValid(); } } @@ -664,7 +660,6 @@ export class FormComponent implements OnInit { this.currentPage++; // page structureOtherAccompaniment skip and go to page structureWorkshop this.progressStatus += 100 / this.nbPagesForm; } - // Check if going to the last page to submit form and send email verification. if (this.currentPage == this.nbPagesForm - 1) { this.validateForm(); diff --git a/src/app/form/pageType.enum.ts b/src/app/form/pageType.enum.ts index 4742e76b2..3b3f5071a 100644 --- a/src/app/form/pageType.enum.ts +++ b/src/app/form/pageType.enum.ts @@ -23,5 +23,4 @@ export enum PageTypeEnum { structureDescription = 21, structureCovidInfo = 22, cgu = 23, - addUserToStructure = 24, } diff --git a/src/app/utils/CustomRegExp.ts b/src/app/utils/CustomRegExp.ts index 94ead16e4..a5fcf6b9a 100644 --- a/src/app/utils/CustomRegExp.ts +++ b/src/app/utils/CustomRegExp.ts @@ -14,7 +14,7 @@ export class CustomRegExp { public static readonly FACEBOOK: string = '(facebook.com/.{1,})'; public static readonly TWITTER: string = '(twitter.com/.{1,})'; public static readonly INSTAGRAM: string = '(instagram.com/.{1,})'; - public static readonly NO_NULL_NUMBER: string = '[1-9]{1}[0-9]'; + public static readonly NO_NULL_NUMBER: string = '[1-9]{1}[0-9]*?'; /** * Validate a location request in search bar */ -- GitLab From c906086851da133757fd728ab2c74557feffb1da Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 23 Feb 2021 12:10:58 +0100 Subject: [PATCH 49/72] clean: clean code --- .../claim-structure.component.html | 2 +- .../claim-structure.component.scss | 0 .../claim-structure/claim-structure.component.ts | 1 - .../admin/components/panel/panel.component.scss | 0 src/app/admin/components/panel/panel.component.ts | 1 - src/app/form/form.component.scss | 14 ++++++-------- src/app/form/form.component.spec.ts | 15 +++++++-------- src/app/guards/admin.guard.ts | 5 +---- src/app/guards/auth.guard.ts | 4 +--- src/app/header/header.component.ts | 2 +- src/app/home/home.component.spec.ts | 1 - src/app/map/components/map.component.ts | 14 ++------------ src/app/profile/services/profile.service.ts | 4 ++-- src/app/reset-email/reset-email.component.scss | 0 src/app/reset-email/reset-email.component.ts | 3 +-- src/app/services/structure.service.ts | 1 - src/app/services/tcl.service.ts | 1 - .../hour-picker/hour-picker.component.ts | 1 - .../modal-confirmation.component.ts | 1 - .../structure-type-picker.component.scss | 2 -- .../structure-join/structure-join.component.scss | 0 .../structure-join/structure-join.component.ts | 1 - .../components/card/card.component.ts | 2 -- src/app/utils/CustomRegExp.ts | 12 ++++++------ 24 files changed, 28 insertions(+), 59 deletions(-) delete mode 100644 src/app/admin/components/claim-structure/claim-structure.component.scss delete mode 100644 src/app/admin/components/panel/panel.component.scss delete mode 100644 src/app/reset-email/reset-email.component.scss delete mode 100644 src/app/structure-join/structure-join.component.scss diff --git a/src/app/admin/components/claim-structure/claim-structure.component.html b/src/app/admin/components/claim-structure/claim-structure.component.html index 46494e955..b272cebed 100644 --- a/src/app/admin/components/claim-structure/claim-structure.component.html +++ b/src/app/admin/components/claim-structure/claim-structure.component.html @@ -1,5 +1,5 @@ <div fxLayout="row" fxLayoutAlign="center center"> - <table *ngIf="demandsAttachment" summary="demands attachment results"> + <table *ngIf="demandsAttachment" aria-describedby="demands attachment results"> <thead> <th scope="col">Utilisateur</th> <th scope="col">Structure</th> diff --git a/src/app/admin/components/claim-structure/claim-structure.component.scss b/src/app/admin/components/claim-structure/claim-structure.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/admin/components/claim-structure/claim-structure.component.ts b/src/app/admin/components/claim-structure/claim-structure.component.ts index 019a1e680..e6e8f1338 100644 --- a/src/app/admin/components/claim-structure/claim-structure.component.ts +++ b/src/app/admin/components/claim-structure/claim-structure.component.ts @@ -5,7 +5,6 @@ import { AdminService } from '../../services/admin.service'; @Component({ selector: 'app-admin-claim-structure', templateUrl: './claim-structure.component.html', - styleUrls: ['./claim-structure.component.scss'], }) export class ClaimStructureComponent implements OnInit { public demandsAttachment: DemandAttachment[]; diff --git a/src/app/admin/components/panel/panel.component.scss b/src/app/admin/components/panel/panel.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/admin/components/panel/panel.component.ts b/src/app/admin/components/panel/panel.component.ts index c6d2adffa..c4a216409 100644 --- a/src/app/admin/components/panel/panel.component.ts +++ b/src/app/admin/components/panel/panel.component.ts @@ -3,7 +3,6 @@ import { Component, OnInit } from '@angular/core'; @Component({ selector: 'app-admin-panel', templateUrl: './panel.component.html', - styleUrls: ['./panel.component.scss'], }) export class PanelComponent implements OnInit { constructor() {} diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index fb6d475f8..01975ebbf 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -299,14 +299,6 @@ img { @media #{$tablet} { width: 296px; } - .logo { - .hide { - display: block; - } - .show { - display: none; - } - } &.notCollapsed { margin-bottom: 8px; background: $grey-6; @@ -413,6 +405,12 @@ img { height: 100%; fill: $grey-1; } + .hide { + display: block; + } + .show { + display: none; + } } } diff --git a/src/app/form/form.component.spec.ts b/src/app/form/form.component.spec.ts index 9e6733867..940d9cf27 100644 --- a/src/app/form/form.component.spec.ts +++ b/src/app/form/form.component.spec.ts @@ -1,7 +1,6 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { FormArray, FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; -import { Time } from '../models/time.model'; import { SharedModule } from '../shared/shared.module'; import { FormComponent } from './form.component'; @@ -56,27 +55,27 @@ describe('FormComponent', () => { }), tuesday: new FormGroup({ open: new FormControl(true, Validators.required), - time: new FormArray([]) as FormArray, + time: new FormArray([]), }), wednesday: new FormGroup({ open: new FormControl(true, Validators.required), - time: new FormArray([]) as FormArray, + time: new FormArray([]), }), thursday: new FormGroup({ open: new FormControl(true, Validators.required), - time: new FormArray([]) as FormArray, + time: new FormArray([]), }), friday: new FormGroup({ open: new FormControl(true, Validators.required), - time: new FormArray([]) as FormArray, + time: new FormArray([]), }), saturday: new FormGroup({ open: new FormControl(false, Validators.required), - time: new FormArray([]) as FormArray, + time: new FormArray([]), }), sunday: new FormGroup({ open: new FormControl(false, Validators.required), - time: new FormArray([]) as FormArray, + time: new FormArray([]), }), }), exceptionalClosures: new FormControl('structure.exceptionalClosures'), @@ -114,7 +113,7 @@ describe('FormComponent', () => { it('should return the correct Time from a specific day', () => { const day = 'monday'; const control = component.getTime(day); - const TimeForm = component.structureForm.get('hours').get(day).get('time') as FormArray; + const TimeForm = component.structureForm.get('hours').get(day).get('time'); expect(control).toEqual(TimeForm); }); it('should return true', () => { diff --git a/src/app/guards/admin.guard.ts b/src/app/guards/admin.guard.ts index 10b920afe..2f00de8ac 100644 --- a/src/app/guards/admin.guard.ts +++ b/src/app/guards/admin.guard.ts @@ -1,10 +1,7 @@ -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivate, Router, UrlTree } from '@angular/router'; import { Injectable } from '@angular/core'; import { AuthService } from '../services/auth.service'; import { ProfileService } from '../profile/services/profile.service'; -import decode from 'jwt-decode'; -import { User } from '../models/user.model'; -import { UserRole } from '../shared/enum/userRole.enum'; /** * Guard to assert that we are logged in admin. Otherwise redirect to home */ diff --git a/src/app/guards/auth.guard.ts b/src/app/guards/auth.guard.ts index 1c6867835..9c7fb4ab2 100644 --- a/src/app/guards/auth.guard.ts +++ b/src/app/guards/auth.guard.ts @@ -1,8 +1,6 @@ -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot, UrlTree } from '@angular/router'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; import { Injectable } from '@angular/core'; import { AuthService } from '../services/auth.service'; -import { Observable } from 'rxjs'; - /** * Guard to assert that we are logged in. Otherwise redirect to home */ diff --git a/src/app/header/header.component.ts b/src/app/header/header.component.ts index 1fff90f72..ee446b18f 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 { ActivatedRoute, NavigationEnd, NavigationStart, Router } from '@angular/router'; +import { ActivatedRoute, NavigationEnd, Router } from '@angular/router'; import { ProfileService } from '../profile/services/profile.service'; import { AuthService } from '../services/auth.service'; diff --git a/src/app/home/home.component.spec.ts b/src/app/home/home.component.spec.ts index 55739b5a3..5606d294a 100644 --- a/src/app/home/home.component.spec.ts +++ b/src/app/home/home.component.spec.ts @@ -1,5 +1,4 @@ import { HttpClientModule } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HomeComponent } from './home.component'; diff --git a/src/app/map/components/map.component.ts b/src/app/map/components/map.component.ts index b5d66dd81..a038e5759 100644 --- a/src/app/map/components/map.component.ts +++ b/src/app/map/components/map.component.ts @@ -1,21 +1,11 @@ -import { - Component, - EventEmitter, - HostListener, - Input, - OnChanges, - Output, - SimpleChanges, - ViewChild, -} from '@angular/core'; -import { latLng, MapOptions, geoJSON, tileLayer, Map, latLngBounds, layerGroup, Control } from 'leaflet'; +import { Component, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { latLng, MapOptions, geoJSON, tileLayer, Map, latLngBounds, layerGroup } from 'leaflet'; import { Structure } from '../../models/structure.model'; import { GeojsonService } from '../../services/geojson.service'; import { MapService } from '../services/map.service'; import * as _ from 'lodash'; import { GeoJsonProperties } from '../models/geoJsonProperties.model'; import { MarkerType } from './markerType.enum'; -import { typeStructureEnum } from '../../shared/enum/typeStructure.enum'; import metropole from '../../../assets/geojson/metropole.json'; import brignais from '../../../assets/geojson/brignais.json'; import L from 'leaflet'; diff --git a/src/app/profile/services/profile.service.ts b/src/app/profile/services/profile.service.ts index 6b7515fd4..5ac816cbf 100644 --- a/src/app/profile/services/profile.service.ts +++ b/src/app/profile/services/profile.service.ts @@ -71,7 +71,7 @@ export class ProfileService { return this.http.post<any>(`${this.baseUrl}/change-email`, { newEmail, oldEmail }); } - public isEmailAlreadyUsed(newMail: string): Observable<Boolean> { - return this.http.post<Boolean>(`${this.baseUrl}/verify-exist-user`, { newMail }); + public isEmailAlreadyUsed(newMail: string): Observable<boolean> { + return this.http.post<boolean>(`${this.baseUrl}/verify-exist-user`, { newMail }); } } diff --git a/src/app/reset-email/reset-email.component.scss b/src/app/reset-email/reset-email.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/reset-email/reset-email.component.ts b/src/app/reset-email/reset-email.component.ts index 0c92a21ca..705a40ecc 100644 --- a/src/app/reset-email/reset-email.component.ts +++ b/src/app/reset-email/reset-email.component.ts @@ -1,11 +1,10 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { ProfileService } from '../profile/services/profile.service'; import { AuthService } from '../services/auth.service'; @Component({ selector: 'app-reset-email', templateUrl: './reset-email.component.html', - styleUrls: ['./reset-email.component.scss'], }) export class ResetEmailComponent implements OnInit { public token: string; diff --git a/src/app/services/structure.service.ts b/src/app/services/structure.service.ts index f65757bff..b94990936 100644 --- a/src/app/services/structure.service.ts +++ b/src/app/services/structure.service.ts @@ -13,7 +13,6 @@ import { Time } from '../models/time.model'; import { Filter } from '../structure-list/models/filter.model'; import { User } from '../models/user.model'; import { StructureWithOwners } from '../models/structureWithOwners.model'; -import { Owner } from '../models/owner.model'; import { TempUser } from '../models/temp-user.model'; @Injectable({ diff --git a/src/app/services/tcl.service.ts b/src/app/services/tcl.service.ts index bb14963c6..03cefff1f 100644 --- a/src/app/services/tcl.service.ts +++ b/src/app/services/tcl.service.ts @@ -1,7 +1,6 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; import { TclStopPoint } from '../models/tclStopPoint.model'; @Injectable({ diff --git a/src/app/shared/components/hour-picker/hour-picker.component.ts b/src/app/shared/components/hour-picker/hour-picker.component.ts index 63f42f543..43c401154 100644 --- a/src/app/shared/components/hour-picker/hour-picker.component.ts +++ b/src/app/shared/components/hour-picker/hour-picker.component.ts @@ -1,6 +1,5 @@ import { Component, Input, Output, EventEmitter, OnDestroy, OnChanges } from '@angular/core'; import { AbstractControl, FormArray, FormControl, FormGroup, Validators } from '@angular/forms'; -import * as _ from 'lodash'; import { Day } from '../../../models/day.model'; import { Time } from '../../../models/time.model'; import { WeekDayEnum } from '../../enum/weekDay.enum'; diff --git a/src/app/shared/components/modal-confirmation/modal-confirmation.component.ts b/src/app/shared/components/modal-confirmation/modal-confirmation.component.ts index 8c75b179f..a143aa820 100644 --- a/src/app/shared/components/modal-confirmation/modal-confirmation.component.ts +++ b/src/app/shared/components/modal-confirmation/modal-confirmation.component.ts @@ -1,5 +1,4 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Subject } from 'rxjs'; @Component({ selector: 'app-modal-confirmation', diff --git a/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss b/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss index 9772d6210..bd8bc6eb7 100644 --- a/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss +++ b/src/app/shared/components/structure-type-picker/structure-type-picker.component.scss @@ -55,8 +55,6 @@ button { button { background: none; max-width: 114px; - &:focus { - } } .containerBtn { @include background-hash($grey-2); diff --git a/src/app/structure-join/structure-join.component.scss b/src/app/structure-join/structure-join.component.scss deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/app/structure-join/structure-join.component.ts b/src/app/structure-join/structure-join.component.ts index c38d14032..59d6ae04f 100644 --- a/src/app/structure-join/structure-join.component.ts +++ b/src/app/structure-join/structure-join.component.ts @@ -5,7 +5,6 @@ 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) {} diff --git a/src/app/structure-list/components/card/card.component.ts b/src/app/structure-list/components/card/card.component.ts index 8a64274b6..ed8b3158f 100644 --- a/src/app/structure-list/components/card/card.component.ts +++ b/src/app/structure-list/components/card/card.component.ts @@ -1,8 +1,6 @@ import { Component, Input, Output, OnInit, EventEmitter } from '@angular/core'; import { Structure } from '../../../models/structure.model'; import { GeojsonService } from '../../../services/geojson.service'; -import { typeStructureEnum } from '../../../shared/enum/typeStructure.enum'; -import { Equipment } from '../../enum/equipment.enum'; @Component({ selector: 'app-card', diff --git a/src/app/utils/CustomRegExp.ts b/src/app/utils/CustomRegExp.ts index e268d8229..9fdba4fe0 100644 --- a/src/app/utils/CustomRegExp.ts +++ b/src/app/utils/CustomRegExp.ts @@ -2,17 +2,17 @@ export class CustomRegExp { /** * Validate a password (at least 8 characters, 1 uppercase letter, 1 lowercase letter, 1 number, and 1 special character) */ - public static readonly PASSWORD: RegExp = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/; + public static readonly PASSWORD: RegExp = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,})/; //NOSONAR /** * Validate an email */ - public static readonly EMAIL: RegExp = /^[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}/; - public static readonly TEXT_WITHOUT_NUMBER: RegExp = /^[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}$/; + public static readonly EMAIL: RegExp = /^[a-z0-9.-]+@[a-z0-9.-]+[.][a-z]{2,3}/; //NOSONAR + public static readonly TEXT_WITHOUT_NUMBER: RegExp = /^[A-Za-zÀ-ÖØ-öø-ÿ- ]{1,}$/; //NOSONAR /** * Validate a password (at least 8 characters, 1 uppercase letter, 1 lowercase letter, 1 number, and 1 special character) */ - public static readonly PHONE: RegExp = /^(?:(?:\+|00)|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/; - public static readonly WEBSITE: RegExp = /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/; + public static readonly PHONE: RegExp = /^(?:(?:\+|00)|0)\s*[1-9](?:[\s.-]*\d{2}){4}$/; //NOSONAR + public static readonly WEBSITE: RegExp = /^(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/; //NOSONAR public static readonly LINKEDIN: string = '(linkedin.com/in/.{1,})'; public static readonly FACEBOOK: string = '(facebook.com/.{1,})'; public static readonly TWITTER: string = '(twitter.com/.{1,})'; @@ -21,5 +21,5 @@ export class CustomRegExp { /** * Validate a location request in search bar */ - public static readonly LOCATION: RegExp = /^\d+\s[A-z]+\s[A-z]+/g; + public static readonly LOCATION: RegExp = /^\d+\s[A-z]+\s[A-z]+/g; //NOSONAR } -- GitLab From 0545c60a014a3a86e44ab3698dff3e7a9c5a703d Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 23 Feb 2021 15:54:12 +0100 Subject: [PATCH 50/72] fix(profile) : export logic to new component --- src/app/profile/profile.component.html | 255 +----------------- src/app/profile/profile.component.ts | 228 +--------------- src/app/profile/profile.module.ts | 3 +- src/app/shared/components/index.ts | 6 + .../modal-options.component.html | 0 .../modal-options.component.scss | 8 +- .../modal-options.component.spec.ts | 0 .../modal-options/modal-options.component.ts | 2 +- .../structure-options-modal.component.html | 226 ++++++++++++++++ .../structure-options-modal.component.scss | 86 ++++++ .../structure-options-modal.component.spec.ts | 24 ++ .../structure-options-modal.component.ts | 236 ++++++++++++++++ .../enum/functionTypeModalOptions.enum.ts | 0 src/styles.scss | 1 + 14 files changed, 594 insertions(+), 481 deletions(-) rename src/app/{profile => shared/components}/modal-options/modal-options.component.html (100%) rename src/app/{profile => shared/components}/modal-options/modal-options.component.scss (78%) rename src/app/{profile => shared/components}/modal-options/modal-options.component.spec.ts (100%) rename src/app/{profile => shared/components}/modal-options/modal-options.component.ts (86%) create mode 100644 src/app/shared/components/structure-options-modal/structure-options-modal.component.html create mode 100644 src/app/shared/components/structure-options-modal/structure-options-modal.component.scss create mode 100644 src/app/shared/components/structure-options-modal/structure-options-modal.component.spec.ts create mode 100644 src/app/shared/components/structure-options-modal/structure-options-modal.component.ts rename src/app/{profile => shared}/enum/functionTypeModalOptions.enum.ts (100%) diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index 3ef2c4743..96e8c9734 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -13,26 +13,10 @@ <span>Identifiant</span> <div fxLayout="row" fxLayoutAlign="space-between center"> <p>{{ userProfile.email }}</p> - <nav aria-label="modalOption"> - <ul> - <li> - <button - [ngClass]="{ active: isModalOptsProfile }" - (click)="openModalOptsProfile()" - class="btn-primary transparent" - > - <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'moreOpts'"></app-svg-icon> - </button> - <ul *ngIf="isModalOptsProfile" class="dropdown"> - <app-modal-options - [isModalProfileOpts]="true" - [hasOwners]="false" - (closed)="closeModalOpts($event)" - ></app-modal-options> - </ul> - </li> - </ul> - </nav> + <app-structure-options-modal + [userProfile]="userProfile" + (closed)="ngOnInit()" + ></app-structure-options-modal> </div> </div> </div> @@ -47,26 +31,7 @@ <a class="structureName" routerLink="/home" [state]="{ data: s.structure }">{{ s.structure.structureName }}</a> - <nav aria-label="modalOption"> - <ul> - <li> - <button - [ngClass]="{ active: modalOptsStructureIndex == i }" - (click)="openModalOptsStructure(i, s)" - class="btn-primary transparent" - > - <app-svg-icon [type]="'ico'" [iconColor]="inherit" [icon]="'moreOpts'"></app-svg-icon> - </button> - <ul *ngIf="modalOptsStructureIndex == i" class="dropdown"> - <app-modal-options - [isModalProfileOpts]="false" - [hasOwners]="currentStructureOwners.owners.length > 0" - (closed)="closeModalOpts($event)" - ></app-modal-options> - </ul> - </li> - </ul> - </nav> + <app-structure-options-modal [structure]="s" (closed)="ngOnInit()"></app-structure-options-modal> </div> <div fxLayout="column" fxLayoutGap="14px"> <p class="ownerName" *ngFor="let owner of s.owners">{{ owner.email }}</p> @@ -99,213 +64,3 @@ </div> </div> </div> -<div *ngIf="editModal" class="modalBackground"> - <div class="modal" (clickOutside)="closeModalOptsProfile()"> - <form - *ngIf="editModal == typeModalProfile.password" - [formGroup]="formPassword" - class="contentModal" - fxLayout="column" - fxLayoutAlign="center start" - fxLayoutGap="20px" - > - <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> - <h2>Changer de mot de passe</h2> - <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> - </div> - <div class="form-group" fxLayout="column" fxLayoutGap="4px"> - <label for="oldPassword">Ancien mot de passe</label> - <p *ngIf="passwordError" class="special invalid">Votre ancien mot de passe est incorrect.</p> - <div fxLayout="row" fxLayoutGap="13px"> - <input - [type]="isShowOldPassword ? 'text' : 'password'" - formControlName="oldPassword" - class="form-input password" - autocomplete="on" - /> - <app-svg-icon - (click)="showOldPassword()" - [type]="'form'" - [iconClass]="'grey'" - [icon]="'eyePassword'" - ></app-svg-icon> - <app-svg-icon *ngIf="passwordError" [type]="'form'" [icon]="'notValidate'"></app-svg-icon> - </div> - </div> - <div class="form-group" fxLayout="column"> - <label for="password">Nouveau mot de passe</label> - <p class="special" [ngClass]="{ invalid: fpass.password.invalid && fpass.password.value }"> - Le mot de passe doit contenir au minimum : 8 caractères dont un caractère spécial, un caractère en majuscule - et un chiffre. - </p> - <div fxLayout="row" fxLayoutGap="13px"> - <input - [type]="isShowPassword ? 'text' : 'password'" - formControlName="password" - class="form-input password" - autocomplete="on" - /> - <app-svg-icon - (click)="showPassword()" - [type]="'form'" - [iconClass]="'grey'" - [icon]="'eyePassword'" - ></app-svg-icon> - <app-svg-icon *ngIf="fpass.password.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> - <app-svg-icon - *ngIf="fpass.password.invalid && fpass.password.value" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> - </div> - </div> - <div class="form-group" fxLayout="column"> - <label for="confirmPassword">Confirmation du mot de passe</label> - <div fxLayout="row" fxLayoutGap="13px"> - <input - [type]="isShowConfirmPassword ? 'text' : 'password'" - formControlName="confirmPassword" - class="form-input password" - autocomplete="on" - /> - <app-svg-icon - (click)="showConfirmPassword()" - [type]="'form'" - [iconClass]="'grey'" - [icon]="'eyePassword'" - ></app-svg-icon> - <app-svg-icon - *ngIf="fpass.confirmPassword.valid && fpass.password.value" - [type]="'form'" - [icon]="'validate'" - ></app-svg-icon> - <app-svg-icon - *ngIf="fpass.confirmPassword.invalid && fpass.confirmPassword.value" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> - </div> - </div> - <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> - <button - type="submit" - [ngClass]="{ invalid: formPassword.invalid }" - class="btn-primary small leave" - (click)="submitPassword()" - > - Valider - </button> - </div> - </form> - <form - *ngIf="editModal == typeModalProfile.email" - [formGroup]="formEmail" - class="contentModal" - fxLayout="column" - fxLayoutAlign="center start" - > - <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> - <h2>Changer de courriel</h2> - <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> - </div> - <div class="form-group" fxLayout="column"> - <label for="email">Nouveau courriel</label> - <p class="special invalid" *ngIf="this.fmail.email.hasError('alreadyExist')">L'email est déja utilisé.</p> - <div fxLayout="row" fxLayoutGap="13px"> - <input - type="text" - formControlName="email" - class="form-input" - autocomplete="on" - (keyup)="verifyEmailAlreadyUsed($event.target.value, this.fmail.email)" - /> - <app-svg-icon *ngIf="fmail.email.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> - <app-svg-icon - *ngIf="fmail.email.invalid && fmail.email.value" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> - </div> - </div> - <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> - <button - type="submit" - [ngClass]="{ invalid: formEmail.invalid }" - class="btn-primary small leave" - (click)="submitEmail()" - > - Valider - </button> - </div> - </form> - <div - *ngIf="editModal == typeModalProfile.deleteAccount" - class="contentModal" - fxLayout="column" - fxLayoutAlign="center start" - fxLayoutGap="30px" - > - <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> - <h2>Supprimer un compte</h2> - <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> - </div> - <div fxLayout="column" fxLayoutGap="16px"> - <div class="row removeOwner" *ngFor="let owner of currentStructureOwners.owners" fxLayoutGap="16px"> - <button class="btn-primary small" (click)="removeOwner(owner.id)">X</button> - <span> - {{ owner.email }} - </span> - </div> - </div> - <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> - <button type="button" class="btn-primary small leave" (click)="closeModalOptsProfile()">Terminer</button> - </div> - </div> - <form - *ngIf="editModal == typeModalProfile.addAccount" - [formGroup]="formAddAccount" - class="contentModal" - fxLayout="column" - fxLayoutAlign="center start" - fxLayoutGap="30px" - > - <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> - <h2>Ajouter un compte</h2> - <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> - </div> - <div class="form-group" fxLayout="column"> - <label for="email">Courriel du compte à ajouter</label> - <p *ngIf="ownerAlreadyLinked" class="special invalid">L'email est déjà rattaché à la structure.</p> - <div fxLayout="row" fxLayoutGap="13px"> - <input type="text" formControlName="email" class="form-input" autocomplete="on" /> - <app-svg-icon *ngIf="fAddAccount.email.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> - <app-svg-icon - *ngIf="fAddAccount.email.invalid && fAddAccount.email.value" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> - </div> - </div> - <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> - <button - type="submit" - [ngClass]="{ invalid: formAddAccount.invalid }" - class="btn-primary small leave" - (click)="addOwner()" - > - Envoyer - </button> - </div> - </form> - </div> -</div> -<app-modal-confirmation - [openned]="deleteModalStructureOpenned" - [content]="'Voulez-vous vraiment supprimer cette structure ?'" - (closed)="deleteStructure($event)" -></app-modal-confirmation> -<app-modal-confirmation - [openned]="deleteModalAccountOpenned" - [content]="'Voulez-vous vraiment supprimer votre compte ?'" - (closed)="deleteAccount($event)" -></app-modal-confirmation> diff --git a/src/app/profile/profile.component.ts b/src/app/profile/profile.component.ts index 67cb92574..0b3b1d36c 100644 --- a/src/app/profile/profile.component.ts +++ b/src/app/profile/profile.component.ts @@ -1,16 +1,9 @@ import { Component, OnInit } from '@angular/core'; -import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; -import { Structure } from '../models/structure.model'; import { StructureWithOwners } from '../models/structureWithOwners.model'; -import { TempUser } from '../models/temp-user.model'; import { User } from '../models/user.model'; import { AuthService } from '../services/auth.service'; import { StructureService } from '../services/structure.service'; -import { MustMatch } from '../shared/validator/form'; -import { CustomRegExp } from '../utils/CustomRegExp'; -import { FunctionTypeModalOptions } from './enum/functionTypeModalOptions.enum'; -import { TypeModalProfile } from './enum/TypeModalProfile.enum'; import { ProfileService } from './services/profile.service'; @Component({ @@ -19,42 +12,14 @@ import { ProfileService } from './services/profile.service'; styleUrls: ['./profile.component.scss'], }) export class ProfileComponent implements OnInit { - // Password profile - public formPassword: FormGroup; - public isShowOldPassword = false; - public isShowPassword = false; - public isShowConfirmPassword = false; - public changePassword = false; - public passwordError = false; - - // Email profile - public formEmail: FormGroup; - public changeEmail = false; - - // formAddAccount - public formAddAccount: FormGroup; - public ownerAlreadyLinked = false; - - // Global var public userProfile: User; - public loading = false; public structures: StructureWithOwners[] = []; - public editModal: TypeModalProfile; - public typeModalProfile = TypeModalProfile; - - // Modal options - public modalOptsStructureIndex: number; - public isModalOptsProfile = false; - public currentStructureOwners: StructureWithOwners; - public deleteModalStructureOpenned = false; - public deleteModalAccountOpenned = false; constructor( - private authService: AuthService, - private formBuilder: FormBuilder, private profileService: ProfileService, private structureService: StructureService, - private router: Router + private router: Router, + private authService: AuthService ) {} ngOnInit(): void { @@ -67,198 +32,13 @@ export class ProfileComponent implements OnInit { }); }); }); - this.initForm(); - } - public initForm(): void { - this.formPassword = this.formBuilder.group( - { - oldPassword: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], - password: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], - confirmPassword: [''], - }, - { validator: MustMatch('password', 'confirmPassword') } - ); - - this.formEmail = this.formBuilder.group({ - email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], - }); - - this.formAddAccount = this.formBuilder.group({ - email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], - }); - } - // getter for form fields - get fpass(): { [key: string]: AbstractControl } { - return this.formPassword.controls; - } - - // getter for form fields - get fmail(): { [key: string]: AbstractControl } { - return this.formEmail.controls; - } - - get fAddAccount(): { [key: string]: AbstractControl } { - return this.formAddAccount.controls; - } - - public closeModalOpts(functionType: number): void { - switch (functionType) { - case FunctionTypeModalOptions.changeEmail: - this.editModal = TypeModalProfile.email; - break; - case FunctionTypeModalOptions.changePassword: - this.editModal = TypeModalProfile.password; - break; - case FunctionTypeModalOptions.deleteAccount: - this.toggleDeleteAccountModal(); - break; - case FunctionTypeModalOptions.addUser: - this.editModal = TypeModalProfile.addAccount; - this.ownerAlreadyLinked = false; - break; - case FunctionTypeModalOptions.removeUser: - this.editModal = TypeModalProfile.deleteAccount; - break; - case FunctionTypeModalOptions.editStructure: - this.router.navigateByUrl('/create-structure', { state: { data: this.currentStructureOwners.structure } }); - break; - case FunctionTypeModalOptions.removeStructure: - this.toggleDeleteStructureModal(); - break; - default: - break; - } - this.isModalOptsProfile = false; - this.modalOptsStructureIndex = null; - } - - // Profile Section - public closeModalOptsProfile(): void { - this.editModal = null; - this.formAddAccount.reset(); - this.formEmail.reset(); - this.formPassword.reset(); - } - public submitEmail(): void { - // stop here if form is invalid - if (this.formEmail.invalid) { - return; - } - this.loading = true; - this.profileService.changeEmail(this.formEmail.value.email, this.userProfile.email).subscribe( - () => { - this.closeModalOptsProfile(); - this.formEmail.reset(); - this.loading = false; - }, - (err) => { - this.loading = false; - } - ); - } - public submitPassword(): void { - // stop here if form is invalid - if (this.formPassword.invalid) { - return; - } - this.loading = true; - - this.profileService.changePassword(this.formPassword.value.password, this.formPassword.value.oldPassword).subscribe( - () => { - this.closeModalOptsProfile(); - this.formPassword.reset(); - this.loading = false; - this.passwordError = false; - }, - (error) => { - this.passwordError = true; - this.loading = false; - } - ); - } - public openModalOptsProfile(): void { - this.isModalOptsProfile = true; - } - public showOldPassword(): void { - this.isShowOldPassword = !this.isShowOldPassword; - } - public showPassword(): void { - this.isShowPassword = !this.isShowPassword; - } - public showConfirmPassword(): void { - this.isShowConfirmPassword = !this.isShowConfirmPassword; - } - public logout(): void { - this.authService.logout(); - } - public deleteAccount(shouldDelete: boolean): void { - this.toggleDeleteAccountModal(); - if (shouldDelete) { - this.profileService.deleteProfile().subscribe(() => { - this.logout(); - }); - } } - // Structure section - public openModalOptsStructure(index: number, s: StructureWithOwners): void { - this.modalOptsStructureIndex = index; - this.currentStructureOwners = s; - } public addStructure(): void { this.router.navigateByUrl('/create-structure'); } - private toggleDeleteStructureModal(): void { - this.deleteModalStructureOpenned = !this.deleteModalStructureOpenned; - } - private toggleDeleteAccountModal(): void { - this.deleteModalAccountOpenned = !this.deleteModalAccountOpenned; - } - public deleteStructure(shouldDelete: boolean): void { - this.toggleDeleteStructureModal(); - if (shouldDelete) { - this.structureService.delete(this.currentStructureOwners.structure._id).subscribe((structure: Structure) => { - this.ngOnInit(); - }); - } - } - - public verifyEmailAlreadyUsed(inputEmail, formControl: FormControl): void { - if (formControl.valid) { - this.profileService.isEmailAlreadyUsed(inputEmail).subscribe((isExist) => { - if (isExist) { - formControl.setErrors({ alreadyExist: true }); - } - }); - } - } - public removeOwner(owner: string): void { - this.structureService.removeOwnerFromStructure(owner, this.currentStructureOwners.structure._id).subscribe(() => { - this.currentStructureOwners.owners = this.currentStructureOwners.owners.filter((o) => o.id !== owner); - if (this.currentStructureOwners.owners.length == 0) { - this.closeModalOptsProfile(); - } - }); - } - public addOwner(): void { - // stop here if form is invalid - if (this.formAddAccount.invalid) { - return; - } - this.loading = true; - const user = new TempUser(); - user.email = this.fAddAccount.email.value; - this.structureService.addOwnerToStructure(user, this.currentStructureOwners.structure._id).subscribe( - () => { - this.closeModalOptsProfile(); - this.formAddAccount.reset(); - this.loading = false; - }, - (err) => { - this.ownerAlreadyLinked = true; - this.loading = false; - } - ); + public logout(): void { + this.authService.logout(); } } diff --git a/src/app/profile/profile.module.ts b/src/app/profile/profile.module.ts index adf5fb74c..a9427003f 100644 --- a/src/app/profile/profile.module.ts +++ b/src/app/profile/profile.module.ts @@ -3,11 +3,10 @@ import { ProfileComponent } from './profile.component'; import { SharedModule } from '../shared/shared.module'; import { CommonModule } from '@angular/common'; import { BrowserModule } from '@angular/platform-browser'; -import { ModalOptionsComponent } from './modal-options/modal-options.component'; @NgModule({ imports: [CommonModule, BrowserModule, SharedModule], - declarations: [ProfileComponent, ModalOptionsComponent], + declarations: [ProfileComponent], exports: [ProfileComponent], }) export class ProfileModule {} diff --git a/src/app/shared/components/index.ts b/src/app/shared/components/index.ts index 4fecd7be0..9cad91820 100644 --- a/src/app/shared/components/index.ts +++ b/src/app/shared/components/index.ts @@ -12,6 +12,8 @@ import { HourPickerComponent } from './hour-picker/hour-picker.component'; import { CopyPasteComponent } from './hour-picker/copy-paste/copy-paste.component'; import { RadioFormComponent } from './radio-form/radio-form.component'; import { ModalConfirmationComponent } from './modal-confirmation/modal-confirmation.component'; +import { StructureOptionsModalComponent } from './structure-options-modal/structure-options-modal.component'; +import { ModalOptionsComponent } from './modal-options/modal-options.component'; // tslint:disable-next-line: max-line-length export { @@ -29,6 +31,8 @@ export { CopyPasteComponent, RadioFormComponent, ModalConfirmationComponent, + StructureOptionsModalComponent, + ModalOptionsComponent, }; // tslint:disable-next-line:variable-name @@ -47,4 +51,6 @@ export const SharedComponents = [ CopyPasteComponent, RadioFormComponent, ModalConfirmationComponent, + StructureOptionsModalComponent, + ModalOptionsComponent, ]; diff --git a/src/app/profile/modal-options/modal-options.component.html b/src/app/shared/components/modal-options/modal-options.component.html similarity index 100% rename from src/app/profile/modal-options/modal-options.component.html rename to src/app/shared/components/modal-options/modal-options.component.html diff --git a/src/app/profile/modal-options/modal-options.component.scss b/src/app/shared/components/modal-options/modal-options.component.scss similarity index 78% rename from src/app/profile/modal-options/modal-options.component.scss rename to src/app/shared/components/modal-options/modal-options.component.scss index 907e3f944..33159ef26 100644 --- a/src/app/profile/modal-options/modal-options.component.scss +++ b/src/app/shared/components/modal-options/modal-options.component.scss @@ -1,7 +1,7 @@ -@import '../../../assets/scss/color'; -@import '../../../assets/scss/typography'; -@import '../../../assets/scss/shapes'; -@import '../../../assets/scss/z-index'; +@import '../../../../assets/scss/color'; +@import '../../../../assets/scss/typography'; +@import '../../../../assets/scss/shapes'; +@import '../../../../assets/scss/z-index'; .modalOptions { width: 300px; diff --git a/src/app/profile/modal-options/modal-options.component.spec.ts b/src/app/shared/components/modal-options/modal-options.component.spec.ts similarity index 100% rename from src/app/profile/modal-options/modal-options.component.spec.ts rename to src/app/shared/components/modal-options/modal-options.component.spec.ts diff --git a/src/app/profile/modal-options/modal-options.component.ts b/src/app/shared/components/modal-options/modal-options.component.ts similarity index 86% rename from src/app/profile/modal-options/modal-options.component.ts rename to src/app/shared/components/modal-options/modal-options.component.ts index e9eb32e28..1fa03b1ed 100644 --- a/src/app/profile/modal-options/modal-options.component.ts +++ b/src/app/shared/components/modal-options/modal-options.component.ts @@ -1,5 +1,5 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { FunctionTypeModalOptions } from '../enum/functionTypeModalOptions.enum'; +import { FunctionTypeModalOptions } from '../../enum/functionTypeModalOptions.enum'; @Component({ selector: 'app-modal-options', diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.html b/src/app/shared/components/structure-options-modal/structure-options-modal.component.html new file mode 100644 index 000000000..1857d0baa --- /dev/null +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.html @@ -0,0 +1,226 @@ +<nav aria-label="modalOption"> + <ul> + <li> + <button [ngClass]="{ active: active }" (click)="openModalOpts()" class="btn-primary transparent"> + <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'moreOpts'"></app-svg-icon> + </button> + <ul *ngIf="showModalOption" class="dropdown"> + <app-modal-options + [isModalProfileOpts]="!structure" + [hasOwners]="structure && structure.owners.length > 0" + (closed)="closeModalOpts($event)" + ></app-modal-options> + </ul> + </li> + </ul> +</nav> +<div *ngIf="editModal" class="modalBackground"> + <div class="modal" (clickOutside)="closeModalOptsProfile()"> + <form + *ngIf="editModal == typeModalProfile.password" + [formGroup]="formPassword" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + fxLayoutGap="20px" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Changer de mot de passe</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div class="form-group" fxLayout="column" fxLayoutGap="4px"> + <label for="oldPassword">Ancien mot de passe</label> + <p *ngIf="passwordError" class="special invalid">Votre ancien mot de passe est incorrect.</p> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowOldPassword ? 'text' : 'password'" + formControlName="oldPassword" + class="form-input password" + autocomplete="on" + /> + <app-svg-icon + (click)="showOldPassword()" + [type]="'form'" + [iconClass]="'grey'" + [icon]="'eyePassword'" + ></app-svg-icon> + <app-svg-icon *ngIf="passwordError" [type]="'form'" [icon]="'notValidate'"></app-svg-icon> + </div> + </div> + <div class="form-group" fxLayout="column"> + <label for="password">Nouveau mot de passe</label> + <p class="special" [ngClass]="{ invalid: fpass.password.invalid && fpass.password.value }"> + Le mot de passe doit contenir au minimum : 8 caractères dont un caractère spécial, un caractère en majuscule + et un chiffre. + </p> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowPassword ? 'text' : 'password'" + formControlName="password" + class="form-input password" + autocomplete="on" + /> + <app-svg-icon + (click)="showPassword()" + [type]="'form'" + [iconClass]="'grey'" + [icon]="'eyePassword'" + ></app-svg-icon> + <app-svg-icon *ngIf="fpass.password.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> + <app-svg-icon + *ngIf="fpass.password.invalid && fpass.password.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + <div class="form-group" fxLayout="column"> + <label for="confirmPassword">Confirmation du mot de passe</label> + <div fxLayout="row" fxLayoutGap="13px"> + <input + [type]="isShowConfirmPassword ? 'text' : 'password'" + formControlName="confirmPassword" + class="form-input password" + autocomplete="on" + /> + <app-svg-icon + (click)="showConfirmPassword()" + [type]="'form'" + [iconClass]="'grey'" + [icon]="'eyePassword'" + ></app-svg-icon> + <app-svg-icon + *ngIf="fpass.confirmPassword.valid && fpass.password.value" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="fpass.confirmPassword.invalid && fpass.confirmPassword.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button + type="submit" + [ngClass]="{ invalid: formPassword.invalid }" + class="btn-primary small leave" + (click)="submitPassword()" + > + Valider + </button> + </div> + </form> + <form + *ngIf="editModal == typeModalProfile.email" + [formGroup]="formEmail" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Changer de courriel</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div class="form-group" fxLayout="column"> + <label for="email">Nouveau courriel</label> + <p class="special invalid" *ngIf="this.fmail.email.hasError('alreadyExist')">L'email est déja utilisé.</p> + <div fxLayout="row" fxLayoutGap="13px"> + <input + type="text" + formControlName="email" + class="form-input" + autocomplete="on" + (keyup)="verifyEmailAlreadyUsed($event.target.value, this.fmail.email)" + /> + <app-svg-icon *ngIf="fmail.email.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> + <app-svg-icon + *ngIf="fmail.email.invalid && fmail.email.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button + type="submit" + [ngClass]="{ invalid: formEmail.invalid }" + class="btn-primary small leave" + (click)="submitEmail()" + > + Valider + </button> + </div> + </form> + <div + *ngIf="editModal == typeModalProfile.deleteAccount" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + fxLayoutGap="30px" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Supprimer un compte</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div fxLayout="column" fxLayoutGap="16px"> + <div class="row removeOwner" *ngFor="let owner of structure.owners" fxLayoutGap="16px"> + <button class="btn-primary small" (click)="removeOwner(owner.id)">X</button> + <span> + {{ owner.email }} + </span> + </div> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button type="button" class="btn-primary small leave" (click)="closeModalOptsProfile()">Terminer</button> + </div> + </div> + <form + *ngIf="editModal == typeModalProfile.addAccount" + [formGroup]="formAddAccount" + class="contentModal" + fxLayout="column" + fxLayoutAlign="center start" + fxLayoutGap="30px" + > + <div fxLayout="row" class="headerModal" fxLayoutAlign="space-between center"> + <h2>Ajouter un compte</h2> + <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> + </div> + <div class="form-group" fxLayout="column"> + <label for="email">Courriel du compte à ajouter</label> + <p *ngIf="ownerAlreadyLinked" class="special invalid">L'email est déjà rattaché à la structure.</p> + <div fxLayout="row" fxLayoutGap="13px"> + <input type="text" formControlName="email" class="form-input" autocomplete="on" /> + <app-svg-icon *ngIf="fAddAccount.email.valid" [type]="'form'" [icon]="'validate'"></app-svg-icon> + <app-svg-icon + *ngIf="fAddAccount.email.invalid && fAddAccount.email.value" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + <div class="footerModal" fxLayout="row" fxLayoutAlign="center center"> + <button + type="submit" + [ngClass]="{ invalid: formAddAccount.invalid }" + class="btn-primary small leave" + (click)="addOwner()" + > + Envoyer + </button> + </div> + </form> + </div> +</div> +<app-modal-confirmation + [openned]="deleteModalStructureOpenned" + [content]="'Voulez-vous vraiment supprimer cette structure ?'" + (closed)="deleteStructure($event)" +></app-modal-confirmation> +<app-modal-confirmation + [openned]="deleteModalAccountOpenned" + [content]="'Voulez-vous vraiment supprimer votre compte ?'" + (closed)="deleteAccount($event)" +></app-modal-confirmation> diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss b/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss new file mode 100644 index 000000000..6edbfaaaf --- /dev/null +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss @@ -0,0 +1,86 @@ +@import '../../../../assets/scss/color'; +@import '../../../../assets/scss/typography'; + +ul { + list-style: none; + margin: 0; + padding-left: 0; +} + +li { + fill: $secondary-color; + color: $black; + display: block; + float: left; + position: relative; + text-decoration: none; + button { + width: 40px; + fill: $secondary-color; + &.active { + background-color: $secondary-color; + fill: $white; + border-color: $secondary-color; + } + &:hover { + background-color: $secondary-color; + fill: $white; + border-color: $secondary-color; + } + } +} + +ul li ul { + position: absolute; + display: block; + margin-left: -268px; + margin-top: 7px; +} + +button { + &.transparent { + background: none; + border: 1px solid $grey-4; + border-radius: 6px; + } +} +.contentModal { + padding: 35px 34px 18px 54px !important; + .headerModal { + width: 100%; + } + p { + &.special { + margin: 8px 0; + @include cn-regular-14; + color: $grey-3; + &.invalid { + color: $orange-warning; + } + } + } + .removeOwner { + button { + width: 40px; + background-color: $red-default; + } + span { + @include cn-bold-18; + } + } + button { + &.invalid { + opacity: 0.4; + } + } + .form-group { + width: 100%; + padding-right: 40px; + input { + width: 100%; + } + } + .ico-close-details { + min-width: 40px; + } +} diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.spec.ts b/src/app/shared/components/structure-options-modal/structure-options-modal.component.spec.ts new file mode 100644 index 000000000..3ffffb034 --- /dev/null +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StructureOptionsModalComponent } from './structure-options-modal.component'; + +describe('StructureOptionsModalComponent', () => { + let component: StructureOptionsModalComponent; + let fixture: ComponentFixture<StructureOptionsModalComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [StructureOptionsModalComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(StructureOptionsModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts b/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts new file mode 100644 index 000000000..2ae893c84 --- /dev/null +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts @@ -0,0 +1,236 @@ +import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core'; +import { AbstractControl, FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { StructureWithOwners } from '../../../models/structureWithOwners.model'; +import { TempUser } from '../../../models/temp-user.model'; +import { User } from '../../../models/user.model'; +import { TypeModalProfile } from '../../../profile/enum/TypeModalProfile.enum'; +import { ProfileService } from '../../../profile/services/profile.service'; +import { AuthService } from '../../../services/auth.service'; +import { StructureService } from '../../../services/structure.service'; +import { CustomRegExp } from '../../../utils/CustomRegExp'; +import { FunctionTypeModalOptions } from '../../enum/functionTypeModalOptions.enum'; +import { MustMatch } from '../../validator/form'; + +@Component({ + selector: 'app-structure-options-modal', + templateUrl: './structure-options-modal.component.html', + styleUrls: ['./structure-options-modal.component.scss'], +}) +export class StructureOptionsModalComponent implements OnInit { + // Global var + @Input() public structure?: StructureWithOwners; + @Input() public userProfile?: User; + @Output() closed = new EventEmitter(); + public active: boolean; + + // Password profile + public formPassword: FormGroup; + public isShowOldPassword = false; + public isShowPassword = false; + public isShowConfirmPassword = false; + public passwordError = false; + + // AddAccount + public formAddAccount: FormGroup; + public ownerAlreadyLinked = false; + + // Email profile + public formEmail: FormGroup; + public changeEmail = false; + + // Modal var + public editModal: TypeModalProfile; + public deleteModalAccountOpenned = false; + public deleteModalStructureOpenned = false; + public showModalOption = false; + public typeModalProfile = TypeModalProfile; + + constructor( + private router: Router, + private formBuilder: FormBuilder, + private profileService: ProfileService, + private authService: AuthService, + private structureService: StructureService + ) {} + + ngOnInit(): void { + this.formPassword = this.formBuilder.group( + { + oldPassword: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], + password: ['', [Validators.required, Validators.pattern(CustomRegExp.PASSWORD)]], + confirmPassword: [''], + }, + { validator: MustMatch('password', 'confirmPassword') } + ); + this.formEmail = this.formBuilder.group({ + email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + }); + this.formAddAccount = this.formBuilder.group({ + email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + }); + } + + public openModalOpts(): void { + this.showModalOption = true; + this.active = true; + } + + // getter for form fields + get fmail(): { [key: string]: AbstractControl } { + return this.formEmail.controls; + } + + // getter for form fields + get fAddAccount(): { [key: string]: AbstractControl } { + return this.formAddAccount.controls; + } + + // getter for form fields + get fpass(): { [key: string]: AbstractControl } { + return this.formPassword.controls; + } + + public showOldPassword(): void { + this.isShowOldPassword = !this.isShowOldPassword; + } + public showPassword(): void { + this.isShowPassword = !this.isShowPassword; + } + public showConfirmPassword(): void { + this.isShowConfirmPassword = !this.isShowConfirmPassword; + } + + public closeModalOpts(functionType: number): void { + switch (functionType) { + case FunctionTypeModalOptions.changeEmail: + this.editModal = TypeModalProfile.email; + break; + case FunctionTypeModalOptions.changePassword: + this.editModal = TypeModalProfile.password; + break; + case FunctionTypeModalOptions.deleteAccount: + this.toggleDeleteAccountModal(); + break; + case FunctionTypeModalOptions.addUser: + this.editModal = TypeModalProfile.addAccount; + this.ownerAlreadyLinked = false; + break; + case FunctionTypeModalOptions.removeUser: + this.editModal = TypeModalProfile.deleteAccount; + break; + case FunctionTypeModalOptions.editStructure: + this.router.navigateByUrl('/create-structure', { state: { data: this.structure.structure } }); + break; + case FunctionTypeModalOptions.removeStructure: + this.toggleDeleteStructureModal(); + break; + default: + break; + } + this.showModalOption = false; + this.active = false; + } + + // Profile Section + public closeModalOptsProfile(): void { + this.editModal = null; + //this.formAddAccount.reset(); + this.formEmail.reset(); + this.formPassword.reset(); + } + + private toggleDeleteAccountModal(): void { + this.deleteModalAccountOpenned = !this.deleteModalAccountOpenned; + } + private toggleDeleteStructureModal(): void { + this.deleteModalStructureOpenned = !this.deleteModalStructureOpenned; + } + + public deleteAccount(shouldDelete: boolean): void { + this.toggleDeleteAccountModal(); + if (shouldDelete) { + this.profileService.deleteProfile().subscribe(() => { + this.logout(); + }); + } + } + + public deleteStructure(shouldDelete: boolean): void { + this.toggleDeleteStructureModal(); + if (shouldDelete) { + this.structureService.delete(this.structure.structure._id).subscribe(() => { + this.closed.emit(''); + }); + } + } + + public logout(): void { + this.authService.logout(); + } + + public submitPassword(): void { + // stop here if form is invalid + if (this.formPassword.invalid) { + return; + } + this.profileService.changePassword(this.formPassword.value.password, this.formPassword.value.oldPassword).subscribe( + () => { + this.closeModalOptsProfile(); + this.formPassword.reset(); + this.passwordError = false; + }, + (error) => { + this.passwordError = true; + } + ); + } + + public addOwner(): void { + // stop here if form is invalid + if (this.formAddAccount.invalid) { + return; + } + const user = new TempUser(); + user.email = this.fAddAccount.email.value; + this.structureService.addOwnerToStructure(user, this.structure.structure._id).subscribe( + () => { + this.closeModalOptsProfile(); + this.formAddAccount.reset(); + }, + (err) => { + this.ownerAlreadyLinked = true; + } + ); + } + + public removeOwner(owner: string): void { + this.structureService.removeOwnerFromStructure(owner, this.structure.structure._id).subscribe(() => { + this.structure.owners = this.structure.owners.filter((o) => o.id !== owner); + if (this.structure.owners.length == 0) { + this.closeModalOptsProfile(); + } + }); + } + + public verifyEmailAlreadyUsed(inputEmail, formControl: FormControl): void { + if (formControl.valid) { + this.profileService.isEmailAlreadyUsed(inputEmail).subscribe((isExist) => { + if (isExist) { + formControl.setErrors({ alreadyExist: true }); + } + }); + } + } + + public submitEmail(): void { + // stop here if form is invalid + if (this.formEmail.invalid) { + return; + } + this.profileService.changeEmail(this.formEmail.value.email, this.userProfile.email).subscribe(() => { + this.closeModalOptsProfile(); + this.formEmail.reset(); + }); + } +} diff --git a/src/app/profile/enum/functionTypeModalOptions.enum.ts b/src/app/shared/enum/functionTypeModalOptions.enum.ts similarity index 100% rename from src/app/profile/enum/functionTypeModalOptions.enum.ts rename to src/app/shared/enum/functionTypeModalOptions.enum.ts diff --git a/src/styles.scss b/src/styles.scss index b49b24295..c1f107ae1 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -193,6 +193,7 @@ button { position: absolute; content: ''; top: 0; + left: 0; background-color: $modal-background; .modal { max-height: 90%; -- GitLab From a72cc2710b328a1fa9e57c6f63fd0db60d71573b Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 23 Feb 2021 16:24:28 +0100 Subject: [PATCH 51/72] fix(form) : add optionFormBtn to form --- src/app/form/form.component.html | 7 ++++++- src/app/form/form.component.ts | 12 +++++++++++- .../modal-options/modal-options.component.html | 8 +++++++- .../modal-options/modal-options.component.ts | 1 + .../structure-options-modal.component.html | 1 + .../structure-options-modal.component.ts | 1 + 6 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 517892876..dbfe52fe2 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -5,8 +5,13 @@ (closed)="hasRedirectionAccepted($event)" ></app-modal-confirmation> <div class="content" *ngIf="!isLoading" [ngClass]="{ editMode: isEditMode }"> - <div class="headerEditMode" *ngIf="isEditMode"> + <div class="headerEditMode" *ngIf="isEditMode" fxLayout="row" fxLayoutAlign="space-between center"> <h2>Modification de {{ editForm.get('structureName').value }}</h2> + <app-structure-options-modal + [structure]="structureWithOwners" + [isEditFormView]="true" + (closed)="structureDeleted()" + ></app-structure-options-modal> </div> <div class="returnBtnSection" *ngIf="isEditMode && currentPage != 0"> <button class="btn-primary previous" (click)="goToSpecificPage(0, false)"> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 2b9b3f25a..dcc151211 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 { PageTypeEnum } from './pageType.enum'; import { CustomRegExp } from '../utils/CustomRegExp'; +import { StructureWithOwners } from '../models/structureWithOwners.model'; const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', @@ -71,6 +72,7 @@ export class FormComponent implements OnInit { public isJoinMode = false; public isLoading = false; public isWifiChoosen = false; + public structureWithOwners: StructureWithOwners; constructor( private structureService: StructureService, @@ -92,7 +94,11 @@ export class FormComponent implements OnInit { if (history.state.data) { this.isEditMode = true; this.isWifiChoosen = true; - this.initForm(new Structure(history.state.data)); + 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) { this.isClaimMode = true; // Handle join strucutre, the case is very similar to claim @@ -903,4 +909,8 @@ export class FormComponent implements OnInit { public displayClaimStructure(): boolean { return this.currentPage == this.pageTypeEnum.summary && !this.isEditMode && this.isClaimMode; } + + public structureDeleted(): void { + this.router.navigateByUrl('home'); + } } diff --git a/src/app/shared/components/modal-options/modal-options.component.html b/src/app/shared/components/modal-options/modal-options.component.html index 6d8a62025..0e45ca7cc 100644 --- a/src/app/shared/components/modal-options/modal-options.component.html +++ b/src/app/shared/components/modal-options/modal-options.component.html @@ -28,7 +28,13 @@ <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'remove'"></app-svg-icon> <p>Supprimer un compte</p> </div> - <div (click)="closeModal(functionType.editStructure)" fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="9px"> + <div + *ngIf="!isEditFormView" + (click)="closeModal(functionType.editStructure)" + fxLayout="row" + fxLayoutAlign="start center" + fxLayoutGap="9px" + > <app-svg-icon [type]="'ico'" [iconColor]="'inherit'" [icon]="'edit'"></app-svg-icon> <p>Modifier la structure</p> </div> diff --git a/src/app/shared/components/modal-options/modal-options.component.ts b/src/app/shared/components/modal-options/modal-options.component.ts index 1fa03b1ed..4de502a5c 100644 --- a/src/app/shared/components/modal-options/modal-options.component.ts +++ b/src/app/shared/components/modal-options/modal-options.component.ts @@ -12,6 +12,7 @@ export class ModalOptionsComponent implements OnInit { constructor() {} @Input() isModalProfileOpts = false; @Input() hasOwners = true; + @Input() public isEditFormView? = false; @Output() closed = new EventEmitter<number>(); ngOnInit(): void {} diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.html b/src/app/shared/components/structure-options-modal/structure-options-modal.component.html index 1857d0baa..a5f68cc4d 100644 --- a/src/app/shared/components/structure-options-modal/structure-options-modal.component.html +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.html @@ -7,6 +7,7 @@ <ul *ngIf="showModalOption" class="dropdown"> <app-modal-options [isModalProfileOpts]="!structure" + [isEditFormView]="structure && isEditFormView" [hasOwners]="structure && structure.owners.length > 0" (closed)="closeModalOpts($event)" ></app-modal-options> diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts b/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts index 2ae893c84..dccf978c4 100644 --- a/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.ts @@ -21,6 +21,7 @@ export class StructureOptionsModalComponent implements OnInit { // Global var @Input() public structure?: StructureWithOwners; @Input() public userProfile?: User; + @Input() public isEditFormView? = false; @Output() closed = new EventEmitter(); public active: boolean; -- GitLab From 980992f9d89584aff9ef4b753aff89027ae94c82 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Tue, 23 Feb 2021 16:27:42 +0100 Subject: [PATCH 52/72] fix(profile) : clean css --- src/app/profile/profile.component.scss | 83 -------------------------- 1 file changed, 83 deletions(-) diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss index c7b151d23..a2a120f30 100644 --- a/src/app/profile/profile.component.scss +++ b/src/app/profile/profile.component.scss @@ -46,13 +46,6 @@ } } } -button { - &.transparent { - background: none; - border: 1px solid $grey-4; - border-radius: 6px; - } -} .structureSection { margin-bottom: 108px; .structureCard { @@ -88,79 +81,3 @@ button { color: $white; } } -.contentModal { - padding: 35px 34px 18px 54px !important; - .headerModal { - width: 100%; - } - p { - &.special { - margin: 8px 0; - @include cn-regular-14; - color: $grey-3; - &.invalid { - color: $orange-warning; - } - } - } - .removeOwner { - button { - width: 40px; - background-color: $red-default; - } - span { - @include cn-bold-18; - } - } - button { - &.invalid { - opacity: 0.4; - } - } - .form-group { - width: 100%; - padding-right: 40px; - input { - width: 100%; - } - } - .ico-close-details { - min-width: 40px; - } -} - -ul { - list-style: none; - margin: 0; - padding-left: 0; -} - -li { - fill: $secondary-color; - color: $black; - display: block; - float: left; - position: relative; - text-decoration: none; - button { - width: 40px; - fill: $secondary-color; - &.active { - background-color: $secondary-color; - fill: $white; - border-color: $secondary-color; - } - &:hover { - background-color: $secondary-color; - fill: $white; - border-color: $secondary-color; - } - } -} - -ul li ul { - position: absolute; - display: block; - margin-left: -268px; - margin-top: 7px; -} -- GitLab From 35df2f9fb58879a4af33e000cf788707b5bb4efc Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 23 Feb 2021 16:34:10 +0100 Subject: [PATCH 53/72] fix: structure list card height --- src/app/structure-list/components/card/card.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/structure-list/components/card/card.component.scss b/src/app/structure-list/components/card/card.component.scss index bf275ebec..dcae0f479 100644 --- a/src/app/structure-list/components/card/card.component.scss +++ b/src/app/structure-list/components/card/card.component.scss @@ -6,7 +6,7 @@ .structure { padding: 12px 0 12px 0; border-bottom: 1px dashed $grey !important; - height: 110px; + min-height: 110px; cursor: pointer; @media #{$large-phone} { height: unset; -- GitLab From ecff2a37d20e6f85f0823bc96899975b5298d219 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 23 Feb 2021 16:36:28 +0100 Subject: [PATCH 54/72] feat: add outdated browser handling --- Dockerfile | 4 ++++ nginx/dev.conf | 23 ++++++++++++++++++++--- nginx/outdated.html | 21 +++++++++++++++++++++ 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 nginx/outdated.html diff --git a/Dockerfile b/Dockerfile index bee682a15..802c807ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,9 +30,13 @@ RUN apt-get update COPY --from=build /app/dist/fr /usr/share/nginx/html +# Add outdated browser page +ADD ./nginx/outdated.html /usr/share/nginx/html + RUN touch /var/run/nginx.pid RUN ls -l /usr/share/nginx/html + # expose port 8080 EXPOSE 8080 diff --git a/nginx/dev.conf b/nginx/dev.conf index bb472e0c2..7609f90b7 100644 --- a/nginx/dev.conf +++ b/nginx/dev.conf @@ -1,3 +1,19 @@ +map $http_user_agent $outdated { + default 0; + "~MSIE [1-10]\." 1; + "~Trident/[5-7]\." 1; + "~Mozilla.*Firefox/[1-9]\." 1; + "~Mozilla.*Firefox/[0-2][0-9]\." 1; + "~Mozilla.*Firefox/3[0-1]\." 1; + "~Opera.*Version/[0-9]\." 1; + "~Opera.*Version/[0-1][0-9]\." 1; + "~Opera.*Version/2[0-1]\." 1; + "~AppleWebKit.*Version/[0-6]\..*Safari" 1; + "~Chrome/[0-9]\." 1; + "~Chrome/[0-2][0-9]\." 1; + "~Chrome/3[0-3]\." 1; +} + server { listen 8080 default_server; @@ -17,6 +33,10 @@ server { </script>"; location / { + # Redirect outdated nav + if ($outdated = 1){ + rewrite ^ /outdated.html break; + } # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ /index.html; @@ -32,7 +52,6 @@ server { proxy_pass https://passerelle.formulaireextranet.grandlyon.com/base-adresse/base-adresse-nationale/streets; } - location /geocoding/photon/api { proxy_pass https://download.data.grandlyon.com/geocoding/photon/api; } @@ -51,8 +70,6 @@ server { proxy_pass http://ghost:2368; } - - # REALLY important for JavaScript modules (type="module") to work as expected!!! location ~ \.js { add_header Content-Type text/javascript; diff --git a/nginx/outdated.html b/nginx/outdated.html new file mode 100644 index 000000000..9403101b5 --- /dev/null +++ b/nginx/outdated.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html lang="fr"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <title>RESIN: Navigateur incompatible</title> + </head> + + <body> + <div> + <h1>Navigateur incompatible</h1> + <p> + Votre navigateur ne vous permet pas d'accéder à RESIN dans de bonnes conditions. Merci d'utiliser + <a href="https://www.mozilla.org/fr/firefox/new/">Firefox</a>, + <a href="https://www.google.com/intl/fr/chrome/">Chrome</a>, ou la dernière version d'<a + href="https://www.microsoft.com/fr-fr/edge" + >Edge</a + >. + </p> + </div> + </body> +</html> -- GitLab From a5e2f93bb8d8782074faf14936af7f3cb797a4ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20BRISON?= <ext.sopra.jbrison@grandlyon.com> Date: Tue, 23 Feb 2021 16:53:55 +0100 Subject: [PATCH 55/72] fix: typo and color --- src/app/footer/footer.component.scss | 8 +- src/app/form/form.component.scss | 8 +- src/app/header/header.component.scss | 11 +-- src/app/map/components/map.component.scss | 13 ++- src/app/map/services/map.service.ts | 43 +-------- .../components/button/button.component.scss | 2 + .../signin-modal/signin-modal.component.scss | 2 +- .../signup-modal/signup-modal.component.scss | 2 +- .../validator-form.component.scss | 2 +- .../components/card/card.component.scss | 5 +- .../modal-filter/modal-filter.component.scss | 4 +- .../components/search/search.component.html | 2 +- .../components/search/search.component.scss | 2 +- .../structure-details.component.scss | 2 +- src/assets/ico/sprite.svg | 13 +-- src/assets/scss/_buttons.scss | 2 +- src/assets/scss/_color.scss | 3 +- src/assets/scss/_hyperlink.scss | 2 +- src/assets/scss/_typography.scss | 94 +++++++------------ src/styles.scss | 4 +- 20 files changed, 82 insertions(+), 142 deletions(-) diff --git a/src/app/footer/footer.component.scss b/src/app/footer/footer.component.scss index 4c3179c71..7fac9223d 100644 --- a/src/app/footer/footer.component.scss +++ b/src/app/footer/footer.component.scss @@ -13,6 +13,8 @@ a { color: $white; margin: 0px 0px 0px 10px; + text-decoration: none; + @include cn-regular-12; &:hover { text-decoration: underline; } @@ -35,12 +37,6 @@ text-align: center; } } - @media #{$desktop} { - font-size: 1em; - } - @media #{$tablet} { - font-size: 0.875em; - } } .metro-title { diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 01975ebbf..6a6f572f7 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -108,7 +108,7 @@ h3 { overflow-y: auto; height: auto; h2 { - @include cn-bold-28; + @include cn-bold-26; color: $black; margin-bottom: 0; span { @@ -119,7 +119,11 @@ h3 { } } h3 { - @include cn-bold-22; + @include cn-bold-26; + + @media #{$tablet} { + @include cn-bold-22; + } } .page { max-width: 960px; diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index b745464c0..37b4b41bc 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -52,6 +52,7 @@ a { border-bottom: 2px solid $red-default; text-decoration: none; } + @include cn-regular-14; } @media print { @@ -114,19 +115,11 @@ a { } .title { margin-bottom: 35px; - @include cn-regular-28; + @include cn-regular-26; span { color: $grey-2; } } -.footer { - a { - height: unset; - display: table-cell; - vertical-align: middle; - margin: auto 0 auto 0; - } -} .desktop-show { display: block; diff --git a/src/app/map/components/map.component.scss b/src/app/map/components/map.component.scss index 817150907..cb53c0d90 100644 --- a/src/app/map/components/map.component.scss +++ b/src/app/map/components/map.component.scss @@ -76,6 +76,14 @@ stroke-width: unset !important; } } + &:hover { + svg { + fill: $red-info; + &.mdm { + fill: #bd9e6a; + } + } + } } ::ng-deep .leaflet-left { @@ -97,12 +105,15 @@ h1 { color: $grey-1; @include cn-bold-20; + font-size: 18px; margin: 0; } p { color: $grey-3; @include cn-regular-16; + font-size: 16px; margin: 0 0 13px 0; + font-style: italic; } .pop-up { text-align: center; @@ -111,6 +122,7 @@ button { @include btn-search-filter; @include cn-bold-14; + font-size: 16px; } } span { @@ -127,7 +139,6 @@ ::ng-deep .leaflet-popup-tip-container { display: none; } - @media print { .map-wrapper { display: none; diff --git a/src/app/map/services/map.service.ts b/src/app/map/services/map.service.ts index 52d5ba0b7..901b4d615 100644 --- a/src/app/map/services/map.service.ts +++ b/src/app/map/services/map.service.ts @@ -9,16 +9,9 @@ import { MarkerType } from '../components/markerType.enum'; export class MapService { private static markersList = {}; private isMarkerActive = false; - public markerIconHover = divIcon({ - className: null, - html: '<svg width="40" height="46"><use xlink:href="assets/ico/sprite.svg#map-marker-locate"></use></svg>', - iconSize: [40, 46], - iconAnchor: [20, 46], - popupAnchor: [0, -46], - }); public markerIconActive = divIcon({ className: null, - html: '<svg width="40" height="46" fill="#d50000"><use xlink:href="assets/ico/sprite.svg#map-marker"></use></svg>', + html: '<svg width="40" height="46" fill="#A00000"><use xlink:href="assets/ico/sprite.svg#map-marker"></use></svg>', iconSize: [40, 46], iconAnchor: [20, 46], popupAnchor: [0, -46], @@ -33,13 +26,8 @@ export class MapService { }); public markerIconMdm = divIcon({ className: null, - html: '<svg width="19" height="24"><use xlink:href="assets/ico/sprite.svg#mdm"></use></svg>', - iconSize: [19, 24], - iconAnchor: [9, 0], - }); - public markerIconMdmHover = divIcon({ - className: null, - html: '<svg width="19" height="24"><use xlink:href="assets/ico/sprite.svg#mdm-hover"></use></svg>', + html: + '<svg width="19" height="24" fill="#D4C4A9" class="mdm"><use xlink:href="assets/ico/sprite.svg#mdm"></use></svg>', iconSize: [19, 24], iconAnchor: [9, 0], }); @@ -63,7 +51,7 @@ export class MapService { if (id) { MapService.markersList[id] = marker; } - return this.bindMouseEventOnMarker(marker, this.getMarkerIcon(markerType), this.getMarkerIconHover(markerType)); + return marker; } private getLayerAttributton(markerType: MarkerType): string { @@ -82,32 +70,11 @@ export class MapService { } } - private getMarkerIconHover(markerType: MarkerType): DivIcon { - if (markerType === MarkerType.mdm) { - return this.markerIconMdmHover; - } else { - return this.markerIconHover; - } - } - - private bindMouseEventOnMarker(marker: Marker, regularIcon: DivIcon, hoverIcon: DivIcon): Marker { - marker.on('mouseover', (e) => { - if (marker.getIcon() === regularIcon) { - marker.setIcon(hoverIcon); - } - }); - - marker.on('mouseout', (e) => { - marker.setIcon(regularIcon); - }); - return marker; - } - /** * @param id marker id */ public setActiveMarker(id: string): void { - this.getMarker(id).setIcon(this.getMarkerIconHover(MarkerType.structure)); + this.getMarker(id).setIcon(this.markerIconActive); } public setUnactiveMarker(id: string): void { diff --git a/src/app/shared/components/button/button.component.scss b/src/app/shared/components/button/button.component.scss index 55ae3ca67..04a71d547 100644 --- a/src/app/shared/components/button/button.component.scss +++ b/src/app/shared/components/button/button.component.scss @@ -47,6 +47,8 @@ button { vertical-align: middle; border-radius: 4px; @include btn-bold; + font-size: 16px; + border-radius: 4px; &.withIcon { color: $black; height: 36px; diff --git a/src/app/shared/components/signin-modal/signin-modal.component.scss b/src/app/shared/components/signin-modal/signin-modal.component.scss index 529354b01..35477fdae 100644 --- a/src/app/shared/components/signin-modal/signin-modal.component.scss +++ b/src/app/shared/components/signin-modal/signin-modal.component.scss @@ -5,7 +5,7 @@ width: 100%; } h3 { - @include cn-bold-24; + @include cn-bold-26; color: $black; margin-top: 0; } diff --git a/src/app/shared/components/signup-modal/signup-modal.component.scss b/src/app/shared/components/signup-modal/signup-modal.component.scss index e5ae20ec9..2133bd8f4 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.scss +++ b/src/app/shared/components/signup-modal/signup-modal.component.scss @@ -11,7 +11,7 @@ color: $orange-warning; } h3 { - @include cn-bold-24; + @include cn-bold-26; color: $black; margin-top: 0; } diff --git a/src/app/shared/components/validator-form/validator-form.component.scss b/src/app/shared/components/validator-form/validator-form.component.scss index 425065fba..2031c9c19 100644 --- a/src/app/shared/components/validator-form/validator-form.component.scss +++ b/src/app/shared/components/validator-form/validator-form.component.scss @@ -1,5 +1,5 @@ @import '../../../../assets/scss/color'; .alert { - color: $red-metro; + color: $red-default; } diff --git a/src/app/structure-list/components/card/card.component.scss b/src/app/structure-list/components/card/card.component.scss index dcae0f479..687abf098 100644 --- a/src/app/structure-list/components/card/card.component.scss +++ b/src/app/structure-list/components/card/card.component.scss @@ -14,17 +14,18 @@ .typeStructure { color: $grey-3; @include cn-regular-16; + font-style: italic; } .structure-name { color: $grey-1; - @include cn-bold-20; + @include cn-bold-18; padding-bottom: 5px; width: 100%; text-transform: capitalize; } .distanceStructure { @include cn-regular-16; - color: $grey-1; + color: $grey-3; width: 50%; } &:last-child { diff --git a/src/app/structure-list/components/modal-filter/modal-filter.component.scss b/src/app/structure-list/components/modal-filter/modal-filter.component.scss index 7c31c30f9..29ac09368 100644 --- a/src/app/structure-list/components/modal-filter/modal-filter.component.scss +++ b/src/app/structure-list/components/modal-filter/modal-filter.component.scss @@ -57,7 +57,7 @@ .titleFilter { display: none !important; margin: 27px 25px 0px 25px; - @include cn-bold-28; + @include cn-bold-26; @media #{$large-phone} { display: flex !important; } @@ -109,7 +109,7 @@ padding: 5px 0; } h4 { - @include cn-bold-14; + @include cn-bold-16; line-height: 17px; text-transform: uppercase; color: $grey-3; diff --git a/src/app/structure-list/components/search/search.component.html b/src/app/structure-list/components/search/search.component.html index 3c933c712..5294bc019 100644 --- a/src/app/structure-list/components/search/search.component.html +++ b/src/app/structure-list/components/search/search.component.html @@ -101,7 +101,7 @@ (change)="numericPassCheck($event, 'labelsQualifications')" /> <span class="customCheck"></span> - <div class="label">Pass numérique</div> + <div class="label pass">Pass numérique</div> </label> </div> </div> diff --git a/src/app/structure-list/components/search/search.component.scss b/src/app/structure-list/components/search/search.component.scss index 8883ab5d8..657eeeffe 100644 --- a/src/app/structure-list/components/search/search.component.scss +++ b/src/app/structure-list/components/search/search.component.scss @@ -9,7 +9,7 @@ } .header { .title { - @include cn-bold-20; + @include cn-bold-26; padding: 16px 0 16px 0; display: flex; align-items: center; diff --git a/src/app/structure-list/components/structure-details/structure-details.component.scss b/src/app/structure-list/components/structure-details/structure-details.component.scss index d8ea58095..5759b6c02 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.scss +++ b/src/app/structure-list/components/structure-details/structure-details.component.scss @@ -64,7 +64,7 @@ a { h2 { margin-top: 0; margin-bottom: 5px; - @include cn-regular-24; + @include cn-regular-26; } h3 { margin: 0; diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 02b135476..748235c0a 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -1,7 +1,5 @@ <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="#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"/> </symbol> @@ -224,16 +222,7 @@ <path fill-rule="evenodd" clip-rule="evenodd" d="M2.84054 5.57617C1.72559 6.04339 1 7.13416 1 8.34305V23H8V18.5C8 17.6716 8.67157 17 9.5 17C10.3284 17 11 17.6716 11 18.5V23H18V8.40362C18 7.1644 17.2381 6.05271 16.0823 5.60565L14.5555 5.01505C14.1291 4.85011 13.746 4.5899 13.4355 4.2543L10.9183 1.5332C10.1451 0.6974 8.83121 0.674761 8.0297 1.48343L5.19821 4.34019C4.92065 4.62023 4.5906 4.84281 4.22694 4.9952L2.84054 5.57617Z"/> </mask> <path d="M2.84054 5.57617L3.03378 6.03731L2.84054 5.57617ZM1 23H0.5V23.5H1V23ZM8 23V23.5H8.5V23H8ZM11 23H10.5V23.5H11V23ZM18 23V23.5H18.5V23H18ZM16.0823 5.60565L16.2627 5.13932L16.0823 5.60565ZM14.5555 5.01505L14.3751 5.48138H14.3751L14.5555 5.01505ZM13.4355 4.2543L13.0685 4.59383V4.59383L13.4355 4.2543ZM10.9183 1.5332L11.2854 1.19366V1.19366L10.9183 1.5332ZM8.0297 1.48343L8.38482 1.8354V1.8354L8.0297 1.48343ZM5.19821 4.34019L4.84309 3.98821L5.19821 4.34019ZM4.22694 4.9952L4.42019 5.45634L4.22694 4.9952ZM1.5 8.34305C1.5 7.33564 2.10466 6.42666 3.03378 6.03731L2.6473 5.11502C1.34652 5.66011 0.5 6.93268 0.5 8.34305H1.5ZM1.5 23V8.34305H0.5V23H1.5ZM8 22.5H1V23.5H8V22.5ZM8.5 23V18.5H7.5V23H8.5ZM8.5 18.5C8.5 17.9477 8.94772 17.5 9.5 17.5V16.5C8.39543 16.5 7.5 17.3954 7.5 18.5H8.5ZM9.5 17.5C10.0523 17.5 10.5 17.9477 10.5 18.5H11.5C11.5 17.3954 10.6046 16.5 9.5 16.5V17.5ZM10.5 18.5V23H11.5V18.5H10.5ZM18 22.5H11V23.5H18V22.5ZM17.5 8.40362V23H18.5V8.40362H17.5ZM15.9019 6.07197C16.865 6.44453 17.5 7.37094 17.5 8.40362H18.5C18.5 6.95786 17.6111 5.66089 16.2627 5.13932L15.9019 6.07197ZM14.3751 5.48138L15.9019 6.07197L16.2627 5.13932L14.7359 4.54872L14.3751 5.48138ZM13.0685 4.59383C13.4307 4.98537 13.8776 5.28895 14.3751 5.48138L14.7359 4.54872C14.3805 4.41127 14.0613 4.19443 13.8026 3.91476L13.0685 4.59383ZM10.5513 1.87273L13.0685 4.59383L13.8026 3.91476L11.2854 1.19366L10.5513 1.87273ZM8.38482 1.8354C8.98595 1.22891 9.97141 1.24589 10.5513 1.87273L11.2854 1.19366C10.3189 0.148915 8.67647 0.120616 7.67458 1.13145L8.38482 1.8354ZM5.55333 4.69216L8.38482 1.8354L7.67458 1.13145L4.84309 3.98821L5.55333 4.69216ZM4.42019 5.45634C4.84445 5.27856 5.22951 5.01888 5.55333 4.69216L4.84309 3.98821C4.61179 4.22158 4.33675 4.40706 4.0337 4.53405L4.42019 5.45634ZM3.03378 6.03731L4.42019 5.45634L4.0337 4.53405L2.6473 5.11502L3.03378 6.03731Z" fill="white" mask="url(#path-1-outside-1)"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M2.84054 5.57617C1.72559 6.04339 1 7.13416 1 8.34305V23H8V18.5C8 17.6716 8.67157 17 9.5 17C10.3284 17 11 17.6716 11 18.5V23H18V8.40362C18 7.1644 17.2381 6.05271 16.0823 5.60565L14.5555 5.01505C14.1291 4.85011 13.746 4.5899 13.4355 4.2543L10.9183 1.5332C10.1451 0.6974 8.83121 0.674761 8.0297 1.48343L5.19821 4.34018C4.92065 4.62023 4.5906 4.84281 4.22694 4.9952L2.84054 5.57617ZM11 4.5C11 5.32843 10.3284 6 9.5 6C8.67157 6 8 5.32843 8 4.5C8 3.67157 8.67157 3 9.5 3C10.3284 3 11 3.67157 11 4.5ZM4.25 15.9354C3.54057 16.0544 3 16.6714 3 17.4146V18.75H4.25V15.9354ZM3 21.9146V19.25H4.25V21.9146H3ZM4.75 21.9146V19.25H6V21.9146H4.75ZM6 17.4146V18.75H4.75V15.9354C5.45943 16.0544 6 16.6714 6 17.4146ZM13 17.4146C13 16.6714 13.5406 16.0544 14.25 15.9354V18.75H13V17.4146ZM13 19.25V21.9146H14.25V19.25H13ZM14.75 19.25V21.9146H16V19.25H14.75ZM16 18.75V17.4146C16 16.6714 15.4594 16.0544 14.75 15.9354V18.75H16ZM14.25 8C13.5406 8.11902 13 8.73601 13 9.47926V10.8146H14.25V8ZM13 13.9793V11.3146H14.25V13.9793H13ZM14.75 13.9793V11.3146H16V13.9793H14.75ZM16 9.47926V10.8146H14.75V8C15.4594 8.11902 16 8.73601 16 9.47926ZM8 9.47926C8 8.73601 8.54057 8.11902 9.25 8V10.8146H8V9.47926ZM8 11.3146V13.9793H9.25V11.3146H8ZM9.75 11.3146V13.9793H11V11.3146H9.75ZM11 10.8146V9.47926C11 8.73601 10.4594 8.11902 9.75 8V10.8146H11ZM4.25 8C3.54057 8.11902 3 8.73601 3 9.47926V10.8146H4.25V8ZM3 13.9793V11.3146H4.25V13.9793H3ZM4.75 13.9793V11.3146H6V13.9793H4.75ZM6 9.47926V10.8146H4.75V8C5.45943 8.11902 6 8.73601 6 9.47926Z" fill="#D4C4A9"/> -</symbol> - -<symbol id="mdm-hover" viewBox="0 0 19 24" xmlns="http://www.w3.org/2000/svg"> -<mask id="path-1-outside-1" maskUnits="userSpaceOnUse" x="0" y="-0.108661" width="19" height="24" fill="black"> -<rect fill="white" y="-0.108661" width="19" height="24"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M2.84054 5.57617C1.72559 6.04339 1 7.13416 1 8.34305V23H8V18.5C8 17.6716 8.67157 17 9.5 17C10.3284 17 11 17.6716 11 18.5V23H18V8.40362C18 7.1644 17.2381 6.05271 16.0823 5.60565L14.5555 5.01505C14.1291 4.85011 13.746 4.5899 13.4355 4.2543L10.9183 1.5332C10.1451 0.6974 8.83121 0.674761 8.0297 1.48343L5.19821 4.34019C4.92065 4.62023 4.5906 4.84281 4.22694 4.9952L2.84054 5.57617Z"/> -</mask> -<path d="M2.84054 5.57617L3.03378 6.03731L2.84054 5.57617ZM1 23H0.5V23.5H1V23ZM8 23V23.5H8.5V23H8ZM11 23H10.5V23.5H11V23ZM18 23V23.5H18.5V23H18ZM16.0823 5.60565L16.2627 5.13932L16.0823 5.60565ZM14.5555 5.01505L14.3751 5.48138H14.3751L14.5555 5.01505ZM13.4355 4.2543L13.0685 4.59383V4.59383L13.4355 4.2543ZM10.9183 1.5332L11.2854 1.19366V1.19366L10.9183 1.5332ZM8.0297 1.48343L8.38482 1.8354V1.8354L8.0297 1.48343ZM5.19821 4.34019L4.84309 3.98821L5.19821 4.34019ZM4.22694 4.9952L4.42019 5.45634L4.22694 4.9952ZM1.5 8.34305C1.5 7.33564 2.10466 6.42666 3.03378 6.03731L2.6473 5.11502C1.34652 5.66011 0.5 6.93268 0.5 8.34305H1.5ZM1.5 23V8.34305H0.5V23H1.5ZM8 22.5H1V23.5H8V22.5ZM8.5 23V18.5H7.5V23H8.5ZM8.5 18.5C8.5 17.9477 8.94772 17.5 9.5 17.5V16.5C8.39543 16.5 7.5 17.3954 7.5 18.5H8.5ZM9.5 17.5C10.0523 17.5 10.5 17.9477 10.5 18.5H11.5C11.5 17.3954 10.6046 16.5 9.5 16.5V17.5ZM10.5 18.5V23H11.5V18.5H10.5ZM18 22.5H11V23.5H18V22.5ZM17.5 8.40362V23H18.5V8.40362H17.5ZM15.9019 6.07197C16.865 6.44453 17.5 7.37094 17.5 8.40362H18.5C18.5 6.95786 17.6111 5.66089 16.2627 5.13932L15.9019 6.07197ZM14.3751 5.48138L15.9019 6.07197L16.2627 5.13932L14.7359 4.54872L14.3751 5.48138ZM13.0685 4.59383C13.4307 4.98537 13.8776 5.28895 14.3751 5.48138L14.7359 4.54872C14.3805 4.41127 14.0613 4.19443 13.8026 3.91476L13.0685 4.59383ZM10.5513 1.87273L13.0685 4.59383L13.8026 3.91476L11.2854 1.19366L10.5513 1.87273ZM8.38482 1.8354C8.98595 1.22891 9.97141 1.24589 10.5513 1.87273L11.2854 1.19366C10.3189 0.148915 8.67647 0.120616 7.67458 1.13145L8.38482 1.8354ZM5.55333 4.69216L8.38482 1.8354L7.67458 1.13145L4.84309 3.98821L5.55333 4.69216ZM4.42019 5.45634C4.84445 5.27856 5.22951 5.01888 5.55333 4.69216L4.84309 3.98821C4.61179 4.22158 4.33675 4.40706 4.0337 4.53405L4.42019 5.45634ZM3.03378 6.03731L4.42019 5.45634L4.0337 4.53405L2.6473 5.11502L3.03378 6.03731Z" fill="white" mask="url(#path-1-outside-1)"/> -<path fill-rule="evenodd" clip-rule="evenodd" d="M2.84054 5.57617C1.72559 6.04339 1 7.13416 1 8.34305V23H8V18.5C8 17.6716 8.67157 17 9.5 17C10.3284 17 11 17.6716 11 18.5V23H18V8.40362C18 7.1644 17.2381 6.05271 16.0823 5.60565L14.5555 5.01505C14.1291 4.85011 13.746 4.5899 13.4355 4.2543L10.9183 1.5332C10.1451 0.6974 8.83121 0.674761 8.0297 1.48343L5.19821 4.34018C4.92065 4.62023 4.5906 4.84281 4.22694 4.9952L2.84054 5.57617ZM11 4.5C11 5.32843 10.3284 6 9.5 6C8.67157 6 8 5.32843 8 4.5C8 3.67157 8.67157 3 9.5 3C10.3284 3 11 3.67157 11 4.5ZM4.25 15.9354C3.54057 16.0544 3 16.6714 3 17.4146V18.75H4.25V15.9354ZM3 21.9146V19.25H4.25V21.9146H3ZM4.75 21.9146V19.25H6V21.9146H4.75ZM6 17.4146V18.75H4.75V15.9354C5.45943 16.0544 6 16.6714 6 17.4146ZM13 17.4146C13 16.6714 13.5406 16.0544 14.25 15.9354V18.75H13V17.4146ZM13 19.25V21.9146H14.25V19.25H13ZM14.75 19.25V21.9146H16V19.25H14.75ZM16 18.75V17.4146C16 16.6714 15.4594 16.0544 14.75 15.9354V18.75H16ZM14.25 8C13.5406 8.11902 13 8.73601 13 9.47926V10.8146H14.25V8ZM13 13.9793V11.3146H14.25V13.9793H13ZM14.75 13.9793V11.3146H16V13.9793H14.75ZM16 9.47926V10.8146H14.75V8C15.4594 8.11902 16 8.73601 16 9.47926ZM8 9.47926C8 8.73601 8.54057 8.11902 9.25 8V10.8146H8V9.47926ZM8 11.3146V13.9793H9.25V11.3146H8ZM9.75 11.3146V13.9793H11V11.3146H9.75ZM11 10.8146V9.47926C11 8.73601 10.4594 8.11902 9.75 8V10.8146H11ZM4.25 8C3.54057 8.11902 3 8.73601 3 9.47926V10.8146H4.25V8ZM3 13.9793V11.3146H4.25V13.9793H3ZM4.75 13.9793V11.3146H6V13.9793H4.75ZM6 9.47926V10.8146H4.75V8C5.45943 8.11902 6 8.73601 6 9.47926Z" fill="#BD9E6A"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M2.84054 5.57617C1.72559 6.04339 1 7.13416 1 8.34305V23H8V18.5C8 17.6716 8.67157 17 9.5 17C10.3284 17 11 17.6716 11 18.5V23H18V8.40362C18 7.1644 17.2381 6.05271 16.0823 5.60565L14.5555 5.01505C14.1291 4.85011 13.746 4.5899 13.4355 4.2543L10.9183 1.5332C10.1451 0.6974 8.83121 0.674761 8.0297 1.48343L5.19821 4.34018C4.92065 4.62023 4.5906 4.84281 4.22694 4.9952L2.84054 5.57617ZM11 4.5C11 5.32843 10.3284 6 9.5 6C8.67157 6 8 5.32843 8 4.5C8 3.67157 8.67157 3 9.5 3C10.3284 3 11 3.67157 11 4.5ZM4.25 15.9354C3.54057 16.0544 3 16.6714 3 17.4146V18.75H4.25V15.9354ZM3 21.9146V19.25H4.25V21.9146H3ZM4.75 21.9146V19.25H6V21.9146H4.75ZM6 17.4146V18.75H4.75V15.9354C5.45943 16.0544 6 16.6714 6 17.4146ZM13 17.4146C13 16.6714 13.5406 16.0544 14.25 15.9354V18.75H13V17.4146ZM13 19.25V21.9146H14.25V19.25H13ZM14.75 19.25V21.9146H16V19.25H14.75ZM16 18.75V17.4146C16 16.6714 15.4594 16.0544 14.75 15.9354V18.75H16ZM14.25 8C13.5406 8.11902 13 8.73601 13 9.47926V10.8146H14.25V8ZM13 13.9793V11.3146H14.25V13.9793H13ZM14.75 13.9793V11.3146H16V13.9793H14.75ZM16 9.47926V10.8146H14.75V8C15.4594 8.11902 16 8.73601 16 9.47926ZM8 9.47926C8 8.73601 8.54057 8.11902 9.25 8V10.8146H8V9.47926ZM8 11.3146V13.9793H9.25V11.3146H8ZM9.75 11.3146V13.9793H11V11.3146H9.75ZM11 10.8146V9.47926C11 8.73601 10.4594 8.11902 9.75 8V10.8146H11ZM4.25 8C3.54057 8.11902 3 8.73601 3 9.47926V10.8146H4.25V8ZM3 13.9793V11.3146H4.25V13.9793H3ZM4.75 13.9793V11.3146H6V13.9793H4.75ZM6 9.47926V10.8146H4.75V8C5.45943 8.11902 6 8.73601 6 9.47926Z" stroke="none"/> </symbol> <symbol id="borne" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss index ec08d9b85..3827c75d7 100644 --- a/src/assets/scss/_buttons.scss +++ b/src/assets/scss/_buttons.scss @@ -38,7 +38,7 @@ line-break: 18px; } @mixin btn-search-addStructure { - @include btn-bold-sousligne; + @include btn-bold-underline; color: $secondary-color; outline: none; } diff --git a/src/assets/scss/_color.scss b/src/assets/scss/_color.scss index b5a0368f8..75dd41e99 100644 --- a/src/assets/scss/_color.scss +++ b/src/assets/scss/_color.scss @@ -18,11 +18,10 @@ $orange-warning: #da6c2e; $blue: #348899; $blue-hover: #117083; $blue-active: #8cb6be; -$red-metro: #d50000; $red-info: #a00000; $red-default: #ed3939; /* APP COLORS */ -$primary-color: $red-metro; +$primary-color: $red-default; $secondary-color: $blue; $default-link-color: $grey-2; $button-secondary: $blue; diff --git a/src/assets/scss/_hyperlink.scss b/src/assets/scss/_hyperlink.scss index 5eed9c1f2..81b49c00e 100644 --- a/src/assets/scss/_hyperlink.scss +++ b/src/assets/scss/_hyperlink.scss @@ -4,7 +4,7 @@ padding: 8px 8px 6px 8px; color: $secondary-color; outline: none; - @include btn-bold-sousligne; + @include btn-bold-underline; &:hover { color: $blue-hover; } diff --git a/src/assets/scss/_typography.scss b/src/assets/scss/_typography.scss index 8d7611006..d5f92bac1 100644 --- a/src/assets/scss/_typography.scss +++ b/src/assets/scss/_typography.scss @@ -5,12 +5,12 @@ $title-font: 'Trebuchet MS', 'Helvetica', sans-serif; $font-size-xxsmall: 0.75em; // 12px $font-size-xsmall: 0.875em; // 14px $font-size-small: 1em; // 16px -$font-size-smedium: 1.25em; // 20px -$font-size-medium: 1.375em; // 22px -$font-size-xmedium: 1.5em; // 24px -$font-size-large: 1.75em; // 28px -$font-size-xlarge: 2em; // 32px -$font-size-medium-mobile: 1.1em; + +$font-size-smedium: 1.125em; // 18px +$font-size-medium: 1.25em; // 20px +$font-size-xmedium: 1.375em; // 22px + +$font-size-large: 1.625em; // 26px html, body, @@ -37,116 +37,73 @@ h6, font-family: $title-font; } -@mixin btn-bold { - @include cn-bold-16; - line-height: 18px; -} - -@mixin btn-bold-sousligne { - @include btn-bold; - text-decoration: underline; -} -@mixin btn-normal { - @include cn-regular-16; - line-height: 19px; -} - -@mixin btn-pass { - @include cn-regular-18; - line-height: 21px; -} - @mixin arial-regular-16 { font-family: $footer-text-font; font-style: normal; font-size: $font-size-small; } -@mixin cn-bold-36 { - font-family: $text-font; - font-style: normal; - font-weight: bold; - font-size: $font-size-large; -} -@mixin cn-bold-32 { - font-family: $title-font; - font-style: normal; - font-weight: bold; - font-size: $font-size-xlarge; -} -@mixin cn-bold-28 { +@mixin cn-bold-26 { font-family: $title-font; font-style: normal; font-weight: bold; font-size: $font-size-large; } -@mixin cn-regular-28 { +@mixin cn-regular-26 { font-family: $text-font; font-style: normal; font-weight: normal; font-size: $font-size-large; } -@mixin cn-regular-24 { - font-family: $text-font; - font-style: normal; - font-weight: normal; - font-size: $font-size-xmedium; -} -@mixin cn-bold-24 { - font-family: $text-font; - font-style: normal; - font-weight: bold; - font-size: $font-size-xmedium; -} @mixin cn-regular-22 { font-family: $text-font; font-style: normal; font-weight: normal; - font-size: $font-size-medium; + font-size: $font-size-xmedium; } @mixin cn-bold-22 { font-family: $text-font; font-style: normal; font-weight: bold; - font-size: $font-size-medium; + font-size: $font-size-xmedium; } @mixin cn-bold-20 { font-family: $text-font; font-style: normal; font-weight: bold; - font-size: $font-size-smedium; + font-size: $font-size-medium; } @mixin cn-regular-20 { font-family: $text-font; font-style: normal; font-weight: normal; - font-size: $font-size-smedium; + font-size: $font-size-medium; } @mixin cn-bold-20 { font-family: $title-font; font-style: normal; font-weight: bold; - font-size: $font-size-smedium; + font-size: $font-size-medium; } @mixin cn-regular-20 { font-family: $title-font; font-style: normal; font-weight: normal; - font-size: $font-size-smedium; + font-size: $font-size-medium; } @mixin cn-regular-18 { font-family: $title-font; font-style: normal; font-weight: normal; - font-size: $font-size-medium-mobile; + font-size: $font-size-smedium; } @mixin cn-bold-18 { font-family: $title-font; font-style: normal; font-weight: bold; - font-size: $font-size-medium-mobile; + font-size: $font-size-smedium; } @mixin cn-bold-16 { font-family: $text-font; @@ -184,3 +141,22 @@ h6, white-space: nowrap; text-overflow: ellipsis; } + +@mixin btn-bold { + @include cn-bold-16; + line-height: 18px; +} + +@mixin btn-bold-underline { + @include btn-bold; + text-decoration: underline; +} +@mixin btn-normal { + @include cn-regular-16; + line-height: 19px; +} + +@mixin btn-pass { + @include cn-regular-14; + line-height: 21px; +} diff --git a/src/styles.scss b/src/styles.scss index b49b24295..09328f51f 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -26,7 +26,6 @@ body { min-height: 100vh; min-height: -webkit-fill-available; } - a { color: $default-link-color; text-decoration: none; @@ -127,6 +126,9 @@ button { padding-left: 16px; padding-right: 10px; @include btn-pass; + &.pass { + @include cn-regular-18; + } } .customCheck { display: inline-grid; -- GitLab From a9241a7959687acbe9dad12ac5a31716e0e99977 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 23 Feb 2021 18:26:24 +0100 Subject: [PATCH 56/72] fix: unexisting css class --- src/app/profile/profile.component.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss index c7b151d23..090368df6 100644 --- a/src/app/profile/profile.component.scss +++ b/src/app/profile/profile.component.scss @@ -32,7 +32,7 @@ background: $white; border-bottom: 1px solid $grey-4; .profileName { - @include cn-bold-24; + @include cn-bold-26; margin: 0 !important; } .profileEmail { @@ -69,7 +69,7 @@ button { a { margin: 0; &.structureName { - @include cn-bold-24; + @include cn-bold-26; color: $secondary-color; text-decoration: underline; } -- GitLab From 516446d600b197df71a8508ee8e722289e806fd3 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Tue, 23 Feb 2021 18:34:47 +0100 Subject: [PATCH 57/72] fix: remove structure delete for users from structure-details --- .../components/structure-details/structure-details.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 42eb58de7..1e12ab4d7 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 @@ -234,7 +234,7 @@ export class StructureDetailsComponent implements OnInit { }); } public canDelete(): boolean { - if (this.profileService.isAdmin() || this.profileService.isLinkedToStructure(this.structure._id)) { + if (this.profileService.isAdmin()) { return true; } return false; -- GitLab From a9bb6d5da584d0b31fd9b75613353f99e2b3cce5 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 24 Feb 2021 09:27:18 +0100 Subject: [PATCH 58/72] fix: move about page link + fix icon --- src/app/footer/footer.component.html | 1 - src/app/header/header.component.html | 1 + src/assets/ico/sprite.svg | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html index e043a1e44..6cc2485c7 100644 --- a/src/app/footer/footer.component.html +++ b/src/app/footer/footer.component.html @@ -3,7 +3,6 @@ <a class="clickable text-align-center" routerLink="/legal-notice" i18n>Mentions légales</a> <!-- <a class="clickable text-align-center" routerLink="/sitemap" i18n>Plan du site</a> --> <a class="clickable text-align-center" href="mailto:inclusionnumerique@grandlyon.com">Contact</a> - <a routerLink="/about" i18n>Qui sommes-nous ?</a> </div> <a class="metro-link" diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index c26ce85cc..2d4732e1f 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -10,6 +10,7 @@ </div> <div fxLayout="row" class="right-header" fxLayoutAlign="center center" fxLayoutGap="3vw"> <a routerLink="/home" [routerLinkActive]="'active'" i18n>Les acteurs</a> + <a routerLink="/about" [routerLinkActive]="'active'" i18n>Qui sommes-nous ?</a> <!-- <a routerLink="/news" [routerLinkActive]="'active'" i18n>Actualités</a> --> <!-- <a routerLink="/resources" [routerLinkActive]="'active'" i18n>Ressources</a> --> <a *ngIf="isAdmin" routerLink="/admin" [routerLinkActive]="'active'">Administration</a> diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 748235c0a..83c868b1b 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -78,7 +78,7 @@ </symbol> <symbol id ="email" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4H19.5C20.3284 4 21 4.67157 21 5.5V16.0714C21 16.8998 20.3284 17.5714 19.5 17.5714H3.5C2.67157 17.5714 2 16.8998 2 16.0714V5.5C2 4.67157 2.67157 4 3.5 4ZM2.91716 6.02444C3.04832 5.78143 3.35163 5.69075 3.59464 5.8219L11.2431 9.94966C11.5474 10.1138 11.9148 10.1093 12.2149 9.93753L19.3945 5.82797C19.6341 5.69079 19.9396 5.77387 20.0768 6.01353C20.214 6.25318 20.1309 6.55867 19.8913 6.69585L12.7116 10.8054C12.1116 11.1489 11.3767 11.1581 10.7682 10.8297L3.11971 6.70192C2.8767 6.57077 2.78602 6.26745 2.91716 6.02444Z" stroke="none"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4H19.5C20.3284 4 21 4.67157 21 5.5V16.0714C21 16.8998 20.3284 17.5714 19.5 17.5714H3.5C2.67157 17.5714 2 16.8998 2 16.0714V5.5C2 4.67157 2.67157 4 3.5 4ZM2.91716 6.02444C3.04832 5.78143 3.35163 5.69075 3.59464 5.8219L11.2431 9.94966C11.5474 10.1138 11.9148 10.1093 12.2149 9.93753L19.3945 5.82797C19.6341 5.69079 19.9396 5.77387 20.0768 6.01353C20.214 6.25318 20.1309 6.55867 19.8913 6.69585L12.7116 10.8054C12.1116 11.1489 11.3767 11.1581 10.7682 10.8297L3.11971 6.70192C2.8767 6.57077 2.78602 6.26745 2.91716 6.02444Z" fill="#333333"/> </symbol> <symbol id="password" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> -- GitLab From 513b86c46df1451427c83fb976e310390fb6cd9c Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 24 Feb 2021 11:17:34 +0100 Subject: [PATCH 59/72] fix(svg) : change img to svg --- src/app/form/form.component.html | 390 +++++++++++------- src/app/form/form.component.scss | 31 +- .../hour-picker/hour-picker.component.html | 4 +- .../hour-picker/hour-picker.component.scss | 2 +- .../svg-icon/svg-icon.component.scss | 10 + 5 files changed, 267 insertions(+), 170 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 5667595b7..0c4ec892d 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -110,24 +110,36 @@ <label for="surname">Nom</label> <div fxLayout="row" fxLayoutGap="13px"> <input type="text" (input)="setValidationsForm()" formControlName="surname" class="form-input" /> - <img *ngIf="accountForm.get('surname').valid" src="../../assets/form/validate.svg" alt="logo valid" /> - <img + <app-svg-icon + *ngIf="accountForm.get('surname').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('surname').invalid && accountForm.get('surname').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> <div class="form-group" fxLayout="column"> <label for="name">Prénom</label> <div fxLayout="row" fxLayoutGap="13px"> <input type="text" (input)="setValidationsForm()" formControlName="name" class="form-input" /> - <img *ngIf="accountForm.get('name').valid" src="../../assets/form/validate.svg" alt="logo valid" /> - <img + <app-svg-icon + *ngIf="accountForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('name').invalid && accountForm.get('name').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> <div class="form-group" fxLayout="column"> @@ -139,12 +151,18 @@ class="form-input phone" (input)="modifyPhoneInput(accountForm, 'phone', $event.target.value)" /> - <img *ngIf="accountForm.get('phone').valid" src="../../assets/form/validate.svg" alt="logo valid" /> - <img + <app-svg-icon + *ngIf="accountForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('phone').invalid && accountForm.get('phone').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> </div> @@ -167,12 +185,18 @@ [readonly]="isAccountMode" [ngClass]="{ disabled: isAccountMode }" /> - <img *ngIf="accountForm.get('email').valid" src="../../assets/form/validate.svg" alt="logo valid" /> - <img + <app-svg-icon + *ngIf="accountForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('email').invalid && accountForm.get('email').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> <div class="form-group" fxLayout="column"> @@ -192,18 +216,25 @@ (input)="setValidationsForm()" autocomplete="on" /> - <img + <app-svg-icon + [iconClass]="'validation grey hover'" + [type]="'form'" + [icon]="'eyePassword'" (click)="showPassword()" - class="eyePassword" - src="../../assets/form/eyePassword.svg" - alt="logo eyePassword" - /> - <img *ngIf="accountForm.get('password').valid" src="../../assets/form/validate.svg" alt="logo valid" /> - <img + ></app-svg-icon> + + <app-svg-icon + *ngIf="accountForm.get('password').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('password').invalid && accountForm.get('password').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> <div class="form-group" fxLayout="column"> @@ -216,22 +247,24 @@ (input)="setValidationsForm()" autocomplete="on" /> - <img + <app-svg-icon + [iconClass]="'validation grey hover'" + [type]="'form'" + [icon]="'eyePassword'" (click)="showConfirmPassword()" - class="eyePassword" - src="../../assets/form/eyePassword.svg" - alt="logo eyePassword" - /> - <img + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('confirmPassword').valid && accountForm.get('confirmPassword').value" - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="accountForm.get('confirmPassword').invalid && accountForm.get('confirmPassword').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> </div> @@ -250,23 +283,30 @@ formControlName="structureName" class="form-input structureName" /> - <img + <app-svg-icon *ngIf="getStructureControl('structureName').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> </div> </div> <div class="form-group" fxLayout="column"> <label for="address">Adresse de la structure</label> - <div fxLayout="row" fxLayoutGap="13px"> + <div class="addressRow" fxLayout="row" fxLayoutGap="13px"> <app-address-autocomplete [address]="getStructureControl('address').valid ? getStructureControl('address').value : null" (inputAddress)="setAddressStructure()" (selectedAddress)="setAddressStructure($event)" ></app-address-autocomplete> - <img *ngIf="getStructureControl('address').valid" src="../../assets/form/validate.svg" alt="logo valid" /> + <app-svg-icon + *ngIf="getStructureControl('address').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validateIcon" + ></app-svg-icon> </div> </div> </div> @@ -283,16 +323,18 @@ class="form-input" (input)="modifyPhoneInput(structureForm, 'contactPhone', $event.target.value)" /> - <img + <app-svg-icon *ngIf="getStructureControl('contactPhone').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('contactPhone').invalid && getStructureControl('contactPhone').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> </div> @@ -377,17 +419,18 @@ <label for="structureName">Courriel de la structure</label> <div fxLayout="row" fxLayoutGap="13px"> <input type="text" (input)="setValidationsForm()" formControlName="contactMail" class="form-input" /> - <img + <app-svg-icon *ngIf="getStructureControl('contactMail').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - /> - - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('contactMail').invalid && getStructureControl('contactMail').value" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> </div> </div> <div class="collapse" [ngClass]="{ notCollapsed: !showWebsite }"> @@ -404,7 +447,7 @@ </div> </div> <div *ngIf="showWebsite" class="inputSection"> - <div class="form-group" fxLayout="column"> + <div class="form-group website" fxLayout="column"> <label for="website">Adresse du site web</label> <div fxLayout="row" fxLayoutGap="27px"> <input @@ -414,16 +457,20 @@ formControlName="website" class="form-input" /> - <img + <app-svg-icon *ngIf="getStructureControl('website').valid && getStructureControl('website').value" - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('website').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </div> </div> </div> @@ -455,20 +502,24 @@ formControlName="facebook" class="form-input withIcon" /> - <img + <app-svg-icon *ngIf=" getStructureControl('facebook').valid && getStructureControl('facebook').value != null && getStructureControl('facebook').value != '' " - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('facebook').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </div> </div> <div class="form-group twitter"> @@ -483,20 +534,24 @@ formControlName="twitter" class="form-input withIcon" /> - <img + <app-svg-icon *ngIf=" getStructureControl('twitter').valid && getStructureControl('twitter').value != null && getStructureControl('twitter').value != '' " - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('twitter').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </div> </div> <div class="form-group instagram"> @@ -511,20 +566,25 @@ placeholder="instagram.com/resonum" class="form-input withIcon" /> - <img + + <app-svg-icon *ngIf=" getStructureControl('instagram').valid && getStructureControl('instagram').value != null && getStructureControl('instagram').value != '' " - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('instagram').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </div> </div> <div class="form-group linkedin"> @@ -539,20 +599,24 @@ placeholder="linkedin.com/in/resonum" class="form-input withIcon" /> - <img + <app-svg-icon *ngIf=" getStructureControl('linkedin').valid && getStructureControl('linkedin').value != null && getStructureControl('linkedin').value != '' " - src="../../assets/form/validate.svg" - alt="logo valid" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('linkedin').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </div> </div> </div> @@ -774,7 +838,7 @@ </div> </div> <div *ngIf="equipment.openned" class="inputSection"> - <div class="form-group" fxLayout="column"> + <div class="form-group equipments" fxLayout="column"> <label for="equipment">Nombre</label> <div fxLayout="row" fxLayoutAlign=" center" fxLayoutGap="27px"> <ng-container *ngIf="equipment.module.id == 'ordinateurs'"> @@ -784,18 +848,20 @@ formControlName="nbComputers" class="form-input nbEquipment" /> - <img + <app-svg-icon *ngIf="getStructureControl('nbComputers').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - class="nbEquipment" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('nbComputers').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo valid" - class="nbEquipment" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </ng-container> <ng-container *ngIf="equipment.module.id == 'tablettes'"> <input @@ -804,18 +870,20 @@ formControlName="nbTablets" class="form-input nbEquipment" /> - <img + <app-svg-icon *ngIf="getStructureControl('nbTablets').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - class="nbEquipment" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('nbTablets').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - class="nbEquipment" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </ng-container> <ng-container *ngIf="equipment.module.id == 'imprimantes'"> <input @@ -824,18 +892,20 @@ formControlName="nbPrinters" class="form-input nbEquipment" /> - <img + <app-svg-icon *ngIf="getStructureControl('nbPrinters').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - class="nbEquipment" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('nbPrinters').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - class="nbEquipment" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </ng-container> <ng-container *ngIf="equipment.module.id == 'bornesNumeriques'"> <input @@ -844,18 +914,20 @@ formControlName="nbNumericTerminal" class="form-input nbEquipment" /> - <img + <app-svg-icon *ngIf="getStructureControl('nbNumericTerminal').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - class="nbEquipment" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('nbNumericTerminal').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo invalid" - class="nbEquipment" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </ng-container> <ng-container *ngIf="equipment.module.id == 'scanners'"> <input @@ -864,18 +936,20 @@ formControlName="nbScanners" class="form-input nbEquipment" /> - <img + <app-svg-icon *ngIf="getStructureControl('nbScanners').valid" - src="../../assets/form/validate.svg" - alt="logo valid" - class="nbEquipment" - /> - <img + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + class="validationIcon" + ></app-svg-icon> + <app-svg-icon *ngIf="getStructureControl('nbScanners').invalid" - src="../../assets/form/notvalidate.svg" - alt="logo valid" - class="nbEquipment" - /> + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + class="validationIcon" + ></app-svg-icon> </ng-container> </div> </div> diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 01975ebbf..898d651ad 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -267,6 +267,28 @@ h3 { label { color: $grey-2; } + &.facebook, + &.twitter, + &.instagram, + &.linkedin, + &.website, + &.equipments { + div { + height: 52px; + .validationIcon { + margin-right: -53px; + } + } + } + div { + height: 44px; + } + .addressRow { + height: auto; + .validateIcon { + height: 38px !important; + } + } } input { margin-top: 4px; @@ -283,7 +305,6 @@ input { img { max-height: 340px; &.eyePassword { - padding: 0 2.5px; // Align to email input cursor: pointer; &:hover { opacity: 0.8; @@ -366,14 +387,6 @@ img { } } padding: 0px 15px 19px 12px; - img { - margin-top: 8px; - //padding-left: 41px; - margin-right: -54px; - &.nbEquipment { - padding-left: 214px; - } - } svg { margin-top: 8px; width: 22px; diff --git a/src/app/shared/components/hour-picker/hour-picker.component.html b/src/app/shared/components/hour-picker/hour-picker.component.html index ff785893b..d8eb0f6ae 100644 --- a/src/app/shared/components/hour-picker/hour-picker.component.html +++ b/src/app/shared/components/hour-picker/hour-picker.component.html @@ -62,10 +62,10 @@ <div> <div *ngIf="hour.error === 'wrong' || hour.error === 'incomplete'" class="error-message"> - <app-svg-icon [type]="'ico'" [icon]="'nok'"></app-svg-icon> + <app-svg-icon [iconClass]="'icon-32'" [type]="'ico'" [icon]="'nok'"></app-svg-icon> </div> <div *ngIf="hour.error === null" class="error-message"> - <app-svg-icon [type]="'ico'" [icon]="'ok'"></app-svg-icon> + <app-svg-icon [iconClass]="'icon-32'" [type]="'ico'" [icon]="'ok'"></app-svg-icon> </div> </div> </div> diff --git a/src/app/shared/components/hour-picker/hour-picker.component.scss b/src/app/shared/components/hour-picker/hour-picker.component.scss index b10e64ec2..64584da1b 100644 --- a/src/app/shared/components/hour-picker/hour-picker.component.scss +++ b/src/app/shared/components/hour-picker/hour-picker.component.scss @@ -46,7 +46,7 @@ height: 40px; display: grid; // grid-template-columns: auto 70px auto 70px 30px 80px 1fr; - grid-template-columns: auto 52px auto 61px 30px; + grid-template-columns: auto 52px auto 61px 30px 0px; column-gap: 10px; align-items: center; justify-items: center; diff --git a/src/app/shared/components/svg-icon/svg-icon.component.scss b/src/app/shared/components/svg-icon/svg-icon.component.scss index 30dac9094..c17749f5b 100644 --- a/src/app/shared/components/svg-icon/svg-icon.component.scss +++ b/src/app/shared/components/svg-icon/svg-icon.component.scss @@ -9,6 +9,16 @@ &.icon-75 { width: 4.688em; } + &.validation { + height: 100%; + width: 26px; + } + &.hover { + cursor: pointer; + &:hover { + opacity: 0.8; + } + } &.grey { fill: $grey-3; stroke: $grey-3; -- GitLab From 7031971082fae414423abdecc701789f42a87fda Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 24 Feb 2021 11:31:02 +0100 Subject: [PATCH 60/72] feat: handle sign-in with form --- src/app/header/header.component.html | 2 +- src/app/profile/profile.component.html | 11 ----------- .../components/signup-modal/signup-modal.component.ts | 3 ++- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index 2d4732e1f..ee833e54b 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -46,7 +46,7 @@ </div> <app-signup-modal *ngIf="displaySignUp" [openned]="isPopUpOpen" (closed)="closeSignUpModal($event)"></app-signup-modal> -<app-signin-modal *ngIf="!displaySignUp" [openned]="isPopUpOpen" (closed)="closeSignInModal()"></app-signin-modal> +<!-- <app-signin-modal *ngIf="!displaySignUp" [openned]="isPopUpOpen" (closed)="closeSignInModal()"></app-signin-modal> --> <ng-template #customTitle> <img class="desktop-show logo-grand-lyon" src="/assets/logos/resin.svg" alt /> diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index 96e8c9734..d0a27a13b 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -52,15 +52,4 @@ </div> </div> </div> - - <div> - <div *ngIf="userProfile" fxLayout="column" fxLayoutAlign="center" fxLayoutGap="10px"> - <p fxLayout="column" *ngIf="userProfile.pendingStructuresLink.length > 0"> - Mes structures en attente de validation: - <span *ngFor="let structureId of userProfile.pendingStructuresLink"> - <strong>{{ structureId }}</strong> - </span> - </p> - </div> - </div> </div> diff --git a/src/app/shared/components/signup-modal/signup-modal.component.ts b/src/app/shared/components/signup-modal/signup-modal.component.ts index 2c13f60c1..128646d1b 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.ts +++ b/src/app/shared/components/signup-modal/signup-modal.component.ts @@ -46,7 +46,8 @@ export class SignUpModalComponent implements OnInit { } public sendSwitchToSignIn(): void { - this.closed.emit(false); + this.closed.emit(true); + this.router.navigate(['/create-structure']); } public swithToResetPassword(): void { -- GitLab From 85d3f1c4f2024d3446655cc9f7c928c25bb5a696 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Wed, 24 Feb 2021 11:40:41 +0100 Subject: [PATCH 61/72] fix(form) : fix freeWifi bug --- src/app/form/form.component.html | 8 +++++++- src/app/form/form.component.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 9baca98e4..9b76a819f 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -807,7 +807,13 @@ <h3>Proposez-vous le wifi en accès libre ?</h3> </div> <app-radio-form - [selectedOption]="isEditMode ? isInArray('wifiEnAccesLibre', 'equipmentsAndServices') : null" + [selectedOption]=" + isEditMode + ? isInArray('wifiEnAccesLibre', 'equipmentsAndServices') + : isWifiChoosen + ? isInArray('wifiEnAccesLibre', 'equipmentsAndServices') + : null + " (selectedEvent)="onCheckChange($event, 'equipmentsAndServices', 'wifiEnAccesLibre')" > </app-radio-form> diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index dcc151211..769104607 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -71,7 +71,7 @@ export class FormComponent implements OnInit { public isAccountMode = false; public isJoinMode = false; public isLoading = false; - public isWifiChoosen = false; + public isWifiChoosen = null; public structureWithOwners: StructureWithOwners; constructor( -- GitLab From de230889cce920786d9d48f56084c23c012d4703 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 24 Feb 2021 16:51:02 +0100 Subject: [PATCH 62/72] fix: wording for form / profile and outdated page --- nginx/outdated.html | 6 +++--- src/app/form/form.component.html | 14 ++++++++++---- src/app/profile/profile.component.html | 2 +- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/nginx/outdated.html b/nginx/outdated.html index 9403101b5..24987fe33 100644 --- a/nginx/outdated.html +++ b/nginx/outdated.html @@ -2,14 +2,14 @@ <html lang="fr"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> - <title>RESIN: Navigateur incompatible</title> + <title>RES'in: Navigateur incompatible</title> </head> <body> <div> - <h1>Navigateur incompatible</h1> + <h1>Navigateur obsolète</h1> <p> - Votre navigateur ne vous permet pas d'accéder à RESIN dans de bonnes conditions. Merci d'utiliser + Votre navigateur ne vous permet pas d'accéder à RES'in dans de bonnes conditions. Merci d'utiliser <a href="https://www.mozilla.org/fr/firefox/new/">Firefox</a>, <a href="https://www.google.com/intl/fr/chrome/">Chrome</a>, ou la dernière version d'<a href="https://www.microsoft.com/fr-fr/edge" diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index dbfe52fe2..b88d6be47 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -48,7 +48,7 @@ <p>Une fois réalisé cela vous permettra d'être référencé sur la platefome</p> </div> <div class="btnStart"> - <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> + <button class="btn-primary start" (click)="nextPage()">C'est parti</button> </div> </div> <div @@ -65,7 +65,7 @@ <p>Une fois réalisé cela vous permettra de devenir propriétaire de cette structure</p> </div> <div class="btnStart"> - <button class="btn-primary start" (click)="nextPage()">C'est Parti</button> + <button class="btn-primary start" (click)="nextPage()">C'est parti</button> </div> </div> <div @@ -994,7 +994,13 @@ démarches en ligne dans la Métropole de Lyon </p> </div> - <div *ngIf="currentPage == nbPagesForm && !profile" class="page" fxLayout="column" fxLayoutGap="69px"> + <div + *ngIf="currentPage == nbPagesForm && !profile" + class="page" + fxLayout="column" + fxLayoutAlign="center center" + fxLayoutGap="69px" + > <svg aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#emailVerification'"></use> </svg> @@ -1008,7 +1014,7 @@ </h3> </div> - <div class="structureInfoBlock" fxLayout="row" fxLayoutAlign=" center"> + <div class="structureInfoBlock" fxLayout="row" fxLayoutAlign="center"> <div class="structureInfoContent" fxLayout="column"> {{ getStructureControl('structureName').value }} <span>{{ getStructureControl('structureType').value }}</span> diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index d0a27a13b..4a5e03d17 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -6,7 +6,7 @@ </svg> <div class="profileInformation" fxLayoutGap="18px" fxLayout="column"> <div fxLayout="row" fxLayoutAlign="space-between center"> - <p class="profileName">{{ userProfile.surname | titlecase }} {{ userProfile.name | titlecase }}</p> + <p class="profileName">{{ userProfile.name | titlecase }} {{ userProfile.surname | titlecase }}</p> <button class="btn-primary" (click)="logout()">Déconnexion</button> </div> <div class="profileEmail" fxLayout="column"> -- GitLab From 6f32ee8659c17790b762cafd3c2f34263e2cf522 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Wed, 24 Feb 2021 18:53:37 +0100 Subject: [PATCH 63/72] fix: add admin structureName + bug fix on sturcutredetails --- .../claim-structure/claim-structure.component.html | 2 +- src/app/admin/models/demandAttachment.model.ts | 1 + .../structure-details/structure-details.component.html | 9 +++++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app/admin/components/claim-structure/claim-structure.component.html b/src/app/admin/components/claim-structure/claim-structure.component.html index b272cebed..aa0adbc58 100644 --- a/src/app/admin/components/claim-structure/claim-structure.component.html +++ b/src/app/admin/components/claim-structure/claim-structure.component.html @@ -8,7 +8,7 @@ <tbody> <tr *ngFor="let demand of demandsAttachment"> <td>{{ demand.userEmail }}</td> - <td>{{ demand.structureId }}</td> + <td>{{ demand.structureName }}</td> <td> <button (click)="acceptDemand(demand)">Valider</button><button (click)="refuseDemand(demand)">Refuser</button> </td> diff --git a/src/app/admin/models/demandAttachment.model.ts b/src/app/admin/models/demandAttachment.model.ts index b19d1f375..f7b9ae4b4 100644 --- a/src/app/admin/models/demandAttachment.model.ts +++ b/src/app/admin/models/demandAttachment.model.ts @@ -1,4 +1,5 @@ export class DemandAttachment { userEmail: string; structureId: number; + structureName: string; } 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 803f77bdf..f0393fbff 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 @@ -102,7 +102,12 @@ </div> </div> <div fxLayout="column" fxFlex="50%"> - <div *ngIf="structure.contactMail" fxLayout="row" fxLayoutAlign="none center" fxLayoutGap="13px"> + <div + *ngIf="structure.contactMail && structure.contactMail !== 'unknown@unknown.com'" + fxLayout="row" + fxLayoutAlign="none center" + fxLayoutGap="13px" + > <app-svg-icon [type]="'ico'" [icon]="'email'"></app-svg-icon> <p>{{ structure.contactMail }}</p> </div> @@ -186,7 +191,7 @@ > <p class="no-margin">{{ getAccessLabel(acces) }}</p> </div> - <p class="no-margin">Accessibles aux personnes à mobilité réduite</p> + <p *ngIf="structure.pmrAccess" class="no-margin">Accessibles aux personnes à mobilité réduite</p> </div> <div *ngFor="let public of structure.publics" fxLayout="row" fxLayoutAlign="none flex-end" fxLayoutGap="8px"> <p class="no-margin">{{ getPublicLabel(public) }}</p> -- GitLab From ce914c8e917610d4a5812dee6fdc4a607e4cf763 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 25 Feb 2021 10:58:15 +0100 Subject: [PATCH 64/72] feat: add form widget for claim and register --- src/app/profile/profile.component.scss | 27 +------------ .../user-verification.component.html | 32 ++++++++------- .../user-verification.component.scss | 39 +++++++------------ src/styles.scss | 27 +++++++++++++ 4 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss index 50f831bd0..f2547348e 100644 --- a/src/app/profile/profile.component.scss +++ b/src/app/profile/profile.component.scss @@ -48,33 +48,8 @@ } .structureSection { margin-bottom: 108px; - .structureCard { - border: 1px solid $grey-4; - border-radius: 6px; - .structureInfo { - border-radius: 6px; - background: $white; - min-height: 100px; - padding: 33px 55px; - @media #{$large-phone} { - padding: 33px 25px; - } - a { - margin: 0; - &.structureName { - @include cn-bold-26; - color: $secondary-color; - text-decoration: underline; - } - } - .ownerName { - @include cn-regular-18; - color: $grey-2; - } - } - @include background-hash($grey-2); - } } + .addSection { button { background: $red-default; diff --git a/src/app/user-verification/user-verification.component.html b/src/app/user-verification/user-verification.component.html index ed99a6f36..628bb8efe 100644 --- a/src/app/user-verification/user-verification.component.html +++ b/src/app/user-verification/user-verification.component.html @@ -11,25 +11,31 @@ Votre compte a bien été créé. </h3> </div> - <div *ngIf="structure" class="structureInfoBlock" fxLayout="row" fxLayoutAlign=" center"> - <div class="structureInfoContent" fxLayout="column"> - {{ structure.structureName }} - <span>{{ structure.getLabelTypeStructure() }}</span> - </div> - <div class="validateSvg"> - <svg class="validate" aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#checkVector'"></use> - </svg> + <div *ngIf="structure" class="structureCard"> + <div class="structureInfo" fxLayout="column" fxLayoutGap="14px"> + <div fxLayout="row" fxLayoutAlign="space-between start" fxLayoutGap="20px"> + <a class="structureName" routerLink="/home" [state]="{ data: structure }">{{ structure.structureName }}</a> + </div> </div> </div> </div> <p *ngIf="verificationIssue"> Une erreur est survenue lors de la validation de votre email... Veuillez envoyer un mail au support. </p> - <div - class="typeform-widget test" - data-url="https://form.typeform.com/to/m7DV3CdW?typeform-medium=embed-snippet" - ></div> + <!-- TypeForm --> + <div *ngIf="!verificationIssue"> + <iframe + *ngIf="structure" + class="typeform-widget custom-form" + title="typeform" + src="https://form.typeform.com/to/m7DV3CdW?typeform-medium=embed-snippet" + ></iframe> + <div + *ngIf="!structure" + class="typeform-widget custom-form" + data-url="https://form.typeform.com/to/ASJH3B7Z?typeform-medium=embed-snippet" + ></div> + </div> <div class="btnSection" fxLayout="row" fxLayoutAlign="space-around center"> <button *ngIf="structure && verificationSuccess" class="btn" routerLink="/home" [state]="{ data: structure }"> Voir ma structure diff --git a/src/app/user-verification/user-verification.component.scss b/src/app/user-verification/user-verification.component.scss index b6e482712..8fb73ba43 100644 --- a/src/app/user-verification/user-verification.component.scss +++ b/src/app/user-verification/user-verification.component.scss @@ -7,28 +7,7 @@ padding: 18px; margin: auto; } -.structureInfoBlock { - background: $green-1; - color: $white; - padding: 16px; - border-radius: 6px; - @include cn-bold-18; - .structureInfoContent { - width: 100%; - } - span { - font-style: italic; - @include cn-regular-14; - } - .validateSvg { - stroke: $white; - text-align: right; - svg { - height: 14px; - width: 14px; - } - } -} + .btn { background: $secondary-color; border-radius: 4px; @@ -58,8 +37,18 @@ } } -.test { +.custom-form { width: 100%; - height: 300px; - transform: scale(0.75); + height: 480px; + border: none; +} + +// Override button style to be the same as typeform button +button { + margin: 0px; + max-width: 100%; + font-size: 24px !important; + line-height: 32px !important; + min-height: 48px; + width: 245px !important; } diff --git a/src/styles.scss b/src/styles.scss index 966c57e67..24a7585c8 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -239,3 +239,30 @@ button { transform: translate(-50%, -50%); } } + +.structureCard { + border: 1px solid $grey-4; + border-radius: 6px; + .structureInfo { + border-radius: 6px; + background: $white; + min-height: 100px; + padding: 33px 55px; + @media #{$large-phone} { + padding: 33px 25px; + } + a { + margin: 0; + &.structureName { + @include cn-bold-26; + color: $secondary-color; + text-decoration: underline; + } + } + .ownerName { + @include cn-regular-18; + color: $grey-2; + } + } + @include background-hash($grey-2); +} -- GitLab From 48aa30c00068fc110bcbeefabde6b9ebb207c832 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Thu, 25 Feb 2021 11:56:24 +0100 Subject: [PATCH 65/72] fix(navigation) : add enter key on form --- src/app/form/footer-form/footer-form.component.html | 1 + src/app/form/footer-form/footer-form.component.scss | 2 +- src/app/form/form.component.html | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/form/footer-form/footer-form.component.html b/src/app/form/footer-form/footer-form.component.html index c2e91330f..a2d4f21ff 100644 --- a/src/app/form/footer-form/footer-form.component.html +++ b/src/app/form/footer-form/footer-form.component.html @@ -11,6 +11,7 @@ class="btn-primary small next" (click)="goToNextPage()" [disabled]="!isValid" + type="submit" [ngClass]="{ invalid: !isValid }" > <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> diff --git a/src/app/form/footer-form/footer-form.component.scss b/src/app/form/footer-form/footer-form.component.scss index 31dbd2f3b..6ebf95c44 100644 --- a/src/app/form/footer-form/footer-form.component.scss +++ b/src/app/form/footer-form/footer-form.component.scss @@ -25,6 +25,6 @@ .chevronRight { height: 24px; width: 24px; - stroke: $white; + stroke: inherit; margin-left: 10px; } diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index b88d6be47..42e688ad1 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -105,7 +105,7 @@ </ul> </div> </div> - <form [formGroup]="accountForm" *ngIf="accountForm && !profile"> + <form [formGroup]="accountForm" *ngIf="accountForm && !profile" (keyup.enter)="isPageValid && !isEditMode? nextPage() : null"> <div *ngIf="currentPage == pageTypeEnum.accountInfo" class="page"> <div class="title"> <h3>Qui êtes-vous ?</h3> @@ -241,7 +241,7 @@ </div> </div> </form> - <form [formGroup]="structureForm" *ngIf="structureForm"> + <form [formGroup]="structureForm" *ngIf="structureForm" (keyup.enter)="isPageValid && !isEditMode? nextPage() : null"> <div *ngIf="currentPage == pageTypeEnum.structureNameAndAddress" class="page"> <div class="title"> <h3>Quelle structure voulez-vous réferencer ?</h3> -- GitLab From 0916fca1547832910516a7d2ae0b45529a29d979 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Thu, 25 Feb 2021 11:56:42 +0100 Subject: [PATCH 66/72] fix(navigation) : add enter key on signup --- .../components/signup-modal/signup-modal.component.html | 6 +++--- .../components/signup-modal/signup-modal.component.scss | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/app/shared/components/signup-modal/signup-modal.component.html b/src/app/shared/components/signup-modal/signup-modal.component.html index 91900cf36..492b39e29 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.html +++ b/src/app/shared/components/signup-modal/signup-modal.component.html @@ -39,13 +39,13 @@ <use [attr.xlink:href]="'assets/form/sprite.svg#notValidate'"></use> </svg> </div> - <div class="invalid" *ngIf="authFailed">Identifiant ou mot de passe invalide</div> + <div class="incorrectId" *ngIf="authFailed">Identifiant ou mot de passe invalide</div> </div> <div class="footerModal" fxLayout="row" fxLayoutAlign="space-around center"> - <button class="btn confirm" (click)="swithToResetPassword()">Mot de passe oublié</button> + <button type="button" class="btn confirm" (click)="swithToResetPassword()">Mot de passe oublié</button> <button type="submit" - class="btn" + class="btn-primary" [disabled]="loginForm.invalid || loading" [ngClass]="{ invalid: loginForm.invalid || loading }" > diff --git a/src/app/shared/components/signup-modal/signup-modal.component.scss b/src/app/shared/components/signup-modal/signup-modal.component.scss index 2133bd8f4..ecf2f6b56 100644 --- a/src/app/shared/components/signup-modal/signup-modal.component.scss +++ b/src/app/shared/components/signup-modal/signup-modal.component.scss @@ -7,9 +7,11 @@ .ico-close { width: 100%; } -.invalid { + +.incorrectId { color: $orange-warning; } + h3 { @include cn-bold-26; color: $black; -- GitLab From bd67c0f48f01fcded21b48785e9ea5369c30e303 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Thu, 25 Feb 2021 11:57:00 +0100 Subject: [PATCH 67/72] fix(navigation) : fix focus style btn --- src/assets/scss/_buttons.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/assets/scss/_buttons.scss b/src/assets/scss/_buttons.scss index 3827c75d7..e17ec875c 100644 --- a/src/assets/scss/_buttons.scss +++ b/src/assets/scss/_buttons.scss @@ -73,10 +73,17 @@ height: 40px; width: 192px; @include btn-bold; + stroke: $white; &.small { width: 149px; } &.invalid { opacity: 0.4; } + &:focus { + background: $white; + color: $secondary-color; + border: 1px solid $secondary-color; + stroke: $secondary-color; + } } -- GitLab From 83ea88f74f4f1c7d5dd84be9671df4e05ed3a89c Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 25 Feb 2021 12:09:34 +0100 Subject: [PATCH 68/72] fix: icon color + alignements --- src/app/form/form.component.scss | 6 +++++- src/app/shared/components/svg-icon/svg-icon.component.scss | 4 ++++ .../structure-details/structure-details.component.html | 2 +- src/assets/ico/sprite.svg | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 6a6f572f7..ee642eea1 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -87,7 +87,7 @@ h3 { .content { .editHome { height: calc( - 100vh - #{$header-height} - #{$footer-height} - 81px - 1px - 55px + 100vh - #{$header-height} - #{$footer-height} - 81px - 1px - 55px - 24px ) !important; // -1px because of header border } @media #{$tablet} { @@ -512,9 +512,13 @@ img { .headerEditMode { max-width: 960px; margin: auto; + margin-top: 24px; h2 { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } + @media #{$tablet} { + margin-top: 0; + } } diff --git a/src/app/shared/components/svg-icon/svg-icon.component.scss b/src/app/shared/components/svg-icon/svg-icon.component.scss index 30dac9094..b6117f5ca 100644 --- a/src/app/shared/components/svg-icon/svg-icon.component.scss +++ b/src/app/shared/components/svg-icon/svg-icon.component.scss @@ -13,6 +13,10 @@ fill: $grey-3; stroke: $grey-3; } + &.grey-1 { + fill: $grey-1; + stroke: $grey-1; + } } svg { 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 f0393fbff..9eda0b470 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 @@ -108,7 +108,7 @@ fxLayoutAlign="none center" fxLayoutGap="13px" > - <app-svg-icon [type]="'ico'" [icon]="'email'"></app-svg-icon> + <app-svg-icon [type]="'ico'" [iconClass]="'grey-1'" [icon]="'email'"></app-svg-icon> <p>{{ structure.contactMail }}</p> </div> <div *ngIf="structure.hasPassNumeric()" fxLayout="row" fxLayoutAlign="none center" fxLayoutGap="13px"> diff --git a/src/assets/ico/sprite.svg b/src/assets/ico/sprite.svg index 83c868b1b..748235c0a 100644 --- a/src/assets/ico/sprite.svg +++ b/src/assets/ico/sprite.svg @@ -78,7 +78,7 @@ </symbol> <symbol id ="email" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> -<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4H19.5C20.3284 4 21 4.67157 21 5.5V16.0714C21 16.8998 20.3284 17.5714 19.5 17.5714H3.5C2.67157 17.5714 2 16.8998 2 16.0714V5.5C2 4.67157 2.67157 4 3.5 4ZM2.91716 6.02444C3.04832 5.78143 3.35163 5.69075 3.59464 5.8219L11.2431 9.94966C11.5474 10.1138 11.9148 10.1093 12.2149 9.93753L19.3945 5.82797C19.6341 5.69079 19.9396 5.77387 20.0768 6.01353C20.214 6.25318 20.1309 6.55867 19.8913 6.69585L12.7116 10.8054C12.1116 11.1489 11.3767 11.1581 10.7682 10.8297L3.11971 6.70192C2.8767 6.57077 2.78602 6.26745 2.91716 6.02444Z" fill="#333333"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 4H19.5C20.3284 4 21 4.67157 21 5.5V16.0714C21 16.8998 20.3284 17.5714 19.5 17.5714H3.5C2.67157 17.5714 2 16.8998 2 16.0714V5.5C2 4.67157 2.67157 4 3.5 4ZM2.91716 6.02444C3.04832 5.78143 3.35163 5.69075 3.59464 5.8219L11.2431 9.94966C11.5474 10.1138 11.9148 10.1093 12.2149 9.93753L19.3945 5.82797C19.6341 5.69079 19.9396 5.77387 20.0768 6.01353C20.214 6.25318 20.1309 6.55867 19.8913 6.69585L12.7116 10.8054C12.1116 11.1489 11.3767 11.1581 10.7682 10.8297L3.11971 6.70192C2.8767 6.57077 2.78602 6.26745 2.91716 6.02444Z" stroke="none"/> </symbol> <symbol id="password" viewBox="0 0 22 22" xmlns="http://www.w3.org/2000/svg"> -- GitLab From 3649623903f8ee27ebce50fdf04e3db3912ddd74 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Thu, 25 Feb 2021 16:09:30 +0100 Subject: [PATCH 69/72] fix(form) : add previous btn to go to previous url --- src/app/app.component.ts | 4 ++- .../footer-form/footer-form.component.html | 5 ++-- .../form/footer-form/footer-form.component.ts | 1 + src/app/form/form.component.html | 22 +++++++++++---- src/app/form/form.component.scss | 1 + src/app/form/form.component.ts | 18 ++++++++++-- src/app/services/routerListener.service.ts | 28 +++++++++++++++++++ 7 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 src/app/services/routerListener.service.ts diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 9b9e30d8d..13fba3cf1 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,6 +1,7 @@ import { Component } from '@angular/core'; import { ProfileService } from './profile/services/profile.service'; import { AuthService } from './services/auth.service'; +import { RouterListenerService } from './services/routerListener.service'; import { PrintService } from './shared/service/print.service'; @Component({ @@ -14,7 +15,8 @@ export class AppComponent { constructor( public printService: PrintService, private authService: AuthService, - private profilService: ProfileService + private profilService: ProfileService, + private routerListenerService: RouterListenerService ) { if (this.authService.isLoggedIn()) { this.profilService.getProfile(); diff --git a/src/app/form/footer-form/footer-form.component.html b/src/app/form/footer-form/footer-form.component.html index c2e91330f..0e5e0f1ee 100644 --- a/src/app/form/footer-form/footer-form.component.html +++ b/src/app/form/footer-form/footer-form.component.html @@ -4,7 +4,7 @@ <svg class="chevronLeft" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronLeft'"></use> </svg> - Précédent + {{ btnName[0] }} </div> </button> <button @@ -14,7 +14,8 @@ [ngClass]="{ invalid: !isValid }" > <div class="rowBtn" fxLayout="row" fxLayoutAlign="center center"> - Suivant<svg class="chevronRight" aria-hidden="true"> + {{ btnName[1] + }}<svg class="chevronRight" aria-hidden="true"> <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> </svg> </div> diff --git a/src/app/form/footer-form/footer-form.component.ts b/src/app/form/footer-form/footer-form.component.ts index e5e6fbfaf..15a5456ff 100644 --- a/src/app/form/footer-form/footer-form.component.ts +++ b/src/app/form/footer-form/footer-form.component.ts @@ -7,6 +7,7 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; }) export class FooterFormComponent implements OnInit { @Input() isValid: boolean; + @Input() btnName: string[]; @Output() nextPage = new EventEmitter<any>(); @Output() previousPage = new EventEmitter<any>(); constructor() {} diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index cff729715..4d9393827 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -47,9 +47,13 @@ <p class="titleDesc">Cela va prendre une quinzaine de minutes</p> <p>Une fois réalisé cela vous permettra d'être référencé sur la platefome</p> </div> - <div class="btnStart"> - <button class="btn-primary start" (click)="nextPage()">C'est parti</button> - </div> + <app-footer-form + class="btnStart" + [btnName]="['Précédent', 'C\'est parti']" + (previousPage)="previousUrl()" + (nextPage)="nextPage()" + [isValid]="isPageValid" + ></app-footer-form> </div> <div *ngIf="displayClaimStructure()" @@ -64,9 +68,13 @@ <div> <p>Une fois réalisé cela vous permettra de devenir propriétaire de cette structure</p> </div> - <div class="btnStart"> - <button class="btn-primary start" (click)="nextPage()">C'est parti</button> - </div> + <app-footer-form + class="btnStart" + [btnName]="['Précédent', 'C\'est parti']" + (previousPage)="previousUrl()" + (nextPage)="nextPage()" + [isValid]="isPageValid" + ></app-footer-form> </div> <div *ngIf="currentPage == pageTypeEnum.summary && isEditMode" @@ -1109,6 +1117,7 @@ <div *ngIf="currentPage != 0" class="footer desktop"> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form + [btnName]="['Précédent', 'Suivant']" (previousPage)="previousPage()" (nextPage)="nextPage()" [isValid]="isPageValid" @@ -1150,6 +1159,7 @@ <div *ngIf="currentPage != 0" class="footer phone"> <div fxLayout="row" fxLayoutAlign="center center" *ngIf="currentPage != nbPagesForm && !isEditMode"> <app-footer-form + [btnName]="['Précédent', 'Suivant']" (previousPage)="previousPage()" (nextPage)="nextPage()" [isValid]="isPageValid" diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 9ed4fd2ae..1e9e644ba 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -139,6 +139,7 @@ h3 { height: 100%; .btnStart { margin-top: 90px; + margin-bottom: 10px; text-align: center; } } diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 769104607..0d26c642d 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -19,6 +19,7 @@ import { first } from 'rxjs/operators'; import { PageTypeEnum } from './pageType.enum'; import { CustomRegExp } from '../utils/CustomRegExp'; import { StructureWithOwners } from '../models/structureWithOwners.model'; +import { RouterListenerService } from '../services/routerListener.service'; const { DateTime } = require('luxon'); @Component({ selector: 'app-structureForm', @@ -80,7 +81,8 @@ export class FormComponent implements OnInit { private profileService: ProfileService, private authService: AuthService, private router: Router, - private route: ActivatedRoute + private route: ActivatedRoute, + private routerListener: RouterListenerService ) {} async ngOnInit(): Promise<void> { @@ -123,6 +125,16 @@ export class FormComponent implements OnInit { }); } + public previousUrl(): void { + if (this.claimStructure) { + this.routerListener.goToPreviousUrl(this.claimStructure); + } else if (this.editForm) { + this.routerListener.goToPreviousUrl(this.editForm.value); + } else { + this.routerListener.goToPreviousUrl(); + } + } + async setCategories(): Promise<void> { this.searchService.getCategoriesAccompaniment().subscribe((categories: Category[]) => { this.proceduresAccompaniment = categories[0]; @@ -166,7 +178,9 @@ export class FormComponent implements OnInit { // Init form this.structureForm = this.createStructureForm(structure); - this.editForm = this.createStructureForm(structure); + if (this.isEditMode) { + this.editForm = this.createStructureForm(structure); + } // Init hours form this.hoursForm = new FormGroup({ diff --git a/src/app/services/routerListener.service.ts b/src/app/services/routerListener.service.ts new file mode 100644 index 000000000..ff42af685 --- /dev/null +++ b/src/app/services/routerListener.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { Router, RoutesRecognized } from '@angular/router'; +import { filter, pairwise } from 'rxjs/operators'; + +@Injectable({ + providedIn: 'root', +}) +export class RouterListenerService { + private previousUrl: string; + constructor(private router: Router) { + this.router.events + .pipe( + filter((evt: any) => evt instanceof RoutesRecognized), + pairwise() + ) + .subscribe((events: RoutesRecognized[]) => { + this.previousUrl = events[0].urlAfterRedirects; + }); + } + + public goToPreviousUrl(data?: any): void { + if (data) { + this.router.navigateByUrl(this.previousUrl, { state: { data: data } }); + } else { + this.router.navigateByUrl(this.previousUrl); + } + } +} -- GitLab From 9b8df1d03eff87014d263dfa84e9eb2e82fcda55 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 25 Feb 2021 16:37:47 +0100 Subject: [PATCH 70/72] fix: various design bug --- src/app/form/form.component.html | 44 +++++++++++-------- src/app/form/form.component.scss | 9 ++-- src/app/form/form.component.ts | 2 +- src/app/header/header.component.html | 1 + src/app/header/header.component.scss | 1 + src/app/profile/services/profile.service.ts | 8 ++++ src/app/services/structure.service.ts | 4 +- .../components/button/button.component.html | 2 +- .../structure-details.component.html | 2 +- .../structure-details.component.scss | 2 +- .../structure-details.component.ts | 18 +++++--- src/styles.scss | 4 ++ 12 files changed, 61 insertions(+), 36 deletions(-) diff --git a/src/app/form/form.component.html b/src/app/form/form.component.html index 5f5583778..7378f6c9c 100644 --- a/src/app/form/form.component.html +++ b/src/app/form/form.component.html @@ -6,7 +6,7 @@ ></app-modal-confirmation> <div class="content" *ngIf="!isLoading" [ngClass]="{ editMode: isEditMode }"> <div class="headerEditMode" *ngIf="isEditMode" fxLayout="row" fxLayoutAlign="space-between center"> - <h2>Modification de {{ editForm.get('structureName').value }}</h2> + <h2 class="no-margin">Modification de {{ editForm.get('structureName').value }}</h2> <app-structure-options-modal [structure]="structureWithOwners" [isEditFormView]="true" @@ -74,21 +74,19 @@ fxLayout="column" fxLayoutAlign="space-between" > - <div> - <div class="summary" *ngFor="let page of pagesValidation; let index = index"> - <div - class="itemSummary" - [ngClass]="{ last: index == 22 }" - fxLayout="row" - fxLayoutAlign="space-between center" - *ngIf="page.name" - (click)="goToSpecificPage(index, false)" - > - {{ page.name }} - <svg class="chevronRight" aria-hidden="true"> - <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> - </svg> - </div> + <div class="summary" *ngFor="let page of pagesValidation; let index = index"> + <div + class="itemSummary" + [ngClass]="{ last: index == 22 }" + fxLayout="row" + fxLayoutAlign="space-between center" + *ngIf="page.name" + (click)="goToSpecificPage(index, false)" + > + {{ page.name }} + <svg class="chevronRight" aria-hidden="true"> + <use [attr.xlink:href]="'assets/form/sprite.svg#chevronRight'"></use> + </svg> </div> </div> </div> @@ -105,7 +103,11 @@ </ul> </div> </div> - <form [formGroup]="accountForm" *ngIf="accountForm && !profile" (keyup.enter)="isPageValid && !isEditMode? nextPage() : null"> + <form + [formGroup]="accountForm" + *ngIf="accountForm && !profile" + (keyup.enter)="isPageValid && !isEditMode ? nextPage() : null" + > <div *ngIf="currentPage == pageTypeEnum.accountInfo" class="page"> <div class="title"> <h3>Qui êtes-vous ?</h3> @@ -274,7 +276,11 @@ </div> </div> </form> - <form [formGroup]="structureForm" *ngIf="structureForm" (keyup.enter)="isPageValid && !isEditMode? nextPage() : null"> + <form + [formGroup]="structureForm" + *ngIf="structureForm" + (keyup.enter)="isPageValid && !isEditMode ? nextPage() : null" + > <div *ngIf="currentPage == pageTypeEnum.structureNameAndAddress" class="page"> <div class="title"> <h3>Quelle structure voulez-vous réferencer ?</h3> @@ -1016,7 +1022,7 @@ <div class="textareaBlock" fxLayout="column"> <textarea rows="8" - placeholder="Exemple : nous sommes une équipe de 7 bénévoles qui orientont les personnes pour qui le numérique est une langue étrangère" + placeholder="Exemple : nous sommes une équipe de 7 bénévoles qui orientons les personnes pour qui le numérique est une langue étrangère" maxlength="500" formControlName="description" ></textarea> diff --git a/src/app/form/form.component.scss b/src/app/form/form.component.scss index 9ed4fd2ae..aef1dad97 100644 --- a/src/app/form/form.component.scss +++ b/src/app/form/form.component.scss @@ -87,7 +87,7 @@ h3 { .content { .editHome { height: calc( - 100vh - #{$header-height} - #{$footer-height} - 81px - 1px - 55px - 24px + 100vh - #{$header-height} - #{$footer-height} - 81px - 1px - 55px - 48px ) !important; // -1px because of header border } @media #{$tablet} { @@ -99,7 +99,8 @@ h3 { ); // -1px because of header border } .editHome { - height: calc(100vh - #{$header-height-phone} - 87px - 1px - 55px) !important; // -1px because of header border + height: unset !important; // -1px because of header border + margin-bottom: 80px; } } } @@ -526,12 +527,10 @@ img { max-width: 960px; margin: auto; margin-top: 24px; + margin-bottom: 24px; h2 { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } - @media #{$tablet} { - margin-top: 0; - } } diff --git a/src/app/form/form.component.ts b/src/app/form/form.component.ts index 769104607..1acd268a7 100644 --- a/src/app/form/form.component.ts +++ b/src/app/form/form.component.ts @@ -566,7 +566,7 @@ export class FormComponent implements OnInit { this.progressStatus = 100; }); } else { - this.structureService.claimStructureWithAccount(this.claimStructure._id, user).subscribe(() => { + this.structureService.claimStructureWithAccount(this.claimStructure._id, user.email).subscribe(() => { this.progressStatus = 100; }); } diff --git a/src/app/header/header.component.html b/src/app/header/header.component.html index ee833e54b..9d31ad373 100644 --- a/src/app/header/header.component.html +++ b/src/app/header/header.component.html @@ -33,6 +33,7 @@ </div> <div fxLayout="column" class="right-header" fxLayoutAlign="none baseline" fxLayoutGap="5vw"> <a routerLink="/home" [routerLinkActive]="'active'" (click)="closeMenu()" i18n>Les acteurs</a> + <a routerLink="/about" [routerLinkActive]="'active'" i18n>Qui sommes-nous ?</a> <a *ngIf="isAdmin" routerLink="/admin" [routerLinkActive]="'active'" (click)="closeMenu()">Administration</a> </div> </div> diff --git a/src/app/header/header.component.scss b/src/app/header/header.component.scss index 37b4b41bc..7470c080a 100644 --- a/src/app/header/header.component.scss +++ b/src/app/header/header.component.scss @@ -130,6 +130,7 @@ a { .mobile-show { display: none; + @include cn-bold-16; @media #{$tablet} { display: block; } diff --git a/src/app/profile/services/profile.service.ts b/src/app/profile/services/profile.service.ts index 5ac816cbf..370137312 100644 --- a/src/app/profile/services/profile.service.ts +++ b/src/app/profile/services/profile.service.ts @@ -36,6 +36,14 @@ export class ProfileService { return this.currentProfile.structuresLink.includes(idStructure); } + public isPendingLinkedToStructure(idStructure: string): boolean { + if (!this.currentProfile) { + return false; + } + console.log(this.currentProfile.pendingStructuresLink); + return this.currentProfile.pendingStructuresLink.includes(idStructure); + } + public removeProfile(): void { this.currentProfile = null; } diff --git a/src/app/services/structure.service.ts b/src/app/services/structure.service.ts index b94990936..fe7e09396 100644 --- a/src/app/services/structure.service.ts +++ b/src/app/services/structure.service.ts @@ -44,8 +44,8 @@ export class StructureService { return this.http.post<boolean>(`${this.baseUrl}/${id}/isClaimed`, profile); } - public claimStructureWithAccount(id: string, user: User): Observable<string[]> { - return this.http.post<any>(`${this.baseUrl}/${id}/claim`, user); + public claimStructureWithAccount(id: string, email: string): Observable<string[]> { + return this.http.post<any>(`${this.baseUrl}/${id}/claim`, { email }); } public getStructure(id: string): Observable<Structure> { diff --git a/src/app/shared/components/button/button.component.html b/src/app/shared/components/button/button.component.html index dcfb2a0f7..d9ee298fb 100644 --- a/src/app/shared/components/button/button.component.html +++ b/src/app/shared/components/button/button.component.html @@ -8,7 +8,7 @@ fxLayoutAlign="space-between center" fxLayoutGap="5px" > - <app-svg-icon style="height: 100%" [type]="'ico'" [icon]="iconBtn" [iconColor]="'currentColor'"></app-svg-icon> + <app-svg-icon [type]="'ico'" [icon]="iconBtn" [iconColor]="'currentColor'"></app-svg-icon> <span>{{ text }}</span> </div> </button> 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 9eda0b470..2e865d93f 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 @@ -5,7 +5,7 @@ </div> <div fxLayout="row" class="structure-details-block" fxLayoutAlign="baseline baseline" fxLayoutGap="8px"> <div fxLayout="column" fxLayoutGap="10px" fxFlex="100%"> - <div fxLayout="column" fxLayoutAlign="space-between start"> + <div fxLayout="column" class="no-margin" fxLayoutAlign="space-between start"> <h2 class="bold">{{ structure.structureName }}</h2> </div> <div fxLayout="row" fxLayoutAlign="space-between center"> diff --git a/src/app/structure-list/components/structure-details/structure-details.component.scss b/src/app/structure-list/components/structure-details/structure-details.component.scss index 5759b6c02..19284a7cc 100644 --- a/src/app/structure-list/components/structure-details/structure-details.component.scss +++ b/src/app/structure-list/components/structure-details/structure-details.component.scss @@ -67,7 +67,7 @@ h2 { @include cn-regular-26; } h3 { - margin: 0; + margin: 0 0 8px 0; @include cn-regular-16; } h4 { 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 1e12ab4d7..9604623b9 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 @@ -161,11 +161,13 @@ export class StructureDetailsComponent implements OnInit { public claimStructure(shouldClaim: boolean): void { this.toggleClaimModal(); if (shouldClaim) { - this.profileService.getProfile().then((user: User) => { - this.structureService.claimStructureWithAccount(this.structure._id, user).subscribe(() => { - this.isClaimed = true; + this.structureService + .claimStructureWithAccount(this.structure._id, this.authService.userValue.username) + .subscribe(() => { + this.profileService.getProfile().then((user: User) => { + this.isClaimed = true; + }); }); - }); } } @@ -173,7 +175,9 @@ export class StructureDetailsComponent implements OnInit { this.toggleJoinModal(); if (shouldClaim) { this.structureService.joinStructure(this.structure._id, this.authService.userValue.username).subscribe((res) => { - this.isClaimed = true; + this.profileService.getProfile().then((user: User) => { + this.isClaimed = true; + }); }); } } @@ -247,7 +251,9 @@ export class StructureDetailsComponent implements OnInit { public displayJoin(): boolean { return ( - !(this.profileService.isLinkedToStructure(this.structure._id) || this.profileService.isAdmin()) && this.isClaimed + !(this.profileService.isLinkedToStructure(this.structure._id) || this.profileService.isAdmin()) && + this.isClaimed && + !this.profileService.isPendingLinkedToStructure(this.structure._id) ); } } diff --git a/src/styles.scss b/src/styles.scss index 24a7585c8..f851c8164 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -266,3 +266,7 @@ button { } @include background-hash($grey-2); } + +.no-margin { + margin: 0 !important; +} -- GitLab From 76ec783626fc9be22774308dcd22d20d03326ad5 Mon Sep 17 00:00:00 2001 From: Jeremie BRISON <ext.sopra.jbrison@grandlyon.com> Date: Thu, 25 Feb 2021 16:41:15 +0100 Subject: [PATCH 71/72] fix(profile) : fix max-height of list owners --- src/app/profile/profile.component.html | 2 +- src/app/profile/profile.component.scss | 5 +++++ .../structure-options-modal.component.html | 2 +- .../structure-options-modal.component.scss | 8 ++++++++ src/styles.scss | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/app/profile/profile.component.html b/src/app/profile/profile.component.html index 4a5e03d17..da389cd0f 100644 --- a/src/app/profile/profile.component.html +++ b/src/app/profile/profile.component.html @@ -33,7 +33,7 @@ }}</a> <app-structure-options-modal [structure]="s" (closed)="ngOnInit()"></app-structure-options-modal> </div> - <div fxLayout="column" fxLayoutGap="14px"> + <div fxLayout="column" fxLayoutGap="14px" class="ownersBlock"> <p class="ownerName" *ngFor="let owner of s.owners">{{ owner.email }}</p> </div> </div> diff --git a/src/app/profile/profile.component.scss b/src/app/profile/profile.component.scss index f2547348e..341f12ec1 100644 --- a/src/app/profile/profile.component.scss +++ b/src/app/profile/profile.component.scss @@ -48,6 +48,11 @@ } .structureSection { margin-bottom: 108px; + .ownersBlock { + width: 100%; + max-height: 300px; + overflow: auto; + } } .addSection { diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.html b/src/app/shared/components/structure-options-modal/structure-options-modal.component.html index a5f68cc4d..a9aab9a78 100644 --- a/src/app/shared/components/structure-options-modal/structure-options-modal.component.html +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.html @@ -165,7 +165,7 @@ <h2>Supprimer un compte</h2> <div class="ico-close-details" (click)="closeModalOptsProfile()"></div> </div> - <div fxLayout="column" fxLayoutGap="16px"> + <div fxLayout="column" fxLayoutGap="16px" class="ownersBlock"> <div class="row removeOwner" *ngFor="let owner of structure.owners" fxLayoutGap="16px"> <button class="btn-primary small" (click)="removeOwner(owner.id)">X</button> <span> diff --git a/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss b/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss index 6edbfaaaf..2d34b631e 100644 --- a/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss +++ b/src/app/shared/components/structure-options-modal/structure-options-modal.component.scss @@ -60,6 +60,9 @@ button { } } .removeOwner { + white-space: nowrap; + width: 84%; + text-overflow: ellipsis; button { width: 40px; background-color: $red-default; @@ -83,4 +86,9 @@ button { .ico-close-details { min-width: 40px; } + .ownersBlock { + width: 100%; + max-height: 300px; + overflow: auto; + } } diff --git a/src/styles.scss b/src/styles.scss index 24a7585c8..3a1cf55d2 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -233,7 +233,7 @@ button { border-radius: 6px; @include background-hash($grey-2); border: 1px solid $grey-4; - position: absolute; + position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); -- GitLab From d2681178c7b2168b396460ecef907f5419dd4823 Mon Sep 17 00:00:00 2001 From: Hugo SUBTIL <ext.sopra.husubtil@grandlyon.com> Date: Thu, 25 Feb 2021 17:04:59 +0100 Subject: [PATCH 72/72] chore(release): 1.5.0 --- CHANGELOG.md | 71 +++++++++++++++++++++++++++++++++++++++++++++++ package-lock.json | 2 +- package.json | 2 +- 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a64770ba..8a26c75de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,77 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [1.5.0](https://forge.grandlyon.com///compare/v1.4.0...v1.5.0) (2021-02-25) + + +### Features + +* add connect redirection + handle structure join ([f5abd5e](https://forge.grandlyon.com///commit/f5abd5e5fea66c8e6d04aee335be93da9976b3e7)) +* add favicon ([ff8adb8](https://forge.grandlyon.com///commit/ff8adb8c40679fbd539ce644c00eb2cec109f531)) +* add form widget for claim and register ([ce914c8](https://forge.grandlyon.com///commit/ce914c8e917610d4a5812dee6fdc4a607e4cf763)) +* add hour-picker first version from boussole project ([4333076](https://forge.grandlyon.com///commit/4333076c2212ceb08d73cc6d7205e0e3bc75b986)) +* add outdated browser handling ([ecff2a3](https://forge.grandlyon.com///commit/ecff2a37d20e6f85f0823bc96899975b5298d219)) +* add registration for claim when not connected ([0bbb3f0](https://forge.grandlyon.com///commit/0bbb3f0e1b55ff2461cdbf48d0380774aebaea4c)) +* handle sign-in with form ([7031971](https://forge.grandlyon.com///commit/7031971082fae414423abdecc701789f42a87fda)) +* **about:** add europe and region logo + update style ([34e3aeb](https://forge.grandlyon.com///commit/34e3aebd188f3984dcd5bfb0028bfd20793924da)) +* add logic to hout-picker + remove copy/paste feature ([c61a63e](https://forge.grandlyon.com///commit/c61a63e391636645af45b26c174dd57d86d17e80)) +* add registration form from email ([60fa0c8](https://forge.grandlyon.com///commit/60fa0c8e41df9c82e5d6957dd752e4c210c4c9c8)) +* add social network on structure details ([a057084](https://forge.grandlyon.com///commit/a05708495671d20610c102091f6c2893e8214bcb)) +* add user delete structure ([f85d2c4](https://forge.grandlyon.com///commit/f85d2c4e938941eef70ce6a36f12b8c507b2524b)) +* move structureType to back ([8d7aa8e](https://forge.grandlyon.com///commit/8d7aa8e28347610ba36b833e41ce9e5f55a04f0c)) +* update date-picker style ([36904c7](https://forge.grandlyon.com///commit/36904c785c075d64c68fffe669e3e9b33c9d119f)) +* update header/footer design ([0838655](https://forge.grandlyon.com///commit/083865546cf32c0f209abe3f3a48db5d6c7cdf94)) +* update nginx conf for ghost ([c390497](https://forge.grandlyon.com///commit/c390497d8127190ea6f56873c3fa28b86b53f39d)) + + +### Bug Fixes + +* add admin structureName + bug fix on sturcutredetails ([6f32ee8](https://forge.grandlyon.com///commit/6f32ee8659c17790b762cafd3c2f34263e2cf522)) +* add claim structure message ([41cfbb8](https://forge.grandlyon.com///commit/41cfbb801537b662fe08ed4133e803a15d914088)) +* add page type ([3c037a3](https://forge.grandlyon.com///commit/3c037a3f441d9569ba6f47ef9ff047bab6181630)) +* add previous page handling for claim ([ee7b65a](https://forge.grandlyon.com///commit/ee7b65a8a1f48c218796b145d2b4ed51db2690b9)) +* claim structure issue ([7b2218b](https://forge.grandlyon.com///commit/7b2218b6e0c61f5b7c66b5580691ac63d0de9410)) +* design profile ([8c829ba](https://forge.grandlyon.com///commit/8c829ba2b118414c6cb536b6033b6f1faf1a864c)) +* firefox issue on hour picker ([12b8fe0](https://forge.grandlyon.com///commit/12b8fe0ec470876f506bb64c69644cb1d1a628d7)) +* header issue on add structure navigation ([1e210a0](https://forge.grandlyon.com///commit/1e210a0b974199990ac3b5d48a34e0c6a8bb4419)) +* icon color + alignements ([83ea88f](https://forge.grandlyon.com///commit/83ea88f74f4f1c7d5dd84be9671df4e05ed3a89c)) +* map marker color + stroke-width ([6375907](https://forge.grandlyon.com///commit/637590750aa921e625bbe77c879ba194131b62c0)) +* move about page link + fix icon ([a9bb6d5](https://forge.grandlyon.com///commit/a9bb6d5da584d0b31fd9b75613353f99e2b3cce5)) +* move footer-form ([2f2e7ba](https://forge.grandlyon.com///commit/2f2e7baa422b788d590c5dafced0cb69a2c35f83)) +* regex issue on phone number and text without number ([e886673](https://forge.grandlyon.com///commit/e886673882248a5884f5ff57229463257b23b8ab)) +* remove locate button on search ([66ca00f](https://forge.grandlyon.com///commit/66ca00f332fffdda2115ee050bb834a1821105ea)) +* remove menu-phone component and fix bug ([cb2607f](https://forge.grandlyon.com///commit/cb2607f6a28ed51ad1d4cf985a0baf8b75329ebf)) +* remove structure delete for users from structure-details ([516446d](https://forge.grandlyon.com///commit/516446d600b197df71a8508ee8e722289e806fd3)) +* structure list card height ([35df2f9](https://forge.grandlyon.com///commit/35df2f9fb58879a4af33e000cf788707b5bb4efc)) +* structure type display ([4abc885](https://forge.grandlyon.com///commit/4abc885932771e89f53b497743ebb882cbe3c369)) +* structure type for map ([667bc2d](https://forge.grandlyon.com///commit/667bc2d54d58f3c3d1b016852bce616b54a06e87)) +* typo and color ([a5e2f93](https://forge.grandlyon.com///commit/a5e2f93bb8d8782074faf14936af7f3cb797a4ce)) +* unexisting css class ([a9241a7](https://forge.grandlyon.com///commit/a9241a7959687acbe9dad12ac5a31716e0e99977)) +* upadte import ([cc61eed](https://forge.grandlyon.com///commit/cc61eedb495879f173ac91f2ae1d716f60906de7)) +* update modale position ([8a18689](https://forge.grandlyon.com///commit/8a186897bc4c713cacd8fd11e17f89dbfd40ce51)) +* update typeform update ([d0d134d](https://forge.grandlyon.com///commit/d0d134d0753584d31919034449672e379a5e72f0)) +* various design bug ([9b8df1d](https://forge.grandlyon.com///commit/9b8df1d03eff87014d263dfa84e9eb2e82fcda55)) +* wording for form / profile and outdated page ([de23088](https://forge.grandlyon.com///commit/de230889cce920786d9d48f56084c23c012d4703)) +* **editForm:** fix logic validate/close form ([c68f9ba](https://forge.grandlyon.com///commit/c68f9baf3e433db7f34d06446d205e17d5ed48cd)) +* **form:** add menu icon ([e822be4](https://forge.grandlyon.com///commit/e822be4a83295d6a4558abd21c15053d6abc8f11)) +* **form:** add page (phoneContact structure) ([b8e91b8](https://forge.grandlyon.com///commit/b8e91b894d5200ef9e906750c3f04f4694b16b7c)) +* **form:** add page (pmrAccess) ([2c7a9fc](https://forge.grandlyon.com///commit/2c7a9fcb7409785a584effca7d4711853c139ed6)) +* **form:** add page(Description) ([4f458c7](https://forge.grandlyon.com///commit/4f458c7ad599ba33999bc7b939483d18da322405)) +* **form:** add page(description) + fix page(exceptionnalClosures) ([a398d07](https://forge.grandlyon.com///commit/a398d0712c2d9834260099f1c31d196b622e0692)) +* **form:** add page(Equipments) ([cd58628](https://forge.grandlyon.com///commit/cd586281c0f56591a8ccec9899b1e1b4653ca107)) +* **form:** add page(invited User) ([beda94d](https://forge.grandlyon.com///commit/beda94dc8e0e8b0829457855e52778020db12f26)) +* **form:** add page(isPassNumeric) ([0e82b75](https://forge.grandlyon.com///commit/0e82b75cce7bebc144d9a5f98f2575c112e93521)) +* **form:** add page(labelsQualifications) ([1a798c2](https://forge.grandlyon.com///commit/1a798c224953f6e1e1bae3f7133cd45b3b0e895e)) +* **form:** add scanner ([cda0a0d](https://forge.grandlyon.com///commit/cda0a0d39ff8a0e6c919c6ed30b72fe63666b853)) +* **form:** bug homePage blink on editForm ([dfa65ec](https://forge.grandlyon.com///commit/dfa65ec175cb18593408166212ae0140bf6546b8)) +* **form:** clean code ([5d0bd5b](https://forge.grandlyon.com///commit/5d0bd5b25a09ca950494da43823afd393a0f6197)) +* **form:** fix and clean code ([74daf90](https://forge.grandlyon.com///commit/74daf9003cc4c3acf9ff78cc8915aa07e0a308f4)) +* **form:** fix collaspse size + header titler phone ([6007a0d](https://forge.grandlyon.com///commit/6007a0dfc47b372be1d1372151889efd587feeeb)) +* **form:** fix css ([d5c1ea7](https://forge.grandlyon.com///commit/d5c1ea74acdd948559b396d30feb389e4a6054c5)) +* **form:** fix password hint ([05cf734](https://forge.grandlyon.com///commit/05cf7349d1622017fe209fc900fdbe01f9cbf9cf)) +* **form:** fix progress 100% on validate Mail ([2e15ee6](https://forge.grandlyon.com///commit/2e15ee6ccfd0653b391ae582a28d794cb11ef774)) +* **structures:** update strucutes list design ([1422095](https://forge.grandlyon.com///commit/14220950f13df2b5e0b7863d4796d61072ef45be)) + ## [1.4.0](https://forge.grandlyon.com///compare/v1.3.0...v1.4.0) (2021-02-01) diff --git a/package-lock.json b/package-lock.json index b2c01537e..cb903debf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "pamn", - "version": "1.4.0", + "version": "1.5.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 4d3dc600d..4f2d1c0ae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pamn", - "version": "1.4.0", + "version": "1.5.0", "scripts": { "ng": "ng", "start": "ng serve --configuration=fr --proxy-config proxy.conf.json", -- GitLab