Commit dd402814 authored by Hugo SUBTIL's avatar Hugo SUBTIL
Browse files

Merge branch 'feat/refacto-forms' into 'V2.0'

Feat/refacto forms

See merge request !237
parents da44d387 bb89936f
Pipeline #23365 passed with stages
in 3 minutes and 41 seconds
......@@ -14,13 +14,14 @@ 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';
import { NewsletterSubscriptionComponent } from './newsletter-subscription/newsletter-subscription.component';
import { OrientationFormComponent } from './form/orientation-form/orientation-form.component';
import { StructureListPrintComponent } from './form/orientation-form/component/structure-list-print/structure-list-print.component';
import { StructureResolver } from './resolvers/structure.resolver';
import { RoleGuard } from './guards/role.guard';
import { RouteRole } from './shared/enum/routeRole.enum';
import { LoginComponent } from './login/login.component';
import { PasswordFormComponent } from './shared/components';
import { FooterComponent } from './footer/footer.component';
const footerOutletRoute: Route = {
......@@ -59,7 +60,7 @@ const routes: Routes = [
children: [
{
path: '',
component: CartoComponent,
component: LoginComponent,
},
footerOutletRoute,
],
......@@ -109,7 +110,7 @@ const routes: Routes = [
children: [
{
path: '',
component: UserVerificationComponent,
component: LoginComponent,
},
footerOutletRoute,
],
......@@ -170,6 +171,10 @@ const routes: Routes = [
footerOutletRoute,
],
},
{
path: 'new-password',
component: PasswordFormComponent,
},
{
path: 'create-structure',
children: [
......@@ -239,6 +244,10 @@ const routes: Routes = [
footerOutletRoute,
],
},
{
path: 'form',
loadChildren: () => import('./form/form-view/form-view.module').then((m) => m.FormViewModule),
},
{
path: 'home',
redirectTo: 'news',
......
......@@ -22,14 +22,12 @@ import { LegalNoticeComponent } from './legal-notice/legal-notice.component';
import { PageComponent } from './page/page.component';
import { ContactComponent } from './contact/contact.component';
import { FormComponent } from './form/structure-form/form.component';
import { UserVerificationComponent } from './user-verification/user-verification.component';
import { AuthGuard } from './guards/auth.guard';
import { CustomHttpInterceptor } from './config/http-interceptor';
import { ResetEmailComponent } from './reset-email/reset-email.component';
import { ResetPasswordComponent } from './reset-password/reset-password.component';
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';
import { RouterListenerService } from './services/routerListener.service';
......@@ -45,6 +43,8 @@ import { StructureResolver } from './resolvers/structure.resolver';
import { RoleGuard } from './guards/role.guard';
import { UpdateService } from './services/update.service';
import { DataShareConsentComponent } from './shared/components/data-share-consent/data-share-consent.component';
import { FormViewModule } from './form/form-view/form-view.module';
import { LoginComponent } from './login/login.component';
@NgModule({
declarations: [
......@@ -60,11 +60,9 @@ import { DataShareConsentComponent } from './shared/components/data-share-consen
LegalNoticeComponent,
PageComponent,
ContactComponent,
UserVerificationComponent,
ResetEmailComponent,
ResetPasswordComponent,
FormComponent,
FooterFormComponent,
StructureJoinComponent,
NewsletterSubscriptionComponent,
OrientationFormComponent,
......@@ -73,6 +71,7 @@ import { DataShareConsentComponent } from './shared/components/data-share-consen
StructurePrintHeaderComponent,
DataShareConsentComponent,
OrientationComponent,
LoginComponent,
],
imports: [
BrowserModule,
......@@ -82,6 +81,7 @@ import { DataShareConsentComponent } from './shared/components/data-share-consen
MapModule,
BrowserAnimationsModule,
ToastrModule.forRoot(),
FormViewModule,
ServiceWorkerModule.register('ngsw-worker.js', {
enabled: environment.production,
}),
......
<div fxLayout="row" [ngClass]="{ column: hasFinishButton() }" fxLayoutGap="10px" fxLayoutAlign="center center">
<div
class="footerForm"
fxLayout="row"
[ngClass]="{ column: hasFinishButton() }"
fxLayoutGap="10px"
fxLayoutAlign="center center"
>
<app-button
*ngIf="displayPreviousButton"
(action)="goToPreviousPage()"
*ngIf="!isLastFormStep && !isNextFormTransition && !isStructureLastPage() && !isPersonalOfferFirstPage()"
(action)="prevPage()"
[text]="btnName[0]"
[iconType]="'form'"
[iconBtn]="'chevronLeft'"
></app-button>
<app-button *ngIf="hasFinishButton()" (action)="finishedModal()" [text]="btnName[2]" [iconBtn]="'flag'"></app-button>
<app-button *ngIf="isLastFormStep" (action)="goToHome()" [text]="'Ok'" [style]="buttonTypeEnum.Primary"></app-button>
<app-button
(action)="goToNextPage()"
*ngIf="!isLastFormStep && !isNextFormTransition"
(action)="nextPage()"
[disabled]="!isValid"
[text]="btnName[1]"
[iconBtn]="btnName[1] == 'Imprimer' ? 'print' : 'chevronRight'"
......
......@@ -2,6 +2,10 @@
@import '../../../assets/scss/typography';
@import '../../../assets/scss/breakpoint';
.footerForm {
padding: 20px 0;
}
.btn-primary {
&.previous {
background-color: $white;
......
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router';
import { User } from '../../models/user.model';
import { ProfileService } from '../../profile/services/profile.service';
import { AuthService } from '../../services/auth.service';
import { NewsletterService } from '../../services/newsletter.service';
import { StructureService } from '../../services/structure.service';
import { ButtonType } from '../../shared/components/button/buttonType.enum';
import { Utils } from '../../utils/utils';
import { accountFormStep } from '../form-view/account-form/accountFormStep.enum';
import { formType } from '../form-view/formType.enum';
import { personalOfferFormStep } from '../form-view/personal-offer-form/personalOfferFormStep.enum';
import { profileFormStep } from '../form-view/profile-form/profileFormStep.enum';
import { structureFormStep } from '../form-view/structure-form/structureFormStep.enum';
@Component({
selector: 'app-footer-form',
templateUrl: './footer-form.component.html',
styleUrls: ['./footer-form.component.scss'],
})
export class FooterFormComponent {
export class FooterFormComponent implements OnChanges {
@Input() currentForm: formType;
@Input() isValid: boolean;
@Input() isClaimMode: boolean;
@Input() isAccountMode: boolean;
@Input() btnName: string[];
@Input() displayPreviousButton: boolean = true;
@Output() nextPage = new EventEmitter<any>();
@Output() previousPage = new EventEmitter<any>();
@Input() nbPagesForm: number;
@Input() form: FormGroup;
@Input() linkedStructureId: Array<string> = null;
@Input() acceptNewsletter: boolean;
@Input() currentStep: accountFormStep | profileFormStep | structureFormStep | personalOfferFormStep;
@Input() hasOtherPersonalOffer: boolean;
@Output() goNext = new EventEmitter<any>();
@Output() goPrev = new EventEmitter<any>();
@Output() endPage = new EventEmitter<any>();
@Output() endForm = new EventEmitter<any>();
@Output() changeCurrentStep = new EventEmitter<any>();
public isLastFormStep: boolean = false;
public isNextFormTransition: boolean = false;
public buttonTypeEnum = ButtonType;
constructor(
private authService: AuthService,
public utils: Utils,
private router: Router,
private structureService: StructureService,
private profileService: ProfileService,
private newsletterService: NewsletterService
) {}
public goToNextPage(): void {
this.nextPage.emit();
this.goNext.emit();
}
async ngOnChanges(changes: SimpleChanges): Promise<void> {
if (changes.currentStep) {
if (this.currentStep === accountFormStep.confirmEmailSentInfo && this.currentForm === formType.account) {
this.isLastFormStep = true;
}
if (
this.currentForm === formType.personaloffer &&
this.currentStep === personalOfferFormStep.personalOfferFinishedInfo &&
!this.hasOtherPersonalOffer
) {
this.isNextFormTransition = true;
}
if (this.currentForm === formType.structure && this.currentStep === structureFormStep.mailSentInfo) {
const user: User = await this.profileService.getProfile();
if (!user.job.hasPersonalOffer) {
this.isLastFormStep = true;
}
}
}
}
public goToPreviousPage(): void {
this.previousPage.emit();
this.goPrev.emit();
}
public hasFinishButton(): boolean {
......@@ -29,4 +84,94 @@ export class FooterFormComponent {
public finishedModal(): void {
this.endPage.emit();
}
public goToHome(): void {
this.router.navigateByUrl('news');
}
public prevPage(): void {
if (this.currentForm === formType.structure && this.currentStep === structureFormStep.structureType) {
this.changeCurrentStep.emit(structureFormStep.structureFormTime);
return;
}
this.goToPreviousPage();
}
public async nextPage(): Promise<void> {
if (this.currentForm === formType.account && this.currentStep === accountFormStep.accountNewsletter) {
const user = new User(this.form.value);
// Create user with structure
user.structuresLink = this.linkedStructureId;
this.authService.register(user).subscribe(() => {});
this.newsletterService.newsletterSubscribe(user.email).subscribe(() => {});
document.getElementsByClassName('page')[0].scrollTo(0, 0);
}
if (this.isProfileLastPage()) {
this.endForm.emit({ formType: this.currentForm });
return;
}
if (this.currentForm === formType.structure) {
if (this.currentStep === structureFormStep.structureChoiceCompletion) {
const chooseCompleteStructInfo = this.form.get('choiceCompletion').value;
if (!chooseCompleteStructInfo) {
this.changeCurrentStep.emit(structureFormStep.structureContactCompletion);
return;
}
}
if (this.currentStep === structureFormStep.structureFormTime) {
this.changeCurrentStep.emit(structureFormStep.structureType);
return;
}
if (this.currentStep === structureFormStep.structureContactCompletion) {
//TODO Go to send mail page and send the mail
return;
}
if (this.currentStep === structureFormStep.structureConsent) {
const user: User = await this.profileService.getProfile();
this.structureService.createStructure(this.form.value, user).subscribe(() => {});
}
}
if (this.isPersonalOfferpage()) {
this.endForm.emit({ formType: this.currentForm });
return;
}
if (this.isStructureChoiceValid()) {
this.endForm.emit({ formType: this.currentForm, formStep: this.currentStep });
return;
}
if (this.isStructureLastPage()) {
this.endForm.emit({ formType: this.currentForm, formStep: this.currentStep });
return;
}
this.goToNextPage();
}
private isStructureChoiceValid(): boolean {
return (
this.currentForm === formType.structure &&
this.currentStep === structureFormStep.structureChoice &&
this.form.value._id
);
}
public isStructureLastPage(): boolean {
return (
this.currentForm === formType.structure &&
(this.currentStep === structureFormStep.mailSentInfo ||
this.currentStep === structureFormStep.structureCreationFinishedInfo)
);
}
private isProfileLastPage(): boolean {
return this.currentForm === formType.profile && this.currentStep === profileFormStep.profileJobSelection;
}
private isPersonalOfferpage(): boolean {
return (
this.currentForm === formType.personaloffer &&
this.currentStep === personalOfferFormStep.personalOfferStructureChoice
);
}
public isPersonalOfferFirstPage(): boolean {
return this.currentStep === personalOfferFormStep.personalOfferAccompaniment;
}
}
<form
[formGroup]="accountForm"
*ngIf="accountForm && !profile"
(keyup.enter)="isPageValid && !isEditMode ? nextPage() : null"
>
<div class="title">
<h3>Quels identifiants utiliserez-vous pour vous connecter&nbsp;?</h3>
</div>
<div class="form-group" fxLayout="column">
<label for="email">Email du compte</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()"
(keyup)="verifyUserExist($event.target.value)"
formControlName="email"
placeholder="exemple: prenom.nom@grandlyon.com"
class="form-input email-placeholder"
[readonly]="isAccountMode"
[ngClass]="{ disabled: isAccountMode }"
/>
<app-svg-icon
*ngIf="accountForm.get('email').valid"
[iconClass]="'icon-26'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="accountForm.get('email').invalid && accountForm.get('email').value"
[iconClass]="'icon-26'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
</div>
</div>
<div class="form-group" fxLayout="column">
<p
class="special"
[ngClass]="{ invalid: accountForm.get('password').invalid && accountForm.get('password').value }"
>
Le mot de passe doit contenir au minimum
</p>
<ul>
<li
fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="10px"
[ngClass]="{
invalid: accountForm.get('password').value.length < 8,
valid: accountForm.get('password').value.length >= 8
}"
class=""
>
<app-svg-icon
*ngIf="accountForm.get('password').value.length >= 8"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="accountForm.get('password').value.length < 8"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
<p>8 caractères</p>
</li>
<li
fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="10px"
[ngClass]="{
invalid: !checkIfPasswordHasSpecialChar(accountForm.get('password').value),
valid: checkIfPasswordHasSpecialChar(accountForm.get('password').value)
}"
>
<app-svg-icon
*ngIf="checkIfPasswordHasSpecialChar(accountForm.get('password').value)"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="!checkIfPasswordHasSpecialChar(accountForm.get('password').value)"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
<p>un caractère spécial</p>
</li>
<li
fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="10px"
[ngClass]="{
invalid: !checkIfPasswordHasLowerCase(accountForm.get('password').value),
valid: checkIfPasswordHasLowerCase(accountForm.get('password').value)
}"
>
<app-svg-icon
*ngIf="checkIfPasswordHasLowerCase(accountForm.get('password').value)"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="!checkIfPasswordHasLowerCase(accountForm.get('password').value); s"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
<p>un caractère en minuscule</p>
</li>
<li
fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="10px"
[ngClass]="{
invalid: !checkIfPasswordHasUpperCase(accountForm.get('password').value),
valid: checkIfPasswordHasUpperCase(accountForm.get('password').value)
}"
>
<app-svg-icon
*ngIf="checkIfPasswordHasUpperCase(accountForm.get('password').value)"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="!checkIfPasswordHasUpperCase(accountForm.get('password').value); s"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
<p>un caractère en majuscule</p>
</li>
<li
fxLayout="row"
fxLayoutAlign="start center"
fxLayoutGap="10px"
[ngClass]="{
invalid: !checkIfPasswordHasDigit(accountForm.get('password').value),
valid: checkIfPasswordHasDigit(accountForm.get('password').value)
}"
>
<app-svg-icon
*ngIf="checkIfPasswordHasDigit(accountForm.get('password').value)"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="!checkIfPasswordHasDigit(accountForm.get('password').value)"
[iconClass]="'icon-16'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
<p>un chiffre</p>
</li>
</ul>
<div fxLayout="row" fxLayoutAlign="none center" fxLayoutGap="13px">
<input
[type]="isShowPassword ? 'text' : 'password'"
formControlName="password"
class="form-input password"
(input)="setValidationsForm()"
autocomplete="on"
/>
<app-svg-icon
[iconClass]="'icon-26 grey hover'"
[type]="'form'"
[icon]="'eyePassword'"
(click)="showPassword()"
></app-svg-icon>
<app-svg-icon
*ngIf="accountForm.get('password').valid"
[iconClass]="'icon-26'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="accountForm.get('password').invalid && accountForm.get('password').value"
[iconClass]="'icon-26'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
</div>
</div>
<div class="form-group" fxLayout="column">
<label for="confirmPassword">Vérification du mot de passe</label>
<div fxLayout="row" fxLayoutAlign="none center" fxLayoutGap="13px">
<input
[type]="isShowConfirmPassword ? 'text' : 'password'"
formControlName="confirmPassword"
class="form-input password"
(input)="setValidationsForm()"
autocomplete="on"
/>
<app-svg-icon
[iconClass]="'icon-26 grey hover'"
[type]="'form'"
[icon]="'eyePassword'"
(click)="showConfirmPassword()"
></app-svg-icon>
<app-svg-icon
*ngIf="accountForm.get('confirmPassword').valid && accountForm.get('confirmPassword').value"
[iconClass]="'icon-26'"
[type]="'form'"
[icon]="'validate'"
></app-svg-icon>
<app-svg-icon
*ngIf="accountForm.get('confirmPassword').invalid && accountForm.get('confirmPassword').value"
[iconClass]="'icon-26'"
[type]="'form'"
[icon]="'notValidate'"
></app-svg-icon>
</div>
</div>
</form>
@import '../../../../../assets/scss/color';
@import '../../../../../assets/scss/typography';
p.special {
@include lato-regular-14;
color: $grey-3;
margin: 4px 0 8px 0;
width: 280px;
&.invalid {
color: $orange-warning;
}
}
ul {
padding-left: 0;
margin: 0 0 8px 8px;
li {
display: flex;
margin: 4px 0 0 0;
font-size: small;
align-items: center;
p {
margin-top: unset;
margin-bottom: 0;
@include lato-regular-14;