// Enhanced JavaScript for Left Sidebar Navigation
class QRManager {
constructor() {
this.initializeApp();
}
initializeApp() {
this.initSidebar();
this.initModals();
this.initDropdowns();
this.initActiveNavigation();
this.initFlashMessages();
}
// Sidebar Management
initSidebar() {
const sidebar = document.getElementById('sidebar');
const sidebarToggle = document.getElementById('sidebarToggle');
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
const sidebarOverlay = document.getElementById('sidebarOverlay');
if (!sidebar) return;
// Desktop sidebar toggle
if (sidebarToggle) {
sidebarToggle.addEventListener('click', () => {
this.toggleSidebar();
});
}
// Mobile menu toggle
if (mobileMenuBtn) {
mobileMenuBtn.addEventListener('click', () => {
this.toggleMobileSidebar();
});
}
// Close mobile sidebar when clicking overlay
if (sidebarOverlay) {
sidebarOverlay.addEventListener('click', () => {
this.closeMobileSidebar();
});
}
// Close mobile sidebar when clicking menu items
const menuItems = sidebar.querySelectorAll('.menu-item');
menuItems.forEach(item => {
item.addEventListener('click', () => {
if (window.innerWidth <= 768) {
this.closeMobileSidebar();
}
});
});
// Handle window resize
window.addEventListener('resize', () => {
this.handleResize();
});
// Initialize sidebar state based on screen size
this.handleResize();
}
toggleSidebar() {
const sidebar = document.getElementById('sidebar');
if (sidebar) {
sidebar.classList.toggle('collapsed');
this.saveSidebarState();
}
}
toggleMobileSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
const mobileBtn = document.getElementById('mobileMenuBtn');
if (sidebar && overlay && mobileBtn) {
const isOpen = sidebar.classList.contains('mobile-open');
if (isOpen) {
this.closeMobileSidebar();
} else {
this.openMobileSidebar();
}
}
}
openMobileSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
const mobileBtn = document.getElementById('mobileMenuBtn');
if (sidebar && overlay && mobileBtn) {
sidebar.classList.add('mobile-open');
overlay.classList.add('active');
mobileBtn.classList.add('active');
document.body.style.overflow = 'hidden';
}
}
closeMobileSidebar() {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebarOverlay');
const mobileBtn = document.getElementById('mobileMenuBtn');
if (sidebar && overlay && mobileBtn) {
sidebar.classList.remove('mobile-open');
overlay.classList.remove('active');
mobileBtn.classList.remove('active');
document.body.style.overflow = '';
}
}
handleResize() {
const sidebar = document.getElementById('sidebar');
if (!sidebar) return;
if (window.innerWidth <= 768) {
// Mobile: ensure sidebar is hidden and mobile menu is available
this.closeMobileSidebar();
} else if (window.innerWidth <= 1024) {
// Tablet: auto-collapse sidebar
sidebar.classList.add('collapsed');
this.closeMobileSidebar();
} else {
// Desktop: restore saved state
this.restoreSidebarState();
this.closeMobileSidebar();
}
}
saveSidebarState() {
const sidebar = document.getElementById('sidebar');
if (sidebar && window.innerWidth > 1024) {
const isCollapsed = sidebar.classList.contains('collapsed');
localStorage.setItem('sidebarCollapsed', isCollapsed);
}
}
restoreSidebarState() {
const sidebar = document.getElementById('sidebar');
if (sidebar && window.innerWidth > 1024) {
const isCollapsed = localStorage.getItem('sidebarCollapsed') === 'true';
sidebar.classList.toggle('collapsed', isCollapsed);
}
}
// Active Navigation Highlighting
initActiveNavigation() {
const menuItems = document.querySelectorAll('.menu-item[href]');
const currentPath = window.location.pathname;
menuItems.forEach(item => {
const href = item.getAttribute('href');
if (href === currentPath || (currentPath.startsWith(href) && href !== '/')) {
item.classList.add('active');
} else {
item.classList.remove('active');
}
});
}
// Theme Management
initTheme() {
const themeToggle = document.getElementById('themeToggle');
if (!themeToggle) return;
// Load saved theme
const savedTheme = localStorage.getItem('theme') || 'light';
this.setTheme(savedTheme);
themeToggle.addEventListener('click', () => {
const currentTheme = document.body.getAttribute('data-theme') || 'light';
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
this.setTheme(newTheme);
localStorage.setItem('theme', newTheme);
});
}
setTheme(theme) {
document.body.setAttribute('data-theme', theme);
const themeToggle = document.getElementById('themeToggle');
if (themeToggle) {
const icon = themeToggle.querySelector('i');
const text = themeToggle.querySelector('.menu-text');
if (theme === 'dark') {
icon.className = 'fas fa-sun';
if (text) text.textContent = 'Light Mode';
} else {
icon.className = 'fas fa-moon';
if (text) text.textContent = 'Dark Mode';
}
}
}
// Modal Management
initModals() {
// Close modal when clicking outside
document.addEventListener('click', (e) => {
if (e.target.classList.contains('modal')) {
this.closeModal(e.target);
}
});
// Close modal with close button
document.addEventListener('click', (e) => {
if (e.target.classList.contains('modal-close') ||
e.target.closest('.modal-close')) {
const modal = e.target.closest('.modal');
if (modal) {
this.closeModal(modal);
}
}
});
// Close modal with Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const openModal = document.querySelector('.modal.show');
if (openModal) {
this.closeModal(openModal);
}
}
});
}
showModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('show');
document.body.style.overflow = 'hidden';
// Focus first focusable element
const focusableElement = modal.querySelector('input, textarea, select, button, [tabindex]:not([tabindex="-1"])');
if (focusableElement) {
setTimeout(() => focusableElement.focus(), 100);
}
}
}
closeModal(modal) {
if (modal) {
modal.classList.remove('show');
document.body.style.overflow = '';
}
}
// Dropdown Management
initDropdowns() {
const dropdowns = document.querySelectorAll('.dropdown');
dropdowns.forEach(dropdown => {
const trigger = dropdown.querySelector('.dropdown-trigger');
const menu = dropdown.querySelector('.dropdown-menu');
if (trigger && menu) {
trigger.addEventListener('click', (e) => {
e.stopPropagation();
this.toggleDropdown(dropdown);
});
}
});
// Close dropdowns when clicking outside
document.addEventListener('click', () => {
this.closeAllDropdowns();
});
// Close dropdowns with Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
this.closeAllDropdowns();
}
});
}
toggleDropdown(dropdown) {
const menu = dropdown.querySelector('.dropdown-menu');
const isOpen = menu.classList.contains('show');
this.closeAllDropdowns();
if (!isOpen) {
menu.classList.add('show');
}
}
closeAllDropdowns() {
const openMenus = document.querySelectorAll('.dropdown-menu.show');
openMenus.forEach(menu => {
menu.classList.remove('show');
});
}
// Flash Messages
initFlashMessages() {
const alerts = document.querySelectorAll('.alert');
alerts.forEach(alert => {
// Auto-dismiss after 5 seconds
setTimeout(() => {
this.dismissAlert(alert);
}, 5000);
// Manual dismiss
const closeBtn = alert.querySelector('.alert-close');
if (closeBtn) {
closeBtn.addEventListener('click', () => {
this.dismissAlert(alert);
});
}
});
}
dismissAlert(alert) {
alert.style.opacity = '0';
alert.style.transform = 'translateX(100%)';
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 300);
}
// Utility Methods
showToast(message, type = 'info', duration = 3000) {
const toast = document.createElement('div');
toast.className = `alert alert-${type}`;
toast.innerHTML = `
${message}
`;
const container = document.querySelector('.flash-messages') || document.body;
container.appendChild(toast);
// Trigger animation
setTimeout(() => {
toast.classList.add('show');
}, 10);
// Auto dismiss
setTimeout(() => {
this.dismissAlert(toast);
}, duration);
return toast;
}
// Form Validation Helper
validateForm(formElement) {
const requiredFields = formElement.querySelectorAll('[required]');
let isValid = true;
requiredFields.forEach(field => {
if (!field.value.trim()) {
this.showFieldError(field, 'This field is required');
isValid = false;
} else {
this.clearFieldError(field);
}
});
return isValid;
}
showFieldError(field, message) {
this.clearFieldError(field);
field.classList.add('error');
const errorElement = document.createElement('div');
errorElement.className = 'field-error';
errorElement.textContent = message;
field.parentNode.appendChild(errorElement);
}
clearFieldError(field) {
field.classList.remove('error');
const existingError = field.parentNode.querySelector('.field-error');
if (existingError) {
existingError.remove();
}
}
// AJAX Helper
async makeRequest(url, options = {}) {
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
...options
};
try {
const response = await fetch(url, defaultOptions);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Request failed:', error);
this.showToast('An error occurred. Please try again.', 'error');
throw error;
}
}
}
// Keep existing date/time utilities
const DateTimeUtils = {
formatDate: (dateString) => {
const date = new Date(dateString);
return date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
});
},
formatDateTime: (dateString) => {
const date = new Date(dateString);
return date.toLocaleString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});
},
formatTime: (dateString) => {
const date = new Date(dateString);
return date.toLocaleTimeString("en-US", {
hour: "2-digit",
minute: "2-digit",
});
},
};
// Legacy Navigation Manager for backward compatibility
class NavigationManager {
constructor() {
this.initMobileMenu();
this.initDropdowns();
}
initMobileMenu() {
const mobileMenuBtn = document.getElementById("mobile-menu");
const navMenu = document.getElementById("navMenu");
if (mobileMenuBtn && navMenu) {
mobileMenuBtn.addEventListener("click", () => {
mobileMenuBtn.classList.toggle("active");
navMenu.classList.toggle("active");
});
const navLinks = navMenu.querySelectorAll(".nav-link");
navLinks.forEach((link) => {
link.addEventListener("click", () => {
mobileMenuBtn.classList.remove("active");
navMenu.classList.remove("active");
});
});
}
}
initDropdowns() {
const dropdowns = document.querySelectorAll(".dropdown");
dropdowns.forEach((dropdown) => {
const trigger = dropdown.querySelector(".dropdown-trigger");
const menu = dropdown.querySelector(".dropdown-menu");
if (trigger && menu) {
trigger.addEventListener("click", (e) => {
e.stopPropagation();
this.toggleDropdown(dropdown);
});
}
});
document.addEventListener("click", () => {
this.closeAllDropdowns();
});
}
toggleDropdown(dropdown) {
const menu = dropdown.querySelector(".dropdown-menu");
const isOpen = menu.classList.contains("show");
this.closeAllDropdowns();
if (!isOpen) {
menu.classList.add("show");
}
}
closeAllDropdowns() {
const openMenus = document.querySelectorAll(".dropdown-menu.show");
openMenus.forEach((menu) => {
menu.classList.remove("show");
});
}
}
// Initialize the application
document.addEventListener('DOMContentLoaded', () => {
// Initialize main app if sidebar exists (authenticated users)
if (document.getElementById('sidebar')) {
window.qrManager = new QRManager();
} else {
// Initialize legacy navigation for non-authenticated pages
window.navigationManager = new NavigationManager();
}
});
// Export for use in other scripts
window.DateTimeUtils = DateTimeUtils;
// Keep any existing global functions for backward compatibility
if (typeof showConfirmation === 'undefined') {
window.showConfirmation = async function(title, message, description = '') {
return new Promise((resolve) => {
const confirmed = confirm(`${title}\n\n${message}\n${description}`);
resolve(confirmed);
});
};
}