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'; @customElement('ob-view-dashboard') export class ObViewDashboard extends DeesElement { @state() accessor systemState: appstate.ISystemState = { status: null }; @state() accessor servicesState: appstate.IServicesState = { services: [], currentService: null, currentServiceLogs: [], currentServiceStats: null, platformServices: [], currentPlatformService: null, }; @state() accessor networkState: appstate.INetworkState = { targets: [], stats: null, trafficStats: null, dnsRecords: [], domains: [], certificates: [], }; constructor() { super(); const systemSub = appstate.systemStatePart .select((s) => s) .subscribe((newState) => { this.systemState = newState; }); this.rxSubscriptions.push(systemSub); const servicesSub = appstate.servicesStatePart .select((s) => s) .subscribe((newState) => { this.servicesState = newState; }); this.rxSubscriptions.push(servicesSub); const networkSub = appstate.networkStatePart .select((s) => s) .subscribe((newState) => { this.networkState = newState; }); this.rxSubscriptions.push(networkSub); } public static styles = [ cssManager.defaultStyles, shared.viewHostCss, css``, ]; async connectedCallback() { super.connectedCallback(); await Promise.all([ appstate.systemStatePart.dispatchAction(appstate.fetchSystemStatusAction, null), appstate.servicesStatePart.dispatchAction(appstate.fetchServicesAction, null), appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServicesAction, null), appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null), appstate.networkStatePart.dispatchAction(appstate.fetchCertificatesAction, null), ]); } public render(): TemplateResult { const status = this.systemState.status; const services = this.servicesState.services; const platformServices = this.servicesState.platformServices; const networkStats = this.networkState.stats; const certificates = this.networkState.certificates; const runningServices = services.filter((s) => s.status === 'running').length; const stoppedServices = services.filter((s) => s.status === 'stopped').length; const validCerts = certificates.filter((c) => c.isValid).length; const expiringCerts = certificates.filter( (c) => c.isValid && c.expiresAt && c.expiresAt - Date.now() < 30 * 24 * 60 * 60 * 1000, ).length; const expiredCerts = certificates.filter((c) => !c.isValid).length; return html` Dashboard ({ name: ps.displayName, status: ps.status === 'running' ? 'running' : 'stopped', running: ps.status === 'running', })), traffic: { requests: 0, errors: 0, errorPercent: 0, avgResponse: 0, reqPerMin: 0, status2xx: 0, status3xx: 0, status4xx: 0, status5xx: 0, }, proxy: { httpPort: networkStats?.proxy?.httpPort || 80, httpsPort: networkStats?.proxy?.httpsPort || 443, httpActive: networkStats?.proxy?.running || false, httpsActive: networkStats?.proxy?.running || false, routeCount: networkStats?.proxy?.routes || 0, }, certificates: { valid: validCerts, expiring: expiringCerts, expired: expiredCerts, }, dnsConfigured: true, acmeConfigured: true, quickActions: [ { label: 'Deploy Service', icon: 'lucide:Plus', primary: true }, { label: 'Add Domain', icon: 'lucide:Globe' }, { label: 'View Logs', icon: 'lucide:FileText' }, ], }} @action-click=${(e: CustomEvent) => this.handleQuickAction(e)} > `; } private handleQuickAction(e: CustomEvent) { const action = e.detail?.action || e.detail?.label; if (action === 'Deploy Service') { appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, { view: 'services' }); } else if (action === 'Add Domain') { appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, { view: 'network' }); } } }