update
This commit is contained in:
876
ts_web/elements/upl-statuspage-statusmonth.demo.ts
Normal file
876
ts_web/elements/upl-statuspage-statusmonth.demo.ts
Normal file
@@ -0,0 +1,876 @@
|
||||
import { html } from '@design.estate/dees-element';
|
||||
import type { IMonthlyUptime, IUptimeDay } 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;
|
||||
}
|
||||
.stats-display {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
.stat-card {
|
||||
background: white;
|
||||
padding: 12px;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
.stat-value {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #2196F3;
|
||||
}
|
||||
.stat-label {
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.month-nav {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="demo-container">
|
||||
<!-- Different Month Patterns -->
|
||||
<div class="demo-section">
|
||||
<div class="demo-title">Different Month Patterns</div>
|
||||
<dees-demowrapper
|
||||
.runAfterRender=${async (wrapperElement: any) => {
|
||||
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
||||
|
||||
// Pattern generators
|
||||
const generateMonthPattern = (monthCount: number, pattern: 'perfect' | 'problematic' | 'improving' | 'degrading' | 'seasonal'): IMonthlyUptime[] => {
|
||||
const months: IMonthlyUptime[] = [];
|
||||
const now = new Date();
|
||||
|
||||
for (let monthOffset = monthCount - 1; monthOffset >= 0; monthOffset--) {
|
||||
const monthDate = new Date(now.getFullYear(), now.getMonth() - monthOffset, 1);
|
||||
const year = monthDate.getFullYear();
|
||||
const month = monthDate.getMonth();
|
||||
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
const days: IUptimeDay[] = [];
|
||||
let totalIncidents = 0;
|
||||
let totalUptimeMinutes = 0;
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
let uptime = 100;
|
||||
let incidents = 0;
|
||||
let downtime = 0;
|
||||
let status: IUptimeDay['status'] = 'operational';
|
||||
|
||||
switch (pattern) {
|
||||
case 'perfect':
|
||||
// Near perfect uptime
|
||||
if (Math.random() < 0.02) {
|
||||
uptime = 99.9 + Math.random() * 0.099;
|
||||
status = 'degraded';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'problematic':
|
||||
// Frequent issues
|
||||
const problemRand = Math.random();
|
||||
if (problemRand < 0.1) {
|
||||
uptime = 70 + Math.random() * 20;
|
||||
incidents = 2 + Math.floor(Math.random() * 3);
|
||||
status = 'major_outage';
|
||||
} else if (problemRand < 0.25) {
|
||||
uptime = 90 + Math.random() * 8;
|
||||
incidents = 1 + Math.floor(Math.random() * 2);
|
||||
status = 'partial_outage';
|
||||
} else if (problemRand < 0.4) {
|
||||
uptime = 98 + Math.random() * 1.5;
|
||||
incidents = 1;
|
||||
status = 'degraded';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'improving':
|
||||
// Getting better over time
|
||||
const improvementFactor = (monthCount - monthOffset) / monthCount;
|
||||
const improveRand = Math.random();
|
||||
if (improveRand < 0.3 * (1 - improvementFactor)) {
|
||||
uptime = 85 + Math.random() * 10 + (improvementFactor * 10);
|
||||
incidents = Math.max(0, 3 - Math.floor(improvementFactor * 3));
|
||||
status = improvementFactor > 0.7 ? 'degraded' : 'partial_outage';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'degrading':
|
||||
// Getting worse over time
|
||||
const degradationFactor = monthOffset / monthCount;
|
||||
const degradeRand = Math.random();
|
||||
if (degradeRand < 0.3 * (1 - degradationFactor)) {
|
||||
uptime = 85 + Math.random() * 10 + (degradationFactor * 10);
|
||||
incidents = Math.max(0, 3 - Math.floor(degradationFactor * 3));
|
||||
status = degradationFactor > 0.7 ? 'degraded' : 'major_outage';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'seasonal':
|
||||
// Worse during certain months (simulating high traffic periods)
|
||||
const monthNum = month;
|
||||
if (monthNum === 11 || monthNum === 0) { // December, January
|
||||
if (Math.random() < 0.3) {
|
||||
uptime = 92 + Math.random() * 6;
|
||||
incidents = 1 + Math.floor(Math.random() * 2);
|
||||
status = 'degraded';
|
||||
}
|
||||
} else if (monthNum === 6 || monthNum === 7) { // July, August
|
||||
if (Math.random() < 0.2) {
|
||||
uptime = 94 + Math.random() * 5;
|
||||
incidents = 1;
|
||||
status = 'degraded';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
downtime = Math.floor((100 - uptime) * 14.4);
|
||||
totalIncidents += incidents;
|
||||
totalUptimeMinutes += uptime * 14.4;
|
||||
|
||||
days.push({
|
||||
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
||||
uptime,
|
||||
incidents,
|
||||
totalDowntime: downtime,
|
||||
status
|
||||
});
|
||||
}
|
||||
|
||||
const overallUptime = totalUptimeMinutes / (daysInMonth * 1440) * 100;
|
||||
|
||||
months.push({
|
||||
month: monthKey,
|
||||
days,
|
||||
overallUptime,
|
||||
totalIncidents
|
||||
});
|
||||
}
|
||||
|
||||
return months;
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
statusMonth.serviceId = 'production-api';
|
||||
statusMonth.serviceName = 'Production API';
|
||||
statusMonth.monthlyData = generateMonthPattern(6, 'perfect');
|
||||
|
||||
// Create pattern controls
|
||||
const controls = document.createElement('div');
|
||||
controls.className = 'demo-controls';
|
||||
|
||||
const patterns = [
|
||||
{ key: 'perfect', label: 'Perfect Uptime' },
|
||||
{ key: 'problematic', label: 'Problematic' },
|
||||
{ key: 'improving', label: 'Improving Trend' },
|
||||
{ key: 'degrading', label: 'Degrading Trend' },
|
||||
{ key: 'seasonal', label: 'Seasonal Pattern' }
|
||||
];
|
||||
|
||||
patterns.forEach((pattern, index) => {
|
||||
const button = document.createElement('button');
|
||||
button.className = 'demo-button' + (index === 0 ? ' active' : '');
|
||||
button.textContent = pattern.label;
|
||||
button.onclick = () => {
|
||||
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
||||
button.classList.add('active');
|
||||
|
||||
statusMonth.loading = true;
|
||||
setTimeout(() => {
|
||||
statusMonth.monthlyData = generateMonthPattern(6, pattern.key as any);
|
||||
statusMonth.loading = false;
|
||||
updateStats();
|
||||
}, 500);
|
||||
};
|
||||
controls.appendChild(button);
|
||||
});
|
||||
|
||||
wrapperElement.appendChild(controls);
|
||||
|
||||
// Add statistics display
|
||||
const statsDiv = document.createElement('div');
|
||||
statsDiv.className = 'stats-display';
|
||||
wrapperElement.appendChild(statsDiv);
|
||||
|
||||
const updateStats = () => {
|
||||
const data = statusMonth.monthlyData || [];
|
||||
const avgUptime = data.reduce((sum, month) => sum + month.overallUptime, 0) / data.length;
|
||||
const totalIncidents = data.reduce((sum, month) => sum + month.totalIncidents, 0);
|
||||
const worstMonth = data.reduce((worst, month) =>
|
||||
month.overallUptime < worst.overallUptime ? month : worst, data[0]);
|
||||
|
||||
statsDiv.innerHTML = `
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${avgUptime.toFixed(3)}%</div>
|
||||
<div class="stat-label">Avg Uptime</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${totalIncidents}</div>
|
||||
<div class="stat-label">Total Incidents</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${data.length}</div>
|
||||
<div class="stat-label">Months</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-value">${worstMonth ? worstMonth.overallUptime.toFixed(2) : '100'}%</div>
|
||||
<div class="stat-label">Worst Month</div>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
|
||||
updateStats();
|
||||
|
||||
// Handle day clicks
|
||||
statusMonth.addEventListener('dayClick', (event: CustomEvent) => {
|
||||
const { date, uptime, incidents, status, totalDowntime } = event.detail;
|
||||
alert(`Day Details for ${date}:\n\nUptime: ${uptime.toFixed(3)}%\nIncidents: ${incidents}\nStatus: ${status}\nDowntime: ${totalDowntime} minutes`);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
||||
</dees-demowrapper>
|
||||
</div>
|
||||
|
||||
<!-- Different Time Spans -->
|
||||
<div class="demo-section">
|
||||
<div class="demo-title">Different Time Spans</div>
|
||||
<dees-demowrapper
|
||||
.runAfterRender=${async (wrapperElement: any) => {
|
||||
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
||||
|
||||
// Generate data for different time spans
|
||||
const generateTimeSpanData = (months: number): IMonthlyUptime[] => {
|
||||
const data: IMonthlyUptime[] = [];
|
||||
const now = new Date();
|
||||
|
||||
for (let monthOffset = months - 1; monthOffset >= 0; monthOffset--) {
|
||||
const monthDate = new Date(now.getFullYear(), now.getMonth() - monthOffset, 1);
|
||||
const year = monthDate.getFullYear();
|
||||
const month = monthDate.getMonth();
|
||||
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
const days: IUptimeDay[] = [];
|
||||
let totalIncidents = 0;
|
||||
let totalUptimeMinutes = 0;
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
// Create realistic patterns
|
||||
let uptime = 99.9 + Math.random() * 0.099;
|
||||
let incidents = 0;
|
||||
let status: IUptimeDay['status'] = 'operational';
|
||||
|
||||
if (Math.random() < 0.05) {
|
||||
uptime = 95 + Math.random() * 4.9;
|
||||
incidents = 1;
|
||||
status = 'degraded';
|
||||
} else if (Math.random() < 0.01) {
|
||||
uptime = 85 + Math.random() * 10;
|
||||
incidents = 2;
|
||||
status = 'partial_outage';
|
||||
}
|
||||
|
||||
const downtime = Math.floor((100 - uptime) * 14.4);
|
||||
totalIncidents += incidents;
|
||||
totalUptimeMinutes += uptime * 14.4;
|
||||
|
||||
days.push({
|
||||
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
||||
uptime,
|
||||
incidents,
|
||||
totalDowntime: downtime,
|
||||
status
|
||||
});
|
||||
}
|
||||
|
||||
const overallUptime = totalUptimeMinutes / (daysInMonth * 1440) * 100;
|
||||
|
||||
data.push({
|
||||
month: monthKey,
|
||||
days,
|
||||
overallUptime,
|
||||
totalIncidents
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
statusMonth.serviceId = 'multi-region-lb';
|
||||
statusMonth.serviceName = 'Multi-Region Load Balancer';
|
||||
statusMonth.monthlyData = generateTimeSpanData(3);
|
||||
|
||||
// Create time span controls
|
||||
const controls = document.createElement('div');
|
||||
controls.className = 'demo-controls';
|
||||
|
||||
const timeSpans = [
|
||||
{ months: 3, label: 'Last 3 Months' },
|
||||
{ months: 6, label: 'Last 6 Months' },
|
||||
{ months: 12, label: 'Last 12 Months' },
|
||||
{ months: 24, label: 'Last 24 Months' }
|
||||
];
|
||||
|
||||
timeSpans.forEach((span, index) => {
|
||||
const button = document.createElement('button');
|
||||
button.className = 'demo-button' + (index === 0 ? ' active' : '');
|
||||
button.textContent = span.label;
|
||||
button.onclick = () => {
|
||||
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
||||
button.classList.add('active');
|
||||
|
||||
statusMonth.loading = true;
|
||||
setTimeout(() => {
|
||||
statusMonth.monthlyData = generateTimeSpanData(span.months);
|
||||
statusMonth.loading = false;
|
||||
}, 500);
|
||||
};
|
||||
controls.appendChild(button);
|
||||
});
|
||||
|
||||
wrapperElement.appendChild(controls);
|
||||
|
||||
// Add info display
|
||||
const info = document.createElement('div');
|
||||
info.className = 'demo-info';
|
||||
info.innerHTML = 'Click on different time spans to see historical uptime data. The component automatically adjusts the display based on the number of months.';
|
||||
wrapperElement.appendChild(info);
|
||||
}}
|
||||
>
|
||||
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
||||
</dees-demowrapper>
|
||||
</div>
|
||||
|
||||
<!-- Current Month Real-time Updates -->
|
||||
<div class="demo-section">
|
||||
<div class="demo-title">Current Month with Real-time Updates</div>
|
||||
<dees-demowrapper
|
||||
.runAfterRender=${async (wrapperElement: any) => {
|
||||
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
||||
|
||||
// Generate current month data
|
||||
const generateCurrentMonthData = (): IMonthlyUptime[] => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth();
|
||||
const today = now.getDate();
|
||||
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
const days: IUptimeDay[] = [];
|
||||
let totalIncidents = 0;
|
||||
let totalUptimeMinutes = 0;
|
||||
|
||||
// Generate data only up to today
|
||||
for (let day = 1; day <= today; day++) {
|
||||
let uptime = 99.9 + Math.random() * 0.099;
|
||||
let incidents = 0;
|
||||
let status: IUptimeDay['status'] = 'operational';
|
||||
|
||||
// Today might have ongoing issues
|
||||
if (day === today) {
|
||||
if (Math.random() < 0.3) {
|
||||
uptime = 95 + Math.random() * 4;
|
||||
incidents = 1;
|
||||
status = 'degraded';
|
||||
}
|
||||
} else if (Math.random() < 0.05) {
|
||||
uptime = 97 + Math.random() * 2.9;
|
||||
incidents = 1;
|
||||
status = 'degraded';
|
||||
}
|
||||
|
||||
const downtime = Math.floor((100 - uptime) * 14.4);
|
||||
totalIncidents += incidents;
|
||||
totalUptimeMinutes += uptime * 14.4;
|
||||
|
||||
days.push({
|
||||
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
||||
uptime,
|
||||
incidents,
|
||||
totalDowntime: downtime,
|
||||
status
|
||||
});
|
||||
}
|
||||
|
||||
// Fill remaining days with placeholder
|
||||
for (let day = today + 1; day <= daysInMonth; day++) {
|
||||
days.push({
|
||||
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
||||
uptime: 0,
|
||||
incidents: 0,
|
||||
totalDowntime: 0,
|
||||
status: 'operational'
|
||||
});
|
||||
}
|
||||
|
||||
const overallUptime = today > 0 ? totalUptimeMinutes / (today * 1440) * 100 : 100;
|
||||
|
||||
return [{
|
||||
month: monthKey,
|
||||
days,
|
||||
overallUptime,
|
||||
totalIncidents
|
||||
}];
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
statusMonth.serviceId = 'realtime-monitor';
|
||||
statusMonth.serviceName = 'Real-time Monitoring Service';
|
||||
statusMonth.monthlyData = generateCurrentMonthData();
|
||||
statusMonth.showCurrentDay = true;
|
||||
|
||||
// Update today's status periodically
|
||||
const updateInterval = setInterval(() => {
|
||||
const data = statusMonth.monthlyData;
|
||||
if (data && data.length > 0) {
|
||||
const currentMonth = data[0];
|
||||
const today = new Date().getDate() - 1;
|
||||
|
||||
if (currentMonth.days[today]) {
|
||||
// Simulate status changes
|
||||
const rand = Math.random();
|
||||
if (rand < 0.1) {
|
||||
currentMonth.days[today].uptime = 95 + Math.random() * 4.9;
|
||||
currentMonth.days[today].incidents = (currentMonth.days[today].incidents || 0) + 1;
|
||||
currentMonth.days[today].status = 'degraded';
|
||||
currentMonth.days[today].totalDowntime = Math.floor((100 - currentMonth.days[today].uptime) * 14.4);
|
||||
|
||||
// Recalculate overall uptime
|
||||
let totalUptime = 0;
|
||||
let validDays = 0;
|
||||
currentMonth.days.forEach((day, index) => {
|
||||
if (index <= today && day.uptime > 0) {
|
||||
totalUptime += day.uptime;
|
||||
validDays++;
|
||||
}
|
||||
});
|
||||
currentMonth.overallUptime = validDays > 0 ? totalUptime / validDays : 100;
|
||||
currentMonth.totalIncidents = currentMonth.days.reduce((sum, day) => sum + (day.incidents || 0), 0);
|
||||
|
||||
statusMonth.requestUpdate();
|
||||
logUpdate('Status degraded - Uptime: ' + currentMonth.days[today].uptime.toFixed(2) + '%');
|
||||
} else if (rand < 0.05 && currentMonth.days[today].status !== 'operational') {
|
||||
// Recover from issues
|
||||
currentMonth.days[today].uptime = 99.9 + Math.random() * 0.099;
|
||||
currentMonth.days[today].status = 'operational';
|
||||
currentMonth.days[today].totalDowntime = Math.floor((100 - currentMonth.days[today].uptime) * 14.4);
|
||||
|
||||
statusMonth.requestUpdate();
|
||||
logUpdate('Service recovered to operational status');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
// Create controls
|
||||
const controls = document.createElement('div');
|
||||
controls.className = 'demo-controls';
|
||||
controls.innerHTML = '<button class="demo-button" id="simulateOutage">Simulate Outage</button>' +
|
||||
'<button class="demo-button" id="simulateRecovery">Simulate Recovery</button>' +
|
||||
'<button class="demo-button" id="refreshData">Refresh Data</button>';
|
||||
wrapperElement.appendChild(controls);
|
||||
|
||||
controls.querySelector('#simulateOutage')?.addEventListener('click', () => {
|
||||
const data = statusMonth.monthlyData;
|
||||
if (data && data.length > 0) {
|
||||
const today = new Date().getDate() - 1;
|
||||
data[0].days[today].uptime = 85 + Math.random() * 10;
|
||||
data[0].days[today].incidents = (data[0].days[today].incidents || 0) + 1;
|
||||
data[0].days[today].status = 'major_outage';
|
||||
data[0].days[today].totalDowntime = Math.floor((100 - data[0].days[today].uptime) * 14.4);
|
||||
statusMonth.requestUpdate();
|
||||
logUpdate('Major outage simulated');
|
||||
}
|
||||
});
|
||||
|
||||
controls.querySelector('#simulateRecovery')?.addEventListener('click', () => {
|
||||
const data = statusMonth.monthlyData;
|
||||
if (data && data.length > 0) {
|
||||
const today = new Date().getDate() - 1;
|
||||
data[0].days[today].uptime = 99.95;
|
||||
data[0].days[today].status = 'operational';
|
||||
data[0].days[today].totalDowntime = Math.floor((100 - data[0].days[today].uptime) * 14.4);
|
||||
statusMonth.requestUpdate();
|
||||
logUpdate('Service recovered');
|
||||
}
|
||||
});
|
||||
|
||||
controls.querySelector('#refreshData')?.addEventListener('click', () => {
|
||||
statusMonth.monthlyData = generateCurrentMonthData();
|
||||
logUpdate('Data refreshed');
|
||||
});
|
||||
|
||||
// Add update log
|
||||
const logDiv = document.createElement('div');
|
||||
logDiv.className = 'demo-info';
|
||||
logDiv.style.maxHeight = '100px';
|
||||
logDiv.style.overflowY = 'auto';
|
||||
logDiv.innerHTML = '<strong>Update Log:</strong><br>';
|
||||
wrapperElement.appendChild(logDiv);
|
||||
|
||||
const logUpdate = (message: string) => {
|
||||
const time = new Date().toLocaleTimeString();
|
||||
logDiv.innerHTML += '[' + time + '] ' + message + '<br>';
|
||||
logDiv.scrollTop = logDiv.scrollHeight;
|
||||
};
|
||||
|
||||
// Cleanup
|
||||
wrapperElement.addEventListener('remove', () => {
|
||||
clearInterval(updateInterval);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
||||
</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 statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
||||
|
||||
const scenarios = {
|
||||
noData: {
|
||||
name: 'No Data',
|
||||
data: []
|
||||
},
|
||||
singleMonth: {
|
||||
name: 'Single Month',
|
||||
data: [{
|
||||
month: '2024-01',
|
||||
days: Array.from({ length: 31 }, (_, i) => ({
|
||||
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
||||
uptime: 99.9 + Math.random() * 0.099,
|
||||
incidents: 0,
|
||||
totalDowntime: 0,
|
||||
status: 'operational' as const
|
||||
})),
|
||||
overallUptime: 99.95,
|
||||
totalIncidents: 0
|
||||
}]
|
||||
},
|
||||
allDown: {
|
||||
name: 'Complete Outage Month',
|
||||
data: [{
|
||||
month: '2024-01',
|
||||
days: Array.from({ length: 31 }, (_, i) => ({
|
||||
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
||||
uptime: 0,
|
||||
incidents: 5,
|
||||
totalDowntime: 1440,
|
||||
status: 'major_outage' as const
|
||||
})),
|
||||
overallUptime: 0,
|
||||
totalIncidents: 155
|
||||
}]
|
||||
},
|
||||
maintenanceMonth: {
|
||||
name: 'Maintenance Heavy Month',
|
||||
data: [{
|
||||
month: '2024-01',
|
||||
days: Array.from({ length: 31 }, (_, i) => {
|
||||
// Maintenance every weekend
|
||||
const dayOfWeek = new Date(2024, 0, i + 1).getDay();
|
||||
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
||||
return {
|
||||
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
||||
uptime: 95,
|
||||
incidents: 0,
|
||||
totalDowntime: 72,
|
||||
status: 'maintenance' as const
|
||||
};
|
||||
}
|
||||
return {
|
||||
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
||||
uptime: 99.95,
|
||||
incidents: 0,
|
||||
totalDowntime: 0.7,
|
||||
status: 'operational' as const
|
||||
};
|
||||
}),
|
||||
overallUptime: 98.2,
|
||||
totalIncidents: 0
|
||||
}]
|
||||
},
|
||||
mixedYear: {
|
||||
name: 'Full Year Mixed',
|
||||
data: Array.from({ length: 12 }, (_, monthIndex) => {
|
||||
const year = 2023;
|
||||
const month = monthIndex;
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
// Different pattern each quarter
|
||||
let monthPattern = 'operational';
|
||||
if (monthIndex < 3) monthPattern = 'degraded';
|
||||
else if (monthIndex < 6) monthPattern = 'improving';
|
||||
else if (monthIndex < 9) monthPattern = 'stable';
|
||||
else monthPattern = 'volatile';
|
||||
|
||||
const days = Array.from({ length: daysInMonth }, (_, dayIndex) => {
|
||||
let uptime = 99.9;
|
||||
let status: IUptimeDay['status'] = 'operational';
|
||||
let incidents = 0;
|
||||
|
||||
if (monthPattern === 'degraded' && Math.random() < 0.3) {
|
||||
uptime = 85 + Math.random() * 10;
|
||||
status = 'degraded';
|
||||
incidents = 1;
|
||||
} else if (monthPattern === 'volatile' && Math.random() < 0.2) {
|
||||
uptime = 90 + Math.random() * 9;
|
||||
status = Math.random() < 0.5 ? 'partial_outage' : 'degraded';
|
||||
incidents = Math.floor(Math.random() * 3) + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(dayIndex + 1).padStart(2, '0')}`,
|
||||
uptime,
|
||||
incidents,
|
||||
totalDowntime: Math.floor((100 - uptime) * 14.4),
|
||||
status
|
||||
};
|
||||
});
|
||||
|
||||
const totalIncidents = days.reduce((sum, day) => sum + day.incidents, 0);
|
||||
const overallUptime = days.reduce((sum, day) => sum + day.uptime, 0) / days.length;
|
||||
|
||||
return {
|
||||
month: `${year}-${String(month + 1).padStart(2, '0')}`,
|
||||
days,
|
||||
overallUptime,
|
||||
totalIncidents
|
||||
};
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
// Initial setup
|
||||
let currentScenario = 'singleMonth';
|
||||
statusMonth.serviceId = 'edge-case-service';
|
||||
statusMonth.serviceName = 'Edge Case Service';
|
||||
statusMonth.monthlyData = scenarios[currentScenario].data;
|
||||
|
||||
// Create scenario controls
|
||||
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;
|
||||
statusMonth.loading = true;
|
||||
setTimeout(() => {
|
||||
statusMonth.monthlyData = scenario.data;
|
||||
statusMonth.loading = false;
|
||||
}, 300);
|
||||
};
|
||||
controls.appendChild(button);
|
||||
});
|
||||
|
||||
wrapperElement.appendChild(controls);
|
||||
}}
|
||||
>
|
||||
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
||||
</dees-demowrapper>
|
||||
</div>
|
||||
|
||||
<!-- Loading and Navigation States -->
|
||||
<div class="demo-section">
|
||||
<div class="demo-title">Loading and Navigation Features</div>
|
||||
<dees-demowrapper
|
||||
.runAfterRender=${async (wrapperElement: any) => {
|
||||
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
||||
|
||||
// Start with loading
|
||||
statusMonth.loading = true;
|
||||
statusMonth.serviceId = 'navigation-demo';
|
||||
statusMonth.serviceName = 'Navigation 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="toggleTooltip">Toggle Tooltip</button>';
|
||||
wrapperElement.appendChild(controls);
|
||||
|
||||
controls.querySelector('#toggleLoading')?.addEventListener('click', () => {
|
||||
statusMonth.loading = !statusMonth.loading;
|
||||
});
|
||||
|
||||
controls.querySelector('#loadSuccess')?.addEventListener('click', () => {
|
||||
statusMonth.loading = true;
|
||||
setTimeout(() => {
|
||||
const months = 6;
|
||||
const data: IMonthlyUptime[] = [];
|
||||
const now = new Date();
|
||||
|
||||
for (let i = months - 1; i >= 0; i--) {
|
||||
const monthDate = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
||||
const year = monthDate.getFullYear();
|
||||
const month = monthDate.getMonth();
|
||||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||||
|
||||
data.push({
|
||||
month: `${year}-${String(month + 1).padStart(2, '0')}`,
|
||||
days: Array.from({ length: daysInMonth }, (_, d) => ({
|
||||
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(d + 1).padStart(2, '0')}`,
|
||||
uptime: 99 + Math.random(),
|
||||
incidents: Math.random() < 0.05 ? 1 : 0,
|
||||
totalDowntime: Math.random() < 0.05 ? Math.floor(Math.random() * 60) : 0,
|
||||
status: Math.random() < 0.05 ? 'degraded' : 'operational'
|
||||
})),
|
||||
overallUptime: 99.5 + Math.random() * 0.4,
|
||||
totalIncidents: Math.floor(Math.random() * 5)
|
||||
});
|
||||
}
|
||||
|
||||
statusMonth.monthlyData = data;
|
||||
statusMonth.loading = false;
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
controls.querySelector('#loadError')?.addEventListener('click', () => {
|
||||
statusMonth.loading = true;
|
||||
setTimeout(() => {
|
||||
statusMonth.loading = false;
|
||||
statusMonth.monthlyData = [];
|
||||
statusMonth.errorMessage = 'Failed to load monthly uptime data';
|
||||
}, 1500);
|
||||
});
|
||||
|
||||
controls.querySelector('#toggleTooltip')?.addEventListener('click', () => {
|
||||
statusMonth.showTooltip = !statusMonth.showTooltip;
|
||||
const btn = controls.querySelector('#toggleTooltip');
|
||||
if (btn) btn.textContent = 'Toggle Tooltip (' + (statusMonth.showTooltip ? 'ON' : 'OFF') + ')';
|
||||
});
|
||||
|
||||
// Month navigation
|
||||
const navDiv = document.createElement('div');
|
||||
navDiv.className = 'month-nav';
|
||||
navDiv.innerHTML = '<button class="demo-button" id="prevMonth">← Previous</button>' +
|
||||
'<span id="currentMonth">Loading...</span>' +
|
||||
'<button class="demo-button" id="nextMonth">Next →</button>';
|
||||
wrapperElement.appendChild(navDiv);
|
||||
|
||||
let currentMonthIndex = 0;
|
||||
const updateNavigation = () => {
|
||||
const data = statusMonth.monthlyData || [];
|
||||
if (data.length > 0 && currentMonthIndex < data.length) {
|
||||
const month = data[currentMonthIndex];
|
||||
const currentMonthEl = navDiv.querySelector('#currentMonth');
|
||||
if (currentMonthEl) currentMonthEl.textContent = month.month;
|
||||
const prevBtn = navDiv.querySelector('#prevMonth') as HTMLButtonElement;
|
||||
const nextBtn = navDiv.querySelector('#nextMonth') as HTMLButtonElement;
|
||||
if (prevBtn) prevBtn.disabled = currentMonthIndex === 0;
|
||||
if (nextBtn) nextBtn.disabled = currentMonthIndex === data.length - 1;
|
||||
}
|
||||
};
|
||||
|
||||
navDiv.querySelector('#prevMonth')?.addEventListener('click', () => {
|
||||
if (currentMonthIndex > 0) {
|
||||
currentMonthIndex--;
|
||||
updateNavigation();
|
||||
// Highlight the month somehow
|
||||
statusMonth.highlightMonth = statusMonth.monthlyData[currentMonthIndex].month;
|
||||
}
|
||||
});
|
||||
|
||||
navDiv.querySelector('#nextMonth')?.addEventListener('click', () => {
|
||||
if (currentMonthIndex < (statusMonth.monthlyData?.length || 0) - 1) {
|
||||
currentMonthIndex++;
|
||||
updateNavigation();
|
||||
statusMonth.highlightMonth = statusMonth.monthlyData[currentMonthIndex].month;
|
||||
}
|
||||
});
|
||||
|
||||
// Initial load
|
||||
setTimeout(() => {
|
||||
const data = Array.from({ length: 3 }, (_, i) => ({
|
||||
month: `2024-${String(i + 1).padStart(2, '0')}`,
|
||||
days: Array.from({ length: 31 }, (_, d) => ({
|
||||
date: `2024-${String(i + 1).padStart(2, '0')}-${String(d + 1).padStart(2, '0')}`,
|
||||
uptime: 99.5 + Math.random() * 0.5,
|
||||
incidents: 0,
|
||||
totalDowntime: 0,
|
||||
status: 'operational' as const
|
||||
})),
|
||||
overallUptime: 99.7 + Math.random() * 0.3,
|
||||
totalIncidents: Math.floor(Math.random() * 3)
|
||||
}));
|
||||
|
||||
statusMonth.monthlyData = data;
|
||||
statusMonth.loading = false;
|
||||
statusMonth.showTooltip = true;
|
||||
updateNavigation();
|
||||
}, 1000);
|
||||
}}
|
||||
>
|
||||
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
||||
</dees-demowrapper>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
Reference in New Issue
Block a user