// NetEvent - Main JavaScript document.addEventListener('DOMContentLoaded', function() { // ----- CSRF Token Injection ----- var csrfMeta = document.querySelector('meta[name="csrf-token"]'); var csrfToken = csrfMeta ? csrfMeta.getAttribute('content') : ''; if (csrfToken) { document.querySelectorAll('form[method="POST"], form[method="post"]').forEach(function(form) { if (!form.querySelector('input[name="_csrf_token"]')) { var input = document.createElement('input'); input.type = 'hidden'; input.name = '_csrf_token'; input.value = csrfToken; form.appendChild(input); } }); } // ----- Toast Notification System ----- const toastContainer = document.getElementById('toast-container'); function showToast(message, type = 'info', duration = 5000) { // Expose globally for inline scripts window.showToast = showToast; const toast = document.createElement('div'); toast.className = `toast toast-${type}`; const iconMap = { success: 'check-circle', error: 'x-circle', warning: 'alert-triangle', info: 'info' }; toast.innerHTML = `

${message}

`; toastContainer.appendChild(toast); lucide.createIcons({ icons: { 'check-circle': lucide.icons['check-circle'], 'x-circle': lucide.icons['x-circle'], 'alert-triangle': lucide.icons['alert-triangle'], 'info': lucide.icons['info'], 'x': lucide.icons['x'] } }); // Close button handler toast.querySelector('.toast-close').addEventListener('click', () => { dismissToast(toast); }); // Auto dismiss if (duration > 0) { setTimeout(() => { dismissToast(toast); }, duration); } return toast; } function dismissToast(toast) { toast.classList.add('toast-out'); setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 300); } // Convert existing flash messages to toasts const flashMessages = document.querySelectorAll('.flash-message'); flashMessages.forEach(msg => { const message = msg.textContent.trim(); const typeMatch = message.toLowerCase().match(/\b(success|error|warning|info)\b/); const type = typeMatch ? typeMatch[1] : 'info'; // Create toast (no auto-dismiss for flash messages, they persist) showToast(message, type, 0); // Remove original flash message msg.remove(); }); // Remove flash-messages container if empty const flashContainer = document.querySelector('.flash-messages'); if (flashContainer && flashContainer.children.length === 0) { flashContainer.remove(); } // ----- Hamburger Menu Toggle ----- const hamburger = document.querySelector('.hamburger'); const navLinks = document.querySelector('.nav-links'); if (hamburger && navLinks) { hamburger.addEventListener('click', function() { navLinks.classList.toggle('active'); const isExpanded = navLinks.classList.contains('active'); hamburger.setAttribute('aria-expanded', isExpanded); // Toggle icon visibility using CSS classes const menuIcon = hamburger.querySelector('.hamburger-icon'); const closeIcon = hamburger.querySelector('.hamburger-close-icon'); if (isExpanded) { menuIcon.classList.add('d-none'); closeIcon.classList.remove('d-none'); } else { menuIcon.classList.remove('d-none'); closeIcon.classList.add('d-none'); } }); // Close menu when clicking a link (for mobile) navLinks.querySelectorAll('a').forEach(link => { link.addEventListener('click', () => { navLinks.classList.remove('active'); hamburger.setAttribute('aria-expanded', 'false'); hamburger.querySelector('.hamburger-icon').classList.remove('d-none'); hamburger.querySelector('.hamburger-close-icon').classList.add('d-none'); }); }); // Close menu when clicking outside document.addEventListener('click', function(e) { if (!hamburger.contains(e.target) && !navLinks.contains(e.target)) { navLinks.classList.remove('active'); hamburger.setAttribute('aria-expanded', 'false'); const menuIcon = hamburger.querySelector('.hamburger-icon'); const closeIcon = hamburger.querySelector('.hamburger-close-icon'); if (menuIcon) menuIcon.classList.remove('d-none'); if (closeIcon) closeIcon.classList.add('d-none'); } }); } // ----- Form Validation with Toast ----- const forms = document.querySelectorAll('form'); forms.forEach(form => { form.addEventListener('submit', function(e) { // Skip if already handled or has data-no-validate if (e.target.dataset.noValidate) return; const requiredInputs = form.querySelectorAll('[required]'); let isValid = true; let firstInvalid = null; requiredInputs.forEach(input => { if (!input.value.trim()) { isValid = false; input.style.borderColor = 'var(--danger-color)'; input.style.boxShadow = '0 0 0 3px var(--danger-light)'; if (!firstInvalid) firstInvalid = input; } else { input.style.borderColor = ''; input.style.boxShadow = ''; } }); if (!isValid) { e.preventDefault(); showToast('Please fill in all required fields.', 'error'); if (firstInvalid) firstInvalid.focus(); } }); }); // ----- Connection Request Buttons ----- const connectForms = document.querySelectorAll('.connect-form'); connectForms.forEach(form => { form.addEventListener('submit', async function(e) { e.preventDefault(); const submitBtn = this.querySelector('button[type="submit"]'); if (submitBtn) { submitBtn.classList.add('btn-loading'); submitBtn.disabled = true; } const formData = new FormData(this); try { const response = await fetch(this.action, { method: 'POST', body: formData }); const data = await response.json(); if (data.success) { const card = this.closest('.attendee-card'); const actionsDiv = card.querySelector('.attendee-actions'); actionsDiv.innerHTML = ' Request Pending'; lucide.createIcons(); showToast('Connection request sent!', 'success'); } else { showToast(data.error || 'Error sending connection request', 'error'); if (submitBtn) { submitBtn.classList.remove('btn-loading'); submitBtn.disabled = false; } } } catch (error) { showToast('Error sending connection request', 'error'); if (submitBtn) { submitBtn.classList.remove('btn-loading'); submitBtn.disabled = false; } } }); }); // ----- Button Loading States on Form Submit ----- document.querySelectorAll('form').forEach(form => { form.addEventListener('submit', function(e) { // Skip if already loading or novalidate if (this.dataset.noValidate) return; const submitBtn = this.querySelector('button[type="submit"]:not([data-no-loading])'); if (submitBtn && !submitBtn.classList.contains('btn-loading')) { submitBtn.classList.add('btn-loading'); submitBtn.disabled = true; } }); }); // ----- Toggle Switch Initialization ----- const toggles = document.querySelectorAll('.toggle input'); toggles.forEach(toggle => { toggle.addEventListener('change', function() { const form = this.closest('form'); if (form) { form.submit(); } }); }); // ----- Modal Close on Escape ----- document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { const modals = document.querySelectorAll('.modal-overlay'); modals.forEach(modal => { const closeBtn = modal.querySelector('.modal-close'); if (closeBtn) closeBtn.click(); }); } }); // ----- Copy Link Utility (shared across templates) ----- window.copyLink = function(url) { if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(url).then(function() { showToast('Link copied!', 'success'); }).catch(function() { fallbackCopy(url); }); } else { fallbackCopy(url); } }; window.fallbackCopy = function(url) { var textArea = document.createElement('textarea'); textArea.value = url; textArea.style.position = 'fixed'; textArea.style.left = '-9999px'; document.body.appendChild(textArea); textArea.select(); try { document.execCommand('copy'); showToast('Link copied!', 'success'); } catch (err) { showToast('Failed to copy link.', 'error'); } document.body.removeChild(textArea); }; // ----- Smooth scroll for anchor links ----- document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function(e) { const targetId = this.getAttribute('href'); if (targetId === '#') return; const target = document.querySelector(targetId); if (target) { e.preventDefault(); target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // ----- Custom Confirmation Modal (replaces native confirm()) ----- const confirmModal = document.getElementById('confirm-modal'); const confirmTitle = document.getElementById('confirm-title'); const confirmMessage = document.getElementById('confirm-message'); const confirmOk = document.getElementById('confirm-ok'); const confirmCancel = document.getElementById('confirm-cancel'); let confirmCallback = null; function showConfirm(message, callback, title) { confirmMessage.textContent = message; confirmTitle.textContent = title || 'Confirm'; confirmModal.style.display = 'flex'; confirmCallback = callback; confirmOk.focus(); } function hideConfirm() { confirmModal.style.display = 'none'; confirmCallback = null; } if (confirmOk) { confirmOk.addEventListener('click', function() { if (confirmCallback) confirmCallback(); hideConfirm(); }); } if (confirmCancel) { confirmCancel.addEventListener('click', hideConfirm); } if (confirmModal) { confirmModal.addEventListener('click', function(e) { if (e.target === confirmModal) hideConfirm(); }); } // Intercept data-confirm on forms and buttons document.addEventListener('click', function(e) { const target = e.target.closest('[data-confirm]'); if (!target) return; const message = target.getAttribute('data-confirm'); if (!message) return; e.preventDefault(); showConfirm(message, function() { if (target.tagName === 'FORM') { target.submit(); } else if (target.tagName === 'A') { window.location.href = target.getAttribute('href'); } else { // For buttons, remove data-confirm and re-click target.removeAttribute('data-confirm'); target.click(); target.setAttribute('data-confirm', message); } }); }, true); });