@uptime.link/statuspage 1.0.73 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_bundle/bundle.js +4096 -504
- package/dist_bundle/bundle.js.map +4 -4
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/elements/index.d.ts +3 -0
- package/dist_ts_web/elements/index.js +6 -1
- package/dist_ts_web/elements/internal/uplinternal-miniheading.d.ts +1 -0
- package/dist_ts_web/elements/internal/uplinternal-miniheading.js +78 -28
- package/dist_ts_web/elements/upl-statuspage-assetsselector.d.ts +14 -0
- package/dist_ts_web/elements/upl-statuspage-assetsselector.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-assetsselector.demo.js +575 -0
- package/dist_ts_web/elements/upl-statuspage-assetsselector.js +605 -43
- package/dist_ts_web/elements/upl-statuspage-footer.d.ts +46 -2
- package/dist_ts_web/elements/upl-statuspage-footer.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-footer.demo.js +679 -0
- package/dist_ts_web/elements/upl-statuspage-footer.js +792 -61
- package/dist_ts_web/elements/upl-statuspage-header.d.ts +5 -1
- package/dist_ts_web/elements/upl-statuspage-header.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-header.demo.js +220 -0
- package/dist_ts_web/elements/upl-statuspage-header.js +313 -86
- package/dist_ts_web/elements/upl-statuspage-incidents.d.ts +22 -4
- package/dist_ts_web/elements/upl-statuspage-incidents.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-incidents.demo.js +1147 -0
- package/dist_ts_web/elements/upl-statuspage-incidents.js +750 -74
- package/dist_ts_web/elements/upl-statuspage-pagetitle.d.ts +15 -0
- package/dist_ts_web/elements/upl-statuspage-pagetitle.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-pagetitle.demo.js +25 -0
- package/dist_ts_web/elements/upl-statuspage-pagetitle.js +148 -0
- package/dist_ts_web/elements/upl-statuspage-statsgrid.d.ts +23 -0
- package/dist_ts_web/elements/upl-statuspage-statsgrid.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-statsgrid.demo.js +295 -0
- package/dist_ts_web/elements/upl-statuspage-statsgrid.js +374 -0
- package/dist_ts_web/elements/upl-statuspage-statusbar.d.ts +4 -0
- package/dist_ts_web/elements/upl-statuspage-statusbar.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-statusbar.demo.js +365 -0
- package/dist_ts_web/elements/upl-statuspage-statusbar.js +357 -44
- package/dist_ts_web/elements/upl-statuspage-statusdetails.d.ts +14 -0
- package/dist_ts_web/elements/upl-statuspage-statusdetails.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-statusdetails.demo.js +706 -0
- package/dist_ts_web/elements/upl-statuspage-statusdetails.js +373 -63
- package/dist_ts_web/elements/upl-statuspage-statusmonth.d.ts +15 -0
- package/dist_ts_web/elements/upl-statuspage-statusmonth.demo.d.ts +1 -0
- package/dist_ts_web/elements/upl-statuspage-statusmonth.demo.js +798 -0
- package/dist_ts_web/elements/upl-statuspage-statusmonth.js +474 -100
- package/dist_ts_web/interfaces/index.d.ts +84 -0
- package/dist_ts_web/interfaces/index.js +4 -0
- package/dist_ts_web/pages/index.d.ts +4 -1
- package/dist_ts_web/pages/index.js +5 -2
- package/dist_ts_web/pages/statuspage-allgreen.d.ts +1 -0
- package/dist_ts_web/pages/statuspage-allgreen.js +386 -0
- package/dist_ts_web/pages/statuspage-demo.d.ts +1 -0
- package/dist_ts_web/pages/statuspage-demo.js +616 -0
- package/dist_ts_web/pages/statuspage-maintenance.d.ts +1 -0
- package/dist_ts_web/pages/statuspage-maintenance.js +544 -0
- package/dist_ts_web/pages/statuspage-outage.d.ts +1 -0
- package/dist_ts_web/pages/statuspage-outage.js +543 -0
- package/dist_ts_web/styles/shared.styles.d.ts +80 -0
- package/dist_ts_web/styles/shared.styles.js +351 -0
- package/dist_watch/bundle.js +54534 -26433
- package/dist_watch/bundle.js.map +4 -4
- package/dist_watch/index.html +3 -10
- package/npmextra.json +9 -3
- package/package.json +19 -19
- package/readme.hints.md +292 -0
- package/readme.md +326 -149
- package/readme.plan.md +261 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/index.ts +6 -0
- package/ts_web/elements/internal/uplinternal-miniheading.ts +24 -17
- package/ts_web/elements/upl-statuspage-assetsselector.demo.ts +607 -0
- package/ts_web/elements/upl-statuspage-assetsselector.ts +526 -18
- package/ts_web/elements/upl-statuspage-footer.demo.ts +744 -0
- package/ts_web/elements/upl-statuspage-footer.ts +608 -30
- package/ts_web/elements/upl-statuspage-header.demo.ts +241 -0
- package/ts_web/elements/upl-statuspage-header.ts +220 -52
- package/ts_web/elements/upl-statuspage-incidents.demo.ts +1216 -0
- package/ts_web/elements/upl-statuspage-incidents.ts +649 -26
- package/ts_web/elements/upl-statuspage-pagetitle.demo.ts +25 -0
- package/ts_web/elements/upl-statuspage-pagetitle.ts +89 -0
- package/ts_web/elements/upl-statuspage-statsgrid.demo.ts +315 -0
- package/ts_web/elements/upl-statuspage-statsgrid.ts +306 -0
- package/ts_web/elements/upl-statuspage-statusbar.demo.ts +393 -0
- package/ts_web/elements/upl-statuspage-statusbar.ts +281 -20
- package/ts_web/elements/upl-statuspage-statusdetails.demo.ts +754 -0
- package/ts_web/elements/upl-statuspage-statusdetails.ts +297 -38
- package/ts_web/elements/upl-statuspage-statusmonth.demo.ts +876 -0
- package/ts_web/elements/upl-statuspage-statusmonth.ts +397 -76
- package/ts_web/interfaces/index.ts +95 -0
- package/ts_web/pages/index.ts +4 -1
- package/ts_web/pages/statuspage-allgreen.ts +412 -0
- package/ts_web/pages/statuspage-demo.ts +653 -0
- package/ts_web/pages/statuspage-maintenance.ts +570 -0
- package/ts_web/pages/statuspage-outage.ts +568 -0
- package/ts_web/styles/shared.styles.ts +367 -0
- package/dist_ts_web/pages/page1.d.ts +0 -1
- package/dist_ts_web/pages/page1.js +0 -11
- package/ts_web/pages/page1.ts +0 -11
|
@@ -0,0 +1,876 @@
|
|
|
1
|
+
import { html } from '@design.estate/dees-element';
|
|
2
|
+
import type { IMonthlyUptime, IUptimeDay } from '../interfaces/index.js';
|
|
3
|
+
|
|
4
|
+
export const demoFunc = () => html`
|
|
5
|
+
<style>
|
|
6
|
+
.demo-container {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
gap: 20px;
|
|
10
|
+
}
|
|
11
|
+
.demo-section {
|
|
12
|
+
border: 1px solid #ddd;
|
|
13
|
+
border-radius: 8px;
|
|
14
|
+
padding: 20px;
|
|
15
|
+
background: #f5f5f5;
|
|
16
|
+
}
|
|
17
|
+
.demo-title {
|
|
18
|
+
font-size: 14px;
|
|
19
|
+
font-weight: 600;
|
|
20
|
+
margin-bottom: 16px;
|
|
21
|
+
color: #333;
|
|
22
|
+
}
|
|
23
|
+
.demo-controls {
|
|
24
|
+
display: flex;
|
|
25
|
+
gap: 10px;
|
|
26
|
+
margin-top: 16px;
|
|
27
|
+
flex-wrap: wrap;
|
|
28
|
+
}
|
|
29
|
+
.demo-button {
|
|
30
|
+
padding: 6px 12px;
|
|
31
|
+
border: 1px solid #ddd;
|
|
32
|
+
background: white;
|
|
33
|
+
border-radius: 4px;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
font-size: 13px;
|
|
36
|
+
}
|
|
37
|
+
.demo-button:hover {
|
|
38
|
+
background: #f0f0f0;
|
|
39
|
+
}
|
|
40
|
+
.demo-button.active {
|
|
41
|
+
background: #2196F3;
|
|
42
|
+
color: white;
|
|
43
|
+
border-color: #2196F3;
|
|
44
|
+
}
|
|
45
|
+
.demo-info {
|
|
46
|
+
margin-top: 12px;
|
|
47
|
+
padding: 12px;
|
|
48
|
+
background: white;
|
|
49
|
+
border-radius: 4px;
|
|
50
|
+
font-size: 13px;
|
|
51
|
+
line-height: 1.6;
|
|
52
|
+
}
|
|
53
|
+
.stats-display {
|
|
54
|
+
display: grid;
|
|
55
|
+
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
|
56
|
+
gap: 12px;
|
|
57
|
+
margin-top: 12px;
|
|
58
|
+
}
|
|
59
|
+
.stat-card {
|
|
60
|
+
background: white;
|
|
61
|
+
padding: 12px;
|
|
62
|
+
border-radius: 4px;
|
|
63
|
+
text-align: center;
|
|
64
|
+
}
|
|
65
|
+
.stat-value {
|
|
66
|
+
font-size: 18px;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
color: #2196F3;
|
|
69
|
+
}
|
|
70
|
+
.stat-label {
|
|
71
|
+
font-size: 11px;
|
|
72
|
+
color: #666;
|
|
73
|
+
margin-top: 4px;
|
|
74
|
+
}
|
|
75
|
+
.month-nav {
|
|
76
|
+
display: flex;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
align-items: center;
|
|
79
|
+
margin-top: 12px;
|
|
80
|
+
}
|
|
81
|
+
</style>
|
|
82
|
+
|
|
83
|
+
<div class="demo-container">
|
|
84
|
+
<!-- Different Month Patterns -->
|
|
85
|
+
<div class="demo-section">
|
|
86
|
+
<div class="demo-title">Different Month Patterns</div>
|
|
87
|
+
<dees-demowrapper
|
|
88
|
+
.runAfterRender=${async (wrapperElement: any) => {
|
|
89
|
+
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
|
90
|
+
|
|
91
|
+
// Pattern generators
|
|
92
|
+
const generateMonthPattern = (monthCount: number, pattern: 'perfect' | 'problematic' | 'improving' | 'degrading' | 'seasonal'): IMonthlyUptime[] => {
|
|
93
|
+
const months: IMonthlyUptime[] = [];
|
|
94
|
+
const now = new Date();
|
|
95
|
+
|
|
96
|
+
for (let monthOffset = monthCount - 1; monthOffset >= 0; monthOffset--) {
|
|
97
|
+
const monthDate = new Date(now.getFullYear(), now.getMonth() - monthOffset, 1);
|
|
98
|
+
const year = monthDate.getFullYear();
|
|
99
|
+
const month = monthDate.getMonth();
|
|
100
|
+
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
|
|
101
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
102
|
+
|
|
103
|
+
const days: IUptimeDay[] = [];
|
|
104
|
+
let totalIncidents = 0;
|
|
105
|
+
let totalUptimeMinutes = 0;
|
|
106
|
+
|
|
107
|
+
for (let day = 1; day <= daysInMonth; day++) {
|
|
108
|
+
let uptime = 100;
|
|
109
|
+
let incidents = 0;
|
|
110
|
+
let downtime = 0;
|
|
111
|
+
let status: IUptimeDay['status'] = 'operational';
|
|
112
|
+
|
|
113
|
+
switch (pattern) {
|
|
114
|
+
case 'perfect':
|
|
115
|
+
// Near perfect uptime
|
|
116
|
+
if (Math.random() < 0.02) {
|
|
117
|
+
uptime = 99.9 + Math.random() * 0.099;
|
|
118
|
+
status = 'degraded';
|
|
119
|
+
}
|
|
120
|
+
break;
|
|
121
|
+
|
|
122
|
+
case 'problematic':
|
|
123
|
+
// Frequent issues
|
|
124
|
+
const problemRand = Math.random();
|
|
125
|
+
if (problemRand < 0.1) {
|
|
126
|
+
uptime = 70 + Math.random() * 20;
|
|
127
|
+
incidents = 2 + Math.floor(Math.random() * 3);
|
|
128
|
+
status = 'major_outage';
|
|
129
|
+
} else if (problemRand < 0.25) {
|
|
130
|
+
uptime = 90 + Math.random() * 8;
|
|
131
|
+
incidents = 1 + Math.floor(Math.random() * 2);
|
|
132
|
+
status = 'partial_outage';
|
|
133
|
+
} else if (problemRand < 0.4) {
|
|
134
|
+
uptime = 98 + Math.random() * 1.5;
|
|
135
|
+
incidents = 1;
|
|
136
|
+
status = 'degraded';
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
|
|
140
|
+
case 'improving':
|
|
141
|
+
// Getting better over time
|
|
142
|
+
const improvementFactor = (monthCount - monthOffset) / monthCount;
|
|
143
|
+
const improveRand = Math.random();
|
|
144
|
+
if (improveRand < 0.3 * (1 - improvementFactor)) {
|
|
145
|
+
uptime = 85 + Math.random() * 10 + (improvementFactor * 10);
|
|
146
|
+
incidents = Math.max(0, 3 - Math.floor(improvementFactor * 3));
|
|
147
|
+
status = improvementFactor > 0.7 ? 'degraded' : 'partial_outage';
|
|
148
|
+
}
|
|
149
|
+
break;
|
|
150
|
+
|
|
151
|
+
case 'degrading':
|
|
152
|
+
// Getting worse over time
|
|
153
|
+
const degradationFactor = monthOffset / monthCount;
|
|
154
|
+
const degradeRand = Math.random();
|
|
155
|
+
if (degradeRand < 0.3 * (1 - degradationFactor)) {
|
|
156
|
+
uptime = 85 + Math.random() * 10 + (degradationFactor * 10);
|
|
157
|
+
incidents = Math.max(0, 3 - Math.floor(degradationFactor * 3));
|
|
158
|
+
status = degradationFactor > 0.7 ? 'degraded' : 'major_outage';
|
|
159
|
+
}
|
|
160
|
+
break;
|
|
161
|
+
|
|
162
|
+
case 'seasonal':
|
|
163
|
+
// Worse during certain months (simulating high traffic periods)
|
|
164
|
+
const monthNum = month;
|
|
165
|
+
if (monthNum === 11 || monthNum === 0) { // December, January
|
|
166
|
+
if (Math.random() < 0.3) {
|
|
167
|
+
uptime = 92 + Math.random() * 6;
|
|
168
|
+
incidents = 1 + Math.floor(Math.random() * 2);
|
|
169
|
+
status = 'degraded';
|
|
170
|
+
}
|
|
171
|
+
} else if (monthNum === 6 || monthNum === 7) { // July, August
|
|
172
|
+
if (Math.random() < 0.2) {
|
|
173
|
+
uptime = 94 + Math.random() * 5;
|
|
174
|
+
incidents = 1;
|
|
175
|
+
status = 'degraded';
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
downtime = Math.floor((100 - uptime) * 14.4);
|
|
182
|
+
totalIncidents += incidents;
|
|
183
|
+
totalUptimeMinutes += uptime * 14.4;
|
|
184
|
+
|
|
185
|
+
days.push({
|
|
186
|
+
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
|
187
|
+
uptime,
|
|
188
|
+
incidents,
|
|
189
|
+
totalDowntime: downtime,
|
|
190
|
+
status
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const overallUptime = totalUptimeMinutes / (daysInMonth * 1440) * 100;
|
|
195
|
+
|
|
196
|
+
months.push({
|
|
197
|
+
month: monthKey,
|
|
198
|
+
days,
|
|
199
|
+
overallUptime,
|
|
200
|
+
totalIncidents
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return months;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
// Initial setup
|
|
208
|
+
statusMonth.serviceId = 'production-api';
|
|
209
|
+
statusMonth.serviceName = 'Production API';
|
|
210
|
+
statusMonth.monthlyData = generateMonthPattern(6, 'perfect');
|
|
211
|
+
|
|
212
|
+
// Create pattern controls
|
|
213
|
+
const controls = document.createElement('div');
|
|
214
|
+
controls.className = 'demo-controls';
|
|
215
|
+
|
|
216
|
+
const patterns = [
|
|
217
|
+
{ key: 'perfect', label: 'Perfect Uptime' },
|
|
218
|
+
{ key: 'problematic', label: 'Problematic' },
|
|
219
|
+
{ key: 'improving', label: 'Improving Trend' },
|
|
220
|
+
{ key: 'degrading', label: 'Degrading Trend' },
|
|
221
|
+
{ key: 'seasonal', label: 'Seasonal Pattern' }
|
|
222
|
+
];
|
|
223
|
+
|
|
224
|
+
patterns.forEach((pattern, index) => {
|
|
225
|
+
const button = document.createElement('button');
|
|
226
|
+
button.className = 'demo-button' + (index === 0 ? ' active' : '');
|
|
227
|
+
button.textContent = pattern.label;
|
|
228
|
+
button.onclick = () => {
|
|
229
|
+
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
|
230
|
+
button.classList.add('active');
|
|
231
|
+
|
|
232
|
+
statusMonth.loading = true;
|
|
233
|
+
setTimeout(() => {
|
|
234
|
+
statusMonth.monthlyData = generateMonthPattern(6, pattern.key as any);
|
|
235
|
+
statusMonth.loading = false;
|
|
236
|
+
updateStats();
|
|
237
|
+
}, 500);
|
|
238
|
+
};
|
|
239
|
+
controls.appendChild(button);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
wrapperElement.appendChild(controls);
|
|
243
|
+
|
|
244
|
+
// Add statistics display
|
|
245
|
+
const statsDiv = document.createElement('div');
|
|
246
|
+
statsDiv.className = 'stats-display';
|
|
247
|
+
wrapperElement.appendChild(statsDiv);
|
|
248
|
+
|
|
249
|
+
const updateStats = () => {
|
|
250
|
+
const data = statusMonth.monthlyData || [];
|
|
251
|
+
const avgUptime = data.reduce((sum, month) => sum + month.overallUptime, 0) / data.length;
|
|
252
|
+
const totalIncidents = data.reduce((sum, month) => sum + month.totalIncidents, 0);
|
|
253
|
+
const worstMonth = data.reduce((worst, month) =>
|
|
254
|
+
month.overallUptime < worst.overallUptime ? month : worst, data[0]);
|
|
255
|
+
|
|
256
|
+
statsDiv.innerHTML = `
|
|
257
|
+
<div class="stat-card">
|
|
258
|
+
<div class="stat-value">${avgUptime.toFixed(3)}%</div>
|
|
259
|
+
<div class="stat-label">Avg Uptime</div>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="stat-card">
|
|
262
|
+
<div class="stat-value">${totalIncidents}</div>
|
|
263
|
+
<div class="stat-label">Total Incidents</div>
|
|
264
|
+
</div>
|
|
265
|
+
<div class="stat-card">
|
|
266
|
+
<div class="stat-value">${data.length}</div>
|
|
267
|
+
<div class="stat-label">Months</div>
|
|
268
|
+
</div>
|
|
269
|
+
<div class="stat-card">
|
|
270
|
+
<div class="stat-value">${worstMonth ? worstMonth.overallUptime.toFixed(2) : '100'}%</div>
|
|
271
|
+
<div class="stat-label">Worst Month</div>
|
|
272
|
+
</div>
|
|
273
|
+
`;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
updateStats();
|
|
277
|
+
|
|
278
|
+
// Handle day clicks
|
|
279
|
+
statusMonth.addEventListener('dayClick', (event: CustomEvent) => {
|
|
280
|
+
const { date, uptime, incidents, status, totalDowntime } = event.detail;
|
|
281
|
+
alert(`Day Details for ${date}:\n\nUptime: ${uptime.toFixed(3)}%\nIncidents: ${incidents}\nStatus: ${status}\nDowntime: ${totalDowntime} minutes`);
|
|
282
|
+
});
|
|
283
|
+
}}
|
|
284
|
+
>
|
|
285
|
+
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
|
286
|
+
</dees-demowrapper>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<!-- Different Time Spans -->
|
|
290
|
+
<div class="demo-section">
|
|
291
|
+
<div class="demo-title">Different Time Spans</div>
|
|
292
|
+
<dees-demowrapper
|
|
293
|
+
.runAfterRender=${async (wrapperElement: any) => {
|
|
294
|
+
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
|
295
|
+
|
|
296
|
+
// Generate data for different time spans
|
|
297
|
+
const generateTimeSpanData = (months: number): IMonthlyUptime[] => {
|
|
298
|
+
const data: IMonthlyUptime[] = [];
|
|
299
|
+
const now = new Date();
|
|
300
|
+
|
|
301
|
+
for (let monthOffset = months - 1; monthOffset >= 0; monthOffset--) {
|
|
302
|
+
const monthDate = new Date(now.getFullYear(), now.getMonth() - monthOffset, 1);
|
|
303
|
+
const year = monthDate.getFullYear();
|
|
304
|
+
const month = monthDate.getMonth();
|
|
305
|
+
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
|
|
306
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
307
|
+
|
|
308
|
+
const days: IUptimeDay[] = [];
|
|
309
|
+
let totalIncidents = 0;
|
|
310
|
+
let totalUptimeMinutes = 0;
|
|
311
|
+
|
|
312
|
+
for (let day = 1; day <= daysInMonth; day++) {
|
|
313
|
+
// Create realistic patterns
|
|
314
|
+
let uptime = 99.9 + Math.random() * 0.099;
|
|
315
|
+
let incidents = 0;
|
|
316
|
+
let status: IUptimeDay['status'] = 'operational';
|
|
317
|
+
|
|
318
|
+
if (Math.random() < 0.05) {
|
|
319
|
+
uptime = 95 + Math.random() * 4.9;
|
|
320
|
+
incidents = 1;
|
|
321
|
+
status = 'degraded';
|
|
322
|
+
} else if (Math.random() < 0.01) {
|
|
323
|
+
uptime = 85 + Math.random() * 10;
|
|
324
|
+
incidents = 2;
|
|
325
|
+
status = 'partial_outage';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const downtime = Math.floor((100 - uptime) * 14.4);
|
|
329
|
+
totalIncidents += incidents;
|
|
330
|
+
totalUptimeMinutes += uptime * 14.4;
|
|
331
|
+
|
|
332
|
+
days.push({
|
|
333
|
+
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
|
334
|
+
uptime,
|
|
335
|
+
incidents,
|
|
336
|
+
totalDowntime: downtime,
|
|
337
|
+
status
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const overallUptime = totalUptimeMinutes / (daysInMonth * 1440) * 100;
|
|
342
|
+
|
|
343
|
+
data.push({
|
|
344
|
+
month: monthKey,
|
|
345
|
+
days,
|
|
346
|
+
overallUptime,
|
|
347
|
+
totalIncidents
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return data;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
// Initial setup
|
|
355
|
+
statusMonth.serviceId = 'multi-region-lb';
|
|
356
|
+
statusMonth.serviceName = 'Multi-Region Load Balancer';
|
|
357
|
+
statusMonth.monthlyData = generateTimeSpanData(3);
|
|
358
|
+
|
|
359
|
+
// Create time span controls
|
|
360
|
+
const controls = document.createElement('div');
|
|
361
|
+
controls.className = 'demo-controls';
|
|
362
|
+
|
|
363
|
+
const timeSpans = [
|
|
364
|
+
{ months: 3, label: 'Last 3 Months' },
|
|
365
|
+
{ months: 6, label: 'Last 6 Months' },
|
|
366
|
+
{ months: 12, label: 'Last 12 Months' },
|
|
367
|
+
{ months: 24, label: 'Last 24 Months' }
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
timeSpans.forEach((span, index) => {
|
|
371
|
+
const button = document.createElement('button');
|
|
372
|
+
button.className = 'demo-button' + (index === 0 ? ' active' : '');
|
|
373
|
+
button.textContent = span.label;
|
|
374
|
+
button.onclick = () => {
|
|
375
|
+
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
|
376
|
+
button.classList.add('active');
|
|
377
|
+
|
|
378
|
+
statusMonth.loading = true;
|
|
379
|
+
setTimeout(() => {
|
|
380
|
+
statusMonth.monthlyData = generateTimeSpanData(span.months);
|
|
381
|
+
statusMonth.loading = false;
|
|
382
|
+
}, 500);
|
|
383
|
+
};
|
|
384
|
+
controls.appendChild(button);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
wrapperElement.appendChild(controls);
|
|
388
|
+
|
|
389
|
+
// Add info display
|
|
390
|
+
const info = document.createElement('div');
|
|
391
|
+
info.className = 'demo-info';
|
|
392
|
+
info.innerHTML = 'Click on different time spans to see historical uptime data. The component automatically adjusts the display based on the number of months.';
|
|
393
|
+
wrapperElement.appendChild(info);
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
|
397
|
+
</dees-demowrapper>
|
|
398
|
+
</div>
|
|
399
|
+
|
|
400
|
+
<!-- Current Month Real-time Updates -->
|
|
401
|
+
<div class="demo-section">
|
|
402
|
+
<div class="demo-title">Current Month with Real-time Updates</div>
|
|
403
|
+
<dees-demowrapper
|
|
404
|
+
.runAfterRender=${async (wrapperElement: any) => {
|
|
405
|
+
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
|
406
|
+
|
|
407
|
+
// Generate current month data
|
|
408
|
+
const generateCurrentMonthData = (): IMonthlyUptime[] => {
|
|
409
|
+
const now = new Date();
|
|
410
|
+
const year = now.getFullYear();
|
|
411
|
+
const month = now.getMonth();
|
|
412
|
+
const today = now.getDate();
|
|
413
|
+
const monthKey = `${year}-${String(month + 1).padStart(2, '0')}`;
|
|
414
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
415
|
+
|
|
416
|
+
const days: IUptimeDay[] = [];
|
|
417
|
+
let totalIncidents = 0;
|
|
418
|
+
let totalUptimeMinutes = 0;
|
|
419
|
+
|
|
420
|
+
// Generate data only up to today
|
|
421
|
+
for (let day = 1; day <= today; day++) {
|
|
422
|
+
let uptime = 99.9 + Math.random() * 0.099;
|
|
423
|
+
let incidents = 0;
|
|
424
|
+
let status: IUptimeDay['status'] = 'operational';
|
|
425
|
+
|
|
426
|
+
// Today might have ongoing issues
|
|
427
|
+
if (day === today) {
|
|
428
|
+
if (Math.random() < 0.3) {
|
|
429
|
+
uptime = 95 + Math.random() * 4;
|
|
430
|
+
incidents = 1;
|
|
431
|
+
status = 'degraded';
|
|
432
|
+
}
|
|
433
|
+
} else if (Math.random() < 0.05) {
|
|
434
|
+
uptime = 97 + Math.random() * 2.9;
|
|
435
|
+
incidents = 1;
|
|
436
|
+
status = 'degraded';
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const downtime = Math.floor((100 - uptime) * 14.4);
|
|
440
|
+
totalIncidents += incidents;
|
|
441
|
+
totalUptimeMinutes += uptime * 14.4;
|
|
442
|
+
|
|
443
|
+
days.push({
|
|
444
|
+
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
|
445
|
+
uptime,
|
|
446
|
+
incidents,
|
|
447
|
+
totalDowntime: downtime,
|
|
448
|
+
status
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Fill remaining days with placeholder
|
|
453
|
+
for (let day = today + 1; day <= daysInMonth; day++) {
|
|
454
|
+
days.push({
|
|
455
|
+
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
|
456
|
+
uptime: 0,
|
|
457
|
+
incidents: 0,
|
|
458
|
+
totalDowntime: 0,
|
|
459
|
+
status: 'operational'
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
const overallUptime = today > 0 ? totalUptimeMinutes / (today * 1440) * 100 : 100;
|
|
464
|
+
|
|
465
|
+
return [{
|
|
466
|
+
month: monthKey,
|
|
467
|
+
days,
|
|
468
|
+
overallUptime,
|
|
469
|
+
totalIncidents
|
|
470
|
+
}];
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// Initial setup
|
|
474
|
+
statusMonth.serviceId = 'realtime-monitor';
|
|
475
|
+
statusMonth.serviceName = 'Real-time Monitoring Service';
|
|
476
|
+
statusMonth.monthlyData = generateCurrentMonthData();
|
|
477
|
+
statusMonth.showCurrentDay = true;
|
|
478
|
+
|
|
479
|
+
// Update today's status periodically
|
|
480
|
+
const updateInterval = setInterval(() => {
|
|
481
|
+
const data = statusMonth.monthlyData;
|
|
482
|
+
if (data && data.length > 0) {
|
|
483
|
+
const currentMonth = data[0];
|
|
484
|
+
const today = new Date().getDate() - 1;
|
|
485
|
+
|
|
486
|
+
if (currentMonth.days[today]) {
|
|
487
|
+
// Simulate status changes
|
|
488
|
+
const rand = Math.random();
|
|
489
|
+
if (rand < 0.1) {
|
|
490
|
+
currentMonth.days[today].uptime = 95 + Math.random() * 4.9;
|
|
491
|
+
currentMonth.days[today].incidents = (currentMonth.days[today].incidents || 0) + 1;
|
|
492
|
+
currentMonth.days[today].status = 'degraded';
|
|
493
|
+
currentMonth.days[today].totalDowntime = Math.floor((100 - currentMonth.days[today].uptime) * 14.4);
|
|
494
|
+
|
|
495
|
+
// Recalculate overall uptime
|
|
496
|
+
let totalUptime = 0;
|
|
497
|
+
let validDays = 0;
|
|
498
|
+
currentMonth.days.forEach((day, index) => {
|
|
499
|
+
if (index <= today && day.uptime > 0) {
|
|
500
|
+
totalUptime += day.uptime;
|
|
501
|
+
validDays++;
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
currentMonth.overallUptime = validDays > 0 ? totalUptime / validDays : 100;
|
|
505
|
+
currentMonth.totalIncidents = currentMonth.days.reduce((sum, day) => sum + (day.incidents || 0), 0);
|
|
506
|
+
|
|
507
|
+
statusMonth.requestUpdate();
|
|
508
|
+
logUpdate('Status degraded - Uptime: ' + currentMonth.days[today].uptime.toFixed(2) + '%');
|
|
509
|
+
} else if (rand < 0.05 && currentMonth.days[today].status !== 'operational') {
|
|
510
|
+
// Recover from issues
|
|
511
|
+
currentMonth.days[today].uptime = 99.9 + Math.random() * 0.099;
|
|
512
|
+
currentMonth.days[today].status = 'operational';
|
|
513
|
+
currentMonth.days[today].totalDowntime = Math.floor((100 - currentMonth.days[today].uptime) * 14.4);
|
|
514
|
+
|
|
515
|
+
statusMonth.requestUpdate();
|
|
516
|
+
logUpdate('Service recovered to operational status');
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}, 3000);
|
|
521
|
+
|
|
522
|
+
// Create controls
|
|
523
|
+
const controls = document.createElement('div');
|
|
524
|
+
controls.className = 'demo-controls';
|
|
525
|
+
controls.innerHTML = '<button class="demo-button" id="simulateOutage">Simulate Outage</button>' +
|
|
526
|
+
'<button class="demo-button" id="simulateRecovery">Simulate Recovery</button>' +
|
|
527
|
+
'<button class="demo-button" id="refreshData">Refresh Data</button>';
|
|
528
|
+
wrapperElement.appendChild(controls);
|
|
529
|
+
|
|
530
|
+
controls.querySelector('#simulateOutage')?.addEventListener('click', () => {
|
|
531
|
+
const data = statusMonth.monthlyData;
|
|
532
|
+
if (data && data.length > 0) {
|
|
533
|
+
const today = new Date().getDate() - 1;
|
|
534
|
+
data[0].days[today].uptime = 85 + Math.random() * 10;
|
|
535
|
+
data[0].days[today].incidents = (data[0].days[today].incidents || 0) + 1;
|
|
536
|
+
data[0].days[today].status = 'major_outage';
|
|
537
|
+
data[0].days[today].totalDowntime = Math.floor((100 - data[0].days[today].uptime) * 14.4);
|
|
538
|
+
statusMonth.requestUpdate();
|
|
539
|
+
logUpdate('Major outage simulated');
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
controls.querySelector('#simulateRecovery')?.addEventListener('click', () => {
|
|
544
|
+
const data = statusMonth.monthlyData;
|
|
545
|
+
if (data && data.length > 0) {
|
|
546
|
+
const today = new Date().getDate() - 1;
|
|
547
|
+
data[0].days[today].uptime = 99.95;
|
|
548
|
+
data[0].days[today].status = 'operational';
|
|
549
|
+
data[0].days[today].totalDowntime = Math.floor((100 - data[0].days[today].uptime) * 14.4);
|
|
550
|
+
statusMonth.requestUpdate();
|
|
551
|
+
logUpdate('Service recovered');
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
controls.querySelector('#refreshData')?.addEventListener('click', () => {
|
|
556
|
+
statusMonth.monthlyData = generateCurrentMonthData();
|
|
557
|
+
logUpdate('Data refreshed');
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
// Add update log
|
|
561
|
+
const logDiv = document.createElement('div');
|
|
562
|
+
logDiv.className = 'demo-info';
|
|
563
|
+
logDiv.style.maxHeight = '100px';
|
|
564
|
+
logDiv.style.overflowY = 'auto';
|
|
565
|
+
logDiv.innerHTML = '<strong>Update Log:</strong><br>';
|
|
566
|
+
wrapperElement.appendChild(logDiv);
|
|
567
|
+
|
|
568
|
+
const logUpdate = (message: string) => {
|
|
569
|
+
const time = new Date().toLocaleTimeString();
|
|
570
|
+
logDiv.innerHTML += '[' + time + '] ' + message + '<br>';
|
|
571
|
+
logDiv.scrollTop = logDiv.scrollHeight;
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
// Cleanup
|
|
575
|
+
wrapperElement.addEventListener('remove', () => {
|
|
576
|
+
clearInterval(updateInterval);
|
|
577
|
+
});
|
|
578
|
+
}}
|
|
579
|
+
>
|
|
580
|
+
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
|
581
|
+
</dees-demowrapper>
|
|
582
|
+
</div>
|
|
583
|
+
|
|
584
|
+
<!-- Edge Cases -->
|
|
585
|
+
<div class="demo-section">
|
|
586
|
+
<div class="demo-title">Edge Cases and Special Scenarios</div>
|
|
587
|
+
<dees-demowrapper
|
|
588
|
+
.runAfterRender=${async (wrapperElement: any) => {
|
|
589
|
+
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
|
590
|
+
|
|
591
|
+
const scenarios = {
|
|
592
|
+
noData: {
|
|
593
|
+
name: 'No Data',
|
|
594
|
+
data: []
|
|
595
|
+
},
|
|
596
|
+
singleMonth: {
|
|
597
|
+
name: 'Single Month',
|
|
598
|
+
data: [{
|
|
599
|
+
month: '2024-01',
|
|
600
|
+
days: Array.from({ length: 31 }, (_, i) => ({
|
|
601
|
+
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
|
602
|
+
uptime: 99.9 + Math.random() * 0.099,
|
|
603
|
+
incidents: 0,
|
|
604
|
+
totalDowntime: 0,
|
|
605
|
+
status: 'operational' as const
|
|
606
|
+
})),
|
|
607
|
+
overallUptime: 99.95,
|
|
608
|
+
totalIncidents: 0
|
|
609
|
+
}]
|
|
610
|
+
},
|
|
611
|
+
allDown: {
|
|
612
|
+
name: 'Complete Outage Month',
|
|
613
|
+
data: [{
|
|
614
|
+
month: '2024-01',
|
|
615
|
+
days: Array.from({ length: 31 }, (_, i) => ({
|
|
616
|
+
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
|
617
|
+
uptime: 0,
|
|
618
|
+
incidents: 5,
|
|
619
|
+
totalDowntime: 1440,
|
|
620
|
+
status: 'major_outage' as const
|
|
621
|
+
})),
|
|
622
|
+
overallUptime: 0,
|
|
623
|
+
totalIncidents: 155
|
|
624
|
+
}]
|
|
625
|
+
},
|
|
626
|
+
maintenanceMonth: {
|
|
627
|
+
name: 'Maintenance Heavy Month',
|
|
628
|
+
data: [{
|
|
629
|
+
month: '2024-01',
|
|
630
|
+
days: Array.from({ length: 31 }, (_, i) => {
|
|
631
|
+
// Maintenance every weekend
|
|
632
|
+
const dayOfWeek = new Date(2024, 0, i + 1).getDay();
|
|
633
|
+
if (dayOfWeek === 0 || dayOfWeek === 6) {
|
|
634
|
+
return {
|
|
635
|
+
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
|
636
|
+
uptime: 95,
|
|
637
|
+
incidents: 0,
|
|
638
|
+
totalDowntime: 72,
|
|
639
|
+
status: 'maintenance' as const
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
return {
|
|
643
|
+
date: `2024-01-${String(i + 1).padStart(2, '0')}`,
|
|
644
|
+
uptime: 99.95,
|
|
645
|
+
incidents: 0,
|
|
646
|
+
totalDowntime: 0.7,
|
|
647
|
+
status: 'operational' as const
|
|
648
|
+
};
|
|
649
|
+
}),
|
|
650
|
+
overallUptime: 98.2,
|
|
651
|
+
totalIncidents: 0
|
|
652
|
+
}]
|
|
653
|
+
},
|
|
654
|
+
mixedYear: {
|
|
655
|
+
name: 'Full Year Mixed',
|
|
656
|
+
data: Array.from({ length: 12 }, (_, monthIndex) => {
|
|
657
|
+
const year = 2023;
|
|
658
|
+
const month = monthIndex;
|
|
659
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
660
|
+
|
|
661
|
+
// Different pattern each quarter
|
|
662
|
+
let monthPattern = 'operational';
|
|
663
|
+
if (monthIndex < 3) monthPattern = 'degraded';
|
|
664
|
+
else if (monthIndex < 6) monthPattern = 'improving';
|
|
665
|
+
else if (monthIndex < 9) monthPattern = 'stable';
|
|
666
|
+
else monthPattern = 'volatile';
|
|
667
|
+
|
|
668
|
+
const days = Array.from({ length: daysInMonth }, (_, dayIndex) => {
|
|
669
|
+
let uptime = 99.9;
|
|
670
|
+
let status: IUptimeDay['status'] = 'operational';
|
|
671
|
+
let incidents = 0;
|
|
672
|
+
|
|
673
|
+
if (monthPattern === 'degraded' && Math.random() < 0.3) {
|
|
674
|
+
uptime = 85 + Math.random() * 10;
|
|
675
|
+
status = 'degraded';
|
|
676
|
+
incidents = 1;
|
|
677
|
+
} else if (monthPattern === 'volatile' && Math.random() < 0.2) {
|
|
678
|
+
uptime = 90 + Math.random() * 9;
|
|
679
|
+
status = Math.random() < 0.5 ? 'partial_outage' : 'degraded';
|
|
680
|
+
incidents = Math.floor(Math.random() * 3) + 1;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return {
|
|
684
|
+
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(dayIndex + 1).padStart(2, '0')}`,
|
|
685
|
+
uptime,
|
|
686
|
+
incidents,
|
|
687
|
+
totalDowntime: Math.floor((100 - uptime) * 14.4),
|
|
688
|
+
status
|
|
689
|
+
};
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
const totalIncidents = days.reduce((sum, day) => sum + day.incidents, 0);
|
|
693
|
+
const overallUptime = days.reduce((sum, day) => sum + day.uptime, 0) / days.length;
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
month: `${year}-${String(month + 1).padStart(2, '0')}`,
|
|
697
|
+
days,
|
|
698
|
+
overallUptime,
|
|
699
|
+
totalIncidents
|
|
700
|
+
};
|
|
701
|
+
})
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
// Initial setup
|
|
706
|
+
let currentScenario = 'singleMonth';
|
|
707
|
+
statusMonth.serviceId = 'edge-case-service';
|
|
708
|
+
statusMonth.serviceName = 'Edge Case Service';
|
|
709
|
+
statusMonth.monthlyData = scenarios[currentScenario].data;
|
|
710
|
+
|
|
711
|
+
// Create scenario controls
|
|
712
|
+
const controls = document.createElement('div');
|
|
713
|
+
controls.className = 'demo-controls';
|
|
714
|
+
|
|
715
|
+
Object.entries(scenarios).forEach(([key, scenario]) => {
|
|
716
|
+
const button = document.createElement('button');
|
|
717
|
+
button.className = 'demo-button' + (key === currentScenario ? ' active' : '');
|
|
718
|
+
button.textContent = scenario.name;
|
|
719
|
+
button.onclick = () => {
|
|
720
|
+
controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
|
|
721
|
+
button.classList.add('active');
|
|
722
|
+
|
|
723
|
+
currentScenario = key;
|
|
724
|
+
statusMonth.loading = true;
|
|
725
|
+
setTimeout(() => {
|
|
726
|
+
statusMonth.monthlyData = scenario.data;
|
|
727
|
+
statusMonth.loading = false;
|
|
728
|
+
}, 300);
|
|
729
|
+
};
|
|
730
|
+
controls.appendChild(button);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
wrapperElement.appendChild(controls);
|
|
734
|
+
}}
|
|
735
|
+
>
|
|
736
|
+
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
|
737
|
+
</dees-demowrapper>
|
|
738
|
+
</div>
|
|
739
|
+
|
|
740
|
+
<!-- Loading and Navigation States -->
|
|
741
|
+
<div class="demo-section">
|
|
742
|
+
<div class="demo-title">Loading and Navigation Features</div>
|
|
743
|
+
<dees-demowrapper
|
|
744
|
+
.runAfterRender=${async (wrapperElement: any) => {
|
|
745
|
+
const statusMonth = wrapperElement.querySelector('upl-statuspage-statusmonth') as any;
|
|
746
|
+
|
|
747
|
+
// Start with loading
|
|
748
|
+
statusMonth.loading = true;
|
|
749
|
+
statusMonth.serviceId = 'navigation-demo';
|
|
750
|
+
statusMonth.serviceName = 'Navigation Demo Service';
|
|
751
|
+
|
|
752
|
+
const controls = document.createElement('div');
|
|
753
|
+
controls.className = 'demo-controls';
|
|
754
|
+
controls.innerHTML = '<button class="demo-button" id="toggleLoading">Toggle Loading</button>' +
|
|
755
|
+
'<button class="demo-button" id="loadSuccess">Load Successfully</button>' +
|
|
756
|
+
'<button class="demo-button" id="loadError">Simulate Error</button>' +
|
|
757
|
+
'<button class="demo-button" id="toggleTooltip">Toggle Tooltip</button>';
|
|
758
|
+
wrapperElement.appendChild(controls);
|
|
759
|
+
|
|
760
|
+
controls.querySelector('#toggleLoading')?.addEventListener('click', () => {
|
|
761
|
+
statusMonth.loading = !statusMonth.loading;
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
controls.querySelector('#loadSuccess')?.addEventListener('click', () => {
|
|
765
|
+
statusMonth.loading = true;
|
|
766
|
+
setTimeout(() => {
|
|
767
|
+
const months = 6;
|
|
768
|
+
const data: IMonthlyUptime[] = [];
|
|
769
|
+
const now = new Date();
|
|
770
|
+
|
|
771
|
+
for (let i = months - 1; i >= 0; i--) {
|
|
772
|
+
const monthDate = new Date(now.getFullYear(), now.getMonth() - i, 1);
|
|
773
|
+
const year = monthDate.getFullYear();
|
|
774
|
+
const month = monthDate.getMonth();
|
|
775
|
+
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
|
776
|
+
|
|
777
|
+
data.push({
|
|
778
|
+
month: `${year}-${String(month + 1).padStart(2, '0')}`,
|
|
779
|
+
days: Array.from({ length: daysInMonth }, (_, d) => ({
|
|
780
|
+
date: `${year}-${String(month + 1).padStart(2, '0')}-${String(d + 1).padStart(2, '0')}`,
|
|
781
|
+
uptime: 99 + Math.random(),
|
|
782
|
+
incidents: Math.random() < 0.05 ? 1 : 0,
|
|
783
|
+
totalDowntime: Math.random() < 0.05 ? Math.floor(Math.random() * 60) : 0,
|
|
784
|
+
status: Math.random() < 0.05 ? 'degraded' : 'operational'
|
|
785
|
+
})),
|
|
786
|
+
overallUptime: 99.5 + Math.random() * 0.4,
|
|
787
|
+
totalIncidents: Math.floor(Math.random() * 5)
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
statusMonth.monthlyData = data;
|
|
792
|
+
statusMonth.loading = false;
|
|
793
|
+
}, 1000);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
controls.querySelector('#loadError')?.addEventListener('click', () => {
|
|
797
|
+
statusMonth.loading = true;
|
|
798
|
+
setTimeout(() => {
|
|
799
|
+
statusMonth.loading = false;
|
|
800
|
+
statusMonth.monthlyData = [];
|
|
801
|
+
statusMonth.errorMessage = 'Failed to load monthly uptime data';
|
|
802
|
+
}, 1500);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
controls.querySelector('#toggleTooltip')?.addEventListener('click', () => {
|
|
806
|
+
statusMonth.showTooltip = !statusMonth.showTooltip;
|
|
807
|
+
const btn = controls.querySelector('#toggleTooltip');
|
|
808
|
+
if (btn) btn.textContent = 'Toggle Tooltip (' + (statusMonth.showTooltip ? 'ON' : 'OFF') + ')';
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// Month navigation
|
|
812
|
+
const navDiv = document.createElement('div');
|
|
813
|
+
navDiv.className = 'month-nav';
|
|
814
|
+
navDiv.innerHTML = '<button class="demo-button" id="prevMonth">← Previous</button>' +
|
|
815
|
+
'<span id="currentMonth">Loading...</span>' +
|
|
816
|
+
'<button class="demo-button" id="nextMonth">Next →</button>';
|
|
817
|
+
wrapperElement.appendChild(navDiv);
|
|
818
|
+
|
|
819
|
+
let currentMonthIndex = 0;
|
|
820
|
+
const updateNavigation = () => {
|
|
821
|
+
const data = statusMonth.monthlyData || [];
|
|
822
|
+
if (data.length > 0 && currentMonthIndex < data.length) {
|
|
823
|
+
const month = data[currentMonthIndex];
|
|
824
|
+
const currentMonthEl = navDiv.querySelector('#currentMonth');
|
|
825
|
+
if (currentMonthEl) currentMonthEl.textContent = month.month;
|
|
826
|
+
const prevBtn = navDiv.querySelector('#prevMonth') as HTMLButtonElement;
|
|
827
|
+
const nextBtn = navDiv.querySelector('#nextMonth') as HTMLButtonElement;
|
|
828
|
+
if (prevBtn) prevBtn.disabled = currentMonthIndex === 0;
|
|
829
|
+
if (nextBtn) nextBtn.disabled = currentMonthIndex === data.length - 1;
|
|
830
|
+
}
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
navDiv.querySelector('#prevMonth')?.addEventListener('click', () => {
|
|
834
|
+
if (currentMonthIndex > 0) {
|
|
835
|
+
currentMonthIndex--;
|
|
836
|
+
updateNavigation();
|
|
837
|
+
// Highlight the month somehow
|
|
838
|
+
statusMonth.highlightMonth = statusMonth.monthlyData[currentMonthIndex].month;
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
navDiv.querySelector('#nextMonth')?.addEventListener('click', () => {
|
|
843
|
+
if (currentMonthIndex < (statusMonth.monthlyData?.length || 0) - 1) {
|
|
844
|
+
currentMonthIndex++;
|
|
845
|
+
updateNavigation();
|
|
846
|
+
statusMonth.highlightMonth = statusMonth.monthlyData[currentMonthIndex].month;
|
|
847
|
+
}
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
// Initial load
|
|
851
|
+
setTimeout(() => {
|
|
852
|
+
const data = Array.from({ length: 3 }, (_, i) => ({
|
|
853
|
+
month: `2024-${String(i + 1).padStart(2, '0')}`,
|
|
854
|
+
days: Array.from({ length: 31 }, (_, d) => ({
|
|
855
|
+
date: `2024-${String(i + 1).padStart(2, '0')}-${String(d + 1).padStart(2, '0')}`,
|
|
856
|
+
uptime: 99.5 + Math.random() * 0.5,
|
|
857
|
+
incidents: 0,
|
|
858
|
+
totalDowntime: 0,
|
|
859
|
+
status: 'operational' as const
|
|
860
|
+
})),
|
|
861
|
+
overallUptime: 99.7 + Math.random() * 0.3,
|
|
862
|
+
totalIncidents: Math.floor(Math.random() * 3)
|
|
863
|
+
}));
|
|
864
|
+
|
|
865
|
+
statusMonth.monthlyData = data;
|
|
866
|
+
statusMonth.loading = false;
|
|
867
|
+
statusMonth.showTooltip = true;
|
|
868
|
+
updateNavigation();
|
|
869
|
+
}, 1000);
|
|
870
|
+
}}
|
|
871
|
+
>
|
|
872
|
+
<upl-statuspage-statusmonth></upl-statuspage-statusmonth>
|
|
873
|
+
</dees-demowrapper>
|
|
874
|
+
</div>
|
|
875
|
+
</div>
|
|
876
|
+
`;
|