Spaces:
Sleeping
Sleeping
| /** | |
| * StudentMS β script.js | |
| * Client-side validation, modals, and UI helpers. | |
| */ | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 1. AUTO-DISMISS FLASH MESSAGES | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const alerts = document.querySelectorAll('.alert'); | |
| alerts.forEach(alert => { | |
| setTimeout(() => { | |
| alert.style.transition = 'opacity .4s, transform .4s'; | |
| alert.style.opacity = '0'; | |
| alert.style.transform = 'translateX(40px)'; | |
| setTimeout(() => alert.remove(), 420); | |
| }, 4500); | |
| }); | |
| }); | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 2. MOBILE NAVBAR TOGGLE | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| const navToggle = document.getElementById('navToggle'); | |
| const navLinks = document.getElementById('navLinks'); | |
| if (navToggle && navLinks) { | |
| navToggle.addEventListener('click', () => { | |
| navLinks.classList.toggle('open'); | |
| }); | |
| // Close when clicking outside | |
| document.addEventListener('click', (e) => { | |
| if (!navToggle.contains(e.target) && !navLinks.contains(e.target)) { | |
| navLinks.classList.remove('open'); | |
| } | |
| }); | |
| } | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 3. PASSWORD TOGGLE | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| function togglePassword(inputId, btn) { | |
| const input = document.getElementById(inputId); | |
| const icon = btn.querySelector('i'); | |
| if (input.type === 'password') { | |
| input.type = 'text'; | |
| icon.classList.replace('fa-eye', 'fa-eye-slash'); | |
| } else { | |
| input.type = 'password'; | |
| icon.classList.replace('fa-eye-slash', 'fa-eye'); | |
| } | |
| } | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 4. LOGIN FORM VALIDATION | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| const loginForm = document.getElementById('loginForm'); | |
| if (loginForm) { | |
| loginForm.addEventListener('submit', (e) => { | |
| let valid = true; | |
| const username = document.getElementById('username'); | |
| const password = document.getElementById('password'); | |
| const errUser = document.getElementById('err-username'); | |
| const errPass = document.getElementById('err-password'); | |
| // Reset | |
| [errUser, errPass].forEach(el => { if (el) el.textContent = ''; }); | |
| [username, password].forEach(el => el.classList.remove('is-invalid')); | |
| if (!username.value.trim()) { | |
| errUser.textContent = 'Username is required.'; | |
| username.classList.add('is-invalid'); | |
| valid = false; | |
| } | |
| if (!password.value.trim()) { | |
| errPass.textContent = 'Password is required.'; | |
| password.classList.add('is-invalid'); | |
| valid = false; | |
| } | |
| if (!valid) e.preventDefault(); | |
| }); | |
| } | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 5. STUDENT FORM VALIDATION (add & edit) | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| const studentForm = document.getElementById('studentForm'); | |
| if (studentForm) { | |
| studentForm.addEventListener('submit', (e) => { | |
| const errors = validateStudentForm(); | |
| if (errors > 0) e.preventDefault(); | |
| }); | |
| } | |
| function validateStudentForm() { | |
| let errorCount = 0; | |
| // Helper: show error | |
| function showErr(fieldName, msg) { | |
| const errEl = document.getElementById(`err-${fieldName}`); | |
| const input = studentForm.querySelector(`[name="${fieldName}"]`); | |
| if (errEl) errEl.textContent = msg; | |
| if (input) input.classList.add('is-invalid'); | |
| errorCount++; | |
| } | |
| // Helper: clear error | |
| function clearErr(fieldName) { | |
| const errEl = document.getElementById(`err-${fieldName}`); | |
| const input = studentForm.querySelector(`[name="${fieldName}"]`); | |
| if (errEl) errEl.textContent = ''; | |
| if (input) input.classList.remove('is-invalid'); | |
| } | |
| // Fields to validate | |
| const required = [ | |
| 'student_id', 'full_name', 'father_name', 'mother_name', | |
| 'gender', 'dob', 'email', 'phone', | |
| 'course', 'branch', 'year_sem', | |
| 'address', 'city', 'state', 'pin_code' | |
| ]; | |
| required.forEach(name => clearErr(name)); | |
| // Required check | |
| required.forEach(name => { | |
| const el = studentForm.querySelector(`[name="${name}"]`); | |
| if (!el) return; | |
| if (!el.value.trim()) { | |
| const label = name.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); | |
| showErr(name, `${label} is required.`); | |
| } | |
| }); | |
| // Email format | |
| const emailEl = studentForm.querySelector('[name="email"]'); | |
| if (emailEl && emailEl.value.trim()) { | |
| const re = /^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$/; | |
| if (!re.test(emailEl.value.trim())) { | |
| showErr('email', 'Enter a valid email address.'); | |
| } | |
| } | |
| // Phone β 10 digits only | |
| const phoneEl = studentForm.querySelector('[name="phone"]'); | |
| if (phoneEl && phoneEl.value.trim()) { | |
| if (!/^\d{10}$/.test(phoneEl.value.trim())) { | |
| showErr('phone', 'Phone must be exactly 10 digits.'); | |
| } | |
| } | |
| // Pin code β 6 digits only | |
| const pinEl = studentForm.querySelector('[name="pin_code"]'); | |
| if (pinEl && pinEl.value.trim()) { | |
| if (!/^\d{6}$/.test(pinEl.value.trim())) { | |
| showErr('pin_code', 'Pin Code must be exactly 6 digits.'); | |
| } | |
| } | |
| // Date of birth β must not be in the future | |
| const dobEl = studentForm.querySelector('[name="dob"]'); | |
| if (dobEl && dobEl.value) { | |
| const dob = new Date(dobEl.value); | |
| if (dob >= new Date()) { | |
| showErr('dob', 'Date of Birth cannot be in the future.'); | |
| } | |
| } | |
| return errorCount; | |
| } | |
| // Live phone & pin β allow only digits | |
| document.addEventListener('DOMContentLoaded', () => { | |
| const phoneInput = document.querySelector('[name="phone"]'); | |
| const pinInput = document.querySelector('[name="pin_code"]'); | |
| if (phoneInput) { | |
| phoneInput.addEventListener('input', () => { | |
| phoneInput.value = phoneInput.value.replace(/\D/g, '').slice(0, 10); | |
| }); | |
| } | |
| if (pinInput) { | |
| pinInput.addEventListener('input', () => { | |
| pinInput.value = pinInput.value.replace(/\D/g, '').slice(0, 6); | |
| }); | |
| } | |
| }); | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 6. DELETE MODAL | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| let pendingDeleteId = null; | |
| function confirmDelete(studentId, studentName) { | |
| pendingDeleteId = studentId; | |
| const modal = document.getElementById('deleteModal'); | |
| const nameEl = document.getElementById('deleteStudentName'); | |
| if (nameEl) nameEl.textContent = studentName; | |
| if (modal) modal.style.display = 'flex'; | |
| } | |
| function closeModal() { | |
| const modal = document.getElementById('deleteModal'); | |
| if (modal) modal.style.display = 'none'; | |
| pendingDeleteId = null; | |
| } | |
| function submitDelete() { | |
| if (!pendingDeleteId) return; | |
| const form = document.getElementById('deleteForm'); | |
| if (form) { | |
| form.action = `/delete_student/${pendingDeleteId}`; | |
| form.submit(); | |
| } | |
| } | |
| // Close modal when clicking backdrop | |
| document.addEventListener('click', (e) => { | |
| const modal = document.getElementById('deleteModal'); | |
| if (modal && e.target === modal) closeModal(); | |
| }); | |
| // Close modal on Escape key | |
| document.addEventListener('keydown', (e) => { | |
| if (e.key === 'Escape') closeModal(); | |
| }); | |
| /* βββββββββββββββββββββββββββββββββββββββββββ | |
| 7. SEARCH β CLEAR | |
| βββββββββββββββββββββββββββββββββββββββββββ */ | |
| function clearSearch() { | |
| const input = document.getElementById('searchInput'); | |
| if (input) { | |
| input.value = ''; | |
| document.getElementById('searchForm').submit(); | |
| } | |
| } | |