/**
* 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();
}