@uptime.link/statuspage 1.0.74 → 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 (95) 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 +51691 -32432
  59. package/dist_watch/bundle.js.map +4 -4
  60. package/npmextra.json +9 -3
  61. package/package.json +19 -19
  62. package/readme.hints.md +292 -0
  63. package/readme.md +326 -149
  64. package/readme.plan.md +261 -0
  65. package/ts_web/00_commitinfo_data.ts +1 -1
  66. package/ts_web/elements/index.ts +6 -0
  67. package/ts_web/elements/internal/uplinternal-miniheading.ts +24 -17
  68. package/ts_web/elements/upl-statuspage-assetsselector.demo.ts +607 -0
  69. package/ts_web/elements/upl-statuspage-assetsselector.ts +526 -18
  70. package/ts_web/elements/upl-statuspage-footer.demo.ts +744 -0
  71. package/ts_web/elements/upl-statuspage-footer.ts +608 -30
  72. package/ts_web/elements/upl-statuspage-header.demo.ts +241 -0
  73. package/ts_web/elements/upl-statuspage-header.ts +220 -52
  74. package/ts_web/elements/upl-statuspage-incidents.demo.ts +1216 -0
  75. package/ts_web/elements/upl-statuspage-incidents.ts +649 -26
  76. package/ts_web/elements/upl-statuspage-pagetitle.demo.ts +25 -0
  77. package/ts_web/elements/upl-statuspage-pagetitle.ts +89 -0
  78. package/ts_web/elements/upl-statuspage-statsgrid.demo.ts +315 -0
  79. package/ts_web/elements/upl-statuspage-statsgrid.ts +306 -0
  80. package/ts_web/elements/upl-statuspage-statusbar.demo.ts +393 -0
  81. package/ts_web/elements/upl-statuspage-statusbar.ts +281 -20
  82. package/ts_web/elements/upl-statuspage-statusdetails.demo.ts +754 -0
  83. package/ts_web/elements/upl-statuspage-statusdetails.ts +297 -38
  84. package/ts_web/elements/upl-statuspage-statusmonth.demo.ts +876 -0
  85. package/ts_web/elements/upl-statuspage-statusmonth.ts +397 -76
  86. package/ts_web/interfaces/index.ts +95 -0
  87. package/ts_web/pages/index.ts +4 -1
  88. package/ts_web/pages/statuspage-allgreen.ts +412 -0
  89. package/ts_web/pages/statuspage-demo.ts +653 -0
  90. package/ts_web/pages/statuspage-maintenance.ts +570 -0
  91. package/ts_web/pages/statuspage-outage.ts +568 -0
  92. package/ts_web/styles/shared.styles.ts +367 -0
  93. package/dist_ts_web/pages/page1.d.ts +0 -1
  94. package/dist_ts_web/pages/page1.js +0 -11
  95. package/ts_web/pages/page1.ts +0 -11
@@ -0,0 +1,1216 @@
1
+ import { html } from '@design.estate/dees-element';
2
+ import type { IIncidentDetails, IIncidentUpdate } 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
+ .incident-stats {
54
+ display: grid;
55
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
56
+ gap: 12px;
57
+ margin-top: 12px;
58
+ }
59
+ .stat-box {
60
+ background: white;
61
+ padding: 12px;
62
+ border-radius: 4px;
63
+ text-align: center;
64
+ }
65
+ .stat-value {
66
+ font-size: 20px;
67
+ font-weight: 600;
68
+ color: #2196F3;
69
+ }
70
+ .stat-label {
71
+ font-size: 12px;
72
+ color: #666;
73
+ margin-top: 4px;
74
+ }
75
+ .incident-log {
76
+ margin-top: 12px;
77
+ padding: 12px;
78
+ background: #f9f9f9;
79
+ border-radius: 4px;
80
+ font-size: 12px;
81
+ max-height: 150px;
82
+ overflow-y: auto;
83
+ font-family: monospace;
84
+ }
85
+ </style>
86
+
87
+ <div class="demo-container">
88
+ <!-- Different Incident Types -->
89
+ <div class="demo-section">
90
+ <div class="demo-title">Different Incident Types and Severities</div>
91
+ <dees-demowrapper
92
+ .runAfterRender=${async (wrapperElement: any) => {
93
+ const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
94
+
95
+ // Generate different types of incidents
96
+ const incidentScenarios = {
97
+ critical: {
98
+ name: 'Critical Outage',
99
+ incidents: [
100
+ {
101
+ id: 'crit-001',
102
+ title: 'Complete Platform Outage',
103
+ status: 'investigating' as const,
104
+ severity: 'critical' as const,
105
+ affectedServices: ['api-gateway', 'web-server', 'database', 'cdn'],
106
+ startTime: Date.now() - 15 * 60 * 1000,
107
+ impact: 'All services are currently unavailable. We are working on immediate resolution.',
108
+ updates: [
109
+ {
110
+ id: 'upd-c1',
111
+ timestamp: Date.now() - 15 * 60 * 1000,
112
+ status: 'investigating' as const,
113
+ message: 'Multiple alerts triggered. All hands on deck.',
114
+ author: 'Incident Commander'
115
+ },
116
+ {
117
+ id: 'upd-c2',
118
+ timestamp: Date.now() - 10 * 60 * 1000,
119
+ status: 'investigating' as const,
120
+ message: 'Root cause appears to be datacenter power failure.',
121
+ author: 'Infrastructure Team'
122
+ }
123
+ ]
124
+ }
125
+ ]
126
+ },
127
+ major: {
128
+ name: 'Major Issues',
129
+ incidents: [
130
+ {
131
+ id: 'maj-001',
132
+ title: 'Database Performance Degradation',
133
+ status: 'identified' as const,
134
+ severity: 'major' as const,
135
+ affectedServices: ['database', 'api-gateway'],
136
+ startTime: Date.now() - 45 * 60 * 1000,
137
+ impact: 'Significant slowdown in database queries affecting 60% of API requests.',
138
+ updates: [
139
+ {
140
+ id: 'upd-m1',
141
+ timestamp: Date.now() - 45 * 60 * 1000,
142
+ status: 'investigating' as const,
143
+ message: 'Investigating database performance issues.',
144
+ author: 'Database Team'
145
+ },
146
+ {
147
+ id: 'upd-m2',
148
+ timestamp: Date.now() - 30 * 60 * 1000,
149
+ status: 'identified' as const,
150
+ message: 'Identified runaway queries causing resource exhaustion.',
151
+ author: 'Database Team'
152
+ }
153
+ ]
154
+ },
155
+ {
156
+ id: 'maj-002',
157
+ title: 'CDN Routing Issues',
158
+ status: 'monitoring' as const,
159
+ severity: 'major' as const,
160
+ affectedServices: ['cdn'],
161
+ startTime: Date.now() - 2 * 60 * 60 * 1000,
162
+ impact: 'Assets loading slowly for users in EU region.',
163
+ updates: [
164
+ {
165
+ id: 'upd-m3',
166
+ timestamp: Date.now() - 2 * 60 * 60 * 1000,
167
+ status: 'investigating' as const,
168
+ message: 'Reports of slow asset loading from EU users.',
169
+ author: 'CDN Team'
170
+ },
171
+ {
172
+ id: 'upd-m4',
173
+ timestamp: Date.now() - 90 * 60 * 1000,
174
+ status: 'identified' as const,
175
+ message: 'CDN provider experiencing issues with EU PoPs.',
176
+ author: 'CDN Team'
177
+ },
178
+ {
179
+ id: 'upd-m5',
180
+ timestamp: Date.now() - 30 * 60 * 1000,
181
+ status: 'monitoring' as const,
182
+ message: 'Rerouted traffic to alternate PoPs. Monitoring performance.',
183
+ author: 'CDN Team'
184
+ }
185
+ ]
186
+ }
187
+ ]
188
+ },
189
+ minor: {
190
+ name: 'Minor Incidents',
191
+ incidents: [
192
+ {
193
+ id: 'min-001',
194
+ title: 'Delayed Webhook Deliveries',
195
+ status: 'monitoring' as const,
196
+ severity: 'minor' as const,
197
+ affectedServices: ['webhook-service'],
198
+ startTime: Date.now() - 3 * 60 * 60 * 1000,
199
+ impact: 'Webhooks experiencing 2-5 minute delays.',
200
+ updates: [
201
+ {
202
+ id: 'upd-n1',
203
+ timestamp: Date.now() - 3 * 60 * 60 * 1000,
204
+ status: 'investigating' as const,
205
+ message: 'Investigating webhook delivery delays.',
206
+ author: 'API Team'
207
+ },
208
+ {
209
+ id: 'upd-n2',
210
+ timestamp: Date.now() - 2 * 60 * 60 * 1000,
211
+ status: 'monitoring' as const,
212
+ message: 'Queue processing optimized. Delays reducing.',
213
+ author: 'API Team'
214
+ }
215
+ ]
216
+ },
217
+ {
218
+ id: 'min-002',
219
+ title: 'Search Indexing Lag',
220
+ status: 'identified' as const,
221
+ severity: 'minor' as const,
222
+ affectedServices: ['search-service'],
223
+ startTime: Date.now() - 4 * 60 * 60 * 1000,
224
+ impact: 'New content taking up to 30 minutes to appear in search results.',
225
+ updates: [
226
+ {
227
+ id: 'upd-n3',
228
+ timestamp: Date.now() - 4 * 60 * 60 * 1000,
229
+ status: 'identified' as const,
230
+ message: 'Search indexing pipeline experiencing backlog.',
231
+ author: 'Search Team'
232
+ }
233
+ ]
234
+ }
235
+ ]
236
+ },
237
+ maintenance: {
238
+ name: 'Maintenance',
239
+ incidents: [
240
+ {
241
+ id: 'maint-001',
242
+ title: 'Scheduled Database Maintenance',
243
+ status: 'identified' as const,
244
+ severity: 'maintenance' as const,
245
+ affectedServices: ['database'],
246
+ startTime: Date.now() + 24 * 60 * 60 * 1000,
247
+ endTime: Date.now() + 26 * 60 * 60 * 1000,
248
+ impact: 'Database will be in read-only mode during maintenance window.',
249
+ updates: [
250
+ {
251
+ id: 'upd-mt1',
252
+ timestamp: Date.now(),
253
+ status: 'identified' as const,
254
+ message: 'Scheduled maintenance for database version upgrade.',
255
+ author: 'Database Team'
256
+ }
257
+ ]
258
+ },
259
+ {
260
+ id: 'maint-002',
261
+ title: 'Network Equipment Upgrade',
262
+ status: 'identified' as const,
263
+ severity: 'maintenance' as const,
264
+ affectedServices: ['cdn', 'api-gateway'],
265
+ startTime: Date.now() + 48 * 60 * 60 * 1000,
266
+ endTime: Date.now() + 49 * 60 * 60 * 1000,
267
+ impact: 'Brief connectivity interruptions expected during upgrade window.',
268
+ updates: [
269
+ {
270
+ id: 'upd-mt2',
271
+ timestamp: Date.now() - 2 * 60 * 60 * 1000,
272
+ status: 'identified' as const,
273
+ message: 'Core network switches scheduled for firmware upgrade.',
274
+ author: 'Network Team'
275
+ }
276
+ ]
277
+ }
278
+ ]
279
+ }
280
+ };
281
+
282
+ // Initial setup
283
+ let currentScenario = 'major';
284
+ incidents.currentIncidents = incidentScenarios[currentScenario].incidents;
285
+ incidents.pastIncidents = [];
286
+
287
+ // Create controls
288
+ const controls = document.createElement('div');
289
+ controls.className = 'demo-controls';
290
+
291
+ Object.entries(incidentScenarios).forEach(([key, scenario]) => {
292
+ const button = document.createElement('button');
293
+ button.className = 'demo-button' + (key === currentScenario ? ' active' : '');
294
+ button.textContent = scenario.name;
295
+ button.onclick = () => {
296
+ controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
297
+ button.classList.add('active');
298
+
299
+ currentScenario = key;
300
+ incidents.loading = true;
301
+ setTimeout(() => {
302
+ incidents.currentIncidents = scenario.incidents;
303
+ incidents.loading = false;
304
+ updateStats();
305
+ }, 500);
306
+ };
307
+ controls.appendChild(button);
308
+ });
309
+
310
+ wrapperElement.appendChild(controls);
311
+
312
+ // Add statistics
313
+ const statsDiv = document.createElement('div');
314
+ statsDiv.className = 'incident-stats';
315
+ wrapperElement.appendChild(statsDiv);
316
+
317
+ const updateStats = () => {
318
+ const current = incidents.currentIncidents || [];
319
+ const critical = current.filter(i => i.severity === 'critical').length;
320
+ const major = current.filter(i => i.severity === 'major').length;
321
+ const minor = current.filter(i => i.severity === 'minor').length;
322
+ const maintenance = current.filter(i => i.severity === 'maintenance').length;
323
+
324
+ statsDiv.innerHTML = `
325
+ <div class="stat-box">
326
+ <div class="stat-value">${critical}</div>
327
+ <div class="stat-label">Critical</div>
328
+ </div>
329
+ <div class="stat-box">
330
+ <div class="stat-value">${major}</div>
331
+ <div class="stat-label">Major</div>
332
+ </div>
333
+ <div class="stat-box">
334
+ <div class="stat-value">${minor}</div>
335
+ <div class="stat-label">Minor</div>
336
+ </div>
337
+ <div class="stat-box">
338
+ <div class="stat-value">${maintenance}</div>
339
+ <div class="stat-label">Maintenance</div>
340
+ </div>
341
+ `;
342
+ };
343
+
344
+ updateStats();
345
+
346
+ // Handle incident clicks
347
+ incidents.addEventListener('incidentClick', (event: CustomEvent) => {
348
+ alert(`Incident Details:\n\nTitle: ${event.detail.incident.title}\nSeverity: ${event.detail.incident.severity}\nStatus: ${event.detail.incident.status}\nImpact: ${event.detail.incident.impact}`);
349
+ });
350
+ }}
351
+ >
352
+ <upl-statuspage-incidents></upl-statuspage-incidents>
353
+ </dees-demowrapper>
354
+ </div>
355
+
356
+ <!-- Real-time Incident Progression -->
357
+ <div class="demo-section">
358
+ <div class="demo-title">Real-time Incident Progression</div>
359
+ <dees-demowrapper
360
+ .runAfterRender=${async (wrapperElement: any) => {
361
+ const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
362
+
363
+ // Create a progressing incident
364
+ const progressingIncident: IIncidentDetails = {
365
+ id: 'prog-001',
366
+ title: 'Live Incident: Payment Processing Delays',
367
+ status: 'investigating',
368
+ severity: 'major',
369
+ affectedServices: ['payment-gateway', 'api-gateway'],
370
+ startTime: Date.now(),
371
+ impact: 'Payment processing experiencing intermittent failures.',
372
+ updates: [
373
+ {
374
+ id: 'upd-p1',
375
+ timestamp: Date.now(),
376
+ status: 'investigating',
377
+ message: 'We are investigating reports of payment processing failures.',
378
+ author: 'Payment Team'
379
+ }
380
+ ]
381
+ };
382
+
383
+ incidents.currentIncidents = [progressingIncident];
384
+ incidents.pastIncidents = [];
385
+
386
+ // Progression timeline
387
+ const progressionSteps = [
388
+ {
389
+ delay: 10000,
390
+ update: {
391
+ status: 'investigating' as const,
392
+ message: 'Initial investigation shows 30% of payment attempts failing.',
393
+ author: 'Payment Team'
394
+ }
395
+ },
396
+ {
397
+ delay: 20000,
398
+ update: {
399
+ status: 'identified' as const,
400
+ message: 'Root cause identified: Third-party payment processor API rate limiting.',
401
+ author: 'Engineering Team'
402
+ },
403
+ incidentUpdate: { status: 'identified' as const }
404
+ },
405
+ {
406
+ delay: 30000,
407
+ update: {
408
+ status: 'monitoring' as const,
409
+ message: 'Implemented retry logic and request throttling. Success rate improving.',
410
+ author: 'Engineering Team'
411
+ },
412
+ incidentUpdate: { status: 'monitoring' as const }
413
+ },
414
+ {
415
+ delay: 40000,
416
+ update: {
417
+ status: 'monitoring' as const,
418
+ message: 'Payment success rate now at 95%. Continuing to monitor.',
419
+ author: 'Payment Team'
420
+ }
421
+ },
422
+ {
423
+ delay: 50000,
424
+ update: {
425
+ status: 'resolved' as const,
426
+ message: 'All systems back to normal. Payment processing fully restored.',
427
+ author: 'Payment Team'
428
+ },
429
+ incidentUpdate: {
430
+ status: 'resolved' as const,
431
+ endTime: Date.now() + 50000,
432
+ resolution: 'Implemented rate limiting and retry logic to prevent future occurrences.'
433
+ }
434
+ }
435
+ ];
436
+
437
+ // Progress through steps
438
+ progressionSteps.forEach((step, index) => {
439
+ setTimeout(() => {
440
+ const newUpdate: IIncidentUpdate = {
441
+ id: `upd-p${index + 2}`,
442
+ timestamp: Date.now(),
443
+ ...step.update
444
+ };
445
+
446
+ progressingIncident.updates.push(newUpdate);
447
+
448
+ if (step.incidentUpdate) {
449
+ Object.assign(progressingIncident, step.incidentUpdate);
450
+ }
451
+
452
+ if (progressingIncident.status === 'resolved') {
453
+ // Move to past incidents
454
+ incidents.pastIncidents = [progressingIncident, ...incidents.pastIncidents];
455
+ incidents.currentIncidents = [];
456
+ }
457
+
458
+ incidents.requestUpdate();
459
+ logUpdate(`[${step.update.status.toUpperCase()}] ${step.update.message}`);
460
+ }, step.delay);
461
+ });
462
+
463
+ // Create controls
464
+ const controls = document.createElement('div');
465
+ controls.className = 'demo-controls';
466
+ controls.innerHTML = `
467
+ <button class="demo-button" id="addUpdate">Add Custom Update</button>
468
+ <button class="demo-button" id="escalate">Escalate to Critical</button>
469
+ <button class="demo-button" id="resolve">Resolve Immediately</button>
470
+ `;
471
+ wrapperElement.appendChild(controls);
472
+
473
+ controls.querySelector('#addUpdate')?.addEventListener('click', () => {
474
+ if (progressingIncident.status !== 'resolved') {
475
+ const message = prompt('Enter update message:');
476
+ if (message) {
477
+ progressingIncident.updates.push({
478
+ id: `upd-custom-${Date.now()}`,
479
+ timestamp: Date.now(),
480
+ status: progressingIncident.status,
481
+ message,
482
+ author: 'Manual Update'
483
+ });
484
+ incidents.requestUpdate();
485
+ logUpdate(`[MANUAL] ${message}`);
486
+ }
487
+ }
488
+ });
489
+
490
+ controls.querySelector('#escalate')?.addEventListener('click', () => {
491
+ if (progressingIncident.status !== 'resolved') {
492
+ progressingIncident.severity = 'critical';
493
+ progressingIncident.updates.push({
494
+ id: `upd-esc-${Date.now()}`,
495
+ timestamp: Date.now(),
496
+ status: progressingIncident.status,
497
+ message: 'Incident escalated to CRITICAL severity.',
498
+ author: 'Incident Commander'
499
+ });
500
+ incidents.requestUpdate();
501
+ logUpdate('[ESCALATED] Incident severity raised to CRITICAL');
502
+ }
503
+ });
504
+
505
+ controls.querySelector('#resolve')?.addEventListener('click', () => {
506
+ if (progressingIncident.status !== 'resolved') {
507
+ progressingIncident.status = 'resolved';
508
+ progressingIncident.endTime = Date.now();
509
+ progressingIncident.resolution = 'Manually resolved.';
510
+ progressingIncident.updates.push({
511
+ id: `upd-res-${Date.now()}`,
512
+ timestamp: Date.now(),
513
+ status: 'resolved',
514
+ message: 'Incident manually resolved.',
515
+ author: 'Manual Resolution'
516
+ });
517
+ incidents.pastIncidents = [progressingIncident, ...incidents.pastIncidents];
518
+ incidents.currentIncidents = [];
519
+ incidents.requestUpdate();
520
+ logUpdate('[RESOLVED] Incident manually resolved');
521
+ }
522
+ });
523
+
524
+ // Handle incident subscriptions
525
+ incidents.addEventListener('incidentSubscribe', (event: CustomEvent) => {
526
+ console.log('Subscribed to incident:', event.detail);
527
+ logUpdate(`[SUBSCRIBED] User subscribed to incident "${event.detail.incidentTitle}"`);
528
+ });
529
+
530
+ incidents.addEventListener('incidentUnsubscribe', (event: CustomEvent) => {
531
+ console.log('Unsubscribed from incident:', event.detail);
532
+ logUpdate(`[UNSUBSCRIBED] User unsubscribed from incident "${event.detail.incident.title}"`);
533
+ });
534
+
535
+ // Add update log
536
+ const logDiv = document.createElement('div');
537
+ logDiv.className = 'incident-log';
538
+ logDiv.innerHTML = '<strong>Incident Timeline:</strong><br>';
539
+ wrapperElement.appendChild(logDiv);
540
+
541
+ const logUpdate = (message: string) => {
542
+ const time = new Date().toLocaleTimeString();
543
+ logDiv.innerHTML += `[${time}] ${message}<br>`;
544
+ logDiv.scrollTop = logDiv.scrollHeight;
545
+ };
546
+
547
+ logUpdate('[CREATED] New incident: Payment Processing Delays');
548
+ }}
549
+ >
550
+ <upl-statuspage-incidents></upl-statuspage-incidents>
551
+ </dees-demowrapper>
552
+ </div>
553
+
554
+ <!-- Historical Incident Patterns -->
555
+ <div class="demo-section">
556
+ <div class="demo-title">Historical Incident Patterns</div>
557
+ <dees-demowrapper
558
+ .runAfterRender=${async (wrapperElement: any) => {
559
+ const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
560
+
561
+ // Generate historical patterns
562
+ const generateHistoricalIncidents = (pattern: 'frequent' | 'seasonal' | 'improving' | 'stable'): IIncidentDetails[] => {
563
+ const incidents: IIncidentDetails[] = [];
564
+ const now = Date.now();
565
+
566
+ switch (pattern) {
567
+ case 'frequent':
568
+ // Many incidents in recent history
569
+ for (let i = 0; i < 20; i++) {
570
+ const daysAgo = Math.floor(Math.random() * 30);
571
+ const duration = Math.floor(Math.random() * 4 + 1) * 60 * 60 * 1000;
572
+ const startTime = now - daysAgo * 24 * 60 * 60 * 1000;
573
+
574
+ incidents.push({
575
+ id: `freq-${i}`,
576
+ title: `Service Disruption #${20 - i}`,
577
+ status: 'resolved',
578
+ severity: Math.random() < 0.3 ? 'major' : 'minor',
579
+ affectedServices: ['api-gateway', 'database'].slice(0, Math.floor(Math.random() * 2) + 1),
580
+ startTime,
581
+ endTime: startTime + duration,
582
+ impact: 'Service degradation affecting subset of users.',
583
+ rootCause: 'Various infrastructure issues.',
584
+ resolution: 'Applied temporary fixes.',
585
+ updates: [
586
+ {
587
+ id: `upd-${i}-1`,
588
+ timestamp: startTime,
589
+ status: 'investigating',
590
+ message: 'Investigating service issues.',
591
+ author: 'Ops Team'
592
+ },
593
+ {
594
+ id: `upd-${i}-2`,
595
+ timestamp: startTime + duration,
596
+ status: 'resolved',
597
+ message: 'Issue resolved.',
598
+ author: 'Ops Team'
599
+ }
600
+ ]
601
+ });
602
+ }
603
+ break;
604
+
605
+ case 'seasonal':
606
+ // Incidents clustered around specific times
607
+ const peakDays = [7, 14, 21, 28]; // Weekly pattern
608
+ peakDays.forEach((day, index) => {
609
+ for (let j = 0; j < 3; j++) {
610
+ const startTime = now - day * 24 * 60 * 60 * 1000 + (j - 1) * 60 * 60 * 1000;
611
+ incidents.push({
612
+ id: `seas-${index}-${j}`,
613
+ title: `Peak Traffic Overload`,
614
+ status: 'resolved',
615
+ severity: 'minor',
616
+ affectedServices: ['api-gateway'],
617
+ startTime,
618
+ endTime: startTime + 30 * 60 * 1000,
619
+ impact: 'Slow response times during peak hours.',
620
+ rootCause: 'Insufficient capacity for peak traffic.',
621
+ resolution: 'Auto-scaling adjusted.',
622
+ updates: [
623
+ {
624
+ id: `upd-s-${index}-${j}`,
625
+ timestamp: startTime,
626
+ status: 'resolved',
627
+ message: 'Peak traffic handled by auto-scaling.',
628
+ author: 'Auto-remediation'
629
+ }
630
+ ]
631
+ });
632
+ }
633
+ });
634
+ break;
635
+
636
+ case 'improving':
637
+ // Fewer incidents over time
638
+ for (let i = 0; i < 15; i++) {
639
+ const daysAgo = 30 - i * 2;
640
+ if (Math.random() < (15 - i) / 15) {
641
+ const startTime = now - daysAgo * 24 * 60 * 60 * 1000;
642
+ incidents.push({
643
+ id: `imp-${i}`,
644
+ title: `System Issue #${i + 1}`,
645
+ status: 'resolved',
646
+ severity: i < 5 ? 'major' : 'minor',
647
+ affectedServices: ['database'],
648
+ startTime,
649
+ endTime: startTime + 2 * 60 * 60 * 1000,
650
+ impact: 'Database performance issues.',
651
+ rootCause: 'Legacy infrastructure limitations.',
652
+ resolution: 'Infrastructure improvements implemented.',
653
+ updates: [
654
+ {
655
+ id: `upd-imp-${i}`,
656
+ timestamp: startTime,
657
+ status: 'resolved',
658
+ message: 'Resolved with infrastructure improvements.',
659
+ author: 'Infrastructure Team'
660
+ }
661
+ ]
662
+ });
663
+ }
664
+ }
665
+ break;
666
+
667
+ case 'stable':
668
+ // Very few incidents, all minor
669
+ for (let i = 0; i < 3; i++) {
670
+ const daysAgo = Math.floor(Math.random() * 30);
671
+ const startTime = now - daysAgo * 24 * 60 * 60 * 1000;
672
+ incidents.push({
673
+ id: `stab-${i}`,
674
+ title: ['Routine Maintenance', 'Minor Configuration Update', 'Planned Restart'][i],
675
+ status: 'resolved',
676
+ severity: 'maintenance',
677
+ affectedServices: ['web-server'],
678
+ startTime,
679
+ endTime: startTime + 15 * 60 * 1000,
680
+ impact: 'No user impact.',
681
+ resolution: 'Completed as planned.',
682
+ updates: [
683
+ {
684
+ id: `upd-stab-${i}`,
685
+ timestamp: startTime,
686
+ status: 'resolved',
687
+ message: 'Maintenance completed successfully.',
688
+ author: 'Maintenance Team'
689
+ }
690
+ ]
691
+ });
692
+ }
693
+ break;
694
+ }
695
+
696
+ return incidents.sort((a, b) => b.startTime - a.startTime);
697
+ };
698
+
699
+ // Initial setup
700
+ let currentPattern: 'frequent' | 'seasonal' | 'improving' | 'stable' = 'frequent';
701
+ incidents.currentIncidents = [];
702
+ incidents.pastIncidents = generateHistoricalIncidents(currentPattern);
703
+
704
+ // Create pattern controls
705
+ const controls = document.createElement('div');
706
+ controls.className = 'demo-controls';
707
+
708
+ const patterns = [
709
+ { key: 'frequent', label: 'Frequent Incidents' },
710
+ { key: 'seasonal', label: 'Seasonal Pattern' },
711
+ { key: 'improving', label: 'Improving Trend' },
712
+ { key: 'stable', label: 'Stable System' }
713
+ ];
714
+
715
+ patterns.forEach((pattern) => {
716
+ const button = document.createElement('button');
717
+ button.className = 'demo-button' + (pattern.key === currentPattern ? ' active' : '');
718
+ button.textContent = pattern.label;
719
+ button.onclick = () => {
720
+ controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
721
+ button.classList.add('active');
722
+
723
+ currentPattern = pattern.key as any;
724
+ incidents.loading = true;
725
+ setTimeout(() => {
726
+ incidents.pastIncidents = generateHistoricalIncidents(currentPattern as any);
727
+ incidents.loading = false;
728
+ }, 500);
729
+ };
730
+ controls.appendChild(button);
731
+ });
732
+
733
+ wrapperElement.appendChild(controls);
734
+
735
+ // Add info about the pattern
736
+ const info = document.createElement('div');
737
+ info.className = 'demo-info';
738
+ info.innerHTML = `
739
+ <strong>Pattern Description:</strong><br>
740
+ <strong>Frequent:</strong> Many incidents indicating stability issues<br>
741
+ <strong>Seasonal:</strong> Incidents following a predictable pattern<br>
742
+ <strong>Improving:</strong> Decreasing incident frequency over time<br>
743
+ <strong>Stable:</strong> Minimal incidents, mostly maintenance
744
+ `;
745
+ wrapperElement.appendChild(info);
746
+ }}
747
+ >
748
+ <upl-statuspage-incidents></upl-statuspage-incidents>
749
+ </dees-demowrapper>
750
+ </div>
751
+
752
+ <!-- Edge Cases -->
753
+ <div class="demo-section">
754
+ <div class="demo-title">Edge Cases and Special Scenarios</div>
755
+ <dees-demowrapper
756
+ .runAfterRender=${async (wrapperElement: any) => {
757
+ const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
758
+
759
+ const edgeCases = {
760
+ none: {
761
+ name: 'No Incidents',
762
+ current: [],
763
+ past: []
764
+ },
765
+ manyActive: {
766
+ name: 'Many Active',
767
+ current: Array.from({ length: 8 }, (_, i) => ({
768
+ id: `many-${i}`,
769
+ title: `Concurrent Issue #${i + 1}`,
770
+ status: ['investigating', 'identified', 'monitoring'][i % 3] as any,
771
+ severity: ['critical', 'major', 'minor'][i % 3] as any,
772
+ affectedServices: [`service-${i}`],
773
+ startTime: Date.now() - (i + 1) * 30 * 60 * 1000,
774
+ impact: `Impact on service ${i + 1}`,
775
+ updates: [{
776
+ id: `upd-many-${i}`,
777
+ timestamp: Date.now() - (i + 1) * 30 * 60 * 1000,
778
+ status: 'investigating' as const,
779
+ message: 'Investigating issue.',
780
+ author: 'Team'
781
+ }]
782
+ })),
783
+ past: []
784
+ },
785
+ longRunning: {
786
+ name: 'Long Running',
787
+ current: [{
788
+ id: 'long-001',
789
+ title: 'Persistent Infrastructure Issue',
790
+ status: 'monitoring' as const,
791
+ severity: 'major' as const,
792
+ affectedServices: ['database', 'api-gateway'],
793
+ startTime: Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days ago
794
+ impact: 'Intermittent issues affecting 5-10% of requests.',
795
+ updates: Array.from({ length: 50 }, (_, i) => ({
796
+ id: `upd-long-${i}`,
797
+ timestamp: Date.now() - 7 * 24 * 60 * 60 * 1000 + i * 3 * 60 * 60 * 1000,
798
+ status: 'monitoring' as const,
799
+ message: `Update #${i + 1}: Continuing to monitor. Partial mitigation in place.`,
800
+ author: 'Ops Team'
801
+ }))
802
+ }],
803
+ past: []
804
+ },
805
+ complexUpdates: {
806
+ name: 'Complex Updates',
807
+ current: [{
808
+ id: 'complex-001',
809
+ title: 'Multi-phase Incident Resolution',
810
+ status: 'monitoring' as const,
811
+ severity: 'critical' as const,
812
+ affectedServices: ['api-gateway', 'database', 'cdn', 'email-service'],
813
+ startTime: Date.now() - 4 * 60 * 60 * 1000,
814
+ impact: 'Multiple services experiencing cascading failures.',
815
+ updates: [
816
+ {
817
+ id: 'upd-c1',
818
+ timestamp: Date.now() - 4 * 60 * 60 * 1000,
819
+ status: 'investigating' as const,
820
+ message: '🚨 CRITICAL: Multiple service alerts triggered simultaneously.',
821
+ author: 'Monitoring System'
822
+ },
823
+ {
824
+ id: 'upd-c2',
825
+ timestamp: Date.now() - 3.5 * 60 * 60 * 1000,
826
+ status: 'investigating' as const,
827
+ message: 'All hands on deck. Incident commander taking charge.\n\n- API Gateway: DOWN\n- Database: DEGRADED\n- CDN: PARTIAL OUTAGE\n- Email: DELAYED',
828
+ author: 'Incident Commander'
829
+ },
830
+ {
831
+ id: 'upd-c3',
832
+ timestamp: Date.now() - 3 * 60 * 60 * 1000,
833
+ status: 'identified' as const,
834
+ message: 'Root cause identified: Cascading failure triggered by database connection pool exhaustion.\n\nAction items:\n1. Restart connection pools\n2. Implement circuit breakers\n3. Scale up database replicas',
835
+ author: 'Engineering Lead'
836
+ },
837
+ {
838
+ id: 'upd-c4',
839
+ timestamp: Date.now() - 2 * 60 * 60 * 1000,
840
+ status: 'monitoring' as const,
841
+ message: 'Partial recovery achieved:\n✅ API Gateway: RESTORED\n⚠️ Database: RECOVERING\n✅ CDN: OPERATIONAL\n⚠️ Email: CATCHING UP\n\nContinuing remediation efforts.',
842
+ author: 'Ops Team'
843
+ }
844
+ ]
845
+ }],
846
+ past: []
847
+ }
848
+ };
849
+
850
+ // Initial setup
851
+ let currentCase = 'none';
852
+ incidents.currentIncidents = edgeCases[currentCase].current;
853
+ incidents.pastIncidents = edgeCases[currentCase].past;
854
+
855
+ // Create controls
856
+ const controls = document.createElement('div');
857
+ controls.className = 'demo-controls';
858
+
859
+ Object.entries(edgeCases).forEach(([key, scenario]) => {
860
+ const button = document.createElement('button');
861
+ button.className = 'demo-button' + (key === currentCase ? ' active' : '');
862
+ button.textContent = scenario.name;
863
+ button.onclick = () => {
864
+ controls.querySelectorAll('.demo-button').forEach(btn => btn.classList.remove('active'));
865
+ button.classList.add('active');
866
+
867
+ currentCase = key;
868
+ incidents.loading = true;
869
+ setTimeout(() => {
870
+ incidents.currentIncidents = scenario.current;
871
+ incidents.pastIncidents = scenario.past;
872
+ incidents.loading = false;
873
+ }, 300);
874
+ };
875
+ controls.appendChild(button);
876
+ });
877
+
878
+ wrapperElement.appendChild(controls);
879
+ }}
880
+ >
881
+ <upl-statuspage-incidents></upl-statuspage-incidents>
882
+ </dees-demowrapper>
883
+ </div>
884
+
885
+ <!-- Loading and Filtering -->
886
+ <div class="demo-section">
887
+ <div class="demo-title">Loading States and Filtering</div>
888
+ <dees-demowrapper
889
+ .runAfterRender=${async (wrapperElement: any) => {
890
+ const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
891
+
892
+ // Start with loading
893
+ incidents.loading = true;
894
+
895
+ // Generate mixed incidents for filtering
896
+ const allIncidents: IIncidentDetails[] = [
897
+ {
898
+ id: 'filter-1',
899
+ title: 'Critical Database Failure',
900
+ status: 'resolved',
901
+ severity: 'critical',
902
+ affectedServices: ['database'],
903
+ startTime: Date.now() - 2 * 24 * 60 * 60 * 1000,
904
+ endTime: Date.now() - 2 * 24 * 60 * 60 * 1000 + 4 * 60 * 60 * 1000,
905
+ impact: 'Complete database unavailability.',
906
+ resolution: 'Failed over to secondary cluster.',
907
+ updates: [{
908
+ id: 'u1',
909
+ timestamp: Date.now() - 2 * 24 * 60 * 60 * 1000,
910
+ status: 'resolved',
911
+ message: 'Database restored.',
912
+ author: 'DBA Team'
913
+ }]
914
+ },
915
+ {
916
+ id: 'filter-2',
917
+ title: 'API Rate Limiting Issues',
918
+ status: 'resolved',
919
+ severity: 'major',
920
+ affectedServices: ['api-gateway'],
921
+ startTime: Date.now() - 5 * 24 * 60 * 60 * 1000,
922
+ endTime: Date.now() - 5 * 24 * 60 * 60 * 1000 + 2 * 60 * 60 * 1000,
923
+ impact: 'API requests throttled for some users.',
924
+ resolution: 'Rate limits adjusted.',
925
+ updates: [{
926
+ id: 'u2',
927
+ timestamp: Date.now() - 5 * 24 * 60 * 60 * 1000,
928
+ status: 'resolved',
929
+ message: 'Rate limiting resolved.',
930
+ author: 'API Team'
931
+ }]
932
+ },
933
+ {
934
+ id: 'filter-3',
935
+ title: 'Email Delays',
936
+ status: 'resolved',
937
+ severity: 'minor',
938
+ affectedServices: ['email-service'],
939
+ startTime: Date.now() - 10 * 24 * 60 * 60 * 1000,
940
+ endTime: Date.now() - 10 * 24 * 60 * 60 * 1000 + 30 * 60 * 1000,
941
+ impact: 'Emails delayed by 5-10 minutes.',
942
+ resolution: 'Queue cleared.',
943
+ updates: [{
944
+ id: 'u3',
945
+ timestamp: Date.now() - 10 * 24 * 60 * 60 * 1000,
946
+ status: 'resolved',
947
+ message: 'Email queue cleared.',
948
+ author: 'Email Team'
949
+ }]
950
+ },
951
+ {
952
+ id: 'filter-4',
953
+ title: 'Scheduled Maintenance',
954
+ status: 'resolved',
955
+ severity: 'maintenance',
956
+ affectedServices: ['web-server'],
957
+ startTime: Date.now() - 15 * 24 * 60 * 60 * 1000,
958
+ endTime: Date.now() - 15 * 24 * 60 * 60 * 1000 + 60 * 60 * 1000,
959
+ impact: 'Brief downtime during upgrade.',
960
+ resolution: 'Upgrade completed.',
961
+ updates: [{
962
+ id: 'u4',
963
+ timestamp: Date.now() - 15 * 24 * 60 * 60 * 1000,
964
+ status: 'resolved',
965
+ message: 'Maintenance completed.',
966
+ author: 'Ops Team'
967
+ }]
968
+ }
969
+ ];
970
+
971
+ const controls = document.createElement('div');
972
+ controls.className = 'demo-controls';
973
+ controls.innerHTML = `
974
+ <button class="demo-button" id="toggleLoading">Toggle Loading</button>
975
+ <button class="demo-button" id="loadSuccess">Load Successfully</button>
976
+ <button class="demo-button" id="loadError">Simulate Error</button>
977
+ <button class="demo-button" id="filterCritical">Show Critical Only</button>
978
+ <button class="demo-button" id="showAll">Show All</button>
979
+ `;
980
+ wrapperElement.appendChild(controls);
981
+
982
+ controls.querySelector('#toggleLoading')?.addEventListener('click', () => {
983
+ incidents.loading = !incidents.loading;
984
+ });
985
+
986
+ controls.querySelector('#loadSuccess')?.addEventListener('click', () => {
987
+ incidents.loading = true;
988
+ setTimeout(() => {
989
+ incidents.currentIncidents = [
990
+ {
991
+ id: 'current-1',
992
+ title: 'Ongoing Investigation',
993
+ status: 'investigating',
994
+ severity: 'minor',
995
+ affectedServices: ['api-gateway'],
996
+ startTime: Date.now() - 30 * 60 * 1000,
997
+ impact: 'Elevated error rates on API endpoints.',
998
+ updates: [{
999
+ id: 'u-current',
1000
+ timestamp: Date.now() - 30 * 60 * 1000,
1001
+ status: 'investigating',
1002
+ message: 'Investigating elevated error rates.',
1003
+ author: 'API Team'
1004
+ }]
1005
+ }
1006
+ ];
1007
+ incidents.pastIncidents = allIncidents;
1008
+ incidents.loading = false;
1009
+ }, 1000);
1010
+ });
1011
+
1012
+ controls.querySelector('#loadError')?.addEventListener('click', () => {
1013
+ incidents.loading = true;
1014
+ setTimeout(() => {
1015
+ incidents.loading = false;
1016
+ incidents.currentIncidents = [];
1017
+ incidents.pastIncidents = [];
1018
+ incidents.errorMessage = 'Failed to load incident data';
1019
+ }, 1500);
1020
+ });
1021
+
1022
+ controls.querySelector('#filterCritical')?.addEventListener('click', () => {
1023
+ incidents.pastIncidents = allIncidents.filter(i => i.severity === 'critical');
1024
+ });
1025
+
1026
+ controls.querySelector('#showAll')?.addEventListener('click', () => {
1027
+ incidents.pastIncidents = allIncidents;
1028
+ });
1029
+
1030
+ // Initial load after delay
1031
+ setTimeout(() => {
1032
+ incidents.currentIncidents = [];
1033
+ incidents.pastIncidents = allIncidents;
1034
+ incidents.loading = false;
1035
+ }, 2000);
1036
+ }}
1037
+ >
1038
+ <upl-statuspage-incidents></upl-statuspage-incidents>
1039
+ </dees-demowrapper>
1040
+ </div>
1041
+
1042
+ <!-- Incident Subscription Demo -->
1043
+ <div class="demo-section">
1044
+ <div class="demo-title">Incident Subscription Management</div>
1045
+ <dees-demowrapper
1046
+ .runAfterRender=${async (wrapperElement: any) => {
1047
+ const incidents = wrapperElement.querySelector('upl-statuspage-incidents') as any;
1048
+
1049
+ // Sample incidents with subscription
1050
+ const currentIncidents: IIncidentDetails[] = [
1051
+ {
1052
+ id: 'sub-001',
1053
+ title: 'Payment Gateway Intermittent Failures',
1054
+ status: 'monitoring',
1055
+ severity: 'major',
1056
+ affectedServices: ['payment-gateway', 'checkout'],
1057
+ startTime: Date.now() - 2 * 60 * 60 * 1000,
1058
+ impact: 'Some customers may experience payment failures during checkout',
1059
+ updates: [
1060
+ {
1061
+ id: 'sub-u1',
1062
+ timestamp: Date.now() - 2 * 60 * 60 * 1000,
1063
+ status: 'investigating',
1064
+ message: 'We are investigating reports of payment failures',
1065
+ author: 'Payment Team'
1066
+ },
1067
+ {
1068
+ id: 'sub-u2',
1069
+ timestamp: Date.now() - 1 * 60 * 60 * 1000,
1070
+ status: 'identified',
1071
+ message: 'Issue identified with payment processor API rate limits',
1072
+ author: 'Payment Team'
1073
+ },
1074
+ {
1075
+ id: 'sub-u3',
1076
+ timestamp: Date.now() - 30 * 60 * 1000,
1077
+ status: 'monitoring',
1078
+ message: 'Temporary fix applied, monitoring for stability',
1079
+ author: 'Payment Team'
1080
+ }
1081
+ ]
1082
+ },
1083
+ {
1084
+ id: 'sub-002',
1085
+ title: 'Email Delivery Delays',
1086
+ status: 'identified',
1087
+ severity: 'minor',
1088
+ affectedServices: ['email-service'],
1089
+ startTime: Date.now() - 45 * 60 * 1000,
1090
+ impact: 'Transactional emails may be delayed by 5-10 minutes',
1091
+ updates: [
1092
+ {
1093
+ id: 'sub-u4',
1094
+ timestamp: Date.now() - 45 * 60 * 1000,
1095
+ status: 'investigating',
1096
+ message: 'Investigating email queue backlog',
1097
+ author: 'Infrastructure Team'
1098
+ },
1099
+ {
1100
+ id: 'sub-u5',
1101
+ timestamp: Date.now() - 20 * 60 * 1000,
1102
+ status: 'identified',
1103
+ message: 'High volume causing queue delays, scaling up workers',
1104
+ author: 'Infrastructure Team'
1105
+ }
1106
+ ]
1107
+ }
1108
+ ];
1109
+
1110
+ incidents.currentIncidents = currentIncidents;
1111
+ incidents.pastIncidents = [];
1112
+
1113
+ // Pre-subscribe to first incident
1114
+ incidents.subscribedIncidentIds = ['sub-001'];
1115
+
1116
+ // Create subscription status display
1117
+ const statusDiv = document.createElement('div');
1118
+ statusDiv.className = 'subscription-status';
1119
+ statusDiv.style.cssText = `
1120
+ margin-top: 16px;
1121
+ padding: 16px;
1122
+ background: #f0f9ff;
1123
+ border: 1px solid #bae6fd;
1124
+ border-radius: 6px;
1125
+ font-size: 14px;
1126
+ `;
1127
+
1128
+ const updateStatus = () => {
1129
+ const subscribed = Array.from(incidents.subscribedIncidents || []);
1130
+ statusDiv.innerHTML = `
1131
+ <strong>Subscription Status:</strong><br>
1132
+ ${subscribed.length === 0 ?
1133
+ 'Not subscribed to any incidents' :
1134
+ `Subscribed to ${subscribed.length} incident(s):<br>` +
1135
+ subscribed.map(id => {
1136
+ const incident = currentIncidents.find(i => i.id === id);
1137
+ return incident ? `• ${incident.title}` : '';
1138
+ }).filter(Boolean).join('<br>')}
1139
+ `;
1140
+ };
1141
+
1142
+ updateStatus();
1143
+ wrapperElement.appendChild(statusDiv);
1144
+
1145
+ // Handle subscription events
1146
+ incidents.addEventListener('incidentSubscribe', (event: CustomEvent) => {
1147
+ console.log('Subscribed:', event.detail);
1148
+ updateStatus();
1149
+
1150
+ // Show notification
1151
+ const notification = document.createElement('div');
1152
+ notification.style.cssText = `
1153
+ position: fixed;
1154
+ top: 20px;
1155
+ right: 20px;
1156
+ padding: 16px;
1157
+ background: #10b981;
1158
+ color: white;
1159
+ border-radius: 6px;
1160
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
1161
+ z-index: 1000;
1162
+ animation: slideIn 0.3s ease;
1163
+ `;
1164
+ notification.textContent = `✓ Subscribed to: ${event.detail.incidentTitle}`;
1165
+ document.body.appendChild(notification);
1166
+ setTimeout(() => notification.remove(), 3000);
1167
+ });
1168
+
1169
+ incidents.addEventListener('incidentUnsubscribe', (event: CustomEvent) => {
1170
+ console.log('Unsubscribed:', event.detail);
1171
+ updateStatus();
1172
+
1173
+ // Show notification
1174
+ const notification = document.createElement('div');
1175
+ notification.style.cssText = `
1176
+ position: fixed;
1177
+ top: 20px;
1178
+ right: 20px;
1179
+ padding: 16px;
1180
+ background: #6b7280;
1181
+ color: white;
1182
+ border-radius: 6px;
1183
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
1184
+ z-index: 1000;
1185
+ animation: slideIn 0.3s ease;
1186
+ `;
1187
+ notification.textContent = `Unsubscribed from: ${event.detail.incident.title}`;
1188
+ document.body.appendChild(notification);
1189
+ setTimeout(() => notification.remove(), 3000);
1190
+ });
1191
+
1192
+ // Add info text
1193
+ const infoDiv = document.createElement('div');
1194
+ infoDiv.style.cssText = `
1195
+ margin-top: 12px;
1196
+ padding: 12px;
1197
+ background: #f3f4f6;
1198
+ border-radius: 4px;
1199
+ font-size: 13px;
1200
+ color: #6b7280;
1201
+ `;
1202
+ infoDiv.innerHTML = `
1203
+ <strong>How it works:</strong><br>
1204
+ • Click on an incident to expand it<br>
1205
+ • Click "Subscribe to updates" to get notifications<br>
1206
+ • Subscribed incidents show a checkmark<br>
1207
+ • Click again to unsubscribe
1208
+ `;
1209
+ wrapperElement.appendChild(infoDiv);
1210
+ }}
1211
+ >
1212
+ <upl-statuspage-incidents></upl-statuspage-incidents>
1213
+ </dees-demowrapper>
1214
+ </div>
1215
+ </div>
1216
+ `;