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, } from '@design.estate/dees-element'; @customElement('ops-view-logs') export class OpsViewLogs extends DeesElement { @state() accessor logState: appstate.ILogState = { recentLogs: [], isStreaming: false, filters: {}, }; @state() accessor filterLevel: string | undefined; @state() accessor filterCategory: string | undefined; @state() accessor filterLimit: number = 100; constructor() { super(); const subscription = appstate.logStatePart .select((stateArg) => stateArg) .subscribe((logState) => { this.logState = logState; }); this.rxSubscriptions.push(subscription); } public static styles = [ cssManager.defaultStyles, shared.viewHostCss, css` .controls { display: flex; gap: 16px; margin-bottom: 24px; flex-wrap: wrap; } .filterGroup { display: flex; align-items: center; gap: 8px; } .logContainer { background: ${cssManager.bdTheme('#f8f9fa', '#1e1e1e')}; border-radius: 8px; padding: 16px; max-height: 600px; overflow-y: auto; font-family: 'Consolas', 'Monaco', monospace; font-size: 13px; } .logEntry { margin-bottom: 8px; line-height: 1.5; } .logTimestamp { color: ${cssManager.bdTheme('#7a7a7a', '#7a7a7a')}; margin-right: 8px; } .logLevel { font-weight: bold; margin-right: 8px; padding: 2px 6px; border-radius: 3px; font-size: 11px; } .logLevel.debug { color: ${cssManager.bdTheme('#6a9955', '#6a9955')}; background: ${cssManager.bdTheme('rgba(106, 153, 85, 0.1)', 'rgba(106, 153, 85, 0.1)')}; } .logLevel.info { color: ${cssManager.bdTheme('#569cd6', '#569cd6')}; background: ${cssManager.bdTheme('rgba(86, 156, 214, 0.1)', 'rgba(86, 156, 214, 0.1)')}; } .logLevel.warn { color: ${cssManager.bdTheme('#ce9178', '#ce9178')}; background: ${cssManager.bdTheme('rgba(206, 145, 120, 0.1)', 'rgba(206, 145, 120, 0.1)')}; } .logLevel.error { color: ${cssManager.bdTheme('#f44747', '#f44747')}; background: ${cssManager.bdTheme('rgba(244, 71, 71, 0.1)', 'rgba(244, 71, 71, 0.1)')}; } .logCategory { color: ${cssManager.bdTheme('#c586c0', '#c586c0')}; margin-right: 8px; } .logMessage { color: ${cssManager.bdTheme('#333', '#d4d4d4')}; } .noLogs { color: ${cssManager.bdTheme('#7a7a7a', '#7a7a7a')}; text-align: center; padding: 40px; } `, ]; public render() { return html` Logs
this.fetchLogs()} > Refresh Logs this.toggleStreaming()} .type=${this.logState.isStreaming ? 'highlighted' : 'normal'} > ${this.logState.isStreaming ? 'Stop Streaming' : 'Start Streaming'}
this.updateFilter('level', e.detail)} >
this.updateFilter('category', e.detail)} >
this.updateFilter('limit', e.detail)} >
${this.logState.recentLogs.length > 0 ? this.logState.recentLogs.map(log => html`
${new Date(log.timestamp).toLocaleTimeString()} ${log.level.toUpperCase()} [${log.category}] ${log.message}
`) : html`
No logs to display
` }
`; } async connectedCallback() { super.connectedCallback(); // Auto-fetch logs when the view mounts this.fetchLogs(); } private async fetchLogs() { await appstate.logStatePart.dispatchAction(appstate.fetchRecentLogsAction, { limit: this.filterLimit, level: this.filterLevel as 'debug' | 'info' | 'warn' | 'error' | undefined, category: this.filterCategory as 'smtp' | 'dns' | 'security' | 'system' | 'email' | undefined, }); } private updateFilter(type: string, value: string) { const resolved = value === 'all' ? undefined : value; switch (type) { case 'level': this.filterLevel = resolved; break; case 'category': this.filterCategory = resolved; break; case 'limit': this.filterLimit = resolved ? parseInt(resolved, 10) : 100; break; } this.fetchLogs(); } private getActiveFilters() { return { level: this.filterLevel, category: this.filterCategory, limit: this.filterLimit, }; } private toggleStreaming() { // TODO: Implement log streaming with VirtualStream console.log('Streaming toggle not yet implemented'); } }