diff --git a/example.statuspage.html b/example.statuspage.html new file mode 100644 index 0000000..73b2a42 --- /dev/null +++ b/example.statuspage.html @@ -0,0 +1,228 @@ + + + + + + Status Page Example + + + + +
+ + + + + + + + + +
+ + + + + + + + + + + +
+ + + +
+ + + + \ No newline at end of file diff --git a/readme.hints.md b/readme.hints.md index a2bbb09..b8662c2 100644 --- a/readme.hints.md +++ b/readme.hints.md @@ -186,6 +186,16 @@ To make these components production-ready requires implementing: ## Recent Updates (Post v1.0.74) +### Component Order (Top to Bottom) +1. `upl-statuspage-header` - Navigation header +2. `upl-statuspage-statusbar` - Overall system status +3. `upl-statuspage-statsgrid` - Key metrics grid (uptime, response time, incidents) +4. `upl-statuspage-assetsselector` - Service selection +5. `upl-statuspage-statusdetails` - 48-hour status visualization +6. `upl-statuspage-statusmonth` - Monthly calendar view +7. `upl-statuspage-incidents` - Current and past incidents +8. `upl-statuspage-footer` - Page footer + ### Components Made Production-Ready All components have been significantly enhanced with the following improvements: @@ -238,6 +248,16 @@ All components have been significantly enhanced with the following improvements: - RSS feed and API status links - Last updated timestamp with relative formatting +8. **upl-statuspage-statsgrid** (NEW) + - Displays key performance metrics in a responsive grid + - Shows current status with color indicator + - Uptime percentage with configurable time period + - Average response time with performance trend indicators + - Total incidents count with affected services + - Loading state with skeleton animation + - Responsive design that stacks on mobile + - Used stats data that was previously in statusdetails component + ### Demo Architecture - All demos have been updated to use dees-demowrapper with runAfterRender callbacks - Properties are set dynamically on elements within runAfterRender @@ -252,7 +272,7 @@ Created comprehensive TypeScript interfaces in ts_web/interfaces/index.ts: - IIncidentUpdate - Incident update entries - IIncidentDetails - Full incident information - IMonthlyUptime - Monthly uptime calendar data -- IStatusDetail - Hourly status data points +- IStatusHistoryPoint - Hourly status data points - IStatusPageConfig - Configuration options ### Remaining Tasks diff --git a/test-demo-no-banners.html b/test-demo-no-banners.html new file mode 100644 index 0000000..020b9b0 --- /dev/null +++ b/test-demo-no-banners.html @@ -0,0 +1,84 @@ + + + + + + Demo Pages Without Banners + + + + +
+ + +
+ +
+ + + + \ No newline at end of file diff --git a/test-statsgrid.html b/test-statsgrid.html new file mode 100644 index 0000000..320c96b --- /dev/null +++ b/test-statsgrid.html @@ -0,0 +1,107 @@ + + + + + + Stats Grid Test + + + + +

Stats Grid Component Test

+ +
+

Normal Operation

+ +
+ +
+

Degraded Performance

+ +
+ +
+

Major Outage

+ +
+ +
+

Loading State

+ +
+ + + + \ No newline at end of file diff --git a/ts_web/elements/index.ts b/ts_web/elements/index.ts index 777739b..de8dda8 100644 --- a/ts_web/elements/index.ts +++ b/ts_web/elements/index.ts @@ -7,6 +7,7 @@ export * from './upl-statuspage-pagetitle.js'; export * from './upl-statuspage-statusbar.js'; export * from './upl-statuspage-statusdetails.js'; export * from './upl-statuspage-statusmonth.js'; +export * from './upl-statuspage-statsgrid.js'; // Export interfaces export * from '../interfaces/index.js'; diff --git a/ts_web/elements/upl-statuspage-assetsselector.ts b/ts_web/elements/upl-statuspage-assetsselector.ts index 96a0c03..2677e23 100644 --- a/ts_web/elements/upl-statuspage-assetsselector.ts +++ b/ts_web/elements/upl-statuspage-assetsselector.ts @@ -257,11 +257,11 @@ export class UplStatuspageAssetsselector extends DeesElement { border-radius: ${unsafeCSS(borderRadius.full)}; } - .status-indicator.operational, .status-dot.operational { background: ${cssManager.bdTheme('#10b981', '#10b981')}; } - .status-indicator.degraded, .status-dot.degraded { background: ${cssManager.bdTheme('#f59e0b', '#f59e0b')}; } - .status-indicator.partial_outage, .status-dot.partial_outage { background: ${cssManager.bdTheme('#ef4444', '#ef4444')}; } - .status-indicator.major_outage, .status-dot.major_outage { background: ${cssManager.bdTheme('#dc2626', '#dc2626')}; } - .status-indicator.maintenance, .status-dot.maintenance { background: ${cssManager.bdTheme('#3b82f6', '#3b82f6')}; } + .status-indicator.operational, .status-dot.operational { background: #22c55e; } + .status-indicator.degraded, .status-dot.degraded { background: #fbbf24; } + .status-indicator.partial_outage, .status-dot.partial_outage { background: #f87171; } + .status-indicator.major_outage, .status-dot.major_outage { background: #ef4444; } + .status-indicator.maintenance, .status-dot.maintenance { background: #60a5fa; } .status-text { font-size: 12px; diff --git a/ts_web/elements/upl-statuspage-statsgrid.demo.ts b/ts_web/elements/upl-statuspage-statsgrid.demo.ts new file mode 100644 index 0000000..9620c53 --- /dev/null +++ b/ts_web/elements/upl-statuspage-statsgrid.demo.ts @@ -0,0 +1,315 @@ +import { html } from '@design.estate/dees-element'; + +export const demoFunc = () => html` + + +
+ +
+
Normal Operation
+ { + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; + + // Set initial values + statsGrid.currentStatus = 'operational'; + statsGrid.uptime = 99.95; + statsGrid.avgResponseTime = 125; + statsGrid.totalIncidents = 0; + statsGrid.affectedServices = 0; + statsGrid.totalServices = 12; + statsGrid.timePeriod = '90 days'; + }} + > + + +
+ + +
+
Degraded Performance
+ { + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; + + statsGrid.currentStatus = 'degraded'; + statsGrid.uptime = 98.50; + statsGrid.avgResponseTime = 450; + statsGrid.totalIncidents = 3; + statsGrid.affectedServices = 2; + statsGrid.totalServices = 12; + statsGrid.timePeriod = '30 days'; + }} + > + + +
+ + +
+
Major Outage
+ { + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; + + statsGrid.currentStatus = 'major_outage'; + statsGrid.uptime = 95.20; + statsGrid.avgResponseTime = 1250; + statsGrid.totalIncidents = 8; + statsGrid.affectedServices = 7; + statsGrid.totalServices = 12; + statsGrid.timePeriod = '7 days'; + }} + > + + +
+ + +
+
Interactive Demo
+ { + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; + + // Initial state + statsGrid.currentStatus = 'operational'; + statsGrid.uptime = 99.99; + statsGrid.avgResponseTime = 85; + statsGrid.totalIncidents = 0; + statsGrid.affectedServices = 0; + statsGrid.totalServices = 15; + statsGrid.timePeriod = '90 days'; + + // Create controls + const controls = document.createElement('div'); + controls.className = 'demo-controls'; + + // Status buttons + const statuses = ['operational', 'degraded', 'partial_outage', 'major_outage', 'maintenance']; + statuses.forEach((status, index) => { + const button = document.createElement('button'); + button.className = 'demo-button' + (index === 0 ? ' active' : ''); + button.textContent = status.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); + button.onclick = () => { + controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active')); + button.classList.add('active'); + + statsGrid.currentStatus = status; + + // Adjust other values based on status + switch (status) { + case 'operational': + statsGrid.uptime = 99.99; + statsGrid.avgResponseTime = 85; + statsGrid.totalIncidents = 0; + statsGrid.affectedServices = 0; + break; + case 'degraded': + statsGrid.uptime = 98.50; + statsGrid.avgResponseTime = 350; + statsGrid.totalIncidents = 2; + statsGrid.affectedServices = 1; + break; + case 'partial_outage': + statsGrid.uptime = 97.00; + statsGrid.avgResponseTime = 750; + statsGrid.totalIncidents = 5; + statsGrid.affectedServices = 3; + break; + case 'major_outage': + statsGrid.uptime = 94.50; + statsGrid.avgResponseTime = 1500; + statsGrid.totalIncidents = 10; + statsGrid.affectedServices = 8; + break; + case 'maintenance': + statsGrid.uptime = 99.00; + statsGrid.avgResponseTime = 150; + statsGrid.totalIncidents = 1; + statsGrid.affectedServices = 2; + break; + } + }; + controls.appendChild(button); + }); + + wrapperElement.appendChild(controls); + + // Add time period selector + const timePeriodControls = document.createElement('div'); + timePeriodControls.className = 'demo-controls'; + timePeriodControls.style.marginTop = '10px'; + + const periods = ['24 hours', '7 days', '30 days', '90 days']; + periods.forEach((period, index) => { + const button = document.createElement('button'); + button.className = 'demo-button' + (index === 3 ? ' active' : ''); + button.textContent = period; + button.onclick = () => { + timePeriodControls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active')); + button.classList.add('active'); + statsGrid.timePeriod = period; + }; + timePeriodControls.appendChild(button); + }); + + wrapperElement.appendChild(timePeriodControls); + }} + > + + +
+ + +
+
Loading State
+ { + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; + statsGrid.loading = true; + + // Create toggle button + const controls = document.createElement('div'); + controls.className = 'demo-controls'; + + const toggleButton = document.createElement('button'); + toggleButton.className = 'demo-button'; + toggleButton.textContent = 'Toggle Loading'; + toggleButton.onclick = () => { + statsGrid.loading = !statsGrid.loading; + if (!statsGrid.loading) { + statsGrid.currentStatus = 'operational'; + statsGrid.uptime = 99.95; + statsGrid.avgResponseTime = 125; + statsGrid.totalIncidents = 0; + statsGrid.affectedServices = 0; + statsGrid.totalServices = 12; + } + }; + controls.appendChild(toggleButton); + + wrapperElement.appendChild(controls); + }} + > + + +
+ + +
+
Real-time Updates
+ { + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; + + // Initial values + statsGrid.currentStatus = 'operational'; + statsGrid.uptime = 99.95; + statsGrid.avgResponseTime = 100; + statsGrid.totalIncidents = 0; + statsGrid.affectedServices = 0; + statsGrid.totalServices = 10; + statsGrid.timePeriod = '24 hours'; + + // Simulate real-time updates + let interval = setInterval(() => { + // Slight variations in response time + statsGrid.avgResponseTime = Math.floor(80 + Math.random() * 40); + + // Occasionally change status + if (Math.random() < 0.1) { + const statuses = ['operational', 'degraded']; + statsGrid.currentStatus = statuses[Math.floor(Math.random() * statuses.length)]; + + if (statsGrid.currentStatus === 'degraded') { + statsGrid.avgResponseTime = Math.floor(300 + Math.random() * 200); + statsGrid.totalIncidents = Math.min(statsGrid.totalIncidents + 1, 5); + statsGrid.affectedServices = Math.min(Math.floor(Math.random() * 3) + 1, statsGrid.totalServices); + statsGrid.uptime = Math.max(99.0, statsGrid.uptime - 0.05); + } else { + statsGrid.affectedServices = 0; + } + } + }, 2000); + + // Add control button + const controls = document.createElement('div'); + controls.className = 'demo-controls'; + + const toggleButton = document.createElement('button'); + toggleButton.className = 'demo-button active'; + toggleButton.textContent = 'Stop Updates'; + toggleButton.onclick = () => { + if (interval) { + clearInterval(interval); + interval = null; + toggleButton.textContent = 'Start Updates'; + toggleButton.classList.remove('active'); + } else { + interval = setInterval(() => { + statsGrid.avgResponseTime = Math.floor(80 + Math.random() * 40); + if (Math.random() < 0.1) { + const statuses = ['operational', 'degraded']; + statsGrid.currentStatus = statuses[Math.floor(Math.random() * statuses.length)]; + } + }, 2000); + toggleButton.textContent = 'Stop Updates'; + toggleButton.classList.add('active'); + } + }; + controls.appendChild(toggleButton); + + wrapperElement.appendChild(controls); + + // Cleanup on unmount + wrapperElement.addEventListener('remove', () => { + if (interval) clearInterval(interval); + }); + }} + > + + +
+
+`; \ No newline at end of file diff --git a/ts_web/elements/upl-statuspage-statsgrid.ts b/ts_web/elements/upl-statuspage-statsgrid.ts new file mode 100644 index 0000000..ca37047 --- /dev/null +++ b/ts_web/elements/upl-statuspage-statsgrid.ts @@ -0,0 +1,306 @@ +import { + DeesElement, + property, + html, + customElement, + type TemplateResult, + css, + cssManager, + unsafeCSS, +} from '@design.estate/dees-element'; +import * as domtools from '@design.estate/dees-domtools'; +import { fonts, colors, shadows, borderRadius, spacing, commonStyles } from '../styles/shared.styles.js'; + +import './internal/uplinternal-miniheading.js'; +import { demoFunc } from './upl-statuspage-statsgrid.demo.js'; + +declare global { + interface HTMLElementTagNameMap { + 'upl-statuspage-statsgrid': UplStatuspageStatsgrid; + } +} + +@customElement('upl-statuspage-statsgrid') +export class UplStatuspageStatsgrid extends DeesElement { + public static demo = demoFunc; + + @property({ type: Number }) + public uptime: number = 99.99; + + @property({ type: Number }) + public avgResponseTime: number = 125; + + @property({ type: Number }) + public totalIncidents: number = 0; + + @property({ type: Number }) + public affectedServices: number = 0; + + @property({ type: Number }) + public totalServices: number = 0; + + @property({ type: String }) + public currentStatus: string = 'operational'; + + @property({ type: Boolean }) + public loading: boolean = false; + + @property({ type: String }) + public timePeriod: string = '90 days'; + + constructor() { + super(); + } + + public static styles = [ + domtools.elementBasic.staticStyles, + commonStyles, + css` + :host { + display: block; + background: transparent; + font-family: ${unsafeCSS(fonts.base)}; + color: ${colors.text.primary}; + } + + .container { + max-width: 1200px; + margin: 0 auto; + padding: 0 ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)} ${unsafeCSS(spacing.lg)}; + } + + .stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: ${unsafeCSS(spacing.md)}; + } + + .stat-card { + background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; + border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + border-radius: ${unsafeCSS(borderRadius.base)}; + padding: ${unsafeCSS(spacing.lg)}; + transition: all 0.2s ease; + } + + .stat-card:hover { + border-color: ${cssManager.bdTheme('#d1d5db', '#3f3f46')}; + box-shadow: ${cssManager.bdTheme( + '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + '0 0 0 1px rgba(255, 255, 255, 0.1)' + )}; + } + + .stat-label { + font-size: 12px; + color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 500; + margin-bottom: ${unsafeCSS(spacing.sm)}; + display: flex; + align-items: center; + gap: ${unsafeCSS(spacing.xs)}; + } + + .stat-value { + font-size: 24px; + font-weight: 600; + color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; + font-variant-numeric: tabular-nums; + line-height: 1.2; + } + + .stat-unit { + font-size: 14px; + font-weight: 400; + color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; + margin-left: 4px; + } + + .stat-change { + font-size: 12px; + margin-top: ${unsafeCSS(spacing.xs)}; + display: flex; + align-items: center; + gap: 4px; + } + + .stat-change.positive { + color: ${cssManager.bdTheme('#10b981', '#10b981')}; + } + + .stat-change.negative { + color: ${cssManager.bdTheme('#ef4444', '#ef4444')}; + } + + .stat-change.neutral { + color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; + } + + .loading-skeleton { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: ${unsafeCSS(spacing.md)}; + } + + .skeleton-card { + background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')}; + border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + border-radius: ${unsafeCSS(borderRadius.base)}; + padding: ${unsafeCSS(spacing.lg)}; + height: 100px; + } + + .skeleton-label { + height: 14px; + width: 80px; + background: ${cssManager.bdTheme('#f3f4f6', '#27272a')}; + border-radius: 4px; + margin-bottom: ${unsafeCSS(spacing.sm)}; + animation: pulse 2s infinite; + } + + .skeleton-value { + height: 28px; + width: 120px; + background: ${cssManager.bdTheme('#f3f4f6', '#27272a')}; + border-radius: 4px; + animation: pulse 2s infinite; + animation-delay: 0.1s; + } + + @keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } + } + + .status-indicator { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + } + + .status-indicator.operational { + background: ${colors.status.operational}; + } + + .status-indicator.degraded { + background: ${colors.status.degraded}; + } + + .status-indicator.partial_outage { + background: ${colors.status.partial}; + } + + .status-indicator.major_outage { + background: ${colors.status.major}; + } + + .status-indicator.maintenance { + background: ${colors.status.maintenance}; + } + + @media (max-width: 640px) { + .container { + padding: 0 ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)} ${unsafeCSS(spacing.md)}; + } + + .stats-grid { + grid-template-columns: 1fr; + gap: ${unsafeCSS(spacing.sm)}; + } + + .stat-card { + padding: ${unsafeCSS(spacing.md)}; + } + + .stat-value { + font-size: 20px; + } + } + `, + ]; + + public render(): TemplateResult { + return html` +
+ ${this.loading ? html` +
+ ${Array(4).fill(0).map(() => html` +
+
+
+
+ `)} +
+ ` : html` +
+
+
+ + Current Status +
+
+ ${this.formatStatus(this.currentStatus)} +
+
+ +
+
Uptime
+
+ ${this.uptime.toFixed(2)}% +
+
+ Last ${this.timePeriod} +
+
+ +
+
Avg Response Time
+
+ ${this.avgResponseTime}ms +
+ ${this.renderResponseChange()} +
+ +
+
Incidents
+
+ ${this.totalIncidents} +
+
+ ${this.affectedServices} of ${this.totalServices} services +
+
+
+ `} +
+ `; + } + + private formatStatus(status: string): string { + const statusMap: Record = { + operational: 'Operational', + degraded: 'Degraded', + partial_outage: 'Partial Outage', + major_outage: 'Major Outage', + maintenance: 'Maintenance', + }; + return statusMap[status] || 'Unknown'; + } + + private renderResponseChange(): TemplateResult { + // This could be enhanced with actual trend data + const trend = this.avgResponseTime < 200 ? 'positive' : this.avgResponseTime > 500 ? 'negative' : 'neutral'; + const icon = trend === 'positive' ? '↓' : trend === 'negative' ? '↑' : '→'; + + return html` +
+ ${icon} + ${trend === 'positive' ? 'Fast' : trend === 'negative' ? 'Slow' : 'Normal'} +
+ `; + } +} \ No newline at end of file diff --git a/ts_web/elements/upl-statuspage-statusdetails.ts b/ts_web/elements/upl-statuspage-statusdetails.ts index 6948464..4a7a977 100644 --- a/ts_web/elements/upl-statuspage-statusdetails.ts +++ b/ts_web/elements/upl-statuspage-statusdetails.ts @@ -85,10 +85,10 @@ export class UplStatuspageStatusdetails extends DeesElement { .mainbox .barContainer { position: relative; display: flex; - gap: 1px; + gap: 2px; padding: ${unsafeCSS(spacing.sm)}; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; - border: 1px solid ${cssManager.bdTheme('#f3f4f6', '#1f1f1f')}; + border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')}; border-radius: ${unsafeCSS(borderRadius.base)}; overflow: hidden; height: 40px; @@ -98,12 +98,14 @@ export class UplStatuspageStatusdetails extends DeesElement { flex: 1; height: 100%; cursor: pointer; - transition: opacity 0.15s ease; + transition: all 0.15s ease; position: relative; + border-radius: 2px; } .mainbox .barContainer .bar:hover { - opacity: 0.8; + transform: scaleY(1.1); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } .mainbox .barContainer .bar.operational { @@ -127,7 +129,8 @@ export class UplStatuspageStatusdetails extends DeesElement { } .mainbox .barContainer .bar.no-data { - background: ${cssManager.bdTheme('#f3f4f6', '#27272a')}; + background: ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + opacity: 0.6; } @@ -196,36 +199,6 @@ export class UplStatuspageStatusdetails extends DeesElement { 100% { background-position: -200% 0; } } - .stats-row { - display: flex; - align-items: center; - justify-content: space-between; - margin-top: ${unsafeCSS(spacing.lg)}; - padding: ${unsafeCSS(spacing.sm)} 0; - border-top: 1px solid ${cssManager.bdTheme('#f3f4f6', '#1f1f1f')}; - font-size: 12px; - } - - .stat-item { - display: flex; - align-items: center; - gap: ${unsafeCSS(spacing.sm)}; - color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; - } - - .stat-value { - font-weight: 500; - color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')}; - font-variant-numeric: tabular-nums; - } - - .status-indicator { - display: inline-block; - width: 6px; - height: 6px; - border-radius: 50%; - margin-right: 4px; - } @media (max-width: 640px) { .container { @@ -283,11 +256,6 @@ export class UplStatuspageStatusdetails extends DeesElement { `} - ${!this.loading && this.getData().length > 0 ? html` -
- ${this.renderStats()} -
- ` : ''}
`; @@ -383,45 +351,4 @@ export class UplStatuspageStatusdetails extends DeesElement { })); } - private renderStats(): TemplateResult { - const data = this.getData(); - const operational = data.filter(d => d.status === 'operational').length; - const uptime = (operational / data.length) * 100; - const avgResponseTime = data - .filter(d => d.responseTime && d.responseTime > 0) - .reduce((sum, d) => sum + (d.responseTime || 0), 0) / data.length || 0; - - const currentStatus = data[data.length - 1]?.status || 'no-data'; - - return html` -
- - ${this.formatStatus(currentStatus)} -
-
- ${uptime.toFixed(1)}% - uptime -
-
- ${avgResponseTime.toFixed(0)}ms - avg response -
- `; - } - - private getStatusColor(status: string): string { - const colors: Record = { - operational: '#22c55e', - degraded: '#fbbf24', - partial_outage: '#f87171', - major_outage: '#ef4444', - maintenance: '#60a5fa', - 'no-data': '#e5e7eb' - }; - return colors[status] || '#e5e7eb'; - } - - private formatStatus(status: string): string { - return status.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); - } } diff --git a/ts_web/elements/upl-statuspage-statusmonth.ts b/ts_web/elements/upl-statuspage-statusmonth.ts index f7ddade..c99cd1e 100644 --- a/ts_web/elements/upl-statuspage-statusmonth.ts +++ b/ts_web/elements/upl-statuspage-statusmonth.ts @@ -138,24 +138,28 @@ export class UplStatuspageStatusmonth extends DeesElement { } .statusDay.operational { - background: ${cssManager.bdTheme('#10b981', '#10b981')}; + background: #22c55e; } .statusDay.degraded { - background: ${cssManager.bdTheme('#f59e0b', '#f59e0b')}; + background: #fbbf24; } .statusDay.partial_outage { - background: ${cssManager.bdTheme('#ef4444', '#ef4444')}; + background: #f87171; } .statusDay.major_outage { - background: ${cssManager.bdTheme('#dc2626', '#dc2626')}; + background: #ef4444; + } + + .statusDay.maintenance { + background: #60a5fa; } .statusDay.no-data { - background: ${cssManager.bdTheme('#f3f4f6', '#27272a')}; - opacity: 0.5; + background: ${cssManager.bdTheme('#e5e7eb', '#27272a')}; + opacity: 0.6; } .statusDay.empty { diff --git a/ts_web/pages/statuspage-allgreen.ts b/ts_web/pages/statuspage-allgreen.ts index d119df4..6a8fae2 100644 --- a/ts_web/pages/statuspage-allgreen.ts +++ b/ts_web/pages/statuspage-allgreen.ts @@ -7,37 +7,15 @@ export const statuspageAllgreen = () => html` min-height: 100vh; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; } - - .demo-info { - padding: 20px; - background: #4CAF50; - color: white; - text-align: center; - font-family: 'Geist Sans', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - position: relative; - z-index: 1; - } - .demo-info h3 { - margin: 0 0 10px 0; - font-weight: 600; - } - .demo-info p { - margin: 0; - opacity: 0.9; - font-size: 14px; - }
-
-

All Systems Operational

-

This demo shows a status page where everything is running perfectly with no issues.

-
{ const header = wrapperElement.querySelector('upl-statuspage-header') as any; const statusBar = wrapperElement.querySelector('upl-statuspage-statusbar') as any; + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; const assetsSelector = wrapperElement.querySelector('upl-statuspage-assetsselector') as any; const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any; const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any; @@ -221,6 +199,15 @@ export const statuspageAllgreen = () => html` assetsSelector.services = services; + // Configure Stats Grid - All green metrics + statsGrid.currentStatus = 'operational'; + statsGrid.uptime = 99.98; + statsGrid.avgResponseTime = Math.round(services.reduce((sum, s) => sum + (s.responseTime || 0), 0) / services.length); + statsGrid.totalIncidents = 0; + statsGrid.affectedServices = 0; + statsGrid.totalServices = services.length; + statsGrid.timePeriod = '30 days'; + // Configure Status Details - All operational for 48 hours const generateStatusDetails = (): IStatusHistoryPoint[] => { const details: IStatusHistoryPoint[] = []; @@ -409,6 +396,7 @@ export const statuspageAllgreen = () => html` > + diff --git a/ts_web/pages/statuspage-demo.ts b/ts_web/pages/statuspage-demo.ts index 75c12a7..8a65784 100644 --- a/ts_web/pages/statuspage-demo.ts +++ b/ts_web/pages/statuspage-demo.ts @@ -7,37 +7,16 @@ export const statuspageDemo = () => html` min-height: 100vh; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; } - - .demo-info { - padding: 20px; - background: #2196F3; - color: white; - text-align: center; - font-family: 'Geist Sans', Inter, sans-serif; - } - .demo-info h3 { - margin: 0 0 10px 0; - font-size: 20px; - font-weight: 600; - } - .demo-info p { - margin: 0; - opacity: 0.9; - font-size: 14px; - }
-
-

CloudFlow Infrastructure Status Page

-

This demo shows a comprehensive status page for a cloud infrastructure provider with real-time monitoring.

-
{ const header = wrapperElement.querySelector('upl-statuspage-header') as any; const pageTitle = wrapperElement.querySelector('upl-statuspage-pagetitle') as any; const statusBar = wrapperElement.querySelector('upl-statuspage-statusbar') as any; + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; const assetsSelector = wrapperElement.querySelector('upl-statuspage-assetsselector') as any; const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any; const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any; @@ -578,6 +557,20 @@ export const statuspageDemo = () => html` incidents.currentIncidents = currentIncidents; incidents.pastIncidents = pastIncidents; + // Configure Stats Grid + const operationalCount = services.filter(s => s.currentStatus === 'operational').length; + const totalIncidents = currentIncidents.length + 3; // Current + recent past + const avgResponseTime = services.reduce((sum, s) => sum + (s.responseTime || 0), 0) / services.length; + const avgUptime = services.reduce((sum, s) => sum + (s.uptime30d || 0), 0) / services.length; + + statsGrid.currentStatus = 'degraded'; + statsGrid.uptime = avgUptime; + statsGrid.avgResponseTime = Math.round(avgResponseTime); + statsGrid.totalIncidents = totalIncidents; + statsGrid.affectedServices = services.filter(s => s.currentStatus !== 'operational').length; + statsGrid.totalServices = services.length; + statsGrid.timePeriod = '30 days'; + // Configure Footer footer.companyName = 'CloudFlow Infrastructure'; footer.legalUrl = 'https://cloudflow.example.com/legal'; @@ -644,6 +637,7 @@ export const statuspageDemo = () => html` + diff --git a/ts_web/pages/statuspage-maintenance.ts b/ts_web/pages/statuspage-maintenance.ts index 72bb5b6..c4e62ca 100644 --- a/ts_web/pages/statuspage-maintenance.ts +++ b/ts_web/pages/statuspage-maintenance.ts @@ -7,37 +7,15 @@ export const statuspageMaintenance = () => html` min-height: 100vh; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; } - - .demo-info { - padding: 20px; - background: #2196F3; - color: white; - text-align: center; - font-family: 'Geist Sans', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - position: relative; - z-index: 1; - } - .demo-info h3 { - margin: 0 0 10px 0; - font-weight: 600; - } - .demo-info p { - margin: 0; - opacity: 0.9; - font-size: 14px; - }
-
-

Scheduled Maintenance

-

This demo shows a status page during a scheduled maintenance window with some services offline.

-
{ const header = wrapperElement.querySelector('upl-statuspage-header') as any; const statusBar = wrapperElement.querySelector('upl-statuspage-statusbar') as any; + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; const assetsSelector = wrapperElement.querySelector('upl-statuspage-assetsselector') as any; const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any; const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any; @@ -252,6 +230,19 @@ export const statuspageMaintenance = () => html` assetsSelector.services = services; + // Configure Stats Grid - Maintenance mode metrics + const operationalCount = services.filter(s => s.currentStatus === 'operational').length; + const avgResponseTime = services.reduce((sum, s) => sum + (s.responseTime || 0), 0) / services.length; + const avgUptime = services.reduce((sum, s) => sum + (s.uptime30d || 0), 0) / services.length; + + statsGrid.currentStatus = 'maintenance'; + statsGrid.uptime = avgUptime; + statsGrid.avgResponseTime = Math.round(avgResponseTime); + statsGrid.totalIncidents = 1; // Just the maintenance + statsGrid.affectedServices = services.filter(s => s.currentStatus === 'maintenance').length; + statsGrid.totalServices = services.length; + statsGrid.timePeriod = '30 days'; + // Configure Status Details - Showing maintenance period const generateStatusDetails = (): IStatusHistoryPoint[] => { const details: IStatusHistoryPoint[] = []; @@ -563,6 +554,7 @@ export const statuspageMaintenance = () => html` > + diff --git a/ts_web/pages/statuspage-outage.ts b/ts_web/pages/statuspage-outage.ts index 0e95279..fac1b5c 100644 --- a/ts_web/pages/statuspage-outage.ts +++ b/ts_web/pages/statuspage-outage.ts @@ -7,37 +7,15 @@ export const statuspageOutage = () => html` min-height: 100vh; background: ${cssManager.bdTheme('#fafafa', '#0a0a0a')}; } - - .demo-info { - padding: 20px; - background: #F44336; - color: white; - text-align: center; - font-family: 'Geist Sans', Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; - position: relative; - z-index: 1; - } - .demo-info h3 { - margin: 0 0 10px 0; - font-weight: 600; - } - .demo-info p { - margin: 0; - opacity: 0.9; - font-size: 14px; - }
-
-

Major Outage Scenario

-

This demo shows a critical situation with multiple services experiencing major outages.

-
{ const header = wrapperElement.querySelector('upl-statuspage-header') as any; const statusBar = wrapperElement.querySelector('upl-statuspage-statusbar') as any; + const statsGrid = wrapperElement.querySelector('upl-statuspage-statsgrid') as any; const assetsSelector = wrapperElement.querySelector('upl-statuspage-assetsselector') as any; const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any; const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any; @@ -265,6 +243,19 @@ export const statuspageOutage = () => html` assetsSelector.services = services; + // Configure Stats Grid - Major outage metrics + const operationalCount = services.filter(s => s.currentStatus === 'operational').length; + const avgResponseTime = services.reduce((sum, s) => sum + (s.responseTime || 0), 0) / services.length; + const avgUptime = services.reduce((sum, s) => sum + (s.uptime30d || 0), 0) / services.length; + + statsGrid.currentStatus = 'major_outage'; + statsGrid.uptime = avgUptime; + statsGrid.avgResponseTime = Math.round(avgResponseTime); + statsGrid.totalIncidents = 15; // High number of incidents + statsGrid.affectedServices = services.filter(s => s.currentStatus !== 'operational').length; + statsGrid.totalServices = services.length; + statsGrid.timePeriod = '30 days'; + // Configure Status Details - Showing the outage timeline const generateStatusDetails = (): IStatusHistoryPoint[] => { const details: IStatusHistoryPoint[] = []; @@ -561,6 +552,7 @@ export const statuspageOutage = () => html` > + diff --git a/ts_web/styles/shared.styles.ts b/ts_web/styles/shared.styles.ts index 0cfefad..1e64d11 100644 --- a/ts_web/styles/shared.styles.ts +++ b/ts_web/styles/shared.styles.ts @@ -27,13 +27,13 @@ export const colors = { muted: cssManager.bdTheme('#9ca3af', '#71717a') }, - // Status colors + // Status colors - using bright colors for better visibility in both themes status: { - operational: cssManager.bdTheme('#047857', '#10b981'), - degraded: cssManager.bdTheme('#b45309', '#fbbf24'), - partial: cssManager.bdTheme('#b91c1c', '#f87171'), - major: cssManager.bdTheme('#7f1d1d', '#ef4444'), - maintenance: cssManager.bdTheme('#1e40af', '#60a5fa') + operational: cssManager.bdTheme('#22c55e', '#22c55e'), + degraded: cssManager.bdTheme('#fbbf24', '#fbbf24'), + partial: cssManager.bdTheme('#f87171', '#f87171'), + major: cssManager.bdTheme('#ef4444', '#ef4444'), + maintenance: cssManager.bdTheme('#60a5fa', '#60a5fa') } };