Files
catalog/ts_web/elements/upl-statuspage-incidents.demo.ts

1216 lines
48 KiB
TypeScript
Raw Normal View History

2025-06-29 19:55:58 +00:00
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');
}
});
2025-06-30 07:54:17 +00:00
// Handle incident subscriptions
incidents.addEventListener('incidentSubscribe', (event: CustomEvent) => {
console.log('Subscribed to incident:', event.detail);
logUpdate(`[SUBSCRIBED] User subscribed to incident "${event.detail.incidentTitle}"`);
});
incidents.addEventListener('incidentUnsubscribe', (event: CustomEvent) => {
console.log('Unsubscribed from incident:', event.detail);
logUpdate(`[UNSUBSCRIBED] User unsubscribed from incident "${event.detail.incident.title}"`);
});
2025-06-29 19:55:58 +00:00
// 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>
2025-06-30 07:54:17 +00:00
<!-- Incident Subscription Demo -->
<div class="demo-section">
<div class="demo-title">Incident Subscription Management</div>
<dees-demowrapper
.runAfterRender=${async (wrapperElement: any) => {
const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
// Sample incidents with subscription
const currentIncidents: IIncidentDetails[] = [
{
id: 'sub-001',
title: 'Payment Gateway Intermittent Failures',
status: 'monitoring',
severity: 'major',
affectedServices: ['payment-gateway', 'checkout'],
startTime: Date.now() - 2 * 60 * 60 * 1000,
impact: 'Some customers may experience payment failures during checkout',
updates: [
{
id: 'sub-u1',
timestamp: Date.now() - 2 * 60 * 60 * 1000,
status: 'investigating',
message: 'We are investigating reports of payment failures',
author: 'Payment Team'
},
{
id: 'sub-u2',
timestamp: Date.now() - 1 * 60 * 60 * 1000,
status: 'identified',
message: 'Issue identified with payment processor API rate limits',
author: 'Payment Team'
},
{
id: 'sub-u3',
timestamp: Date.now() - 30 * 60 * 1000,
status: 'monitoring',
message: 'Temporary fix applied, monitoring for stability',
author: 'Payment Team'
}
]
},
{
id: 'sub-002',
title: 'Email Delivery Delays',
status: 'identified',
severity: 'minor',
affectedServices: ['email-service'],
startTime: Date.now() - 45 * 60 * 1000,
impact: 'Transactional emails may be delayed by 5-10 minutes',
updates: [
{
id: 'sub-u4',
timestamp: Date.now() - 45 * 60 * 1000,
status: 'investigating',
message: 'Investigating email queue backlog',
author: 'Infrastructure Team'
},
{
id: 'sub-u5',
timestamp: Date.now() - 20 * 60 * 1000,
status: 'identified',
message: 'High volume causing queue delays, scaling up workers',
author: 'Infrastructure Team'
}
]
}
];
incidents.currentIncidents = currentIncidents;
incidents.pastIncidents = [];
// Pre-subscribe to first incident
incidents.subscribedIncidentIds = ['sub-001'];
// Create subscription status display
const statusDiv = document.createElement('div');
statusDiv.className = 'subscription-status';
statusDiv.style.cssText = `
margin-top: 16px;
padding: 16px;
background: #f0f9ff;
border: 1px solid #bae6fd;
border-radius: 6px;
font-size: 14px;
`;
const updateStatus = () => {
const subscribed = Array.from(incidents.subscribedIncidents || []);
statusDiv.innerHTML = `
<strong>Subscription Status:</strong><br>
${subscribed.length === 0 ?
'Not subscribed to any incidents' :
`Subscribed to ${subscribed.length} incident(s):<br>` +
subscribed.map(id => {
const incident = currentIncidents.find(i => i.id === id);
return incident ? `${incident.title}` : '';
}).filter(Boolean).join('<br>')}
`;
};
updateStatus();
wrapperElement.appendChild(statusDiv);
// Handle subscription events
incidents.addEventListener('incidentSubscribe', (event: CustomEvent) => {
console.log('Subscribed:', event.detail);
updateStatus();
// Show notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 16px;
background: #10b981;
color: white;
border-radius: 6px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 1000;
animation: slideIn 0.3s ease;
`;
notification.textContent = `✓ Subscribed to: ${event.detail.incidentTitle}`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
});
incidents.addEventListener('incidentUnsubscribe', (event: CustomEvent) => {
console.log('Unsubscribed:', event.detail);
updateStatus();
// Show notification
const notification = document.createElement('div');
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
padding: 16px;
background: #6b7280;
color: white;
border-radius: 6px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
z-index: 1000;
animation: slideIn 0.3s ease;
`;
notification.textContent = `Unsubscribed from: ${event.detail.incident.title}`;
document.body.appendChild(notification);
setTimeout(() => notification.remove(), 3000);
});
// Add info text
const infoDiv = document.createElement('div');
infoDiv.style.cssText = `
margin-top: 12px;
padding: 12px;
background: #f3f4f6;
border-radius: 4px;
font-size: 13px;
color: #6b7280;
`;
infoDiv.innerHTML = `
<strong>How it works:</strong><br>
Click on an incident to expand it<br>
Click "Subscribe to updates" to get notifications<br>
Subscribed incidents show a checkmark<br>
Click again to unsubscribe
`;
wrapperElement.appendChild(infoDiv);
}}
>
<upl-statuspage-incidents></upl-statuspage-incidents>
</dees-demowrapper>
</div>
2025-06-29 19:55:58 +00:00
</div>
`;