/** * URL router for the SipRouter dashboard. * Maps URL paths to views in dees-simple-appdash. */ const VIEWS = ['overview', 'calls', 'phone', 'routes', 'contacts', 'providers', 'log'] as const; type TViewSlug = (typeof VIEWS)[number]; class AppRouter { private currentView: TViewSlug = 'overview'; private onNavigate: ((view: TViewSlug) => void) | null = null; private suppressPush = false; init(): void { // Parse initial URL. const path = location.pathname.replace(/^\/+/, '').split('/')[0] || 'overview'; if (VIEWS.includes(path as TViewSlug)) { this.currentView = path as TViewSlug; } // Handle browser back/forward. window.addEventListener('popstate', () => { const p = location.pathname.replace(/^\/+/, '').split('/')[0] || 'overview'; if (VIEWS.includes(p as TViewSlug)) { this.suppressPush = true; this.navigateTo(p as TViewSlug); this.suppressPush = false; } }); } setNavigateHandler(handler: (view: TViewSlug) => void): void { this.onNavigate = handler; } navigateTo(view: TViewSlug, skipCallback = false): void { this.currentView = view; if (!this.suppressPush) { const url = `/${view}`; if (location.pathname !== url) { history.pushState(null, '', url); } } if (!skipCallback) { this.onNavigate?.(view); } } /** Called when the user selects a tab in dees-simple-appdash. */ onViewSelect(viewName: string): void { const slug = viewName.toLowerCase().replace(/\s+/g, '-'); const mapped = VIEWS.find((v) => v === slug || viewName.toLowerCase().startsWith(v)); if (mapped) { this.navigateTo(mapped); } } getCurrentView(): TViewSlug { return this.currentView; } } export const appRouter = new AppRouter(); export type { TViewSlug };