2026-04-09 23:03:55 +00:00
|
|
|
import { DeesElement, customElement, html, css, cssManager, state, type TemplateResult } from '../plugins.js';
|
|
|
|
|
import { deesCatalog } from '../plugins.js';
|
2026-05-22 13:45:21 +00:00
|
|
|
import type { IView } from '@design.estate/dees-catalog';
|
2026-04-09 23:03:55 +00:00
|
|
|
import { NotificationManager } from '../state/notification-manager.js';
|
2026-05-22 13:45:21 +00:00
|
|
|
import { appRouter, type TSubviewSlug, type TViewSlug } from '../router.js';
|
2026-04-09 23:03:55 +00:00
|
|
|
import { SipproxyViewOverview } from './sipproxy-view-overview.js';
|
|
|
|
|
import { SipproxyViewCalls } from './sipproxy-view-calls.js';
|
|
|
|
|
import { SipproxyViewPhone } from './sipproxy-view-phone.js';
|
|
|
|
|
import { SipproxyViewContacts } from './sipproxy-view-contacts.js';
|
|
|
|
|
import { SipproxyViewProviders } from './sipproxy-view-providers.js';
|
|
|
|
|
import { SipproxyViewLog } from './sipproxy-view-log.js';
|
2026-04-10 08:22:12 +00:00
|
|
|
import { SipproxyViewRoutes } from './sipproxy-view-routes.js';
|
2026-04-10 08:54:46 +00:00
|
|
|
import { SipproxyViewVoicemail } from './sipproxy-view-voicemail.js';
|
|
|
|
|
import { SipproxyViewIvr } from './sipproxy-view-ivr.js';
|
2026-04-09 23:03:55 +00:00
|
|
|
|
2026-05-22 13:45:21 +00:00
|
|
|
interface ITabbedView extends IView {
|
|
|
|
|
slug?: string;
|
|
|
|
|
subViews?: ITabbedView[];
|
|
|
|
|
}
|
2026-04-09 23:03:55 +00:00
|
|
|
|
2026-05-22 13:45:21 +00:00
|
|
|
const VIEW_TABS: ITabbedView[] = [
|
|
|
|
|
{
|
|
|
|
|
name: 'Overview',
|
|
|
|
|
slug: 'overview',
|
|
|
|
|
iconName: 'lucide:layoutDashboard',
|
|
|
|
|
subViews: [
|
|
|
|
|
{ name: 'Stats', slug: 'stats', iconName: 'lucide:activity', element: SipproxyViewOverview },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Telephony',
|
|
|
|
|
slug: 'telephony',
|
|
|
|
|
iconName: 'lucide:phoneCall',
|
|
|
|
|
subViews: [
|
|
|
|
|
{ name: 'Calls', slug: 'calls', iconName: 'lucide:phone', element: SipproxyViewCalls },
|
|
|
|
|
{ name: 'Phone', slug: 'phone', iconName: 'lucide:headset', element: SipproxyViewPhone },
|
|
|
|
|
{ name: 'Routes', slug: 'routes', iconName: 'lucide:route', element: SipproxyViewRoutes },
|
|
|
|
|
{ name: 'Voicemail', slug: 'voicemail', iconName: 'lucide:voicemail', element: SipproxyViewVoicemail },
|
|
|
|
|
{ name: 'IVR', slug: 'ivr', iconName: 'lucide:ListTree', element: SipproxyViewIvr },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'Configuration',
|
|
|
|
|
slug: 'configuration',
|
|
|
|
|
iconName: 'lucide:settings',
|
|
|
|
|
subViews: [
|
|
|
|
|
{ name: 'Contacts', slug: 'contacts', iconName: 'lucide:contactRound', element: SipproxyViewContacts },
|
|
|
|
|
{ name: 'Providers', slug: 'providers', iconName: 'lucide:server', element: SipproxyViewProviders },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'System',
|
|
|
|
|
slug: 'system',
|
|
|
|
|
iconName: 'lucide:serverCog',
|
|
|
|
|
subViews: [
|
|
|
|
|
{ name: 'Log', slug: 'log', iconName: 'lucide:scrollText', element: SipproxyViewLog },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
];
|
2026-04-09 23:03:55 +00:00
|
|
|
|
|
|
|
|
@customElement('sipproxy-app')
|
|
|
|
|
export class SipproxyApp extends DeesElement {
|
|
|
|
|
private notificationManager = new NotificationManager();
|
|
|
|
|
private appdash: InstanceType<typeof deesCatalog.DeesSimpleAppDash> | null = null;
|
2026-05-22 13:45:21 +00:00
|
|
|
private viewTabs = VIEW_TABS;
|
2026-04-09 23:03:55 +00:00
|
|
|
|
|
|
|
|
public static styles = [
|
|
|
|
|
cssManager.defaultStyles,
|
|
|
|
|
css`
|
|
|
|
|
:host { display: block; height: 100%; }
|
|
|
|
|
dees-simple-appdash { height: 100%; }
|
|
|
|
|
`,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
private suppressViewSelectEvent = false;
|
|
|
|
|
|
2026-05-22 13:45:21 +00:00
|
|
|
private slugFor(view: ITabbedView): string {
|
|
|
|
|
return view.slug ?? view.name.toLowerCase().replace(/\s+/g, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private findParent(view: ITabbedView): ITabbedView | undefined {
|
|
|
|
|
return this.viewTabs.find((tab) => tab.subViews?.includes(view));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private findViewBySlug(viewSlug: string, subviewSlug: string): ITabbedView | undefined {
|
|
|
|
|
const top = this.viewTabs.find((tab) => this.slugFor(tab) === viewSlug);
|
|
|
|
|
if (!top) return undefined;
|
|
|
|
|
return top.subViews?.find((subview) => this.slugFor(subview) === subviewSlug) ?? top;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private get currentViewTab(): ITabbedView {
|
|
|
|
|
const currentRoute = appRouter.getCurrentRoute();
|
|
|
|
|
return this.findViewBySlug(currentRoute.view, currentRoute.subview) ?? this.viewTabs[0].subViews![0];
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-09 23:03:55 +00:00
|
|
|
async firstUpdated() {
|
|
|
|
|
this.appdash = this.shadowRoot?.querySelector('dees-simple-appdash') as InstanceType<typeof deesCatalog.DeesSimpleAppDash>;
|
|
|
|
|
if (this.appdash) {
|
|
|
|
|
this.notificationManager.init(this.appdash);
|
|
|
|
|
|
2026-05-22 13:45:21 +00:00
|
|
|
// Listen for user tab selections and sync grouped URLs.
|
2026-04-09 23:03:55 +00:00
|
|
|
this.appdash.addEventListener('view-select', ((e: CustomEvent) => {
|
|
|
|
|
if (this.suppressViewSelectEvent) return;
|
2026-05-22 13:45:21 +00:00
|
|
|
const view = e.detail?.view as ITabbedView | undefined;
|
|
|
|
|
if (!view) return;
|
|
|
|
|
|
|
|
|
|
const parent = this.findParent(view);
|
|
|
|
|
if (!parent) return;
|
|
|
|
|
|
|
|
|
|
const parentSlug = this.slugFor(parent) as TViewSlug;
|
|
|
|
|
const subviewSlug = this.slugFor(view) as TSubviewSlug;
|
|
|
|
|
if (!appRouter.isCurrentRoute(parentSlug, subviewSlug)) {
|
|
|
|
|
appRouter.navigateToView(parentSlug, subviewSlug, true);
|
2026-04-09 23:03:55 +00:00
|
|
|
}
|
|
|
|
|
}) as EventListener);
|
|
|
|
|
|
|
|
|
|
// Wire up router -> appdash (for browser back/forward).
|
2026-05-22 13:45:21 +00:00
|
|
|
appRouter.setNavigateHandler((view, subview) => {
|
|
|
|
|
const tab = this.findViewBySlug(view, subview);
|
2026-04-09 23:03:55 +00:00
|
|
|
if (tab && this.appdash) {
|
|
|
|
|
this.suppressViewSelectEvent = true;
|
|
|
|
|
this.appdash.loadView(tab);
|
|
|
|
|
this.suppressViewSelectEvent = false;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2026-05-22 13:45:21 +00:00
|
|
|
const initialTab = this.currentViewTab;
|
|
|
|
|
if (initialTab) {
|
|
|
|
|
this.suppressViewSelectEvent = true;
|
|
|
|
|
this.appdash.loadView(initialTab);
|
|
|
|
|
this.suppressViewSelectEvent = false;
|
2026-04-09 23:03:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-22 13:45:21 +00:00
|
|
|
public async disconnectedCallback() {
|
|
|
|
|
await super.disconnectedCallback();
|
2026-04-09 23:03:55 +00:00
|
|
|
this.notificationManager.destroy();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public render(): TemplateResult {
|
|
|
|
|
return html`
|
|
|
|
|
<dees-simple-appdash
|
|
|
|
|
.name=${'SipRouter'}
|
2026-05-22 13:45:21 +00:00
|
|
|
.viewTabs=${this.viewTabs}
|
|
|
|
|
.selectedView=${this.currentViewTab}
|
2026-04-09 23:03:55 +00:00
|
|
|
></dees-simple-appdash>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
}
|