Files
dcrouter/ts_web/elements/ops-view-logs.ts

207 lines
5.2 KiB
TypeScript
Raw Normal View History

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()
private logState: appstate.ILogState = {
recentLogs: [],
isStreaming: false,
filters: {},
};
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: #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: #7a7a7a;
margin-right: 8px;
}
.logLevel {
font-weight: bold;
margin-right: 8px;
padding: 2px 6px;
border-radius: 3px;
font-size: 11px;
}
.logLevel.debug {
color: #6a9955;
background: rgba(106, 153, 85, 0.1);
}
.logLevel.info {
color: #569cd6;
background: rgba(86, 156, 214, 0.1);
}
.logLevel.warn {
color: #ce9178;
background: rgba(206, 145, 120, 0.1);
}
.logLevel.error {
color: #f44747;
background: rgba(244, 71, 71, 0.1);
}
.logCategory {
color: #c586c0;
margin-right: 8px;
}
.logMessage {
color: #d4d4d4;
}
.noLogs {
color: #7a7a7a;
text-align: center;
padding: 40px;
}
`,
];
public render() {
return html`
<ops-sectionheading>Logs</ops-sectionheading>
<div class="controls">
<div class="filterGroup">
<dees-button
@click=${() => this.fetchLogs()}
>
Refresh Logs
</dees-button>
<dees-button
@click=${() => this.toggleStreaming()}
.type=${this.logState.isStreaming ? 'highlighted' : 'normal'}
>
${this.logState.isStreaming ? 'Stop Streaming' : 'Start Streaming'}
</dees-button>
</div>
<div class="filterGroup">
<label>Level:</label>
<dees-input-dropdown
.options=${['all', 'debug', 'info', 'warn', 'error']}
.selectedOption=${'all'}
@selectedOption=${(e) => this.updateFilter('level', e.detail)}
></dees-input-dropdown>
</div>
<div class="filterGroup">
<label>Category:</label>
<dees-input-dropdown
.options=${['all', 'smtp', 'dns', 'security', 'system', 'email']}
.selectedOption=${'all'}
@selectedOption=${(e) => this.updateFilter('category', e.detail)}
></dees-input-dropdown>
</div>
<div class="filterGroup">
<label>Limit:</label>
<dees-input-dropdown
.options=${['50', '100', '200', '500']}
.selectedOption=${'100'}
@selectedOption=${(e) => this.updateFilter('limit', e.detail)}
></dees-input-dropdown>
</div>
</div>
<div class="logContainer">
${this.logState.recentLogs.length > 0 ?
this.logState.recentLogs.map(log => html`
<div class="logEntry">
<span class="logTimestamp">${new Date(log.timestamp).toLocaleTimeString()}</span>
<span class="logLevel ${log.level}">${log.level.toUpperCase()}</span>
<span class="logCategory">[${log.category}]</span>
<span class="logMessage">${log.message}</span>
</div>
`) : html`
<div class="noLogs">No logs to display</div>
`
}
</div>
`;
}
private async fetchLogs() {
const filters = this.getActiveFilters();
await appstate.logStatePart.dispatchAction(appstate.fetchRecentLogsAction, {
limit: filters.limit || 100,
level: filters.level as 'debug' | 'info' | 'warn' | 'error' | undefined,
category: filters.category as 'smtp' | 'dns' | 'security' | 'system' | 'email' | undefined,
});
}
private updateFilter(type: string, value: string) {
if (value === 'all') {
value = undefined;
}
// Update filters then fetch logs
this.fetchLogs();
}
private getActiveFilters() {
return {
level: this.logState.filters.level?.[0],
category: this.logState.filters.category?.[0],
limit: 100,
};
}
private toggleStreaming() {
// TODO: Implement log streaming with VirtualStream
console.log('Streaming toggle not yet implemented');
}
}