293 lines
8.2 KiB
TypeScript
293 lines
8.2 KiB
TypeScript
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 { IIncidentDetails, TIncidentSeverity, TIncidentStatus } from '../../interfaces/index.js';
|
|
import '../../elements/upladmin-incident-list/upladmin-incident-list.js';
|
|
import '../../elements/upladmin-incident-form/upladmin-incident-form.js';
|
|
import '../../elements/upladmin-incident-update/upladmin-incident-update.js';
|
|
|
|
type TViewMode = 'list' | 'form' | 'update';
|
|
type TTimeFilter = 'current' | 'past' | 'all';
|
|
|
|
@customElement('upladmin-incidents-view')
|
|
export class UpladminIncidentsView extends DeesElement implements IViewLifecycle {
|
|
@state()
|
|
accessor currentMode: TViewMode = 'list';
|
|
|
|
@state()
|
|
accessor selectedIncidentId: string | null = null;
|
|
|
|
@state()
|
|
accessor timeFilter: TTimeFilter = 'current';
|
|
|
|
@state()
|
|
accessor severityFilter: TIncidentSeverity | 'all' = '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 and view ID
|
|
if (context.params?.id) {
|
|
if (context.viewId === 'incident-update') {
|
|
this.currentMode = 'update';
|
|
this.selectedIncidentId = context.params.id;
|
|
} else {
|
|
this.currentMode = 'form';
|
|
this.selectedIncidentId = context.params.id === 'create' ? null : context.params.id;
|
|
}
|
|
} else {
|
|
this.currentMode = 'list';
|
|
this.selectedIncidentId = null;
|
|
}
|
|
|
|
// Set secondary menu
|
|
this.updateSecondaryMenu();
|
|
|
|
// No content tabs - incident-list has internal tabs
|
|
context.appui.setContentTabs([]);
|
|
}
|
|
|
|
private updateSecondaryMenu(): void {
|
|
if (!this.appuiRef) return;
|
|
|
|
const activeCount = adminState.getActiveIncidents().length;
|
|
const pastCount = adminState.incidents.filter((i) => i.status === 'resolved' || i.status === 'postmortem').length;
|
|
|
|
this.appuiRef.setSecondaryMenu({
|
|
heading: 'Incidents',
|
|
groups: [
|
|
{
|
|
name: 'Filter',
|
|
iconName: 'lucide:filter',
|
|
items: [
|
|
{
|
|
key: 'current',
|
|
iconName: 'lucide:alertCircle',
|
|
action: () => this.setTimeFilter('current'),
|
|
badge: activeCount,
|
|
badgeVariant: activeCount > 0 ? 'error' : 'default',
|
|
},
|
|
{
|
|
key: 'past',
|
|
iconName: 'lucide:history',
|
|
action: () => this.setTimeFilter('past'),
|
|
badge: pastCount,
|
|
},
|
|
{
|
|
key: 'all',
|
|
iconName: 'lucide:list',
|
|
action: () => this.setTimeFilter('all'),
|
|
badge: adminState.incidents.length,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'Severity',
|
|
iconName: 'lucide:alertTriangle',
|
|
collapsed: true,
|
|
items: [
|
|
{
|
|
key: 'critical',
|
|
iconName: 'lucide:xCircle',
|
|
action: () => this.setSeverityFilter('critical'),
|
|
},
|
|
{
|
|
key: 'major',
|
|
iconName: 'lucide:alertOctagon',
|
|
action: () => this.setSeverityFilter('major'),
|
|
},
|
|
{
|
|
key: 'minor',
|
|
iconName: 'lucide:alertTriangle',
|
|
action: () => this.setSeverityFilter('minor'),
|
|
},
|
|
{
|
|
key: 'maintenance',
|
|
iconName: 'lucide:wrench',
|
|
action: () => this.setSeverityFilter('maintenance'),
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'Actions',
|
|
iconName: 'lucide:zap',
|
|
items: [
|
|
{
|
|
key: 'create',
|
|
iconName: 'lucide:plus',
|
|
action: () => this.showForm(null),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
// Select current filter
|
|
this.appuiRef.setSecondaryMenuSelection(this.timeFilter);
|
|
}
|
|
|
|
private setTimeFilter(filter: TTimeFilter): void {
|
|
this.timeFilter = filter;
|
|
this.severityFilter = 'all';
|
|
this.appuiRef?.setSecondaryMenuSelection(filter);
|
|
}
|
|
|
|
private setSeverityFilter(severity: TIncidentSeverity): void {
|
|
this.severityFilter = severity;
|
|
this.appuiRef?.setSecondaryMenuSelection(severity);
|
|
}
|
|
|
|
private showForm(incidentId: string | null): void {
|
|
this.currentMode = 'form';
|
|
this.selectedIncidentId = incidentId;
|
|
}
|
|
|
|
private showUpdate(incidentId: string): void {
|
|
this.currentMode = 'update';
|
|
this.selectedIncidentId = incidentId;
|
|
}
|
|
|
|
private showList(): void {
|
|
this.currentMode = 'list';
|
|
this.selectedIncidentId = null;
|
|
}
|
|
|
|
private get filteredIncidents(): IIncidentDetails[] {
|
|
let incidents = adminState.incidents;
|
|
|
|
// Apply time filter
|
|
if (this.timeFilter === 'current') {
|
|
incidents = incidents.filter(
|
|
(i) => i.status !== 'resolved' && i.status !== 'postmortem'
|
|
);
|
|
} else if (this.timeFilter === 'past') {
|
|
incidents = incidents.filter(
|
|
(i) => i.status === 'resolved' || i.status === 'postmortem'
|
|
);
|
|
}
|
|
|
|
// Apply severity filter
|
|
if (this.severityFilter !== 'all') {
|
|
incidents = incidents.filter((i) => i.severity === this.severityFilter);
|
|
}
|
|
|
|
return incidents;
|
|
}
|
|
|
|
private handleIncidentSave = (e: CustomEvent): void => {
|
|
console.log('Incident saved:', e.detail);
|
|
this.showList();
|
|
};
|
|
|
|
private handleUpdateSave = (e: CustomEvent): void => {
|
|
console.log('Update saved:', e.detail);
|
|
this.showList();
|
|
};
|
|
|
|
private handleCancel = (): void => {
|
|
this.showList();
|
|
};
|
|
|
|
private handleIncidentEdit = (e: CustomEvent): void => {
|
|
const incident = e.detail?.incident as IIncidentDetails;
|
|
if (incident) {
|
|
this.showForm(incident.id);
|
|
}
|
|
};
|
|
|
|
private handleIncidentAddUpdate = (e: CustomEvent): void => {
|
|
const incident = e.detail?.incident as IIncidentDetails;
|
|
if (incident) {
|
|
this.showUpdate(incident.id);
|
|
}
|
|
};
|
|
|
|
render() {
|
|
if (this.currentMode === 'update') {
|
|
const incident = this.selectedIncidentId
|
|
? adminState.incidents.find((i) => i.id === this.selectedIncidentId)
|
|
: null;
|
|
|
|
return html`
|
|
<upladmin-incident-update
|
|
.incident=${incident}
|
|
.loading=${this.loading}
|
|
@updateSave=${this.handleUpdateSave}
|
|
@cancel=${this.handleCancel}
|
|
></upladmin-incident-update>
|
|
`;
|
|
}
|
|
|
|
if (this.currentMode === 'form') {
|
|
const incident = this.selectedIncidentId
|
|
? adminState.incidents.find((i) => i.id === this.selectedIncidentId)
|
|
: null;
|
|
|
|
return html`
|
|
<upladmin-incident-form
|
|
.incident=${incident
|
|
? {
|
|
id: incident.id,
|
|
title: incident.title,
|
|
severity: incident.severity,
|
|
status: incident.status,
|
|
affectedServices: incident.affectedServices,
|
|
impact: incident.impact,
|
|
rootCause: incident.rootCause,
|
|
resolution: incident.resolution,
|
|
}
|
|
: null}
|
|
.availableServices=${adminState.monitors}
|
|
.loading=${this.loading}
|
|
@incidentSave=${this.handleIncidentSave}
|
|
@cancel=${this.handleCancel}
|
|
></upladmin-incident-form>
|
|
`;
|
|
}
|
|
|
|
return html`
|
|
<upladmin-incident-list
|
|
.incidents=${this.filteredIncidents}
|
|
.loading=${this.loading}
|
|
@incidentAdd=${() => this.showForm(null)}
|
|
@incidentEdit=${this.handleIncidentEdit}
|
|
@incidentAddUpdate=${this.handleIncidentAddUpdate}
|
|
></upladmin-incident-list>
|
|
`;
|
|
}
|
|
}
|