import { LitElement, html, css, state, customElement } from './plugins.js'; import type { CSSResult, TemplateResult } from './plugins.js'; import { sharedStyles, terminalStyles, navStyles } from './sw-dash-styles.js'; import type { IMetricsData } from './sw-dash-overview.js'; import type { ICachedResource } from './sw-dash-urls.js'; import type { IDomainStats } from './sw-dash-domains.js'; import type { IContentTypeStats } from './sw-dash-types.js'; // Import components to register them import './sw-dash-overview.js'; import './sw-dash-urls.js'; import './sw-dash-domains.js'; import './sw-dash-types.js'; import './sw-dash-events.js'; import './sw-dash-table.js'; type ViewType = 'overview' | 'urls' | 'domains' | 'types' | 'events'; interface IResourceData { resources: ICachedResource[]; domains: IDomainStats[]; contentTypes: IContentTypeStats[]; resourceCount: number; } /** * Main SW Dashboard application shell */ @customElement('sw-dash-app') export class SwDashApp extends LitElement { public static styles: CSSResult[] = [ sharedStyles, terminalStyles, navStyles, css` :host { display: block; background: var(--bg-primary); min-height: 100vh; padding: var(--space-5); } .view { display: none; } .view.active { display: block; } .header-left { display: flex; align-items: center; gap: var(--space-3); } .logo { width: 24px; height: 24px; background: var(--accent-primary); border-radius: var(--radius-sm); display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 12px; color: white; } .uptime-badge { display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2); background: var(--bg-tertiary); border-radius: var(--radius-sm); font-size: 11px; color: var(--text-tertiary); } .uptime-badge .value { color: var(--text-primary); font-weight: 500; font-variant-numeric: tabular-nums; } .footer-left { display: flex; align-items: center; gap: var(--space-2); color: var(--text-tertiary); font-size: 11px; } .footer-right { display: flex; align-items: center; gap: var(--space-2); } .auto-refresh { display: inline-flex; align-items: center; gap: var(--space-1); padding: var(--space-1) var(--space-2); background: rgba(34, 197, 94, 0.1); color: var(--accent-success); border-radius: var(--radius-sm); font-size: 11px; font-weight: 500; } .auto-refresh .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } ` ]; @state() accessor currentView: ViewType = 'overview'; @state() accessor metrics: IMetricsData | null = null; @state() accessor resourceData: IResourceData = { resources: [], domains: [], contentTypes: [], resourceCount: 0 }; @state() accessor lastRefresh = new Date().toLocaleTimeString(); private refreshInterval: ReturnType | null = null; connectedCallback(): void { super.connectedCallback(); this.loadMetrics(); this.loadResourceData(); // Auto-refresh every 2 seconds this.refreshInterval = setInterval(() => { this.loadMetrics(); if (this.currentView !== 'overview') { this.loadResourceData(); } }, 2000); } disconnectedCallback(): void { super.disconnectedCallback(); if (this.refreshInterval) { clearInterval(this.refreshInterval); } } private async loadMetrics(): Promise { try { const response = await fetch('/sw-dash/metrics'); this.metrics = await response.json(); this.lastRefresh = new Date().toLocaleTimeString(); } catch (err) { console.error('Failed to load metrics:', err); } } private async loadResourceData(): Promise { try { const response = await fetch('/sw-dash/resources'); this.resourceData = await response.json(); } catch (err) { console.error('Failed to load resources:', err); } } private setView(view: ViewType): void { this.currentView = view; if (view !== 'overview') { this.loadResourceData(); } } private handleSpeedtestComplete(_e: CustomEvent): void { // Refresh metrics after speedtest this.loadMetrics(); } private formatUptime(ms: number): string { const s = Math.floor(ms / 1000); const m = Math.floor(s / 60); const h = Math.floor(m / 60); const d = Math.floor(h / 24); if (d > 0) return `${d}d ${h % 24}h`; if (h > 0) return `${h}h ${m % 60}m`; if (m > 0) return `${m}m ${s % 60}s`; return `${s}s`; } public render(): TemplateResult { return html`
Service Worker Dashboard
Uptime: ${this.metrics ? this.formatUptime(this.metrics.uptime) : '--'}
`; } }