feat(admin-ui): introduce view layer and refactor admin UI to use view components, consolidate demos, and update interfaces
This commit is contained in:
10
changelog.md
10
changelog.md
@@ -1,5 +1,15 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-12-27 - 1.3.0 - feat(admin-ui)
|
||||||
|
introduce view layer and refactor admin UI to use view components, consolidate demos, and update interfaces
|
||||||
|
|
||||||
|
- Added a new views/ layer with view components: upladmin-dashboard-view, upladmin-monitors-view, upladmin-incidents-view, upladmin-config-view and exported them from ts_web/views/index.ts
|
||||||
|
- Refactored upladmin-app to use the new view components (updated menu routes/content to view tags)
|
||||||
|
- Removed multiple demo page files under ts_web/pages (consolidated demo surface into the view-based app)
|
||||||
|
- Updated upladmin-statuspage-config: changed activeSection to a property, removed the side navigation markup/CSS and simplified layout to be view-driven
|
||||||
|
- Reworked ts_web/interfaces to re-export core types from @uptime.link/interfaces and added UI-specific interfaces and form types
|
||||||
|
- Bumped dependency @uptime.link/interfaces to ^2.1.0 in package.json
|
||||||
|
|
||||||
## 2025-12-26 - 1.2.0 - feat(elements)
|
## 2025-12-26 - 1.2.0 - feat(elements)
|
||||||
add upladmin-option-card component and migrate option/status UIs to use it; refactor monitor form multitoggle subscriptions and event handling; improve theme color handling and dark-mode styles; add demos, Playwright snapshots, and migration plan
|
add upladmin-option-card component and migrate option/status UIs to use it; refactor monitor form multitoggle subscriptions and event handling; improve theme color handling and dark-mode styles; add demos, Playwright snapshots, and migration plan
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"@design.estate/dees-domtools": "^2.3.6",
|
"@design.estate/dees-domtools": "^2.3.6",
|
||||||
"@design.estate/dees-element": "^2.1.3",
|
"@design.estate/dees-element": "^2.1.3",
|
||||||
"@design.estate/dees-wcctools": "^3.2.0",
|
"@design.estate/dees-wcctools": "^3.2.0",
|
||||||
"@uptime.link/interfaces": "^2.0.21"
|
"@uptime.link/interfaces": "^2.1.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^4.0.2",
|
"@git.zone/tsbuild": "^4.0.2",
|
||||||
|
|||||||
19
pnpm-lock.yaml
generated
19
pnpm-lock.yaml
generated
@@ -21,8 +21,8 @@ importers:
|
|||||||
specifier: ^3.2.0
|
specifier: ^3.2.0
|
||||||
version: 3.2.0
|
version: 3.2.0
|
||||||
'@uptime.link/interfaces':
|
'@uptime.link/interfaces':
|
||||||
specifier: ^2.0.21
|
specifier: ^2.1.0
|
||||||
version: 2.0.21
|
version: 2.1.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@git.zone/tsbuild':
|
'@git.zone/tsbuild':
|
||||||
specifier: ^4.0.2
|
specifier: ^4.0.2
|
||||||
@@ -65,9 +65,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@push.rocks/smartserve': '>=1.1.0'
|
'@push.rocks/smartserve': '>=1.1.0'
|
||||||
|
|
||||||
'@apiglobal/typedrequest-interfaces@2.0.1':
|
|
||||||
resolution: {integrity: sha512-Oi7pNU4vKo5UvcCJmqkH43Us237Ws/Pp/WDYnwnonRnTmIMd+6QjNfN/gXcPnP6tbamk8r8Xzcz9mgnSDM2ysw==}
|
|
||||||
|
|
||||||
'@aws-crypto/crc32@5.2.0':
|
'@aws-crypto/crc32@5.2.0':
|
||||||
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
|
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
@@ -1512,8 +1509,8 @@ packages:
|
|||||||
'@ungap/structured-clone@1.3.0':
|
'@ungap/structured-clone@1.3.0':
|
||||||
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
||||||
|
|
||||||
'@uptime.link/interfaces@2.0.21':
|
'@uptime.link/interfaces@2.1.0':
|
||||||
resolution: {integrity: sha512-sy7WBzHOxU7Kt0BGofK0R3CS8D8QtbTXB00i75QKYXkEesdLd91SbFL80wTupKfjzeldE0ejUVSgvtlZEr8XlQ==}
|
resolution: {integrity: sha512-KlsAUp4Vvb4TeFNBDq4MZ9v4RaeLPr1GRlj4mkNmGLSfEiSasj1FWGLEj8iRdfeUuxMu0WRpAvWe2Ol0MRzoqg==}
|
||||||
|
|
||||||
'@webcontainer/api@1.2.0':
|
'@webcontainer/api@1.2.0':
|
||||||
resolution: {integrity: sha512-tzoKBd4lLdhHy5GHFpUkl+ndoSba8JqmB7x0ZQFnWfjbcbQOvKQfxA8MEMUYhgqjWHnbrWdAfnBEHz5f5lYG5A==}
|
resolution: {integrity: sha512-tzoKBd4lLdhHy5GHFpUkl+ndoSba8JqmB7x0ZQFnWfjbcbQOvKQfxA8MEMUYhgqjWHnbrWdAfnBEHz5f5lYG5A==}
|
||||||
@@ -2976,8 +2973,6 @@ snapshots:
|
|||||||
'@push.rocks/smartstring': 4.1.0
|
'@push.rocks/smartstring': 4.1.0
|
||||||
'@push.rocks/smarturl': 3.1.0
|
'@push.rocks/smarturl': 3.1.0
|
||||||
|
|
||||||
'@apiglobal/typedrequest-interfaces@2.0.1': {}
|
|
||||||
|
|
||||||
'@aws-crypto/crc32@5.2.0':
|
'@aws-crypto/crc32@5.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@aws-crypto/util': 5.2.0
|
'@aws-crypto/util': 5.2.0
|
||||||
@@ -5254,10 +5249,10 @@ snapshots:
|
|||||||
|
|
||||||
'@ungap/structured-clone@1.3.0': {}
|
'@ungap/structured-clone@1.3.0': {}
|
||||||
|
|
||||||
'@uptime.link/interfaces@2.0.21':
|
'@uptime.link/interfaces@2.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@apiglobal/typedrequest-interfaces': 2.0.1
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
'@tsclass/tsclass': 4.4.4
|
'@tsclass/tsclass': 9.3.0
|
||||||
|
|
||||||
'@webcontainer/api@1.2.0': {}
|
'@webcontainer/api@1.2.0': {}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@uptime.link/statuspage-admin',
|
name: '@uptime.link/statuspage-admin',
|
||||||
version: '1.2.0',
|
version: '1.3.0',
|
||||||
description: 'Admin components for managing UptimeLink status pages, monitors, and incidents.'
|
description: 'Admin components for managing UptimeLink status pages, monitors, and incidents.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class UpladminStatuspageConfig extends DeesElement {
|
|||||||
@state()
|
@state()
|
||||||
accessor formData: IStatusPageConfig = {};
|
accessor formData: IStatusPageConfig = {};
|
||||||
|
|
||||||
@state()
|
@property({ type: String })
|
||||||
accessor activeSection: string = 'branding';
|
accessor activeSection: string = 'branding';
|
||||||
|
|
||||||
@state()
|
@state()
|
||||||
@@ -49,67 +49,10 @@ export class UpladminStatuspageConfig extends DeesElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.config-container {
|
.config-container {
|
||||||
display: grid;
|
display: block;
|
||||||
grid-template-columns: 220px 1fr;
|
|
||||||
gap: ${unsafeCSS(sharedStyles.spacing.lg)};
|
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.config-container {
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-nav {
|
|
||||||
background: ${sharedStyles.colors.background.secondary};
|
|
||||||
border: 1px solid ${sharedStyles.colors.border.default};
|
|
||||||
border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
|
|
||||||
padding: ${unsafeCSS(sharedStyles.spacing.sm)};
|
|
||||||
height: fit-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 14px 16px;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 500;
|
|
||||||
font-family: ${unsafeCSS(sharedStyles.fonts.base)};
|
|
||||||
color: ${sharedStyles.colors.text.secondary};
|
|
||||||
background: transparent;
|
|
||||||
border: none;
|
|
||||||
border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
|
|
||||||
cursor: pointer;
|
|
||||||
text-align: left;
|
|
||||||
transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:hover {
|
|
||||||
background: ${sharedStyles.colors.background.muted};
|
|
||||||
color: ${sharedStyles.colors.text.primary};
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item.active {
|
|
||||||
background: ${sharedStyles.colors.accent.primary};
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item.active dees-icon {
|
|
||||||
--icon-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item dees-icon {
|
|
||||||
--icon-color: ${sharedStyles.colors.text.muted};
|
|
||||||
transition: color ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-item:hover dees-icon {
|
|
||||||
--icon-color: ${sharedStyles.colors.text.primary};
|
|
||||||
}
|
|
||||||
|
|
||||||
.config-content {
|
.config-content {
|
||||||
background: ${sharedStyles.colors.background.secondary};
|
background: ${sharedStyles.colors.background.secondary};
|
||||||
border: 1px solid ${sharedStyles.colors.border.default};
|
border: 1px solid ${sharedStyles.colors.border.default};
|
||||||
@@ -343,18 +286,6 @@ export class UpladminStatuspageConfig extends DeesElement {
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="config-container">
|
<div class="config-container">
|
||||||
<nav class="config-nav">
|
|
||||||
${sections.map(section => html`
|
|
||||||
<button
|
|
||||||
class="nav-item ${this.activeSection === section.id ? 'active' : ''}"
|
|
||||||
@click="${() => this.activeSection = section.id}"
|
|
||||||
>
|
|
||||||
<dees-icon .icon=${section.icon} .iconSize=${18}></dees-icon>
|
|
||||||
<span>${section.label}</span>
|
|
||||||
</button>
|
|
||||||
`)}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="config-content">
|
<div class="config-content">
|
||||||
<div class="content-header">
|
<div class="content-header">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from './elements/index.js';
|
export * from './elements/index.js';
|
||||||
|
export * from './views/index.js';
|
||||||
export * from './pages/index.js';
|
export * from './pages/index.js';
|
||||||
export * from './interfaces/index.js';
|
export * from './interfaces/index.js';
|
||||||
export * from './services/index.js';
|
export * from './services/index.js';
|
||||||
|
|||||||
@@ -1,19 +1,63 @@
|
|||||||
// Re-export interfaces from the public catalog for consistency
|
// ============================================
|
||||||
|
// Re-export shared types from @uptime.link/interfaces
|
||||||
|
// ============================================
|
||||||
|
|
||||||
// Status types
|
import { data } from '@uptime.link/interfaces';
|
||||||
export type TStatusType = 'operational' | 'degraded' | 'partial_outage' | 'major_outage' | 'maintenance' | 'initializing' | 'error' | 'paused';
|
|
||||||
export type TCheckType = 'assumption' | 'function' | 'pwa' | 'pagerank';
|
|
||||||
export type TStatusMode = 'auto' | 'manual';
|
|
||||||
|
|
||||||
// Check configuration interface
|
// Re-export core types
|
||||||
|
export type TStatusType = data.TStatusType;
|
||||||
|
export type TCheckType = data.TCheckType;
|
||||||
|
export type TStatusMode = data.TStatusMode;
|
||||||
|
export type TIncidentSeverity = data.TIncidentSeverity;
|
||||||
|
export type TIncidentStatus = data.TIncidentStatus;
|
||||||
|
export type TCheckResultStatus = data.TCheckResultStatus;
|
||||||
|
export type TCheckLastResult = data.TCheckLastResult;
|
||||||
|
|
||||||
|
// Re-export check configuration interfaces
|
||||||
|
export type ICheckBase = data.ICheckBase;
|
||||||
|
export type IAssumptionCheckConfig = data.IAssumptionCheckConfig;
|
||||||
|
export type IFunctionCheckConfig = data.IFunctionCheckConfig;
|
||||||
|
export type IPwaCheckConfig = data.IPwaCheckConfig;
|
||||||
|
export type IPageRankCheckConfig = data.IPageRankCheckConfig;
|
||||||
|
export type TCheckConfig = data.TCheckConfig;
|
||||||
|
|
||||||
|
// Re-export check execution interfaces
|
||||||
|
export type IAssumptionCheck = data.IAssumptionCheck;
|
||||||
|
export type IFunctionCheck = data.IFunctionCheck;
|
||||||
|
export type IPwaCheck = data.IPwaCheck;
|
||||||
|
export type IPageRankCheck = data.IPageRankCheck;
|
||||||
|
export type ICheckCollection = data.ICheckCollection;
|
||||||
|
|
||||||
|
// Re-export incident interfaces
|
||||||
|
export type IIncident = data.IIncident;
|
||||||
|
export type IIncidentUpdateBase = data.IIncidentUpdate;
|
||||||
|
|
||||||
|
// Re-export status page config
|
||||||
|
export type IServiceGroup = data.IServiceGroup;
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Extended/UI-specific Interfaces
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flat check configuration for forms.
|
||||||
|
* Maps to TCheckConfig discriminated union when saving.
|
||||||
|
*/
|
||||||
export interface ICheckConfig {
|
export interface ICheckConfig {
|
||||||
domain: string;
|
domain: string;
|
||||||
// Assumption check fields
|
// Assumption check fields
|
||||||
expectedTitle?: string;
|
expectedTitle?: string;
|
||||||
expectedStatusCode?: string;
|
expectedStatusCode?: string;
|
||||||
expectedDescription?: string;
|
expectedDescription?: string;
|
||||||
|
assumedStatus?: TStatusType;
|
||||||
// Function check fields
|
// Function check fields
|
||||||
functionDef?: string;
|
functionDef?: string;
|
||||||
|
functionUrl?: string;
|
||||||
|
expectedStatusCodeNum?: number;
|
||||||
|
timeoutMs?: number;
|
||||||
|
// PWA check fields
|
||||||
|
targetUrl?: string;
|
||||||
|
lighthouseThreshold?: number;
|
||||||
// PageRank check fields
|
// PageRank check fields
|
||||||
searchTerm?: string;
|
searchTerm?: string;
|
||||||
checkBing?: boolean;
|
checkBing?: boolean;
|
||||||
@@ -22,6 +66,10 @@ export interface ICheckConfig {
|
|||||||
googleMinRank?: number;
|
googleMinRank?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service status for display and management.
|
||||||
|
* Extended with UI-specific fields.
|
||||||
|
*/
|
||||||
export interface IServiceStatus {
|
export interface IServiceStatus {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -45,6 +93,9 @@ export interface IServiceStatus {
|
|||||||
intervalMs?: number;
|
intervalMs?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status history point.
|
||||||
|
*/
|
||||||
export interface IStatusHistoryPoint {
|
export interface IStatusHistoryPoint {
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
status: TStatusType;
|
status: TStatusType;
|
||||||
@@ -52,28 +103,9 @@ export interface IStatusHistoryPoint {
|
|||||||
errorRate?: number;
|
errorRate?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IIncidentUpdate {
|
/**
|
||||||
id: string;
|
* Overall status with service counts for dashboard.
|
||||||
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 {
|
export interface IOverallStatus {
|
||||||
status: TStatusType;
|
status: TStatusType;
|
||||||
message: string;
|
message: string;
|
||||||
@@ -82,6 +114,9 @@ export interface IOverallStatus {
|
|||||||
totalServices: number;
|
totalServices: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status page configuration.
|
||||||
|
*/
|
||||||
export interface IStatusPageConfig {
|
export interface IStatusPageConfig {
|
||||||
apiEndpoint?: string;
|
apiEndpoint?: string;
|
||||||
refreshInterval?: number;
|
refreshInterval?: number;
|
||||||
@@ -100,7 +135,41 @@ export interface IStatusPageConfig {
|
|||||||
legalUrl?: string;
|
legalUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin-specific interfaces
|
/**
|
||||||
|
* Incident update entry (UI version with timestamp).
|
||||||
|
*/
|
||||||
|
export interface IIncidentUpdate {
|
||||||
|
id: string;
|
||||||
|
timestamp: number;
|
||||||
|
status: TIncidentStatus;
|
||||||
|
message: string;
|
||||||
|
author?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incident details for display.
|
||||||
|
*/
|
||||||
|
export interface IIncidentDetails {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
status: TIncidentStatus;
|
||||||
|
severity: TIncidentSeverity;
|
||||||
|
affectedServices: string[];
|
||||||
|
startTime: number;
|
||||||
|
endTime?: number;
|
||||||
|
updates: IIncidentUpdate[];
|
||||||
|
impact: string;
|
||||||
|
rootCause?: string;
|
||||||
|
resolution?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// Form Interfaces (UI-specific)
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitor form data for creating/editing monitors.
|
||||||
|
*/
|
||||||
export interface IMonitorFormData {
|
export interface IMonitorFormData {
|
||||||
id?: string;
|
id?: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -118,19 +187,25 @@ export interface IMonitorFormData {
|
|||||||
intervalMs: number;
|
intervalMs: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incident form data for creating/editing incidents.
|
||||||
|
*/
|
||||||
export interface IIncidentFormData {
|
export interface IIncidentFormData {
|
||||||
id?: string;
|
id?: string;
|
||||||
title: string;
|
title: string;
|
||||||
severity: 'critical' | 'major' | 'minor' | 'maintenance';
|
severity: TIncidentSeverity;
|
||||||
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
|
status: TIncidentStatus;
|
||||||
affectedServices: string[];
|
affectedServices: string[];
|
||||||
impact: string;
|
impact: string;
|
||||||
rootCause?: string;
|
rootCause?: string;
|
||||||
resolution?: string;
|
resolution?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incident update form data.
|
||||||
|
*/
|
||||||
export interface IIncidentUpdateFormData {
|
export interface IIncidentUpdateFormData {
|
||||||
status: 'investigating' | 'identified' | 'monitoring' | 'resolved' | 'postmortem';
|
status: TIncidentStatus;
|
||||||
message: string;
|
message: string;
|
||||||
author?: string;
|
author?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
import { html, cssManager } from "@design.estate/dees-element";
|
|
||||||
import type { IStatusPageConfig } from '../interfaces/index.js';
|
|
||||||
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: any) => {
|
|
||||||
const config = wrapperElement.querySelector('upladmin-statuspage-config') as any;
|
|
||||||
|
|
||||||
const configData: IStatusPageConfig = {
|
|
||||||
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: any) => {
|
|
||||||
const config = wrapperElement.querySelector('upladmin-statuspage-config') as any;
|
|
||||||
|
|
||||||
const configData: IStatusPageConfig = {
|
|
||||||
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>
|
|
||||||
`;
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
import { html, cssManager } from "@design.estate/dees-element";
|
|
||||||
import type { IServiceStatus, IIncidentDetails } from '../interfaces/index.js';
|
|
||||||
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: any) => {
|
|
||||||
const dashboard = wrapperElement.querySelector('upladmin-dashboard') as any;
|
|
||||||
|
|
||||||
// Demo monitors
|
|
||||||
const monitors: IServiceStatus[] = [
|
|
||||||
{
|
|
||||||
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: IIncidentDetails[] = [
|
|
||||||
{
|
|
||||||
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>
|
|
||||||
`;
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
import { html, cssManager } from "@design.estate/dees-element";
|
|
||||||
import type { IServiceStatus, IIncidentDetails } from '../interfaces/index.js';
|
|
||||||
import '../elements/index.js';
|
|
||||||
|
|
||||||
export const adminpageIncidents = () => 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: any) => {
|
|
||||||
const incidentList = wrapperElement.querySelector('upladmin-incident-list') as any;
|
|
||||||
|
|
||||||
const incidents: IIncidentDetails[] = [
|
|
||||||
{
|
|
||||||
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' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'inc-003',
|
|
||||||
title: 'Search Engine Partial Outage',
|
|
||||||
status: 'identified',
|
|
||||||
severity: 'major',
|
|
||||||
affectedServices: ['search-engine', 'api-server'],
|
|
||||||
startTime: Date.now() - 45 * 60 * 1000,
|
|
||||||
impact: 'Search functionality is degraded. Some queries may timeout or return incomplete results.',
|
|
||||||
updates: [
|
|
||||||
{ id: 'upd-005', timestamp: Date.now() - 45 * 60 * 1000, status: 'investigating', message: 'We are aware of issues with search functionality.', author: 'Engineering Team' },
|
|
||||||
{ id: 'upd-006', timestamp: Date.now() - 20 * 60 * 1000, status: 'identified', message: 'Root cause identified: disk space exhaustion on search cluster nodes.', author: 'Engineering Team' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'inc-004',
|
|
||||||
title: 'API Server Outage',
|
|
||||||
status: 'resolved',
|
|
||||||
severity: 'critical',
|
|
||||||
affectedServices: ['api-server', 'web-app'],
|
|
||||||
startTime: Date.now() - 24 * 60 * 60 * 1000,
|
|
||||||
endTime: Date.now() - 23 * 60 * 60 * 1000,
|
|
||||||
impact: 'Complete service unavailability for all API-dependent services.',
|
|
||||||
rootCause: 'Database connection pool exhaustion due to a query performance regression.',
|
|
||||||
resolution: 'Rolled back recent deployment and optimized database queries.',
|
|
||||||
updates: [
|
|
||||||
{ id: 'upd-007', timestamp: Date.now() - 24 * 60 * 60 * 1000, status: 'investigating', message: 'We are aware of service unavailability and actively investigating.', author: 'Platform Team' },
|
|
||||||
{ id: 'upd-008', timestamp: Date.now() - 23.5 * 60 * 60 * 1000, status: 'identified', message: 'Root cause identified as database connection pool exhaustion.', author: 'Platform Team' },
|
|
||||||
{ id: 'upd-009', timestamp: Date.now() - 23 * 60 * 60 * 1000, status: 'resolved', message: 'Service has been restored. All systems operational.', author: 'Platform Team' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'inc-005',
|
|
||||||
title: 'Email Delivery Delays',
|
|
||||||
status: 'resolved',
|
|
||||||
severity: 'minor',
|
|
||||||
affectedServices: ['email-service'],
|
|
||||||
startTime: Date.now() - 48 * 60 * 60 * 1000,
|
|
||||||
endTime: Date.now() - 46 * 60 * 60 * 1000,
|
|
||||||
impact: 'Email notifications may be delayed by up to 30 minutes.',
|
|
||||||
rootCause: 'Third-party email provider experiencing capacity issues.',
|
|
||||||
resolution: 'Provider resolved their capacity issues.',
|
|
||||||
updates: [
|
|
||||||
{ id: 'upd-010', timestamp: Date.now() - 48 * 60 * 60 * 1000, status: 'investigating', message: 'Investigating reports of delayed email delivery.', author: 'Support Team' },
|
|
||||||
{ id: 'upd-011', timestamp: Date.now() - 46 * 60 * 60 * 1000, status: 'resolved', message: 'Email delivery has returned to normal.', author: 'Support Team' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
incidentList.incidents = incidents;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<upladmin-incident-list></upladmin-incident-list>
|
|
||||||
</dees-demowrapper>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const adminpageIncidentForm = () => 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: any) => {
|
|
||||||
const incidentForm = wrapperElement.querySelector('upladmin-incident-form') as any;
|
|
||||||
|
|
||||||
const services: IServiceStatus[] = [
|
|
||||||
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
|
|
||||||
{ id: 'web-app', name: 'web-app', displayName: 'Web Application', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.99, uptime90d: 99.97, responseTime: 120 },
|
|
||||||
{ id: 'database-primary', name: 'database-primary', displayName: 'Primary Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
|
|
||||||
{ id: 'cdn', name: 'cdn', displayName: 'Content Delivery Network', currentStatus: 'degraded', lastChecked: Date.now(), uptime30d: 99.5, uptime90d: 99.8, responseTime: 200 },
|
|
||||||
{ id: 'email-service', name: 'email-service', displayName: 'Email Service', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.9, uptime90d: 99.85, responseTime: 500 },
|
|
||||||
{ id: 'payment-gateway', name: 'payment-gateway', displayName: 'Payment Gateway', currentStatus: 'maintenance', lastChecked: Date.now(), uptime30d: 99.95, uptime90d: 99.9, responseTime: 350 },
|
|
||||||
];
|
|
||||||
|
|
||||||
incidentForm.availableServices = services;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<upladmin-incident-form></upladmin-incident-form>
|
|
||||||
</dees-demowrapper>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const adminpageIncidentUpdate = () => 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: any) => {
|
|
||||||
const incidentUpdate = wrapperElement.querySelector('upladmin-incident-update') as any;
|
|
||||||
|
|
||||||
incidentUpdate.incident = {
|
|
||||||
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' },
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<upladmin-incident-update></upladmin-incident-update>
|
|
||||||
</dees-demowrapper>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
@@ -1,213 +0,0 @@
|
|||||||
import { html, cssManager } from "@design.estate/dees-element";
|
|
||||||
import type { IServiceStatus } from '../interfaces/index.js';
|
|
||||||
import '../elements/index.js';
|
|
||||||
|
|
||||||
export const adminpageMonitors = () => 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: any) => {
|
|
||||||
const monitorList = wrapperElement.querySelector('upladmin-monitor-list') as any;
|
|
||||||
|
|
||||||
const monitors: IServiceStatus[] = [
|
|
||||||
{
|
|
||||||
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: 'database-replica',
|
|
||||||
name: 'database-replica',
|
|
||||||
displayName: 'Database Replica',
|
|
||||||
description: 'PostgreSQL read replica',
|
|
||||||
currentStatus: 'operational',
|
|
||||||
lastChecked: Date.now(),
|
|
||||||
uptime30d: 99.99,
|
|
||||||
uptime90d: 99.95,
|
|
||||||
responseTime: 8,
|
|
||||||
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: 'sms-service',
|
|
||||||
name: 'sms-service',
|
|
||||||
displayName: 'SMS Service',
|
|
||||||
description: 'SMS notifications and 2FA',
|
|
||||||
currentStatus: 'operational',
|
|
||||||
lastChecked: Date.now(),
|
|
||||||
uptime30d: 99.8,
|
|
||||||
uptime90d: 99.75,
|
|
||||||
responseTime: 800,
|
|
||||||
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',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
monitorList.monitors = monitors;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<upladmin-monitor-list></upladmin-monitor-list>
|
|
||||||
</dees-demowrapper>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const adminpageMonitorForm = () => 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: any) => {
|
|
||||||
const monitorForm = wrapperElement.querySelector('upladmin-monitor-form') as any;
|
|
||||||
|
|
||||||
const availableMonitors: IServiceStatus[] = [
|
|
||||||
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
|
|
||||||
{ id: 'database-primary', name: 'database-primary', displayName: 'Primary Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
|
|
||||||
];
|
|
||||||
|
|
||||||
monitorForm.availableMonitors = availableMonitors;
|
|
||||||
monitorForm.categories = ['Core Services', 'Infrastructure', 'External Services', 'Web Services'];
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<upladmin-monitor-form></upladmin-monitor-form>
|
|
||||||
</dees-demowrapper>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const adminpageMonitorFormEdit = () => 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: any) => {
|
|
||||||
const monitorForm = wrapperElement.querySelector('upladmin-monitor-form') as any;
|
|
||||||
|
|
||||||
const availableMonitors: IServiceStatus[] = [
|
|
||||||
{ id: 'api-server', name: 'api-server', displayName: 'API Server', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.98, uptime90d: 99.95, responseTime: 45 },
|
|
||||||
{ id: 'database-primary', name: 'database-primary', displayName: 'Primary Database', currentStatus: 'operational', lastChecked: Date.now(), uptime30d: 99.999, uptime90d: 99.998, responseTime: 5 },
|
|
||||||
];
|
|
||||||
|
|
||||||
monitorForm.availableMonitors = availableMonitors;
|
|
||||||
monitorForm.categories = ['Core Services', 'Infrastructure', 'External Services', 'Web Services'];
|
|
||||||
monitorForm.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',
|
|
||||||
};
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<upladmin-monitor-form></upladmin-monitor-form>
|
|
||||||
</dees-demowrapper>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
export * from './adminpage-dashboard.js';
|
// Consolidated app demo - showcases all views through dees-appui
|
||||||
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';
|
export { demoFunc as adminpageApp } from './upladmin-app/upladmin-app.demo.js';
|
||||||
|
export { UpladminApp } from './upladmin-app/upladmin-app.js';
|
||||||
|
|||||||
@@ -11,14 +11,11 @@ import type { DeesAppuiBase } from '@design.estate/dees-catalog';
|
|||||||
import { adminState } from '../../services/admin-state.js';
|
import { adminState } from '../../services/admin-state.js';
|
||||||
import { demoFunc } from './upladmin-app.demo.js';
|
import { demoFunc } from './upladmin-app.demo.js';
|
||||||
|
|
||||||
// Import components directly
|
// Import view components
|
||||||
import '../../elements/upladmin-dashboard/upladmin-dashboard.js';
|
import '../../views/upladmin-dashboard-view/upladmin-dashboard-view.js';
|
||||||
import '../../elements/upladmin-monitor-list/upladmin-monitor-list.js';
|
import '../../views/upladmin-monitors-view/upladmin-monitors-view.js';
|
||||||
import '../../elements/upladmin-monitor-form/upladmin-monitor-form.js';
|
import '../../views/upladmin-incidents-view/upladmin-incidents-view.js';
|
||||||
import '../../elements/upladmin-incident-list/upladmin-incident-list.js';
|
import '../../views/upladmin-config-view/upladmin-config-view.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 {
|
declare global {
|
||||||
interface HTMLElementTagNameMap {
|
interface HTMLElementTagNameMap {
|
||||||
@@ -176,14 +173,14 @@ export class UpladminApp extends DeesElement {
|
|||||||
id: 'dashboard',
|
id: 'dashboard',
|
||||||
name: 'Dashboard',
|
name: 'Dashboard',
|
||||||
iconName: 'lucide:layoutDashboard',
|
iconName: 'lucide:layoutDashboard',
|
||||||
content: 'upladmin-dashboard',
|
content: 'upladmin-dashboard-view',
|
||||||
route: 'dashboard',
|
route: 'dashboard',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'monitors',
|
id: 'monitors',
|
||||||
name: 'Monitors',
|
name: 'Monitors',
|
||||||
iconName: 'lucide:activity',
|
iconName: 'lucide:activity',
|
||||||
content: 'upladmin-monitor-list',
|
content: 'upladmin-monitors-view',
|
||||||
route: 'monitors',
|
route: 'monitors',
|
||||||
badge: adminState.monitors.length,
|
badge: adminState.monitors.length,
|
||||||
},
|
},
|
||||||
@@ -191,7 +188,7 @@ export class UpladminApp extends DeesElement {
|
|||||||
id: 'monitor-form',
|
id: 'monitor-form',
|
||||||
name: 'Monitor',
|
name: 'Monitor',
|
||||||
iconName: 'lucide:activity',
|
iconName: 'lucide:activity',
|
||||||
content: 'upladmin-monitor-form',
|
content: 'upladmin-monitors-view',
|
||||||
route: 'monitors/:id',
|
route: 'monitors/:id',
|
||||||
cache: false,
|
cache: false,
|
||||||
},
|
},
|
||||||
@@ -199,7 +196,7 @@ export class UpladminApp extends DeesElement {
|
|||||||
id: 'incidents',
|
id: 'incidents',
|
||||||
name: 'Incidents',
|
name: 'Incidents',
|
||||||
iconName: 'lucide:alertCircle',
|
iconName: 'lucide:alertCircle',
|
||||||
content: 'upladmin-incident-list',
|
content: 'upladmin-incidents-view',
|
||||||
route: 'incidents',
|
route: 'incidents',
|
||||||
badge: adminState.getActiveIncidents().length,
|
badge: adminState.getActiveIncidents().length,
|
||||||
badgeVariant: adminState.getActiveIncidents().length > 0 ? 'warning' : 'default',
|
badgeVariant: adminState.getActiveIncidents().length > 0 ? 'warning' : 'default',
|
||||||
@@ -208,7 +205,7 @@ export class UpladminApp extends DeesElement {
|
|||||||
id: 'incident-form',
|
id: 'incident-form',
|
||||||
name: 'Incident',
|
name: 'Incident',
|
||||||
iconName: 'lucide:alertCircle',
|
iconName: 'lucide:alertCircle',
|
||||||
content: 'upladmin-incident-form',
|
content: 'upladmin-incidents-view',
|
||||||
route: 'incidents/:id',
|
route: 'incidents/:id',
|
||||||
cache: false,
|
cache: false,
|
||||||
},
|
},
|
||||||
@@ -216,7 +213,7 @@ export class UpladminApp extends DeesElement {
|
|||||||
id: 'incident-update',
|
id: 'incident-update',
|
||||||
name: 'Post Update',
|
name: 'Post Update',
|
||||||
iconName: 'lucide:messageSquarePlus',
|
iconName: 'lucide:messageSquarePlus',
|
||||||
content: 'upladmin-incident-update',
|
content: 'upladmin-incidents-view',
|
||||||
route: 'incidents/:id/update',
|
route: 'incidents/:id/update',
|
||||||
cache: false,
|
cache: false,
|
||||||
},
|
},
|
||||||
@@ -224,9 +221,17 @@ export class UpladminApp extends DeesElement {
|
|||||||
id: 'config',
|
id: 'config',
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
iconName: 'lucide:settings',
|
iconName: 'lucide:settings',
|
||||||
content: 'upladmin-statuspage-config',
|
content: 'upladmin-config-view',
|
||||||
route: 'config',
|
route: 'config',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'config-section',
|
||||||
|
name: 'Settings',
|
||||||
|
iconName: 'lucide:settings',
|
||||||
|
content: 'upladmin-config-view',
|
||||||
|
route: 'config/:section',
|
||||||
|
cache: false,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
mainMenu: {
|
mainMenu: {
|
||||||
|
|||||||
4
ts_web/views/index.ts
Normal file
4
ts_web/views/index.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export * from './upladmin-dashboard-view/upladmin-dashboard-view.js';
|
||||||
|
export * from './upladmin-monitors-view/upladmin-monitors-view.js';
|
||||||
|
export * from './upladmin-incidents-view/upladmin-incidents-view.js';
|
||||||
|
export * from './upladmin-config-view/upladmin-config-view.js';
|
||||||
124
ts_web/views/upladmin-config-view/upladmin-config-view.ts
Normal file
124
ts_web/views/upladmin-config-view/upladmin-config-view.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import {
|
||||||
|
DeesElement,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
state,
|
||||||
|
css,
|
||||||
|
cssManager,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
import type { DeesAppuiBase } from '@design.estate/dees-catalog';
|
||||||
|
|
||||||
|
// View lifecycle interfaces (defined locally as they're not exported from dees-catalog)
|
||||||
|
interface IViewActivationContext {
|
||||||
|
appui: DeesAppuiBase;
|
||||||
|
viewId: string;
|
||||||
|
params?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IViewLifecycle {
|
||||||
|
onActivate?: (context: IViewActivationContext) => void | Promise<void>;
|
||||||
|
onDeactivate?: () => void | Promise<void>;
|
||||||
|
}
|
||||||
|
import { adminState } from '../../services/admin-state.js';
|
||||||
|
import '../../elements/upladmin-statuspage-config/upladmin-statuspage-config.js';
|
||||||
|
|
||||||
|
type TConfigSection = 'branding' | 'urls' | 'behavior' | 'advanced';
|
||||||
|
|
||||||
|
@customElement('upladmin-config-view')
|
||||||
|
export class UpladminConfigView extends DeesElement implements IViewLifecycle {
|
||||||
|
@state()
|
||||||
|
accessor activeSection: TConfigSection = 'branding';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor loading: boolean = false;
|
||||||
|
|
||||||
|
private appuiRef: DeesAppuiBase | null = null;
|
||||||
|
|
||||||
|
public static styles = [
|
||||||
|
cssManager.defaultStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
|
||||||
|
async onActivate(context: IViewActivationContext): Promise<void> {
|
||||||
|
this.appuiRef = context.appui;
|
||||||
|
|
||||||
|
// Check route params for section
|
||||||
|
if (context.params?.section) {
|
||||||
|
const section = context.params.section as TConfigSection;
|
||||||
|
if (['branding', 'urls', 'behavior', 'advanced'].includes(section)) {
|
||||||
|
this.activeSection = section;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set secondary menu for configuration sections
|
||||||
|
this.updateSecondaryMenu();
|
||||||
|
|
||||||
|
// No content tabs for config
|
||||||
|
context.appui.setContentTabs([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSecondaryMenu(): void {
|
||||||
|
if (!this.appuiRef) return;
|
||||||
|
|
||||||
|
this.appuiRef.setSecondaryMenu({
|
||||||
|
heading: 'Configuration',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
name: 'Settings',
|
||||||
|
iconName: 'lucide:settings',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'branding',
|
||||||
|
iconName: 'lucide:palette',
|
||||||
|
action: () => this.setSection('branding'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'urls',
|
||||||
|
iconName: 'lucide:link',
|
||||||
|
action: () => this.setSection('urls'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'behavior',
|
||||||
|
iconName: 'lucide:sliders',
|
||||||
|
action: () => this.setSection('behavior'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'advanced',
|
||||||
|
iconName: 'lucide:wrench',
|
||||||
|
action: () => this.setSection('advanced'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select current section
|
||||||
|
this.appuiRef.setSecondaryMenuSelection(this.activeSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setSection(section: TConfigSection): void {
|
||||||
|
this.activeSection = section;
|
||||||
|
this.appuiRef?.setSecondaryMenuSelection(section);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleConfigSave = (e: CustomEvent): void => {
|
||||||
|
console.log('Config saved:', e.detail);
|
||||||
|
// In a real implementation, this would save to the backend
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<upladmin-statuspage-config
|
||||||
|
.config=${adminState.config || {}}
|
||||||
|
.activeSection=${this.activeSection}
|
||||||
|
.loading=${this.loading}
|
||||||
|
@configSave=${this.handleConfigSave}
|
||||||
|
></upladmin-statuspage-config>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
DeesElement,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
state,
|
||||||
|
css,
|
||||||
|
cssManager,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
import type { DeesAppuiBase } from '@design.estate/dees-catalog';
|
||||||
|
|
||||||
|
// View lifecycle interfaces (defined locally as they're not exported from dees-catalog)
|
||||||
|
interface IViewActivationContext {
|
||||||
|
appui: DeesAppuiBase;
|
||||||
|
viewId: string;
|
||||||
|
params?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IViewLifecycle {
|
||||||
|
onActivate?: (context: IViewActivationContext) => void | Promise<void>;
|
||||||
|
onDeactivate?: () => void | Promise<void>;
|
||||||
|
}
|
||||||
|
import { adminState } from '../../services/admin-state.js';
|
||||||
|
import '../../elements/upladmin-dashboard/upladmin-dashboard.js';
|
||||||
|
|
||||||
|
@customElement('upladmin-dashboard-view')
|
||||||
|
export class UpladminDashboardView extends DeesElement implements IViewLifecycle {
|
||||||
|
@state()
|
||||||
|
accessor loading: boolean = true;
|
||||||
|
|
||||||
|
public static styles = [
|
||||||
|
cssManager.defaultStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
|
||||||
|
async onActivate(context: IViewActivationContext): Promise<void> {
|
||||||
|
// Dashboard has no secondary menu - clear any existing
|
||||||
|
context.appui.clearSecondaryMenu();
|
||||||
|
|
||||||
|
// No content tabs for dashboard
|
||||||
|
context.appui.setContentTabs([]);
|
||||||
|
|
||||||
|
// Load data
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<upladmin-dashboard
|
||||||
|
.monitors=${adminState.monitors}
|
||||||
|
.incidents=${adminState.incidents}
|
||||||
|
.loading=${this.loading}
|
||||||
|
></upladmin-dashboard>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
292
ts_web/views/upladmin-incidents-view/upladmin-incidents-view.ts
Normal file
292
ts_web/views/upladmin-incidents-view/upladmin-incidents-view.ts
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
import {
|
||||||
|
DeesElement,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
state,
|
||||||
|
css,
|
||||||
|
cssManager,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
import type { DeesAppuiBase } from '@design.estate/dees-catalog';
|
||||||
|
|
||||||
|
// View lifecycle interfaces (defined locally as they're not exported from dees-catalog)
|
||||||
|
interface IViewActivationContext {
|
||||||
|
appui: DeesAppuiBase;
|
||||||
|
viewId: string;
|
||||||
|
params?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IViewLifecycle {
|
||||||
|
onActivate?: (context: IViewActivationContext) => void | Promise<void>;
|
||||||
|
onDeactivate?: () => void | Promise<void>;
|
||||||
|
}
|
||||||
|
import { adminState } from '../../services/admin-state.js';
|
||||||
|
import type { IIncidentDetails, TIncidentSeverity, TIncidentStatus } from '../../interfaces/index.js';
|
||||||
|
import '../../elements/upladmin-incident-list/upladmin-incident-list.js';
|
||||||
|
import '../../elements/upladmin-incident-form/upladmin-incident-form.js';
|
||||||
|
import '../../elements/upladmin-incident-update/upladmin-incident-update.js';
|
||||||
|
|
||||||
|
type TViewMode = 'list' | 'form' | 'update';
|
||||||
|
type TTimeFilter = 'current' | 'past' | 'all';
|
||||||
|
|
||||||
|
@customElement('upladmin-incidents-view')
|
||||||
|
export class UpladminIncidentsView extends DeesElement implements IViewLifecycle {
|
||||||
|
@state()
|
||||||
|
accessor currentMode: TViewMode = 'list';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor selectedIncidentId: string | null = null;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor timeFilter: TTimeFilter = 'current';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor severityFilter: TIncidentSeverity | 'all' = 'all';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor loading: boolean = false;
|
||||||
|
|
||||||
|
private appuiRef: DeesAppuiBase | null = null;
|
||||||
|
|
||||||
|
public static styles = [
|
||||||
|
cssManager.defaultStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
|
||||||
|
async onActivate(context: IViewActivationContext): Promise<void> {
|
||||||
|
this.appuiRef = context.appui;
|
||||||
|
|
||||||
|
// Check route params and view ID
|
||||||
|
if (context.params?.id) {
|
||||||
|
if (context.viewId === 'incident-update') {
|
||||||
|
this.currentMode = 'update';
|
||||||
|
this.selectedIncidentId = context.params.id;
|
||||||
|
} else {
|
||||||
|
this.currentMode = 'form';
|
||||||
|
this.selectedIncidentId = context.params.id === 'create' ? null : context.params.id;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.currentMode = 'list';
|
||||||
|
this.selectedIncidentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set secondary menu
|
||||||
|
this.updateSecondaryMenu();
|
||||||
|
|
||||||
|
// No content tabs - incident-list has internal tabs
|
||||||
|
context.appui.setContentTabs([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSecondaryMenu(): void {
|
||||||
|
if (!this.appuiRef) return;
|
||||||
|
|
||||||
|
const activeCount = adminState.getActiveIncidents().length;
|
||||||
|
const pastCount = adminState.incidents.filter((i) => i.status === 'resolved' || i.status === 'postmortem').length;
|
||||||
|
|
||||||
|
this.appuiRef.setSecondaryMenu({
|
||||||
|
heading: 'Incidents',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
name: 'Filter',
|
||||||
|
iconName: 'lucide:filter',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'current',
|
||||||
|
iconName: 'lucide:alertCircle',
|
||||||
|
action: () => this.setTimeFilter('current'),
|
||||||
|
badge: activeCount,
|
||||||
|
badgeVariant: activeCount > 0 ? 'error' : 'default',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'past',
|
||||||
|
iconName: 'lucide:history',
|
||||||
|
action: () => this.setTimeFilter('past'),
|
||||||
|
badge: pastCount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'all',
|
||||||
|
iconName: 'lucide:list',
|
||||||
|
action: () => this.setTimeFilter('all'),
|
||||||
|
badge: adminState.incidents.length,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Severity',
|
||||||
|
iconName: 'lucide:alertTriangle',
|
||||||
|
collapsed: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'critical',
|
||||||
|
iconName: 'lucide:xCircle',
|
||||||
|
action: () => this.setSeverityFilter('critical'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'major',
|
||||||
|
iconName: 'lucide:alertOctagon',
|
||||||
|
action: () => this.setSeverityFilter('major'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'minor',
|
||||||
|
iconName: 'lucide:alertTriangle',
|
||||||
|
action: () => this.setSeverityFilter('minor'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'maintenance',
|
||||||
|
iconName: 'lucide:wrench',
|
||||||
|
action: () => this.setSeverityFilter('maintenance'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Actions',
|
||||||
|
iconName: 'lucide:zap',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'create',
|
||||||
|
iconName: 'lucide:plus',
|
||||||
|
action: () => this.showForm(null),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select current filter
|
||||||
|
this.appuiRef.setSecondaryMenuSelection(this.timeFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setTimeFilter(filter: TTimeFilter): void {
|
||||||
|
this.timeFilter = filter;
|
||||||
|
this.severityFilter = 'all';
|
||||||
|
this.appuiRef?.setSecondaryMenuSelection(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setSeverityFilter(severity: TIncidentSeverity): void {
|
||||||
|
this.severityFilter = severity;
|
||||||
|
this.appuiRef?.setSecondaryMenuSelection(severity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private showForm(incidentId: string | null): void {
|
||||||
|
this.currentMode = 'form';
|
||||||
|
this.selectedIncidentId = incidentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private showUpdate(incidentId: string): void {
|
||||||
|
this.currentMode = 'update';
|
||||||
|
this.selectedIncidentId = incidentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private showList(): void {
|
||||||
|
this.currentMode = 'list';
|
||||||
|
this.selectedIncidentId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get filteredIncidents(): IIncidentDetails[] {
|
||||||
|
let incidents = adminState.incidents;
|
||||||
|
|
||||||
|
// Apply time filter
|
||||||
|
if (this.timeFilter === 'current') {
|
||||||
|
incidents = incidents.filter(
|
||||||
|
(i) => i.status !== 'resolved' && i.status !== 'postmortem'
|
||||||
|
);
|
||||||
|
} else if (this.timeFilter === 'past') {
|
||||||
|
incidents = incidents.filter(
|
||||||
|
(i) => i.status === 'resolved' || i.status === 'postmortem'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply severity filter
|
||||||
|
if (this.severityFilter !== 'all') {
|
||||||
|
incidents = incidents.filter((i) => i.severity === this.severityFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return incidents;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleIncidentSave = (e: CustomEvent): void => {
|
||||||
|
console.log('Incident saved:', e.detail);
|
||||||
|
this.showList();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleUpdateSave = (e: CustomEvent): void => {
|
||||||
|
console.log('Update saved:', e.detail);
|
||||||
|
this.showList();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleCancel = (): void => {
|
||||||
|
this.showList();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleIncidentEdit = (e: CustomEvent): void => {
|
||||||
|
const incident = e.detail?.incident as IIncidentDetails;
|
||||||
|
if (incident) {
|
||||||
|
this.showForm(incident.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleIncidentAddUpdate = (e: CustomEvent): void => {
|
||||||
|
const incident = e.detail?.incident as IIncidentDetails;
|
||||||
|
if (incident) {
|
||||||
|
this.showUpdate(incident.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.currentMode === 'update') {
|
||||||
|
const incident = this.selectedIncidentId
|
||||||
|
? adminState.incidents.find((i) => i.id === this.selectedIncidentId)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<upladmin-incident-update
|
||||||
|
.incident=${incident}
|
||||||
|
.loading=${this.loading}
|
||||||
|
@updateSave=${this.handleUpdateSave}
|
||||||
|
@cancel=${this.handleCancel}
|
||||||
|
></upladmin-incident-update>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentMode === 'form') {
|
||||||
|
const incident = this.selectedIncidentId
|
||||||
|
? adminState.incidents.find((i) => i.id === this.selectedIncidentId)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<upladmin-incident-form
|
||||||
|
.incident=${incident
|
||||||
|
? {
|
||||||
|
id: incident.id,
|
||||||
|
title: incident.title,
|
||||||
|
severity: incident.severity,
|
||||||
|
status: incident.status,
|
||||||
|
affectedServices: incident.affectedServices,
|
||||||
|
impact: incident.impact,
|
||||||
|
rootCause: incident.rootCause,
|
||||||
|
resolution: incident.resolution,
|
||||||
|
}
|
||||||
|
: null}
|
||||||
|
.availableServices=${adminState.monitors}
|
||||||
|
.loading=${this.loading}
|
||||||
|
@incidentSave=${this.handleIncidentSave}
|
||||||
|
@cancel=${this.handleCancel}
|
||||||
|
></upladmin-incident-form>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<upladmin-incident-list
|
||||||
|
.incidents=${this.filteredIncidents}
|
||||||
|
.loading=${this.loading}
|
||||||
|
@incidentAdd=${() => this.showForm(null)}
|
||||||
|
@incidentEdit=${this.handleIncidentEdit}
|
||||||
|
@incidentAddUpdate=${this.handleIncidentAddUpdate}
|
||||||
|
></upladmin-incident-list>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
244
ts_web/views/upladmin-monitors-view/upladmin-monitors-view.ts
Normal file
244
ts_web/views/upladmin-monitors-view/upladmin-monitors-view.ts
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
import {
|
||||||
|
DeesElement,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
state,
|
||||||
|
css,
|
||||||
|
cssManager,
|
||||||
|
} from '@design.estate/dees-element';
|
||||||
|
import type { DeesAppuiBase } from '@design.estate/dees-catalog';
|
||||||
|
|
||||||
|
// View lifecycle interfaces (defined locally as they're not exported from dees-catalog)
|
||||||
|
interface IViewActivationContext {
|
||||||
|
appui: DeesAppuiBase;
|
||||||
|
viewId: string;
|
||||||
|
params?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IViewLifecycle {
|
||||||
|
onActivate?: (context: IViewActivationContext) => void | Promise<void>;
|
||||||
|
onDeactivate?: () => void | Promise<void>;
|
||||||
|
}
|
||||||
|
import { adminState } from '../../services/admin-state.js';
|
||||||
|
import type { IServiceStatus, TStatusType } from '../../interfaces/index.js';
|
||||||
|
import '../../elements/upladmin-monitor-list/upladmin-monitor-list.js';
|
||||||
|
import '../../elements/upladmin-monitor-form/upladmin-monitor-form.js';
|
||||||
|
|
||||||
|
type TViewMode = 'list' | 'form';
|
||||||
|
type TStatusFilter = 'all' | 'issues' | 'maintenance';
|
||||||
|
|
||||||
|
@customElement('upladmin-monitors-view')
|
||||||
|
export class UpladminMonitorsView extends DeesElement implements IViewLifecycle {
|
||||||
|
@state()
|
||||||
|
accessor currentMode: TViewMode = 'list';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor selectedMonitorId: string | null = null;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor statusFilter: TStatusFilter = 'all';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor categoryFilter: string = 'all';
|
||||||
|
|
||||||
|
@state()
|
||||||
|
accessor loading: boolean = false;
|
||||||
|
|
||||||
|
private appuiRef: DeesAppuiBase | null = null;
|
||||||
|
|
||||||
|
public static styles = [
|
||||||
|
cssManager.defaultStyles,
|
||||||
|
css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
|
||||||
|
async onActivate(context: IViewActivationContext): Promise<void> {
|
||||||
|
this.appuiRef = context.appui;
|
||||||
|
|
||||||
|
// Check route params for edit mode
|
||||||
|
if (context.params?.id) {
|
||||||
|
this.currentMode = 'form';
|
||||||
|
this.selectedMonitorId = context.params.id === 'create' ? null : context.params.id;
|
||||||
|
} else {
|
||||||
|
this.currentMode = 'list';
|
||||||
|
this.selectedMonitorId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set secondary menu with categories
|
||||||
|
this.updateSecondaryMenu();
|
||||||
|
|
||||||
|
// Set content tabs for status filtering
|
||||||
|
context.appui.setContentTabs([
|
||||||
|
{
|
||||||
|
key: 'All',
|
||||||
|
iconName: 'lucide:activity',
|
||||||
|
action: () => this.setStatusFilter('all'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Issues',
|
||||||
|
iconName: 'lucide:alertTriangle',
|
||||||
|
action: () => this.setStatusFilter('issues'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'Maintenance',
|
||||||
|
iconName: 'lucide:wrench',
|
||||||
|
action: () => this.setStatusFilter('maintenance'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateSecondaryMenu(): void {
|
||||||
|
if (!this.appuiRef) return;
|
||||||
|
|
||||||
|
const categories = this.getCategories();
|
||||||
|
|
||||||
|
this.appuiRef.setSecondaryMenu({
|
||||||
|
heading: 'Monitors',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
name: 'Categories',
|
||||||
|
iconName: 'lucide:folder',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'all',
|
||||||
|
iconName: 'lucide:list',
|
||||||
|
action: () => this.setCategoryFilter('all'),
|
||||||
|
badge: adminState.monitors.length,
|
||||||
|
},
|
||||||
|
...categories.map((cat) => ({
|
||||||
|
key: cat,
|
||||||
|
iconName: 'lucide:folder',
|
||||||
|
action: () => this.setCategoryFilter(cat),
|
||||||
|
badge: adminState.monitors.filter((m) => m.category === cat).length,
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Actions',
|
||||||
|
iconName: 'lucide:zap',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
key: 'add',
|
||||||
|
iconName: 'lucide:plus',
|
||||||
|
action: () => this.showForm(null),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Select current category
|
||||||
|
this.appuiRef.setSecondaryMenuSelection(this.categoryFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCategories(): string[] {
|
||||||
|
const cats = new Set<string>();
|
||||||
|
for (const m of adminState.monitors) {
|
||||||
|
if (m.category) cats.add(m.category);
|
||||||
|
}
|
||||||
|
return Array.from(cats).sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private setStatusFilter(filter: TStatusFilter): void {
|
||||||
|
this.statusFilter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
private setCategoryFilter(category: string): void {
|
||||||
|
this.categoryFilter = category;
|
||||||
|
this.appuiRef?.setSecondaryMenuSelection(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
private showForm(monitorId: string | null): void {
|
||||||
|
this.currentMode = 'form';
|
||||||
|
this.selectedMonitorId = monitorId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private showList(): void {
|
||||||
|
this.currentMode = 'list';
|
||||||
|
this.selectedMonitorId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get filteredMonitors(): IServiceStatus[] {
|
||||||
|
let monitors = adminState.monitors;
|
||||||
|
|
||||||
|
// Apply category filter
|
||||||
|
if (this.categoryFilter !== 'all') {
|
||||||
|
monitors = monitors.filter((m) => m.category === this.categoryFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply status filter
|
||||||
|
if (this.statusFilter === 'issues') {
|
||||||
|
monitors = monitors.filter((m) =>
|
||||||
|
['degraded', 'partial_outage', 'major_outage', 'error'].includes(m.currentStatus)
|
||||||
|
);
|
||||||
|
} else if (this.statusFilter === 'maintenance') {
|
||||||
|
monitors = monitors.filter((m) => m.currentStatus === 'maintenance');
|
||||||
|
}
|
||||||
|
|
||||||
|
return monitors;
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleMonitorSave = (e: CustomEvent): void => {
|
||||||
|
// Handle save logic
|
||||||
|
console.log('Monitor saved:', e.detail);
|
||||||
|
this.showList();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleCancel = (): void => {
|
||||||
|
this.showList();
|
||||||
|
};
|
||||||
|
|
||||||
|
private handleMonitorEdit = (e: CustomEvent): void => {
|
||||||
|
const monitor = e.detail?.monitor as IServiceStatus;
|
||||||
|
if (monitor) {
|
||||||
|
this.showForm(monitor.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
if (this.currentMode === 'form') {
|
||||||
|
const monitor = this.selectedMonitorId
|
||||||
|
? adminState.monitors.find((m) => m.id === this.selectedMonitorId)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<upladmin-monitor-form
|
||||||
|
.monitor=${monitor
|
||||||
|
? {
|
||||||
|
id: monitor.id,
|
||||||
|
name: monitor.name,
|
||||||
|
displayName: monitor.displayName,
|
||||||
|
description: monitor.description,
|
||||||
|
category: monitor.category,
|
||||||
|
dependencies: monitor.dependencies,
|
||||||
|
statusMode: monitor.statusMode || 'auto',
|
||||||
|
manualStatus: monitor.manualStatus,
|
||||||
|
paused: monitor.paused || false,
|
||||||
|
checkType: monitor.checkType || 'assumption',
|
||||||
|
checkConfig: monitor.checkConfig || { domain: '' },
|
||||||
|
intervalMs: monitor.intervalMs || 60000,
|
||||||
|
}
|
||||||
|
: null}
|
||||||
|
.availableMonitors=${adminState.monitors}
|
||||||
|
.categories=${this.getCategories()}
|
||||||
|
.loading=${this.loading}
|
||||||
|
@monitorSave=${this.handleMonitorSave}
|
||||||
|
@cancel=${this.handleCancel}
|
||||||
|
></upladmin-monitor-form>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<upladmin-monitor-list
|
||||||
|
.monitors=${this.filteredMonitors}
|
||||||
|
.loading=${this.loading}
|
||||||
|
@monitorAdd=${() => this.showForm(null)}
|
||||||
|
@monitorEdit=${this.handleMonitorEdit}
|
||||||
|
></upladmin-monitor-list>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user