This commit is contained in:
2025-12-24 10:57:43 +00:00
commit ba79b4bfb6
118 changed files with 292546 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

27673
dist_bundle/bundle.js Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
dist_ts_web/elements/index.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
export * from './upladmin-monitor-form/index.js';
export * from './upladmin-monitor-list/index.js';
export * from './upladmin-incident-form/index.js';
export * from './upladmin-incident-list/index.js';
export * from './upladmin-incident-update/index.js';
export * from './upladmin-statuspage-config/index.js';
export * from './upladmin-dashboard/index.js';

View File

@@ -0,0 +1,12 @@
// Monitor components
export * from './upladmin-monitor-form/index.js';
export * from './upladmin-monitor-list/index.js';
// Incident components
export * from './upladmin-incident-form/index.js';
export * from './upladmin-incident-list/index.js';
export * from './upladmin-incident-update/index.js';
// Configuration components
export * from './upladmin-statuspage-config/index.js';
// Dashboard components
export * from './upladmin-dashboard/index.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c193ZWIvZWxlbWVudHMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEscUJBQXFCO0FBQ3JCLGNBQWMsa0NBQWtDLENBQUM7QUFDakQsY0FBYyxrQ0FBa0MsQ0FBQztBQUVqRCxzQkFBc0I7QUFDdEIsY0FBYyxtQ0FBbUMsQ0FBQztBQUNsRCxjQUFjLG1DQUFtQyxDQUFDO0FBQ2xELGNBQWMscUNBQXFDLENBQUM7QUFFcEQsMkJBQTJCO0FBQzNCLGNBQWMsdUNBQXVDLENBQUM7QUFFdEQsdUJBQXVCO0FBQ3ZCLGNBQWMsK0JBQStCLENBQUMifQ==

View File

@@ -0,0 +1 @@
export * from './upladmin-dashboard.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-dashboard.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tZGFzaGJvYXJkL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMseUJBQXlCLENBQUMifQ==

View File

@@ -0,0 +1,28 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IServiceStatus, IIncidentDetails, IOverallStatus } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-dashboard': UpladminDashboard;
}
}
export declare class UpladminDashboard extends DeesElement {
static demo: () => TemplateResult<1>;
accessor monitors: IServiceStatus[];
accessor incidents: IIncidentDetails[];
accessor overallStatus: IOverallStatus | null;
accessor loading: boolean;
static styles: import("@design.estate/dees-element").CSSResult[];
private get statsTiles();
render(): TemplateResult;
private renderStatusBanner;
private renderIncidentItem;
private renderCategoryStatus;
private calculateOverallStatus;
private handleViewAllIncidents;
private handleViewAllMonitors;
private handleIncidentClick;
private handleNewIncident;
private handleNewMonitor;
private handleScheduleMaintenance;
private handleViewConfig;
}

View File

@@ -0,0 +1,2 @@
import './upladmin-dashboard.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,56 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-dashboard.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#f4f4f5', '#09090b')};
min-height: 100vh;
}
`}
</style>
<div class="demo-container">
<upladmin-dashboard
.monitors=${[
{ id: 'api', name: 'api', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45, category: 'Core' },
{ id: 'web', name: 'web', displayName: 'Web App', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120, category: 'Core' },
{ id: 'db', name: 'db', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5, category: 'Infrastructure' },
{ id: 'cdn', name: 'cdn', displayName: 'CDN', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200, category: 'Infrastructure' },
{ id: 'cache', name: 'cache', displayName: 'Redis Cache', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 2, category: 'Infrastructure' },
{ id: 'email', name: 'email', displayName: 'Email Service', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.9, uptime90d: 99.85, responseTime: 500, category: 'External' },
{ id: 'payment', name: 'payment', displayName: 'Payment Gateway', currentStatus: 'maintenance', lastChecked: Date.now(), uptime30d: 99.95, uptime90d: 99.9, responseTime: 350, category: 'External' },
{ id: 'search', name: 'search', displayName: 'Search Engine', currentStatus: 'partial_outage', lastChecked: Date.now(), uptime30d: 98.5, uptime90d: 99.2, responseTime: 150, category: 'Core' },
]}
.activeIncidents=${[
{
id: 'inc-1',
title: 'CDN Performance Degradation',
status: 'identified',
severity: 'minor',
affectedServices: ['cdn'],
startTime: Date.now() - 2 * 60 * 60 * 1000,
impact: 'Some users may experience slower page loads',
updates: [
{ id: 'u1', timestamp: Date.now() - 1 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified as network congestion' },
{ id: 'u2', timestamp: Date.now() - 2 * 60 * 60 * 1000, status: 'investigating', message: 'We are investigating reports of slow content delivery' },
],
},
{
id: 'inc-2',
title: 'Search Cluster Partial Failure',
status: 'investigating',
severity: 'major',
affectedServices: ['search'],
startTime: Date.now() - 30 * 60 * 1000,
impact: 'Search functionality may return incomplete results',
updates: [
{ id: 'u3', timestamp: Date.now() - 30 * 60 * 1000, status: 'investigating', message: 'We are investigating issues with the search cluster' },
],
},
]}
></upladmin-dashboard>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4tZGFzaGJvYXJkLmRlbW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tZGFzaGJvYXJkL3VwbGFkbWluLWRhc2hib2FyZC5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8seUJBQXlCLENBQUM7QUFFakMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7c0JBR2EsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7S0FHekQ7Ozs7O2tCQUthO0lBQ1YsRUFBRSxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFlBQVksRUFBRSxFQUFFLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRTtJQUNwTCxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsU0FBUyxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLEdBQUcsRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFO0lBQ2xMLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxX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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export * from './upladmin-incident-form.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-incident-form.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4taW5jaWRlbnQtZm9ybS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDZCQUE2QixDQUFDIn0=

View File

@@ -0,0 +1,33 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IIncidentFormData, IServiceStatus } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-incident-form': UpladminIncidentForm;
}
}
export declare class UpladminIncidentForm extends DeesElement {
static demo: () => TemplateResult<1>;
accessor incident: IIncidentFormData | null;
accessor availableServices: IServiceStatus[];
accessor loading: boolean;
accessor formData: IIncidentFormData;
accessor errors: Record<string, string>;
private severityIcons;
private statusIcons;
static styles: import("@design.estate/dees-element").CSSResult[];
connectedCallback(): Promise<void>;
updated(changedProperties: Map<string, unknown>): void;
render(): TemplateResult;
private handleTitleChange;
private handleImpactChange;
private handleRootCauseChange;
private handleResolutionChange;
private handleSeverityChange;
private handleStatusChange;
private toggleService;
private validate;
private handleSave;
private handleCancel;
reset(): void;
setIncident(incident: IIncidentFormData): void;
}

View File

@@ -0,0 +1,2 @@
import './upladmin-incident-form.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,69 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-incident-form.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px;
max-width: 900px;
margin: 0 auto;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 24px;
}
.demo-section h3 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Create New Incident</h3>
<upladmin-incident-form
.availableServices=${[
{ id: 'api', name: 'api', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'web', name: 'web', displayName: 'Web Application', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120 },
{ id: 'db', name: 'db', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
{ id: 'cdn', name: 'cdn', displayName: 'CDN', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200 },
{ id: 'cache', name: 'cache', displayName: 'Redis Cache', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 2 },
]}
></upladmin-incident-form>
</div>
<div class="demo-section">
<h3>Edit Existing Incident</h3>
<upladmin-incident-form
.incident=${{
id: 'inc-123',
title: 'Database Connection Issues',
severity: 'major',
status: 'identified',
affectedServices: ['db', 'api'],
impact: 'Users may experience slow response times and occasional timeouts when accessing the application.',
rootCause: 'Connection pool exhaustion due to a memory leak in the database driver.',
}}
.availableServices=${[
{ id: 'api', name: 'api', displayName: 'API Server', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'web', name: 'web', displayName: 'Web Application', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120 },
{ id: 'db', name: 'db', displayName: 'Database', currentStatus: 'partial_outage', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
{ id: 'cdn', name: 'cdn', displayName: 'CDN', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200 },
{ id: 'cache', name: 'cache', displayName: 'Redis Cache', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 2 },
]}
></upladmin-incident-form>
</div>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4taW5jaWRlbnQtZm9ybS5kZW1vLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL3VwbGFkbWluLWluY2lkZW50LWZvcm0vdXBsYWRtaW4taW5jaWRlbnQtZm9ybS5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8sNkJBQTZCLENBQUM7QUFFckMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7Ozs7OztzQkFRYSxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUM7Ozs7Ozs7Ozs7OztpQkFZN0MsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7O0tBSXBEOzs7Ozs7OzZCQU93QjtJQUNuQixFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLEVBQUUsRUFBRTtJQUNsSyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQUUsaUJBQWlCLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxXQUFXLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxZQUFZLEVBQUUsR0FBRyxFQUFFO0lBQ3hLLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFF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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export * from './upladmin-incident-list.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-incident-list.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4taW5jaWRlbnQtbGlzdC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLDZCQUE2QixDQUFDIn0=

View File

@@ -0,0 +1,29 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IIncidentDetails } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-incident-list': UpladminIncidentList;
}
}
type TSeverity = 'critical' | 'major' | 'minor' | 'maintenance';
type TTabFilter = 'current' | 'past' | 'all';
export declare class UpladminIncidentList extends DeesElement {
static demo: () => TemplateResult<1>;
accessor incidents: IIncidentDetails[];
accessor loading: boolean;
accessor tabFilter: TTabFilter;
accessor severityFilter: TSeverity | 'all';
accessor expandedIncidents: Set<string>;
private statusIcons;
private statusLabels;
static styles: import("@design.estate/dees-element").CSSResult[];
render(): TemplateResult;
private renderIncidentCard;
private getFilteredIncidents;
private handleSeverityFilter;
private toggleExpanded;
private handleAddClick;
private handleEdit;
private handleAddUpdate;
}
export {};

View File

@@ -0,0 +1,2 @@
import './upladmin-incident-list.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,93 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-incident-list.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
`}
</style>
<div class="demo-container">
<upladmin-incident-list
.incidents=${[
{
id: 'inc-1',
title: 'CDN Performance Degradation',
status: 'monitoring',
severity: 'minor',
affectedServices: ['cdn'],
startTime: Date.now() - 4 * 60 * 60 * 1000,
impact: 'Some users may experience slower page loads for static assets.',
rootCause: 'Network congestion at edge locations',
updates: [
{ id: 'u1', timestamp: Date.now() - 1 * 60 * 60 * 1000, status: 'monitoring', message: 'Fix deployed, monitoring results.', author: 'Infrastructure Team' },
{ id: 'u2', timestamp: Date.now() - 2 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified as network congestion at edge nodes.', author: 'Infrastructure Team' },
{ id: 'u3', timestamp: Date.now() - 4 * 60 * 60 * 1000, status: 'investigating', message: 'We are investigating reports of slow content delivery.', author: 'Support Team' },
],
},
{
id: 'inc-2',
title: 'Search Cluster Partial Failure',
status: 'investigating',
severity: 'major',
affectedServices: ['search', 'api'],
startTime: Date.now() - 45 * 60 * 1000,
impact: 'Search functionality may return incomplete results. API responses may be delayed.',
updates: [
{ id: 'u4', timestamp: Date.now() - 45 * 60 * 1000, status: 'investigating', message: 'We are investigating issues with the search cluster. Some nodes are not responding.', author: 'Platform Team' },
],
},
{
id: 'inc-3',
title: 'Scheduled Database Maintenance',
status: 'investigating',
severity: 'maintenance',
affectedServices: ['db', 'api', 'web'],
startTime: Date.now() - 15 * 60 * 1000,
impact: 'Brief interruptions may occur during the maintenance window.',
updates: [
{ id: 'u5', timestamp: Date.now() - 15 * 60 * 1000, status: 'investigating', message: 'Starting scheduled database maintenance. Expected duration: 2 hours.', author: 'DBA Team' },
],
},
{
id: 'inc-4',
title: 'Authentication Service Outage',
status: 'resolved',
severity: 'critical',
affectedServices: ['auth', 'api', 'web'],
startTime: Date.now() - 24 * 60 * 60 * 1000,
endTime: Date.now() - 22 * 60 * 60 * 1000,
impact: 'Users were unable to log in or access authenticated features.',
rootCause: 'Certificate expiration on the identity provider.',
resolution: 'Renewed certificates and implemented automated monitoring for future expirations.',
updates: [
{ id: 'u6', timestamp: Date.now() - 22 * 60 * 60 * 1000, status: 'resolved', message: 'Issue has been fully resolved. All authentication services are operational.', author: 'Security Team' },
{ id: 'u7', timestamp: Date.now() - 23 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified: expired SSL certificate on identity provider.', author: 'Security Team' },
{ id: 'u8', timestamp: Date.now() - 24 * 60 * 60 * 1000, status: 'investigating', message: 'We are aware of authentication issues and are investigating.', author: 'On-call Engineer' },
],
},
{
id: 'inc-5',
title: 'Payment Processing Delays',
status: 'postmortem',
severity: 'major',
affectedServices: ['payment', 'api'],
startTime: Date.now() - 72 * 60 * 60 * 1000,
endTime: Date.now() - 70 * 60 * 60 * 1000,
impact: 'Payment transactions were delayed by up to 5 minutes.',
rootCause: 'Third-party payment provider experienced capacity issues.',
resolution: 'Provider resolved their capacity issues. Implemented fallback payment routing.',
updates: [
{ id: 'u9', timestamp: Date.now() - 48 * 60 * 60 * 1000, status: 'postmortem', message: 'Postmortem complete. Implementing additional redundancy measures.', author: 'Engineering Lead' },
{ id: 'u10', timestamp: Date.now() - 70 * 60 * 60 * 1000, status: 'resolved', message: 'Payment processing has returned to normal.', author: 'Payments Team' },
],
},
]}
></upladmin-incident-list>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4taW5jaWRlbnQtbGlzdC5kZW1vLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHNfd2ViL2VsZW1lbnRzL3VwbGFkbWluLWluY2lkZW50LWxpc3QvdXBsYWRtaW4taW5jaWRlbnQtbGlzdC5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8sNkJBQTZCLENBQUM7QUFFckMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7c0JBR2EsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7S0FHekQ7Ozs7O21CQUtjO0lBQ1g7UUFDRSxFQUFFLEVBQUUsT0FBTztRQUNYLEtBQUssRUFBRSw2QkFBNkI7UUFDcEMsTUFBTSxFQUFFLFlBQVk7UUFDcEIsUUFBUSxFQUFFLE9BQU87UUFDakIsZ0JBQWdCLEVBQUUsQ0FBQyxLQUFLLENBQUM7UUFDekIsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJO1FBQzFDLE1BQU0sRUFBRSxnRUFBZ0U7UUFDeEUsU0FBUyxFQUFFLHNDQUFzQztRQUNqRCxPQUFPLEVBQUU7WUFDUCxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsbUNBQW1DLEVBQUUsTUFBTSxFQUFFLHFCQUFxQixFQUFFO1lBQzNKLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxH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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export * from './upladmin-incident-update.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-incident-update.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4taW5jaWRlbnQtdXBkYXRlL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsK0JBQStCLENBQUMifQ==

View File

@@ -0,0 +1,27 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IIncidentUpdateFormData, IIncidentDetails } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-incident-update': UpladminIncidentUpdate;
}
}
export declare class UpladminIncidentUpdate extends DeesElement {
static demo: () => TemplateResult<1>;
accessor incident: IIncidentDetails | null;
accessor loading: boolean;
accessor formData: IIncidentUpdateFormData;
accessor errors: Record<string, string>;
private statusIcons;
static styles: import("@design.estate/dees-element").CSSResult[];
connectedCallback(): Promise<void>;
updated(changedProperties: Map<string, unknown>): void;
render(): TemplateResult;
private handleMessageChange;
private handleAuthorChange;
private handleStatusChange;
private applyTemplate;
private validate;
private handlePost;
private handleCancel;
reset(): void;
}

View File

@@ -0,0 +1,2 @@
import './upladmin-incident-update.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,90 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-incident-update.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px;
max-width: 800px;
margin: 0 auto;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 24px;
}
.demo-section h3 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Update Active Incident (Critical)</h3>
<upladmin-incident-update
.incident=${{
id: 'inc-1',
title: 'Database Connection Pool Exhaustion',
status: 'investigating',
severity: 'critical',
affectedServices: ['db', 'api', 'web'],
startTime: Date.now() - 30 * 60 * 1000,
impact: 'All database-dependent services are experiencing failures.',
updates: [
{ id: 'u1', timestamp: Date.now() - 30 * 60 * 1000, status: 'investigating', message: 'We are investigating reports of service failures.' },
],
}}
></upladmin-incident-update>
</div>
<div class="demo-section">
<h3>Update Active Incident (Minor)</h3>
<upladmin-incident-update
.incident=${{
id: 'inc-2',
title: 'Elevated API Response Times',
status: 'identified',
severity: 'minor',
affectedServices: ['api'],
startTime: Date.now() - 2 * 60 * 60 * 1000,
impact: 'API responses may be slower than usual.',
updates: [
{ id: 'u2', timestamp: Date.now() - 1 * 60 * 60 * 1000, status: 'identified', message: 'Issue identified as increased traffic from a specific client.' },
{ id: 'u3', timestamp: Date.now() - 2 * 60 * 60 * 1000, status: 'investigating', message: 'We are investigating elevated response times.' },
],
}}
></upladmin-incident-update>
</div>
<div class="demo-section">
<h3>Update Maintenance Window</h3>
<upladmin-incident-update
.incident=${{
id: 'inc-3',
title: 'Scheduled Infrastructure Upgrade',
status: 'monitoring',
severity: 'maintenance',
affectedServices: ['api', 'web', 'cdn'],
startTime: Date.now() - 45 * 60 * 1000,
impact: 'Brief interruptions may occur during the upgrade.',
updates: [
{ id: 'u4', timestamp: Date.now() - 30 * 60 * 1000, status: 'monitoring', message: 'Upgrade complete. Monitoring for any issues.' },
{ id: 'u5', timestamp: Date.now() - 45 * 60 * 1000, status: 'investigating', message: 'Starting scheduled infrastructure upgrade.' },
],
}}
></upladmin-incident-update>
</div>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4taW5jaWRlbnQtdXBkYXRlLmRlbW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4taW5jaWRlbnQtdXBkYXRlL3VwbGFkbWluLWluY2lkZW50LXVwZGF0ZS5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8sK0JBQStCLENBQUM7QUFFdkMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7Ozs7OztzQkFRYSxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUM7Ozs7Ozs7Ozs7OztpQkFZN0MsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7O0tBSXBEOzs7Ozs7O29CQU9lO0lBQ1YsRUFBRSxFQUFFLE9BQU87SUFDWCxLQUFLLEVBQUUscUNBQXFDO0lBQzVDLE1BQU0sRUFBRSxlQUFlO0lBQ3ZCLFFBQVEsRUFBRSxVQUFVO0lBQ3BCLGdCQUFnQixFQUFFLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUM7SUFDdEMsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDdEMsTUFBTSxFQUFFLDREQUE0RDtJQUNwRSxPQUFPLEVBQUU7UUFDUCxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLE9BQU8sRUFBRSxtREFBbUQsRUFBRTtLQUM1STtDQUNrQjs7Ozs7OztvQkFPVDtJQUNWLEVBQUUsRUFBRSxPQUFPO0lBQ1gsS0FBSyxFQUFFLDZCQUE2QjtJQUNwQyxNQUFNLEVBQUUsWUFBWTtJQUNwQixRQUFRLEVBQUUsT0FBTztJQUNqQixnQkFBZ0IsRUFBRSxDQUFDLEtBQUssQ0FBQztJQUN6QixTQUFTLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDMUMsTUFBTSxFQUFFLHlDQUF5QztJQUNqRCxPQUFPLEVBQUU7UUFDUCxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsR0FBRyxJQUFJLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxPQUFPLEVBQUUsK0RBQStELEVBQUU7UUFDeEosRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxFQUFFLE1BQU0sRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLCtDQUErQyxFQUFFO0tBQzVJO0NBQ2tCOzs7Ozs7O29CQU9UO0lBQ1YsRUFBRSxFQUFFLE9BQU87SUFDWCxLQUFLLEVBQUUsa0NBQWtDO0lBQ3pDLE1BQU0sRUFBRSxZQUFZO0lBQ3BCLFFBQVEsRUFBRSxhQUFhO0lBQ3ZCLGdCQUFnQixFQUFFLENBQUMsS0FBSyxFQUFFLEtBQUssRUFBRSxLQUFLLENBQUM7SUFDdkMsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7SUFDdEMsTUFBTSxFQUFFLG1EQUFtRDtJQUMzRCxPQUFPLEVBQUU7UUFDUCxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxNQUFNLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSw4Q0FBOEMsRUFBRTtRQUNuSSxFQUFFLEVBQUUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksRUFBRSxNQUFNLEVBQUUsZUFBZSxFQUFFLE9BQU8sRUFBRSw0Q0FBNEMsRUFBRTtLQUNySTtDQUNrQjs7OztDQUk1QixDQUFDIn0=

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export * from './upladmin-monitor-form.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-monitor-form.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tbW9uaXRvci1mb3JtL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsNEJBQTRCLENBQUMifQ==

View File

@@ -0,0 +1,32 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IMonitorFormData, IServiceStatus } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-monitor-form': UpladminMonitorForm;
}
}
export declare class UpladminMonitorForm extends DeesElement {
static demo: () => TemplateResult<1>;
accessor monitor: IMonitorFormData | null;
accessor availableMonitors: IServiceStatus[];
accessor categories: string[];
accessor loading: boolean;
accessor formData: IMonitorFormData;
accessor errors: Record<string, string>;
private statusIcons;
static styles: import("@design.estate/dees-element").CSSResult[];
connectedCallback(): Promise<void>;
updated(changedProperties: Map<string, unknown>): void;
render(): TemplateResult;
private handleNameChange;
private handleDisplayNameChange;
private handleDescriptionChange;
private handleCategoryChange;
private handleDependenciesChange;
private handleStatusChange;
private validate;
private handleSave;
private handleCancel;
reset(): void;
setMonitor(monitor: IMonitorFormData): void;
}

View File

@@ -0,0 +1,2 @@
import './upladmin-monitor-form.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,65 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-monitor-form.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px;
max-width: 900px;
margin: 0 auto;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 24px;
}
.demo-section h3 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Create New Monitor</h3>
<upladmin-monitor-form
.availableMonitors=${[
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'database', name: 'database', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 5 },
]}
.categories=${['Core Services', 'Infrastructure', 'External Services', 'Web Services']}
></upladmin-monitor-form>
</div>
<div class="demo-section">
<h3>Edit Existing Monitor</h3>
<upladmin-monitor-form
.monitor=${{
id: 'cdn',
name: 'cdn',
displayName: 'Content Delivery Network',
description: 'Global CDN for static assets and media files',
category: 'Infrastructure',
dependencies: ['api-server'],
currentStatus: 'degraded',
}}
.availableMonitors=${[
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'database', name: 'database', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 5 },
]}
.categories=${['Core Services', 'Infrastructure', 'External Services', 'Web Services']}
></upladmin-monitor-form>
</div>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4tbW9uaXRvci1mb3JtLmRlbW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tbW9uaXRvci1mb3JtL3VwbGFkbWluLW1vbml0b3ItZm9ybS5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8sNEJBQTRCLENBQUM7QUFFcEMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7Ozs7OztzQkFRYSxVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUM7Ozs7Ozs7Ozs7OztpQkFZN0MsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7O0tBSXBEOzs7Ozs7OzZCQU93QjtJQUNuQixFQUFFLEVBQUUsRUFBRSxZQUFZLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLEVBQUUsRUFBRTtJQUNoTCxFQUFFLEVBQUUsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUUsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsWUFBWSxFQUFFLENBQUMsRUFBRTtDQUN0SjtzQkFDUCxDQUFDLGVBQWUsRUFBRSxnQkFBZ0IsRUFBRSxtQkFBbUIsRUFBRSxjQUFjLENBQUM7Ozs7Ozs7bUJBTzNFO0lBQ1QsRUFBRSxFQUFFLEtBQUs7SUFDVCxJQUFJLEVBQUUsS0FBSztJQUNYLFdBQVcsRUFBRSwwQkFBMEI7SUFDdkMsV0FBVyxFQUFFLDhDQUE4QztJQUMzRCxRQUFRLEVBQUUsZ0JBQWdCO0lBQzFCLFlBQVksRUFBRSxDQUFDLFlBQVksQ0FBQztJQUM1QixhQUFhLEVBQUUsVUFBVTtDQUMxQjs2QkFDb0I7SUFDbkIsRUFBRSxFQUFFLEVBQUUsWUFBWSxFQUFFLElBQUksRUFBRSxZQUFZLEVBQUUsV0FBVyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFlBQVksRUFBRSxFQUFFLEVBQUU7SUFDaEwsRUFBRSxFQUFFLEVBQUUsVUFBVSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLFlBQVksRUFBRSxDQUFDLEVBQUU7Q0FDdEo7c0JBQ1AsQ0FBQyxlQUFlLEVBQUUsZ0JBQWdCLEVBQUUsbUJBQW1CLEVBQUUsY0FBYyxDQUFDOzs7O0NBSTdGLENBQUMifQ==

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export * from './upladmin-monitor-list.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-monitor-list.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tbW9uaXRvci1saXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsNEJBQTRCLENBQUMifQ==

View File

@@ -0,0 +1,29 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IServiceStatus } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-monitor-list': UpladminMonitorList;
}
}
type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
export declare class UpladminMonitorList extends DeesElement {
static demo: () => TemplateResult<1>;
accessor monitors: IServiceStatus[];
accessor loading: boolean;
accessor statusFilter: TStatusType | 'all';
accessor categoryFilter: string;
private statusIcons;
private statusLabels;
static styles: import("@design.estate/dees-element").CSSResult[];
private get filteredMonitors();
private get categories();
private get tableColumns();
private get tableActions();
render(): TemplateResult;
private handleStatusFilter;
private handleCategoryFilter;
private handleAddClick;
private handleEdit;
private handleDelete;
}
export {};

View File

@@ -0,0 +1,2 @@
import './upladmin-monitor-list.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,117 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-monitor-list.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
`}
</style>
<div class="demo-container">
<upladmin-monitor-list
.monitors=${[
{
id: 'api-server',
name: 'api-server',
displayName: 'API Server',
description: 'Main REST API endpoint',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.98,
uptime90d: 99.95,
responseTime: 45,
category: 'Core Services',
},
{
id: 'web-app',
name: 'web-app',
displayName: 'Web Application',
description: 'Customer-facing web application',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.99,
uptime90d: 99.97,
responseTime: 120,
category: 'Core Services',
},
{
id: 'database-primary',
name: 'database-primary',
displayName: 'Primary Database',
description: 'PostgreSQL primary node',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.999,
uptime90d: 99.998,
responseTime: 5,
category: 'Infrastructure',
},
{
id: 'cdn',
name: 'cdn',
displayName: 'Content Delivery Network',
description: 'Global CDN for static assets',
currentStatus: 'degraded',
lastChecked: Date.now(),
uptime30d: 99.5,
uptime90d: 99.8,
responseTime: 200,
category: 'Infrastructure',
},
{
id: 'redis-cache',
name: 'redis-cache',
displayName: 'Redis Cache',
description: 'In-memory caching layer',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.99,
uptime90d: 99.98,
responseTime: 2,
category: 'Infrastructure',
},
{
id: 'email-service',
name: 'email-service',
displayName: 'Email Service',
description: 'Transactional email delivery',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.9,
uptime90d: 99.85,
responseTime: 500,
category: 'External Services',
},
{
id: 'payment-gateway',
name: 'payment-gateway',
displayName: 'Payment Gateway',
description: 'Payment processing integration',
currentStatus: 'maintenance',
lastChecked: Date.now(),
uptime30d: 99.95,
uptime90d: 99.9,
responseTime: 350,
category: 'External Services',
},
{
id: 'search-engine',
name: 'search-engine',
displayName: 'Search Engine',
description: 'Elasticsearch cluster',
currentStatus: 'partial_outage',
lastChecked: Date.now(),
uptime30d: 98.5,
uptime90d: 99.2,
responseTime: 150,
category: 'Core Services',
},
]}
></upladmin-monitor-list>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4tbW9uaXRvci1saXN0LmRlbW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tbW9uaXRvci1saXN0L3VwbGFkbWluLW1vbml0b3ItbGlzdC5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8sNEJBQTRCLENBQUM7QUFFcEMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7c0JBR2EsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7S0FHekQ7Ozs7O2tCQUthO0lBQ1Y7UUFDRSxFQUFFLEVBQUUsWUFBWTtRQUNoQixJQUFJLEVBQUUsWUFBWTtRQUNsQixXQUFXLEVBQUUsWUFBWTtRQUN6QixXQUFXLEVBQUUsd0JBQXdCO1FBQ3JDLGFBQWEsRUFBRSxhQUFhO1FBQzVCLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3ZCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLFlBQVksRUFBRSxFQUFFO1FBQ2hCLFFBQVEsRUFBRSxlQUFlO0tBQzFCO0lBQ0Q7UUFDRSxFQUFFLEVBQUUsU0FBUztRQUNiLElBQUksRUFBRSxTQUFTO1FBQ2YsV0FBVyxFQUFFLGlCQUFpQjtRQUM5QixXQUFXLEVBQUUsaUNBQWlDO1FBQzlDLGFBQWEsRUFBRSxhQUFhO1FBQzVCLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3ZCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLFlBQVksRUFBRSxHQUFHO1FBQ2pCLFFBQVEsRUFBRSxlQUFlO0tBQzFCO0lBQ0Q7UUFDRSxFQUFFLEVBQUUsa0JBQWtCO1FBQ3RCLElBQUksRUFBRSxrQkFBa0I7UUFDeEIsV0FBVyxFQUFFLGtCQUFrQjtRQUMvQixXQUFXLEVBQUUseUJBQXlCO1FBQ3RDLGFBQWEsRUFBRSxhQUFhO1FBQzVCLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3ZCLFNBQVMsRUFBRSxNQUFNO1FBQ2pCLFNBQVMsRUFBRSxNQUFNO1FBQ2pCLFlBQVksRUFBRSxDQUFDO1FBQ2YsUUFBUSxFQUFFLGdCQUFnQjtLQUMzQjtJQUNEO1FBQ0UsRUFBRSxFQUFFLEtBQUs7UUFDVCxJQUFJLEVBQUUsS0FBSztRQUNYLFdBQVcsRUFBRSwwQkFBMEI7UUFDdkMsV0FBVyxFQUFFLDhCQUE4QjtRQUMzQyxhQUFhLEVBQUUsVUFBVTtRQUN6QixXQUFXLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUN2QixTQUFTLEVBQUUsSUFBSTtRQUNmLFNBQVMsRUFBRSxJQUFJO1FBQ2YsWUFBWSxFQUFFLEdBQUc7UUFDakIsUUFBUSxFQUFFLGdCQUFnQjtLQUMzQjtJQUNEO1FBQ0UsRUFBRSxFQUFFLGFBQWE7UUFDakIsSUFBSSxFQUFFLGFBQWE7UUFDbkIsV0FBVyxFQUFFLGFBQWE7UUFDMUIsV0FBVyxFQUFFLHlCQUF5QjtRQUN0QyxhQUFhLEVBQUUsYUFBYTtRQUM1QixXQUFXLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRTtRQUN2QixTQUFTLEVBQUUsS0FBSztRQUNoQixTQUFTLEVBQUUsS0FBSztRQUNoQixZQUFZLEVBQUUsQ0FBQztRQUNmLFFBQVEsRUFBRSxnQkFBZ0I7S0FDM0I7SUFDRDtRQUNFLEVBQUUsRUFBRSxlQUFlO1FBQ25CLElBQUksRUFBRSxlQUFlO1FBQ3JCLFdBQVcsRUFBRSxlQUFlO1FBQzVCLFdBQVcsRUFBRSw4QkFBOEI7UUFDM0MsYUFBYSxFQUFFLGFBQWE7UUFDNUIsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7UUFDdkIsU0FBUyxFQUFFLElBQUk7UUFDZixTQUFTLEVBQUUsS0FBSztRQUNoQixZQUFZLEVBQUUsR0FBRztRQUNqQixRQUFRLEVBQUUsbUJBQW1CO0tBQzlCO0lBQ0Q7UUFDRSxFQUFFLEVBQUUsaUJBQWlCO1FBQ3JCLElBQUksRUFBRSxpQkFBaUI7UUFDdkIsV0FBVyxFQUFFLGlCQUFpQjtRQUM5QixXQUFXLEVBQUUsZ0NBQWdDO1FBQzdDLGFBQWEsRUFBRSxhQUFhO1FBQzVCLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3ZCLFNBQVMsRUFBRSxLQUFLO1FBQ2hCLFNBQVMsRUFBRSxJQUFJO1FBQ2YsWUFBWSxFQUFFLEdBQUc7UUFDakIsUUFBUSxFQUFFLG1CQUFtQjtLQUM5QjtJQUNEO1FBQ0UsRUFBRSxFQUFFLGVBQWU7UUFDbkIsSUFBSSxFQUFFLGVBQWU7UUFDckIsV0FBVyxFQUFFLGVBQWU7UUFDNUIsV0FBVyxFQUFFLHVCQUF1QjtRQUNwQyxhQUFhLEVBQUUsZ0JBQWdCO1FBQy9CLFdBQVcsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3ZCLFNBQVMsRUFBRSxJQUFJO1FBQ2YsU0FBUyxFQUFFLElBQUk7UUFDZixZQUFZLEVBQUUsR0FBRztRQUNqQixRQUFRLEVBQUUsZUFBZTtLQUMxQjtDQUNrQjs7O0NBRzFCLENBQUMifQ==

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
export * from './upladmin-statuspage-config.js';

View File

@@ -0,0 +1,2 @@
export * from './upladmin-statuspage-config.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvZWxlbWVudHMvdXBsYWRtaW4tc3RhdHVzcGFnZS1jb25maWcvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxpQ0FBaUMsQ0FBQyJ9

View File

@@ -0,0 +1,33 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { IStatusPageConfig } from '../../interfaces/index.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-statuspage-config': UpladminStatuspageConfig;
}
}
export declare class UpladminStatuspageConfig extends DeesElement {
static demo: () => TemplateResult<1>;
accessor config: IStatusPageConfig;
accessor loading: boolean;
accessor formData: IStatusPageConfig;
accessor activeSection: string;
accessor hasChanges: boolean;
static styles: import("@design.estate/dees-element").CSSResult[];
connectedCallback(): Promise<void>;
updated(changedProperties: Map<string, unknown>): void;
render(): TemplateResult;
private renderSection;
private renderBrandingSection;
private renderUrlsSection;
private renderBehaviorSection;
private renderAdvancedSection;
private handleTextChange;
private handleNumberChange;
private handleBooleanChange;
private handleDropdownChange;
private handleThemeChange;
private handleLogoError;
private handleSave;
private handleReset;
setConfig(config: IStatusPageConfig): void;
}

View File

@@ -0,0 +1,2 @@
import './upladmin-statuspage-config.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,46 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import './upladmin-statuspage-config.js';
export const demoFunc = () => html `
<style>
${css `
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-title {
margin: 0 0 24px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<h3 class="demo-title">Status Page Configuration</h3>
<upladmin-statuspage-config
.config=${{
companyName: 'Acme Corporation',
companyLogo: 'https://via.placeholder.com/200x60?text=ACME',
supportEmail: 'support@acme.example.com',
statusPageUrl: 'https://status.acme.example.com',
legalUrl: 'https://acme.example.com/terms',
apiEndpoint: 'https://api.acme.example.com/status',
theme: 'auto',
whitelabel: false,
refreshInterval: 60,
showHistoricalDays: 90,
enableWebSocket: true,
enableNotifications: false,
timeZone: 'America/New_York',
language: 'en',
dateFormat: 'relative',
}}
></upladmin-statuspage-config>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4tc3RhdHVzcGFnZS1jb25maWcuZGVtby5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzX3dlYi9lbGVtZW50cy91cGxhZG1pbi1zdGF0dXNwYWdlLWNvbmZpZy91cGxhZG1pbi1zdGF0dXNwYWdlLWNvbmZpZy5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBRXBFLE9BQU8saUNBQWlDLENBQUM7QUFFekMsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7TUFFNUIsR0FBRyxDQUFBOzs7c0JBR2EsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7Ozs7OztpQkFRN0MsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7O0tBSXBEOzs7Ozs7Z0JBTVc7SUFDUixXQUFXLEVBQUUsa0JBQWtCO0lBQy9CLFdBQVcsRUFBRSw4Q0FBOEM7SUFDM0QsWUFBWSxFQUFFLDBCQUEwQjtJQUN4QyxhQUFhLEVBQUUsaUNBQWlDO0lBQ2hELFFBQVEsRUFBRSxnQ0FBZ0M7SUFDMUMsV0FBVyxFQUFFLHFDQUFxQztJQUNsRCxLQUFLLEVBQUUsTUFBTTtJQUNiLFVBQVUsRUFBRSxLQUFLO0lBQ2pCLGVBQWUsRUFBRSxFQUFFO0lBQ25CLGtCQUFrQixFQUFFLEVBQUU7SUFDdEIsZUFBZSxFQUFFLElBQUk7SUFDckIsbUJBQW1CLEVBQUUsS0FBSztJQUMxQixRQUFRLEVBQUUsa0JBQWtCO0lBQzVCLFFBQVEsRUFBRSxJQUFJO0lBQ2QsVUFBVSxFQUFFLFVBQVU7Q0FDRjs7O0NBRzNCLENBQUMifQ==

File diff suppressed because one or more lines are too long

4
dist_ts_web/index.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
export * from './elements/index.js';
export * from './pages/index.js';
export * from './interfaces/index.js';
export * from './services/index.js';

5
dist_ts_web/index.js Normal file
View File

@@ -0,0 +1,5 @@
export * from './elements/index.js';
export * from './pages/index.js';
export * from './interfaces/index.js';
export * from './services/index.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c193ZWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxxQkFBcUIsQ0FBQztBQUNwQyxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsdUJBQXVCLENBQUM7QUFDdEMsY0FBYyxxQkFBcUIsQ0FBQyJ9

88
dist_ts_web/interfaces/index.d.ts vendored Normal file
View File

@@ -0,0 +1,88 @@
export interface IServiceStatus {
id: string;
name: string;
displayName: string;
description?: string;
currentStatus: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
lastChecked: number;
uptime30d: number;
uptime90d: number;
responseTime: number;
category?: string;
dependencies?: string[];
selected?: boolean;
}
export interface IStatusHistoryPoint {
timestamp: number;
status: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
responseTime?: number;
errorRate?: number;
}
export interface IIncidentUpdate {
id: string;
timestamp: number;
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
message: string;
author?: string;
}
export interface IIncidentDetails {
id: string;
title: string;
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
severity: 'critical' | 'major' | 'minor' | 'maintenance';
affectedServices: string[];
startTime: number;
endTime?: number;
updates: IIncidentUpdate[];
impact: string;
rootCause?: string;
resolution?: string;
}
export interface IOverallStatus {
status: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
message: string;
lastUpdated: number;
affectedServices: number;
totalServices: number;
}
export interface IStatusPageConfig {
apiEndpoint?: string;
refreshInterval?: number;
timeZone?: string;
dateFormat?: string;
enableWebSocket?: boolean;
enableNotifications?: boolean;
theme?: 'light' | 'dark' | 'auto';
language?: string;
showHistoricalDays?: number;
whitelabel?: boolean;
companyName?: string;
companyLogo?: string;
supportEmail?: string;
statusPageUrl?: string;
legalUrl?: string;
}
export interface IMonitorFormData {
id?: string;
name: string;
displayName: string;
description?: string;
category?: string;
dependencies?: string[];
currentStatus: 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
}
export interface IIncidentFormData {
id?: string;
title: string;
severity: 'critical' | 'major' | 'minor' | 'maintenance';
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
affectedServices: string[];
impact: string;
rootCause?: string;
resolution?: string;
}
export interface IIncidentUpdateFormData {
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
message: string;
author?: string;
}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c193ZWIvaW50ZXJmYWNlcy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0=

View File

@@ -0,0 +1,3 @@
import '../elements/index.js';
export declare const adminpageConfig: () => import("@design.estate/dees-element").TemplateResult<1>;
export declare const adminpageConfigWhitelabel: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,77 @@
import { html, cssManager } from "@design.estate/dees-element";
import '../elements/index.js';
export const adminpageConfig = () => html `
<style>
.demo-page-wrapper {
min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
padding: 24px;
}
</style>
<div class="demo-page-wrapper">
<dees-demowrapper
.runAfterRender=${async (wrapperElement) => {
const config = wrapperElement.querySelector('upladmin-statuspage-config');
const configData = {
companyName: 'CloudFlow Inc.',
companyLogo: '',
supportEmail: 'support@cloudflow.io',
statusPageUrl: 'https://status.cloudflow.io',
legalUrl: 'https://cloudflow.io/terms',
apiEndpoint: 'https://api.cloudflow.io/status',
refreshInterval: 60,
showHistoricalDays: 90,
theme: 'auto',
language: 'en',
timeZone: 'UTC',
dateFormat: 'relative',
enableWebSocket: true,
enableNotifications: false,
whitelabel: false,
};
config.config = configData;
}}
>
<upladmin-statuspage-config></upladmin-statuspage-config>
</dees-demowrapper>
</div>
`;
export const adminpageConfigWhitelabel = () => html `
<style>
.demo-page-wrapper {
min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
padding: 24px;
}
</style>
<div class="demo-page-wrapper">
<dees-demowrapper
.runAfterRender=${async (wrapperElement) => {
const config = wrapperElement.querySelector('upladmin-statuspage-config');
const configData = {
companyName: 'Enterprise Corp',
companyLogo: 'https://via.placeholder.com/200x60/1a1a2e/ffffff?text=ENTERPRISE',
supportEmail: 'support@enterprise.com',
statusPageUrl: 'https://status.enterprise.com',
legalUrl: 'https://enterprise.com/legal',
apiEndpoint: 'https://api.enterprise.com/v2/status',
refreshInterval: 30,
showHistoricalDays: 180,
theme: 'dark',
language: 'en',
timeZone: 'America/New_York',
dateFormat: 'absolute',
enableWebSocket: true,
enableNotifications: true,
whitelabel: true,
};
config.config = configData;
}}
>
<upladmin-statuspage-config></upladmin-statuspage-config>
</dees-demowrapper>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRtaW5wYWdlLWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3dlYi9wYWdlcy9hZG1pbnBhZ2UtY29uZmlnLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFL0QsT0FBTyxzQkFBc0IsQ0FBQztBQUU5QixNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFBOzs7O29CQUlyQixVQUFVLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUM7Ozs7Ozs7c0JBT3RDLEtBQUssRUFBRSxjQUFtQixFQUFFLEVBQUU7SUFDOUMsTUFBTSxNQUFNLEdBQUcsY0FBYyxDQUFDLGFBQWEsQ0FBQyw0QkFBNEIsQ0FBUSxDQUFDO0lBRWpGLE1BQU0sVUFBVSxHQUFzQjtRQUNwQyxXQUFXLEVBQUUsZ0JBQWdCO1FBQzdCLFdBQVcsRUFBRSxFQUFFO1FBQ2YsWUFBWSxFQUFFLHNCQUFzQjtRQUNwQyxhQUFhLEVBQUUsNkJBQTZCO1FBQzVDLFFBQVEsRUFBRSw0QkFBNEI7UUFDdEMsV0FBVyxFQUFFLGlDQUFpQztRQUM5QyxlQUFlLEVBQUUsRUFBRTtRQUNuQixrQkFBa0IsRUFBRSxFQUFFO1FBQ3RCLEtBQUssRUFBRSxNQUFNO1FBQ2IsUUFBUSxFQUFFLElBQUk7UUFDZCxRQUFRLEVBQUUsS0FBSztRQUNmLFVBQVUsRUFBRSxVQUFVO1FBQ3RCLGVBQWUsRUFBRSxJQUFJO1FBQ3JCLG1CQUFtQixFQUFFLEtBQUs7UUFDMUIsVUFBVSxFQUFFLEtBQUs7S0FDbEIsQ0FBQztJQUVGLE1BQU0sQ0FBQyxNQUFNLEdBQUcsVUFBVSxDQUFDO0FBQzdCLENBQUM7Ozs7O0NBS0osQ0FBQztBQUVGLE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQTs7OztvQkFJL0IsVUFBVSxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDOzs7Ozs7O3NCQU90QyxLQUFLLEVBQUUsY0FBbUIsRUFBRSxFQUFFO0lBQzlDLE1BQU0sTUFBTSxHQUFHLGNBQWMsQ0FBQyxhQUFhLENBQUMsNEJBQTRCLENBQVEsQ0FBQztJQUVqRixNQUFNLFVBQVUsR0FBc0I7UUFDcEMsV0FBVyxFQUFFLGlCQUFpQjtRQUM5QixXQUFXLEVBQUUsa0VBQWtFO1FBQy9FLFlBQVksRUFBRSx3QkFBd0I7UUFDdEMsYUFBYSxFQUFFLCtCQUErQjtRQUM5QyxRQUFRLEVBQUUsOEJBQThCO1FBQ3hDLFdBQVcsRUFBRSxzQ0FBc0M7UUFDbkQsZUFBZSxFQUFFLEVBQUU7UUFDbkIsa0JBQWtCLEVBQUUsR0FBRztRQUN2QixLQUFLLEVBQUUsTUFBTTtRQUNiLFFBQVEsRUFBRSxJQUFJO1FBQ2QsUUFBUSxFQUFFLGtCQUFrQjtRQUM1QixVQUFVLEVBQUUsVUFBVTtRQUN0QixlQUFlLEVBQUUsSUFBSTtRQUNyQixtQkFBbUIsRUFBRSxJQUFJO1FBQ3pCLFVBQVUsRUFBRSxJQUFJO0tBQ2pCLENBQUM7SUFFRixNQUFNLENBQUMsTUFBTSxHQUFHLFVBQVUsQ0FBQztBQUM3QixDQUFDOzs7OztDQUtKLENBQUMifQ==

View File

@@ -0,0 +1,2 @@
import '../elements/index.js';
export declare const adminpageDashboard: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,152 @@
import { html, cssManager } from "@design.estate/dees-element";
import '../elements/index.js';
export const adminpageDashboard = () => html `
<style>
.demo-page-wrapper {
min-height: 100vh;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
padding: 24px;
}
</style>
<div class="demo-page-wrapper">
<dees-demowrapper
.runAfterRender=${async (wrapperElement) => {
const dashboard = wrapperElement.querySelector('upladmin-dashboard');
// Demo monitors
const monitors = [
{
id: 'api-server',
name: 'api-server',
displayName: 'API Server',
description: 'Main REST API endpoint',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.98,
uptime90d: 99.95,
responseTime: 45,
category: 'Core Services',
},
{
id: 'web-app',
name: 'web-app',
displayName: 'Web Application',
description: 'Customer-facing web application',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.99,
uptime90d: 99.97,
responseTime: 120,
category: 'Core Services',
},
{
id: 'database-primary',
name: 'database-primary',
displayName: 'Primary Database',
description: 'PostgreSQL primary node',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.999,
uptime90d: 99.998,
responseTime: 5,
category: 'Infrastructure',
},
{
id: 'cdn',
name: 'cdn',
displayName: 'Content Delivery Network',
description: 'Global CDN for static assets',
currentStatus: 'degraded',
lastChecked: Date.now(),
uptime30d: 99.5,
uptime90d: 99.8,
responseTime: 200,
category: 'Infrastructure',
},
{
id: 'email-service',
name: 'email-service',
displayName: 'Email Service',
description: 'Transactional email delivery',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.9,
uptime90d: 99.85,
responseTime: 500,
category: 'External Services',
},
{
id: 'payment-gateway',
name: 'payment-gateway',
displayName: 'Payment Gateway',
description: 'Payment processing integration',
currentStatus: 'maintenance',
lastChecked: Date.now(),
uptime30d: 99.95,
uptime90d: 99.9,
responseTime: 350,
category: 'External Services',
},
];
// Demo incidents
const incidents = [
{
id: 'inc-001',
title: 'CDN Performance Degradation',
status: 'monitoring',
severity: 'minor',
affectedServices: ['cdn'],
startTime: Date.now() - 2 * 60 * 60 * 1000,
impact: 'Some users may experience slower loading times for images and static assets.',
updates: [
{
id: 'upd-001',
timestamp: Date.now() - 2 * 60 * 60 * 1000,
status: 'investigating',
message: 'We are investigating reports of slow asset loading.',
author: 'Platform Team',
},
{
id: 'upd-002',
timestamp: Date.now() - 1 * 60 * 60 * 1000,
status: 'identified',
message: 'We have identified the issue as a problem with one of our CDN edge nodes.',
author: 'Platform Team',
},
{
id: 'upd-003',
timestamp: Date.now() - 30 * 60 * 1000,
status: 'monitoring',
message: 'Traffic has been rerouted to healthy nodes. Monitoring for stability.',
author: 'Platform Team',
},
],
},
{
id: 'inc-002',
title: 'Payment Gateway Scheduled Maintenance',
status: 'investigating',
severity: 'maintenance',
affectedServices: ['payment-gateway'],
startTime: Date.now() - 30 * 60 * 1000,
impact: 'Payment processing is temporarily unavailable during the maintenance window.',
updates: [
{
id: 'upd-004',
timestamp: Date.now() - 30 * 60 * 1000,
status: 'investigating',
message: 'Scheduled maintenance has begun. Expected duration: 2 hours.',
author: 'DevOps Team',
},
],
},
];
dashboard.monitors = monitors;
dashboard.incidents = incidents;
}}
>
<upladmin-dashboard></upladmin-dashboard>
</dees-demowrapper>
</div>
`;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWRtaW5wYWdlLWRhc2hib2FyZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzX3dlYi9wYWdlcy9hZG1pbnBhZ2UtZGFzaGJvYXJkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsVUFBVSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFFL0QsT0FBTyxzQkFBc0IsQ0FBQztBQUU5QixNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUE7Ozs7b0JBSXhCLFVBQVUsQ0FBQyxPQUFPLENBQUMsU0FBUyxFQUFFLFNBQVMsQ0FBQzs7Ozs7OztzQkFPdEMsS0FBSyxFQUFFLGNBQW1CLEVBQUUsRUFBRTtJQUM5QyxNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsYUFBYSxDQUFDLG9CQUFvQixDQUFRLENBQUM7SUFFNUUsZ0JBQWdCO0lBQ2hCLE1BQU0sUUFBUSxHQUFxQjtRQUNqQztZQUNFLEVBQUUsRUFBRSxZQUFZO1lBQ2hCLElBQUksRUFBRSxZQUFZO1lBQ2xCLFdBQVcsRUFBRSxZQUFZO1lBQ3pCLFdBQVcsRUFBRSx3QkFBd0I7WUFDckMsYUFBYSxFQUFFLGFBQWE7WUFDNUIsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDdkIsU0FBUyxFQUFFLEtBQUs7WUFDaEIsU0FBUyxFQUFFLEtBQUs7WUFDaEIsWUFBWSxFQUFFLEVBQUU7WUFDaEIsUUFBUSxFQUFFLGVBQWU7U0FDMUI7UUFDRDtZQUNFLEVBQUUsRUFBRSxTQUFTO1lBQ2IsSUFBSSxFQUFFLFNBQVM7WUFDZixXQUFXLEVBQUUsaUJBQWlCO1lBQzlCLFdBQVcsRUFBRSxpQ0FBaUM7WUFDOUMsYUFBYSxFQUFFLGFBQWE7WUFDNUIsV0FBVyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDdkIsU0FBUyxFQUFFLEtBQUs7WUFDaEIsU0FBUyxFQUFFLEtBQUs7WUFDaEIsWUFBWSxFQUFFLEdB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

View File

@@ -0,0 +1,4 @@
import '../elements/index.js';
export declare const adminpageIncidents: () => import("@design.estate/dees-element").TemplateResult<1>;
export declare const adminpageIncidentForm: () => import("@design.estate/dees-element").TemplateResult<1>;
export declare const adminpageIncidentUpdate: () => import("@design.estate/dees-element").TemplateResult<1>;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
import '../elements/index.js';
export declare const adminpageMonitors: () => import("@design.estate/dees-element").TemplateResult<1>;
export declare const adminpageMonitorForm: () => import("@design.estate/dees-element").TemplateResult<1>;
export declare const adminpageMonitorFormEdit: () => import("@design.estate/dees-element").TemplateResult<1>;

File diff suppressed because one or more lines are too long

5
dist_ts_web/pages/index.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
export * from './adminpage-dashboard.js';
export * from './adminpage-monitors.js';
export * from './adminpage-incidents.js';
export * from './adminpage-config.js';
export { demoFunc as adminpageApp } from './upladmin-app/upladmin-app.demo.js';

View File

@@ -0,0 +1,6 @@
export * from './adminpage-dashboard.js';
export * from './adminpage-monitors.js';
export * from './adminpage-incidents.js';
export * from './adminpage-config.js';
export { demoFunc as adminpageApp } from './upladmin-app/upladmin-app.demo.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c193ZWIvcGFnZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyx1QkFBdUIsQ0FBQztBQUN0QyxPQUFPLEVBQUUsUUFBUSxJQUFJLFlBQVksRUFBRSxNQUFNLHFDQUFxQyxDQUFDIn0=

View File

@@ -0,0 +1,24 @@
import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
import type { DeesAppuiBase } from '@design.estate/dees-catalog';
import '../../elements/upladmin-dashboard/upladmin-dashboard.js';
import '../../elements/upladmin-monitor-list/upladmin-monitor-list.js';
import '../../elements/upladmin-monitor-form/upladmin-monitor-form.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';
import '../../elements/upladmin-statuspage-config/upladmin-statuspage-config.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-app': UpladminApp;
}
}
export declare class UpladminApp extends DeesElement {
static demo: () => TemplateResult<1>;
accessor appuiBase: DeesAppuiBase | null;
static styles: import("@design.estate/dees-element").CSSResult[];
firstUpdated(): Promise<void>;
private configureApp;
private setupStateSubscriptions;
private reloadData;
render(): TemplateResult;
}

View File

@@ -0,0 +1,2 @@
import './upladmin-app.js';
export declare const demoFunc: () => import("@design.estate/dees-element").TemplateResult<1>;

View File

@@ -0,0 +1,166 @@
import { html } from '@design.estate/dees-element';
import { adminState } from '../../services/admin-state.js';
import './upladmin-app.js';
// Initialize demo data
const initDemoData = () => {
const now = Date.now();
// Demo monitors
const monitors = [
{
id: 'api-server',
name: 'api-server',
displayName: 'API Server',
description: 'Main REST API backend',
category: 'Core Services',
currentStatus: 'operational',
lastChecked: now,
uptime30d: 99.98,
uptime90d: 99.95,
responseTime: 45,
dependencies: [],
},
{
id: 'web-app',
name: 'web-app',
displayName: 'Web Application',
description: 'Frontend web application',
category: 'Core Services',
currentStatus: 'operational',
lastChecked: now,
uptime30d: 99.95,
uptime90d: 99.90,
responseTime: 120,
dependencies: ['api-server'],
},
{
id: 'database',
name: 'database',
displayName: 'Database',
description: 'Primary PostgreSQL database',
category: 'Infrastructure',
currentStatus: 'operational',
lastChecked: now,
uptime30d: 99.99,
uptime90d: 99.98,
responseTime: 5,
dependencies: [],
},
{
id: 'cdn',
name: 'cdn',
displayName: 'CDN',
description: 'Content delivery network',
category: 'Infrastructure',
currentStatus: 'degraded',
lastChecked: now,
uptime30d: 99.85,
uptime90d: 99.80,
responseTime: 25,
dependencies: [],
},
{
id: 'email-service',
name: 'email-service',
displayName: 'Email Service',
description: 'Transactional email delivery',
category: 'External Services',
currentStatus: 'operational',
lastChecked: now,
uptime30d: 99.90,
uptime90d: 99.85,
responseTime: 200,
dependencies: [],
},
];
// Demo incidents
const incidents = [
{
id: 'incident-1',
title: 'CDN Performance Degradation',
impact: 'We are experiencing slower than normal response times from our CDN provider.',
severity: 'minor',
status: 'monitoring',
affectedServices: ['cdn'],
startTime: now - 2 * 60 * 60 * 1000, // 2 hours ago
updates: [
{
id: 'update-1-1',
status: 'investigating',
message: 'We are investigating reports of slow load times.',
timestamp: now - 2 * 60 * 60 * 1000,
},
{
id: 'update-1-2',
status: 'identified',
message: 'The issue has been identified as a CDN edge node problem.',
timestamp: now - 1 * 60 * 60 * 1000,
},
{
id: 'update-1-3',
status: 'monitoring',
message: 'A fix has been deployed. We are monitoring the situation.',
timestamp: now - 30 * 60 * 1000,
},
],
},
{
id: 'incident-2',
title: 'Scheduled Database Maintenance',
impact: 'Routine database maintenance window.',
severity: 'maintenance',
status: 'resolved',
affectedServices: ['database'],
startTime: now - 24 * 60 * 60 * 1000,
endTime: now - 23 * 60 * 60 * 1000,
updates: [
{
id: 'update-2-1',
status: 'investigating',
message: 'Maintenance has begun.',
timestamp: now - 24 * 60 * 60 * 1000,
},
{
id: 'update-2-2',
status: 'resolved',
message: 'Maintenance completed successfully.',
timestamp: now - 23 * 60 * 60 * 1000,
},
],
},
];
// Demo config
const config = {
theme: 'dark',
companyName: 'uptime.link',
companyLogo: '',
supportEmail: 'support@uptime.link',
showHistoricalDays: 90,
timeZone: 'UTC',
};
// Set demo data in state
adminState.monitors = monitors;
adminState.incidents = incidents;
adminState.config = config;
};
export const demoFunc = () => {
// Initialize demo data
initDemoData();
return html `
<dees-demowrapper>
<style>
.demo-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<div class="demo-container">
<upladmin-app></upladmin-app>
</div>
</dees-demowrapper>
`;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBsYWRtaW4tYXBwLmRlbW8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90c193ZWIvcGFnZXMvdXBsYWRtaW4tYXBwL3VwbGFkbWluLWFwcC5kZW1vLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFFM0QsT0FBTyxtQkFBbUIsQ0FBQztBQUUzQix1QkFBdUI7QUFDdkIsTUFBTSxZQUFZLEdBQUcsR0FBRyxFQUFFO0lBQ3hCLE1BQU0sR0FBRyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUV2QixnQkFBZ0I7SUFDaEIsTUFBTSxRQUFRLEdBQXFCO1FBQ2pDO1lBQ0UsRUFBRSxFQUFFLFlBQVk7WUFDaEIsSUFBSSxFQUFFLFlBQVk7WUFDbEIsV0FBVyxFQUFFLFlBQVk7WUFDekIsV0FBVyxFQUFFLHVCQUF1QjtZQUNwQyxRQUFRLEVBQUUsZUFBZTtZQUN6QixhQUFhLEVBQUUsYUFBYTtZQUM1QixXQUFXLEVBQUUsR0FBRztZQUNoQixTQUFTLEVBQUUsS0FBSztZQUNoQixTQUFTLEVBQUUsS0FBSztZQUNoQixZQUFZLEVBQUUsRUFBRTtZQUNoQixZQUFZLEVBQUUsRUFBRTtTQUNqQjtRQUNEO1lBQ0UsRUFBRSxFQUFFLFNBQVM7WUFDYixJQUFJLEVBQUUsU0FBUztZQUNmLFdBQVcsRUFBRSxpQkFBaUI7WUFDOUIsV0FBVyxFQUFFLDBCQUEwQjtZQUN2QyxRQUFRLEVBQUUsZUFBZTtZQUN6QixhQUFhLEVBQUUsYUFBYTtZQUM1QixXQUFXLEVBQUUsR0FBRztZQUNoQixTQUFTLEVBQUUsS0FBSztZQUNoQixTQUFTLEVBQUUsS0FBSztZQUNoQixZQUFZLEVBQUUsR0FBRztZQUNqQixZQUFZLEVBQUUsQ0FBQyxZQUFZLENBQUM7U0FDN0I7UUFDRDtZQUNFLEVBQUUsRUFB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

File diff suppressed because one or more lines are too long

4
dist_ts_web/plugins.d.ts vendored Normal file
View File

@@ -0,0 +1,4 @@
import * as domtools from '@design.estate/dees-domtools';
import * as deesCatalog from '@design.estate/dees-catalog';
import * as uplInterfaces from '@uptime.link/interfaces';
export { domtools, deesCatalog, uplInterfaces };

5
dist_ts_web/plugins.js Normal file
View File

@@ -0,0 +1,5 @@
import * as domtools from '@design.estate/dees-domtools';
import * as deesCatalog from '@design.estate/dees-catalog';
import * as uplInterfaces from '@uptime.link/interfaces';
export { domtools, deesCatalog, uplInterfaces };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX3dlYi9wbHVnaW5zLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxRQUFRLE1BQU0sOEJBQThCLENBQUM7QUFDekQsT0FBTyxLQUFLLFdBQVcsTUFBTSw2QkFBNkIsQ0FBQztBQUMzRCxPQUFPLEtBQUssYUFBYSxNQUFNLHlCQUF5QixDQUFDO0FBRXpELE9BQU8sRUFDTCxRQUFRLEVBQ1IsV0FBVyxFQUNYLGFBQWEsRUFDZCxDQUFBIn0=

50
dist_ts_web/services/admin-state.d.ts vendored Normal file
View File

@@ -0,0 +1,50 @@
import type { IServiceStatus, IIncidentDetails, IStatusPageConfig, IMonitorFormData, IIncidentFormData } from '../interfaces/index.js';
type TStateChangeListener<T> = (data: T) => void;
/**
* Simple observable implementation for state changes
*/
declare class SimpleObservable<T> {
private listeners;
subscribe(listener: TStateChangeListener<T>): () => void;
next(value: T): void;
}
/**
* Centralized state management for the admin dashboard.
* Handles cross-view data passing and state synchronization.
*/
export declare class AdminState {
monitors$: SimpleObservable<IServiceStatus[]>;
incidents$: SimpleObservable<IIncidentDetails[]>;
config$: SimpleObservable<IStatusPageConfig>;
private _monitors;
private _incidents;
private _config;
private _selectedMonitor;
private _selectedIncident;
get monitors(): IServiceStatus[];
set monitors(value: IServiceStatus[]);
get incidents(): IIncidentDetails[];
set incidents(value: IIncidentDetails[]);
get config(): IStatusPageConfig | null;
set config(value: IStatusPageConfig | null);
setSelectedMonitor(monitor: IServiceStatus | null): void;
getSelectedMonitor(): IServiceStatus | null;
clearSelectedMonitor(): void;
setSelectedIncident(incident: IIncidentDetails | null): void;
getSelectedIncident(): IIncidentDetails | null;
clearSelectedIncident(): void;
getCategories(): string[];
getAvailableServices(): IServiceStatus[];
getMonitorById(id: string): IServiceStatus | undefined;
getIncidentById(id: string): IIncidentDetails | undefined;
getActiveIncidents(): IIncidentDetails[];
getPastIncidents(): IIncidentDetails[];
addMonitor(monitor: IServiceStatus): void;
updateMonitor(id: string, data: Partial<IMonitorFormData>): void;
deleteMonitor(id: string): void;
addIncident(incident: IIncidentDetails): void;
updateIncident(id: string, data: Partial<IIncidentFormData>): void;
deleteIncident(id: string): void;
}
export declare const adminState: AdminState;
export {};

File diff suppressed because one or more lines are too long

1
dist_ts_web/services/index.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
export * from './admin-state.js';

View File

@@ -0,0 +1,2 @@
export * from './admin-state.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c193ZWIvc2VydmljZXMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxrQkFBa0IsQ0FBQyJ9

91
dist_ts_web/styles/shared.styles.d.ts vendored Normal file
View File

@@ -0,0 +1,91 @@
export declare const fonts: {
base: string;
mono: string;
};
export declare const colors: {
background: {
primary: import("@design.estate/dees-element").CSSResult;
secondary: import("@design.estate/dees-element").CSSResult;
muted: import("@design.estate/dees-element").CSSResult;
card: import("@design.estate/dees-element").CSSResult;
elevated: import("@design.estate/dees-element").CSSResult;
};
border: {
default: import("@design.estate/dees-element").CSSResult;
muted: import("@design.estate/dees-element").CSSResult;
subtle: import("@design.estate/dees-element").CSSResult;
light: import("@design.estate/dees-element").CSSResult;
strong: import("@design.estate/dees-element").CSSResult;
};
text: {
primary: import("@design.estate/dees-element").CSSResult;
secondary: import("@design.estate/dees-element").CSSResult;
muted: import("@design.estate/dees-element").CSSResult;
};
status: {
operational: import("@design.estate/dees-element").CSSResult;
degraded: import("@design.estate/dees-element").CSSResult;
partial: import("@design.estate/dees-element").CSSResult;
major: import("@design.estate/dees-element").CSSResult;
maintenance: import("@design.estate/dees-element").CSSResult;
partialOutage: import("@design.estate/dees-element").CSSResult;
majorOutage: import("@design.estate/dees-element").CSSResult;
};
accent: {
primary: import("@design.estate/dees-element").CSSResult;
hover: import("@design.estate/dees-element").CSSResult;
focus: import("@design.estate/dees-element").CSSResult;
danger: import("@design.estate/dees-element").CSSResult;
dangerHover: import("@design.estate/dees-element").CSSResult;
success: import("@design.estate/dees-element").CSSResult;
warning: import("@design.estate/dees-element").CSSResult;
};
};
export declare const shadows: {
xs: string;
sm: string;
base: string;
md: string;
lg: string;
xl: string;
inner: string;
};
export declare const borderRadius: {
xs: string;
sm: string;
base: string;
md: string;
lg: string;
xl: string;
'2xl': string;
full: string;
};
export declare const spacing: {
xs: string;
sm: string;
md: string;
lg: string;
xl: string;
'2xl': string;
'3xl': string;
'4xl': string;
};
export declare const easings: {
default: string;
smooth: string;
bounce: string;
snappy: string;
spring: string;
};
export declare const durations: {
instant: string;
fast: string;
normal: string;
slow: string;
slower: string;
slowest: string;
};
export declare const commonStyles: import("@design.estate/dees-element").CSSResult;
export declare const getStatusColor: (status: string) => import("@design.estate/dees-element").CSSResult;
export declare const getSeverityLabel: (severity: string) => string;
export declare const getStatusLabel: (status: string) => string;

File diff suppressed because one or more lines are too long

243750
dist_watch/bundle.js Normal file

File diff suppressed because one or more lines are too long

7
dist_watch/bundle.js.map Normal file

File diff suppressed because one or more lines are too long

29
dist_watch/index.html Normal file
View File

@@ -0,0 +1,29 @@
<!--gitzone element-->
<!-- made by Task Venture Capital GmbH -->
<!-- checkout https://maintainedby.lossless.com for awesome OpenSource projects -->
<html lang="en">
<head>
<!--Lets set some basic meta tags-->
<meta
name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"
/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--Lets load standard fonts-->
<link rel="preconnect" href="https://assetbroker.lossless.one/" crossorigin>
<link rel="preload" href="https://assetbroker.lossless.one/fonts/geist-sans/geistvf.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="https://assetbroker.lossless.one/fonts/fonts.css">
<style>
body {
margin: 0px;
background: #222222;
}
</style>
<script type="module" src="/bundle.js"></script>
</head>
<body>
</body>
</html>

29
html/index.html Normal file
View File

@@ -0,0 +1,29 @@
<!--gitzone element-->
<!-- made by Task Venture Capital GmbH -->
<!-- checkout https://maintainedby.lossless.com for awesome OpenSource projects -->
<html lang="en">
<head>
<!--Lets set some basic meta tags-->
<meta
name="viewport"
content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height"
/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!--Lets load standard fonts-->
<link rel="preconnect" href="https://assetbroker.lossless.one/" crossorigin>
<link rel="preload" href="https://assetbroker.lossless.one/fonts/geist-sans/geistvf.woff2" as="font" type="font/woff2" crossorigin>
<link rel="stylesheet" href="https://assetbroker.lossless.one/fonts/fonts.css">
<style>
body {
margin: 0px;
background: #222222;
}
</style>
<script type="module" src="/bundle.js"></script>
</head>
<body>
</body>
</html>

12
html/index.ts Normal file
View File

@@ -0,0 +1,12 @@
// dees tools
import * as deesWccTools from '@design.estate/dees-wcctools';
import * as deesDomTools from '@design.estate/dees-domtools';
// Import demotools to register dees-demowrapper
import '@design.estate/dees-wcctools/demotools';
// elements and pages
import * as elements from '../ts_web/elements/index.js';
import * as pages from '../ts_web/pages/index.js';
deesWccTools.setupWccTools(elements as any, pages);
deesDomTools.elementBasic.setup();

39
npmextra.json Normal file
View File

@@ -0,0 +1,39 @@
{
"@git.zone/cli": {
"projectType": "wcc",
"module": {
"githost": "code.foss.global",
"gitscope": "uptime.link",
"gitrepo": "statuspage-admin",
"description": "Admin components for managing UptimeLink status pages, monitors, and incidents.",
"npmPackagename": "@uptime.link/statuspage-admin",
"license": "MIT",
"projectDomain": "uptime.link",
"keywords": [
"web components",
"uptimelink",
"admin",
"dashboard",
"status monitoring",
"typescript",
"incidents",
"monitors",
"management",
"frontend",
"UI",
"catalog"
]
},
"release": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
},
"@ship.zone/szci": {
"npmGlobalTools": [],
"npmRegistryUrl": "verdaccio.lossless.one"
}
}

62
package.json Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "@uptime.link/statuspage-admin",
"version": "1.0.0",
"private": false,
"description": "Admin components for managing UptimeLink status pages, monitors, and incidents.",
"main": "dist_ts_web/index.js",
"typings": "dist_ts_web/index.d.ts",
"type": "module",
"scripts": {
"test": "npm run build",
"build": "tsbuild tsfolders --allowimplicitany && tsbundle element --production",
"watch": "tswatch element",
"buildDocs": "tsdoc"
},
"author": "Lossless GmbH",
"license": "UNLICENSED",
"dependencies": {
"@design.estate/dees-catalog": "^3.4.0",
"@design.estate/dees-domtools": "^2.3.6",
"@design.estate/dees-element": "^2.1.3",
"@design.estate/dees-wcctools": "^3.2.0",
"@uptime.link/interfaces": "^2.0.21"
},
"devDependencies": {
"@git.zone/tsbuild": "^4.0.2",
"@git.zone/tsbundle": "^2.6.3",
"@git.zone/tsrun": "^2.0.1",
"@git.zone/tswatch": "^2.3.13",
"@push.rocks/projectinfo": "^5.0.2",
"@push.rocks/smartenv": "^6.0.0",
"@types/node": "^25.0.3"
},
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
],
"browserslist": [
"last 1 Chrome versions"
],
"keywords": [
"web components",
"uptimelink",
"admin",
"dashboard",
"status monitoring",
"typescript",
"incidents",
"monitors",
"management",
"frontend",
"UI",
"catalog"
]
}

6912
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

1
test/test.ts Normal file
View File

@@ -0,0 +1 @@
console.log('hello');

14
ts_web/elements/index.ts Normal file
View File

@@ -0,0 +1,14 @@
// Monitor components
export * from './upladmin-monitor-form/index.js';
export * from './upladmin-monitor-list/index.js';
// Incident components
export * from './upladmin-incident-form/index.js';
export * from './upladmin-incident-list/index.js';
export * from './upladmin-incident-update/index.js';
// Configuration components
export * from './upladmin-statuspage-config/index.js';
// Dashboard components
export * from './upladmin-dashboard/index.js';

View File

@@ -0,0 +1 @@
export * from './upladmin-dashboard.js';

View File

@@ -0,0 +1,57 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { IServiceStatus, IIncidentDetails } from '../../interfaces/index.js';
import './upladmin-dashboard.js';
export const demoFunc = () => html`
<style>
${css`
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#f4f4f5', '#09090b')};
min-height: 100vh;
}
`}
</style>
<div class="demo-container">
<upladmin-dashboard
.monitors=${[
{ id: 'api', name: 'api', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45, category: 'Core' },
{ id: 'web', name: 'web', displayName: 'Web App', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120, category: 'Core' },
{ id: 'db', name: 'db', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5, category: 'Infrastructure' },
{ id: 'cdn', name: 'cdn', displayName: 'CDN', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200, category: 'Infrastructure' },
{ id: 'cache', name: 'cache', displayName: 'Redis Cache', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 2, category: 'Infrastructure' },
{ id: 'email', name: 'email', displayName: 'Email Service', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.9, uptime90d: 99.85, responseTime: 500, category: 'External' },
{ id: 'payment', name: 'payment', displayName: 'Payment Gateway', currentStatus: 'maintenance', lastChecked: Date.now(), uptime30d: 99.95, uptime90d: 99.9, responseTime: 350, category: 'External' },
{ id: 'search', name: 'search', displayName: 'Search Engine', currentStatus: 'partial_outage', lastChecked: Date.now(), uptime30d: 98.5, uptime90d: 99.2, responseTime: 150, category: 'Core' },
] as IServiceStatus[]}
.activeIncidents=${[
{
id: 'inc-1',
title: 'CDN Performance Degradation',
status: 'identified',
severity: 'minor',
affectedServices: ['cdn'],
startTime: Date.now() - 2 * 60 * 60 * 1000,
impact: 'Some users may experience slower page loads',
updates: [
{ id: 'u1', timestamp: Date.now() - 1 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified as network congestion' },
{ id: 'u2', timestamp: Date.now() - 2 * 60 * 60 * 1000, status: 'investigating', message: 'We are investigating reports of slow content delivery' },
],
},
{
id: 'inc-2',
title: 'Search Cluster Partial Failure',
status: 'investigating',
severity: 'major',
affectedServices: ['search'],
startTime: Date.now() - 30 * 60 * 1000,
impact: 'Search functionality may return incomplete results',
updates: [
{ id: 'u3', timestamp: Date.now() - 30 * 60 * 1000, status: 'investigating', message: 'We are investigating issues with the search cluster' },
],
},
] as IIncidentDetails[]}
></upladmin-dashboard>
</div>
`;

View File

@@ -0,0 +1,681 @@
import * as plugins from '../../plugins.js';
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
unsafeCSS,
state,
} from '@design.estate/dees-element';
import * as sharedStyles from '../../styles/shared.styles.js';
import type { IServiceStatus, IIncidentDetails, IOverallStatus } from '../../interfaces/index.js';
import type { IStatsTile } from '@design.estate/dees-catalog';
import { demoFunc } from './upladmin-dashboard.demo.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-dashboard': UpladminDashboard;
}
}
type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
@customElement('upladmin-dashboard')
export class UpladminDashboard extends DeesElement {
public static demo = demoFunc;
@property({ type: Array })
accessor monitors: IServiceStatus[] = [];
@property({ type: Array })
accessor incidents: IIncidentDetails[] = [];
@property({ type: Object })
accessor overallStatus: IOverallStatus | null = null;
@property({ type: Boolean })
accessor loading: boolean = false;
public static styles = [
plugins.domtools.elementBasic.staticStyles,
sharedStyles.commonStyles,
css`
:host {
display: block;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
}
.dashboard {
display: grid;
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
}
/* Overall Status Banner */
.status-banner {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
border: 1px solid;
}
.status-banner.operational {
background: ${cssManager.bdTheme('rgba(34, 197, 94, 0.1)', 'rgba(34, 197, 94, 0.15)')};
border-color: ${sharedStyles.colors.status.operational};
}
.status-banner.degraded {
background: ${cssManager.bdTheme('rgba(234, 179, 8, 0.1)', 'rgba(234, 179, 8, 0.15)')};
border-color: ${sharedStyles.colors.status.degraded};
}
.status-banner.partial_outage {
background: ${cssManager.bdTheme('rgba(249, 115, 22, 0.1)', 'rgba(249, 115, 22, 0.15)')};
border-color: ${sharedStyles.colors.status.partialOutage};
}
.status-banner.major_outage {
background: ${cssManager.bdTheme('rgba(239, 68, 68, 0.1)', 'rgba(239, 68, 68, 0.15)')};
border-color: ${sharedStyles.colors.status.majorOutage};
}
.status-banner.maintenance {
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.15)')};
border-color: ${sharedStyles.colors.status.maintenance};
}
.status-indicator {
width: 48px;
height: 48px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: white;
}
.status-indicator dees-icon {
--icon-size: 24px;
}
.status-indicator.operational { background: ${sharedStyles.colors.status.operational}; }
.status-indicator.degraded { background: ${sharedStyles.colors.status.degraded}; }
.status-indicator.partial_outage { background: ${sharedStyles.colors.status.partialOutage}; }
.status-indicator.major_outage { background: ${sharedStyles.colors.status.majorOutage}; }
.status-indicator.maintenance { background: ${sharedStyles.colors.status.maintenance}; }
.status-content {
flex: 1;
}
.status-title {
font-size: 18px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin-bottom: 4px;
}
.status-message {
font-size: 14px;
color: ${sharedStyles.colors.text.secondary};
}
.status-meta {
font-size: 12px;
color: ${sharedStyles.colors.text.muted};
margin-top: 4px;
}
/* Stats Grid Container */
.stats-container {
margin: 0;
}
dees-statsgrid {
--tile-padding: 20px;
--value-font-size: 28px;
}
/* Content Grid */
.content-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
}
@media (max-width: 900px) {
.content-grid {
grid-template-columns: 1fr;
}
}
/* Section Card */
.section-card {
background: ${sharedStyles.colors.background.secondary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
overflow: hidden;
}
.section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-bottom: 1px solid ${sharedStyles.colors.border.default};
}
.section-title {
font-size: 15px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
}
.section-action {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
font-weight: 500;
color: ${sharedStyles.colors.accent.primary};
background: none;
border: none;
cursor: pointer;
transition: opacity ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.section-action:hover {
opacity: 0.8;
}
.section-action dees-icon {
--icon-size: 14px;
}
.section-body {
padding: ${unsafeCSS(sharedStyles.spacing.md)};
}
/* Status By Category */
.category-list {
display: flex;
flex-direction: column;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.category-item {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.sm)} ${unsafeCSS(sharedStyles.spacing.md)};
background: ${sharedStyles.colors.background.primary};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
}
.category-name {
flex: 1;
font-size: 14px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
}
.category-stats {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.category-count {
font-size: 13px;
color: ${sharedStyles.colors.text.muted};
}
.category-bar {
width: 80px;
height: 6px;
background: ${sharedStyles.colors.background.muted};
border-radius: 3px;
overflow: hidden;
}
.category-bar-fill {
height: 100%;
background: ${sharedStyles.colors.status.operational};
border-radius: 3px;
transition: width ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
}
/* Active Incidents */
.incident-list {
display: flex;
flex-direction: column;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.incident-item {
display: flex;
align-items: flex-start;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.md)};
background: ${sharedStyles.colors.background.primary};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
border-left: 3px solid;
cursor: pointer;
transition: background ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.incident-item:hover {
background: ${sharedStyles.colors.background.muted};
}
.incident-item.critical { border-left-color: ${sharedStyles.colors.status.majorOutage}; }
.incident-item.major { border-left-color: ${sharedStyles.colors.status.partialOutage}; }
.incident-item.minor { border-left-color: ${sharedStyles.colors.status.degraded}; }
.incident-item.maintenance { border-left-color: ${sharedStyles.colors.status.maintenance}; }
.incident-content {
flex: 1;
min-width: 0;
}
.incident-title {
font-size: 14px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.incident-meta {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
font-size: 12px;
color: ${sharedStyles.colors.text.muted};
}
.incident-status {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
border-radius: 9999px;
background: ${sharedStyles.colors.background.muted};
color: ${sharedStyles.colors.text.secondary};
}
/* Quick Actions */
.quick-actions {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.quick-action {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
background: ${sharedStyles.colors.background.primary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
color: ${sharedStyles.colors.text.secondary};
}
.quick-action:hover {
background: ${sharedStyles.colors.background.muted};
border-color: ${sharedStyles.colors.border.strong};
color: ${sharedStyles.colors.text.primary};
}
.quick-action-icon {
display: flex;
align-items: center;
justify-content: center;
}
.quick-action-icon dees-icon {
--icon-size: 24px;
}
.quick-action-label {
font-size: 13px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
text-align: center;
}
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: ${unsafeCSS(sharedStyles.spacing.xl)};
text-align: center;
color: ${sharedStyles.colors.text.muted};
}
.empty-icon {
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
opacity: 0.5;
}
.empty-icon dees-icon {
--icon-size: 32px;
}
.empty-text {
font-size: 14px;
color: ${sharedStyles.colors.text.muted};
}
`
];
private get statsTiles(): IStatsTile[] {
const activeIncidents = this.incidents.filter(i => !['resolved', 'postmortem'].includes(i.status));
const operationalCount = this.monitors.filter(m => m.currentStatus === 'operational').length;
const degradedCount = this.monitors.filter(m => m.currentStatus === 'degraded').length;
const outageCount = this.monitors.filter(m => ['partial_outage', 'major_outage'].includes(m.currentStatus)).length;
const avgUptime = this.monitors.length > 0
? this.monitors.reduce((sum, m) => sum + m.uptime30d, 0) / this.monitors.length
: 100;
const uptimeColor = avgUptime >= 99.9
? sharedStyles.colors.status.operational.cssText
: avgUptime >= 99
? sharedStyles.colors.status.degraded.cssText
: sharedStyles.colors.status.majorOutage.cssText;
return [
{
id: 'uptime',
title: 'Average Uptime (30d)',
value: avgUptime,
unit: '%',
type: 'percentage',
color: uptimeColor,
icon: 'lucide:barChart3',
description: avgUptime >= 99.9 ? 'Excellent' : avgUptime >= 99 ? 'Good' : 'Needs attention',
},
{
id: 'operational',
title: 'Operational Services',
value: operationalCount,
type: 'number',
icon: 'lucide:checkCircle',
color: sharedStyles.colors.status.operational.cssText,
},
{
id: 'issues',
title: 'Services with Issues',
value: degradedCount + outageCount,
type: 'number',
icon: 'lucide:alertTriangle',
color: (degradedCount + outageCount) > 0 ? sharedStyles.colors.status.degraded.cssText : undefined,
},
{
id: 'incidents',
title: 'Active Incidents',
value: activeIncidents.length,
type: 'number',
icon: 'lucide:alertCircle',
color: activeIncidents.length > 0 ? sharedStyles.colors.status.majorOutage.cssText : undefined,
},
];
}
public render(): TemplateResult {
const activeIncidents = this.incidents.filter(i => !['resolved', 'postmortem'].includes(i.status));
return html`
<div class="dashboard">
<!-- Overall Status Banner -->
${this.renderStatusBanner()}
<!-- Stats Grid using dees-statsgrid -->
<div class="stats-container">
<dees-statsgrid
.tiles=${this.statsTiles}
.minTileWidth=${200}
.gap=${16}
></dees-statsgrid>
</div>
<!-- Content Grid -->
<div class="content-grid">
<!-- Active Incidents -->
<div class="section-card">
<div class="section-header">
<span class="section-title">Active Incidents</span>
<button class="section-action" @click="${this.handleViewAllIncidents}">
View All <dees-icon .icon=${'lucide:arrowRight'}></dees-icon>
</button>
</div>
<div class="section-body">
${activeIncidents.length > 0 ? html`
<div class="incident-list">
${activeIncidents.slice(0, 5).map(incident => this.renderIncidentItem(incident))}
</div>
` : html`
<div class="empty-state">
<div class="empty-icon"><dees-icon .icon=${'lucide:partyPopper'}></dees-icon></div>
<div class="empty-text">No active incidents</div>
</div>
`}
</div>
</div>
<!-- Status by Category -->
<div class="section-card">
<div class="section-header">
<span class="section-title">Status by Category</span>
<button class="section-action" @click="${this.handleViewAllMonitors}">
View All <dees-icon .icon=${'lucide:arrowRight'}></dees-icon>
</button>
</div>
<div class="section-body">
${this.renderCategoryStatus()}
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="section-card">
<div class="section-header">
<span class="section-title">Quick Actions</span>
</div>
<div class="section-body">
<div class="quick-actions">
<button class="quick-action" @click="${this.handleNewIncident}">
<span class="quick-action-icon"><dees-icon .icon=${'lucide:alertCircle'}></dees-icon></span>
<span class="quick-action-label">Report Incident</span>
</button>
<button class="quick-action" @click="${this.handleNewMonitor}">
<span class="quick-action-icon"><dees-icon .icon=${'lucide:radio'}></dees-icon></span>
<span class="quick-action-label">Add Monitor</span>
</button>
<button class="quick-action" @click="${this.handleScheduleMaintenance}">
<span class="quick-action-icon"><dees-icon .icon=${'lucide:wrench'}></dees-icon></span>
<span class="quick-action-label">Schedule Maintenance</span>
</button>
<button class="quick-action" @click="${this.handleViewConfig}">
<span class="quick-action-icon"><dees-icon .icon=${'lucide:settings'}></dees-icon></span>
<span class="quick-action-label">Configuration</span>
</button>
</div>
</div>
</div>
</div>
`;
}
private renderStatusBanner(): TemplateResult {
const status = this.overallStatus || this.calculateOverallStatus();
const statusIcons: Record<TStatusType, string> = {
operational: 'lucide:check',
degraded: 'lucide:alertTriangle',
partial_outage: 'lucide:zap',
major_outage: 'lucide:x',
maintenance: 'lucide:wrench',
};
const statusTitles: Record<TStatusType, string> = {
operational: 'All Systems Operational',
degraded: 'Degraded Performance',
partial_outage: 'Partial System Outage',
major_outage: 'Major System Outage',
maintenance: 'Scheduled Maintenance',
};
return html`
<div class="status-banner ${status.status}">
<div class="status-indicator ${status.status}">
<dees-icon .icon=${statusIcons[status.status]}></dees-icon>
</div>
<div class="status-content">
<div class="status-title">${statusTitles[status.status]}</div>
<div class="status-message">${status.message}</div>
<div class="status-meta">
Last updated: ${new Date(status.lastUpdated).toLocaleString()}
</div>
</div>
</div>
`;
}
private renderIncidentItem(incident: IIncidentDetails): TemplateResult {
const formatTime = (timestamp: number) => {
const now = Date.now();
const diff = now - timestamp;
const hours = Math.floor(diff / (1000 * 60 * 60));
if (hours < 1) return `${Math.floor(diff / (1000 * 60))}m ago`;
if (hours < 24) return `${hours}h ago`;
return `${Math.floor(hours / 24)}d ago`;
};
return html`
<div class="incident-item ${incident.severity}" @click="${() => this.handleIncidentClick(incident)}">
<div class="incident-content">
<div class="incident-title">${incident.title}</div>
<div class="incident-meta">
<span class="incident-status">${incident.status}</span>
<span>•</span>
<span>${formatTime(incident.startTime)}</span>
<span>•</span>
<span>${incident.affectedServices.length} services</span>
</div>
</div>
</div>
`;
}
private renderCategoryStatus(): TemplateResult {
const categories = [...new Set(this.monitors.map(m => m.category || 'Uncategorized'))];
if (categories.length === 0) {
return html`
<div class="empty-state">
<div class="empty-icon"><dees-icon .icon=${'lucide:barChart3'}></dees-icon></div>
<div class="empty-text">No monitors configured</div>
</div>
`;
}
return html`
<div class="category-list">
${categories.map(category => {
const categoryMonitors = this.monitors.filter(m => (m.category || 'Uncategorized') === category);
const operational = categoryMonitors.filter(m => m.currentStatus === 'operational').length;
const percentage = (operational / categoryMonitors.length) * 100;
return html`
<div class="category-item">
<span class="category-name">${category}</span>
<div class="category-stats">
<span class="category-count">${operational}/${categoryMonitors.length}</span>
<div class="category-bar">
<div class="category-bar-fill" style="width: ${percentage}%"></div>
</div>
</div>
</div>
`;
})}
</div>
`;
}
private calculateOverallStatus(): IOverallStatus {
const hasOutage = this.monitors.some(m => ['partial_outage', 'major_outage'].includes(m.currentStatus));
const hasDegraded = this.monitors.some(m => m.currentStatus === 'degraded');
const hasMaintenance = this.monitors.some(m => m.currentStatus === 'maintenance');
const affectedCount = this.monitors.filter(m => m.currentStatus !== 'operational').length;
let status: TStatusType = 'operational';
let message = 'All systems are operating normally.';
if (hasOutage) {
status = this.monitors.some(m => m.currentStatus === 'major_outage') ? 'major_outage' : 'partial_outage';
message = `${affectedCount} services are experiencing issues.`;
} else if (hasDegraded) {
status = 'degraded';
message = `${affectedCount} services are experiencing degraded performance.`;
} else if (hasMaintenance) {
status = 'maintenance';
message = `${affectedCount} services are under maintenance.`;
}
return {
status,
message,
lastUpdated: Date.now(),
affectedServices: affectedCount,
totalServices: this.monitors.length,
};
}
private handleViewAllIncidents() {
this.dispatchEvent(new CustomEvent('navigateIncidents', { bubbles: true, composed: true }));
}
private handleViewAllMonitors() {
this.dispatchEvent(new CustomEvent('navigateMonitors', { bubbles: true, composed: true }));
}
private handleIncidentClick(incident: IIncidentDetails) {
this.dispatchEvent(new CustomEvent('incidentSelect', {
detail: { incident },
bubbles: true,
composed: true
}));
}
private handleNewIncident() {
this.dispatchEvent(new CustomEvent('createIncident', { bubbles: true, composed: true }));
}
private handleNewMonitor() {
this.dispatchEvent(new CustomEvent('createMonitor', { bubbles: true, composed: true }));
}
private handleScheduleMaintenance() {
this.dispatchEvent(new CustomEvent('scheduleMaintenance', { bubbles: true, composed: true }));
}
private handleViewConfig() {
this.dispatchEvent(new CustomEvent('navigateConfig', { bubbles: true, composed: true }));
}
}

View File

@@ -0,0 +1 @@
export * from './upladmin-incident-form.js';

View File

@@ -0,0 +1,70 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { IServiceStatus } from '../../interfaces/index.js';
import './upladmin-incident-form.js';
export const demoFunc = () => html`
<style>
${css`
.demo-container {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px;
max-width: 900px;
margin: 0 auto;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 24px;
}
.demo-section h3 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Create New Incident</h3>
<upladmin-incident-form
.availableServices=${[
{ id: 'api', name: 'api', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'web', name: 'web', displayName: 'Web Application', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120 },
{ id: 'db', name: 'db', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
{ id: 'cdn', name: 'cdn', displayName: 'CDN', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200 },
{ id: 'cache', name: 'cache', displayName: 'Redis Cache', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 2 },
] as IServiceStatus[]}
></upladmin-incident-form>
</div>
<div class="demo-section">
<h3>Edit Existing Incident</h3>
<upladmin-incident-form
.incident=${{
id: 'inc-123',
title: 'Database Connection Issues',
severity: 'major',
status: 'identified',
affectedServices: ['db', 'api'],
impact: 'Users may experience slow response times and occasional timeouts when accessing the application.',
rootCause: 'Connection pool exhaustion due to a memory leak in the database driver.',
}}
.availableServices=${[
{ id: 'api', name: 'api', displayName: 'API Server', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'web', name: 'web', displayName: 'Web Application', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120 },
{ id: 'db', name: 'db', displayName: 'Database', currentStatus: 'partial_outage', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
{ id: 'cdn', name: 'cdn', displayName: 'CDN', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200 },
{ id: 'cache', name: 'cache', displayName: 'Redis Cache', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 2 },
] as IServiceStatus[]}
></upladmin-incident-form>
</div>
</div>
`;

View File

@@ -0,0 +1,585 @@
import * as plugins from '../../plugins.js';
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
unsafeCSS,
state,
} from '@design.estate/dees-element';
import * as sharedStyles from '../../styles/shared.styles.js';
import type { IIncidentFormData, IServiceStatus } from '../../interfaces/index.js';
import { demoFunc } from './upladmin-incident-form.demo.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-incident-form': UpladminIncidentForm;
}
}
type TSeverity = 'critical' | 'major' | 'minor' | 'maintenance';
type TIncidentStatus = 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
@customElement('upladmin-incident-form')
export class UpladminIncidentForm extends DeesElement {
public static demo = demoFunc;
@property({ type: Object })
accessor incident: IIncidentFormData | null = null;
@property({ type: Array })
accessor availableServices: IServiceStatus[] = [];
@property({ type: Boolean })
accessor loading: boolean = false;
@state()
accessor formData: IIncidentFormData = {
title: '',
severity: 'minor',
status: 'investigating',
affectedServices: [],
impact: '',
rootCause: '',
resolution: '',
};
@state()
accessor errors: Record<string, string> = {};
private severityIcons: Record<TSeverity, string> = {
critical: 'lucide:AlertCircle',
major: 'lucide:AlertTriangle',
minor: 'lucide:Info',
maintenance: 'lucide:Wrench',
};
private statusIcons: Record<TIncidentStatus, string> = {
investigating: 'lucide:Search',
identified: 'lucide:Target',
monitoring: 'lucide:Eye',
resolved: 'lucide:CheckCircle',
postmortem: 'lucide:FileText',
};
public static styles = [
plugins.domtools.elementBasic.staticStyles,
sharedStyles.commonStyles,
css`
:host {
display: block;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
}
.form-container {
background: ${sharedStyles.colors.background.secondary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
overflow: hidden;
}
.form-header {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
border-bottom: 1px solid ${sharedStyles.colors.border.default};
background: ${sharedStyles.colors.background.muted};
}
.form-header dees-icon {
--icon-color: ${cssManager.bdTheme('#f97316', '#fb923c')};
}
.form-title-wrapper {
flex: 1;
}
.form-title {
font-size: 18px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin: 0;
}
.form-subtitle {
font-size: 13px;
color: ${sharedStyles.colors.text.muted};
margin-top: 4px;
}
.form-body {
display: grid;
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
}
dees-form {
display: contents;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
}
@media (max-width: 600px) {
.form-row {
grid-template-columns: 1fr;
}
}
.form-section {
margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
padding-top: ${unsafeCSS(sharedStyles.spacing.md)};
border-top: 1px solid ${sharedStyles.colors.border.light};
}
.section-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
}
.section-title dees-icon {
--icon-color: ${sharedStyles.colors.text.muted};
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-top: 1px solid ${sharedStyles.colors.border.default};
background: ${sharedStyles.colors.background.muted};
}
.option-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.option-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 18px 14px;
background: ${sharedStyles.colors.background.primary};
border: 2px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
text-align: center;
}
.option-card:hover {
border-color: ${sharedStyles.colors.border.strong};
background: ${sharedStyles.colors.background.muted};
}
.option-card.selected {
border-color: ${sharedStyles.colors.accent.primary};
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.05)', 'rgba(96, 165, 250, 0.1)')};
}
.option-card input {
display: none;
}
.option-label {
font-size: 13px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
}
.option-desc {
font-size: 11px;
color: ${sharedStyles.colors.text.muted};
line-height: 1.3;
}
.severity-critical dees-icon { --icon-color: ${sharedStyles.colors.status.majorOutage}; }
.severity-major dees-icon { --icon-color: ${sharedStyles.colors.status.partialOutage}; }
.severity-minor dees-icon { --icon-color: ${sharedStyles.colors.status.degraded}; }
.severity-maintenance dees-icon { --icon-color: ${sharedStyles.colors.status.maintenance}; }
.field-label {
display: block;
font-size: 13px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.field-label.required::after {
content: ' *';
color: ${sharedStyles.colors.accent.danger};
}
.services-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
max-height: 220px;
overflow-y: auto;
padding: 4px;
}
.service-checkbox {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 14px;
background: ${sharedStyles.colors.background.primary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.service-checkbox:hover {
background: ${sharedStyles.colors.background.muted};
border-color: ${sharedStyles.colors.border.strong};
}
.service-checkbox.selected {
border-color: ${sharedStyles.colors.accent.primary};
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.05)', 'rgba(96, 165, 250, 0.1)')};
}
.service-checkbox input {
width: 16px;
height: 16px;
accent-color: ${sharedStyles.colors.accent.primary};
cursor: pointer;
}
.service-checkbox label {
flex: 1;
font-size: 13px;
color: ${sharedStyles.colors.text.primary};
cursor: pointer;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.status-dot.operational { background: ${sharedStyles.colors.status.operational}; }
.status-dot.degraded { background: ${sharedStyles.colors.status.degraded}; }
.status-dot.partial_outage { background: ${sharedStyles.colors.status.partialOutage}; }
.status-dot.major_outage { background: ${sharedStyles.colors.status.majorOutage}; }
.status-dot.maintenance { background: ${sharedStyles.colors.status.maintenance}; }
.error-text {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: ${sharedStyles.colors.accent.danger};
margin-top: ${unsafeCSS(sharedStyles.spacing.xs)};
}
.error-text dees-icon {
--icon-color: ${sharedStyles.colors.accent.danger};
}
/* Style dees-input components */
dees-input-text {
--dees-input-background: ${sharedStyles.colors.background.primary};
--dees-input-border-color: ${sharedStyles.colors.border.default};
}
`
];
async connectedCallback() {
await super.connectedCallback();
if (this.incident) {
this.formData = { ...this.incident };
}
}
updated(changedProperties: Map<string, unknown>) {
if (changedProperties.has('incident') && this.incident) {
this.formData = { ...this.incident };
}
}
public render(): TemplateResult {
const isEdit = !!this.incident?.id;
const severityOptions: Array<{ value: TSeverity; label: string; desc: string }> = [
{ value: 'critical', label: 'Critical', desc: 'Major system failure' },
{ value: 'major', label: 'Major', desc: 'Significant impact' },
{ value: 'minor', label: 'Minor', desc: 'Limited impact' },
{ value: 'maintenance', label: 'Maintenance', desc: 'Planned work' },
];
const statusOptions: Array<{ value: TIncidentStatus; label: string }> = [
{ value: 'investigating', label: 'Investigating' },
{ value: 'identified', label: 'Identified' },
{ value: 'monitoring', label: 'Monitoring' },
{ value: 'resolved', label: 'Resolved' },
{ value: 'postmortem', label: 'Postmortem' },
];
return html`
<div class="form-container">
<div class="form-header">
<dees-icon .icon=${isEdit ? 'lucide:Pencil' : 'lucide:AlertTriangle'} .iconSize=${24}></dees-icon>
<div class="form-title-wrapper">
<h2 class="form-title">${isEdit ? 'Edit Incident' : 'Create Incident'}</h2>
<p class="form-subtitle">
${isEdit ? 'Update incident details' : 'Report a new incident or maintenance'}
</p>
</div>
</div>
<div class="form-body">
<dees-form>
<dees-input-text
key="title"
label="Incident Title"
.value="${this.formData.title}"
placeholder="Brief description of the incident"
required
@changeSubject="${this.handleTitleChange}"
></dees-input-text>
<div>
<label class="field-label required">Severity</label>
<div class="option-grid">
${severityOptions.map(opt => html`
<label
class="option-card severity-${opt.value} ${this.formData.severity === opt.value ? 'selected' : ''}"
@click="${() => this.handleSeverityChange(opt.value)}"
>
<input
type="radio"
name="severity"
value="${opt.value}"
?checked="${this.formData.severity === opt.value}"
/>
<dees-icon .icon=${this.severityIcons[opt.value]} .iconSize=${24}></dees-icon>
<span class="option-label">${opt.label}</span>
<span class="option-desc">${opt.desc}</span>
</label>
`)}
</div>
</div>
<div>
<label class="field-label required">Status</label>
<div class="option-grid">
${statusOptions.map(opt => html`
<label
class="option-card ${this.formData.status === opt.value ? 'selected' : ''}"
@click="${() => this.handleStatusChange(opt.value)}"
>
<input
type="radio"
name="status"
value="${opt.value}"
?checked="${this.formData.status === opt.value}"
/>
<dees-icon .icon=${this.statusIcons[opt.value]} .iconSize=${24}></dees-icon>
<span class="option-label">${opt.label}</span>
</label>
`)}
</div>
</div>
<div>
<label class="field-label required">Affected Services</label>
<div class="services-grid">
${this.availableServices.map(service => html`
<div
class="service-checkbox ${this.formData.affectedServices.includes(service.id) ? 'selected' : ''}"
@click="${() => this.toggleService(service.id)}"
>
<input
type="checkbox"
id="service-${service.id}"
?checked="${this.formData.affectedServices.includes(service.id)}"
/>
<span class="status-dot ${service.currentStatus}"></span>
<label for="service-${service.id}">${service.displayName || service.name}</label>
</div>
`)}
</div>
${this.errors.affectedServices ? html`
<div class="error-text">
<dees-icon .icon=${'lucide:AlertCircle'} .iconSize=${12}></dees-icon>
${this.errors.affectedServices}
</div>
` : ''}
</div>
<dees-input-text
key="impact"
label="Impact Description"
inputType="textarea"
.value="${this.formData.impact}"
placeholder="Describe how users are affected by this incident..."
required
@changeSubject="${this.handleImpactChange}"
></dees-input-text>
<div class="form-section">
<div class="section-title">
<dees-icon .icon=${'lucide:FileSearch'} .iconSize=${16}></dees-icon>
Resolution Details (Optional)
</div>
<div class="form-row">
<dees-input-text
key="rootCause"
label="Root Cause"
inputType="textarea"
.value="${this.formData.rootCause || ''}"
placeholder="What caused this incident..."
@changeSubject="${this.handleRootCauseChange}"
></dees-input-text>
<dees-input-text
key="resolution"
label="Resolution"
inputType="textarea"
.value="${this.formData.resolution || ''}"
placeholder="How was this incident resolved..."
@changeSubject="${this.handleResolutionChange}"
></dees-input-text>
</div>
</div>
</dees-form>
</div>
<div class="form-actions">
<dees-button type="discreet" @click="${this.handleCancel}" ?disabled="${this.loading}">
Cancel
</dees-button>
<dees-button type="highlighted" @click="${this.handleSave}" ?disabled="${this.loading}">
${this.loading ? html`<dees-spinner .size=${16}></dees-spinner>` : ''}
${isEdit ? 'Update Incident' : 'Create Incident'}
</dees-button>
</div>
</div>
`;
}
private handleTitleChange(e: CustomEvent) {
this.formData = { ...this.formData, title: e.detail };
if (this.errors.title) {
this.errors = { ...this.errors, title: '' };
}
}
private handleImpactChange(e: CustomEvent) {
this.formData = { ...this.formData, impact: e.detail };
if (this.errors.impact) {
this.errors = { ...this.errors, impact: '' };
}
}
private handleRootCauseChange(e: CustomEvent) {
this.formData = { ...this.formData, rootCause: e.detail };
}
private handleResolutionChange(e: CustomEvent) {
this.formData = { ...this.formData, resolution: e.detail };
}
private handleSeverityChange(severity: TSeverity) {
this.formData = { ...this.formData, severity };
}
private handleStatusChange(status: TIncidentStatus) {
this.formData = { ...this.formData, status };
}
private toggleService(serviceId: string) {
const current = this.formData.affectedServices;
if (current.includes(serviceId)) {
this.formData = {
...this.formData,
affectedServices: current.filter(id => id !== serviceId)
};
} else {
this.formData = {
...this.formData,
affectedServices: [...current, serviceId]
};
}
if (this.errors.affectedServices) {
this.errors = { ...this.errors, affectedServices: '' };
}
}
private validate(): boolean {
const errors: Record<string, string> = {};
if (!this.formData.title?.trim()) {
errors.title = 'Title is required';
}
if (this.formData.affectedServices.length === 0) {
errors.affectedServices = 'At least one service must be selected';
}
if (!this.formData.impact?.trim()) {
errors.impact = 'Impact description is required';
}
this.errors = errors;
return Object.keys(errors).length === 0;
}
private handleSave() {
if (!this.validate()) {
return;
}
this.dispatchEvent(new CustomEvent('incidentSave', {
detail: { incident: { ...this.formData } },
bubbles: true,
composed: true
}));
}
private handleCancel() {
this.dispatchEvent(new CustomEvent('incidentCancel', {
bubbles: true,
composed: true
}));
}
public reset() {
this.formData = {
title: '',
severity: 'minor',
status: 'investigating',
affectedServices: [],
impact: '',
rootCause: '',
resolution: '',
};
this.errors = {};
}
public setIncident(incident: IIncidentFormData) {
this.formData = { ...incident };
this.errors = {};
}
}

View File

@@ -0,0 +1 @@
export * from './upladmin-incident-list.js';

View File

@@ -0,0 +1,94 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { IIncidentDetails } from '../../interfaces/index.js';
import './upladmin-incident-list.js';
export const demoFunc = () => html`
<style>
${css`
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
`}
</style>
<div class="demo-container">
<upladmin-incident-list
.incidents=${[
{
id: 'inc-1',
title: 'CDN Performance Degradation',
status: 'monitoring',
severity: 'minor',
affectedServices: ['cdn'],
startTime: Date.now() - 4 * 60 * 60 * 1000,
impact: 'Some users may experience slower page loads for static assets.',
rootCause: 'Network congestion at edge locations',
updates: [
{ id: 'u1', timestamp: Date.now() - 1 * 60 * 60 * 1000, status: 'monitoring', message: 'Fix deployed, monitoring results.', author: 'Infrastructure Team' },
{ id: 'u2', timestamp: Date.now() - 2 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified as network congestion at edge nodes.', author: 'Infrastructure Team' },
{ id: 'u3', timestamp: Date.now() - 4 * 60 * 60 * 1000, status: 'investigating', message: 'We are investigating reports of slow content delivery.', author: 'Support Team' },
],
},
{
id: 'inc-2',
title: 'Search Cluster Partial Failure',
status: 'investigating',
severity: 'major',
affectedServices: ['search', 'api'],
startTime: Date.now() - 45 * 60 * 1000,
impact: 'Search functionality may return incomplete results. API responses may be delayed.',
updates: [
{ id: 'u4', timestamp: Date.now() - 45 * 60 * 1000, status: 'investigating', message: 'We are investigating issues with the search cluster. Some nodes are not responding.', author: 'Platform Team' },
],
},
{
id: 'inc-3',
title: 'Scheduled Database Maintenance',
status: 'investigating',
severity: 'maintenance',
affectedServices: ['db', 'api', 'web'],
startTime: Date.now() - 15 * 60 * 1000,
impact: 'Brief interruptions may occur during the maintenance window.',
updates: [
{ id: 'u5', timestamp: Date.now() - 15 * 60 * 1000, status: 'investigating', message: 'Starting scheduled database maintenance. Expected duration: 2 hours.', author: 'DBA Team' },
],
},
{
id: 'inc-4',
title: 'Authentication Service Outage',
status: 'resolved',
severity: 'critical',
affectedServices: ['auth', 'api', 'web'],
startTime: Date.now() - 24 * 60 * 60 * 1000,
endTime: Date.now() - 22 * 60 * 60 * 1000,
impact: 'Users were unable to log in or access authenticated features.',
rootCause: 'Certificate expiration on the identity provider.',
resolution: 'Renewed certificates and implemented automated monitoring for future expirations.',
updates: [
{ id: 'u6', timestamp: Date.now() - 22 * 60 * 60 * 1000, status: 'resolved', message: 'Issue has been fully resolved. All authentication services are operational.', author: 'Security Team' },
{ id: 'u7', timestamp: Date.now() - 23 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified: expired SSL certificate on identity provider.', author: 'Security Team' },
{ id: 'u8', timestamp: Date.now() - 24 * 60 * 60 * 1000, status: 'investigating', message: 'We are aware of authentication issues and are investigating.', author: 'On-call Engineer' },
],
},
{
id: 'inc-5',
title: 'Payment Processing Delays',
status: 'postmortem',
severity: 'major',
affectedServices: ['payment', 'api'],
startTime: Date.now() - 72 * 60 * 60 * 1000,
endTime: Date.now() - 70 * 60 * 60 * 1000,
impact: 'Payment transactions were delayed by up to 5 minutes.',
rootCause: 'Third-party payment provider experienced capacity issues.',
resolution: 'Provider resolved their capacity issues. Implemented fallback payment routing.',
updates: [
{ id: 'u9', timestamp: Date.now() - 48 * 60 * 60 * 1000, status: 'postmortem', message: 'Postmortem complete. Implementing additional redundancy measures.', author: 'Engineering Lead' },
{ id: 'u10', timestamp: Date.now() - 70 * 60 * 60 * 1000, status: 'resolved', message: 'Payment processing has returned to normal.', author: 'Payments Team' },
],
},
] as IIncidentDetails[]}
></upladmin-incident-list>
</div>
`;

View File

@@ -0,0 +1,722 @@
import * as plugins from '../../plugins.js';
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
unsafeCSS,
state,
} from '@design.estate/dees-element';
import * as sharedStyles from '../../styles/shared.styles.js';
import type { IIncidentDetails } from '../../interfaces/index.js';
import { demoFunc } from './upladmin-incident-list.demo.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-incident-list': UpladminIncidentList;
}
}
type TSeverity = 'critical' | 'major' | 'minor' | 'maintenance';
type TIncidentStatus = 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
type TTabFilter = 'current' | 'past' | 'all';
@customElement('upladmin-incident-list')
export class UpladminIncidentList extends DeesElement {
public static demo = demoFunc;
@property({ type: Array })
accessor incidents: IIncidentDetails[] = [];
@property({ type: Boolean })
accessor loading: boolean = false;
@state()
accessor tabFilter: TTabFilter = 'current';
@state()
accessor severityFilter: TSeverity | 'all' = 'all';
@state()
accessor expandedIncidents: Set<string> = new Set();
private statusIcons: Record<TIncidentStatus, string> = {
investigating: 'lucide:Search',
identified: 'lucide:Target',
monitoring: 'lucide:Eye',
resolved: 'lucide:CheckCircle',
postmortem: 'lucide:FileText',
};
private statusLabels: Record<TIncidentStatus, string> = {
investigating: 'Investigating',
identified: 'Identified',
monitoring: 'Monitoring',
resolved: 'Resolved',
postmortem: 'Postmortem',
};
public static styles = [
plugins.domtools.elementBasic.staticStyles,
sharedStyles.commonStyles,
css`
:host {
display: block;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
}
.list-container {
background: ${sharedStyles.colors.background.secondary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
overflow: hidden;
}
.list-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-bottom: 1px solid ${sharedStyles.colors.border.default};
flex-wrap: wrap;
}
.tabs {
display: flex;
gap: 4px;
background: ${sharedStyles.colors.background.muted};
padding: 4px;
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
}
.tab {
padding: 10px 16px;
font-size: 13px;
font-weight: 500;
color: ${sharedStyles.colors.text.secondary};
background: transparent;
border: none;
border-radius: ${unsafeCSS(sharedStyles.borderRadius.sm)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
display: flex;
align-items: center;
gap: 8px;
}
.tab:hover {
color: ${sharedStyles.colors.text.primary};
}
.tab.active {
background: ${sharedStyles.colors.background.primary};
color: ${sharedStyles.colors.text.primary};
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
}
.tab-count {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 22px;
height: 22px;
padding: 0 6px;
font-size: 11px;
font-weight: 600;
background: ${sharedStyles.colors.background.muted};
border-radius: 11px;
}
.tab.active .tab-count {
background: ${sharedStyles.colors.accent.primary};
color: white;
}
.list-controls {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.filter-select {
padding: 10px 32px 10px 12px;
font-size: 13px;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${sharedStyles.colors.text.primary};
background: ${sharedStyles.colors.background.primary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2371717a' d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
}
.incidents-list {
padding: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.incident-card {
background: ${sharedStyles.colors.background.primary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
overflow: hidden;
transition: box-shadow ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.incident-card:last-child {
margin-bottom: 0;
}
.incident-card:hover {
box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
}
.incident-header {
display: flex;
align-items: flex-start;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.md)};
cursor: pointer;
}
.incident-severity {
width: 4px;
align-self: stretch;
border-radius: 2px;
flex-shrink: 0;
}
.incident-severity.critical { background: ${sharedStyles.colors.status.majorOutage}; }
.incident-severity.major { background: ${sharedStyles.colors.status.partialOutage}; }
.incident-severity.minor { background: ${sharedStyles.colors.status.degraded}; }
.incident-severity.maintenance { background: ${sharedStyles.colors.status.maintenance}; }
.incident-main {
flex: 1;
min-width: 0;
}
.incident-title-row {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
margin-bottom: 6px;
flex-wrap: wrap;
}
.incident-title {
font-size: 15px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin: 0;
}
.incident-status {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
font-size: 11px;
font-weight: 500;
border-radius: 9999px;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.incident-status dees-icon {
font-size: 12px;
}
.incident-status.investigating {
background: ${cssManager.bdTheme('rgba(249, 115, 22, 0.1)', 'rgba(249, 115, 22, 0.2)')};
color: ${sharedStyles.colors.status.partialOutage};
--icon-color: ${sharedStyles.colors.status.partialOutage};
}
.incident-status.identified {
background: ${cssManager.bdTheme('rgba(234, 179, 8, 0.1)', 'rgba(234, 179, 8, 0.2)')};
color: ${sharedStyles.colors.status.degraded};
--icon-color: ${sharedStyles.colors.status.degraded};
}
.incident-status.monitoring {
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.2)')};
color: ${sharedStyles.colors.status.maintenance};
--icon-color: ${sharedStyles.colors.status.maintenance};
}
.incident-status.resolved {
background: ${cssManager.bdTheme('rgba(34, 197, 94, 0.1)', 'rgba(34, 197, 94, 0.2)')};
color: ${sharedStyles.colors.status.operational};
--icon-color: ${sharedStyles.colors.status.operational};
}
.incident-status.postmortem {
background: ${cssManager.bdTheme('rgba(168, 85, 247, 0.1)', 'rgba(168, 85, 247, 0.2)')};
color: #a855f7;
--icon-color: #a855f7;
}
.incident-meta {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
font-size: 12px;
color: ${sharedStyles.colors.text.muted};
flex-wrap: wrap;
}
.incident-meta-item {
display: flex;
align-items: center;
gap: 6px;
}
.incident-meta-item dees-icon {
--icon-color: ${sharedStyles.colors.text.muted};
opacity: 0.7;
}
.incident-actions {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.incident-expand {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
padding: 0;
color: ${sharedStyles.colors.text.muted};
background: transparent;
border: none;
border-radius: ${unsafeCSS(sharedStyles.borderRadius.sm)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.incident-expand:hover {
background: ${sharedStyles.colors.background.muted};
color: ${sharedStyles.colors.text.primary};
}
.incident-expand.expanded dees-icon {
transform: rotate(180deg);
}
.incident-expand dees-icon {
transition: transform ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
--icon-color: currentColor;
}
.incident-details {
padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
padding-left: calc(${unsafeCSS(sharedStyles.spacing.md)} + 4px + ${unsafeCSS(sharedStyles.spacing.md)});
border-top: 1px solid ${sharedStyles.colors.border.light};
}
.detail-section {
margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
}
.detail-label {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: ${sharedStyles.colors.text.muted};
margin-bottom: 8px;
}
.detail-text {
font-size: 13px;
color: ${sharedStyles.colors.text.primary};
line-height: 1.6;
}
.services-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.service-tag {
display: inline-block;
padding: 4px 10px;
font-size: 11px;
font-weight: 500;
background: ${sharedStyles.colors.background.muted};
color: ${sharedStyles.colors.text.secondary};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.sm)};
}
.updates-timeline {
position: relative;
padding-left: 24px;
}
.updates-timeline::before {
content: '';
position: absolute;
left: 7px;
top: 8px;
bottom: 8px;
width: 2px;
background: ${sharedStyles.colors.border.default};
border-radius: 1px;
}
.update-item {
position: relative;
padding-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
}
.update-item:last-child {
padding-bottom: 0;
}
.update-item::before {
content: '';
position: absolute;
left: -17px;
top: 6px;
width: 10px;
height: 10px;
background: ${sharedStyles.colors.background.secondary};
border: 2px solid ${sharedStyles.colors.accent.primary};
border-radius: 50%;
}
.update-header {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
margin-bottom: 6px;
}
.update-status {
font-size: 13px;
font-weight: 600;
text-transform: capitalize;
color: ${sharedStyles.colors.text.primary};
}
.update-time {
font-size: 11px;
color: ${sharedStyles.colors.text.muted};
}
.update-message {
font-size: 13px;
color: ${sharedStyles.colors.text.secondary};
line-height: 1.6;
}
.update-author {
font-size: 11px;
color: ${sharedStyles.colors.text.muted};
margin-top: 6px;
font-style: italic;
}
.empty-state {
padding: 64px 24px;
text-align: center;
}
.empty-state dees-icon {
--icon-color: ${sharedStyles.colors.status.operational};
opacity: 0.6;
margin-bottom: 20px;
}
.empty-title {
font-size: 16px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin-bottom: 8px;
}
.empty-text {
font-size: 14px;
color: ${sharedStyles.colors.text.muted};
}
.loading-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: ${cssManager.bdTheme('rgba(255,255,255,0.9)', 'rgba(0,0,0,0.7)')};
z-index: 10;
backdrop-filter: blur(2px);
}
`
];
public render(): TemplateResult {
const filteredIncidents = this.getFilteredIncidents();
const currentCount = this.incidents.filter(i => !['resolved', 'postmortem'].includes(i.status)).length;
const pastCount = this.incidents.filter(i => ['resolved', 'postmortem'].includes(i.status)).length;
return html`
<div class="list-container" style="position: relative;">
${this.loading ? html`
<div class="loading-overlay">
<dees-spinner></dees-spinner>
</div>
` : ''}
<div class="list-header">
<div class="tabs">
<button
class="tab ${this.tabFilter === 'current' ? 'active' : ''}"
@click="${() => this.tabFilter = 'current'}"
>
Current
<span class="tab-count">${currentCount}</span>
</button>
<button
class="tab ${this.tabFilter === 'past' ? 'active' : ''}"
@click="${() => this.tabFilter = 'past'}"
>
Past
<span class="tab-count">${pastCount}</span>
</button>
<button
class="tab ${this.tabFilter === 'all' ? 'active' : ''}"
@click="${() => this.tabFilter = 'all'}"
>
All
<span class="tab-count">${this.incidents.length}</span>
</button>
</div>
<div class="list-controls">
<select class="filter-select" @change="${this.handleSeverityFilter}">
<option value="all" ?selected="${this.severityFilter === 'all'}">All Severities</option>
<option value="critical" ?selected="${this.severityFilter === 'critical'}">Critical</option>
<option value="major" ?selected="${this.severityFilter === 'major'}">Major</option>
<option value="minor" ?selected="${this.severityFilter === 'minor'}">Minor</option>
<option value="maintenance" ?selected="${this.severityFilter === 'maintenance'}">Maintenance</option>
</select>
<dees-button type="highlighted" @click="${this.handleAddClick}">
<dees-icon .icon=${'lucide:Plus'} .iconSize=${16}></dees-icon>
New Incident
</dees-button>
</div>
</div>
${filteredIncidents.length > 0 ? html`
<div class="incidents-list">
${filteredIncidents.map(incident => this.renderIncidentCard(incident))}
</div>
` : html`
<div class="empty-state">
<dees-icon .icon=${'lucide:PartyPopper'} .iconSize=${48}></dees-icon>
<div class="empty-title">
${this.tabFilter === 'current' ? 'No active incidents' : 'No incidents found'}
</div>
<div class="empty-text">
${this.tabFilter === 'current'
? 'All systems are operating normally'
: 'Try adjusting your filters'}
</div>
</div>
`}
</div>
`;
}
private renderIncidentCard(incident: IIncidentDetails): TemplateResult {
const isExpanded = this.expandedIncidents.has(incident.id);
const formatTime = (timestamp: number) => {
const date = new Date(timestamp);
return date.toLocaleString();
};
const formatDuration = (start: number, end?: number) => {
const duration = (end || Date.now()) - start;
const hours = Math.floor(duration / (1000 * 60 * 60));
const minutes = Math.floor((duration % (1000 * 60 * 60)) / (1000 * 60));
if (hours > 0) return `${hours}h ${minutes}m`;
return `${minutes}m`;
};
return html`
<div class="incident-card">
<div class="incident-header" @click="${() => this.toggleExpanded(incident.id)}">
<div class="incident-severity ${incident.severity}"></div>
<div class="incident-main">
<div class="incident-title-row">
<h3 class="incident-title">${incident.title}</h3>
<span class="incident-status ${incident.status}">
<dees-icon .icon=${this.statusIcons[incident.status]} .iconSize=${12}></dees-icon>
${this.statusLabels[incident.status]}
</span>
</div>
<div class="incident-meta">
<span class="incident-meta-item">
<dees-icon .icon=${'lucide:Calendar'} .iconSize=${12}></dees-icon>
${formatTime(incident.startTime)}
</span>
<span class="incident-meta-item">
<dees-icon .icon=${'lucide:Clock'} .iconSize=${12}></dees-icon>
${formatDuration(incident.startTime, incident.endTime)}
</span>
<span class="incident-meta-item">
<dees-icon .icon=${'lucide:Server'} .iconSize=${12}></dees-icon>
${incident.affectedServices.length} services
</span>
<span class="incident-meta-item">
<dees-icon .icon=${'lucide:MessageSquare'} .iconSize=${12}></dees-icon>
${incident.updates.length} updates
</span>
</div>
</div>
<div class="incident-actions" @click="${(e: Event) => e.stopPropagation()}">
${!['resolved', 'postmortem'].includes(incident.status) ? html`
<dees-button type="highlighted" @click="${() => this.handleAddUpdate(incident)}">
<dees-icon .icon=${'lucide:Plus'} .iconSize=${14}></dees-icon>
Update
</dees-button>
` : ''}
<dees-button type="discreet" @click="${() => this.handleEdit(incident)}">
<dees-icon .icon=${'lucide:Pencil'} .iconSize=${14}></dees-icon>
</dees-button>
</div>
<button class="incident-expand ${isExpanded ? 'expanded' : ''}">
<dees-icon .icon=${'lucide:ChevronDown'} .iconSize=${16}></dees-icon>
</button>
</div>
${isExpanded ? html`
<div class="incident-details">
<div class="detail-section">
<div class="detail-label">Impact</div>
<div class="detail-text">${incident.impact}</div>
</div>
<div class="detail-section">
<div class="detail-label">Affected Services</div>
<div class="services-list">
${incident.affectedServices.map(service => html`
<span class="service-tag">${service}</span>
`)}
</div>
</div>
${incident.rootCause ? html`
<div class="detail-section">
<div class="detail-label">Root Cause</div>
<div class="detail-text">${incident.rootCause}</div>
</div>
` : ''}
${incident.resolution ? html`
<div class="detail-section">
<div class="detail-label">Resolution</div>
<div class="detail-text">${incident.resolution}</div>
</div>
` : ''}
${incident.updates.length > 0 ? html`
<div class="detail-section">
<div class="detail-label">Updates Timeline</div>
<div class="updates-timeline">
${incident.updates.slice().reverse().map(update => html`
<div class="update-item">
<div class="update-header">
<span class="update-status">${update.status}</span>
<span class="update-time">${formatTime(update.timestamp)}</span>
</div>
<div class="update-message">${update.message}</div>
${update.author ? html`<div class="update-author">— ${update.author}</div>` : ''}
</div>
`)}
</div>
</div>
` : ''}
</div>
` : ''}
</div>
`;
}
private getFilteredIncidents(): IIncidentDetails[] {
let result = [...this.incidents];
// Tab filter
switch (this.tabFilter) {
case 'current':
result = result.filter(i => !['resolved', 'postmortem'].includes(i.status));
break;
case 'past':
result = result.filter(i => ['resolved', 'postmortem'].includes(i.status));
break;
}
// Severity filter
if (this.severityFilter !== 'all') {
result = result.filter(i => i.severity === this.severityFilter);
}
// Sort by start time descending
result.sort((a, b) => b.startTime - a.startTime);
return result;
}
private handleSeverityFilter(e: Event) {
this.severityFilter = (e.target as HTMLSelectElement).value as TSeverity | 'all';
}
private toggleExpanded(incidentId: string) {
const newSet = new Set(this.expandedIncidents);
if (newSet.has(incidentId)) {
newSet.delete(incidentId);
} else {
newSet.add(incidentId);
}
this.expandedIncidents = newSet;
}
private handleAddClick() {
this.dispatchEvent(new CustomEvent('incidentAdd', {
bubbles: true,
composed: true
}));
}
private handleEdit(incident: IIncidentDetails) {
this.dispatchEvent(new CustomEvent('incidentEdit', {
detail: { incident },
bubbles: true,
composed: true
}));
}
private handleAddUpdate(incident: IIncidentDetails) {
this.dispatchEvent(new CustomEvent('incidentAddUpdate', {
detail: { incident },
bubbles: true,
composed: true
}));
}
}

View File

@@ -0,0 +1 @@
export * from './upladmin-incident-update.js';

View File

@@ -0,0 +1,91 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { IIncidentDetails } from '../../interfaces/index.js';
import './upladmin-incident-update.js';
export const demoFunc = () => html`
<style>
${css`
.demo-container {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px;
max-width: 800px;
margin: 0 auto;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 24px;
}
.demo-section h3 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Update Active Incident (Critical)</h3>
<upladmin-incident-update
.incident=${{
id: 'inc-1',
title: 'Database Connection Pool Exhaustion',
status: 'investigating',
severity: 'critical',
affectedServices: ['db', 'api', 'web'],
startTime: Date.now() - 30 * 60 * 1000,
impact: 'All database-dependent services are experiencing failures.',
updates: [
{ id: 'u1', timestamp: Date.now() - 30 * 60 * 1000, status: 'investigating', message: 'We are investigating reports of service failures.' },
],
} as IIncidentDetails}
></upladmin-incident-update>
</div>
<div class="demo-section">
<h3>Update Active Incident (Minor)</h3>
<upladmin-incident-update
.incident=${{
id: 'inc-2',
title: 'Elevated API Response Times',
status: 'identified',
severity: 'minor',
affectedServices: ['api'],
startTime: Date.now() - 2 * 60 * 60 * 1000,
impact: 'API responses may be slower than usual.',
updates: [
{ id: 'u2', timestamp: Date.now() - 1 * 60 * 60 * 1000, status: 'identified', message: 'Issue identified as increased traffic from a specific client.' },
{ id: 'u3', timestamp: Date.now() - 2 * 60 * 60 * 1000, status: 'investigating', message: 'We are investigating elevated response times.' },
],
} as IIncidentDetails}
></upladmin-incident-update>
</div>
<div class="demo-section">
<h3>Update Maintenance Window</h3>
<upladmin-incident-update
.incident=${{
id: 'inc-3',
title: 'Scheduled Infrastructure Upgrade',
status: 'monitoring',
severity: 'maintenance',
affectedServices: ['api', 'web', 'cdn'],
startTime: Date.now() - 45 * 60 * 1000,
impact: 'Brief interruptions may occur during the upgrade.',
updates: [
{ id: 'u4', timestamp: Date.now() - 30 * 60 * 1000, status: 'monitoring', message: 'Upgrade complete. Monitoring for any issues.' },
{ id: 'u5', timestamp: Date.now() - 45 * 60 * 1000, status: 'investigating', message: 'Starting scheduled infrastructure upgrade.' },
],
} as IIncidentDetails}
></upladmin-incident-update>
</div>
</div>
`;

View File

@@ -0,0 +1,478 @@
import * as plugins from '../../plugins.js';
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
unsafeCSS,
state,
} from '@design.estate/dees-element';
import * as sharedStyles from '../../styles/shared.styles.js';
import type { IIncidentUpdateFormData, IIncidentDetails } from '../../interfaces/index.js';
import { demoFunc } from './upladmin-incident-update.demo.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-incident-update': UpladminIncidentUpdate;
}
}
type TIncidentStatus = 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
@customElement('upladmin-incident-update')
export class UpladminIncidentUpdate extends DeesElement {
public static demo = demoFunc;
@property({ type: Object })
accessor incident: IIncidentDetails | null = null;
@property({ type: Boolean })
accessor loading: boolean = false;
@state()
accessor formData: IIncidentUpdateFormData = {
status: 'investigating',
message: '',
author: '',
};
@state()
accessor errors: Record<string, string> = {};
private statusIcons: Record<TIncidentStatus, string> = {
investigating: 'lucide:Search',
identified: 'lucide:Target',
monitoring: 'lucide:Eye',
resolved: 'lucide:CheckCircle',
postmortem: 'lucide:FileText',
};
public static styles = [
plugins.domtools.elementBasic.staticStyles,
sharedStyles.commonStyles,
css`
:host {
display: block;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
}
.update-container {
background: ${sharedStyles.colors.background.secondary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
overflow: hidden;
}
.update-header {
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
border-bottom: 1px solid ${sharedStyles.colors.border.default};
background: ${sharedStyles.colors.background.muted};
}
.update-title-row {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.update-title-row dees-icon {
--icon-color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
}
.update-title {
font-size: 18px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin: 0;
}
.incident-info {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
padding-left: 36px;
}
.incident-name {
font-size: 14px;
color: ${sharedStyles.colors.text.secondary};
}
.severity-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
font-size: 11px;
font-weight: 500;
border-radius: 9999px;
text-transform: uppercase;
}
.severity-badge dees-icon {
font-size: 12px;
}
.severity-badge.critical {
background: ${cssManager.bdTheme('rgba(239, 68, 68, 0.1)', 'rgba(239, 68, 68, 0.2)')};
color: ${sharedStyles.colors.status.majorOutage};
--icon-color: ${sharedStyles.colors.status.majorOutage};
}
.severity-badge.major {
background: ${cssManager.bdTheme('rgba(249, 115, 22, 0.1)', 'rgba(249, 115, 22, 0.2)')};
color: ${sharedStyles.colors.status.partialOutage};
--icon-color: ${sharedStyles.colors.status.partialOutage};
}
.severity-badge.minor {
background: ${cssManager.bdTheme('rgba(234, 179, 8, 0.1)', 'rgba(234, 179, 8, 0.2)')};
color: ${sharedStyles.colors.status.degraded};
--icon-color: ${sharedStyles.colors.status.degraded};
}
.severity-badge.maintenance {
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.1)', 'rgba(59, 130, 246, 0.2)')};
color: ${sharedStyles.colors.status.maintenance};
--icon-color: ${sharedStyles.colors.status.maintenance};
}
.update-body {
display: grid;
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
}
dees-form {
display: contents;
}
.status-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.status-option {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
padding: 18px 14px;
background: ${sharedStyles.colors.background.primary};
border: 2px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
text-align: center;
}
.status-option:hover {
border-color: ${sharedStyles.colors.border.strong};
background: ${sharedStyles.colors.background.muted};
}
.status-option.selected {
border-color: ${sharedStyles.colors.accent.primary};
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.05)', 'rgba(96, 165, 250, 0.1)')};
}
.status-option input {
display: none;
}
.status-option.investigating dees-icon { --icon-color: ${sharedStyles.colors.status.partialOutage}; }
.status-option.identified dees-icon { --icon-color: ${sharedStyles.colors.status.degraded}; }
.status-option.monitoring dees-icon { --icon-color: ${sharedStyles.colors.status.maintenance}; }
.status-option.resolved dees-icon { --icon-color: ${sharedStyles.colors.status.operational}; }
.status-option.postmortem dees-icon { --icon-color: #a855f7; }
.status-label {
font-size: 13px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
}
.status-desc {
font-size: 11px;
color: ${sharedStyles.colors.text.muted};
line-height: 1.3;
}
.field-label {
display: block;
font-size: 13px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
margin-bottom: ${unsafeCSS(sharedStyles.spacing.xs)};
}
.field-label.required::after {
content: ' *';
color: ${sharedStyles.colors.accent.danger};
}
.template-section {
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.template-label {
font-size: 12px;
color: ${sharedStyles.colors.text.muted};
margin-bottom: 8px;
}
.template-buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.template-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 14px;
font-size: 12px;
font-weight: 500;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
color: ${sharedStyles.colors.text.secondary};
background: ${sharedStyles.colors.background.primary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.template-btn:hover {
background: ${sharedStyles.colors.background.muted};
border-color: ${sharedStyles.colors.border.strong};
color: ${sharedStyles.colors.text.primary};
}
.template-btn dees-icon {
--icon-color: currentColor;
opacity: 0.6;
}
.update-actions {
display: flex;
justify-content: flex-end;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-top: 1px solid ${sharedStyles.colors.border.default};
background: ${sharedStyles.colors.background.muted};
}
/* Style dees-input components */
dees-input-text {
--dees-input-background: ${sharedStyles.colors.background.primary};
--dees-input-border-color: ${sharedStyles.colors.border.default};
}
`
];
async connectedCallback() {
await super.connectedCallback();
if (this.incident) {
this.formData = {
...this.formData,
status: this.incident.status,
};
}
}
updated(changedProperties: Map<string, unknown>) {
if (changedProperties.has('incident') && this.incident) {
this.formData = {
...this.formData,
status: this.incident.status,
};
}
}
public render(): TemplateResult {
if (!this.incident) {
return html`<div class="update-container">No incident selected</div>`;
}
const statusOptions: Array<{ value: TIncidentStatus; label: string; desc: string }> = [
{ value: 'investigating', label: 'Investigating', desc: 'Looking into the issue' },
{ value: 'identified', label: 'Identified', desc: 'Root cause found' },
{ value: 'monitoring', label: 'Monitoring', desc: 'Fix applied, watching' },
{ value: 'resolved', label: 'Resolved', desc: 'Issue is fixed' },
{ value: 'postmortem', label: 'Postmortem', desc: 'Analysis complete' },
];
const templates: Array<{ icon: string; label: string; message: string }> = [
{ icon: 'lucide:Search', label: 'Started investigating', message: 'We are currently investigating this issue.' },
{ icon: 'lucide:Target', label: 'Issue identified', message: 'We have identified the root cause and are working on a fix.' },
{ icon: 'lucide:Rocket', label: 'Fix deployed', message: 'A fix has been deployed. We are monitoring the results.' },
{ icon: 'lucide:CheckCircle', label: 'Resolved', message: 'This incident has been resolved. All systems are operating normally.' },
];
const severityIcons: Record<string, string> = {
critical: 'lucide:AlertCircle',
major: 'lucide:AlertTriangle',
minor: 'lucide:Info',
maintenance: 'lucide:Wrench',
};
return html`
<div class="update-container">
<div class="update-header">
<div class="update-title-row">
<dees-icon .icon=${'lucide:MessageSquarePlus'} .iconSize=${24}></dees-icon>
<h2 class="update-title">Post Update</h2>
</div>
<div class="incident-info">
<span class="severity-badge ${this.incident.severity}">
<dees-icon .icon=${severityIcons[this.incident.severity]} .iconSize=${12}></dees-icon>
${this.incident.severity}
</span>
<span class="incident-name">${this.incident.title}</span>
</div>
</div>
<div class="update-body">
<dees-form>
<div>
<label class="field-label required">Status</label>
<div class="status-grid">
${statusOptions.map(opt => html`
<label
class="status-option ${opt.value} ${this.formData.status === opt.value ? 'selected' : ''}"
@click="${() => this.handleStatusChange(opt.value)}"
>
<input
type="radio"
name="status"
value="${opt.value}"
?checked="${this.formData.status === opt.value}"
/>
<dees-icon .icon=${this.statusIcons[opt.value]} .iconSize=${24}></dees-icon>
<span class="status-label">${opt.label}</span>
<span class="status-desc">${opt.desc}</span>
</label>
`)}
</div>
</div>
<div>
<label class="field-label required">Update Message</label>
<div class="template-section">
<div class="template-label">Quick templates:</div>
<div class="template-buttons">
${templates.map(tpl => html`
<button type="button" class="template-btn" @click="${() => this.applyTemplate(tpl.message)}">
<dees-icon .icon=${tpl.icon} .iconSize=${12}></dees-icon>
${tpl.label}
</button>
`)}
</div>
</div>
<dees-input-text
key="message"
inputType="textarea"
.value="${this.formData.message}"
placeholder="Provide an update on the incident status..."
required
@changeSubject="${this.handleMessageChange}"
></dees-input-text>
</div>
<dees-input-text
key="author"
label="Author (Optional)"
.value="${this.formData.author || ''}"
placeholder="Your name or team name"
@changeSubject="${this.handleAuthorChange}"
></dees-input-text>
</dees-form>
</div>
<div class="update-actions">
<dees-button type="discreet" @click="${this.handleCancel}" ?disabled="${this.loading}">
Cancel
</dees-button>
${this.formData.status === 'resolved' ? html`
<dees-button type="highlighted" @click="${this.handlePost}" ?disabled="${this.loading}" style="--dees-button-background: ${sharedStyles.colors.status.operational}">
${this.loading ? html`<dees-spinner .size=${16}></dees-spinner>` : html`<dees-icon .icon=${'lucide:CheckCircle'} .iconSize=${16}></dees-icon>`}
Resolve Incident
</dees-button>
` : html`
<dees-button type="highlighted" @click="${this.handlePost}" ?disabled="${this.loading}">
${this.loading ? html`<dees-spinner .size=${16}></dees-spinner>` : html`<dees-icon .icon=${'lucide:Send'} .iconSize=${16}></dees-icon>`}
Post Update
</dees-button>
`}
</div>
</div>
`;
}
private handleMessageChange(e: CustomEvent) {
this.formData = { ...this.formData, message: e.detail };
if (this.errors.message) {
this.errors = { ...this.errors, message: '' };
}
}
private handleAuthorChange(e: CustomEvent) {
this.formData = { ...this.formData, author: e.detail };
}
private handleStatusChange(status: TIncidentStatus) {
this.formData = { ...this.formData, status };
}
private applyTemplate(message: string) {
this.formData = { ...this.formData, message };
}
private validate(): boolean {
const errors: Record<string, string> = {};
if (!this.formData.message?.trim()) {
errors.message = 'Update message is required';
}
this.errors = errors;
return Object.keys(errors).length === 0;
}
private handlePost() {
if (!this.validate()) {
return;
}
this.dispatchEvent(new CustomEvent('updatePost', {
detail: {
incidentId: this.incident?.id,
update: { ...this.formData }
},
bubbles: true,
composed: true
}));
}
private handleCancel() {
this.dispatchEvent(new CustomEvent('updateCancel', {
bubbles: true,
composed: true
}));
}
public reset() {
this.formData = {
status: this.incident?.status || 'investigating',
message: '',
author: '',
};
this.errors = {};
}
}

View File

@@ -0,0 +1 @@
export * from './upladmin-monitor-form.js';

View File

@@ -0,0 +1,66 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { IServiceStatus } from '../../interfaces/index.js';
import './upladmin-monitor-form.js';
export const demoFunc = () => html`
<style>
${css`
.demo-container {
display: flex;
flex-direction: column;
gap: 32px;
padding: 24px;
max-width: 900px;
margin: 0 auto;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
.demo-section {
margin-bottom: 24px;
}
.demo-section h3 {
margin: 0 0 16px 0;
font-size: 14px;
font-weight: 600;
color: ${cssManager.bdTheme('#71717a', '#a1a1aa')};
text-transform: uppercase;
letter-spacing: 0.05em;
}
`}
</style>
<div class="demo-container">
<div class="demo-section">
<h3>Create New Monitor</h3>
<upladmin-monitor-form
.availableMonitors=${[
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'database', name: 'database', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 5 },
] as IServiceStatus[]}
.categories=${['Core Services', 'Infrastructure', 'External Services', 'Web Services']}
></upladmin-monitor-form>
</div>
<div class="demo-section">
<h3>Edit Existing Monitor</h3>
<upladmin-monitor-form
.monitor=${{
id: 'cdn',
name: 'cdn',
displayName: 'Content Delivery Network',
description: 'Global CDN for static assets and media files',
category: 'Infrastructure',
dependencies: ['api-server'],
currentStatus: 'degraded',
}}
.availableMonitors=${[
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
{ id: 'database', name: 'database', displayName: 'Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.98, responseTime: 5 },
] as IServiceStatus[]}
.categories=${['Core Services', 'Infrastructure', 'External Services', 'Web Services']}
></upladmin-monitor-form>
</div>
</div>
`;

View File

@@ -0,0 +1,431 @@
import * as plugins from '../../plugins.js';
import {
DeesElement,
property,
html,
customElement,
type TemplateResult,
css,
cssManager,
unsafeCSS,
state,
} from '@design.estate/dees-element';
import * as sharedStyles from '../../styles/shared.styles.js';
import type { IMonitorFormData, IServiceStatus } from '../../interfaces/index.js';
import { demoFunc } from './upladmin-monitor-form.demo.js';
declare global {
interface HTMLElementTagNameMap {
'upladmin-monitor-form': UpladminMonitorForm;
}
}
type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance';
@customElement('upladmin-monitor-form')
export class UpladminMonitorForm extends DeesElement {
public static demo = demoFunc;
@property({ type: Object })
accessor monitor: IMonitorFormData | null = null;
@property({ type: Array })
accessor availableMonitors: IServiceStatus[] = [];
@property({ type: Array })
accessor categories: string[] = [];
@property({ type: Boolean })
accessor loading: boolean = false;
@state()
accessor formData: IMonitorFormData = {
name: '',
displayName: '',
description: '',
category: '',
dependencies: [],
currentStatus: 'operational',
};
@state()
accessor errors: Record<string, string> = {};
private statusIcons: Record<TStatusType, string> = {
operational: 'lucide:CheckCircle',
degraded: 'lucide:AlertTriangle',
partial_outage: 'lucide:AlertOctagon',
major_outage: 'lucide:XCircle',
maintenance: 'lucide:Wrench',
};
public static styles = [
plugins.domtools.elementBasic.staticStyles,
sharedStyles.commonStyles,
css`
:host {
display: block;
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
}
.form-container {
background: ${sharedStyles.colors.background.secondary};
border: 1px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
overflow: hidden;
}
.form-header {
display: flex;
align-items: center;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
border-bottom: 1px solid ${sharedStyles.colors.border.default};
background: ${sharedStyles.colors.background.muted};
}
.form-header dees-icon {
--icon-color: ${cssManager.bdTheme('#3b82f6', '#60a5fa')};
}
.form-title-wrapper {
flex: 1;
}
.form-title {
font-size: 18px;
font-weight: 600;
color: ${sharedStyles.colors.text.primary};
margin: 0;
}
.form-subtitle {
font-size: 13px;
color: ${sharedStyles.colors.text.muted};
margin-top: 4px;
}
.form-body {
display: grid;
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
padding: ${unsafeCSS(sharedStyles.spacing.lg)};
}
dees-form {
display: contents;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: ${unsafeCSS(sharedStyles.spacing.md)};
}
@media (max-width: 600px) {
.form-row {
grid-template-columns: 1fr;
}
}
.form-actions {
display: flex;
justify-content: flex-end;
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
padding: ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.lg)};
border-top: 1px solid ${sharedStyles.colors.border.default};
background: ${sharedStyles.colors.background.muted};
}
.status-section {
margin-top: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.status-options {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.status-option {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
background: ${sharedStyles.colors.background.primary};
border: 2px solid ${sharedStyles.colors.border.default};
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
cursor: pointer;
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
}
.status-option:hover {
border-color: ${sharedStyles.colors.border.strong};
background: ${sharedStyles.colors.background.muted};
}
.status-option.selected {
border-color: ${sharedStyles.colors.accent.primary};
background: ${cssManager.bdTheme('rgba(59, 130, 246, 0.05)', 'rgba(96, 165, 250, 0.1)')};
}
.status-option input {
display: none;
}
.status-option dees-icon {
flex-shrink: 0;
}
.status-option.operational dees-icon { --icon-color: ${sharedStyles.colors.status.operational}; }
.status-option.degraded dees-icon { --icon-color: ${sharedStyles.colors.status.degraded}; }
.status-option.partial_outage dees-icon { --icon-color: ${sharedStyles.colors.status.partialOutage}; }
.status-option.major_outage dees-icon { --icon-color: ${sharedStyles.colors.status.majorOutage}; }
.status-option.maintenance dees-icon { --icon-color: ${sharedStyles.colors.status.maintenance}; }
.status-label {
font-size: 14px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
}
.field-label {
display: block;
font-size: 13px;
font-weight: 500;
color: ${sharedStyles.colors.text.primary};
margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
}
.field-label.required::after {
content: ' *';
color: ${sharedStyles.colors.accent.danger};
}
/* Style dees-input components */
dees-input-text,
dees-input-dropdown {
--dees-input-background: ${sharedStyles.colors.background.primary};
--dees-input-border-color: ${sharedStyles.colors.border.default};
}
`
];
async connectedCallback() {
await super.connectedCallback();
if (this.monitor) {
this.formData = { ...this.monitor };
}
}
updated(changedProperties: Map<string, unknown>) {
if (changedProperties.has('monitor') && this.monitor) {
this.formData = { ...this.monitor };
}
}
public render(): TemplateResult {
const isEdit = !!this.monitor?.id;
const statusOptions: Array<{ value: TStatusType; label: string }> = [
{ value: 'operational', label: 'Operational' },
{ value: 'degraded', label: 'Degraded' },
{ value: 'partial_outage', label: 'Partial Outage' },
{ value: 'major_outage', label: 'Major Outage' },
{ value: 'maintenance', label: 'Maintenance' },
];
const categoryOptions = this.categories.map(cat => ({ key: cat, option: cat, payload: null }));
const dependencyOptions = this.availableMonitors
.filter(m => m.id !== this.monitor?.id)
.map(m => ({ key: m.id, option: m.displayName || m.name, payload: null }));
return html`
<div class="form-container">
<div class="form-header">
<dees-icon .icon=${isEdit ? 'lucide:Pencil' : 'lucide:Plus'} .iconSize=${24}></dees-icon>
<div class="form-title-wrapper">
<h2 class="form-title">${isEdit ? 'Edit Monitor' : 'Create Monitor'}</h2>
<p class="form-subtitle">
${isEdit ? 'Update the monitor configuration' : 'Add a new service to monitor'}
</p>
</div>
</div>
<div class="form-body">
<dees-form>
<div class="form-row">
<dees-input-text
key="name"
label="Internal Name"
.value="${this.formData.name}"
placeholder="api-server"
required
description="Lowercase, no spaces. Used as identifier."
@changeSubject="${this.handleNameChange}"
></dees-input-text>
<dees-input-text
key="displayName"
label="Display Name"
.value="${this.formData.displayName}"
placeholder="API Server"
required
description="Human-readable name shown to users."
@changeSubject="${this.handleDisplayNameChange}"
></dees-input-text>
</div>
<dees-input-text
key="description"
label="Description"
inputType="textarea"
.value="${this.formData.description || ''}"
placeholder="Brief description of what this service does..."
@changeSubject="${this.handleDescriptionChange}"
></dees-input-text>
<div class="form-row">
<dees-input-dropdown
key="category"
label="Category"
.options="${categoryOptions}"
.selectedOption="${this.formData.category || ''}"
placeholder="Select category..."
@selectedOption="${this.handleCategoryChange}"
></dees-input-dropdown>
<dees-input-dropdown
key="dependencies"
label="Dependencies"
.options="${dependencyOptions}"
.selectedOptions="${this.formData.dependencies || []}"
multiple
description="Services this monitor depends on."
@selectedOption="${this.handleDependenciesChange}"
></dees-input-dropdown>
</div>
<div class="status-section">
<label class="field-label required">Current Status</label>
<div class="status-options">
${statusOptions.map(opt => html`
<label
class="status-option ${opt.value} ${this.formData.currentStatus === opt.value ? 'selected' : ''}"
@click="${() => this.handleStatusChange(opt.value)}"
>
<input
type="radio"
name="currentStatus"
value="${opt.value}"
?checked="${this.formData.currentStatus === opt.value}"
/>
<dees-icon .icon=${this.statusIcons[opt.value]} .iconSize=${20}></dees-icon>
<span class="status-label">${opt.label}</span>
</label>
`)}
</div>
</div>
</dees-form>
</div>
<div class="form-actions">
<dees-button type="discreet" @click="${this.handleCancel}" ?disabled="${this.loading}">
Cancel
</dees-button>
<dees-button type="highlighted" @click="${this.handleSave}" ?disabled="${this.loading}">
${this.loading ? html`<dees-spinner .size=${16}></dees-spinner>` : ''}
${isEdit ? 'Update Monitor' : 'Create Monitor'}
</dees-button>
</div>
</div>
`;
}
private handleNameChange(e: CustomEvent) {
this.formData = { ...this.formData, name: e.detail };
if (this.errors.name) {
this.errors = { ...this.errors, name: '' };
}
}
private handleDisplayNameChange(e: CustomEvent) {
this.formData = { ...this.formData, displayName: e.detail };
if (this.errors.displayName) {
this.errors = { ...this.errors, displayName: '' };
}
}
private handleDescriptionChange(e: CustomEvent) {
this.formData = { ...this.formData, description: e.detail };
}
private handleCategoryChange(e: CustomEvent) {
this.formData = { ...this.formData, category: e.detail };
}
private handleDependenciesChange(e: CustomEvent) {
const selected = e.detail;
if (Array.isArray(selected)) {
this.formData = { ...this.formData, dependencies: selected };
} else if (selected) {
// Single selection mode, convert to array
this.formData = { ...this.formData, dependencies: [selected] };
}
}
private handleStatusChange(status: TStatusType) {
this.formData = { ...this.formData, currentStatus: status };
}
private validate(): boolean {
const errors: Record<string, string> = {};
if (!this.formData.name?.trim()) {
errors.name = 'Name is required';
} else if (!/^[a-z0-9-]+$/.test(this.formData.name)) {
errors.name = 'Name must be lowercase letters, numbers, and hyphens only';
}
if (!this.formData.displayName?.trim()) {
errors.displayName = 'Display name is required';
}
this.errors = errors;
return Object.keys(errors).length === 0;
}
private handleSave() {
if (!this.validate()) {
return;
}
this.dispatchEvent(new CustomEvent('monitorSave', {
detail: { monitor: { ...this.formData } },
bubbles: true,
composed: true
}));
}
private handleCancel() {
this.dispatchEvent(new CustomEvent('monitorCancel', {
bubbles: true,
composed: true
}));
}
public reset() {
this.formData = {
name: '',
displayName: '',
description: '',
category: '',
dependencies: [],
currentStatus: 'operational',
};
this.errors = {};
}
public setMonitor(monitor: IMonitorFormData) {
this.formData = { ...monitor };
this.errors = {};
}
}

View File

@@ -0,0 +1 @@
export * from './upladmin-monitor-list.js';

View File

@@ -0,0 +1,118 @@
import { html, css, cssManager } from '@design.estate/dees-element';
import type { IServiceStatus } from '../../interfaces/index.js';
import './upladmin-monitor-list.js';
export const demoFunc = () => html`
<style>
${css`
.demo-container {
padding: 24px;
background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
min-height: 100vh;
}
`}
</style>
<div class="demo-container">
<upladmin-monitor-list
.monitors=${[
{
id: 'api-server',
name: 'api-server',
displayName: 'API Server',
description: 'Main REST API endpoint',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.98,
uptime90d: 99.95,
responseTime: 45,
category: 'Core Services',
},
{
id: 'web-app',
name: 'web-app',
displayName: 'Web Application',
description: 'Customer-facing web application',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.99,
uptime90d: 99.97,
responseTime: 120,
category: 'Core Services',
},
{
id: 'database-primary',
name: 'database-primary',
displayName: 'Primary Database',
description: 'PostgreSQL primary node',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.999,
uptime90d: 99.998,
responseTime: 5,
category: 'Infrastructure',
},
{
id: 'cdn',
name: 'cdn',
displayName: 'Content Delivery Network',
description: 'Global CDN for static assets',
currentStatus: 'degraded',
lastChecked: Date.now(),
uptime30d: 99.5,
uptime90d: 99.8,
responseTime: 200,
category: 'Infrastructure',
},
{
id: 'redis-cache',
name: 'redis-cache',
displayName: 'Redis Cache',
description: 'In-memory caching layer',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.99,
uptime90d: 99.98,
responseTime: 2,
category: 'Infrastructure',
},
{
id: 'email-service',
name: 'email-service',
displayName: 'Email Service',
description: 'Transactional email delivery',
currentStatus: 'operational',
lastChecked: Date.now(),
uptime30d: 99.9,
uptime90d: 99.85,
responseTime: 500,
category: 'External Services',
},
{
id: 'payment-gateway',
name: 'payment-gateway',
displayName: 'Payment Gateway',
description: 'Payment processing integration',
currentStatus: 'maintenance',
lastChecked: Date.now(),
uptime30d: 99.95,
uptime90d: 99.9,
responseTime: 350,
category: 'External Services',
},
{
id: 'search-engine',
name: 'search-engine',
displayName: 'Search Engine',
description: 'Elasticsearch cluster',
currentStatus: 'partial_outage',
lastChecked: Date.now(),
uptime30d: 98.5,
uptime90d: 99.2,
responseTime: 150,
category: 'Core Services',
},
] as IServiceStatus[]}
></upladmin-monitor-list>
</div>
`;

Some files were not shown because too many files have changed in this diff Show More