/** * Navigation Component * Sidebar navigation and user menu */ const MENU_ICON = ` `; const ICONS = { calendar: ` `, users: ` `, rules: ` `, user: ` `, building: ` ` }; const NAV_ITEMS = [ { href: '/presence', icon: 'calendar', label: 'My Presence' }, { href: '/team-calendar', icon: 'users', label: 'Team Calendar' }, { href: '/team-rules', icon: 'rules', label: 'Team Rules', roles: ['admin', 'manager'] }, { href: '/admin/users', icon: 'user', label: 'Manage Users', roles: ['admin'] } ]; function getIcon(name) { return ICONS[name] || ''; } function canAccessNavItem(item, userRole) { if (!item.roles || item.roles.length === 0) return true; return userRole && item.roles.includes(userRole); } function renderNav(currentPath, userRole) { return NAV_ITEMS .filter(item => canAccessNavItem(item, userRole)) .map(item => { const isActive = item.href === currentPath || (currentPath !== '/' && item.href !== '/' && currentPath.startsWith(item.href)); const activeClass = isActive ? ' active' : ''; return ` ${getIcon(item.icon)} ${item.label} `; }).join('\n'); } async function initNav() { const navContainer = document.querySelector('.sidebar-nav'); if (!navContainer) return; const currentPath = window.location.pathname; // Get user info (works with both JWT and Authelia) const currentUser = await api.checkAuth(); // Render navigation navContainer.innerHTML = renderNav(currentPath, currentUser?.role); // Update user info in sidebar if (currentUser) { const userNameEl = document.getElementById('userName'); const userRoleEl = document.getElementById('userRole'); if (userNameEl) userNameEl.textContent = currentUser.name || 'User'; if (userRoleEl) userRoleEl.textContent = currentUser.role || '-'; } // Setup user menu setupUserMenu(); // Setup mobile menu setupMobileMenu(); } function setupMobileMenu() { const sidebar = document.querySelector('.sidebar'); const pageHeader = document.querySelector('.page-header'); if (!sidebar || !pageHeader) return; // Add menu toggle button to page header (at the start) const menuToggle = document.createElement('button'); menuToggle.className = 'menu-toggle'; menuToggle.innerHTML = MENU_ICON; menuToggle.setAttribute('aria-label', 'Toggle menu'); pageHeader.insertBefore(menuToggle, pageHeader.firstChild); // Add overlay const overlay = document.createElement('div'); overlay.className = 'sidebar-overlay'; document.body.appendChild(overlay); // Toggle sidebar menuToggle.addEventListener('click', () => { sidebar.classList.toggle('open'); overlay.classList.toggle('open'); }); // Close sidebar when clicking overlay overlay.addEventListener('click', () => { sidebar.classList.remove('open'); overlay.classList.remove('open'); }); // Close sidebar when clicking a nav item (on mobile) sidebar.querySelectorAll('.nav-item').forEach(item => { item.addEventListener('click', () => { if (window.innerWidth <= 768) { sidebar.classList.remove('open'); overlay.classList.remove('open'); } }); }); } function setupUserMenu() { const userMenuButton = document.getElementById('userMenuButton'); const userDropdown = document.getElementById('userDropdown'); const logoutButton = document.getElementById('logoutButton'); if (userMenuButton && userDropdown) { userMenuButton.addEventListener('click', (e) => { e.stopPropagation(); const isOpen = userDropdown.style.display === 'block'; userDropdown.style.display = isOpen ? 'none' : 'block'; }); document.addEventListener('click', () => { userDropdown.style.display = 'none'; }); userDropdown.addEventListener('click', (e) => e.stopPropagation()); } if (logoutButton) { logoutButton.addEventListener('click', () => { api.logout(); }); } } // Export for use in other scripts window.getIcon = getIcon; // Auto-initialize if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initNav); } else { initNav(); }