import { connectSocket } from './socket.js';
import * as dashboard from './views/dashboard.js';
import * as deviceDetail from './views/device-detail.js';
import * as contentLibrary from './views/content-library.js';
import * as settings from './views/settings.js';
import * as login from './views/login.js';
import * as billing from './views/billing.js';
import * as layoutEditor from './views/layout-editor.js';
import * as schedule from './views/schedule.js';
import * as widgets from './views/widgets.js';
import * as videoWall from './views/video-wall.js';
import * as reports from './views/reports.js';
import * as activity from './views/activity.js';
import * as kiosk from './views/kiosk.js';
import * as onboarding from './views/onboarding.js';
import * as help from './views/help.js';
import * as teams from './views/teams.js';
import * as admin from './views/admin.js';
import * as designer from './views/designer.js';
import * as playlists from './views/playlists.js';
import { applyBranding } from './branding.js';
import { t } from './i18n.js';
const app = document.getElementById('app');
const sidebar = document.querySelector('.sidebar');
let currentView = null;
// Map nav-link data-view to its translation key.
const NAV_LABEL_KEYS = {
dashboard: 'nav.displays',
content: 'nav.content',
playlists: 'nav.playlists',
layouts: 'nav.layouts',
widgets: 'nav.widgets',
schedule: 'nav.schedule',
walls: 'nav.walls',
reports: 'nav.reports',
kiosk: 'nav.kiosk',
designer: 'nav.designer',
activity: 'nav.activity',
teams: 'nav.teams',
help: 'nav.help',
settings: 'nav.settings',
billing: 'nav.subscription',
admin: 'nav.admin',
};
function renderNavLabels() {
document.querySelectorAll('.nav-link').forEach((link) => {
const key = NAV_LABEL_KEYS[link.dataset.view];
if (!key) return;
const span = link.querySelector('span');
if (span) span.textContent = t(key);
});
}
function isAuthenticated() {
return !!localStorage.getItem('token');
}
function getCurrentUser() {
try {
return JSON.parse(localStorage.getItem('user'));
} catch { return null; }
}
// Refresh the cached user from the server. The server reads plan_id fresh
// from the DB on every request, but the frontend only wrote `user` into
// localStorage at login — so plan/role changes made by an admin weren't
// visible until the user logged out and back in.
async function refreshCurrentUser() {
const token = localStorage.getItem('token');
if (!token) return;
try {
const res = await fetch('/api/auth/me', { headers: { Authorization: `Bearer ${token}` } });
if (!res.ok) return;
const fresh = await res.json();
localStorage.setItem('user', JSON.stringify(fresh));
window.dispatchEvent(new CustomEvent('user-refreshed', { detail: fresh }));
} catch {}
}
function route() {
// Cleanup previous view
if (currentView && currentView.cleanup) currentView.cleanup();
const hash = window.location.hash || '#/';
// Auth check - redirect to login if not authenticated
if (!isAuthenticated() && hash !== '#/login') {
window.location.hash = '#/login';
return;
}
// If authenticated and on login page, redirect to dashboard or onboarding
if (isAuthenticated() && hash === '#/login') {
window.location.hash = localStorage.getItem('rd_onboarded') ? '#/' : '#/onboarding';
return;
}
// Onboarding for new users
if (hash === '#/onboarding' && isAuthenticated()) {
sidebar.style.display = 'none';
app.style.marginLeft = '0';
currentView = onboarding;
onboarding.render(app);
return;
}
// Login page - hide sidebar
if (hash === '#/login') {
sidebar.style.display = 'none';
app.style.marginLeft = '0';
const mb = document.getElementById('mobileMenuBtn');
if (mb) mb.style.display = 'none';
currentView = login;
login.render(app);
return;
}
// Show sidebar for authenticated views
sidebar.style.display = '';
app.style.marginLeft = '';
const mb = document.getElementById('mobileMenuBtn');
if (mb) mb.style.display = '';
// Update user info in sidebar
updateSidebarUser();
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
link.classList.remove('active');
if (hash === '#/' && link.dataset.view === 'dashboard') link.classList.add('active');
else if (hash.startsWith('#/content') && link.dataset.view === 'content') link.classList.add('active');
else if (hash.startsWith('#/settings') && link.dataset.view === 'settings') link.classList.add('active');
else if (hash.startsWith('#/billing') && link.dataset.view === 'billing') link.classList.add('active');
else if ((hash.startsWith('#/layout') || hash === '#/layouts') && link.dataset.view === 'layouts') link.classList.add('active');
else if ((hash === '#/playlists' || hash.startsWith('#/playlists/')) && link.dataset.view === 'playlists') link.classList.add('active');
else if (hash === '#/schedule' && link.dataset.view === 'schedule') link.classList.add('active');
else if (hash === '#/widgets' && link.dataset.view === 'widgets') link.classList.add('active');
else if ((hash.startsWith('#/wall') || hash === '#/walls') && link.dataset.view === 'walls') link.classList.add('active');
else if (hash === '#/reports' && link.dataset.view === 'reports') link.classList.add('active');
else if (hash === '#/activity' && link.dataset.view === 'activity') link.classList.add('active');
else if (hash === '#/designer' && link.dataset.view === 'designer') link.classList.add('active');
else if ((hash === '#/kiosk' || hash.startsWith('#/kiosk/')) && link.dataset.view === 'kiosk') link.classList.add('active');
else if (hash === '#/help' && link.dataset.view === 'help') link.classList.add('active');
else if (hash.startsWith('#/device/') && link.dataset.view === 'dashboard') link.classList.add('active');
});
// Route to view
if (hash === '#/' || hash === '#' || hash === '') {
currentView = dashboard;
dashboard.render(app);
} else if (hash.startsWith('#/device/')) {
const deviceId = hash.split('#/device/')[1].split('/')[0];
currentView = deviceDetail;
deviceDetail.render(app, deviceId);
} else if (hash === '#/content') {
currentView = contentLibrary;
contentLibrary.render(app);
} else if (hash === '#/playlists' || hash.startsWith('#/playlists/')) {
currentView = playlists;
playlists.render(app);
} else if (hash === '#/layouts' || hash.startsWith('#/layout/')) {
currentView = layoutEditor;
layoutEditor.render(app);
} else if (hash === '#/schedule') {
currentView = schedule;
schedule.render(app);
} else if (hash === '#/widgets') {
currentView = widgets;
widgets.render(app);
} else if (hash === '#/walls' || hash.startsWith('#/wall/')) {
currentView = videoWall;
videoWall.render(app);
} else if (hash === '#/reports') {
currentView = reports;
reports.render(app);
} else if (hash === '#/kiosk' || hash.startsWith('#/kiosk/')) {
currentView = kiosk;
kiosk.render(app);
} else if (hash === '#/designer') {
currentView = designer;
designer.render(app);
} else if (hash === '#/activity') {
currentView = activity;
activity.render(app);
} else if (hash === '#/teams' || hash.startsWith('#/team/')) {
currentView = teams;
teams.render(app);
} else if (hash === '#/help' || hash.startsWith('#/help')) {
currentView = help;
help.render(app);
} else if (hash === '#/admin') {
currentView = admin;
admin.render(app);
} else if (hash === '#/settings') {
currentView = settings;
settings.render(app);
} else if (hash === '#/billing') {
currentView = billing;
billing.render(app);
} else {
currentView = dashboard;
dashboard.render(app);
}
}
function updateSidebarUser() {
const user = getCurrentUser();
if (!user) return;
// Show admin nav only for superadmins
const adminNav = document.getElementById('adminNavItem');
if (adminNav) adminNav.style.display = user.role === 'superadmin' ? '' : 'none';
let userEl = document.getElementById('sidebarUser');
if (!userEl) {
const footer = document.querySelector('.sidebar-footer');
userEl = document.createElement('div');
userEl.id = 'sidebarUser';
userEl.style.cssText = 'display:flex;align-items:center;gap:8px;margin-bottom:12px;padding-bottom:12px;border-bottom:1px solid var(--border)';
footer.insertBefore(userEl, footer.firstChild);
}
userEl.innerHTML = `
${user.avatar_url ? `` :
`