@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.
Files changed (96) hide show
  1. package/dist_bundle/bundle.js +4096 -504
  2. package/dist_bundle/bundle.js.map +4 -4
  3. package/dist_ts_web/00_commitinfo_data.js +2 -2
  4. package/dist_ts_web/elements/index.d.ts +3 -0
  5. package/dist_ts_web/elements/index.js +6 -1
  6. package/dist_ts_web/elements/internal/uplinternal-miniheading.d.ts +1 -0
  7. package/dist_ts_web/elements/internal/uplinternal-miniheading.js +78 -28
  8. package/dist_ts_web/elements/upl-statuspage-assetsselector.d.ts +14 -0
  9. package/dist_ts_web/elements/upl-statuspage-assetsselector.demo.d.ts +1 -0
  10. package/dist_ts_web/elements/upl-statuspage-assetsselector.demo.js +575 -0
  11. package/dist_ts_web/elements/upl-statuspage-assetsselector.js +605 -43
  12. package/dist_ts_web/elements/upl-statuspage-footer.d.ts +46 -2
  13. package/dist_ts_web/elements/upl-statuspage-footer.demo.d.ts +1 -0
  14. package/dist_ts_web/elements/upl-statuspage-footer.demo.js +679 -0
  15. package/dist_ts_web/elements/upl-statuspage-footer.js +792 -61
  16. package/dist_ts_web/elements/upl-statuspage-header.d.ts +5 -1
  17. package/dist_ts_web/elements/upl-statuspage-header.demo.d.ts +1 -0
  18. package/dist_ts_web/elements/upl-statuspage-header.demo.js +220 -0
  19. package/dist_ts_web/elements/upl-statuspage-header.js +313 -86
  20. package/dist_ts_web/elements/upl-statuspage-incidents.d.ts +22 -4
  21. package/dist_ts_web/elements/upl-statuspage-incidents.demo.d.ts +1 -0
  22. package/dist_ts_web/elements/upl-statuspage-incidents.demo.js +1147 -0
  23. package/dist_ts_web/elements/upl-statuspage-incidents.js +750 -74
  24. package/dist_ts_web/elements/upl-statuspage-pagetitle.d.ts +15 -0
  25. package/dist_ts_web/elements/upl-statuspage-pagetitle.demo.d.ts +1 -0
  26. package/dist_ts_web/elements/upl-statuspage-pagetitle.demo.js +25 -0
  27. package/dist_ts_web/elements/upl-statuspage-pagetitle.js +148 -0
  28. package/dist_ts_web/elements/upl-statuspage-statsgrid.d.ts +23 -0
  29. package/dist_ts_web/elements/upl-statuspage-statsgrid.demo.d.ts +1 -0
  30. package/dist_ts_web/elements/upl-statuspage-statsgrid.demo.js +295 -0
  31. package/dist_ts_web/elements/upl-statuspage-statsgrid.js +374 -0
  32. package/dist_ts_web/elements/upl-statuspage-statusbar.d.ts +4 -0
  33. package/dist_ts_web/elements/upl-statuspage-statusbar.demo.d.ts +1 -0
  34. package/dist_ts_web/elements/upl-statuspage-statusbar.demo.js +365 -0
  35. package/dist_ts_web/elements/upl-statuspage-statusbar.js +357 -44
  36. package/dist_ts_web/elements/upl-statuspage-statusdetails.d.ts +14 -0
  37. package/dist_ts_web/elements/upl-statuspage-statusdetails.demo.d.ts +1 -0
  38. package/dist_ts_web/elements/upl-statuspage-statusdetails.demo.js +706 -0
  39. package/dist_ts_web/elements/upl-statuspage-statusdetails.js +373 -63
  40. package/dist_ts_web/elements/upl-statuspage-statusmonth.d.ts +15 -0
  41. package/dist_ts_web/elements/upl-statuspage-statusmonth.demo.d.ts +1 -0
  42. package/dist_ts_web/elements/upl-statuspage-statusmonth.demo.js +798 -0
  43. package/dist_ts_web/elements/upl-statuspage-statusmonth.js +474 -100
  44. package/dist_ts_web/interfaces/index.d.ts +84 -0
  45. package/dist_ts_web/interfaces/index.js +4 -0
  46. package/dist_ts_web/pages/index.d.ts +4 -1
  47. package/dist_ts_web/pages/index.js +5 -2
  48. package/dist_ts_web/pages/statuspage-allgreen.d.ts +1 -0
  49. package/dist_ts_web/pages/statuspage-allgreen.js +386 -0
  50. package/dist_ts_web/pages/statuspage-demo.d.ts +1 -0
  51. package/dist_ts_web/pages/statuspage-demo.js +616 -0
  52. package/dist_ts_web/pages/statuspage-maintenance.d.ts +1 -0
  53. package/dist_ts_web/pages/statuspage-maintenance.js +544 -0
  54. package/dist_ts_web/pages/statuspage-outage.d.ts +1 -0
  55. package/dist_ts_web/pages/statuspage-outage.js +543 -0
  56. package/dist_ts_web/styles/shared.styles.d.ts +80 -0
  57. package/dist_ts_web/styles/shared.styles.js +351 -0
  58. package/dist_watch/bundle.js +54534 -26433
  59. package/dist_watch/bundle.js.map +4 -4
  60. package/dist_watch/index.html +3 -10
  61. package/npmextra.json +9 -3
  62. package/package.json +19 -19
  63. package/readme.hints.md +292 -0
  64. package/readme.md +326 -149
  65. package/readme.plan.md +261 -0
  66. package/ts_web/00_commitinfo_data.ts +1 -1
  67. package/ts_web/elements/index.ts +6 -0
  68. package/ts_web/elements/internal/uplinternal-miniheading.ts +24 -17
  69. package/ts_web/elements/upl-statuspage-assetsselector.demo.ts +607 -0
  70. package/ts_web/elements/upl-statuspage-assetsselector.ts +526 -18
  71. package/ts_web/elements/upl-statuspage-footer.demo.ts +744 -0
  72. package/ts_web/elements/upl-statuspage-footer.ts +608 -30
  73. package/ts_web/elements/upl-statuspage-header.demo.ts +241 -0
  74. package/ts_web/elements/upl-statuspage-header.ts +220 -52
  75. package/ts_web/elements/upl-statuspage-incidents.demo.ts +1216 -0
  76. package/ts_web/elements/upl-statuspage-incidents.ts +649 -26
  77. package/ts_web/elements/upl-statuspage-pagetitle.demo.ts +25 -0
  78. package/ts_web/elements/upl-statuspage-pagetitle.ts +89 -0
  79. package/ts_web/elements/upl-statuspage-statsgrid.demo.ts +315 -0
  80. package/ts_web/elements/upl-statuspage-statsgrid.ts +306 -0
  81. package/ts_web/elements/upl-statuspage-statusbar.demo.ts +393 -0
  82. package/ts_web/elements/upl-statuspage-statusbar.ts +281 -20
  83. package/ts_web/elements/upl-statuspage-statusdetails.demo.ts +754 -0
  84. package/ts_web/elements/upl-statuspage-statusdetails.ts +297 -38
  85. package/ts_web/elements/upl-statuspage-statusmonth.demo.ts +876 -0
  86. package/ts_web/elements/upl-statuspage-statusmonth.ts +397 -76
  87. package/ts_web/interfaces/index.ts +95 -0
  88. package/ts_web/pages/index.ts +4 -1
  89. package/ts_web/pages/statuspage-allgreen.ts +412 -0
  90. package/ts_web/pages/statuspage-demo.ts +653 -0
  91. package/ts_web/pages/statuspage-maintenance.ts +570 -0
  92. package/ts_web/pages/statuspage-outage.ts +568 -0
  93. package/ts_web/styles/shared.styles.ts +367 -0
  94. package/dist_ts_web/pages/page1.d.ts +0 -1
  95. package/dist_ts_web/pages/page1.js +0 -11
  96. 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
+ `;