1031 lines
41 KiB
TypeScript
1031 lines
41 KiB
TypeScript
|
|
import { html } from '@design.estate/dees-element';
|
|||
|
|
import type { IIncidentDetails, IIncidentUpdate } from '../interfaces/index.js';
|
|||
|
|
|
|||
|
|
export const demoFunc = () => html`
|
|||
|
|
<style>
|
|||
|
|
.demo-container {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
gap: 20px;
|
|||
|
|
}
|
|||
|
|
.demo-section {
|
|||
|
|
border: 1px solid #ddd;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
padding: 20px;
|
|||
|
|
background: #f5f5f5;
|
|||
|
|
}
|
|||
|
|
.demo-title {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
color: #333;
|
|||
|
|
}
|
|||
|
|
.demo-controls {
|
|||
|
|
display: flex;
|
|||
|
|
gap: 10px;
|
|||
|
|
margin-top: 16px;
|
|||
|
|
flex-wrap: wrap;
|
|||
|
|
}
|
|||
|
|
.demo-button {
|
|||
|
|
padding: 6px 12px;
|
|||
|
|
border: 1px solid #ddd;
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: pointer;
|
|||
|
|
font-size: 13px;
|
|||
|
|
}
|
|||
|
|
.demo-button:hover {
|
|||
|
|
background: #f0f0f0;
|
|||
|
|
}
|
|||
|
|
.demo-button.active {
|
|||
|
|
background: #2196F3;
|
|||
|
|
color: white;
|
|||
|
|
border-color: #2196F3;
|
|||
|
|
}
|
|||
|
|
.demo-info {
|
|||
|
|
margin-top: 12px;
|
|||
|
|
padding: 12px;
|
|||
|
|
background: white;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 13px;
|
|||
|
|
line-height: 1.6;
|
|||
|
|
}
|
|||
|
|
.incident-stats {
|
|||
|
|
display: grid;
|
|||
|
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|||
|
|
gap: 12px;
|
|||
|
|
margin-top: 12px;
|
|||
|
|
}
|
|||
|
|
.stat-box {
|
|||
|
|
background: white;
|
|||
|
|
padding: 12px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
text-align: center;
|
|||
|
|
}
|
|||
|
|
.stat-value {
|
|||
|
|
font-size: 20px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #2196F3;
|
|||
|
|
}
|
|||
|
|
.stat-label {
|
|||
|
|
font-size: 12px;
|
|||
|
|
color: #666;
|
|||
|
|
margin-top: 4px;
|
|||
|
|
}
|
|||
|
|
.incident-log {
|
|||
|
|
margin-top: 12px;
|
|||
|
|
padding: 12px;
|
|||
|
|
background: #f9f9f9;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
font-size: 12px;
|
|||
|
|
max-height: 150px;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
font-family: monospace;
|
|||
|
|
}
|
|||
|
|
</style>
|
|||
|
|
|
|||
|
|
<div class="demo-container">
|
|||
|
|
<!-- Different Incident Types -->
|
|||
|
|
<div class="demo-section">
|
|||
|
|
<div class="demo-title">Different Incident Types and Severities</div>
|
|||
|
|
<dees-demowrapper
|
|||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
|||
|
|
const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
|
|||
|
|
|
|||
|
|
// Generate different types of incidents
|
|||
|
|
const incidentScenarios = {
|
|||
|
|
critical: {
|
|||
|
|
name: 'Critical Outage',
|
|||
|
|
incidents: [
|
|||
|
|
{
|
|||
|
|
id: 'crit-001',
|
|||
|
|
title: 'Complete Platform Outage',
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
severity: 'critical' as const,
|
|||
|
|
affectedServices: ['api-gateway', 'web-server', 'database', 'cdn'],
|
|||
|
|
startTime: Date.now() - 15 * 60 * 1000,
|
|||
|
|
impact: 'All services are currently unavailable. We are working on immediate resolution.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-c1',
|
|||
|
|
timestamp: Date.now() - 15 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Multiple alerts triggered. All hands on deck.',
|
|||
|
|
author: 'Incident Commander'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-c2',
|
|||
|
|
timestamp: Date.now() - 10 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Root cause appears to be datacenter power failure.',
|
|||
|
|
author: 'Infrastructure Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
major: {
|
|||
|
|
name: 'Major Issues',
|
|||
|
|
incidents: [
|
|||
|
|
{
|
|||
|
|
id: 'maj-001',
|
|||
|
|
title: 'Database Performance Degradation',
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
severity: 'major' as const,
|
|||
|
|
affectedServices: ['database', 'api-gateway'],
|
|||
|
|
startTime: Date.now() - 45 * 60 * 1000,
|
|||
|
|
impact: 'Significant slowdown in database queries affecting 60% of API requests.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-m1',
|
|||
|
|
timestamp: Date.now() - 45 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Investigating database performance issues.',
|
|||
|
|
author: 'Database Team'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-m2',
|
|||
|
|
timestamp: Date.now() - 30 * 60 * 1000,
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'Identified runaway queries causing resource exhaustion.',
|
|||
|
|
author: 'Database Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'maj-002',
|
|||
|
|
title: 'CDN Routing Issues',
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
severity: 'major' as const,
|
|||
|
|
affectedServices: ['cdn'],
|
|||
|
|
startTime: Date.now() - 2 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Assets loading slowly for users in EU region.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-m3',
|
|||
|
|
timestamp: Date.now() - 2 * 60 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Reports of slow asset loading from EU users.',
|
|||
|
|
author: 'CDN Team'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-m4',
|
|||
|
|
timestamp: Date.now() - 90 * 60 * 1000,
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'CDN provider experiencing issues with EU PoPs.',
|
|||
|
|
author: 'CDN Team'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-m5',
|
|||
|
|
timestamp: Date.now() - 30 * 60 * 1000,
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
message: 'Rerouted traffic to alternate PoPs. Monitoring performance.',
|
|||
|
|
author: 'CDN Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
minor: {
|
|||
|
|
name: 'Minor Incidents',
|
|||
|
|
incidents: [
|
|||
|
|
{
|
|||
|
|
id: 'min-001',
|
|||
|
|
title: 'Delayed Webhook Deliveries',
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
severity: 'minor' as const,
|
|||
|
|
affectedServices: ['webhook-service'],
|
|||
|
|
startTime: Date.now() - 3 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Webhooks experiencing 2-5 minute delays.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-n1',
|
|||
|
|
timestamp: Date.now() - 3 * 60 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Investigating webhook delivery delays.',
|
|||
|
|
author: 'API Team'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-n2',
|
|||
|
|
timestamp: Date.now() - 2 * 60 * 60 * 1000,
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
message: 'Queue processing optimized. Delays reducing.',
|
|||
|
|
author: 'API Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'min-002',
|
|||
|
|
title: 'Search Indexing Lag',
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
severity: 'minor' as const,
|
|||
|
|
affectedServices: ['search-service'],
|
|||
|
|
startTime: Date.now() - 4 * 60 * 60 * 1000,
|
|||
|
|
impact: 'New content taking up to 30 minutes to appear in search results.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-n3',
|
|||
|
|
timestamp: Date.now() - 4 * 60 * 60 * 1000,
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'Search indexing pipeline experiencing backlog.',
|
|||
|
|
author: 'Search Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
maintenance: {
|
|||
|
|
name: 'Maintenance',
|
|||
|
|
incidents: [
|
|||
|
|
{
|
|||
|
|
id: 'maint-001',
|
|||
|
|
title: 'Scheduled Database Maintenance',
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
severity: 'maintenance' as const,
|
|||
|
|
affectedServices: ['database'],
|
|||
|
|
startTime: Date.now() + 24 * 60 * 60 * 1000,
|
|||
|
|
endTime: Date.now() + 26 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Database will be in read-only mode during maintenance window.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-mt1',
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'Scheduled maintenance for database version upgrade.',
|
|||
|
|
author: 'Database Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'maint-002',
|
|||
|
|
title: 'Network Equipment Upgrade',
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
severity: 'maintenance' as const,
|
|||
|
|
affectedServices: ['cdn', 'api-gateway'],
|
|||
|
|
startTime: Date.now() + 48 * 60 * 60 * 1000,
|
|||
|
|
endTime: Date.now() + 49 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Brief connectivity interruptions expected during upgrade window.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-mt2',
|
|||
|
|
timestamp: Date.now() - 2 * 60 * 60 * 1000,
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'Core network switches scheduled for firmware upgrade.',
|
|||
|
|
author: 'Network Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Initial setup
|
|||
|
|
let currentScenario = 'major';
|
|||
|
|
incidents.currentIncidents = incidentScenarios[currentScenario].incidents;
|
|||
|
|
incidents.pastIncidents = [];
|
|||
|
|
|
|||
|
|
// Create controls
|
|||
|
|
const controls = document.createElement('div');
|
|||
|
|
controls.className = 'demo-controls';
|
|||
|
|
|
|||
|
|
Object.entries(incidentScenarios).forEach(([key, scenario]) => {
|
|||
|
|
const button = document.createElement('button');
|
|||
|
|
button.className = 'demo-button' + (key === currentScenario ? ' active' : '');
|
|||
|
|
button.textContent = scenario.name;
|
|||
|
|
button.onclick = () => {
|
|||
|
|
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
|||
|
|
button.classList.add('active');
|
|||
|
|
|
|||
|
|
currentScenario = key;
|
|||
|
|
incidents.loading = true;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
incidents.currentIncidents = scenario.incidents;
|
|||
|
|
incidents.loading = false;
|
|||
|
|
updateStats();
|
|||
|
|
}, 500);
|
|||
|
|
};
|
|||
|
|
controls.appendChild(button);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
wrapperElement.appendChild(controls);
|
|||
|
|
|
|||
|
|
// Add statistics
|
|||
|
|
const statsDiv = document.createElement('div');
|
|||
|
|
statsDiv.className = 'incident-stats';
|
|||
|
|
wrapperElement.appendChild(statsDiv);
|
|||
|
|
|
|||
|
|
const updateStats = () => {
|
|||
|
|
const current = incidents.currentIncidents || [];
|
|||
|
|
const critical = current.filter(i => i.severity === 'critical').length;
|
|||
|
|
const major = current.filter(i => i.severity === 'major').length;
|
|||
|
|
const minor = current.filter(i => i.severity === 'minor').length;
|
|||
|
|
const maintenance = current.filter(i => i.severity === 'maintenance').length;
|
|||
|
|
|
|||
|
|
statsDiv.innerHTML = `
|
|||
|
|
<div class="stat-box">
|
|||
|
|
<div class="stat-value">${critical}</div>
|
|||
|
|
<div class="stat-label">Critical</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-box">
|
|||
|
|
<div class="stat-value">${major}</div>
|
|||
|
|
<div class="stat-label">Major</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-box">
|
|||
|
|
<div class="stat-value">${minor}</div>
|
|||
|
|
<div class="stat-label">Minor</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="stat-box">
|
|||
|
|
<div class="stat-value">${maintenance}</div>
|
|||
|
|
<div class="stat-label">Maintenance</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
updateStats();
|
|||
|
|
|
|||
|
|
// Handle incident clicks
|
|||
|
|
incidents.addEventListener('incidentClick', (event: CustomEvent) => {
|
|||
|
|
alert(`Incident Details:\n\nTitle: ${event.detail.incident.title}\nSeverity: ${event.detail.incident.severity}\nStatus: ${event.detail.incident.status}\nImpact: ${event.detail.incident.impact}`);
|
|||
|
|
});
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<upl-statuspage-incidents></upl-statuspage-incidents>
|
|||
|
|
</dees-demowrapper>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Real-time Incident Progression -->
|
|||
|
|
<div class="demo-section">
|
|||
|
|
<div class="demo-title">Real-time Incident Progression</div>
|
|||
|
|
<dees-demowrapper
|
|||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
|||
|
|
const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
|
|||
|
|
|
|||
|
|
// Create a progressing incident
|
|||
|
|
const progressingIncident: IIncidentDetails = {
|
|||
|
|
id: 'prog-001',
|
|||
|
|
title: 'Live Incident: Payment Processing Delays',
|
|||
|
|
status: 'investigating',
|
|||
|
|
severity: 'major',
|
|||
|
|
affectedServices: ['payment-gateway', 'api-gateway'],
|
|||
|
|
startTime: Date.now(),
|
|||
|
|
impact: 'Payment processing experiencing intermittent failures.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-p1',
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
status: 'investigating',
|
|||
|
|
message: 'We are investigating reports of payment processing failures.',
|
|||
|
|
author: 'Payment Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
incidents.currentIncidents = [progressingIncident];
|
|||
|
|
incidents.pastIncidents = [];
|
|||
|
|
|
|||
|
|
// Progression timeline
|
|||
|
|
const progressionSteps = [
|
|||
|
|
{
|
|||
|
|
delay: 10000,
|
|||
|
|
update: {
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Initial investigation shows 30% of payment attempts failing.',
|
|||
|
|
author: 'Payment Team'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
delay: 20000,
|
|||
|
|
update: {
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'Root cause identified: Third-party payment processor API rate limiting.',
|
|||
|
|
author: 'Engineering Team'
|
|||
|
|
},
|
|||
|
|
incidentUpdate: { status: 'identified' as const }
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
delay: 30000,
|
|||
|
|
update: {
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
message: 'Implemented retry logic and request throttling. Success rate improving.',
|
|||
|
|
author: 'Engineering Team'
|
|||
|
|
},
|
|||
|
|
incidentUpdate: { status: 'monitoring' as const }
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
delay: 40000,
|
|||
|
|
update: {
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
message: 'Payment success rate now at 95%. Continuing to monitor.',
|
|||
|
|
author: 'Payment Team'
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
delay: 50000,
|
|||
|
|
update: {
|
|||
|
|
status: 'resolved' as const,
|
|||
|
|
message: 'All systems back to normal. Payment processing fully restored.',
|
|||
|
|
author: 'Payment Team'
|
|||
|
|
},
|
|||
|
|
incidentUpdate: {
|
|||
|
|
status: 'resolved' as const,
|
|||
|
|
endTime: Date.now() + 50000,
|
|||
|
|
resolution: 'Implemented rate limiting and retry logic to prevent future occurrences.'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
// Progress through steps
|
|||
|
|
progressionSteps.forEach((step, index) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const newUpdate: IIncidentUpdate = {
|
|||
|
|
id: `upd-p${index + 2}`,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
...step.update
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
progressingIncident.updates.push(newUpdate);
|
|||
|
|
|
|||
|
|
if (step.incidentUpdate) {
|
|||
|
|
Object.assign(progressingIncident, step.incidentUpdate);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (progressingIncident.status === 'resolved') {
|
|||
|
|
// Move to past incidents
|
|||
|
|
incidents.pastIncidents = [progressingIncident, ...incidents.pastIncidents];
|
|||
|
|
incidents.currentIncidents = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
incidents.requestUpdate();
|
|||
|
|
logUpdate(`[${step.update.status.toUpperCase()}] ${step.update.message}`);
|
|||
|
|
}, step.delay);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Create controls
|
|||
|
|
const controls = document.createElement('div');
|
|||
|
|
controls.className = 'demo-controls';
|
|||
|
|
controls.innerHTML = `
|
|||
|
|
<button class="demo-button" id="addUpdate">Add Custom Update</button>
|
|||
|
|
<button class="demo-button" id="escalate">Escalate to Critical</button>
|
|||
|
|
<button class="demo-button" id="resolve">Resolve Immediately</button>
|
|||
|
|
`;
|
|||
|
|
wrapperElement.appendChild(controls);
|
|||
|
|
|
|||
|
|
controls.querySelector('#addUpdate')?.addEventListener('click', () => {
|
|||
|
|
if (progressingIncident.status !== 'resolved') {
|
|||
|
|
const message = prompt('Enter update message:');
|
|||
|
|
if (message) {
|
|||
|
|
progressingIncident.updates.push({
|
|||
|
|
id: `upd-custom-${Date.now()}`,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
status: progressingIncident.status,
|
|||
|
|
message,
|
|||
|
|
author: 'Manual Update'
|
|||
|
|
});
|
|||
|
|
incidents.requestUpdate();
|
|||
|
|
logUpdate(`[MANUAL] ${message}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
controls.querySelector('#escalate')?.addEventListener('click', () => {
|
|||
|
|
if (progressingIncident.status !== 'resolved') {
|
|||
|
|
progressingIncident.severity = 'critical';
|
|||
|
|
progressingIncident.updates.push({
|
|||
|
|
id: `upd-esc-${Date.now()}`,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
status: progressingIncident.status,
|
|||
|
|
message: 'Incident escalated to CRITICAL severity.',
|
|||
|
|
author: 'Incident Commander'
|
|||
|
|
});
|
|||
|
|
incidents.requestUpdate();
|
|||
|
|
logUpdate('[ESCALATED] Incident severity raised to CRITICAL');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
controls.querySelector('#resolve')?.addEventListener('click', () => {
|
|||
|
|
if (progressingIncident.status !== 'resolved') {
|
|||
|
|
progressingIncident.status = 'resolved';
|
|||
|
|
progressingIncident.endTime = Date.now();
|
|||
|
|
progressingIncident.resolution = 'Manually resolved.';
|
|||
|
|
progressingIncident.updates.push({
|
|||
|
|
id: `upd-res-${Date.now()}`,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Incident manually resolved.',
|
|||
|
|
author: 'Manual Resolution'
|
|||
|
|
});
|
|||
|
|
incidents.pastIncidents = [progressingIncident, ...incidents.pastIncidents];
|
|||
|
|
incidents.currentIncidents = [];
|
|||
|
|
incidents.requestUpdate();
|
|||
|
|
logUpdate('[RESOLVED] Incident manually resolved');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Add update log
|
|||
|
|
const logDiv = document.createElement('div');
|
|||
|
|
logDiv.className = 'incident-log';
|
|||
|
|
logDiv.innerHTML = '<strong>Incident Timeline:</strong><br>';
|
|||
|
|
wrapperElement.appendChild(logDiv);
|
|||
|
|
|
|||
|
|
const logUpdate = (message: string) => {
|
|||
|
|
const time = new Date().toLocaleTimeString();
|
|||
|
|
logDiv.innerHTML += `[${time}] ${message}<br>`;
|
|||
|
|
logDiv.scrollTop = logDiv.scrollHeight;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
logUpdate('[CREATED] New incident: Payment Processing Delays');
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<upl-statuspage-incidents></upl-statuspage-incidents>
|
|||
|
|
</dees-demowrapper>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Historical Incident Patterns -->
|
|||
|
|
<div class="demo-section">
|
|||
|
|
<div class="demo-title">Historical Incident Patterns</div>
|
|||
|
|
<dees-demowrapper
|
|||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
|||
|
|
const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
|
|||
|
|
|
|||
|
|
// Generate historical patterns
|
|||
|
|
const generateHistoricalIncidents = (pattern: 'frequent' | 'seasonal' | 'improving' | 'stable'): IIncidentDetails[] => {
|
|||
|
|
const incidents: IIncidentDetails[] = [];
|
|||
|
|
const now = Date.now();
|
|||
|
|
|
|||
|
|
switch (pattern) {
|
|||
|
|
case 'frequent':
|
|||
|
|
// Many incidents in recent history
|
|||
|
|
for (let i = 0; i < 20; i++) {
|
|||
|
|
const daysAgo = Math.floor(Math.random() * 30);
|
|||
|
|
const duration = Math.floor(Math.random() * 4 + 1) * 60 * 60 * 1000;
|
|||
|
|
const startTime = now - daysAgo * 24 * 60 * 60 * 1000;
|
|||
|
|
|
|||
|
|
incidents.push({
|
|||
|
|
id: `freq-${i}`,
|
|||
|
|
title: `Service Disruption #${20 - i}`,
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: Math.random() < 0.3 ? 'major' : 'minor',
|
|||
|
|
affectedServices: ['api-gateway', 'database'].slice(0, Math.floor(Math.random() * 2) + 1),
|
|||
|
|
startTime,
|
|||
|
|
endTime: startTime + duration,
|
|||
|
|
impact: 'Service degradation affecting subset of users.',
|
|||
|
|
rootCause: 'Various infrastructure issues.',
|
|||
|
|
resolution: 'Applied temporary fixes.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: `upd-${i}-1`,
|
|||
|
|
timestamp: startTime,
|
|||
|
|
status: 'investigating',
|
|||
|
|
message: 'Investigating service issues.',
|
|||
|
|
author: 'Ops Team'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: `upd-${i}-2`,
|
|||
|
|
timestamp: startTime + duration,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Issue resolved.',
|
|||
|
|
author: 'Ops Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'seasonal':
|
|||
|
|
// Incidents clustered around specific times
|
|||
|
|
const peakDays = [7, 14, 21, 28]; // Weekly pattern
|
|||
|
|
peakDays.forEach((day, index) => {
|
|||
|
|
for (let j = 0; j < 3; j++) {
|
|||
|
|
const startTime = now - day * 24 * 60 * 60 * 1000 + (j - 1) * 60 * 60 * 1000;
|
|||
|
|
incidents.push({
|
|||
|
|
id: `seas-${index}-${j}`,
|
|||
|
|
title: `Peak Traffic Overload`,
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: 'minor',
|
|||
|
|
affectedServices: ['api-gateway'],
|
|||
|
|
startTime,
|
|||
|
|
endTime: startTime + 30 * 60 * 1000,
|
|||
|
|
impact: 'Slow response times during peak hours.',
|
|||
|
|
rootCause: 'Insufficient capacity for peak traffic.',
|
|||
|
|
resolution: 'Auto-scaling adjusted.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: `upd-s-${index}-${j}`,
|
|||
|
|
timestamp: startTime,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Peak traffic handled by auto-scaling.',
|
|||
|
|
author: 'Auto-remediation'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'improving':
|
|||
|
|
// Fewer incidents over time
|
|||
|
|
for (let i = 0; i < 15; i++) {
|
|||
|
|
const daysAgo = 30 - i * 2;
|
|||
|
|
if (Math.random() < (15 - i) / 15) {
|
|||
|
|
const startTime = now - daysAgo * 24 * 60 * 60 * 1000;
|
|||
|
|
incidents.push({
|
|||
|
|
id: `imp-${i}`,
|
|||
|
|
title: `System Issue #${i + 1}`,
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: i < 5 ? 'major' : 'minor',
|
|||
|
|
affectedServices: ['database'],
|
|||
|
|
startTime,
|
|||
|
|
endTime: startTime + 2 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Database performance issues.',
|
|||
|
|
rootCause: 'Legacy infrastructure limitations.',
|
|||
|
|
resolution: 'Infrastructure improvements implemented.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: `upd-imp-${i}`,
|
|||
|
|
timestamp: startTime,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Resolved with infrastructure improvements.',
|
|||
|
|
author: 'Infrastructure Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
|
|||
|
|
case 'stable':
|
|||
|
|
// Very few incidents, all minor
|
|||
|
|
for (let i = 0; i < 3; i++) {
|
|||
|
|
const daysAgo = Math.floor(Math.random() * 30);
|
|||
|
|
const startTime = now - daysAgo * 24 * 60 * 60 * 1000;
|
|||
|
|
incidents.push({
|
|||
|
|
id: `stab-${i}`,
|
|||
|
|
title: ['Routine Maintenance', 'Minor Configuration Update', 'Planned Restart'][i],
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: 'maintenance',
|
|||
|
|
affectedServices: ['web-server'],
|
|||
|
|
startTime,
|
|||
|
|
endTime: startTime + 15 * 60 * 1000,
|
|||
|
|
impact: 'No user impact.',
|
|||
|
|
resolution: 'Completed as planned.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: `upd-stab-${i}`,
|
|||
|
|
timestamp: startTime,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Maintenance completed successfully.',
|
|||
|
|
author: 'Maintenance Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return incidents.sort((a, b) => b.startTime - a.startTime);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Initial setup
|
|||
|
|
let currentPattern: 'frequent' | 'seasonal' | 'improving' | 'stable' = 'frequent';
|
|||
|
|
incidents.currentIncidents = [];
|
|||
|
|
incidents.pastIncidents = generateHistoricalIncidents(currentPattern);
|
|||
|
|
|
|||
|
|
// Create pattern controls
|
|||
|
|
const controls = document.createElement('div');
|
|||
|
|
controls.className = 'demo-controls';
|
|||
|
|
|
|||
|
|
const patterns = [
|
|||
|
|
{ key: 'frequent', label: 'Frequent Incidents' },
|
|||
|
|
{ key: 'seasonal', label: 'Seasonal Pattern' },
|
|||
|
|
{ key: 'improving', label: 'Improving Trend' },
|
|||
|
|
{ key: 'stable', label: 'Stable System' }
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
patterns.forEach((pattern) => {
|
|||
|
|
const button = document.createElement('button');
|
|||
|
|
button.className = 'demo-button' + (pattern.key === currentPattern ? ' active' : '');
|
|||
|
|
button.textContent = pattern.label;
|
|||
|
|
button.onclick = () => {
|
|||
|
|
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
|||
|
|
button.classList.add('active');
|
|||
|
|
|
|||
|
|
currentPattern = pattern.key as any;
|
|||
|
|
incidents.loading = true;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
incidents.pastIncidents = generateHistoricalIncidents(currentPattern as any);
|
|||
|
|
incidents.loading = false;
|
|||
|
|
}, 500);
|
|||
|
|
};
|
|||
|
|
controls.appendChild(button);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
wrapperElement.appendChild(controls);
|
|||
|
|
|
|||
|
|
// Add info about the pattern
|
|||
|
|
const info = document.createElement('div');
|
|||
|
|
info.className = 'demo-info';
|
|||
|
|
info.innerHTML = `
|
|||
|
|
<strong>Pattern Description:</strong><br>
|
|||
|
|
<strong>Frequent:</strong> Many incidents indicating stability issues<br>
|
|||
|
|
<strong>Seasonal:</strong> Incidents following a predictable pattern<br>
|
|||
|
|
<strong>Improving:</strong> Decreasing incident frequency over time<br>
|
|||
|
|
<strong>Stable:</strong> Minimal incidents, mostly maintenance
|
|||
|
|
`;
|
|||
|
|
wrapperElement.appendChild(info);
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<upl-statuspage-incidents></upl-statuspage-incidents>
|
|||
|
|
</dees-demowrapper>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Edge Cases -->
|
|||
|
|
<div class="demo-section">
|
|||
|
|
<div class="demo-title">Edge Cases and Special Scenarios</div>
|
|||
|
|
<dees-demowrapper
|
|||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
|||
|
|
const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
|
|||
|
|
|
|||
|
|
const edgeCases = {
|
|||
|
|
none: {
|
|||
|
|
name: 'No Incidents',
|
|||
|
|
current: [],
|
|||
|
|
past: []
|
|||
|
|
},
|
|||
|
|
manyActive: {
|
|||
|
|
name: 'Many Active',
|
|||
|
|
current: Array.from({ length: 8 }, (_, i) => ({
|
|||
|
|
id: `many-${i}`,
|
|||
|
|
title: `Concurrent Issue #${i + 1}`,
|
|||
|
|
status: ['investigating', 'identified', 'monitoring'][i % 3] as any,
|
|||
|
|
severity: ['critical', 'major', 'minor'][i % 3] as any,
|
|||
|
|
affectedServices: [`service-${i}`],
|
|||
|
|
startTime: Date.now() - (i + 1) * 30 * 60 * 1000,
|
|||
|
|
impact: `Impact on service ${i + 1}`,
|
|||
|
|
updates: [{
|
|||
|
|
id: `upd-many-${i}`,
|
|||
|
|
timestamp: Date.now() - (i + 1) * 30 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'Investigating issue.',
|
|||
|
|
author: 'Team'
|
|||
|
|
}]
|
|||
|
|
})),
|
|||
|
|
past: []
|
|||
|
|
},
|
|||
|
|
longRunning: {
|
|||
|
|
name: 'Long Running',
|
|||
|
|
current: [{
|
|||
|
|
id: 'long-001',
|
|||
|
|
title: 'Persistent Infrastructure Issue',
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
severity: 'major' as const,
|
|||
|
|
affectedServices: ['database', 'api-gateway'],
|
|||
|
|
startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days ago
|
|||
|
|
impact: 'Intermittent issues affecting 5-10% of requests.',
|
|||
|
|
updates: Array.from({ length: 50 }, (_, i) => ({
|
|||
|
|
id: `upd-long-${i}`,
|
|||
|
|
timestamp: Date.now() - 7 * 24 * 60 * 60 * 1000 + i * 3 * 60 * 60 * 1000,
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
message: `Update #${i + 1}: Continuing to monitor. Partial mitigation in place.`,
|
|||
|
|
author: 'Ops Team'
|
|||
|
|
}))
|
|||
|
|
}],
|
|||
|
|
past: []
|
|||
|
|
},
|
|||
|
|
complexUpdates: {
|
|||
|
|
name: 'Complex Updates',
|
|||
|
|
current: [{
|
|||
|
|
id: 'complex-001',
|
|||
|
|
title: 'Multi-phase Incident Resolution',
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
severity: 'critical' as const,
|
|||
|
|
affectedServices: ['api-gateway', 'database', 'cdn', 'email-service'],
|
|||
|
|
startTime: Date.now() - 4 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Multiple services experiencing cascading failures.',
|
|||
|
|
updates: [
|
|||
|
|
{
|
|||
|
|
id: 'upd-c1',
|
|||
|
|
timestamp: Date.now() - 4 * 60 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: '🚨 CRITICAL: Multiple service alerts triggered simultaneously.',
|
|||
|
|
author: 'Monitoring System'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-c2',
|
|||
|
|
timestamp: Date.now() - 3.5 * 60 * 60 * 1000,
|
|||
|
|
status: 'investigating' as const,
|
|||
|
|
message: 'All hands on deck. Incident commander taking charge.\n\n- API Gateway: DOWN\n- Database: DEGRADED\n- CDN: PARTIAL OUTAGE\n- Email: DELAYED',
|
|||
|
|
author: 'Incident Commander'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-c3',
|
|||
|
|
timestamp: Date.now() - 3 * 60 * 60 * 1000,
|
|||
|
|
status: 'identified' as const,
|
|||
|
|
message: 'Root cause identified: Cascading failure triggered by database connection pool exhaustion.\n\nAction items:\n1. Restart connection pools\n2. Implement circuit breakers\n3. Scale up database replicas',
|
|||
|
|
author: 'Engineering Lead'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'upd-c4',
|
|||
|
|
timestamp: Date.now() - 2 * 60 * 60 * 1000,
|
|||
|
|
status: 'monitoring' as const,
|
|||
|
|
message: 'Partial recovery achieved:\n✅ API Gateway: RESTORED\n⚠️ Database: RECOVERING\n✅ CDN: OPERATIONAL\n⚠️ Email: CATCHING UP\n\nContinuing remediation efforts.',
|
|||
|
|
author: 'Ops Team'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}],
|
|||
|
|
past: []
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Initial setup
|
|||
|
|
let currentCase = 'none';
|
|||
|
|
incidents.currentIncidents = edgeCases[currentCase].current;
|
|||
|
|
incidents.pastIncidents = edgeCases[currentCase].past;
|
|||
|
|
|
|||
|
|
// Create controls
|
|||
|
|
const controls = document.createElement('div');
|
|||
|
|
controls.className = 'demo-controls';
|
|||
|
|
|
|||
|
|
Object.entries(edgeCases).forEach(([key, scenario]) => {
|
|||
|
|
const button = document.createElement('button');
|
|||
|
|
button.className = 'demo-button' + (key === currentCase ? ' active' : '');
|
|||
|
|
button.textContent = scenario.name;
|
|||
|
|
button.onclick = () => {
|
|||
|
|
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
|||
|
|
button.classList.add('active');
|
|||
|
|
|
|||
|
|
currentCase = key;
|
|||
|
|
incidents.loading = true;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
incidents.currentIncidents = scenario.current;
|
|||
|
|
incidents.pastIncidents = scenario.past;
|
|||
|
|
incidents.loading = false;
|
|||
|
|
}, 300);
|
|||
|
|
};
|
|||
|
|
controls.appendChild(button);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
wrapperElement.appendChild(controls);
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<upl-statuspage-incidents></upl-statuspage-incidents>
|
|||
|
|
</dees-demowrapper>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Loading and Filtering -->
|
|||
|
|
<div class="demo-section">
|
|||
|
|
<div class="demo-title">Loading States and Filtering</div>
|
|||
|
|
<dees-demowrapper
|
|||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
|||
|
|
const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
|
|||
|
|
|
|||
|
|
// Start with loading
|
|||
|
|
incidents.loading = true;
|
|||
|
|
|
|||
|
|
// Generate mixed incidents for filtering
|
|||
|
|
const allIncidents: IIncidentDetails[] = [
|
|||
|
|
{
|
|||
|
|
id: 'filter-1',
|
|||
|
|
title: 'Critical Database Failure',
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: 'critical',
|
|||
|
|
affectedServices: ['database'],
|
|||
|
|
startTime: Date.now() - 2 * 24 * 60 * 60 * 1000,
|
|||
|
|
endTime: Date.now() - 2 * 24 * 60 * 60 * 1000 + 4 * 60 * 60 * 1000,
|
|||
|
|
impact: 'Complete database unavailability.',
|
|||
|
|
resolution: 'Failed over to secondary cluster.',
|
|||
|
|
updates: [{
|
|||
|
|
id: 'u1',
|
|||
|
|
timestamp: Date.now() - 2 * 24 * 60 * 60 * 1000,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Database restored.',
|
|||
|
|
author: 'DBA Team'
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'filter-2',
|
|||
|
|
title: 'API Rate Limiting Issues',
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: 'major',
|
|||
|
|
affectedServices: ['api-gateway'],
|
|||
|
|
startTime: Date.now() - 5 * 24 * 60 * 60 * 1000,
|
|||
|
|
endTime: Date.now() - 5 * 24 * 60 * 60 * 1000 + 2 * 60 * 60 * 1000,
|
|||
|
|
impact: 'API requests throttled for some users.',
|
|||
|
|
resolution: 'Rate limits adjusted.',
|
|||
|
|
updates: [{
|
|||
|
|
id: 'u2',
|
|||
|
|
timestamp: Date.now() - 5 * 24 * 60 * 60 * 1000,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Rate limiting resolved.',
|
|||
|
|
author: 'API Team'
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'filter-3',
|
|||
|
|
title: 'Email Delays',
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: 'minor',
|
|||
|
|
affectedServices: ['email-service'],
|
|||
|
|
startTime: Date.now() - 10 * 24 * 60 * 60 * 1000,
|
|||
|
|
endTime: Date.now() - 10 * 24 * 60 * 60 * 1000 + 30 * 60 * 1000,
|
|||
|
|
impact: 'Emails delayed by 5-10 minutes.',
|
|||
|
|
resolution: 'Queue cleared.',
|
|||
|
|
updates: [{
|
|||
|
|
id: 'u3',
|
|||
|
|
timestamp: Date.now() - 10 * 24 * 60 * 60 * 1000,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Email queue cleared.',
|
|||
|
|
author: 'Email Team'
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'filter-4',
|
|||
|
|
title: 'Scheduled Maintenance',
|
|||
|
|
status: 'resolved',
|
|||
|
|
severity: 'maintenance',
|
|||
|
|
affectedServices: ['web-server'],
|
|||
|
|
startTime: Date.now() - 15 * 24 * 60 * 60 * 1000,
|
|||
|
|
endTime: Date.now() - 15 * 24 * 60 * 60 * 1000 + 60 * 60 * 1000,
|
|||
|
|
impact: 'Brief downtime during upgrade.',
|
|||
|
|
resolution: 'Upgrade completed.',
|
|||
|
|
updates: [{
|
|||
|
|
id: 'u4',
|
|||
|
|
timestamp: Date.now() - 15 * 24 * 60 * 60 * 1000,
|
|||
|
|
status: 'resolved',
|
|||
|
|
message: 'Maintenance completed.',
|
|||
|
|
author: 'Ops Team'
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const controls = document.createElement('div');
|
|||
|
|
controls.className = 'demo-controls';
|
|||
|
|
controls.innerHTML = `
|
|||
|
|
<button class="demo-button" id="toggleLoading">Toggle Loading</button>
|
|||
|
|
<button class="demo-button" id="loadSuccess">Load Successfully</button>
|
|||
|
|
<button class="demo-button" id="loadError">Simulate Error</button>
|
|||
|
|
<button class="demo-button" id="filterCritical">Show Critical Only</button>
|
|||
|
|
<button class="demo-button" id="showAll">Show All</button>
|
|||
|
|
`;
|
|||
|
|
wrapperElement.appendChild(controls);
|
|||
|
|
|
|||
|
|
controls.querySelector('#toggleLoading')?.addEventListener('click', () => {
|
|||
|
|
incidents.loading = !incidents.loading;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
controls.querySelector('#loadSuccess')?.addEventListener('click', () => {
|
|||
|
|
incidents.loading = true;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
incidents.currentIncidents = [
|
|||
|
|
{
|
|||
|
|
id: 'current-1',
|
|||
|
|
title: 'Ongoing Investigation',
|
|||
|
|
status: 'investigating',
|
|||
|
|
severity: 'minor',
|
|||
|
|
affectedServices: ['api-gateway'],
|
|||
|
|
startTime: Date.now() - 30 * 60 * 1000,
|
|||
|
|
impact: 'Elevated error rates on API endpoints.',
|
|||
|
|
updates: [{
|
|||
|
|
id: 'u-current',
|
|||
|
|
timestamp: Date.now() - 30 * 60 * 1000,
|
|||
|
|
status: 'investigating',
|
|||
|
|
message: 'Investigating elevated error rates.',
|
|||
|
|
author: 'API Team'
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
];
|
|||
|
|
incidents.pastIncidents = allIncidents;
|
|||
|
|
incidents.loading = false;
|
|||
|
|
}, 1000);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
controls.querySelector('#loadError')?.addEventListener('click', () => {
|
|||
|
|
incidents.loading = true;
|
|||
|
|
setTimeout(() => {
|
|||
|
|
incidents.loading = false;
|
|||
|
|
incidents.currentIncidents = [];
|
|||
|
|
incidents.pastIncidents = [];
|
|||
|
|
incidents.errorMessage = 'Failed to load incident data';
|
|||
|
|
}, 1500);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
controls.querySelector('#filterCritical')?.addEventListener('click', () => {
|
|||
|
|
incidents.pastIncidents = allIncidents.filter(i => i.severity === 'critical');
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
controls.querySelector('#showAll')?.addEventListener('click', () => {
|
|||
|
|
incidents.pastIncidents = allIncidents;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// Initial load after delay
|
|||
|
|
setTimeout(() => {
|
|||
|
|
incidents.currentIncidents = [];
|
|||
|
|
incidents.pastIncidents = allIncidents;
|
|||
|
|
incidents.loading = false;
|
|||
|
|
}, 2000);
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<upl-statuspage-incidents></upl-statuspage-incidents>
|
|||
|
|
</dees-demowrapper>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
`;
|