@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
@@ -7,7 +7,12 @@ import {
7
7
  type TemplateResult,
8
8
  css,
9
9
  cssManager,
10
+ unsafeCSS,
10
11
  } from '@design.estate/dees-element';
12
+ import type { IIncidentDetails } from '../interfaces/index.js';
13
+ import * as sharedStyles from '../styles/shared.styles.js';
14
+ import './internal/uplinternal-miniheading.js';
15
+ import { demoFunc } from './upl-statuspage-incidents.demo.js';
11
16
 
12
17
  declare global {
13
18
  interface HTMLElementTagNameMap {
@@ -18,73 +23,691 @@ declare global {
18
23
  @customElement('upl-statuspage-incidents')
19
24
  export class UplStatuspageIncidents extends DeesElement {
20
25
  // STATIC
21
- public static demo = () => html` <upl-statuspage-incidents></upl-statuspage-incidents> `;
26
+ public static demo = demoFunc;
22
27
 
23
28
  // INSTANCE
24
29
  @property({
25
30
  type: Array,
26
31
  })
27
- public currentIncidences: plugins.uplInterfaces.data.IIncident[] = [];
32
+ accessor currentIncidents: IIncidentDetails[] = [];
28
33
 
29
34
  @property({
30
35
  type: Array,
31
36
  })
32
- public pastIncidences: plugins.uplInterfaces.data.IIncident[] = [];
37
+ accessor pastIncidents: IIncidentDetails[] = [];
33
38
 
34
39
  @property({
35
40
  type: Boolean,
36
41
  })
37
- public whitelabel = false;
42
+ accessor whitelabel = false;
43
+
44
+ @property({
45
+ type: Boolean,
46
+ })
47
+ accessor loading = false;
48
+
49
+ @property({
50
+ type: Number,
51
+ })
52
+ accessor daysToShow = 90;
53
+
54
+ @property({
55
+ type: Array,
56
+ })
57
+ accessor subscribedIncidentIds: string[] = [];
58
+
59
+ @property({
60
+ type: Object,
61
+ state: true,
62
+ })
63
+ private accessor expandedIncidents: Set<string> = new Set();
64
+
65
+ @property({
66
+ type: Object,
67
+ state: true,
68
+ })
69
+ private accessor subscribedIncidents: Set<string> = new Set();
38
70
 
39
71
  constructor() {
40
72
  super();
41
73
  }
42
74
 
75
+ async connectedCallback() {
76
+ await super.connectedCallback();
77
+ // Initialize subscribed incidents from the property
78
+ if (this.subscribedIncidentIds.length > 0) {
79
+ this.subscribedIncidents = new Set(this.subscribedIncidentIds);
80
+ }
81
+ }
82
+
83
+ updated(changedProperties: Map<string, any>) {
84
+ super.updated(changedProperties);
85
+ if (changedProperties.has('subscribedIncidentIds')) {
86
+ this.subscribedIncidents = new Set(this.subscribedIncidentIds);
87
+ }
88
+ }
89
+
43
90
  public static styles = [
44
91
  plugins.domtools.elementBasic.staticStyles,
92
+ sharedStyles.commonStyles,
45
93
  css`
46
94
  :host {
47
95
  display: block;
48
- background: ${cssManager.bdTheme('#eeeeeb', '#222222')};
49
- font-family: Inter;
50
- color: ${cssManager.bdTheme('#333333', '#ffffff')};
96
+ background: transparent;
97
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
98
+ color: ${sharedStyles.colors.text.primary};
51
99
  }
52
100
 
53
- .mainbox {
54
- max-width: 900px;
55
- margin: auto;
101
+ .container {
102
+ max-width: 1200px;
103
+ margin: 0 auto;
104
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
56
105
  }
57
106
 
58
107
  .noIncidentBox {
59
- background: ${cssManager.bdTheme('#ffffff', '#333333')};;
60
- padding: 10px;
61
- margin-bottom: 15px;
62
- border-radius: 3px;
108
+ background: ${sharedStyles.colors.background.card};
109
+ padding: ${unsafeCSS(sharedStyles.spacing.xl)};
110
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
111
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
112
+ border: 1px solid ${sharedStyles.colors.border.default};
113
+ text-align: center;
114
+ color: ${sharedStyles.colors.text.secondary};
115
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
116
+ }
117
+
118
+ .incident-card {
119
+ background: ${sharedStyles.colors.background.card};
120
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
121
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
122
+ overflow: hidden;
123
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
124
+ border: 1px solid ${sharedStyles.colors.border.default};
125
+ transition: all 0.2s ease;
126
+ }
127
+
128
+ .incident-card.expanded {
129
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.md)};
130
+ }
131
+
132
+ .incident-header {
133
+ padding: ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
134
+ border-left: 4px solid;
135
+ display: flex;
136
+ align-items: start;
137
+ justify-content: space-between;
138
+ gap: ${unsafeCSS(sharedStyles.spacing.md)};
139
+ cursor: pointer;
140
+ transition: background-color 0.2s ease;
141
+ }
142
+
143
+ .incident-header:hover {
144
+ background: ${cssManager.bdTheme('rgba(0, 0, 0, 0.02)', 'rgba(255, 255, 255, 0.02)')};
145
+ }
146
+
147
+ .incident-header.critical {
148
+ border-left-color: ${sharedStyles.colors.status.major};
149
+ }
150
+
151
+ .incident-header.major {
152
+ border-left-color: ${sharedStyles.colors.status.partial};
153
+ }
154
+
155
+ .incident-header.minor {
156
+ border-left-color: ${sharedStyles.colors.status.degraded};
157
+ }
158
+
159
+ .incident-header.maintenance {
160
+ border-left-color: ${sharedStyles.colors.status.maintenance};
161
+ }
162
+
163
+ .incident-title {
164
+ font-size: 18px;
165
+ font-weight: 600;
166
+ margin: 0;
167
+ line-height: 1.3;
168
+ }
169
+
170
+ .incident-meta {
171
+ display: flex;
172
+ gap: ${unsafeCSS(sharedStyles.spacing.lg)};
173
+ margin-top: ${unsafeCSS(sharedStyles.spacing.sm)};
174
+ font-size: 13px;
175
+ color: ${sharedStyles.colors.text.secondary};
176
+ flex-wrap: wrap;
177
+ }
178
+
179
+ .incident-status {
180
+ display: inline-flex;
181
+ align-items: center;
182
+ gap: ${unsafeCSS(sharedStyles.spacing.xs)};
183
+ padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
184
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
185
+ font-size: 12px;
186
+ font-weight: 600;
187
+ text-transform: uppercase;
188
+ letter-spacing: 0.02em;
189
+ flex-shrink: 0;
190
+ }
191
+
192
+ .incident-status.investigating {
193
+ background: ${cssManager.bdTheme('#fef3c7', '#78350f')};
194
+ color: ${cssManager.bdTheme('#92400e', '#fbbf24')};
195
+ }
196
+
197
+ .incident-status.identified {
198
+ background: ${cssManager.bdTheme('#e9d5ff', '#581c87')};
199
+ color: ${cssManager.bdTheme('#6b21a8', '#d8b4fe')};
200
+ }
201
+
202
+ .incident-status.monitoring {
203
+ background: ${cssManager.bdTheme('#dbeafe', '#1e3a8a')};
204
+ color: ${cssManager.bdTheme('#1e40af', '#93c5fd')};
205
+ }
206
+
207
+ .incident-status.resolved {
208
+ background: ${cssManager.bdTheme('#d1fae5', '#064e3b')};
209
+ color: ${cssManager.bdTheme('#047857', '#6ee7b7')};
210
+ }
211
+
212
+ .incident-status.postmortem {
213
+ background: ${cssManager.bdTheme('#e5e7eb', '#374151')};
214
+ color: ${cssManager.bdTheme('#4b5563', '#d1d5db')};
215
+ }
216
+
217
+ .incident-body {
218
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
219
+ }
220
+
221
+ .incident-impact {
222
+ margin: ${unsafeCSS(sharedStyles.spacing.md)} 0;
223
+ padding: ${unsafeCSS(sharedStyles.spacing.md)};
224
+ background: ${sharedStyles.colors.background.secondary};
225
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
226
+ font-size: 14px;
227
+ line-height: 1.6;
228
+ }
229
+
230
+ .affected-services {
231
+ margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
232
+ }
233
+
234
+ .affected-services-title {
235
+ font-size: 13px;
236
+ font-weight: 600;
237
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
238
+ color: ${sharedStyles.colors.text.primary};
239
+ }
240
+
241
+ .service-tag {
242
+ display: inline-block;
243
+ padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
244
+ margin: 2px;
245
+ background: ${sharedStyles.colors.background.muted};
246
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.sm)};
247
+ font-size: 12px;
248
+ color: ${sharedStyles.colors.text.secondary};
249
+ }
250
+
251
+ .incident-updates {
252
+ margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
253
+ border-top: 1px solid ${sharedStyles.colors.border.default};
254
+ padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
255
+ }
256
+
257
+ .update-item {
258
+ position: relative;
259
+ padding-left: ${unsafeCSS(sharedStyles.spacing.lg)};
260
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
261
+ }
262
+
263
+ .update-item::before {
264
+ content: '';
265
+ position: absolute;
266
+ left: 0;
267
+ top: 6px;
268
+ width: 8px;
269
+ height: 8px;
270
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
271
+ background: ${sharedStyles.colors.border.muted};
272
+ }
273
+
274
+ .update-time {
275
+ font-size: 12px;
276
+ color: ${sharedStyles.colors.text.secondary};
277
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.xs)};
278
+ font-family: ${unsafeCSS(sharedStyles.fonts.mono)};
279
+ }
280
+
281
+ .update-message {
282
+ font-size: 14px;
283
+ line-height: 1.6;
284
+ color: ${sharedStyles.colors.text.primary};
285
+ }
286
+
287
+ .update-author {
288
+ font-size: 12px;
289
+ color: ${sharedStyles.colors.text.secondary};
290
+ margin-top: ${unsafeCSS(sharedStyles.spacing.xs)};
291
+ font-style: italic;
292
+ }
293
+
294
+ .loading-skeleton {
295
+ height: 140px;
296
+ background: ${cssManager.bdTheme(
297
+ 'linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%)',
298
+ 'linear-gradient(90deg, #1f1f1f 25%, #262626 50%, #1f1f1f 75%)'
299
+ )};
300
+ background-size: 200% 100%;
301
+ animation: loading 1.5s infinite;
302
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.md)};
303
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
304
+ }
305
+
306
+ @keyframes loading {
307
+ 0% { background-position: 200% 0; }
308
+ 100% { background-position: -200% 0; }
309
+ }
310
+
311
+ .show-more {
312
+ text-align: center;
313
+ margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
314
+ }
315
+
316
+ .show-more-button {
317
+ display: inline-flex;
318
+ align-items: center;
319
+ justify-content: center;
320
+ padding: ${unsafeCSS(sharedStyles.spacing.sm)} ${unsafeCSS(sharedStyles.spacing.lg)};
321
+ background: transparent;
322
+ border: 1px solid ${sharedStyles.colors.border.default};
323
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
324
+ cursor: pointer;
325
+ font-size: 14px;
326
+ font-weight: 500;
327
+ transition: all 0.2s ease;
328
+ color: ${sharedStyles.colors.text.primary};
329
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
330
+ }
331
+
332
+ .show-more-button:hover {
333
+ background: ${sharedStyles.colors.background.secondary};
334
+ border-color: ${sharedStyles.colors.border.muted};
335
+ transform: translateY(-1px);
336
+ }
337
+
338
+ .show-more-button:active {
339
+ transform: translateY(0);
340
+ }
341
+
342
+ .incident-actions {
343
+ display: flex;
344
+ gap: ${unsafeCSS(sharedStyles.spacing.md)};
345
+ align-items: center;
346
+ margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
347
+ padding-top: ${unsafeCSS(sharedStyles.spacing.lg)};
348
+ border-top: 1px solid ${sharedStyles.colors.border.default};
349
+ }
350
+
351
+ .subscribe-button {
352
+ display: inline-flex;
353
+ align-items: center;
354
+ gap: ${unsafeCSS(sharedStyles.spacing.xs)};
355
+ padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.md)};
356
+ background: transparent;
357
+ border: 1px solid ${sharedStyles.colors.border.default};
358
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
359
+ cursor: pointer;
360
+ font-size: 13px;
361
+ font-weight: 400;
362
+ transition: all 0.2s ease;
363
+ color: ${sharedStyles.colors.text.primary};
364
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
365
+ }
366
+
367
+ .subscribe-button:hover {
368
+ background: ${sharedStyles.colors.background.secondary};
369
+ border-color: ${sharedStyles.colors.border.muted};
370
+ }
371
+
372
+ .subscribe-button.subscribed {
373
+ background: ${cssManager.bdTheme('#f0fdf4', '#064e3b')};
374
+ border-color: ${cssManager.bdTheme('#86efac', '#047857')};
375
+ color: ${cssManager.bdTheme('#047857', '#86efac')};
376
+ }
377
+
378
+ .subscribe-button.subscribed:hover {
379
+ background: ${cssManager.bdTheme('#dcfce7', '#065f46')};
380
+ }
381
+
382
+ .collapsed-hint {
383
+ font-size: 12px;
384
+ color: ${sharedStyles.colors.text.secondary};
385
+ text-align: center;
386
+ margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
387
+ opacity: 0.8;
388
+ }
389
+
390
+ @media (max-width: 640px) {
391
+ .container {
392
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
393
+ }
394
+
395
+ .incident-header {
396
+ padding: ${unsafeCSS(sharedStyles.spacing.md)};
397
+ }
398
+
399
+ .incident-meta {
400
+ flex-direction: column;
401
+ gap: ${unsafeCSS(sharedStyles.spacing.xs)};
402
+ }
63
403
  }
64
404
  `,
65
405
  ];
66
406
 
67
407
  public render(): TemplateResult {
68
408
  return html`
69
- <style></style>
70
- <div class="mainbox">
71
- <uplinternal-miniheading> Current Incidents </uplinternal-miniheading>
72
- ${this.currentIncidences.length
73
- ? html``
74
- : html` <div class="noIncidentBox">No incidents ongoing.</div> `}
75
- <uplinternal-miniheading> Past Incidents </uplinternal-miniheading>
76
- ${this.pastIncidences.length
77
- ? html``
78
- : html` <div class="noIncidentBox">No past incidents in the last 90 days.</div> `}
409
+ <div class="container">
410
+ <uplinternal-miniheading>Current Incidents</uplinternal-miniheading>
411
+ ${this.loading ? html`
412
+ <div class="loading-skeleton"></div>
413
+ ` : this.currentIncidents.length ? html`
414
+ ${this.currentIncidents.map(incident => this.renderIncident(incident, true))}
415
+ ` :
416
+ html`<div class="noIncidentBox">No incidents ongoing.</div>`
417
+ }
418
+
419
+ <uplinternal-miniheading>Past Incidents</uplinternal-miniheading>
420
+ ${this.loading ? html`
421
+ <div class="loading-skeleton"></div>
422
+ <div class="loading-skeleton"></div>
423
+ ` : this.pastIncidents.length ?
424
+ this.pastIncidents.slice(0, 5).map(incident => this.renderIncident(incident, false)) :
425
+ html`<div class="noIncidentBox">No past incidents in the last ${this.daysToShow} days.</div>`
426
+ }
427
+
428
+ ${this.pastIncidents.length > 5 && !this.loading ? html`
429
+ <div class="show-more">
430
+ <button class="show-more-button" @click=${this.handleShowMore}>
431
+ Show ${this.pastIncidents.length - 5} more incidents
432
+ </button>
433
+ </div>
434
+ ` : ''}
435
+ </div>
436
+ `;
437
+ }
438
+
439
+ private renderIncident(incident: IIncidentDetails, isCurrent: boolean): TemplateResult {
440
+ const latestUpdate = incident.updates[incident.updates.length - 1];
441
+ const duration = incident.endTime ?
442
+ this.formatDuration(incident.endTime - incident.startTime) :
443
+ this.formatDuration(Date.now() - incident.startTime);
444
+
445
+ return html`
446
+ <div class="incident-card ${this.expandedIncidents.has(incident.id) ? 'expanded' : ''}">
447
+ <div class="incident-header ${incident.severity}" @click=${() => this.toggleIncident(incident.id)}>
448
+ <div>
449
+ <h3 class="incident-title">${incident.title}</h3>
450
+ <div class="incident-meta">
451
+ <span>Started: ${this.formatDate(incident.startTime)}</span>
452
+ <span>Duration: ${duration}</span>
453
+ ${incident.endTime ? html`
454
+ <span>Ended: ${this.formatDate(incident.endTime)}</span>
455
+ ` : ''}
456
+ </div>
457
+ ${!this.expandedIncidents.has(incident.id) ? html`
458
+ <div style="
459
+ margin-top: ${unsafeCSS(sharedStyles.spacing.sm)};
460
+ font-size: 13px;
461
+ color: ${sharedStyles.colors.text.secondary};
462
+ display: flex;
463
+ align-items: center;
464
+ gap: ${unsafeCSS(sharedStyles.spacing.md)};
465
+ ">
466
+ ${incident.impact ? html`
467
+ <span style="
468
+ overflow: hidden;
469
+ text-overflow: ellipsis;
470
+ white-space: nowrap;
471
+ max-width: 500px;
472
+ ">${incident.impact}</span>
473
+ ` : ''}
474
+ <span style="
475
+ font-size: 12px;
476
+ color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
477
+ ">
478
+ ${incident.updates.length} update${incident.updates.length !== 1 ? 's' : ''}
479
+ </span>
480
+ </div>
481
+ ` : ''}
482
+ </div>
483
+ <div style="display: flex; align-items: center; gap: ${unsafeCSS(sharedStyles.spacing.md)};">
484
+ <div class="incident-status ${latestUpdate.status}">
485
+ ${this.getStatusIcon(latestUpdate.status)}
486
+ ${latestUpdate.status.replace(/_/g, ' ')}
487
+ </div>
488
+ <div class="expand-icon" style="
489
+ font-size: 10px;
490
+ color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
491
+ transition: transform 0.2s ease;
492
+ display: flex;
493
+ align-items: center;
494
+ justify-content: center;
495
+ width: 24px;
496
+ height: 24px;
497
+ border-radius: 4px;
498
+ background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
499
+ ${this.expandedIncidents.has(incident.id) ? 'transform: rotate(180deg);' : ''}
500
+ ">
501
+
502
+ </div>
503
+ </div>
504
+ </div>
505
+
506
+ ${this.expandedIncidents.has(incident.id) ? html`
507
+ <div class="incident-body">
508
+ <div class="incident-impact">
509
+ <strong>Impact:</strong> ${incident.impact}
510
+ </div>
511
+
512
+ ${incident.affectedServices.length > 0 ? html`
513
+ <div class="affected-services">
514
+ <div class="affected-services-title">Affected Services:</div>
515
+ ${incident.affectedServices.map(service => html`
516
+ <span class="service-tag">${service}</span>
517
+ `)}
518
+ </div>
519
+ ` : ''}
520
+
521
+ ${incident.updates.length > 0 ? html`
522
+ <div class="incident-updates">
523
+ <h4 style="font-size: 14px; margin: 0 0 12px 0;">Updates</h4>
524
+ ${incident.updates.slice(-3).reverse().map(update => this.renderUpdate(update))}
525
+ </div>
526
+ ` : ''}
527
+
528
+ ${incident.rootCause && isCurrent === false ? html`
529
+ <div class="incident-impact" style="margin-top: 12px;">
530
+ <strong>Root Cause:</strong> ${incident.rootCause}
531
+ </div>
532
+ ` : ''}
533
+
534
+ ${incident.resolution && isCurrent === false ? html`
535
+ <div class="incident-impact" style="margin-top: 12px;">
536
+ <strong>Resolution:</strong> ${incident.resolution}
537
+ </div>
538
+ ` : ''}
539
+
540
+ <div class="incident-actions">
541
+ <button
542
+ class="subscribe-button ${this.isSubscribedToIncident(incident.id) ? 'subscribed' : ''}"
543
+ @click=${(e: Event) => {
544
+ e.stopPropagation();
545
+ this.handleIncidentSubscribe(incident);
546
+ }}
547
+ >
548
+ ${this.isSubscribedToIncident(incident.id) ? html`
549
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
550
+ <path d="M11.6667 3.5L5.25 9.91667L2.33334 7" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
551
+ </svg>
552
+ Subscribed to updates
553
+ ` : html`
554
+ <svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
555
+ <path d="M10.5 5.25V8.75C10.5 9.34674 10.2629 9.91903 9.84099 10.341C9.41903 10.7629 8.84674 11 8.25 11L3.75 11C3.15326 11 2.58097 10.7629 2.15901 10.341C1.73705 9.91903 1.5 9.34674 1.5 8.75V4.25C1.5 3.65326 1.73705 3.08097 2.15901 2.65901C2.58097 2.23705 3.15326 2 3.75 2H7.25" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
556
+ <path d="M9 1.5H12.5M12.5 1.5V5M12.5 1.5L6 8" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
557
+ </svg>
558
+ Subscribe to updates
559
+ `}
560
+ </button>
561
+ ${isCurrent ? html`
562
+ <span style="
563
+ font-size: 12px;
564
+ color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
565
+ ">Get notified when this incident is updated or resolved</span>
566
+ ` : ''}
567
+ </div>
568
+ </div>
569
+ ` : ''}
570
+ </div>
571
+ `;
572
+ }
573
+
574
+ private renderUpdate(update: any): TemplateResult {
575
+ return html`
576
+ <div class="update-item">
577
+ <div class="update-time">${this.formatDate(update.timestamp)}</div>
578
+ <div class="update-message">${update.message}</div>
579
+ ${update.author ? html`
580
+ <div class="update-author">— ${update.author}</div>
581
+ ` : ''}
79
582
  </div>
80
583
  `;
81
584
  }
82
585
 
586
+ private getStatusIcon(status: string): TemplateResult {
587
+ return html`<span style="
588
+ display: inline-block;
589
+ width: 6px;
590
+ height: 6px;
591
+ border-radius: 50%;
592
+ margin-right: 4px;
593
+ background: ${status === 'resolved' ? sharedStyles.colors.status.operational :
594
+ status === 'monitoring' ? sharedStyles.colors.status.maintenance :
595
+ status === 'identified' ? sharedStyles.colors.status.degraded :
596
+ sharedStyles.colors.status.partial};
597
+ "></span>`;
598
+ }
599
+
600
+ private formatDate(timestamp: number): string {
601
+ const date = new Date(timestamp);
602
+ const now = Date.now();
603
+ const diff = now - timestamp;
604
+
605
+ // Less than 1 hour ago
606
+ if (diff < 60 * 60 * 1000) {
607
+ const minutes = Math.floor(diff / (60 * 1000));
608
+ return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
609
+ }
610
+
611
+ // Less than 24 hours ago
612
+ if (diff < 24 * 60 * 60 * 1000) {
613
+ const hours = Math.floor(diff / (60 * 60 * 1000));
614
+ return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
615
+ }
616
+
617
+ // Less than 7 days ago
618
+ if (diff < 7 * 24 * 60 * 60 * 1000) {
619
+ const days = Math.floor(diff / (24 * 60 * 60 * 1000));
620
+ return `${days} day${days !== 1 ? 's' : ''} ago`;
621
+ }
622
+
623
+ // Default to full date
624
+ return date.toLocaleDateString('en-US', {
625
+ month: 'short',
626
+ day: 'numeric',
627
+ year: date.getFullYear() !== new Date().getFullYear() ? 'numeric' : undefined
628
+ });
629
+ }
630
+
631
+ private formatDuration(milliseconds: number): string {
632
+ const minutes = Math.floor(milliseconds / (60 * 1000));
633
+ const hours = Math.floor(minutes / 60);
634
+ const days = Math.floor(hours / 24);
635
+
636
+ if (days > 0) {
637
+ return `${days}d ${hours % 24}h`;
638
+ } else if (hours > 0) {
639
+ return `${hours}h ${minutes % 60}m`;
640
+ } else {
641
+ return `${minutes}m`;
642
+ }
643
+ }
644
+
645
+ private toggleIncident(incidentId: string) {
646
+ const newExpanded = new Set(this.expandedIncidents);
647
+ if (newExpanded.has(incidentId)) {
648
+ newExpanded.delete(incidentId);
649
+ } else {
650
+ newExpanded.add(incidentId);
651
+ }
652
+ this.expandedIncidents = newExpanded;
653
+ }
654
+
655
+ private handleIncidentClick(incident: IIncidentDetails) {
656
+ this.dispatchEvent(new CustomEvent('incidentClick', {
657
+ detail: { incident },
658
+ bubbles: true,
659
+ composed: true
660
+ }));
661
+ }
662
+
663
+ private handleShowMore() {
664
+ // This would typically load more incidents or navigate to a full list
665
+ console.log('Show more incidents');
666
+ }
667
+
668
+ private isSubscribedToIncident(incidentId: string): boolean {
669
+ return this.subscribedIncidents.has(incidentId);
670
+ }
671
+
672
+ private handleIncidentSubscribe(incident: IIncidentDetails) {
673
+ const newSubscribed = new Set(this.subscribedIncidents);
674
+ if (newSubscribed.has(incident.id)) {
675
+ newSubscribed.delete(incident.id);
676
+ this.dispatchEvent(new CustomEvent('incidentUnsubscribe', {
677
+ detail: {
678
+ incident,
679
+ incidentId: incident.id
680
+ },
681
+ bubbles: true,
682
+ composed: true
683
+ }));
684
+ } else {
685
+ newSubscribed.add(incident.id);
686
+ this.dispatchEvent(new CustomEvent('incidentSubscribe', {
687
+ detail: {
688
+ incident,
689
+ incidentId: incident.id,
690
+ incidentTitle: incident.title,
691
+ affectedServices: incident.affectedServices
692
+ },
693
+ bubbles: true,
694
+ composed: true
695
+ }));
696
+ }
697
+ this.subscribedIncidents = newSubscribed;
698
+ }
699
+
83
700
  public dispatchReportNewIncident() {
84
- this.dispatchEvent(new CustomEvent('reportNewIncident', {}));
701
+ this.dispatchEvent(new CustomEvent('reportNewIncident', {
702
+ bubbles: true,
703
+ composed: true
704
+ }));
85
705
  }
86
706
 
87
707
  public dispatchStatusSubscribe() {
88
- this.dispatchEvent(new CustomEvent('statusSubscribe', {}));
708
+ this.dispatchEvent(new CustomEvent('statusSubscribe', {
709
+ bubbles: true,
710
+ composed: true
711
+ }));
89
712
  }
90
713
  }