import * as plugins from '../plugins.js'; import * as shared from './shared/index.js'; import * as appstate from '../appstate.js'; import { DeesElement, customElement, html, state, css, cssManager, type TemplateResult, } from '@design.estate/dees-element'; import { type IStatsTile } from '@design.estate/dees-catalog'; @customElement('ops-view-overview') export class OpsViewOverview extends DeesElement { @state() private statsState: appstate.IStatsState = { serverStats: null, emailStats: null, dnsStats: null, securityMetrics: null, lastUpdated: 0, isLoading: false, error: null, }; constructor() { super(); const subscription = appstate.statsStatePart .select((stateArg) => stateArg) .subscribe((statsState) => { this.statsState = statsState; }); this.rxSubscriptions.push(subscription); } public static styles = [ cssManager.defaultStyles, shared.viewHostCss, css` h2 { margin: 32px 0 16px 0; font-size: 24px; font-weight: 600; color: ${cssManager.bdTheme('#333', '#ccc')}; } .chartGrid { display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 16px; margin-top: 32px; } .loadingMessage { text-align: center; padding: 40px; color: ${cssManager.bdTheme('#666', '#999')}; } .errorMessage { background-color: ${cssManager.bdTheme('#fee', '#4a1f1f')}; border: 1px solid ${cssManager.bdTheme('#fcc', '#6a2f2f')}; border-radius: 4px; padding: 16px; color: ${cssManager.bdTheme('#c00', '#ff6666')}; margin: 16px 0; } dees-statsgrid { margin-bottom: 32px; } `, ]; public render() { return html` Overview ${this.statsState.isLoading ? html`

Loading statistics...

` : this.statsState.error ? html`
Error loading statistics: ${this.statsState.error}
` : html` ${this.renderServerStats()} ${this.renderEmailStats()} ${this.renderDnsStats()}
`} `; } private formatUptime(seconds: number): string { const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); if (days > 0) { return `${days}d ${hours}h ${minutes}m ${secs}s`; } else if (hours > 0) { return `${hours}h ${minutes}m ${secs}s`; } else if (minutes > 0) { return `${minutes}m ${secs}s`; } else { return `${secs}s`; } } private formatBytes(bytes: number): string { const units = ['B', 'KB', 'MB', 'GB', 'TB']; let size = bytes; let unitIndex = 0; while (size >= 1024 && unitIndex < units.length - 1) { size /= 1024; unitIndex++; } return `${size.toFixed(1)} ${units[unitIndex]}`; } private renderServerStats(): TemplateResult { if (!this.statsState.serverStats) return html``; const cpuUsage = Math.round(this.statsState.serverStats.cpuUsage.user); const memoryUsage = this.statsState.serverStats.memoryUsage.actualUsagePercentage !== undefined ? Math.round(this.statsState.serverStats.memoryUsage.actualUsagePercentage) : Math.round((this.statsState.serverStats.memoryUsage.heapUsed / this.statsState.serverStats.memoryUsage.heapTotal) * 100); const tiles: IStatsTile[] = [ { id: 'status', title: 'Server Status', value: this.statsState.serverStats.uptime ? 'Online' : 'Offline', type: 'text', icon: 'server', color: this.statsState.serverStats.uptime ? '#22c55e' : '#ef4444', description: `Uptime: ${this.formatUptime(this.statsState.serverStats.uptime)}`, }, { id: 'connections', title: 'Active Connections', value: this.statsState.serverStats.activeConnections, type: 'number', icon: 'networkWired', color: '#3b82f6', description: `Total: ${this.statsState.serverStats.totalConnections}`, }, { id: 'cpu', title: 'CPU Usage', value: cpuUsage, type: 'gauge', icon: 'microchip', gaugeOptions: { min: 0, max: 100, thresholds: [ { value: 0, color: '#22c55e' }, { value: 60, color: '#f59e0b' }, { value: 80, color: '#ef4444' }, ], }, }, { id: 'memory', title: 'Memory Usage', value: memoryUsage, type: 'percentage', icon: 'memory', color: memoryUsage > 80 ? '#ef4444' : memoryUsage > 60 ? '#f59e0b' : '#22c55e', description: this.statsState.serverStats.memoryUsage.actualUsageBytes !== undefined && this.statsState.serverStats.memoryUsage.maxMemoryMB !== undefined ? `${this.formatBytes(this.statsState.serverStats.memoryUsage.actualUsageBytes)} / ${this.formatBytes(this.statsState.serverStats.memoryUsage.maxMemoryMB * 1024 * 1024)}` : `${this.formatBytes(this.statsState.serverStats.memoryUsage.rss)} / ${this.formatBytes(this.statsState.serverStats.memoryUsage.heapTotal)}`, }, ]; return html` { await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null); }, }, ]} > `; } private renderEmailStats(): TemplateResult { if (!this.statsState.emailStats) return html``; const deliveryRate = this.statsState.emailStats.deliveryRate || 0; const bounceRate = this.statsState.emailStats.bounceRate || 0; const tiles: IStatsTile[] = [ { id: 'sent', title: 'Emails Sent', value: this.statsState.emailStats.sent, type: 'number', icon: 'paperPlane', color: '#22c55e', description: `Delivery rate: ${(deliveryRate * 100).toFixed(1)}%`, }, { id: 'received', title: 'Emails Received', value: this.statsState.emailStats.received, type: 'number', icon: 'envelope', color: '#3b82f6', }, { id: 'queued', title: 'Queued', value: this.statsState.emailStats.queued, type: 'number', icon: 'clock', color: '#f59e0b', description: 'Pending delivery', }, { id: 'failed', title: 'Failed', value: this.statsState.emailStats.failed, type: 'number', icon: 'triangleExclamation', color: '#ef4444', description: `Bounce rate: ${(bounceRate * 100).toFixed(1)}%`, }, ]; return html`

Email Statistics

`; } private renderDnsStats(): TemplateResult { if (!this.statsState.dnsStats) return html``; const cacheHitRate = Math.round(this.statsState.dnsStats.cacheHitRate * 100); const tiles: IStatsTile[] = [ { id: 'queries', title: 'DNS Queries', value: this.statsState.dnsStats.totalQueries, type: 'number', icon: 'globe', color: '#3b82f6', description: 'Total queries handled', }, { id: 'cacheRate', title: 'Cache Hit Rate', value: cacheHitRate, type: 'percentage', icon: 'database', color: cacheHitRate > 80 ? '#22c55e' : cacheHitRate > 60 ? '#f59e0b' : '#ef4444', description: `${this.statsState.dnsStats.cacheHits} hits / ${this.statsState.dnsStats.cacheMisses} misses`, }, { id: 'domains', title: 'Active Domains', value: this.statsState.dnsStats.activeDomains, type: 'number', icon: 'sitemap', color: '#8b5cf6', }, { id: 'responseTime', title: 'Avg Response Time', value: this.statsState.dnsStats.averageResponseTime.toFixed(1), unit: 'ms', type: 'number', icon: 'clockRotateLeft', color: this.statsState.dnsStats.averageResponseTime < 50 ? '#22c55e' : '#f59e0b', }, ]; return html`

DNS Statistics

`; } }