import { addRoute, startRouter, navigate } from './router.js'; import { loginPage } from './pages/login.js'; import { dashboardPage } from './pages/dashboard.js'; import { editorPage } from './pages/editor.js'; import { resultsPage } from './pages/results.js'; import { exportPage } from './pages/export.js'; import { usersPage } from './pages/users.js'; import { assignmentsPage } from './pages/assignments.js'; import { clientsPage } from './pages/clients.js'; import { translationsPage } from './pages/translations.js'; // Auth state export function isLoggedIn() { return !!localStorage.getItem('qdb_token'); } export function getRole() { return localStorage.getItem('qdb_role') || ''; } export function getUser() { return localStorage.getItem('qdb_user') || ''; } const WEBSITE_ROLES = ['admin', 'supervisor']; export function canAccessWebsite() { return WEBSITE_ROLES.includes(getRole()); } export function canEdit() { return canAccessWebsite(); } function clearSession() { localStorage.removeItem('qdb_token'); localStorage.removeItem('qdb_user'); localStorage.removeItem('qdb_role'); } function rejectCoachSession() { clearSession(); showToast('Coach accounts must use the mobile app.', 'error'); navigate('#/login'); } export function showToast(message, type = 'info') { const container = document.getElementById('toastContainer'); const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; container.appendChild(toast); requestAnimationFrame(() => toast.classList.add('show')); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 3000); } function authGuard(handler) { return (params) => { if (!isLoggedIn()) { navigate('#/login'); return; } if (!canAccessWebsite()) { rejectCoachSession(); return; } return handler(params); }; } function updateNav() { const nav = document.getElementById('mainNav'); const userInfo = document.getElementById('userInfo'); if (isLoggedIn()) { nav.style.display = ''; const role = getRole(); userInfo.textContent = `${getUser()} (${role})`; // Show/hide role-gated nav items document.querySelectorAll('[data-nav-roles]').forEach(li => { const allowed = li.dataset.navRoles.split(' '); li.style.display = allowed.includes(role) ? '' : 'none'; }); // Highlight active nav link const hash = window.location.hash.slice(1) || '/'; document.querySelectorAll('.sidebar-nav a').forEach(a => { const href = a.getAttribute('href').slice(1); // strip leading # a.classList.toggle('active', hash === href || (href !== '/' && hash.startsWith(href))); }); } else { nav.style.display = 'none'; userInfo.textContent = ''; } } function roleGuard(roles, handler) { return (params) => { if (!isLoggedIn()) { navigate('#/login'); return; } if (!roles.includes(getRole())) { navigate('#/'); return; } return handler(params); }; } // Routes addRoute('/login', loginPage); addRoute('/', authGuard(dashboardPage)); addRoute('/dashboard', authGuard(dashboardPage)); addRoute('/questionnaire/new', authGuard(editorPage)); addRoute('/questionnaire/:id/results', authGuard(resultsPage)); addRoute('/questionnaire/:id', authGuard(editorPage)); addRoute('/export', authGuard(exportPage)); addRoute('/users', roleGuard(['admin', 'supervisor'], usersPage)); addRoute('/assignments', roleGuard(['admin', 'supervisor'], assignmentsPage)); addRoute('/clients', roleGuard(['admin', 'supervisor'], clientsPage)); addRoute('/translations', roleGuard(['admin', 'supervisor'], translationsPage)); // Nav link handling & logout document.addEventListener('DOMContentLoaded', () => { document.getElementById('logoutBtn').addEventListener('click', (e) => { e.preventDefault(); clearSession(); navigate('#/login'); }); if (isLoggedIn() && !canAccessWebsite()) { clearSession(); } window.addEventListener('hashchange', updateNav); updateNav(); startRouter(); });