754 lines
28 KiB
TypeScript
754 lines
28 KiB
TypeScript
|
|
import { html } from '@design.estate/dees-element';
|
||
|
|
import type { IStatusHistoryPoint } 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;
|
||
|
|
}
|
||
|
|
.stats-grid {
|
||
|
|
display: grid;
|
||
|
|
grid-template-columns: repeat(auto-fit, minmax(150px, 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;
|
||
|
|
}
|
||
|
|
</style>
|
||
|
|
|
||
|
|
<div class="demo-container">
|
||
|
|
<!-- Time Range Demo -->
|
||
|
|
<div class="demo-section">
|
||
|
|
<div class="demo-title">Different Time Ranges</div>
|
||
|
|
<dees-demowrapper
|
||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
||
|
|
const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any;
|
||
|
|
|
||
|
|
// Generate data for different time ranges
|
||
|
|
const generateDataForRange = (hours: number, pattern: 'stable' | 'degrading' | 'improving' | 'volatile' = 'stable'): IStatusHistoryPoint[] => {
|
||
|
|
const now = Date.now();
|
||
|
|
const data: IStatusHistoryPoint[] = [];
|
||
|
|
|
||
|
|
// For proper display, we need hourly data points that align with actual hours
|
||
|
|
for (let i = hours - 1; i >= 0; i--) {
|
||
|
|
// Create timestamp at the start of each hour
|
||
|
|
const date = new Date();
|
||
|
|
date.setMinutes(0, 0, 0);
|
||
|
|
date.setHours(date.getHours() - i);
|
||
|
|
const timestamp = date.getTime();
|
||
|
|
let status: IStatusHistoryPoint['status'] = 'operational';
|
||
|
|
let responseTime = 50 + Math.random() * 50;
|
||
|
|
let errorRate = 0;
|
||
|
|
|
||
|
|
switch (pattern) {
|
||
|
|
case 'degrading':
|
||
|
|
// Getting worse over time
|
||
|
|
const degradation = (hours - i) / hours;
|
||
|
|
if (degradation > 0.7) {
|
||
|
|
status = 'major_outage';
|
||
|
|
responseTime = 800 + Math.random() * 200;
|
||
|
|
errorRate = 0.3 + Math.random() * 0.2;
|
||
|
|
} else if (degradation > 0.5) {
|
||
|
|
status = 'partial_outage';
|
||
|
|
responseTime = 500 + Math.random() * 200;
|
||
|
|
errorRate = 0.1 + Math.random() * 0.1;
|
||
|
|
} else if (degradation > 0.3) {
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 200 + Math.random() * 100;
|
||
|
|
errorRate = 0.02 + Math.random() * 0.03;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'improving':
|
||
|
|
// Getting better over time
|
||
|
|
const improvement = i / hours;
|
||
|
|
if (improvement < 0.3) {
|
||
|
|
status = 'major_outage';
|
||
|
|
responseTime = 800 + Math.random() * 200;
|
||
|
|
errorRate = 0.3 + Math.random() * 0.2;
|
||
|
|
} else if (improvement < 0.5) {
|
||
|
|
status = 'partial_outage';
|
||
|
|
responseTime = 500 + Math.random() * 200;
|
||
|
|
errorRate = 0.1 + Math.random() * 0.1;
|
||
|
|
} else if (improvement < 0.7) {
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 200 + Math.random() * 100;
|
||
|
|
errorRate = 0.02 + Math.random() * 0.03;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
case 'volatile':
|
||
|
|
// Random ups and downs
|
||
|
|
const rand = Math.random();
|
||
|
|
if (rand < 0.05) {
|
||
|
|
status = 'major_outage';
|
||
|
|
responseTime = 800 + Math.random() * 200;
|
||
|
|
errorRate = 0.3 + Math.random() * 0.2;
|
||
|
|
} else if (rand < 0.1) {
|
||
|
|
status = 'partial_outage';
|
||
|
|
responseTime = 500 + Math.random() * 200;
|
||
|
|
errorRate = 0.1 + Math.random() * 0.1;
|
||
|
|
} else if (rand < 0.2) {
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 200 + Math.random() * 100;
|
||
|
|
errorRate = 0.02 + Math.random() * 0.03;
|
||
|
|
} else if (rand < 0.25) {
|
||
|
|
status = 'maintenance';
|
||
|
|
responseTime = 100 + Math.random() * 50;
|
||
|
|
errorRate = 0;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
|
||
|
|
default:
|
||
|
|
// Stable with occasional hiccups
|
||
|
|
if (Math.random() < 0.02) {
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 200 + Math.random() * 100;
|
||
|
|
errorRate = 0.01 + Math.random() * 0.02;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
data.push({
|
||
|
|
timestamp,
|
||
|
|
status,
|
||
|
|
responseTime,
|
||
|
|
errorRate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return data;
|
||
|
|
};
|
||
|
|
|
||
|
|
// Initial setup
|
||
|
|
statusDetails.serviceId = 'api-gateway';
|
||
|
|
statusDetails.serviceName = 'API Gateway';
|
||
|
|
statusDetails.historyData = generateDataForRange(24);
|
||
|
|
|
||
|
|
// Create controls
|
||
|
|
const controls = document.createElement('div');
|
||
|
|
controls.className = 'demo-controls';
|
||
|
|
|
||
|
|
const timeRanges = [
|
||
|
|
{ hours: 24, label: '24 Hours' },
|
||
|
|
{ hours: 168, label: '7 Days' },
|
||
|
|
{ hours: 720, label: '30 Days' },
|
||
|
|
{ hours: 2160, label: '90 Days' }
|
||
|
|
];
|
||
|
|
|
||
|
|
timeRanges.forEach((range, index) => {
|
||
|
|
const button = document.createElement('button');
|
||
|
|
button.className = 'demo-button' + (index === 0 ? ' active' : '');
|
||
|
|
button.textContent = range.label;
|
||
|
|
button.onclick = () => {
|
||
|
|
// Update active button
|
||
|
|
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
||
|
|
button.classList.add('active');
|
||
|
|
|
||
|
|
// Load new data with loading state
|
||
|
|
statusDetails.loading = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
statusDetails.historyData = generateDataForRange(range.hours, 'volatile');
|
||
|
|
statusDetails.loading = false;
|
||
|
|
updateStats();
|
||
|
|
}, 500);
|
||
|
|
};
|
||
|
|
controls.appendChild(button);
|
||
|
|
});
|
||
|
|
|
||
|
|
wrapperElement.appendChild(controls);
|
||
|
|
|
||
|
|
// Add statistics display
|
||
|
|
const statsDiv = document.createElement('div');
|
||
|
|
statsDiv.className = 'stats-grid';
|
||
|
|
wrapperElement.appendChild(statsDiv);
|
||
|
|
|
||
|
|
const updateStats = () => {
|
||
|
|
const data = statusDetails.historyData || [];
|
||
|
|
const operational = data.filter(d => d.status === 'operational').length;
|
||
|
|
const avgResponseTime = data.reduce((sum, d) => sum + (d.responseTime || 0), 0) / data.length;
|
||
|
|
const uptime = (operational / data.length) * 100;
|
||
|
|
const incidents = data.filter(d => d.status !== 'operational' && d.status !== 'maintenance').length;
|
||
|
|
|
||
|
|
statsDiv.innerHTML = `
|
||
|
|
<div class="stat-box">
|
||
|
|
<div class="stat-value">${uptime.toFixed(2)}%</div>
|
||
|
|
<div class="stat-label">Uptime</div>
|
||
|
|
</div>
|
||
|
|
<div class="stat-box">
|
||
|
|
<div class="stat-value">${avgResponseTime.toFixed(0)}ms</div>
|
||
|
|
<div class="stat-label">Avg Response Time</div>
|
||
|
|
</div>
|
||
|
|
<div class="stat-box">
|
||
|
|
<div class="stat-value">${incidents}</div>
|
||
|
|
<div class="stat-label">Incidents</div>
|
||
|
|
</div>
|
||
|
|
<div class="stat-box">
|
||
|
|
<div class="stat-value">${data.length}</div>
|
||
|
|
<div class="stat-label">Data Points</div>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
};
|
||
|
|
|
||
|
|
updateStats();
|
||
|
|
|
||
|
|
// Handle bar clicks
|
||
|
|
statusDetails.addEventListener('barClick', (event: CustomEvent) => {
|
||
|
|
const { timestamp, status, responseTime, errorRate } = event.detail;
|
||
|
|
const date = new Date(timestamp);
|
||
|
|
alert(`Details for ${date.toLocaleString()}:\n\nStatus: ${status}\nResponse Time: ${responseTime.toFixed(0)}ms\nError Rate: ${(errorRate * 100).toFixed(2)}%`);
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<upl-statuspage-statusdetails></upl-statuspage-statusdetails>
|
||
|
|
</dees-demowrapper>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Data Pattern Scenarios -->
|
||
|
|
<div class="demo-section">
|
||
|
|
<div class="demo-title">Different Data Patterns</div>
|
||
|
|
<dees-demowrapper
|
||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
||
|
|
const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any;
|
||
|
|
|
||
|
|
// Pattern generators
|
||
|
|
const patterns = {
|
||
|
|
stable: () => {
|
||
|
|
const data: IStatusHistoryPoint[] = [];
|
||
|
|
for (let i = 47; i >= 0; i--) {
|
||
|
|
const date = new Date();
|
||
|
|
date.setMinutes(0, 0, 0);
|
||
|
|
date.setHours(date.getHours() - i);
|
||
|
|
data.push({
|
||
|
|
timestamp: date.getTime(),
|
||
|
|
status: 'operational',
|
||
|
|
responseTime: 40 + Math.random() * 20,
|
||
|
|
errorRate: 0
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
},
|
||
|
|
|
||
|
|
degrading: () => {
|
||
|
|
const now = Date.now();
|
||
|
|
const data: IStatusHistoryPoint[] = [];
|
||
|
|
for (let i = 47; i >= 0; i--) {
|
||
|
|
const degradation = (47 - i) / 47;
|
||
|
|
let status: IStatusHistoryPoint['status'] = 'operational';
|
||
|
|
let responseTime = 50;
|
||
|
|
let errorRate = 0;
|
||
|
|
|
||
|
|
if (degradation > 0.8) {
|
||
|
|
status = 'major_outage';
|
||
|
|
responseTime = 800 + Math.random() * 200;
|
||
|
|
errorRate = 0.4;
|
||
|
|
} else if (degradation > 0.6) {
|
||
|
|
status = 'partial_outage';
|
||
|
|
responseTime = 500 + Math.random() * 100;
|
||
|
|
errorRate = 0.2;
|
||
|
|
} else if (degradation > 0.4) {
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 200 + Math.random() * 100;
|
||
|
|
errorRate = 0.05;
|
||
|
|
} else {
|
||
|
|
responseTime = 50 + degradation * 100;
|
||
|
|
}
|
||
|
|
|
||
|
|
data.push({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status,
|
||
|
|
responseTime,
|
||
|
|
errorRate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
},
|
||
|
|
|
||
|
|
recovering: () => {
|
||
|
|
const now = Date.now();
|
||
|
|
const data: IStatusHistoryPoint[] = [];
|
||
|
|
for (let i = 47; i >= 0; i--) {
|
||
|
|
const recovery = i / 47;
|
||
|
|
let status: IStatusHistoryPoint['status'] = 'operational';
|
||
|
|
let responseTime = 50;
|
||
|
|
let errorRate = 0;
|
||
|
|
|
||
|
|
if (recovery < 0.2) {
|
||
|
|
status = 'operational';
|
||
|
|
responseTime = 50 + Math.random() * 20;
|
||
|
|
} else if (recovery < 0.4) {
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 150 + Math.random() * 50;
|
||
|
|
errorRate = 0.02;
|
||
|
|
} else if (recovery < 0.7) {
|
||
|
|
status = 'partial_outage';
|
||
|
|
responseTime = 400 + Math.random() * 100;
|
||
|
|
errorRate = 0.15;
|
||
|
|
} else {
|
||
|
|
status = 'major_outage';
|
||
|
|
responseTime = 800 + Math.random() * 200;
|
||
|
|
errorRate = 0.35;
|
||
|
|
}
|
||
|
|
|
||
|
|
data.push({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status,
|
||
|
|
responseTime,
|
||
|
|
errorRate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
},
|
||
|
|
|
||
|
|
periodic: () => {
|
||
|
|
const now = Date.now();
|
||
|
|
const data: IStatusHistoryPoint[] = [];
|
||
|
|
for (let i = 47; i >= 0; i--) {
|
||
|
|
// Issues every 12 hours
|
||
|
|
const hourOfDay = i % 24;
|
||
|
|
let status: IStatusHistoryPoint['status'] = 'operational';
|
||
|
|
let responseTime = 50 + Math.random() * 30;
|
||
|
|
let errorRate = 0;
|
||
|
|
|
||
|
|
if (hourOfDay >= 9 && hourOfDay <= 11) {
|
||
|
|
// Morning peak
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 200 + Math.random() * 100;
|
||
|
|
errorRate = 0.05;
|
||
|
|
} else if (hourOfDay >= 18 && hourOfDay <= 20) {
|
||
|
|
// Evening peak
|
||
|
|
status = 'degraded';
|
||
|
|
responseTime = 250 + Math.random() * 150;
|
||
|
|
errorRate = 0.08;
|
||
|
|
}
|
||
|
|
|
||
|
|
data.push({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status,
|
||
|
|
responseTime,
|
||
|
|
errorRate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
},
|
||
|
|
|
||
|
|
maintenance: () => {
|
||
|
|
const now = Date.now();
|
||
|
|
const data: IStatusHistoryPoint[] = [];
|
||
|
|
for (let i = 47; i >= 0; i--) {
|
||
|
|
let status: IStatusHistoryPoint['status'] = 'operational';
|
||
|
|
let responseTime = 50 + Math.random() * 30;
|
||
|
|
let errorRate = 0;
|
||
|
|
|
||
|
|
// Maintenance window from hour 20-24
|
||
|
|
if (i >= 20 && i <= 24) {
|
||
|
|
status = 'maintenance';
|
||
|
|
responseTime = 0;
|
||
|
|
errorRate = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
data.push({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status,
|
||
|
|
responseTime,
|
||
|
|
errorRate
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return data;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Initial setup
|
||
|
|
statusDetails.serviceId = 'web-server';
|
||
|
|
statusDetails.serviceName = 'Web Server';
|
||
|
|
statusDetails.historyData = patterns.stable();
|
||
|
|
|
||
|
|
// Create controls
|
||
|
|
const controls = document.createElement('div');
|
||
|
|
controls.className = 'demo-controls';
|
||
|
|
|
||
|
|
Object.entries(patterns).forEach(([name, generator]) => {
|
||
|
|
const button = document.createElement('button');
|
||
|
|
button.className = 'demo-button' + (name === 'stable' ? ' active' : '');
|
||
|
|
button.textContent = name.charAt(0).toUpperCase() + name.slice(1);
|
||
|
|
button.onclick = () => {
|
||
|
|
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
||
|
|
button.classList.add('active');
|
||
|
|
|
||
|
|
statusDetails.loading = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
statusDetails.historyData = generator();
|
||
|
|
statusDetails.loading = false;
|
||
|
|
updateInfo(name);
|
||
|
|
}, 300);
|
||
|
|
};
|
||
|
|
controls.appendChild(button);
|
||
|
|
});
|
||
|
|
|
||
|
|
wrapperElement.appendChild(controls);
|
||
|
|
|
||
|
|
// Add info display
|
||
|
|
const info = document.createElement('div');
|
||
|
|
info.className = 'demo-info';
|
||
|
|
wrapperElement.appendChild(info);
|
||
|
|
|
||
|
|
const updateInfo = (pattern: string) => {
|
||
|
|
const descriptions = {
|
||
|
|
stable: 'Service running smoothly with consistent performance',
|
||
|
|
degrading: 'Service health deteriorating over time',
|
||
|
|
recovering: 'Service recovering from a major outage',
|
||
|
|
periodic: 'Regular performance issues during peak hours (9-11 AM and 6-8 PM)',
|
||
|
|
maintenance: 'Scheduled maintenance window (hours 20-24)'
|
||
|
|
};
|
||
|
|
|
||
|
|
info.innerHTML = `<strong>Pattern:</strong> ${descriptions[pattern as keyof typeof descriptions] || pattern}`;
|
||
|
|
};
|
||
|
|
|
||
|
|
updateInfo('stable');
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<upl-statuspage-statusdetails></upl-statuspage-statusdetails>
|
||
|
|
</dees-demowrapper>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Interactive Real-time Updates -->
|
||
|
|
<div class="demo-section">
|
||
|
|
<div class="demo-title">Real-time Updates with Manual Control</div>
|
||
|
|
<dees-demowrapper
|
||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
||
|
|
const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any;
|
||
|
|
|
||
|
|
// Initialize with recent data
|
||
|
|
const now = Date.now();
|
||
|
|
const initialData: IStatusHistoryPoint[] = [];
|
||
|
|
for (let i = 23; i >= 0; i--) {
|
||
|
|
initialData.push({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status: 'operational',
|
||
|
|
responseTime: 50 + Math.random() * 30,
|
||
|
|
errorRate: 0
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
statusDetails.serviceId = 'real-time-api';
|
||
|
|
statusDetails.serviceName = 'Real-time API';
|
||
|
|
statusDetails.historyData = initialData;
|
||
|
|
statusDetails.timeRange = '24h';
|
||
|
|
|
||
|
|
// Create controls
|
||
|
|
const controls = document.createElement('div');
|
||
|
|
controls.className = 'demo-controls';
|
||
|
|
controls.innerHTML = `
|
||
|
|
<button class="demo-button" id="addHealthy">Add Healthy Point</button>
|
||
|
|
<button class="demo-button" id="addDegraded">Add Degraded Point</button>
|
||
|
|
<button class="demo-button" id="addOutage">Add Outage Point</button>
|
||
|
|
<button class="demo-button" id="simulateSpike">Simulate Traffic Spike</button>
|
||
|
|
<button class="demo-button" id="clearData">Clear All Data</button>
|
||
|
|
`;
|
||
|
|
wrapperElement.appendChild(controls);
|
||
|
|
|
||
|
|
const addDataPoint = (status: IStatusHistoryPoint['status'], responseTime: number, errorRate: number = 0) => {
|
||
|
|
const data = [...(statusDetails.historyData || [])];
|
||
|
|
if (data.length >= 24) {
|
||
|
|
data.shift(); // Keep only 24 points
|
||
|
|
}
|
||
|
|
|
||
|
|
data.push({
|
||
|
|
timestamp: Date.now(),
|
||
|
|
status,
|
||
|
|
responseTime,
|
||
|
|
errorRate
|
||
|
|
});
|
||
|
|
|
||
|
|
statusDetails.historyData = data;
|
||
|
|
};
|
||
|
|
|
||
|
|
controls.querySelector('#addHealthy')?.addEventListener('click', () => {
|
||
|
|
addDataPoint('operational', 50 + Math.random() * 30);
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#addDegraded')?.addEventListener('click', () => {
|
||
|
|
addDataPoint('degraded', 200 + Math.random() * 100, 0.05);
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#addOutage')?.addEventListener('click', () => {
|
||
|
|
addDataPoint('major_outage', 800 + Math.random() * 200, 0.5);
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#simulateSpike')?.addEventListener('click', () => {
|
||
|
|
// Add several degraded points
|
||
|
|
for (let i = 0; i < 3; i++) {
|
||
|
|
setTimeout(() => {
|
||
|
|
addDataPoint('degraded', 300 + Math.random() * 200, 0.1 + Math.random() * 0.1);
|
||
|
|
}, i * 1000);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#clearData')?.addEventListener('click', () => {
|
||
|
|
statusDetails.historyData = [];
|
||
|
|
});
|
||
|
|
|
||
|
|
// Auto-update every 5 seconds
|
||
|
|
let autoUpdate = setInterval(() => {
|
||
|
|
const rand = Math.random();
|
||
|
|
if (rand < 0.8) {
|
||
|
|
addDataPoint('operational', 40 + Math.random() * 40);
|
||
|
|
} else if (rand < 0.95) {
|
||
|
|
addDataPoint('degraded', 150 + Math.random() * 100, 0.02);
|
||
|
|
} else {
|
||
|
|
addDataPoint('partial_outage', 400 + Math.random() * 200, 0.15);
|
||
|
|
}
|
||
|
|
}, 5000);
|
||
|
|
|
||
|
|
// Add toggle for auto-updates
|
||
|
|
const autoToggle = document.createElement('button');
|
||
|
|
autoToggle.className = 'demo-button active';
|
||
|
|
autoToggle.textContent = 'Auto-update: ON';
|
||
|
|
autoToggle.style.marginLeft = '10px';
|
||
|
|
autoToggle.onclick = () => {
|
||
|
|
if (autoUpdate) {
|
||
|
|
clearInterval(autoUpdate);
|
||
|
|
autoUpdate = null;
|
||
|
|
autoToggle.textContent = 'Auto-update: OFF';
|
||
|
|
autoToggle.classList.remove('active');
|
||
|
|
} else {
|
||
|
|
autoUpdate = setInterval(() => {
|
||
|
|
const rand = Math.random();
|
||
|
|
if (rand < 0.8) {
|
||
|
|
addDataPoint('operational', 40 + Math.random() * 40);
|
||
|
|
} else if (rand < 0.95) {
|
||
|
|
addDataPoint('degraded', 150 + Math.random() * 100, 0.02);
|
||
|
|
} else {
|
||
|
|
addDataPoint('partial_outage', 400 + Math.random() * 200, 0.15);
|
||
|
|
}
|
||
|
|
}, 5000);
|
||
|
|
autoToggle.textContent = 'Auto-update: ON';
|
||
|
|
autoToggle.classList.add('active');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
controls.appendChild(autoToggle);
|
||
|
|
|
||
|
|
// Cleanup on unmount
|
||
|
|
wrapperElement.addEventListener('remove', () => {
|
||
|
|
if (autoUpdate) clearInterval(autoUpdate);
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<upl-statuspage-statusdetails></upl-statuspage-statusdetails>
|
||
|
|
</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 statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any;
|
||
|
|
|
||
|
|
const scenarios = {
|
||
|
|
noData: {
|
||
|
|
name: 'No Data Available',
|
||
|
|
data: []
|
||
|
|
},
|
||
|
|
singlePoint: {
|
||
|
|
name: 'Single Data Point',
|
||
|
|
data: [{
|
||
|
|
timestamp: Date.now(),
|
||
|
|
status: 'operational' as const,
|
||
|
|
responseTime: 75,
|
||
|
|
errorRate: 0
|
||
|
|
}]
|
||
|
|
},
|
||
|
|
allDown: {
|
||
|
|
name: 'Complete Outage',
|
||
|
|
data: Array.from({ length: 48 }, (_, i) => ({
|
||
|
|
timestamp: Date.now() - (i * 60 * 60 * 1000),
|
||
|
|
status: 'major_outage' as const,
|
||
|
|
responseTime: 0,
|
||
|
|
errorRate: 1
|
||
|
|
}))
|
||
|
|
},
|
||
|
|
highLatency: {
|
||
|
|
name: 'High Latency Issues',
|
||
|
|
data: Array.from({ length: 48 }, (_, i) => ({
|
||
|
|
timestamp: Date.now() - (i * 60 * 60 * 1000),
|
||
|
|
status: 'operational' as const,
|
||
|
|
responseTime: 2000 + Math.random() * 1000,
|
||
|
|
errorRate: 0
|
||
|
|
}))
|
||
|
|
},
|
||
|
|
mixedStatuses: {
|
||
|
|
name: 'All Status Types',
|
||
|
|
data: Array.from({ length: 50 }, (_, i) => {
|
||
|
|
const statuses: IStatusHistoryPoint['status'][] = ['operational', 'degraded', 'partial_outage', 'major_outage', 'maintenance'];
|
||
|
|
const status = statuses[i % statuses.length];
|
||
|
|
return {
|
||
|
|
timestamp: Date.now() - (i * 60 * 60 * 1000),
|
||
|
|
status,
|
||
|
|
responseTime: status === 'operational' ? 50 : status === 'maintenance' ? 0 : 200 + Math.random() * 600,
|
||
|
|
errorRate: status === 'operational' || status === 'maintenance' ? 0 : 0.1 + Math.random() * 0.4
|
||
|
|
};
|
||
|
|
})
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Initial scenario
|
||
|
|
let currentScenario = 'noData';
|
||
|
|
statusDetails.serviceId = 'edge-case-service';
|
||
|
|
statusDetails.serviceName = 'Edge Case Service';
|
||
|
|
statusDetails.historyData = scenarios[currentScenario].data;
|
||
|
|
|
||
|
|
// Create scenario buttons
|
||
|
|
const controls = document.createElement('div');
|
||
|
|
controls.className = 'demo-controls';
|
||
|
|
|
||
|
|
Object.entries(scenarios).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;
|
||
|
|
statusDetails.loading = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
statusDetails.historyData = scenario.data;
|
||
|
|
statusDetails.loading = false;
|
||
|
|
}, 300);
|
||
|
|
};
|
||
|
|
controls.appendChild(button);
|
||
|
|
});
|
||
|
|
|
||
|
|
wrapperElement.appendChild(controls);
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<upl-statuspage-statusdetails></upl-statuspage-statusdetails>
|
||
|
|
</dees-demowrapper>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<!-- Loading and Error States -->
|
||
|
|
<div class="demo-section">
|
||
|
|
<div class="demo-title">Loading and Error Handling</div>
|
||
|
|
<dees-demowrapper
|
||
|
|
.runAfterRender=${async (wrapperElement: any) => {
|
||
|
|
const statusDetails = wrapperElement.querySelector('upl-statuspage-statusdetails') as any;
|
||
|
|
|
||
|
|
// Start with loading
|
||
|
|
statusDetails.loading = true;
|
||
|
|
statusDetails.serviceId = 'loading-demo';
|
||
|
|
statusDetails.serviceName = 'Loading Demo Service';
|
||
|
|
|
||
|
|
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="loadSlowly">Load Slowly (3s)</button>
|
||
|
|
`;
|
||
|
|
wrapperElement.appendChild(controls);
|
||
|
|
|
||
|
|
controls.querySelector('#toggleLoading')?.addEventListener('click', () => {
|
||
|
|
statusDetails.loading = !statusDetails.loading;
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#loadSuccess')?.addEventListener('click', () => {
|
||
|
|
statusDetails.loading = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
const now = Date.now();
|
||
|
|
statusDetails.historyData = Array.from({ length: 24 }, (_, i) => ({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status: Math.random() > 0.9 ? 'degraded' : 'operational',
|
||
|
|
responseTime: 50 + Math.random() * 50,
|
||
|
|
errorRate: 0
|
||
|
|
}));
|
||
|
|
statusDetails.loading = false;
|
||
|
|
}, 500);
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#loadError')?.addEventListener('click', () => {
|
||
|
|
statusDetails.loading = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
statusDetails.loading = false;
|
||
|
|
statusDetails.historyData = [];
|
||
|
|
statusDetails.errorMessage = 'Failed to load status data: Connection timeout';
|
||
|
|
}, 1500);
|
||
|
|
});
|
||
|
|
|
||
|
|
controls.querySelector('#loadSlowly')?.addEventListener('click', () => {
|
||
|
|
statusDetails.loading = true;
|
||
|
|
setTimeout(() => {
|
||
|
|
const now = Date.now();
|
||
|
|
statusDetails.historyData = Array.from({ length: 48 }, (_, i) => ({
|
||
|
|
timestamp: now - (i * 60 * 60 * 1000),
|
||
|
|
status: 'operational',
|
||
|
|
responseTime: 45 + Math.random() * 30,
|
||
|
|
errorRate: 0
|
||
|
|
}));
|
||
|
|
statusDetails.loading = false;
|
||
|
|
}, 3000);
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
<upl-statuspage-statusdetails></upl-statuspage-statusdetails>
|
||
|
|
</dees-demowrapper>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`;
|