feat(admin-ui): introduce view layer and refactor admin UI to use view components, consolidate demos, and update interfaces

This commit is contained in:
2025-12-27 12:33:14 +00:00
parent 87ac6e506f
commit c5632dae77
18 changed files with 875 additions and 754 deletions

View File

@@ -0,0 +1,244 @@
import {
DeesElement,
customElement,
html,
state,
css,
cssManager,
} from '@design.estate/dees-element';
import type { DeesAppuiBase } from '@design.estate/dees-catalog';
// View lifecycle interfaces (defined locally as they're not exported from dees-catalog)
interface IViewActivationContext {
appui: DeesAppuiBase;
viewId: string;
params?: Record<string, string>;
}
interface IViewLifecycle {
onActivate?: (context: IViewActivationContext) => void | Promise<void>;
onDeactivate?: () => void | Promise<void>;
}
import { adminState } from '../../services/admin-state.js';
import type { IServiceStatus, TStatusType } from '../../interfaces/index.js';
import '../../elements/upladmin-monitor-list/upladmin-monitor-list.js';
import '../../elements/upladmin-monitor-form/upladmin-monitor-form.js';
type TViewMode = 'list' | 'form';
type TStatusFilter = 'all' | 'issues' | 'maintenance';
@customElement('upladmin-monitors-view')
export class UpladminMonitorsView extends DeesElement implements IViewLifecycle {
@state()
accessor currentMode: TViewMode = 'list';
@state()
accessor selectedMonitorId: string | null = null;
@state()
accessor statusFilter: TStatusFilter = 'all';
@state()
accessor categoryFilter: string = 'all';
@state()
accessor loading: boolean = false;
private appuiRef: DeesAppuiBase | null = null;
public static styles = [
cssManager.defaultStyles,
css`
:host {
display: block;
height: 100%;
}
`,
];
async onActivate(context: IViewActivationContext): Promise<void> {
this.appuiRef = context.appui;
// Check route params for edit mode
if (context.params?.id) {
this.currentMode = 'form';
this.selectedMonitorId = context.params.id === 'create' ? null : context.params.id;
} else {
this.currentMode = 'list';
this.selectedMonitorId = null;
}
// Set secondary menu with categories
this.updateSecondaryMenu();
// Set content tabs for status filtering
context.appui.setContentTabs([
{
key: 'All',
iconName: 'lucide:activity',
action: () => this.setStatusFilter('all'),
},
{
key: 'Issues',
iconName: 'lucide:alertTriangle',
action: () => this.setStatusFilter('issues'),
},
{
key: 'Maintenance',
iconName: 'lucide:wrench',
action: () => this.setStatusFilter('maintenance'),
},
]);
}
private updateSecondaryMenu(): void {
if (!this.appuiRef) return;
const categories = this.getCategories();
this.appuiRef.setSecondaryMenu({
heading: 'Monitors',
groups: [
{
name: 'Categories',
iconName: 'lucide:folder',
items: [
{
key: 'all',
iconName: 'lucide:list',
action: () => this.setCategoryFilter('all'),
badge: adminState.monitors.length,
},
...categories.map((cat) => ({
key: cat,
iconName: 'lucide:folder',
action: () => this.setCategoryFilter(cat),
badge: adminState.monitors.filter((m) => m.category === cat).length,
})),
],
},
{
name: 'Actions',
iconName: 'lucide:zap',
items: [
{
key: 'add',
iconName: 'lucide:plus',
action: () => this.showForm(null),
},
],
},
],
});
// Select current category
this.appuiRef.setSecondaryMenuSelection(this.categoryFilter);
}
private getCategories(): string[] {
const cats = new Set<string>();
for (const m of adminState.monitors) {
if (m.category) cats.add(m.category);
}
return Array.from(cats).sort();
}
private setStatusFilter(filter: TStatusFilter): void {
this.statusFilter = filter;
}
private setCategoryFilter(category: string): void {
this.categoryFilter = category;
this.appuiRef?.setSecondaryMenuSelection(category);
}
private showForm(monitorId: string | null): void {
this.currentMode = 'form';
this.selectedMonitorId = monitorId;
}
private showList(): void {
this.currentMode = 'list';
this.selectedMonitorId = null;
}
private get filteredMonitors(): IServiceStatus[] {
let monitors = adminState.monitors;
// Apply category filter
if (this.categoryFilter !== 'all') {
monitors = monitors.filter((m) => m.category === this.categoryFilter);
}
// Apply status filter
if (this.statusFilter === 'issues') {
monitors = monitors.filter((m) =>
['degraded', 'partial_outage', 'major_outage', 'error'].includes(m.currentStatus)
);
} else if (this.statusFilter === 'maintenance') {
monitors = monitors.filter((m) => m.currentStatus === 'maintenance');
}
return monitors;
}
private handleMonitorSave = (e: CustomEvent): void => {
// Handle save logic
console.log('Monitor saved:', e.detail);
this.showList();
};
private handleCancel = (): void => {
this.showList();
};
private handleMonitorEdit = (e: CustomEvent): void => {
const monitor = e.detail?.monitor as IServiceStatus;
if (monitor) {
this.showForm(monitor.id);
}
};
render() {
if (this.currentMode === 'form') {
const monitor = this.selectedMonitorId
? adminState.monitors.find((m) => m.id === this.selectedMonitorId)
: null;
return html`
<upladmin-monitor-form
.monitor=${monitor
? {
id: monitor.id,
name: monitor.name,
displayName: monitor.displayName,
description: monitor.description,
category: monitor.category,
dependencies: monitor.dependencies,
statusMode: monitor.statusMode || 'auto',
manualStatus: monitor.manualStatus,
paused: monitor.paused || false,
checkType: monitor.checkType || 'assumption',
checkConfig: monitor.checkConfig || { domain: '' },
intervalMs: monitor.intervalMs || 60000,
}
: null}
.availableMonitors=${adminState.monitors}
.categories=${this.getCategories()}
.loading=${this.loading}
@monitorSave=${this.handleMonitorSave}
@cancel=${this.handleCancel}
></upladmin-monitor-form>
`;
}
return html`
<upladmin-monitor-list
.monitors=${this.filteredMonitors}
.loading=${this.loading}
@monitorAdd=${() => this.showForm(null)}
@monitorEdit=${this.handleMonitorEdit}
></upladmin-monitor-list>
`;
}
}