@uptime.link/statuspage 1.1.0 → 1.3.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 (29) hide show
  1. package/dist_bundle/bundle.js +1814 -917
  2. package/dist_bundle/bundle.js.map +3 -3
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/elements/upl-statuspage-assetsselector.js +119 -45
  5. package/dist_ts_web/elements/upl-statuspage-footer.js +81 -27
  6. package/dist_ts_web/elements/upl-statuspage-header.js +79 -19
  7. package/dist_ts_web/elements/upl-statuspage-incidents.js +242 -55
  8. package/dist_ts_web/elements/upl-statuspage-statsgrid.d.ts +3 -0
  9. package/dist_ts_web/elements/upl-statuspage-statsgrid.js +239 -52
  10. package/dist_ts_web/elements/upl-statuspage-statusbar.js +58 -7
  11. package/dist_ts_web/elements/upl-statuspage-statusdetails.js +36 -11
  12. package/dist_ts_web/elements/upl-statuspage-statusmonth.d.ts +2 -0
  13. package/dist_ts_web/elements/upl-statuspage-statusmonth.js +415 -230
  14. package/dist_ts_web/styles/shared.styles.d.ts +22 -0
  15. package/dist_ts_web/styles/shared.styles.js +146 -3
  16. package/dist_watch/bundle.js +1425 -446
  17. package/dist_watch/bundle.js.map +3 -3
  18. package/dist_watch/index.html +1 -0
  19. package/package.json +1 -1
  20. package/ts_web/00_commitinfo_data.ts +1 -1
  21. package/ts_web/elements/upl-statuspage-assetsselector.ts +118 -44
  22. package/ts_web/elements/upl-statuspage-footer.ts +80 -26
  23. package/ts_web/elements/upl-statuspage-header.ts +87 -18
  24. package/ts_web/elements/upl-statuspage-incidents.ts +248 -56
  25. package/ts_web/elements/upl-statuspage-statsgrid.ts +235 -55
  26. package/ts_web/elements/upl-statuspage-statusbar.ts +57 -6
  27. package/ts_web/elements/upl-statuspage-statusdetails.ts +35 -10
  28. package/ts_web/elements/upl-statuspage-statusmonth.ts +436 -252
  29. package/ts_web/styles/shared.styles.ts +166 -2
@@ -52,242 +52,402 @@ export class UplStatuspageStatusmonth extends DeesElement {
52
52
  sharedStyles.commonStyles,
53
53
  css`
54
54
  :host {
55
- position: relative;
56
- display: block;
57
- background: transparent;
58
- font-family: ${unsafeCSS(sharedStyles.fonts.base)};
59
- color: ${sharedStyles.colors.text.primary};
55
+ position: relative;
56
+ display: block;
57
+ background: transparent;
58
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
59
+ color: ${sharedStyles.colors.text.primary};
60
+ }
61
+
62
+ .container {
63
+ max-width: 1200px;
64
+ margin: 0 auto;
65
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
66
+ }
67
+
68
+ .mainbox {
69
+ display: grid;
70
+ grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
71
+ gap: ${unsafeCSS(sharedStyles.spacing.lg)};
72
+ }
73
+
74
+ /* Month card with entrance animation */
75
+ .statusMonth {
76
+ background: ${sharedStyles.colors.background.card};
77
+ padding: ${unsafeCSS(sharedStyles.spacing.lg)};
78
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
79
+ border: 1px solid ${sharedStyles.colors.border.default};
80
+ position: relative;
81
+ transition: all ${unsafeCSS(sharedStyles.durations.normal)} ${unsafeCSS(sharedStyles.easings.default)};
82
+ display: flex;
83
+ flex-direction: column;
84
+ min-height: 280px;
85
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
86
+ animation: fadeInUp 0.5s ${unsafeCSS(sharedStyles.easings.default)} both;
87
+ }
88
+
89
+ .statusMonth:nth-child(1) { animation-delay: 0ms; }
90
+ .statusMonth:nth-child(2) { animation-delay: 100ms; }
91
+ .statusMonth:nth-child(3) { animation-delay: 200ms; }
92
+ .statusMonth:nth-child(4) { animation-delay: 300ms; }
93
+ .statusMonth:nth-child(5) { animation-delay: 400ms; }
94
+
95
+ @keyframes fadeInUp {
96
+ from {
97
+ opacity: 0;
98
+ transform: translateY(16px);
60
99
  }
61
-
100
+ to {
101
+ opacity: 1;
102
+ transform: translateY(0);
103
+ }
104
+ }
105
+
106
+ .statusMonth:hover {
107
+ border-color: ${sharedStyles.colors.border.muted};
108
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.md)};
109
+ transform: translateY(-2px);
110
+ }
111
+
112
+ .month-header {
113
+ font-size: 12px;
114
+ font-weight: 600;
115
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
116
+ color: ${sharedStyles.colors.text.primary};
117
+ letter-spacing: 0.04em;
118
+ text-transform: uppercase;
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 8px;
122
+ }
123
+
124
+ .month-header .current-badge {
125
+ font-size: 9px;
126
+ padding: 2px 6px;
127
+ background: ${sharedStyles.colors.status.operational};
128
+ color: white;
129
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
130
+ font-weight: 500;
131
+ letter-spacing: 0.02em;
132
+ }
133
+
134
+ .days-container {
135
+ flex: 1;
136
+ display: flex;
137
+ flex-direction: column;
138
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
139
+ }
140
+
141
+ .days-grid {
142
+ display: grid;
143
+ grid-template-columns: repeat(7, 1fr);
144
+ gap: 4px;
145
+ width: 100%;
146
+ }
147
+
148
+ .weekday-label {
149
+ font-size: 9px;
150
+ text-align: center;
151
+ color: ${sharedStyles.colors.text.muted};
152
+ font-weight: 600;
153
+ height: 20px;
154
+ line-height: 20px;
155
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.xs)};
156
+ text-transform: uppercase;
157
+ letter-spacing: 0.02em;
158
+ }
159
+
160
+ /* Calendar day cell */
161
+ .statusDay {
162
+ aspect-ratio: 1;
163
+ border-radius: 4px;
164
+ cursor: pointer;
165
+ transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
166
+ position: relative;
167
+ animation: dayFadeIn 0.3s ${unsafeCSS(sharedStyles.easings.default)} both;
168
+ animation-delay: calc(var(--day-index, 0) * 15ms);
169
+ }
170
+
171
+ @keyframes dayFadeIn {
172
+ from {
173
+ opacity: 0;
174
+ transform: scale(0.8);
175
+ }
176
+ to {
177
+ opacity: 1;
178
+ transform: scale(1);
179
+ }
180
+ }
181
+
182
+ .statusDay:hover:not(.empty) {
183
+ transform: scale(1.2);
184
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
185
+ z-index: 2;
186
+ }
187
+
188
+ /* Current day highlight */
189
+ .statusDay.today {
190
+ box-shadow: 0 0 0 2px ${sharedStyles.colors.text.primary};
191
+ z-index: 1;
192
+ }
193
+
194
+ .statusDay.today:hover {
195
+ box-shadow: 0 0 0 2px ${sharedStyles.colors.text.primary}, 0 4px 8px rgba(0, 0, 0, 0.15);
196
+ }
197
+
198
+ /* Status colors with intensity variations based on uptime */
199
+ .statusDay.operational {
200
+ background: ${sharedStyles.colors.status.operational};
201
+ }
202
+
203
+ .statusDay.operational.uptime-high {
204
+ background: ${cssManager.bdTheme('#22c55e', '#22c55e')};
205
+ }
206
+
207
+ .statusDay.operational.uptime-mid {
208
+ background: ${cssManager.bdTheme('#4ade80', '#4ade80')};
209
+ }
210
+
211
+ .statusDay.degraded {
212
+ background: ${sharedStyles.colors.status.degraded};
213
+ }
214
+
215
+ .statusDay.partial_outage {
216
+ background: ${sharedStyles.colors.status.partial};
217
+ }
218
+
219
+ .statusDay.major_outage {
220
+ background: ${sharedStyles.colors.status.major};
221
+ animation: dayFadeIn 0.3s ${unsafeCSS(sharedStyles.easings.default)} both,
222
+ majorOutagePulse 2s ease-in-out infinite;
223
+ }
224
+
225
+ @keyframes majorOutagePulse {
226
+ 0%, 100% { opacity: 1; }
227
+ 50% { opacity: 0.8; }
228
+ }
229
+
230
+ .statusDay.maintenance {
231
+ background: ${sharedStyles.colors.status.maintenance};
232
+ }
233
+
234
+ .statusDay.no-data {
235
+ background: ${sharedStyles.colors.background.muted};
236
+ opacity: 0.5;
237
+ }
238
+
239
+ .statusDay.empty {
240
+ background: transparent;
241
+ cursor: default;
242
+ pointer-events: none;
243
+ animation: none;
244
+ }
245
+
246
+ /* Incident count indicator */
247
+ .incident-count {
248
+ position: absolute;
249
+ top: 50%;
250
+ left: 50%;
251
+ transform: translate(-50%, -50%);
252
+ font-size: 8px;
253
+ font-weight: 700;
254
+ color: white;
255
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
256
+ line-height: 1;
257
+ }
258
+
259
+ /* Overall uptime footer */
260
+ .overall-uptime {
261
+ font-size: 12px;
262
+ margin-top: auto;
263
+ padding-top: ${unsafeCSS(sharedStyles.spacing.md)};
264
+ color: ${sharedStyles.colors.text.secondary};
265
+ display: flex;
266
+ flex-direction: column;
267
+ gap: 8px;
268
+ border-top: 1px solid ${sharedStyles.colors.border.default};
269
+ }
270
+
271
+ .uptime-stat {
272
+ display: flex;
273
+ justify-content: space-between;
274
+ align-items: center;
275
+ }
276
+
277
+ .uptime-value {
278
+ font-weight: 600;
279
+ color: ${sharedStyles.colors.text.primary};
280
+ font-variant-numeric: tabular-nums;
281
+ font-size: 13px;
282
+ }
283
+
284
+ .uptime-value.good {
285
+ color: ${sharedStyles.colors.status.operational};
286
+ }
287
+
288
+ .uptime-value.warning {
289
+ color: ${sharedStyles.colors.status.degraded};
290
+ }
291
+
292
+ .uptime-value.bad {
293
+ color: ${sharedStyles.colors.status.partial};
294
+ }
295
+
296
+ /* Uptime bar visualization */
297
+ .uptime-bar {
298
+ height: 4px;
299
+ background: ${sharedStyles.colors.background.muted};
300
+ border-radius: 2px;
301
+ overflow: hidden;
302
+ margin-top: 4px;
303
+ }
304
+
305
+ .uptime-bar-fill {
306
+ height: 100%;
307
+ border-radius: 2px;
308
+ transition: width ${unsafeCSS(sharedStyles.durations.slow)} ${unsafeCSS(sharedStyles.easings.default)};
309
+ }
310
+
311
+ .uptime-bar-fill.good {
312
+ background: ${sharedStyles.colors.status.operational};
313
+ }
314
+
315
+ .uptime-bar-fill.warning {
316
+ background: ${sharedStyles.colors.status.degraded};
317
+ }
318
+
319
+ .uptime-bar-fill.bad {
320
+ background: ${sharedStyles.colors.status.partial};
321
+ }
322
+
323
+ /* Loading skeleton */
324
+ .loading-skeleton {
325
+ display: flex;
326
+ flex-direction: column;
327
+ gap: ${unsafeCSS(sharedStyles.spacing.sm)};
328
+ height: 100%;
329
+ }
330
+
331
+ .skeleton-header {
332
+ height: 20px;
333
+ width: 80px;
334
+ background: ${sharedStyles.colors.background.muted};
335
+ border-radius: 4px;
336
+ animation: shimmer 1.5s infinite;
337
+ }
338
+
339
+ .skeleton-grid {
340
+ flex: 1;
341
+ display: grid;
342
+ grid-template-columns: repeat(7, 1fr);
343
+ gap: 3px;
344
+ }
345
+
346
+ .skeleton-day {
347
+ background: ${sharedStyles.colors.background.muted};
348
+ border-radius: 3px;
349
+ animation: shimmer 1.5s infinite;
350
+ animation-delay: calc(var(--index) * 30ms);
351
+ }
352
+
353
+ @keyframes shimmer {
354
+ 0% { opacity: 0.5; }
355
+ 50% { opacity: 1; }
356
+ 100% { opacity: 0.5; }
357
+ }
358
+
359
+ /* Tooltip */
360
+ .tooltip {
361
+ position: absolute;
362
+ background: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
363
+ color: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
364
+ padding: 10px 14px;
365
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
366
+ font-size: 12px;
367
+ pointer-events: none;
368
+ opacity: 0;
369
+ transition: opacity ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)},
370
+ transform ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
371
+ z-index: 50;
372
+ white-space: nowrap;
373
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.lg)};
374
+ line-height: 1.5;
375
+ transform: translateX(-50%) translateY(4px);
376
+ }
377
+
378
+ .tooltip.visible {
379
+ opacity: 1;
380
+ transform: translateX(-50%) translateY(0);
381
+ }
382
+
383
+ .tooltip-date {
384
+ font-weight: 600;
385
+ margin-bottom: 6px;
386
+ font-size: 13px;
387
+ }
388
+
389
+ .tooltip-stat {
390
+ font-size: 11px;
391
+ opacity: 0.85;
392
+ display: flex;
393
+ align-items: center;
394
+ gap: 6px;
395
+ }
396
+
397
+ .tooltip-stat + .tooltip-stat {
398
+ margin-top: 2px;
399
+ }
400
+
401
+ .tooltip-uptime-bar {
402
+ height: 3px;
403
+ width: 60px;
404
+ background: rgba(128, 128, 128, 0.3);
405
+ border-radius: 2px;
406
+ overflow: hidden;
407
+ margin-top: 8px;
408
+ }
409
+
410
+ .tooltip-uptime-fill {
411
+ height: 100%;
412
+ border-radius: 2px;
413
+ }
414
+
415
+ .no-data-message {
416
+ grid-column: 1 / -1;
417
+ text-align: center;
418
+ padding: ${unsafeCSS(sharedStyles.spacing['2xl'])};
419
+ color: ${sharedStyles.colors.text.secondary};
420
+ animation: fadeInUp 0.4s ${unsafeCSS(sharedStyles.easings.default)} both;
421
+ }
422
+
423
+ @media (max-width: 640px) {
62
424
  .container {
63
- max-width: 1200px;
64
- margin: 0 auto;
65
- padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
425
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
66
426
  }
67
427
 
68
428
  .mainbox {
69
- display: grid;
70
- grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
71
- gap: ${unsafeCSS(sharedStyles.spacing.lg)};
429
+ grid-template-columns: 1fr;
430
+ gap: ${unsafeCSS(sharedStyles.spacing.md)};
72
431
  }
73
432
 
74
433
  .statusMonth {
75
- background: ${cssManager.bdTheme('#ffffff', '#0a0a0a')};
76
- padding: ${unsafeCSS(sharedStyles.spacing.lg)};
77
- border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
78
- border: 1px solid ${cssManager.bdTheme('#e5e7eb', '#27272a')};
79
- position: relative;
80
- transition: all 0.2s ease;
81
- display: flex;
82
- flex-direction: column;
83
- min-height: 280px;
84
- box-shadow: ${cssManager.bdTheme('0 1px 2px 0 rgba(0, 0, 0, 0.05)', 'none')};
85
- }
86
-
87
- .statusMonth:hover {
88
- border-color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
89
- box-shadow: ${cssManager.bdTheme('0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', '0 0 0 1px rgba(255, 255, 255, 0.1)')};
90
- }
91
-
92
- .month-header {
93
- font-size: 13px;
94
- font-weight: 600;
95
- margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
96
- color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
97
- letter-spacing: 0.02em;
98
- text-transform: uppercase;
99
- }
100
-
101
- .days-container {
102
- flex: 1;
103
- display: flex;
104
- flex-direction: column;
105
- margin-bottom: ${unsafeCSS(sharedStyles.spacing.lg)};
434
+ padding: ${unsafeCSS(sharedStyles.spacing.md)};
435
+ min-height: 260px;
106
436
  }
107
437
 
108
438
  .days-grid {
109
- display: grid;
110
- grid-template-columns: repeat(7, 1fr);
111
439
  gap: 3px;
112
- width: 100%;
113
- }
114
-
115
- .weekday-label {
116
- font-size: 10px;
117
- text-align: center;
118
- color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
119
- font-weight: 500;
120
- height: 20px;
121
- line-height: 20px;
122
- margin-bottom: ${unsafeCSS(sharedStyles.spacing.sm)};
123
- text-transform: uppercase;
124
- }
125
-
126
- .statusDay {
127
- aspect-ratio: 1;
128
- border-radius: 4px;
129
- cursor: pointer;
130
- transition: all 0.15s ease;
131
- position: relative;
132
440
  }
133
441
 
134
442
  .statusDay:hover:not(.empty) {
135
- transform: scale(1.15);
136
- box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
137
- z-index: 1;
138
- }
139
-
140
- .statusDay.operational {
141
- background: #22c55e;
142
- }
143
-
144
- .statusDay.degraded {
145
- background: #fbbf24;
146
- }
147
-
148
- .statusDay.partial_outage {
149
- background: #f87171;
150
- }
151
-
152
- .statusDay.major_outage {
153
- background: #ef4444;
154
- }
155
-
156
- .statusDay.maintenance {
157
- background: #60a5fa;
158
- }
159
-
160
- .statusDay.no-data {
161
- background: ${cssManager.bdTheme('#e5e7eb', '#27272a')};
162
- opacity: 0.6;
163
- }
164
-
165
- .statusDay.empty {
166
- background: transparent;
167
- cursor: default;
168
- pointer-events: none;
169
- }
170
-
171
- .overall-uptime {
172
- font-size: 12px;
173
- margin-top: auto;
174
- padding-top: ${unsafeCSS(sharedStyles.spacing.md)};
175
- color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')};
176
- display: flex;
177
- flex-direction: column;
178
- gap: 6px;
179
- border-top: 1px solid ${cssManager.bdTheme('#f3f4f6', '#1f1f1f')};
180
- }
181
-
182
- .uptime-stat {
183
- display: flex;
184
- justify-content: space-between;
185
- align-items: center;
186
- }
187
-
188
- .uptime-value {
189
- font-weight: 600;
190
- color: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
191
- font-variant-numeric: tabular-nums;
192
- font-size: 13px;
443
+ transform: scale(1.1);
193
444
  }
194
445
 
195
446
  .loading-skeleton {
196
- display: flex;
197
- flex-direction: column;
198
- gap: ${unsafeCSS(sharedStyles.spacing.sm)};
199
- height: 100%;
200
- }
201
-
202
- .skeleton-header {
203
- height: 20px;
204
- width: 80px;
205
- background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
206
- border-radius: 4px;
207
- animation: pulse 2s infinite;
208
- }
209
-
210
- .skeleton-grid {
211
- flex: 1;
212
- display: grid;
213
- grid-template-columns: repeat(7, 1fr);
214
- gap: 2px;
215
- }
216
-
217
- .skeleton-day {
218
- background: ${cssManager.bdTheme('#f3f4f6', '#27272a')};
219
- border-radius: 2px;
220
- animation: pulse 2s infinite;
221
- animation-delay: calc(var(--index) * 0.05s);
222
- }
223
-
224
- @keyframes pulse {
225
- 0%, 100% { opacity: 1; }
226
- 50% { opacity: 0.5; }
227
- }
228
-
229
- @keyframes loading {
230
- 0% { transform: translateX(-100%); }
231
- 100% { transform: translateX(200%); }
232
- }
233
-
234
- .tooltip {
235
- position: absolute;
236
- background: ${cssManager.bdTheme('#0a0a0a', '#fafafa')};
237
- color: ${cssManager.bdTheme('#fafafa', '#0a0a0a')};
238
- padding: 8px 12px;
239
- border-radius: 4px;
240
- font-size: 12px;
241
- pointer-events: none;
242
- opacity: 0;
243
- transition: opacity 0.15s;
244
- z-index: 50;
245
- white-space: nowrap;
246
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
247
- line-height: 1.5;
248
- }
249
-
250
- .tooltip.visible {
251
- opacity: 1;
252
- }
253
-
254
- .tooltip-date {
255
- font-weight: 500;
256
- margin-bottom: 4px;
257
- }
258
-
259
- .tooltip-stat {
260
- font-size: 11px;
261
- opacity: 0.9;
262
- }
263
-
264
- .no-data-message {
265
- grid-column: 1 / -1;
266
- text-align: center;
267
- padding: ${unsafeCSS(sharedStyles.spacing['2xl'])};
268
- color: ${sharedStyles.colors.text.secondary};
269
- }
270
-
271
- @media (max-width: 640px) {
272
- .container {
273
- padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
274
- }
275
-
276
- .mainbox {
277
- grid-template-columns: 1fr;
278
- gap: ${unsafeCSS(sharedStyles.spacing.md)};
279
- }
280
-
281
- .statusMonth {
282
- padding: ${unsafeCSS(sharedStyles.spacing.md)};
283
- min-height: 260px;
284
- }
285
-
286
- .loading-skeleton {
287
- height: 180px;
288
- padding: ${unsafeCSS(sharedStyles.spacing.md)};
289
- }
447
+ height: 180px;
448
+ padding: ${unsafeCSS(sharedStyles.spacing.md)};
290
449
  }
450
+ }
291
451
  `
292
452
  ]
293
453
 
@@ -327,22 +487,31 @@ export class UplStatuspageStatusmonth extends DeesElement {
327
487
  const monthDate = new Date(monthData.month + '-01');
328
488
  const monthName = monthDate.toLocaleDateString('en-US', { month: 'short', year: 'numeric' });
329
489
  const firstDayOfWeek = new Date(monthDate.getFullYear(), monthDate.getMonth(), 1).getDay();
330
-
490
+ const now = new Date();
491
+ const isCurrentMonth = monthDate.getMonth() === now.getMonth() && monthDate.getFullYear() === now.getFullYear();
492
+ const uptimeClass = this.getUptimeClass(monthData.overallUptime);
493
+
331
494
  return html`
332
495
  <div class="statusMonth" @mouseleave=${this.hideTooltip}>
333
- <div class="month-header">${monthName}</div>
496
+ <div class="month-header">
497
+ ${monthName}
498
+ ${isCurrentMonth ? html`<span class="current-badge">Current</span>` : ''}
499
+ </div>
334
500
  <div class="days-container">
335
501
  <div class="days-grid">
336
502
  ${this.renderWeekdayLabels()}
337
503
  ${this.renderEmptyDays(firstDayOfWeek)}
338
- ${monthData.days.map(day => this.renderDay(day))}
504
+ ${monthData.days.map((day, index) => this.renderDay(day, index))}
339
505
  ${this.renderTrailingEmptyDays(firstDayOfWeek + monthData.days.length)}
340
506
  </div>
341
507
  </div>
342
508
  <div class="overall-uptime">
343
509
  <div class="uptime-stat">
344
510
  <span>Uptime</span>
345
- <span class="uptime-value">${monthData.overallUptime.toFixed(2)}%</span>
511
+ <span class="uptime-value ${uptimeClass}">${monthData.overallUptime.toFixed(2)}%</span>
512
+ </div>
513
+ <div class="uptime-bar">
514
+ <div class="uptime-bar-fill ${uptimeClass}" style="width: ${monthData.overallUptime}%"></div>
346
515
  </div>
347
516
  ${monthData.totalIncidents > 0 ? html`
348
517
  <div class="uptime-stat">
@@ -355,6 +524,12 @@ export class UplStatuspageStatusmonth extends DeesElement {
355
524
  `;
356
525
  }
357
526
 
527
+ private getUptimeClass(uptime: number): string {
528
+ if (uptime >= 99.9) return 'good';
529
+ if (uptime >= 99) return 'warning';
530
+ return 'bad';
531
+ }
532
+
358
533
  private renderWeekdayLabels(): TemplateResult[] {
359
534
  const weekdays = ['S', 'M', 'T', 'W', 'T', 'F', 'S'];
360
535
  return weekdays.map(day => html`<div class="weekday-label">${day}</div>`);
@@ -370,61 +545,70 @@ export class UplStatuspageStatusmonth extends DeesElement {
370
545
  return Array(trailingCount).fill(0).map(() => html`<div class="statusDay empty"></div>`);
371
546
  }
372
547
 
373
- private renderDay(day: any): TemplateResult {
548
+ private renderDay(day: any, index: number = 0): TemplateResult {
374
549
  const status = day.status || 'no-data';
375
550
  const date = new Date(day.date);
376
- const dayNumber = date.getDate();
377
-
551
+ const now = new Date();
552
+ const isToday = date.toDateString() === now.toDateString();
553
+ const uptimeIntensity = this.getUptimeIntensity(day.uptime);
554
+
378
555
  return html`
379
- <div
380
- class="statusDay ${status}"
556
+ <div
557
+ class="statusDay ${status} ${isToday ? 'today' : ''} ${status === 'operational' ? uptimeIntensity : ''}"
558
+ style="--day-index: ${index}"
381
559
  @mouseenter=${(e: MouseEvent) => this.showTooltip && this.showDayTooltip(e, day)}
382
560
  @click=${() => this.handleDayClick(day)}
383
561
  >
384
- ${status === 'major_outage' || status === 'partial_outage' ? html`
385
- <div style="
386
- position: absolute;
387
- top: 50%;
388
- left: 50%;
389
- transform: translate(-50%, -50%);
390
- font-size: 8px;
391
- font-weight: bold;
392
- color: white;
393
- ">${day.incidents}</div>
562
+ ${(status === 'major_outage' || status === 'partial_outage') && day.incidents > 0 ? html`
563
+ <span class="incident-count">${day.incidents}</span>
394
564
  ` : ''}
395
565
  </div>
396
566
  `;
397
567
  }
398
568
 
569
+ private getUptimeIntensity(uptime: number): string {
570
+ if (uptime >= 99.9) return 'uptime-high';
571
+ if (uptime >= 99) return 'uptime-mid';
572
+ return '';
573
+ }
574
+
399
575
  private showDayTooltip(event: MouseEvent, day: any) {
400
576
  const tooltip = this.shadowRoot?.getElementById('tooltip') as HTMLElement;
401
577
  if (!tooltip) return;
402
-
578
+
403
579
  const date = new Date(day.date);
404
- const dateStr = date.toLocaleDateString('en-US', {
405
- weekday: 'long',
406
- year: 'numeric',
407
- month: 'long',
408
- day: 'numeric'
580
+ const dateStr = date.toLocaleDateString('en-US', {
581
+ weekday: 'short',
582
+ month: 'short',
583
+ day: 'numeric'
409
584
  });
410
-
585
+
411
586
  let statusText = day.status.replace(/_/g, ' ');
412
587
  statusText = statusText.charAt(0).toUpperCase() + statusText.slice(1);
413
-
588
+
589
+ const uptimeColor = day.uptime >= 99.9 ? '#22c55e' :
590
+ day.uptime >= 99 ? '#fbbf24' : '#f87171';
591
+
414
592
  tooltip.innerHTML = `
415
593
  <div class="tooltip-date">${dateStr}</div>
416
- <div class="tooltip-stat">Status: ${statusText}</div>
417
- <div class="tooltip-stat">Uptime: ${day.uptime.toFixed(2)}%</div>
418
- ${day.incidents > 0 ? `<div class="tooltip-stat">Incidents: ${day.incidents}</div>` : ''}
419
- ${day.totalDowntime > 0 ? `<div class="tooltip-stat">Downtime: ${day.totalDowntime} min</div>` : ''}
594
+ <div class="tooltip-stat">
595
+ <span style="display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: ${uptimeColor};"></span>
596
+ ${statusText}
597
+ </div>
598
+ <div class="tooltip-stat">Uptime: <strong>${day.uptime.toFixed(2)}%</strong></div>
599
+ ${day.incidents > 0 ? `<div class="tooltip-stat">Incidents: <strong>${day.incidents}</strong></div>` : ''}
600
+ ${day.totalDowntime > 0 ? `<div class="tooltip-stat">Downtime: <strong>${day.totalDowntime}m</strong></div>` : ''}
601
+ <div class="tooltip-uptime-bar">
602
+ <div class="tooltip-uptime-fill" style="width: ${day.uptime}%; background: ${uptimeColor};"></div>
603
+ </div>
420
604
  `;
421
-
605
+
422
606
  const rect = (event.target as HTMLElement).getBoundingClientRect();
423
607
  const containerRect = this.getBoundingClientRect();
424
-
608
+
425
609
  tooltip.style.left = `${rect.left - containerRect.left + rect.width / 2}px`;
426
- tooltip.style.top = `${rect.top - containerRect.top - 80}px`;
427
- tooltip.style.transform = 'translateX(-50%)';
610
+ tooltip.style.top = `${rect.top - containerRect.top - 10}px`;
611
+ tooltip.style.transform = 'translateX(-50%) translateY(-100%)';
428
612
  tooltip.classList.add('visible');
429
613
  }
430
614