initial
This commit is contained in:
172
ts_web/services/admin-state.ts
Normal file
172
ts_web/services/admin-state.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
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();
|
||||
1
ts_web/services/index.ts
Normal file
1
ts_web/services/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './admin-state.js';
|
||||
Reference in New Issue
Block a user