// 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 = `
`;
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);
});