From 67bcc5ea2f178194a4c89397cebe090f317acf7c Mon Sep 17 00:00:00 2001 From: FORESTIER Fabien <fabien.forestier@soprasteria.com> Date: Tue, 6 Nov 2018 15:21:46 +0100 Subject: [PATCH] Handle connected mode in contact form --- .../components/contact/contact.component.html | 202 +++++++++--------- .../components/contact/contact.component.scss | 28 ++- .../contact/contact.component.spec.ts | 16 +- .../components/contact/contact.component.ts | 20 +- .../header/header.component.spec.ts | 4 +- .../components/header/header.component.ts | 2 +- .../components/login/login.component.spec.ts | 2 +- src/app/core/models/auth.model.ts | 2 + src/app/core/services/auth.service.ts | 2 +- src/i18n/messages.en.xlf | 4 - src/i18n/messages.fr.xlf | 4 - 11 files changed, 166 insertions(+), 120 deletions(-) diff --git a/src/app/core/components/contact/contact.component.html b/src/app/core/components/contact/contact.component.html index c0077ff2..dd9a5bb5 100644 --- a/src/app/core/components/contact/contact.component.html +++ b/src/app/core/components/contact/contact.component.html @@ -1,119 +1,123 @@ <h1 class="has-text-centered" i18n="@@contact.contactUs">Contact us</h1> <div class="contact-form-container"> - <h2 i18n="@@contact.contactForm">Form contact</h2> - <form [formGroup]="form" (ngSubmit)="send()"> - <div class="columns is-multiline"> + <div class="columns"> <div class="column is-12-mobile is-2-tablet is-3-desktop"> <h3 i18n="@@contact.identity">Identity</h3> </div> <div class="column is-12-mobile is-10-tablet is-9-desktop"> - <div class="fields-container"> - - <div class="field"> - <label class="label" for="lastname" i18n="@@contact.lastname">Lastname</label> - <p class="control has-icons-right"> - <input id="lastname" class="input" type="text" formControlName="lastname" (keyup)="toUppercase('lastname')" - [ngClass]="{'is-danger': fieldIsInvalid('lastname'), 'is-success': fieldIsValid('lastname')}"> - <span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('lastname')"> - <i class="fas fa-check-circle"></i> - </span> - <span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('lastname')"> - <i class="fas fa-exclamation-circle"></i> - </span> - </p> - - <div class="incorrect-field-message" *ngIf="fieldIsInvalid('lastname')"> - <div *ngIf="form.controls['lastname'].errors.required" i18n="@@contact.errors.missingLastname"> - You must indicate a lastname. - </div> - <div *ngIf="form.controls['lastname'].errors.pattern" i18n="@@contact.errors.forbiddenCharacters"> - Special characters are forbidden. + <div class="fields-container" *ngIf="user !== null; else unauthenticatedUserTemplate"> + <p class="fullname">{{ user.firstname }} {{ user.lastname }}</p> + <p>{{ user.email }}</p> + </div> + <ng-template #unauthenticatedUserTemplate> + <div class="fields-container"> + + <div class="field"> + <label class="label" for="lastname" i18n="@@contact.lastname">Lastname</label> + <p class="control has-icons-right"> + <input id="lastname" class="input" type="text" formControlName="lastname" (keyup)="toUppercase('lastname')" + [ngClass]="{'is-danger': fieldIsInvalid('lastname'), 'is-success': fieldIsValid('lastname')}"> + <span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('lastname')"> + <i class="fas fa-check-circle"></i> + </span> + <span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('lastname')"> + <i class="fas fa-exclamation-circle"></i> + </span> + </p> + + <div class="incorrect-field-message" *ngIf="fieldIsInvalid('lastname')"> + <div *ngIf="form.controls['lastname'].errors.required" i18n="@@contact.errors.missingLastname"> + You must indicate a lastname. + </div> + <div *ngIf="form.controls['lastname'].errors.pattern" i18n="@@contact.errors.forbiddenCharacters"> + Special characters are forbidden. + </div> </div> </div> - </div> - <div class="field"> - <label class="label" for="firstname" i18n="@@contact.firstname">Firstname</label> - <p class="control has-icons-right"> - <input id="firstname" class="input" type="text" (keyup)="toUppercase('firstname')" formControlName="firstname" - [ngClass]="{'is-danger': fieldIsInvalid('firstname'), 'is-success': fieldIsValid('firstname')}"> - <span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('firstname')"> - <i class="fas fa-check-circle"></i> - </span> - <span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('firstname')"> - <i class="fas fa-exclamation-circle"></i> - </span> - </p> - - <div class="incorrect-field-message" *ngIf="fieldIsInvalid('firstname')"> - <div *ngIf="form.controls['firstname'].errors.required" i18n="@@contact.errors.missingFirstname"> - You must indicate a firstname. - </div> - <div *ngIf="form.controls['lastname'].errors.pattern" i18n="@@contact.errors.forbiddenCharacters"> - Special characters are forbidden. + <div class="field"> + <label class="label" for="firstname" i18n="@@contact.firstname">Firstname</label> + <p class="control has-icons-right"> + <input id="firstname" class="input" type="text" (keyup)="toUppercase('firstname')" formControlName="firstname" + [ngClass]="{'is-danger': fieldIsInvalid('firstname'), 'is-success': fieldIsValid('firstname')}"> + <span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('firstname')"> + <i class="fas fa-check-circle"></i> + </span> + <span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('firstname')"> + <i class="fas fa-exclamation-circle"></i> + </span> + </p> + + <div class="incorrect-field-message" *ngIf="fieldIsInvalid('firstname')"> + <div *ngIf="form.controls['firstname'].errors.required" i18n="@@contact.errors.missingFirstname"> + You must indicate a firstname. + </div> + <div *ngIf="form.controls['lastname'].errors.pattern" i18n="@@contact.errors.forbiddenCharacters"> + Special characters are forbidden. + </div> </div> </div> - </div> - <div class="field"> - <label class="label" for="email" i18n="@@contact.email">Email</label> - <p class="control has-icons-right"> - <input id="email" class="input" type="email" formControlName="email" [ngClass]="{'is-danger': fieldIsInvalid('email'), 'is-success': fieldIsValid('email')}"> - <span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('email')"> - <i class="fas fa-check-circle"></i> - </span> - <span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('email')"> - <i class="fas fa-exclamation-circle"></i> - </span> - </p> - - <div class="incorrect-field-message" *ngIf="fieldIsInvalid('email')"> - <div *ngIf="form.controls['email'].errors.required" i18n="@@contact.errors.missingEmail"> - You must enter an email address. - </div> + <div class="field"> + <label class="label" for="email" i18n="@@contact.email">Email</label> + <p class="control has-icons-right"> + <input id="email" class="input" type="email" formControlName="email" [ngClass]="{'is-danger': fieldIsInvalid('email'), 'is-success': fieldIsValid('email')}"> + <span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('email')"> + <i class="fas fa-check-circle"></i> + </span> + <span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('email')"> + <i class="fas fa-exclamation-circle"></i> + </span> + </p> + + <div class="incorrect-field-message" *ngIf="fieldIsInvalid('email')"> + <div *ngIf="form.controls['email'].errors.required" i18n="@@contact.errors.missingEmail"> + You must enter an email address. + </div> - <div *ngIf="form.controls['email'].errors.email" i18n="@@contact.errors.invalidEmail"> - You must enter a valid email address. + <div *ngIf="form.controls['email'].errors.email" i18n="@@contact.errors.invalidEmail"> + You must enter a valid email address. + </div> </div> </div> - </div> - <div class="field"> - <label class="label" for="emailConfirmation" i18n="@@contact.emailConfirmation">Confirm your email - address</label> - <p class="control has-icons-right"> - <input blockCopyPaste id="emailConfirmation" class="input" type="email" formControlName="emailConfirmation" - [ngClass]="{'is-danger': emailConfirmationError || fieldIsInvalid('emailConfirmation'), 'is-success': !emailConfirmationError && fieldIsValid('emailConfirmation')}"> - <span class="icon is-small is-right has-text-success" *ngIf="!emailConfirmationError && fieldIsValid('emailConfirmation')"> - <i class="fas fa-check-circle"></i> - </span> - <span class="icon is-small is-right has-text-danger" *ngIf="emailConfirmationError || fieldIsInvalid('emailConfirmation')"> - <i class="fas fa-exclamation-circle"></i> - </span> - </p> - - <div class="incorrect-field-message" *ngIf="fieldIsInvalid('emailConfirmation')"> - <div *ngIf="form.controls['emailConfirmation'].errors.required" i18n="@@contact.errors.missingConfirmationEmail"> - You must confirm your email address. - </div> + <div class="field"> + <label class="label" for="emailConfirmation" i18n="@@contact.emailConfirmation">Confirm your email + address</label> + <p class="control has-icons-right"> + <input blockCopyPaste id="emailConfirmation" class="input" type="email" formControlName="emailConfirmation" + [ngClass]="{'is-danger': emailConfirmationError || fieldIsInvalid('emailConfirmation'), 'is-success': !emailConfirmationError && fieldIsValid('emailConfirmation')}"> + <span class="icon is-small is-right has-text-success" *ngIf="!emailConfirmationError && fieldIsValid('emailConfirmation')"> + <i class="fas fa-check-circle"></i> + </span> + <span class="icon is-small is-right has-text-danger" *ngIf="emailConfirmationError || fieldIsInvalid('emailConfirmation')"> + <i class="fas fa-exclamation-circle"></i> + </span> + </p> + + <div class="incorrect-field-message" *ngIf="fieldIsInvalid('emailConfirmation')"> + <div *ngIf="form.controls['emailConfirmation'].errors.required" i18n="@@contact.errors.missingConfirmationEmail"> + You must confirm your email address. + </div> - <div *ngIf="form.controls['emailConfirmation'].errors.email" i18n="@@contact.errors.invalidEmail"> - You must enter a valid email address. + <div *ngIf="form.controls['emailConfirmation'].errors.email" i18n="@@contact.errors.invalidEmail"> + You must enter a valid email address. + </div> </div> - </div> - <div class="incorrect-field-message" *ngIf="emailConfirmationError"> - <div *ngIf="emailConfirmationError" i18n="@@contact.errors.confirmationEmailNotCorresponding"> - You must enter the same email address. + <div class="incorrect-field-message" *ngIf="emailConfirmationError"> + <div *ngIf="emailConfirmationError" i18n="@@contact.errors.confirmationEmailNotCorresponding"> + You must enter the same email address. + </div> </div> </div> </div> - - </div> + </ng-template> </div> - + </div> + <div class="columns"> <div class="column is-12-mobile is-2-tablet is-3-desktop"> <h3 i18n="@@contact.message">Message</h3> </div> @@ -176,11 +180,17 @@ </div> </div> </div> - - <div class="has-text-right button-wrapper"> - <button class="button button-gl is-outlined" type="button" [disabled]="formDisabled" (click)="cancel()" i18n="@@contact.cancel">Cancel</button> - <button type="submit" class="button button-gl" [ngClass]="{'is-loading': formDisabled}" [disabled]="formIsInvalid || formDisabled" - i18n="@@contact.send">Send</button> + </div> + </div> + <div class="columns"> + <div class="column is-12"> + <div class="field"> + <div class="has-text-right button-wrapper"> + <button class="button button-gl is-outlined" type="button" [disabled]="formDisabled" (click)="cancel()" + i18n="@@contact.cancel">Cancel</button> + <button type="submit" class="button button-gl" [ngClass]="{'is-loading': formDisabled}" [disabled]="formIsInvalid || formDisabled" + i18n="@@contact.send">Send</button> + </div> </div> </div> </div> diff --git a/src/app/core/components/contact/contact.component.scss b/src/app/core/components/contact/contact.component.scss index 5b76c9a4..7e29d02c 100644 --- a/src/app/core/components/contact/contact.component.scss +++ b/src/app/core/components/contact/contact.component.scss @@ -8,11 +8,6 @@ h1 { background-color: white; } -h2 { - font-size: 1.6rem; - margin-bottom: 2rem; -} - h3 { margin-top: 0; margin-bottom: 0; @@ -22,15 +17,28 @@ h3 { .contact-form-container { background-color: white; padding: 0 1rem 0 1rem; - margin-left: 1.5rem; - margin-right: 1.5rem; - margin-bottom: 1.25rem; + margin: 1.5rem; + .columns:first-of-type { + margin-top: 0; + } + + @media(max-width: $tablet) { + .columns { + .column:last-of-type { + border-top: solid 2px #f2f2f2; + } + } + } @media(min-width: $tablet) { .fields-container { width: 66%; } + + .columns { + border-bottom: solid 2px #f2f2f2; + } } label { @@ -42,6 +50,10 @@ h3 { } } + .fullname { + font-weight: bold; + } + .incorrect-field-message { color: #333745; font-style: italic; diff --git a/src/app/core/components/contact/contact.component.spec.ts b/src/app/core/components/contact/contact.component.spec.ts index c96cc896..0e25bf27 100644 --- a/src/app/core/components/contact/contact.component.spec.ts +++ b/src/app/core/components/contact/contact.component.spec.ts @@ -2,7 +2,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { ContactComponent } from './contact.component'; import { ReactiveFormsModule, FormControl, AbstractControl } from '@angular/forms'; -import { EmailService, NotificationService, NavigationHistoryService } from '../../services'; +import { EmailService, NotificationService, NavigationHistoryService, AuthService } from '../../services'; import { BehaviorSubject, of } from 'rxjs'; import { Email } from '../../models'; import { RouterTestingModule } from '@angular/router/testing'; @@ -33,6 +33,16 @@ export class EmailServiceMock { } +export class AuthServiceMock { + + constructor() { } + + get userIsSignedIn() { + return false; + } + +} + describe('ContactComponent', () => { let component: ContactComponent; let fixture: ComponentFixture<ContactComponent>; @@ -55,6 +65,10 @@ describe('ContactComponent', () => { provide: EmailService, useClass: EmailServiceMock, }, + { + provide: AuthService, + useClass: AuthServiceMock, + }, { provide: NotificationService, useClass: NotificationServiceMock, diff --git a/src/app/core/components/contact/contact.component.ts b/src/app/core/components/contact/contact.component.ts index e06099f8..92cd6947 100644 --- a/src/app/core/components/contact/contact.component.ts +++ b/src/app/core/components/contact/contact.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; -import { EmailService, NotificationService, NavigationHistoryService } from '../../services'; -import { Email } from '../../models'; +import { EmailService, NotificationService, NavigationHistoryService, AuthService } from '../../services'; +import { Email, User } from '../../models'; import { subjects as Subjects, feedbackMessages } from '../../../../i18n/contact/contact'; import { Router, ActivatedRoute } from '@angular/router'; import { AppRoutes } from '../../../routes'; @@ -17,6 +17,7 @@ export class ContactComponent implements OnInit { subjectDropdownState = false; subjects: Subject[] = Subjects; // Keep this line in order to access the subjects in the html template selectedSubject: Subject = null; + user: User = null; constructor( private _fb: FormBuilder, @@ -25,6 +26,7 @@ export class ContactComponent implements OnInit { private _navigationHistoryService: NavigationHistoryService, private _router: Router, private _route: ActivatedRoute, + private _authService: AuthService, ) { this.form = this._fb.group( { @@ -43,6 +45,16 @@ export class ContactComponent implements OnInit { } ngOnInit() { + // if user authenticated then initialize the form with its name and email + if (this._authService.userIsSignedIn) { + this.user = this._authService.user; + this.form.controls.firstname.patchValue(this.user.firstname); + this.form.controls.lastname.patchValue(this.user.lastname); + this.form.controls.email.patchValue(this.user.email); + this.form.controls.emailConfirmation.patchValue(this.user.email); + } else { + this.user = null; + } this._route.queryParams.subscribe((params) => { if (params.subject) { this.selectedSubject = this.subjects.find(sub => sub.key === 'other'); @@ -153,6 +165,10 @@ export class ContactComponent implements OnInit { return (this.selectedSubject && this.selectedSubject.key) === 'other' ? true : false; } + get userIsSignedIn() { + return this._authService.userIsSignedIn; + } + toUppercase(controlName: string) { const input = this.form.controls[controlName].value; const uppercased = input.substring(0, 1).toUpperCase() + input.substring(1); diff --git a/src/app/core/components/header/header.component.spec.ts b/src/app/core/components/header/header.component.spec.ts index c39130af..bb988afa 100644 --- a/src/app/core/components/header/header.component.spec.ts +++ b/src/app/core/components/header/header.component.spec.ts @@ -13,7 +13,7 @@ export class AuthServiceMock { constructor() { } - userIsSignedIn() { + get userIsSignedIn() { return false; } @@ -65,7 +65,7 @@ describe('HeaderComponent', () => { } as Router; const authServiceMock = { - userIsSignedIn: () => { + get userIsSignedIn() { return false; }, } as AuthService; diff --git a/src/app/core/components/header/header.component.ts b/src/app/core/components/header/header.component.ts index ab8f8a53..e17ac246 100644 --- a/src/app/core/components/header/header.component.ts +++ b/src/app/core/components/header/header.component.ts @@ -33,7 +33,7 @@ export class HeaderComponent implements OnInit { } get userIsSignedIn() { - return this._authService.userIsSignedIn(); + return this._authService.userIsSignedIn; } get username() { diff --git a/src/app/core/components/login/login.component.spec.ts b/src/app/core/components/login/login.component.spec.ts index 94ec03e7..5a44fe2c 100644 --- a/src/app/core/components/login/login.component.spec.ts +++ b/src/app/core/components/login/login.component.spec.ts @@ -9,7 +9,7 @@ export class AuthServiceMock { constructor() { } - userIsSignedIn() { + get userIsSignedIn() { return false; } diff --git a/src/app/core/models/auth.model.ts b/src/app/core/models/auth.model.ts index 6de44da6..c279c0d8 100644 --- a/src/app/core/models/auth.model.ts +++ b/src/app/core/models/auth.model.ts @@ -2,6 +2,7 @@ export class User { id: number; firstname: string; lastname: string; + email: string; // payload is the decrypted payload of the JWT token constructor(payload) { @@ -9,6 +10,7 @@ export class User { this.id = payload.id; this.firstname = payload.firstname; this.lastname = payload.lastname; + this.email = payload.email; } } } diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts index 30ed859e..4bf38b2d 100644 --- a/src/app/core/services/auth.service.ts +++ b/src/app/core/services/auth.service.ts @@ -95,7 +95,7 @@ export class AuthService { return this._user; } - userIsSignedIn() { + get userIsSignedIn() { return this._user != null; } } diff --git a/src/i18n/messages.en.xlf b/src/i18n/messages.en.xlf index de3129be..cfcee6c0 100644 --- a/src/i18n/messages.en.xlf +++ b/src/i18n/messages.en.xlf @@ -307,10 +307,6 @@ <source>Contact us</source> <target>Contact us</target> </trans-unit> - <trans-unit id="contact.contactForm" datatype="html"> - <source>Form contact</source> - <target>Form contact</target> - </trans-unit> <trans-unit id="contact.identity" datatype="html"> <source>Identity</source> <target>Identity</target> diff --git a/src/i18n/messages.fr.xlf b/src/i18n/messages.fr.xlf index 2e716c42..199e92a9 100644 --- a/src/i18n/messages.fr.xlf +++ b/src/i18n/messages.fr.xlf @@ -315,10 +315,6 @@ <source>Contact us</source> <target>Contactez nous</target> </trans-unit> - <trans-unit id="contact.contactForm" datatype="html"> - <source>Form contact</source> - <target>Formulaire de contact</target> - </trans-unit> <trans-unit id="contact.identity" datatype="html"> <source>Identity</source> <target>Identité</target> -- GitLab