import { DeesElement, customElement, html, css, cssManager, state, type TemplateResult } from '../plugins.js';
import { type IStatsTile } from '@design.estate/dees-catalog';
import { appState, type IAppState } from '../state/appstate.js';
import { viewHostCss } from './shared/index.js';
@customElement('sipproxy-view-overview')
export class SipproxyViewOverview extends DeesElement {
@state() accessor appData: IAppState = appState.getState();
public static styles = [
cssManager.defaultStyles,
viewHostCss,
css`
:host {
display: block;
padding: 24px;
}
.section-heading {
font-size: 0.7rem;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: #64748b;
margin: 32px 0 12px;
}
.section-heading:first-of-type {
margin-top: 24px;
}
`,
];
connectedCallback() {
super.connectedCallback();
this.rxSubscriptions.push({
unsubscribe: appState.subscribe((s) => {
this.appData = s;
}),
} as any);
}
private fmtUptime(sec: number): string {
const d = Math.floor(sec / 86400);
const h = Math.floor((sec % 86400) / 3600);
const m = Math.floor((sec % 3600) / 60);
const s = sec % 60;
if (d > 0) return `${d}d ${h}h ${m}m`;
return `${h}h ${String(m).padStart(2, '0')}m ${String(s).padStart(2, '0')}s`;
}
public render(): TemplateResult {
const { appData } = this;
const activeCalls = appData.calls?.filter((c) => c.state !== 'terminated') || [];
const activeCount = activeCalls.length;
const registeredProviders = appData.providers?.filter((p) => p.registered).length || 0;
const connectedDevices = appData.devices?.filter((d) => d.connected).length || 0;
const inboundCalls = activeCalls.filter((c) => c.direction === 'inbound').length;
const outboundCalls = activeCalls.filter((c) => c.direction === 'outbound').length;
const webrtcSessions = activeCalls.reduce(
(sum, c) => sum + c.legs.filter((l) => l.type === 'webrtc').length,
0,
);
const totalRtpPackets = activeCalls.reduce(
(sum, c) => sum + c.legs.reduce((lsum, l) => lsum + l.pktSent + l.pktReceived, 0),
0,
);
const tiles: IStatsTile[] = [
{
id: 'active-calls',
title: 'Active Calls',
value: activeCount,
type: 'number',
icon: 'lucide:phone',
color: 'hsl(142.1 76.2% 36.3%)',
description: activeCount === 1 ? '1 call in progress' : `${activeCount} calls in progress`,
},
{
id: 'providers',
title: 'Registered Providers',
value: registeredProviders,
type: 'number',
icon: 'lucide:server',
color: 'hsl(217.2 91.2% 59.8%)',
description: `${appData.providers?.length || 0} configured`,
},
{
id: 'devices',
title: 'Connected Devices',
value: connectedDevices,
type: 'number',
icon: 'lucide:wifi',
color: 'hsl(270 70% 60%)',
description: `${appData.devices?.length || 0} total`,
},
{
id: 'uptime',
title: 'Uptime',
value: this.fmtUptime(appData.uptime),
type: 'text',
icon: 'lucide:clock',
description: 'Since last restart',
},
{
id: 'inbound',
title: 'Inbound Calls',
value: inboundCalls,
type: 'number',
icon: 'lucide:phone-incoming',
description: 'Currently active',
},
{
id: 'outbound',
title: 'Outbound Calls',
value: outboundCalls,
type: 'number',
icon: 'lucide:phone-outgoing',
description: 'Currently active',
},
{
id: 'webrtc',
title: 'WebRTC Sessions',
value: webrtcSessions,
type: 'number',
icon: 'lucide:globe',
color: 'hsl(166 72% 40%)',
description: 'Browser connections',
},
{
id: 'rtp-packets',
title: 'Total RTP Packets',
value: totalRtpPackets,
type: 'number',
icon: 'lucide:radio',
description: 'Sent + received across legs',
},
];
const allDevices = appData.devices || [];
const onlineCount = allDevices.filter((d) => d.connected).length;
return html`