Skip to content

Commit fe5c515

Browse files
committed
feat: form validation
1 parent ad1d585 commit fe5c515

7 files changed

Lines changed: 299 additions & 96 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,7 @@
33
2. Il est conseillé d'utiliser VisualStudio Code et vous pouvez utiliser Docker, mais ce n'est pas obligatoire ;
44
3. Il n'y a aucune dépendance ;
55
4. Vous ne devez utiliser que du CSS personnalisé et du JavaScript pur, sans jQuery, Bootstrap ou autre librairie.
6+
7+
## Liens utils:
8+
- Maquette projet: https://www.figma.com/file/B7NKBDvSI18uoMLJgpnh48/UI-Design-GameOn-FR?type=design&node-id=106-630&mode=design&t=Kk4ZVcfkSdpHEvwg-0
9+
- RAF: https://github.com/OpenClassrooms-Student-Center/GameOn-website-FR/issues

index.html

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
<img alt="logo" src="assets/images/logo.png" width="auto" height="auto" />
1616
</div>
1717
<div class="main-navbar">
18+
<<<<<<< Updated upstream
1819
<a href="#" class="active"><span>Accueil</span></a>
20+
=======
21+
<a href="#" class="active"><span>Acceuil</span></a>
22+
>>>>>>> Stashed changes
1923
<a href="#"><span>Détails de l'évènement</span></a>
2024
<a href="#"><span>À propos</span></a>
2125
<a href="#"><span>Contact</span></a>
@@ -44,30 +48,40 @@ <h1 class="hero-headline">
4448
<button class="btn-signup modal-btn">je m'inscris</button>
4549
</div>
4650

47-
<div class="bground">
51+
<div class="form bground">
4852
<div class="content">
49-
<span class="close"></span>
53+
<span class="close close-form-modal"></span>
5054
<div class="modal-body">
5155
<form name="reserve">
5256
<div class="formData">
5357
<label for="firstname">Prénom</label>
54-
<input class="text-control" type="text" id="firstname" name="firstname" minlength="2" />
58+
<input class="text-control form-field" type="text" id="firstname" name="firstname" />
59+
<p class="form-error firstname-error">Veuillez entrer 2 caractères ou plus pour le champ du prénom.</p>
5560
</div>
5661
<div class="formData">
5762
<label for="lastname">Nom</label>
58-
<input class="text-control" type="text" id="lastname" name="lastname" minlength="2" />
63+
<input class="text-control form-field" type="text" id="lastname" name="lastname" />
64+
<p class="form-error lastname-error">Veuillez entrer 2 caractères ou plus pour le champ du nom.</p>
5965
</div>
6066
<div class="formData">
6167
<label for="email">E-mail</label>
62-
<input class="text-control" type="email" id="email" name="email" />
68+
<input class="text-control form-field" type="email" id="email" name="email" />
69+
<p class="form-error email-error">Veuillez entrer une adresse mail valide.</p>
6370
</div>
6471
<div class="formData">
6572
<label for="birthdate">Date de naissance</label>
66-
<input class="text-control" type="date" id="birthdate" name="birthdate" />
73+
<input class="text-control form-field" type="date" id="birthdate" name="birthdate" />
74+
<p class="form-error birthdate-error">Vous devez entrer votre date de naissance valide.</p>
6775
</div>
6876
<div class="formData">
6977
<label for="nbTournament">À combien de tournois GameOn avez-vous déjà participé ?</label>
70-
<input type="number" class="text-control" id="nbTournament" name="nbTournament" min="0" max="99" />
78+
<input
79+
type="number"
80+
class="text-control form-field"
81+
id="nbTournament"
82+
name="nbTournament"
83+
/>
84+
<p class="form-error nbTournament-error">Vous devez entrer le nombre de tournois auquel vous avez participé.</p>
7185
</div>
7286
<p class="text-label">A quel tournoi souhaitez-vous participer cette année ?</p>
7387
<div class="formData">
@@ -101,29 +115,40 @@ <h1 class="hero-headline">
101115
<span class="checkbox-icon"></span>
102116
Portland
103117
</label>
118+
<p class="form-error location-error"></p>
104119
</div>
105120

106121
<div class="formData">
107-
<input class="checkbox-input" type="checkbox" id="termsOfUse" checked />
108-
<label class="checkbox2-label" for="termsOfUse" required>
122+
<input class="checkbox-input" type="checkbox" id="termsOfUse" name="termsOfUse"/>
123+
<label class="checkbox2-label" for="termsOfUse">
109124
<span class="checkbox-icon"></span>
110125
J'ai lu et accepté les conditions d'utilisation.
111126
</label>
112-
<input class="checkbox-input" type="checkbox" id="newsletter" />
127+
<p class="form-error termsOfUse-error"></p>
128+
<input class="checkbox-input" type="checkbox" id="newsletter" name="newsletter" />
113129
<label class="checkbox2-label" for="newsletter">
114130
<span class="checkbox-icon"></span>
115131
Je souhaite être prévenu des prochains évènements.
116132
</label>
133+
<p class="form-error newsletter-error"></p>
117134
</div>
118-
<input class="btn-submit" type="submit" class="button" value="C'est parti" />
135+
<input id="btn-submit" class="btn-submit-disable" type="submit" class="button" value="C'est parti" disabled />
119136
</form>
120137
</div>
121138
</div>
122139
</div>
140+
<div class="success-modal bground">
141+
<div class="content">
142+
<span class="close close-success-modal"></span>
143+
<div class="modal-body">
144+
<p>Merci pour votre inscription!</p>
145+
</div>
146+
</div>
147+
</div>
123148
</main>
124149
<footer>
125150
<p class="copyrights">Copyright 2014 - 2022, GameOn Inc.</p>
126151
</footer>
127-
<script src="/src/js/modal.js"></script>
152+
<script src="/src/modal.js" type="module"></script>
128153
</body>
129154
</html>

src/formStore.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// form state
2+
export const formState = {
3+
firstname: { value: '', valid: false },
4+
lastname: { value: '', valid: false },
5+
email: { value: '', valid: false },
6+
birthdate: { value: '', valid: false },
7+
nbTournament: { value: '', valid: false },
8+
location: { value: '', valid: false },
9+
newsletter: { value: false, valid: true },
10+
termsOfUse: { value: false, valid: false }
11+
};

src/js/modal.js

Lines changed: 0 additions & 83 deletions
This file was deleted.

src/modal.js

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { formState } from './formStore.js';
2+
import * as validateForm from './utils/validationSchema.js';
3+
4+
function editNav() {
5+
var x = document.getElementById('myTopnav');
6+
if (x.className === 'topnav') {
7+
x.className += ' responsive';
8+
} else {
9+
x.className = 'topnav';
10+
}
11+
}
12+
13+
/**
14+
* DOM Selectors
15+
*/
16+
let formIsValid = false;
17+
const modalbg = document.querySelector('.form');
18+
const modalBtn = document.querySelectorAll('.modal-btn');
19+
const closeForm = document.querySelector('.close-form-modal');
20+
const closeSuccess = document.querySelector('.close-success-modal');
21+
const form = document.querySelector('form');
22+
const successModal = document.querySelector('.success-modal');
23+
const submitBtn = document.querySelector('#btn-submit');
24+
25+
export const firstname = document.querySelector('#firstname');
26+
export const lastname = document.querySelector('#lastname');
27+
export const email = document.querySelector('#email');
28+
export const birthdate = document.querySelector('#birthdate');
29+
export const nbTournament = document.querySelector('#nbTournament');
30+
export const termsOfUse = document.querySelector('#termsOfUse');
31+
export const newsletter = document.querySelector('#newsletter');
32+
33+
// form fields
34+
export const fields = document.querySelectorAll('.form-field');
35+
export const locations = document.getElementsByName('location');
36+
37+
// launch modal form
38+
function launchModal() {
39+
modalbg.style.display = 'block';
40+
}
41+
42+
// close modal form
43+
function closeModal() {
44+
modalbg.style.display = 'none';
45+
}
46+
47+
function launchSuccesModal() {
48+
modalbg.style.display = 'none';
49+
successModal.style.display = 'block';
50+
}
51+
52+
function closeSuccessModal() {
53+
successModal.style.display = 'none';
54+
}
55+
56+
function disabledSubmitBtn() {
57+
let isNotValid = Object.entries(formState).filter((field) => !field[1].valid);
58+
if (!isNotValid.length) {
59+
submitBtn.disabled = false;
60+
submitBtn.classList.remove('btn-submit-disable');
61+
submitBtn.classList.add('btn-submit');
62+
} else {
63+
submitBtn.disabled = true;
64+
submitBtn.classList.remove('btn-submit');
65+
submitBtn.classList.add('btn-submit-disable');
66+
}
67+
}
68+
69+
function displayError() {
70+
Object.entries(formState).forEach((field) => {
71+
if (field[0] !== 'newsletter') {
72+
const fieldError = document.querySelector(`.${field[0]}-error`);
73+
if (field[1].value) {
74+
if (field[1].valid) {
75+
fieldError.style.display = 'none';
76+
} else if (!field[1].valid) {
77+
fieldError.style.display = 'block';
78+
}
79+
} else {
80+
fieldError.style.display = 'none';
81+
}
82+
}
83+
});
84+
}
85+
86+
function fieldValidation(field, formState) {
87+
if (formState.value) {
88+
if (formState.valid) {
89+
field.style.borderColor = 'green';
90+
} else {
91+
field.style.borderColor = 'red';
92+
}
93+
} else {
94+
field.style.borderColor = '';
95+
}
96+
}
97+
98+
function postForm(formState) {
99+
let isValid = true;
100+
Object.entries(formState).forEach((formField) => {
101+
if (!formField[1].valid) {
102+
displayError();
103+
return (isValid = false);
104+
}
105+
});
106+
return isValid;
107+
}
108+
109+
//
110+
111+
/**
112+
* Events
113+
*/
114+
115+
// launch modal
116+
modalBtn.forEach((btn) => btn.addEventListener('click', launchModal));
117+
submitBtn.addEventListener('click', postForm);
118+
119+
// close modal
120+
closeForm.addEventListener('click', closeModal);
121+
closeSuccess.addEventListener('click', closeSuccessModal);
122+
123+
firstname.addEventListener('change', (e) => {
124+
formState.firstname.value = e.target.value;
125+
validateForm.onChangeValidateName(formState.firstname);
126+
fieldValidation(firstname, formState.firstname);
127+
});
128+
129+
lastname.addEventListener('change', (e) => {
130+
formState.lastname.value = e.target.value;
131+
validateForm.onChangeValidateName(formState.lastname);
132+
fieldValidation(lastname, formState.lastname);
133+
});
134+
135+
email.addEventListener('change', (e) => {
136+
formState.email.value = e.target.value;
137+
validateForm.onChangeValidateEmail(formState.email);
138+
fieldValidation(email, formState.email);
139+
});
140+
141+
birthdate.addEventListener('change', (e) => {
142+
formState.birthdate.value = e.target.value;
143+
validateForm.onChangeValidateBirthdate(formState.birthdate);
144+
fieldValidation(birthdate, formState.birthdate);
145+
});
146+
147+
nbTournament.addEventListener('change', (e) => {
148+
formState.nbTournament.value = parseInt(e.target.value);
149+
validateForm.onChangeValidateNbTournament(formState.nbTournament);
150+
fieldValidation(nbTournament, formState.nbTournament);
151+
});
152+
153+
locations.forEach((location) => {
154+
location.addEventListener('click', (e) => {
155+
formState.location.value = e.target.value;
156+
formState.location.valid = true;
157+
});
158+
});
159+
160+
termsOfUse.addEventListener('click', (e) => {
161+
formState.termsOfUse.value = e.target.checked;
162+
validateForm.onClickValidateTermsOfUse(formState.termsOfUse);
163+
});
164+
165+
newsletter.addEventListener('click', (e) => {
166+
formState.newsletter.value = e.target.checked;
167+
});
168+
169+
// get values from form fields
170+
form.addEventListener('submit', (e) => {
171+
e.preventDefault();
172+
if (postForm(formState)) {
173+
launchSuccesModal();
174+
}
175+
});
176+
177+
form.addEventListener('change', (e) => {
178+
e.preventDefault();
179+
displayError();
180+
disabledSubmitBtn();
181+
});

0 commit comments

Comments
 (0)