/* 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 ✅'); });