// ============================================================ // Elegant Work — Router.js // Hash-based SPA router // ============================================================ const Router = (() => { const routes = {}; let current = null; function register(name, handler) { routes[name] = handler; } function navigate(name, params = {}) { current = { name, params }; window.location.hash = name; render(name, params); } function render(name, params = {}) { // Auth guard if (name !== 'login' && !Auth.loadFromStorage()) { renderLogin(); return; } if (name === 'login' && Auth.loadFromStorage()) { name = 'dashboard'; } if (name === 'login') { renderLogin(); return; } // Show main layout showAppLayout(); // Update nav active state + hide items user can't view document.querySelectorAll('.nav-item[data-page]').forEach(el => { const page = el.dataset.page; el.classList.toggle('active', page === name); // Always show dashboard and settings; check others const alwaysShow = ['dashboard', 'settings']; if (!alwaysShow.includes(page)) { const allowed = Auth.can(page, 'view'); el.style.display = allowed ? '' : 'none'; } }); // Update header title const titles = { dashboard: ['Dashboard', 'Welcome back'], clients: ['Clients', 'Manage client relationships'], employees: ['Employees', 'Team management'], projects: ['Projects', 'Track development & bugs'], jobcards: ['Job Cards', 'Field operations'], stock: ['Stock Control', 'Inventory management'], slips: ['Slip Manager', 'Expense tracking'], cashflow: ['Cash Flow Planner', 'Monthly income, expenses & P/L'], calendar: ['Calendar', 'Scheduled jobs & events'], meetings: ['Events', 'Meetings, tasks & reminders'], fleet: ['Fleet', 'Vehicle management'], checklists: ['Checklists', 'Task compliance'], payroll: ['Payroll', 'SARS-compliant processing'], settings: ['Settings', 'System configuration'], }; const [title, sub] = titles[name] || [name, '']; const ptEl = document.getElementById('page-title'); const pbEl = document.getElementById('page-breadcrumb'); if (ptEl) ptEl.textContent = title; if (pbEl) pbEl.textContent = sub; // Block navigation to pages user has no view access const noCheck = ['dashboard', 'settings', 'login', 'jobcard_settings', 'email_notifications']; if (!noCheck.includes(name) && !Auth.can(name, 'view')) { const contentEl = document.getElementById('page-content'); if (contentEl) contentEl.innerHTML = `
🔒
Access Restricted

You don't have permission to view this module.
Contact your administrator.

`; return; } // Render page content const contentEl = document.getElementById('page-content'); if (contentEl) contentEl.innerHTML = '
'; if (routes[name]) { routes[name](params); } else { if (contentEl) contentEl.innerHTML = `
Page not found

This module is not yet available.

`; } } function renderLogin() { const app = document.getElementById('app'); document.getElementById('login-screen').style.display = 'flex'; const layout = document.querySelector('.app-layout'); if (layout) layout.style.display = 'none'; } function showAppLayout() { document.getElementById('login-screen').style.display = 'none'; const layout = document.querySelector('.app-layout'); if (layout) layout.style.display = 'flex'; } function init() { const hash = window.location.hash.replace('#', '') || 'dashboard'; render(hash); window.addEventListener('hashchange', () => { const h = window.location.hash.replace('#', '') || 'dashboard'; render(h); }); } return { register, navigate, render, init }; })(); // ── Global Tab Utility (used by clients, employees, fleet, jobcards) ── function switchTab(navId, panePrefix, btn) { document.querySelectorAll('#' + navId + ' .tab-btn').forEach(function (b) { b.classList.remove('active'); }); btn.classList.add('active'); document.querySelectorAll('[id^="' + panePrefix + '-"]').forEach(function (p) { p.classList.remove('active'); }); var pane = document.getElementById(panePrefix + '-' + btn.dataset.tab); if (pane) pane.classList.add('active'); }