Files

173 lines
4.2 KiB
TypeScript
Raw Permalink Normal View History

2025-12-24 10:57:43 +00:00
import type {
IServiceStatus,
IIncidentDetails,
IStatusPageConfig,
IMonitorFormData,
IIncidentFormData,
} from '../interfaces/index.js';
type TStateChangeListener<T> = (data: T) => void;
/**
* Simple observable implementation for state changes
*/
class SimpleObservable<T> {
private listeners: Set<TStateChangeListener<T>> = new Set();
subscribe(listener: TStateChangeListener<T>): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
next(value: T): void {
for (const listener of this.listeners) {
listener(value);
}
}
}
/**
* Centralized state management for the admin dashboard.
* Handles cross-view data passing and state synchronization.
*/
export class AdminState {
// Observable subjects for reactive updates
public monitors$ = new SimpleObservable<IServiceStatus[]>();
public incidents$ = new SimpleObservable<IIncidentDetails[]>();
public config$ = new SimpleObservable<IStatusPageConfig>();
// Current data
private _monitors: IServiceStatus[] = [];
private _incidents: IIncidentDetails[] = [];
private _config: IStatusPageConfig | null = null;
// Selected items for navigation context
private _selectedMonitor: IServiceStatus | null = null;
private _selectedIncident: IIncidentDetails | null = null;
// Monitors
get monitors(): IServiceStatus[] {
return this._monitors;
}
set monitors(value: IServiceStatus[]) {
this._monitors = value;
this.monitors$.next(value);
}
// Incidents
get incidents(): IIncidentDetails[] {
return this._incidents;
}
set incidents(value: IIncidentDetails[]) {
this._incidents = value;
this.incidents$.next(value);
}
// Config
get config(): IStatusPageConfig | null {
return this._config;
}
set config(value: IStatusPageConfig | null) {
this._config = value;
if (value) {
this.config$.next(value);
}
}
// Selected monitor for edit navigation
setSelectedMonitor(monitor: IServiceStatus | null): void {
this._selectedMonitor = monitor;
}
getSelectedMonitor(): IServiceStatus | null {
return this._selectedMonitor;
}
clearSelectedMonitor(): void {
this._selectedMonitor = null;
}
// Selected incident for edit navigation
setSelectedIncident(incident: IIncidentDetails | null): void {
this._selectedIncident = incident;
}
getSelectedIncident(): IIncidentDetails | null {
return this._selectedIncident;
}
clearSelectedIncident(): void {
this._selectedIncident = null;
}
// Helper methods
getCategories(): string[] {
const categories = new Set<string>();
for (const monitor of this._monitors) {
if (monitor.category) {
categories.add(monitor.category);
}
}
return Array.from(categories).sort();
}
getAvailableServices(): IServiceStatus[] {
return [...this._monitors];
}
getMonitorById(id: string): IServiceStatus | undefined {
return this._monitors.find(m => m.id === id);
}
getIncidentById(id: string): IIncidentDetails | undefined {
return this._incidents.find(i => i.id === id);
}
getActiveIncidents(): IIncidentDetails[] {
return this._incidents.filter(
i => !['resolved', 'postmortem'].includes(i.status)
);
}
getPastIncidents(): IIncidentDetails[] {
return this._incidents.filter(
i => ['resolved', 'postmortem'].includes(i.status)
);
}
// CRUD operations (these would typically call an API)
addMonitor(monitor: IServiceStatus): void {
this.monitors = [...this._monitors, monitor];
}
updateMonitor(id: string, data: Partial<IMonitorFormData>): void {
this.monitors = this._monitors.map(m =>
m.id === id ? { ...m, ...data } : m
);
}
deleteMonitor(id: string): void {
this.monitors = this._monitors.filter(m => m.id !== id);
}
addIncident(incident: IIncidentDetails): void {
this.incidents = [...this._incidents, incident];
}
updateIncident(id: string, data: Partial<IIncidentFormData>): void {
this.incidents = this._incidents.map(i =>
i.id === id ? { ...i, ...data } : i
);
}
deleteIncident(id: string): void {
this.incidents = this._incidents.filter(i => i.id !== id);
}
}
// Singleton instance
export const adminState = new AdminState();