Commit cd2dc5ec authored by FORESTIER Fabien's avatar FORESTIER Fabien
Browse files

Update contact form template + add associated logic and models and translation...

Update contact form template + add associated logic and models and translation + clickOutside directive
parent 99ce8638
......@@ -47,6 +47,10 @@
{
"replace": "src/i18n/geosource/geosource.ts",
"with": "src/i18n/geosource/geosource.fr.ts"
},
{
"replace": "src/i18n/contact/contact.ts",
"with": "src/i18n/contact/contact.fr.ts"
}
]
},
......@@ -99,6 +103,10 @@
{
"replace": "src/i18n/geosource/geosource.ts",
"with": "src/i18n/geosource/geosource.fr.ts"
},
{
"replace": "src/i18n/contact/contact.ts",
"with": "src/i18n/contact/contact.fr.ts"
}
],
"outputPath": "dist/fr",
......
<h1 class="has-text-centered">Contactez nous</h1>
<h1 class="has-text-centered" i18n="@@contact.contactUs">Contact us</h1>
<div class="container">
<h2>Formulaire de contact</h2>
<div class="section">
<h2 i18n="@@contact.contactForm">Form contact</h2>
<form [formGroup]="form" class="columns is-multiline">
<form [formGroup]="form" class="columns is-multiline" (ngSubmit)="send()">
<div class="column is-2-touch is-3">
<h3>Identité</h3>
<h3 i18n="@@contact.identity">Identity</h3>
</div>
<!-- <div class="notification is-danger" [ngStyle]="{'visibility': (errorLogin === true ? 'visible' : 'hidden')}">
<button class="delete" (click)="closeErrorMessage()"></button>
<span i18n="@@login.incorrectCredentials">Your credentials are not correct.</span>
</div> -->
<div class="column is-10-touch is-9">
<div class="fields-container">
<div class="field">
<label class="label">Nom</label>
<div class="field">
<p class="control">
<input class="input" type="text" formControlName="lastname">
</p>
<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" [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>
</div>
<div class="field">
<label class="label">Prénom</label>
<div class="field">
<p class="control">
<input class="input" type="text" formControlName="firstname">
</p>
<label class="label" for="firstname" i18n="@@contact.firstname">Firstname</label>
<p class="control has-icons-right">
<input id="firstname" class="input" type="text" 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>
</div>
<div class="field">
<label class="label">Email</label>
<div class="field">
<p class="control">
<input class="input" type="email" formControlName="email">
</p>
<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>
</div>
</div>
<!-- <div class="field">
<label class="label">Confirmez l'adresse mail</label>
<div class="field">
<p class="control">
<input class="input" type="email" formControlName="emailConfirmation">
</p>
<div class="field">
<label class="label" for="emailConfirmation" i18n="@@contact.emailConfirmation">Confirm your email address</label>
<p class="control has-icons-right">
<input 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>
</div>
</div> -->
<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>
<div class="column is-2-touch is-3">
<h3>Identité</h3>
<h3 i18n="@@contact.message">Message</h3>
</div>
<div class="column is-10-touch is-9">
<div class="fields-container">
<div class="field">
<label class="label">Sujet</label>
<div class="field">
<p class="control">
<input class="input" type="text" formControlName="subject">
</p>
<label class="label" [for]="subjectLabelFor" i18n="@@contact.subject">Sujet</label>
<div class="dropdown" [ngClass]="{'is-active': subjectDropdownState}" (clickOutside)="closeSubjectDropdown()">
<div class="dropdown-trigger" (click)="toggleSubject()">
<button id="subjectDropdown" type="button" class="button" aria-haspopup="true" aria-controls="dropdown-menu">
<span>{{ selectedSubject !== null && selectedSubject.value !== null ? selectedSubject.value : '---'}}</span>
<span class="icon is-small">
<i class="fas fa-angle-down" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<ul class="dropdown-content">
<li class="dropdown-item" *ngFor="let sub of subjects" (click)="setSubject(sub)">
{{ sub.value }}
</li>
</ul>
</div>
</div>
<p class="control has-icons-right subject-input-control" *ngIf="displaySubjectInput === true">
<input id="subjectInput" class="input" type="text" formControlName="subject" [ngClass]="{'is-danger': fieldIsInvalid('subject'), 'is-success': fieldIsValid('subject')}">
<span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('subject')">
<i class="fas fa-check-circle"></i>
</span>
<span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('subject')">
<i class="fas fa-exclamation-circle"></i>
</span>
</p>
<div class="incorrect-field-message" *ngIf="fieldIsInvalid('subject')">
<div *ngIf="form.controls['subject'].errors.required" i18n="@@contact.errors.missingSubject">
You must enter a subject.
</div>
</div>
</div>
<div class="field">
<label class="label">Message</label>
<div class="field">
<p class="control">
<textarea class="input" type="textarea" formControlName="text"></textarea>
</p>
<label class="label" for="text" i18n="@@contact.messageField">Message</label>
<p class="control has-icons-right">
<textarea id="text" class="textarea has-fixed-size" type="textarea" formControlName="text" [ngClass]="{'is-danger': fieldIsInvalid('text'), 'is-success': fieldIsValid('text')}"
rows=6></textarea>
<span class="icon is-small is-right has-text-success" *ngIf="fieldIsValid('text')">
<i class="fas fa-check-circle"></i>
</span>
<span class="icon is-small is-right has-text-danger" *ngIf="fieldIsInvalid('text')">
<i class="fas fa-exclamation-circle"></i>
</span>
</p>
<div class="incorrect-field-message" *ngIf="fieldIsInvalid('text')">
<div *ngIf="form.controls['text'].errors.required" i18n="@@contact.errors.invalidMessage">
You must enter a message.
</div>
</div>
</div>
<div class="has-text-centered button-wrapper">
<button class="button is-primary" [disabled]="form.invalid" (click)="send()">Envoyer</button>
<div class="has-text-right button-wrapper">
<button type="submit" class="button is-primary" [disabled]="formIsInvalid()" i18n="@@contact.send">Envoyer</button>
</div>
</div>
</div>
</form>
</div>
\ No newline at end of file
@import '../../../../scss/variables.scss';
.container {
margin-top: 3rem;
h3 {
margin:0;
}
h1 {
font-size: 2rem;
margin-bottom: 0;
}
h2 {
font-size: 1.6rem;
margin-bottom: 2rem;
}
h3 {
margin-top: 0;
margin-bottom: 0;
margin-right: 0;
}
.section {
.fields-container {
width: 66%;
}
label {
font-size: 1rem;
font-weight: normal;
font-style: normal;
&:hover {
cursor: pointer;
}
}
.incorrect-field-message {
color: #333745;
font-style: italic;
font-size: $size-6;
}
.subject-input-control {
margin-top: 0.5rem;
}
.dropdown-item:hover {
cursor: pointer;
background-color: lightgrey;
}
}
.title-label.is-danger {
......
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { EmailService } from '../../services';
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
import { EmailService, NotificationService } from '../../services';
import { Email } from '../../models';
import { subjects as Subjects, feedbackMessages } from '../../../../i18n/contact/contact';
import { Router } from '@angular/router';
@Component({
selector: 'app-contact',
......@@ -11,20 +13,26 @@ import { Email } from '../../models';
export class ContactComponent implements OnInit {
form: FormGroup;
subjectDropdownState = false;
subjects: Subject[] = Subjects; // Keep this line in order to access the subjects in the html template
selectedSubject: Subject = null;
displaySubjectInput = false;
constructor(
private _fb: FormBuilder,
private emailService: EmailService,
private _emailService: EmailService,
private _notificationService: NotificationService,
private _router: Router,
) {
this.form = this._fb.group({
firstname: ['', Validators.required],
lastname: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
// emailConfirmation: ['', [Validators.required, Validators.email]],
subject: ['', Validators.required],
text: ['', Validators.required],
});
this.form = this._fb.group(
{
firstname: ['', Validators.required],
lastname: ['', Validators.required],
email: ['', [Validators.required, Validators.email]],
emailConfirmation: ['', [Validators.required, Validators.email]],
subject: ['', Validators.required],
text: ['', Validators.required],
});
}
ngOnInit() {
......@@ -34,13 +42,89 @@ export class ContactComponent implements OnInit {
console.log(this.form.value);
const email = new Email(this.form.value);
console.log(email);
this.emailService.send(email).subscribe(
this._emailService.send(email).subscribe(
(res) => {
console.log(res);
this._notificationService.notify(
{
type: 'success',
message: feedbackMessages.success,
},
);
this.form.reset();
},
(err) => {
console.log(err);
this._notificationService.notify(
{
type: 'error',
message: feedbackMessages.error,
},
);
},
);
}
get emailConfirmationIsCorrect(): boolean {
const value = this.form.controls.email.value === this.form.controls.emailConfirmation.value ? true : false;
console.log(value);
return value;
}
get emailConfirmationError() {
// Display the message if
// Input values have been modified
// Input values have been modified but the email values are not similar
return this.form.controls['email'].touched &&
this.form.controls['emailConfirmation'].touched &&
!this.emailConfirmationIsCorrect;
}
// Return true if one of the fields at least doesn't respect its validators
// or if the confirmation email is different from the email
formIsInvalid() {
const controls = this.form.controls;
const value = this.form.invalid || this.emailConfirmationError;
console.log('FormInvalid: ', value);
return value;
}
fieldIsInvalid(field: string) {
return (this.form.controls[field].touched) && this.form.controls[field].invalid;
}
fieldIsValid(field: string) {
return (this.form.controls[field].touched) && this.form.controls[field].valid;
}
toggleSubject() {
this.subjectDropdownState = !this.subjectDropdownState;
}
closeSubjectDropdown() {
this.subjectDropdownState = false;
}
setSubject(subject: Subject) {
this.toggleSubject();
this.selectedSubject = subject;
if (this.selectedSubject.key === 'other') {
this.form.controls.subject.patchValue('');
this.form.controls.subject.reset();
this.displaySubjectInput = true;
} else {
this.form.controls.subject.patchValue(this.selectedSubject.value);
this.displaySubjectInput = false;
}
}
get subjectLabelFor(): string {
return this.displaySubjectInput === true ? 'subjectInput' : 'subjectDropdown';
}
}
interface Subject {
key: string;
value: string;
}
......@@ -18,7 +18,6 @@ export class HttpErrorResponseInterceptor implements HttpInterceptor {
}
},
(err) => {
console.log(err);
if (err instanceof HttpErrorResponse) {
switch (err.status) {
case 401:
......
import { environment } from '../../../environments/environment';
export interface IEmail {
to: string[];
export interface IContactForm {
email: string;
emailConfirmation: string;
subject: string;
text: string;
firstname: string;
......@@ -10,16 +10,17 @@ export interface IEmail {
}
export class Email {
to: string[];
from: string;
subject: string;
firstname: string;
lastname: string;
text: string;
constructor(email: IEmail) {
this.to = [environment.emailService.contact];
this.subject = email.subject;
this.text = email.text;
this.from = `${email.firstname} ${email.lastname} ${email.email}`;
constructor(contactForm: IContactForm) {
this.subject = contactForm.subject;
this.text = contactForm.text;
this.from = contactForm.email;
this.firstname = contactForm.firstname;
this.lastname = contactForm.lastname;
}
}
import { Notification, INotification } from './notification.model';
import { IMatomoResponse } from './matomo.model';
import { IEmail, Email } from './email.model';
import { IContactForm, Email } from './email.model';
export { Notification, INotification };
export { IMatomoResponse };
export { IEmail, Email };
export { IContactForm, Email };
......@@ -11,6 +11,6 @@ export class EmailService {
) {}
send(email: Email) {
return this._httpClient.post(environment.emailService.url + '/send', email);
return this._httpClient.post(environment.emailService.url + '/contact', email);
}
}
import { Directive, ElementRef, Output, EventEmitter, HostListener } from '@angular/core';
@Directive({
selector: '[clickOutside]',
})
export class ClickOutsideDirective {
constructor(private _elementRef: ElementRef) {
}
@Output()
public clickOutside = new EventEmitter();
@HostListener('document:click', ['$event.target'])
public onClick(targetElement) {
const clickedInside = this._elementRef.nativeElement.contains(targetElement);
if (!clickedInside) {
this.clickOutside.emit(null);
}
}
}
import { PreventDefaultDirective } from './prevent-default.directive';
import { DynamicLinks } from './dynamic-links';
import { ClickOutsideDirective } from './click-outside.directive';
export { PreventDefaultDirective, DynamicLinks };
export { PreventDefaultDirective, DynamicLinks, ClickOutsideDirective };
// tslint:disable-next-line:variable-name
export const SharedDirectives = [
PreventDefaultDirective,
DynamicLinks,
ClickOutsideDirective,
];
......@@ -23,6 +23,10 @@ export const environment = {
url: servicesProxyUrl + '/loopback/api',
},
emailService: {
url: 'https://kong.alpha.grandlyon.com/email',
},
// Path to the built app in a particular language
angularAppHost: {
fr: '/fr',
......
......@@ -25,7 +25,6 @@ export const environment = {
emailService: {
url: 'https://kong.alpha.grandlyon.com/email',
contact: 'fab.forestier42@gmail.com',
},
// Path to the built app in a particular language
......
export const subjects = [
{
key: 'licenseQuestion',
value: 'question sur les licences',
},
{
key: 'technicalQuestion',
value: 'question technique',
},
{
key: 'anomalyReport',
value: 'signalement d\'anomalie',
},
{
key: 'contactRequest',
value: 'demande de contact',
},
{
key: 'infoRequest',
value: 'demande d\'information',
},