// BOC — Business Operations Center // Single-page app shell. One sidebar, dynamic modules. // Linus principle: write it once, route everything through it. const API_BASE = '/api/v1'; // Module registry — add new modules here const modules = { '/': () => import('./modules/dashboard.js'), '/crm': () => import('./modules/crm.js'), '/sales': () => import('./modules/sales.js'), '/finance': () => import('./modules/finance.js'), '/hr': () => import('./modules/hr.js'), '/legal': () => import('./modules/legal.js'), '/marketing': () => import('./modules/marketing.js'), '/support': () => import('./modules/support.js'), '/automation': () => import('./modules/automation.js'), }; // Sidebar configuration — one source of truth const sidebarItems = [ { path: '/', label: 'Dashboard', icon: 'grid' }, { path: '/crm', label: 'CRM', icon: 'users' }, { path: '/sales', label: 'Sales', icon: 'dollar' }, { path: '/finance', label: 'Finance', icon: 'bar-chart' }, { path: '/hr', label: 'HR', icon: 'user' }, { path: '/legal', label: 'Legal', icon: 'file-text' }, { path: '/marketing', label: 'Marketing', icon: 'megaphone' }, { path: '/support', label: 'Support', icon: 'help-circle' }, { path: '/automation', label: 'Automation', icon: 'zap' }, ]; // Auth utilities function getToken() { return localStorage.getItem('boc_token'); } function getUser() { const u = localStorage.getItem('boc_user'); return u ? JSON.parse(u) : null; } function apiRequest(endpoint, options = {}) { const token = getToken(); const url = endpoint.startsWith('http') ? endpoint : `${API_BASE}${endpoint}`; return fetch(url, { ...options, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, ...options.headers } }); } // Router async function navigate(path) { const app = document.getElementById('app'); const loader = modules[path] || modules['/']; try { const mod = await loader(); app.innerHTML = ''; await mod.render(app); updateActiveNav(path); } catch (err) { app.innerHTML = `
Failed to load module: ${err.message}
`; } } function updateActiveNav(path) { document.querySelectorAll('.sidebar-nav a').forEach(a => { a.classList.toggle('active', a.getAttribute('href') === '#' + path); }); } // Sidebar renderer function renderSidebar() { const sidebar = document.getElementById('sidebar'); const user = getUser(); sidebar.innerHTML = ` `; sidebar.querySelectorAll('a').forEach(a => { a.addEventListener('click', (e) => { e.preventDefault(); const path = a.getAttribute('data-path'); location.hash = path; }); }); } function logout() { localStorage.removeItem('boc_token'); localStorage.removeItem('boc_user'); location.href = '/login.html'; } // Auth check function requireAuth() { if (!getToken()) { location.href = '/login.html'; return false; } return true; } // Init window.addEventListener('DOMContentLoaded', () => { if (!requireAuth()) return; renderSidebar(); const path = location.hash.slice(1) || '/'; navigate(path); }); window.addEventListener('hashchange', () => { const path = location.hash.slice(1) || '/'; navigate(path); }); // Export for modules window.apiRequest = apiRequest; window.getToken = getToken; window.getUser = getUser;