108 lines
3.5 KiB
JavaScript
108 lines
3.5 KiB
JavaScript
/* modal_validation.js
|
|
live client-side checks for each .student-edit-form
|
|
─────────────────────────────────────────────── */
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
/* basic regex rules */
|
|
const nameRE = /^[A-Za-z\s-]{2,30}$/;
|
|
const gradeRE = /^[A-Za-z0-9\s-]{1,20}$/;
|
|
const MIN_AGE = 5, MAX_AGE = 18;
|
|
|
|
const calcAge = (d) => {
|
|
const dob = new Date(d);
|
|
const today = new Date();
|
|
let a = today.getFullYear() - dob.getFullYear();
|
|
const m = today.getMonth() - dob.getMonth();
|
|
if (m < 0 || (m === 0 && today.getDate() < dob.getDate())) a--;
|
|
return a;
|
|
};
|
|
|
|
/* attach to every modal edit-form */
|
|
document.querySelectorAll('.student-edit-form').forEach(form => {
|
|
|
|
const feedback = (el, msg) => {
|
|
let f = el.nextElementSibling;
|
|
if (!f || !f.classList.contains('invalid-feedback')) {
|
|
f = document.createElement('div');
|
|
f.className = 'invalid-feedback';
|
|
el.after(f);
|
|
}
|
|
f.textContent = msg;
|
|
el.classList.add('is-invalid');
|
|
};
|
|
|
|
const clear = el => {
|
|
el.classList.remove('is-invalid');
|
|
const f = el.nextElementSibling;
|
|
if (f && f.classList.contains('invalid-feedback')) f.textContent = '';
|
|
};
|
|
|
|
/* live input validation */
|
|
form.addEventListener('input', e => {
|
|
const t = e.target;
|
|
clear(t); // reset first
|
|
|
|
if (t.name === 'firstname' || t.name === 'lastname') {
|
|
if (!nameRE.test(t.value.trim())) {
|
|
feedback(t, 'Only letters, spaces or dashes (2-30)');
|
|
}
|
|
}
|
|
|
|
if (t.name === 'registration_grade') {
|
|
if (!gradeRE.test(t.value.trim())) {
|
|
feedback(t, 'Invalid grade value');
|
|
}
|
|
}
|
|
|
|
if (t.name === 'dob') {
|
|
const age = calcAge(t.value);
|
|
if (!t.value || age < MIN_AGE || age > MAX_AGE) {
|
|
feedback(t, `Age must be ${MIN_AGE}-${MAX_AGE}`);
|
|
}
|
|
}
|
|
});
|
|
|
|
/* submit validation */
|
|
form.addEventListener('submit', e => {
|
|
let firstBad = null;
|
|
|
|
form.querySelectorAll('input,select').forEach(el => clear(el)); // reset all
|
|
|
|
const fname = form.querySelector('[name="firstname"]');
|
|
const lname = form.querySelector('[name="lastname"]');
|
|
const dob = form.querySelector('[name="dob"]');
|
|
const grade = form.querySelector('[name="registration_grade"]');
|
|
const gender= form.querySelector('[name="gender"]');
|
|
const photo = form.querySelector('[name="photo_consent"]');
|
|
|
|
/* run checks */
|
|
if (!nameRE.test(fname.value.trim())) {
|
|
feedback(fname,'Only letters, spaces or dashes (2-30)'); firstBad ??= fname;
|
|
}
|
|
if (!nameRE.test(lname.value.trim())) {
|
|
feedback(lname,'Only letters, spaces or dashes (2-30)'); firstBad ??= lname;
|
|
}
|
|
const age = calcAge(dob.value);
|
|
if (!dob.value || age < MIN_AGE || age > MAX_AGE) {
|
|
feedback(dob,`Age must be ${MIN_AGE}-${MAX_AGE}`); firstBad ??= dob;
|
|
}
|
|
if (!gender.value) {
|
|
feedback(gender,'Required'); firstBad ??= gender;
|
|
}
|
|
if (!photo.value) {
|
|
feedback(photo,'Required'); firstBad ??= photo;
|
|
}
|
|
if (!gradeRE.test(grade.value.trim())) {
|
|
feedback(grade,'Invalid grade'); firstBad ??= grade;
|
|
}
|
|
|
|
if (firstBad) {
|
|
e.preventDefault();
|
|
firstBad.focus();
|
|
}
|
|
});
|
|
});
|
|
|
|
console.log('modal_validation.js initialised ✅');
|
|
});
|