From 6e241f817d04100b0d15cb1ba14803ff9c381a2f Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 13 Jan 2022 11:30:17 +0100 Subject: [PATCH 1/9] add contact form --- src/app/app-routing.module.ts | 5 + src/app/app.module.ts | 2 + src/app/contact/contact.component.html | 124 ++++++++++++++++++++++ src/app/contact/contact.component.scss | 52 +++++++++ src/app/contact/contact.component.spec.ts | 24 +++++ src/app/contact/contact.component.ts | 52 +++++++++ src/app/footer/footer.component.html | 2 +- src/app/models/contact-message.model.ts | 9 ++ src/app/services/auth.service.ts | 4 + src/app/services/contact.service.ts | 15 +++ src/app/utils/utils.ts | 17 +++ 11 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 src/app/contact/contact.component.html create mode 100644 src/app/contact/contact.component.scss create mode 100644 src/app/contact/contact.component.spec.ts create mode 100644 src/app/contact/contact.component.ts create mode 100644 src/app/models/contact-message.model.ts create mode 100644 src/app/services/contact.service.ts create mode 100644 src/app/utils/utils.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index d4fdc3edb..4aa08599e 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -1,6 +1,7 @@ import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { AboutComponent } from './about/about.component'; +import { ContactComponent } from './contact/contact.component'; import { FormComponent } from './form/structure-form/form.component'; import { AdminGuard } from './guards/admin.guard'; import { AuthGuard } from './guards/auth.guard'; @@ -48,6 +49,10 @@ const routes: Routes = [ path: 'about', component: AboutComponent, }, + { + path: 'contact', + component: ContactComponent, + }, { path: 'users/verify/:id', component: UserVerificationComponent, diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4111e2b27..9315cfea2 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -19,6 +19,7 @@ import { StructureOpeningStatusComponent } from './structure-list/components/str import { ModalFilterComponent } from './structure-list/components/modal-filter/modal-filter.component'; import { LegalNoticeComponent } from './legal-notice/legal-notice.component'; import { AboutComponent } from './about/about.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'; @@ -53,6 +54,7 @@ import { RoleGuard } from './guards/role.guard'; StructureOpeningStatusComponent, LegalNoticeComponent, AboutComponent, + ContactComponent, UserVerificationComponent, ResetEmailComponent, ResetPasswordComponent, diff --git a/src/app/contact/contact.component.html b/src/app/contact/contact.component.html new file mode 100644 index 000000000..d95745360 --- /dev/null +++ b/src/app/contact/contact.component.html @@ -0,0 +1,124 @@ +<div fxLayout="column" class="form content-container full-screen"> + <div class="section-container"> + <div class="contactForm"> + <h2>Nous contacter</h2> + <form [formGroup]="contactForm" (ngSubmit)="onSubmit()"> + <div class="form-group"> + <label for="name">Nom et prénom</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="name" class="form-input" /> + <app-svg-icon + *ngIf="contactForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('name').value && !contactForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group"> + <label for="email">Adresse mail</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="email" class="form-input" /> + <app-svg-icon + *ngIf="contactForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('email').value && !contactForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group"> + <label for="phone">N° de téléphone</label> + <p class="notRequired">facultatif</p> + <div fxLayout="row" fxLayoutGap="15px"> + <input + type="text" + autocomplete="on" + formControlName="phone" + class="form-input phone" + (input)="utils.modifyPhoneInput(contactForm, 'phone', $event.target.value)" + /> + <app-svg-icon + *ngIf="contactForm.get('phone').value && contactForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('phone').value && !contactForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group"> + <label for="subject">Objet du message</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="subject" class="form-input subject" /> + <app-svg-icon + *ngIf="contactForm.get('subject').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('subject').value && !contactForm.get('subject').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + </div> + + <div class="form-group" fx-layout="column"> + <label for="message">Message</label> + <div class="textareaBlock" fxLayout="row" fxLayoutGap="15px"> + <textarea + rows="8" + placeholder="Exemple : J'aimerais avoir de l'aide sur Rés'IN." + maxlength="500" + formControlName="message" + ></textarea> + <app-svg-icon + *ngIf="contactForm.get('message').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('message').value && !contactForm.get('message').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + <p>{{ contactForm.get('message').value ? contactForm.get('message').value.length : 0 }}/500</p> + + <div class="button" fxLayout="row" fxLayoutAlign="space-around center"> + <a routerLink="../home" class="btn btn-link">Annuler</a> + <button type="submit" class="btn btn-primary" [ngClass]="{ invalid: !contactForm.valid || loading }"> + <span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span> + Envoyer + </button> + </div> + </div> + </form> + </div> + </div> +</div> diff --git a/src/app/contact/contact.component.scss b/src/app/contact/contact.component.scss new file mode 100644 index 000000000..10651831a --- /dev/null +++ b/src/app/contact/contact.component.scss @@ -0,0 +1,52 @@ +@import '../../assets/scss/color'; +@import '../../assets/scss/layout'; +@import '../../assets/scss/breakpoint'; +@import '../../assets/scss/typography'; +@import '../../assets/scss/shapes'; +@import '../../assets/scss/z-index'; + +.section-container { + max-width: 600px; +} +.form { + background: white; +} +.form-group { + margin-bottom: 26px; + color: #4f4f4f; +} +.form-input { + width: 100%; + height: 40px; + margin-top: 4px; + width: 296px; +} +.phone { + width: 200px; +} +.subject { + width: 100%; +} +.textareaBlock { + flex-direction: column; + box-sizing: border-box; + display: flex; + textarea { + padding: 13px 8px; + background: #f8f8f8; + border: 1px solid #bdbdbd; + border-radius: 1px; + resize: none; + font-family: 'Trebuchet MS', 'Helvetica', sans-serif; + width: 100%; + } +} +p { + text-align: right; +} +p.notRequired { + font-style: italic; + color: #348899; + text-align: left; + margin: 2px 0; +} diff --git a/src/app/contact/contact.component.spec.ts b/src/app/contact/contact.component.spec.ts new file mode 100644 index 000000000..c74f0165b --- /dev/null +++ b/src/app/contact/contact.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ContactComponent } from './contact.component'; + +describe('ContactComponent', () => { + let component: ContactComponent; + let fixture: ComponentFixture<ContactComponent>; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ContactComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ContactComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/contact/contact.component.ts b/src/app/contact/contact.component.ts new file mode 100644 index 000000000..db5605117 --- /dev/null +++ b/src/app/contact/contact.component.ts @@ -0,0 +1,52 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; +import { AuthService } from '../services/auth.service'; +import { ContactService } from '../services/contact.service'; +import { CustomRegExp } from '../utils/CustomRegExp'; +import { Utils } from '../utils/utils'; + +@Component({ + selector: 'app-contact', + templateUrl: './contact.component.html', + styleUrls: ['./contact.component.scss'], +}) +export class ContactComponent implements OnInit { + public contactForm: FormGroup; + public loading = false; + + constructor( + private formBuilder: FormBuilder, + private contactService: ContactService, + private router: Router, + private authService: AuthService, + public utils: Utils + ) {} + + ngOnInit(): void { + this.contactForm = this.formBuilder.group({ + name: [ + this.isLoggedIn ? this.displayFullname : '', + [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)], + ], + phone: ['', [Validators.pattern(CustomRegExp.PHONE)]], + email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + subject: ['', Validators.required], + message: ['', Validators.required], + }); + } + + public get isLoggedIn(): boolean { + return this.authService.isLoggedIn(); + } + public get displayFullname(): string { + return this.authService.getUsernameDisplay() + ' ' + this.authService.getUsersurnameDisplay(); + } + + public onSubmit(): void { + this.loading = true; + if (!this.contactForm.valid) { + return; + } + } +} diff --git a/src/app/footer/footer.component.html b/src/app/footer/footer.component.html index b6de16278..e778892c8 100644 --- a/src/app/footer/footer.component.html +++ b/src/app/footer/footer.component.html @@ -3,7 +3,7 @@ <a class="clickable text-align-center" routerLink="/legal-notice" i18n>Mentions légales</a> <a class="clickable text-align-center" routerLink="/newsletter" i18n>Newsletter</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 class="clickable text-align-center" routerLink="/contact" i18n>Contact</a> </div> <a class="metro-link" diff --git a/src/app/models/contact-message.model.ts b/src/app/models/contact-message.model.ts new file mode 100644 index 000000000..c2b4ea2be --- /dev/null +++ b/src/app/models/contact-message.model.ts @@ -0,0 +1,9 @@ +export class ContactMessage { + public _id: string = null; + public name: string = null; + public surname: string = null; + public email: string = null; + public phone: string = null; + public subject: string = null; + public message: string = null; +} diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index ba2da5e75..c55edfa89 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -46,6 +46,10 @@ export class AuthService { return `${this.userValue.name}`; } + public getUsersurnameDisplay(): string { + return `${this.userValue.surname}`; + } + private getExpiration(): DateTime { return DateTime.fromISO(this.userValue.expiresAt, { zone: 'Europe/Paris' }); } diff --git a/src/app/services/contact.service.ts b/src/app/services/contact.service.ts new file mode 100644 index 000000000..8fb8cd523 --- /dev/null +++ b/src/app/services/contact.service.ts @@ -0,0 +1,15 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable, of } from 'rxjs'; +import { ContactMessage } from '../models/contact-message.model'; + +@Injectable({ + providedIn: 'root', +}) +export class ContactService { + constructor(private http: HttpClient) {} + + public sendMessage(contactMessage: ContactMessage): Observable<any> { + return this.http.post('/api/contact/message', { contactMessage }); + } +} diff --git a/src/app/utils/utils.ts b/src/app/utils/utils.ts new file mode 100644 index 000000000..e37ab99dd --- /dev/null +++ b/src/app/utils/utils.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +@Injectable({ + providedIn: 'root', +}) +export class Utils { + public modifyPhoneInput(form: FormGroup, controlName: string, phoneNumber: string): void { + // Take length of phone number without spaces. + const phoneNoSpace = phoneNumber.replace(/\s/g, ''); + // Check to refresh every 2 number. + if (phoneNoSpace.length % 2 === 0) { + // Add space every 2 number + form.get(controlName).setValue(phoneNoSpace.replace(/(?!^)(?=(?:\d{2})+$)/g, ' ')); //NOSONAR + } + } +} -- GitLab From c82973b9414f9b8f02d184f865e17e491201b1a3 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Thu, 13 Jan 2022 12:04:32 +0100 Subject: [PATCH 2/9] form style adjustments --- src/app/contact/contact.component.scss | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/app/contact/contact.component.scss b/src/app/contact/contact.component.scss index 10651831a..ed3b5648c 100644 --- a/src/app/contact/contact.component.scss +++ b/src/app/contact/contact.component.scss @@ -6,7 +6,7 @@ @import '../../assets/scss/z-index'; .section-container { - max-width: 600px; + max-width: 960px; } .form { background: white; @@ -25,7 +25,7 @@ width: 200px; } .subject { - width: 100%; + width: 600px; } .textareaBlock { flex-direction: column; @@ -39,10 +39,15 @@ resize: none; font-family: 'Trebuchet MS', 'Helvetica', sans-serif; width: 100%; + &:focus { + border: 1px solid $blue; + outline: none !important; + } } } p { text-align: right; + width: 96%; } p.notRequired { font-style: italic; -- GitLab From a536eeaa3ac5296bddd5ceeddea3ac0c7fef3364 Mon Sep 17 00:00:00 2001 From: Etienne LOUPIAS <eloupias@grandlyon.com> Date: Thu, 13 Jan 2022 17:14:05 +0100 Subject: [PATCH 3/9] feat(contact): add toastr --- package-lock.json | 8 +++++++ package.json | 1 + src/app/app.module.ts | 4 +++- src/app/contact/contact.component.ts | 18 +++++++++++++- src/app/services/notification.service.spec.ts | 16 +++++++++++++ src/app/services/notification.service.ts | 24 +++++++++++++++++++ src/styles.scss | 1 + 7 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/app/services/notification.service.spec.ts create mode 100644 src/app/services/notification.service.ts diff --git a/package-lock.json b/package-lock.json index 3bc108ad8..71482cf7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12046,6 +12046,14 @@ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", "dev": true }, + "ngx-toastr": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-13.2.1.tgz", + "integrity": "sha512-UAzp7/xWK9IXA2LsOmhpaaIGCqscvJokoQpBNpAMrjEkDeSlFf8PWQAuQY795KW0mJb3qF9UG/s23nsXfMYKmg==", + "requires": { + "tslib": "^2.0.0" + } + }, "nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", diff --git a/package.json b/package.json index d8b19aab4..6c63e6d1d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "leaflet.locatecontrol": "^0.72.0", "lodash": "^4.17.20", "luxon": "^1.25.0", + "ngx-toastr": "^13.2.1", "npx": "^10.2.2", "rxjs": "~6.6.0", "tslib": "^2.0.0", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9315cfea2..0a62423ac 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,8 @@ import { LOCALE_ID, NgModule } from '@angular/core'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { ToastrModule } from 'ngx-toastr'; import { AppRoutingModule } from './app-routing.module'; @@ -67,7 +69,7 @@ import { RoleGuard } from './guards/role.guard'; StructureListPrintComponent, StructurePrintHeaderComponent, ], - imports: [BrowserModule, HttpClientModule, AppRoutingModule, SharedModule, MapModule], + imports: [BrowserModule, HttpClientModule, AppRoutingModule, SharedModule, MapModule, BrowserAnimationsModule, ToastrModule.forRoot()], providers: [ { provide: LOCALE_ID, useValue: 'fr' }, { provide: HTTP_INTERCEPTORS, useClass: CustomHttpInterceptor, multi: true }, diff --git a/src/app/contact/contact.component.ts b/src/app/contact/contact.component.ts index db5605117..b9159ea17 100644 --- a/src/app/contact/contact.component.ts +++ b/src/app/contact/contact.component.ts @@ -1,8 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router } from '@angular/router'; +import { ContactMessage } from '../models/contact-message.model'; import { AuthService } from '../services/auth.service'; import { ContactService } from '../services/contact.service'; +import { NotificationService } from '../services/notification.service'; import { CustomRegExp } from '../utils/CustomRegExp'; import { Utils } from '../utils/utils'; @@ -20,6 +22,7 @@ export class ContactComponent implements OnInit { private contactService: ContactService, private router: Router, private authService: AuthService, + private notificationService: NotificationService, public utils: Utils ) {} @@ -44,9 +47,22 @@ export class ContactComponent implements OnInit { } public onSubmit(): void { - this.loading = true; if (!this.contactForm.valid) { return; } + this.loading = true; + + let contactMessage: ContactMessage = this.contactForm.value; + this.contactService.sendMessage(contactMessage).subscribe( + () => { + this.loading = false; + this.notificationService.showSuccess("Votre message a bien été envoyé", "Demande de contact"); + this.router.navigate(['']); + }, + () => { + this.loading = false; + this.notificationService.showError("Merci de réessayer plus tard", "Votre demande de contact n'a pas pu être envoyée"); + } + ); } } diff --git a/src/app/services/notification.service.spec.ts b/src/app/services/notification.service.spec.ts new file mode 100644 index 000000000..c4f2cd67e --- /dev/null +++ b/src/app/services/notification.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { NotificationService } from './notification.service'; + +describe('NotificationService', () => { + let service: NotificationService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(NotificationService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts new file mode 100644 index 000000000..69c35723b --- /dev/null +++ b/src/app/services/notification.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ToastrService } from 'ngx-toastr'; + +@Injectable({ + providedIn: 'root' +}) +export class NotificationService { + + constructor(private toastr: ToastrService) { } + + showSuccess(message: string, title: string, timespan: number = 5000) { + this.toastr.success(message, title, { + timeOut: timespan + }) + } + + // Par defaut, l'erreur reste affichée jusqu'à ce qu'on clique dessus + showError(message: string, title: string, timespan: number = 0) { + this.toastr.error(message, title, { + timeOut: timespan, + disableTimeOut: timespan?false:true + }) + } +} \ No newline at end of file diff --git a/src/styles.scss b/src/styles.scss index 1e80fc9f4..a40fa2404 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -10,6 +10,7 @@ @import 'assets/scss/layout'; @import 'assets/scss/buttons'; @import '../node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css'; +@import '~ngx-toastr/toastr'; html { height: -webkit-fill-available; -- GitLab From bbab2aff4208cebf132a0f24cbbcc8b1d4138630 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Fri, 14 Jan 2022 10:18:07 +0100 Subject: [PATCH 4/9] fix(contact): fix name label --- src/app/contact/contact.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/contact/contact.component.html b/src/app/contact/contact.component.html index d95745360..087dec033 100644 --- a/src/app/contact/contact.component.html +++ b/src/app/contact/contact.component.html @@ -4,7 +4,7 @@ <h2>Nous contacter</h2> <form [formGroup]="contactForm" (ngSubmit)="onSubmit()"> <div class="form-group"> - <label for="name">Nom et prénom</label> + <label for="name">Prénom et nom</label> <div fxLayout="row" fxLayoutGap="15px"> <input type="text" autocomplete="on" formControlName="name" class="form-input" /> <app-svg-icon -- GitLab From beeafbd2fd521d5169c3bf76b42791c28149476a Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Fri, 14 Jan 2022 10:30:45 +0100 Subject: [PATCH 5/9] refacto: modifyPhoneInput moved to utils --- src/app/form/structure-form/form.component.html | 4 ++-- src/app/form/structure-form/form.component.ts | 16 ++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/src/app/form/structure-form/form.component.html b/src/app/form/structure-form/form.component.html index e4083fe48..5f39aa2aa 100644 --- a/src/app/form/structure-form/form.component.html +++ b/src/app/form/structure-form/form.component.html @@ -179,7 +179,7 @@ type="text" formControlName="phone" class="form-input phone" - (input)="modifyPhoneInput(accountForm, 'phone', $event.target.value)" + (input)="utils.modifyPhoneInput(accountForm, 'phone', $event.target.value)" /> <app-svg-icon *ngIf="accountForm.get('phone').valid" @@ -436,7 +436,7 @@ type="text" formControlName="contactPhone" class="form-input" - (input)="modifyPhoneInput(structureForm, 'contactPhone', $event.target.value)" + (input)="utils.modifyPhoneInput(structureForm, 'contactPhone', $event.target.value)" /> <app-svg-icon *ngIf="getStructureControl('contactPhone').valid" diff --git a/src/app/form/structure-form/form.component.ts b/src/app/form/structure-form/form.component.ts index 1fd107605..f2e7740a6 100644 --- a/src/app/form/structure-form/form.component.ts +++ b/src/app/form/structure-form/form.component.ts @@ -21,6 +21,8 @@ import { CustomRegExp } from '../../utils/CustomRegExp'; import { StructureWithOwners } from '../../models/structureWithOwners.model'; import { RouterListenerService } from '../../services/routerListener.service'; import { NewsletterService } from '../../services/newsletter.service'; +import { Utils } from '../../utils/utils'; + @Component({ selector: 'app-structure-form', templateUrl: './form.component.html', @@ -90,7 +92,8 @@ export class FormComponent implements OnInit { private router: Router, private route: ActivatedRoute, private routerListener: RouterListenerService, - private newsletterService: NewsletterService + private newsletterService: NewsletterService, + public utils: Utils ) {} async ngOnInit(): Promise<void> { @@ -409,17 +412,6 @@ export class FormComponent implements OnInit { return this.structureForm.get('address').get(nameControl); } - public modifyPhoneInput(form: FormGroup, controlName: string, phoneNumber: string): void { - // Take length of phone number without spaces. - const phoneNoSpace = phoneNumber.replace(/\s/g, ''); - // Check to refresh every 2 number. - if (phoneNoSpace.length % 2 === 0) { - // Add space every 2 number - form.get(controlName).setValue(phoneNoSpace.replace(/(?!^)(?=(?:\d{2})+$)/g, ' ')); //NOSONAR - } - this.setValidationsForm(); - } - private createDay(day: Day): FormGroup { return new FormGroup({ open: new FormControl(day.open, Validators.required), -- GitLab From c3dd6ee9932e08d9e051640c2dee01c9dc24d09c Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Fri, 14 Jan 2022 10:52:38 +0100 Subject: [PATCH 6/9] feat(contact): autocomplete email if logged in --- src/app/contact/contact.component.ts | 12 +++++++++--- src/app/services/auth.service.ts | 4 ++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/app/contact/contact.component.ts b/src/app/contact/contact.component.ts index b9159ea17..367ec8eca 100644 --- a/src/app/contact/contact.component.ts +++ b/src/app/contact/contact.component.ts @@ -33,7 +33,7 @@ export class ContactComponent implements OnInit { [Validators.required, Validators.pattern(CustomRegExp.TEXT_WITHOUT_NUMBER)], ], phone: ['', [Validators.pattern(CustomRegExp.PHONE)]], - email: ['', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], + email: [this.isLoggedIn ? this.displayEmail : '', [Validators.required, Validators.pattern(CustomRegExp.EMAIL)]], subject: ['', Validators.required], message: ['', Validators.required], }); @@ -45,6 +45,9 @@ export class ContactComponent implements OnInit { public get displayFullname(): string { return this.authService.getUsernameDisplay() + ' ' + this.authService.getUsersurnameDisplay(); } + public get displayEmail(): string { + return this.authService.getUserEmailDisplay(); + } public onSubmit(): void { if (!this.contactForm.valid) { @@ -56,12 +59,15 @@ export class ContactComponent implements OnInit { this.contactService.sendMessage(contactMessage).subscribe( () => { this.loading = false; - this.notificationService.showSuccess("Votre message a bien été envoyé", "Demande de contact"); + this.notificationService.showSuccess('Votre message a bien été envoyé', 'Demande de contact'); this.router.navigate(['']); }, () => { this.loading = false; - this.notificationService.showError("Merci de réessayer plus tard", "Votre demande de contact n'a pas pu être envoyée"); + this.notificationService.showError( + 'Merci de réessayer plus tard', + "Votre demande de contact n'a pas pu être envoyée" + ); } ); } diff --git a/src/app/services/auth.service.ts b/src/app/services/auth.service.ts index c55edfa89..3b19a0dbd 100644 --- a/src/app/services/auth.service.ts +++ b/src/app/services/auth.service.ts @@ -50,6 +50,10 @@ export class AuthService { return `${this.userValue.surname}`; } + public getUserEmailDisplay(): string { + return `${this.userValue.username}`; + } + private getExpiration(): DateTime { return DateTime.fromISO(this.userValue.expiresAt, { zone: 'Europe/Paris' }); } -- GitLab From c087926e7fa93dcc8006e4f7ecd46646a1ab419a Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Mon, 17 Jan 2022 15:09:01 +0100 Subject: [PATCH 7/9] fix(contact): fix typo, toastr success timespan, maxlength on subject --- src/app/contact/contact.component.html | 4 ++-- src/app/services/notification.service.ts | 19 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/app/contact/contact.component.html b/src/app/contact/contact.component.html index 087dec033..031c046ea 100644 --- a/src/app/contact/contact.component.html +++ b/src/app/contact/contact.component.html @@ -4,7 +4,7 @@ <h2>Nous contacter</h2> <form [formGroup]="contactForm" (ngSubmit)="onSubmit()"> <div class="form-group"> - <label for="name">Prénom et nom</label> + <label for="name">Prénom et Nom</label> <div fxLayout="row" fxLayoutGap="15px"> <input type="text" autocomplete="on" formControlName="name" class="form-input" /> <app-svg-icon @@ -70,7 +70,7 @@ <div class="form-group"> <label for="subject">Objet du message</label> <div fxLayout="row" fxLayoutGap="15px"> - <input type="text" autocomplete="on" formControlName="subject" class="form-input subject" /> + <input type="text" maxlength="100" formControlName="subject" class="form-input subject" /> <app-svg-icon *ngIf="contactForm.get('subject').valid" [iconClass]="'validation'" diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts index 69c35723b..d409e9e3e 100644 --- a/src/app/services/notification.service.ts +++ b/src/app/services/notification.service.ts @@ -2,23 +2,22 @@ import { Injectable } from '@angular/core'; import { ToastrService } from 'ngx-toastr'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class NotificationService { + constructor(private toastr: ToastrService) {} - constructor(private toastr: ToastrService) { } - - showSuccess(message: string, title: string, timespan: number = 5000) { - this.toastr.success(message, title, { - timeOut: timespan - }) + showSuccess(message: string, title: string, timespan: number = 10000) { + this.toastr.success(message, title, { + timeOut: timespan, + }); } // Par defaut, l'erreur reste affichée jusqu'à ce qu'on clique dessus showError(message: string, title: string, timespan: number = 0) { this.toastr.error(message, title, { timeOut: timespan, - disableTimeOut: timespan?false:true - }) + disableTimeOut: timespan ? false : true, + }); } -} \ No newline at end of file +} -- GitLab From deaa3f06c92a6f2b90c22cdf8e57f88d6e73b892 Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Wed, 19 Jan 2022 11:47:17 +0100 Subject: [PATCH 8/9] Fix(contact):fix notif service, fix css to match new design, refactor form styles --- src/app/contact/contact.component.html | 213 +++++++++--------- src/app/contact/contact.component.scss | 42 ++-- .../form/structure-form/form.component.scss | 18 +- src/app/services/notification.service.ts | 4 +- src/styles.scss | 30 ++- 5 files changed, 154 insertions(+), 153 deletions(-) diff --git a/src/app/contact/contact.component.html b/src/app/contact/contact.component.html index 031c046ea..500329f5a 100644 --- a/src/app/contact/contact.component.html +++ b/src/app/contact/contact.component.html @@ -1,123 +1,124 @@ <div fxLayout="column" class="form content-container full-screen"> <div class="section-container"> <div class="contactForm"> - <h2>Nous contacter</h2> <form [formGroup]="contactForm" (ngSubmit)="onSubmit()"> - <div class="form-group"> - <label for="name">Prénom et Nom</label> - <div fxLayout="row" fxLayoutGap="15px"> - <input type="text" autocomplete="on" formControlName="name" class="form-input" /> - <app-svg-icon - *ngIf="contactForm.get('name').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'validate'" - ></app-svg-icon> - <app-svg-icon - *ngIf="contactForm.get('name').value && !contactForm.get('name').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> + <div class="form-fields"> + <h2>Nous contacter</h2> + <div class="form-group"> + <label for="name">Prénom et Nom</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="name" class="form-input" /> + <app-svg-icon + *ngIf="contactForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('name').value && !contactForm.get('name').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> </div> - </div> - <div class="form-group"> - <label for="email">Adresse mail</label> - <div fxLayout="row" fxLayoutGap="15px"> - <input type="text" autocomplete="on" formControlName="email" class="form-input" /> - <app-svg-icon - *ngIf="contactForm.get('email').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'validate'" - ></app-svg-icon> - <app-svg-icon - *ngIf="contactForm.get('email').value && !contactForm.get('email').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> + <div class="form-group"> + <label for="email">Adresse mail</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" autocomplete="on" formControlName="email" class="form-input" /> + <app-svg-icon + *ngIf="contactForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('email').value && !contactForm.get('email').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> </div> - </div> - <div class="form-group"> - <label for="phone">N° de téléphone</label> - <p class="notRequired">facultatif</p> - <div fxLayout="row" fxLayoutGap="15px"> - <input - type="text" - autocomplete="on" - formControlName="phone" - class="form-input phone" - (input)="utils.modifyPhoneInput(contactForm, 'phone', $event.target.value)" - /> - <app-svg-icon - *ngIf="contactForm.get('phone').value && contactForm.get('phone').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'validate'" - ></app-svg-icon> - <app-svg-icon - *ngIf="contactForm.get('phone').value && !contactForm.get('phone').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> + <div class="form-group"> + <label for="phone">N° de téléphone</label> + <p class="notRequired">facultatif</p> + <div fxLayout="row" fxLayoutGap="15px"> + <input + type="text" + autocomplete="on" + formControlName="phone" + class="form-input phone" + (input)="utils.modifyPhoneInput(contactForm, 'phone', $event.target.value)" + /> + <app-svg-icon + *ngIf="contactForm.get('phone').value && contactForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('phone').value && !contactForm.get('phone').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> </div> - </div> - <div class="form-group"> - <label for="subject">Objet du message</label> - <div fxLayout="row" fxLayoutGap="15px"> - <input type="text" maxlength="100" formControlName="subject" class="form-input subject" /> - <app-svg-icon - *ngIf="contactForm.get('subject').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'validate'" - ></app-svg-icon> - <app-svg-icon - *ngIf="contactForm.get('subject').value && !contactForm.get('subject').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> + <div class="form-group"> + <label for="subject">Objet du message</label> + <div fxLayout="row" fxLayoutGap="15px"> + <input type="text" maxlength="100" formControlName="subject" class="form-input subject" /> + <app-svg-icon + *ngIf="contactForm.get('subject').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('subject').value && !contactForm.get('subject').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> </div> - </div> - <div class="form-group" fx-layout="column"> - <label for="message">Message</label> - <div class="textareaBlock" fxLayout="row" fxLayoutGap="15px"> - <textarea - rows="8" - placeholder="Exemple : J'aimerais avoir de l'aide sur Rés'IN." - maxlength="500" - formControlName="message" - ></textarea> - <app-svg-icon - *ngIf="contactForm.get('message').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'validate'" - ></app-svg-icon> - <app-svg-icon - *ngIf="contactForm.get('message').value && !contactForm.get('message').valid" - [iconClass]="'validation'" - [type]="'form'" - [icon]="'notValidate'" - ></app-svg-icon> - </div> - <p>{{ contactForm.get('message').value ? contactForm.get('message').value.length : 0 }}/500</p> - - <div class="button" fxLayout="row" fxLayoutAlign="space-around center"> - <a routerLink="../home" class="btn btn-link">Annuler</a> - <button type="submit" class="btn btn-primary" [ngClass]="{ invalid: !contactForm.valid || loading }"> - <span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span> - Envoyer - </button> + <div class="form-group" fx-layout="column"> + <label for="message">Message</label> + <div class="textareaBlock" fxLayout="row" fxLayoutGap="15px"> + <textarea + rows="8" + placeholder="Exemple : J'aimerais avoir de l'aide sur Rés'IN." + maxlength="500" + formControlName="message" + ></textarea> + <app-svg-icon + *ngIf="contactForm.get('message').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'validate'" + ></app-svg-icon> + <app-svg-icon + *ngIf="contactForm.get('message').value && !contactForm.get('message').valid" + [iconClass]="'validation'" + [type]="'form'" + [icon]="'notValidate'" + ></app-svg-icon> + </div> + <p>{{ contactForm.get('message').value ? contactForm.get('message').value.length : 0 }}/500</p> </div> </div> + <div class="button" fxLayout="row" fxLayoutAlign="center center"> + <a routerLink="../home" class="btn btn-link">Annuler</a> + <button type="submit" class="btn btn-primary" [ngClass]="{ invalid: !contactForm.valid || loading }"> + <span *ngIf="loading" class="spinner-border spinner-border-sm mr-1"></span> + Envoyer + </button> + </div> </form> </div> </div> diff --git a/src/app/contact/contact.component.scss b/src/app/contact/contact.component.scss index ed3b5648c..625934b44 100644 --- a/src/app/contact/contact.component.scss +++ b/src/app/contact/contact.component.scss @@ -5,21 +5,10 @@ @import '../../assets/scss/shapes'; @import '../../assets/scss/z-index'; -.section-container { +.form-fields { max-width: 960px; -} -.form { - background: white; -} -.form-group { - margin-bottom: 26px; - color: #4f4f4f; -} -.form-input { - width: 100%; - height: 40px; - margin-top: 4px; - width: 296px; + padding: 20px; + background: $white; } .phone { width: 200px; @@ -32,26 +21,25 @@ box-sizing: border-box; display: flex; textarea { - padding: 13px 8px; - background: #f8f8f8; - border: 1px solid #bdbdbd; - border-radius: 1px; - resize: none; - font-family: 'Trebuchet MS', 'Helvetica', sans-serif; - width: 100%; + font-family: $text-font; + width: 94%; + margin-top: 4px; &:focus { border: 1px solid $blue; outline: none !important; } } } +.button { + max-width: 960px; + padding: 35px; + gap: 8px; +} p { text-align: right; width: 96%; -} -p.notRequired { - font-style: italic; - color: #348899; - text-align: left; - margin: 2px 0; + &.notRequired { + text-align: left; + margin: 0; + } } diff --git a/src/app/form/structure-form/form.component.scss b/src/app/form/structure-form/form.component.scss index 986c73d07..1a586a844 100644 --- a/src/app/form/structure-form/form.component.scss +++ b/src/app/form/structure-form/form.component.scss @@ -186,10 +186,6 @@ h3 { color: $orange-warning; } } - &.notRequired { - font-style: italic; - color: $secondary-color; - } &.informationEndForm { margin-top: 18px; color: $grey-2; @@ -200,14 +196,6 @@ h3 { @media #{$tablet} { max-width: 90%; } - textarea { - padding: 13px 8px; - background: $grey-6; - border: 1px solid $grey-4; - border-radius: 1px; - resize: none; - @include cn-regular-16; - } p { text-align: right; } @@ -269,10 +257,6 @@ h3 { } } .form-group { - margin-bottom: 26px; - label { - color: $grey-2; - } &.facebook, &.twitter, &.instagram, @@ -314,8 +298,8 @@ h3 { } } } + input { - margin-top: 4px; &.email-placeholder::placeholder { color: #cacccb; font-style: italic; diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts index d409e9e3e..998656c9c 100644 --- a/src/app/services/notification.service.ts +++ b/src/app/services/notification.service.ts @@ -7,14 +7,14 @@ import { ToastrService } from 'ngx-toastr'; export class NotificationService { constructor(private toastr: ToastrService) {} - showSuccess(message: string, title: string, timespan: number = 10000) { + showSuccess(message: string, title: string, timespan: number = 10000): void { this.toastr.success(message, title, { timeOut: timespan, }); } // Par defaut, l'erreur reste affichée jusqu'à ce qu'on clique dessus - showError(message: string, title: string, timespan: number = 0) { + showError(message: string, title: string, timespan: number = 0): void { this.toastr.error(message, title, { timeOut: timespan, disableTimeOut: timespan ? false : true, diff --git a/src/styles.scss b/src/styles.scss index a40fa2404..921807e22 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -91,6 +91,35 @@ a { } } +// Forms +.form-group { + margin-bottom: 26px; + label { + color: $grey-2; + } +} +form p.notRequired { + font-style: italic; + color: $secondary-color; +} + +/** Inputs **/ +input { + margin-top: 4px; +} + +/** Textarea **/ +.textareaBlock { + textarea { + padding: 13px 8px; + background: $grey-6; + border: 1px solid $grey-4; + border-radius: $input-radius; + resize: none; + @include cn-regular-16; + } +} + /** Buttons **/ button { &:focus { @@ -99,7 +128,6 @@ button { } /** Checkboxes **/ - .checkbox { list-style-type: none; width: 100%; -- GitLab From f796fb396a779f488e8c4a01188c58534898517e Mon Sep 17 00:00:00 2001 From: Marlene Simondant <msimondant@grandlyon.com> Date: Wed, 19 Jan 2022 16:10:21 +0100 Subject: [PATCH 9/9] Merge one new style from branch dev into refactored form styles --- src/styles.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles.scss b/src/styles.scss index 921807e22..309c6eb52 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -99,6 +99,7 @@ a { } } form p.notRequired { + margin-top: 0px; font-style: italic; color: $secondary-color; } -- GitLab