@uptime.link/statuspage 1.0.74 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist_bundle/bundle.js +5019 -519
  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 +679 -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 +846 -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 +373 -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 +937 -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 +549 -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 +408 -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 +397 -62
  40. package/dist_ts_web/elements/upl-statuspage-statusmonth.d.ts +17 -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 +662 -103
  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 +102 -0
  57. package/dist_ts_web/styles/shared.styles.js +494 -0
  58. package/dist_watch/bundle.js +52265 -32033
  59. package/dist_watch/bundle.js.map +4 -4
  60. package/dist_watch/index.html +1 -0
  61. package/npmextra.json +9 -3
  62. package/package.json +19 -19
  63. package/readme.hints.md +292 -0
  64. package/readme.md +326 -149
  65. package/readme.plan.md +261 -0
  66. package/ts_web/00_commitinfo_data.ts +1 -1
  67. package/ts_web/elements/index.ts +6 -0
  68. package/ts_web/elements/internal/uplinternal-miniheading.ts +24 -17
  69. package/ts_web/elements/upl-statuspage-assetsselector.demo.ts +607 -0
  70. package/ts_web/elements/upl-statuspage-assetsselector.ts +600 -18
  71. package/ts_web/elements/upl-statuspage-footer.demo.ts +744 -0
  72. package/ts_web/elements/upl-statuspage-footer.ts +662 -30
  73. package/ts_web/elements/upl-statuspage-header.demo.ts +241 -0
  74. package/ts_web/elements/upl-statuspage-header.ts +289 -52
  75. package/ts_web/elements/upl-statuspage-incidents.demo.ts +1216 -0
  76. package/ts_web/elements/upl-statuspage-incidents.ts +840 -26
  77. package/ts_web/elements/upl-statuspage-pagetitle.demo.ts +25 -0
  78. package/ts_web/elements/upl-statuspage-pagetitle.ts +89 -0
  79. package/ts_web/elements/upl-statuspage-statsgrid.demo.ts +315 -0
  80. package/ts_web/elements/upl-statuspage-statsgrid.ts +478 -0
  81. package/ts_web/elements/upl-statuspage-statusbar.demo.ts +393 -0
  82. package/ts_web/elements/upl-statuspage-statusbar.ts +332 -20
  83. package/ts_web/elements/upl-statuspage-statusdetails.demo.ts +754 -0
  84. package/ts_web/elements/upl-statuspage-statusdetails.ts +321 -37
  85. package/ts_web/elements/upl-statuspage-statusmonth.demo.ts +876 -0
  86. package/ts_web/elements/upl-statuspage-statusmonth.ts +584 -79
  87. package/ts_web/interfaces/index.ts +95 -0
  88. package/ts_web/pages/index.ts +4 -1
  89. package/ts_web/pages/statuspage-allgreen.ts +412 -0
  90. package/ts_web/pages/statuspage-demo.ts +653 -0
  91. package/ts_web/pages/statuspage-maintenance.ts +570 -0
  92. package/ts_web/pages/statuspage-outage.ts +568 -0
  93. package/ts_web/styles/shared.styles.ts +531 -0
  94. package/dist_ts_web/pages/page1.d.ts +0 -1
  95. package/dist_ts_web/pages/page1.js +0 -11
  96. package/ts_web/pages/page1.ts +0 -11
@@ -6,10 +6,14 @@ import {
6
6
  type TemplateResult,
7
7
  cssManager,
8
8
  css,
9
+ unsafeCSS,
9
10
  } from '@design.estate/dees-element';
10
11
  import * as domtools from '@design.estate/dees-domtools';
12
+ import type { IServiceStatus } from '../interfaces/index.js';
13
+ import * as sharedStyles from '../styles/shared.styles.js';
11
14
 
12
15
  import './internal/uplinternal-miniheading.js';
16
+ import { demoFunc } from './upl-statuspage-assetsselector.demo.js';
13
17
 
14
18
  declare global {
15
19
  interface HTMLElementTagNameMap {
@@ -19,9 +23,25 @@ declare global {
19
23
 
20
24
  @customElement('upl-statuspage-assetsselector')
21
25
  export class UplStatuspageAssetsselector extends DeesElement {
22
- public static demo = () => html`
23
- <upl-statuspage-assetsselector></upl-statuspage-assetsselector>
24
- `;
26
+ public static demo = demoFunc;
27
+
28
+ @property({ type: Array })
29
+ accessor services: IServiceStatus[] = [];
30
+
31
+ @property({ type: String })
32
+ accessor filterText: string = '';
33
+
34
+ @property({ type: String })
35
+ accessor filterCategory: string = 'all';
36
+
37
+ @property({ type: Boolean })
38
+ accessor showOnlySelected: boolean = false;
39
+
40
+ @property({ type: Boolean })
41
+ accessor loading: boolean = false;
42
+
43
+ @property({ type: Boolean })
44
+ accessor expanded: boolean = false;
25
45
 
26
46
  constructor() {
27
47
  super();
@@ -29,35 +49,597 @@ export class UplStatuspageAssetsselector extends DeesElement {
29
49
 
30
50
  public static styles = [
31
51
  cssManager.defaultStyles,
52
+ sharedStyles.commonStyles,
32
53
  css`
33
54
  :host {
34
- padding: 0px 0px 15px 0px;
35
55
  display: block;
36
- background: ${cssManager.bdTheme('#eeeeeb', '#222222')};
37
- font-family: Inter;
38
- color: #fff;
56
+ background: transparent;
57
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
58
+ color: ${sharedStyles.colors.text.primary};
59
+ }
60
+
61
+ .container {
62
+ max-width: 1200px;
63
+ margin: 0 auto;
64
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)} ${unsafeCSS(sharedStyles.spacing.lg)};
65
+ }
66
+
67
+ .controls {
68
+ display: flex;
69
+ gap: ${unsafeCSS(sharedStyles.spacing.sm)};
70
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.md)};
71
+ flex-wrap: wrap;
72
+ align-items: center;
73
+ }
74
+
75
+ .search-input {
76
+ flex: 1;
77
+ min-width: 200px;
78
+ padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
79
+ border: 1px solid ${sharedStyles.colors.border.default};
80
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
81
+ background: ${sharedStyles.colors.background.card};
82
+ color: ${sharedStyles.colors.text.primary};
83
+ font-size: 13px;
84
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
85
+ transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
86
+ height: 32px;
87
+ }
88
+
89
+ .search-input:focus {
90
+ outline: none;
91
+ border-color: ${sharedStyles.colors.text.primary};
92
+ box-shadow: 0 0 0 2px ${cssManager.bdTheme('rgba(0, 0, 0, 0.05)', 'rgba(255, 255, 255, 0.05)')};
93
+ }
94
+
95
+ .search-input::placeholder {
96
+ color: ${sharedStyles.colors.text.muted};
97
+ }
98
+
99
+ .filter-button {
100
+ display: inline-flex;
101
+ align-items: center;
102
+ justify-content: center;
103
+ padding: ${unsafeCSS(sharedStyles.spacing.xs)} ${unsafeCSS(sharedStyles.spacing.sm)};
104
+ border: 1px solid ${sharedStyles.colors.border.default};
105
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
106
+ background: ${sharedStyles.colors.background.card};
107
+ color: ${sharedStyles.colors.text.secondary};
108
+ cursor: pointer;
109
+ font-size: 13px;
110
+ font-weight: 500;
111
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
112
+ transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
113
+ height: 32px;
114
+ }
115
+
116
+ .filter-button:hover {
117
+ border-color: ${sharedStyles.colors.border.muted};
118
+ color: ${sharedStyles.colors.text.primary};
119
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
120
+ }
121
+
122
+ .filter-button.active {
123
+ background: ${sharedStyles.colors.text.primary};
124
+ color: ${sharedStyles.colors.background.primary};
125
+ border-color: ${sharedStyles.colors.text.primary};
126
+ }
127
+
128
+ .selected-services {
129
+ display: flex;
130
+ flex-wrap: wrap;
131
+ gap: ${unsafeCSS(sharedStyles.spacing.sm)};
132
+ align-items: center;
133
+ }
134
+
135
+ .service-pill {
136
+ display: inline-flex;
137
+ align-items: center;
138
+ gap: ${unsafeCSS(sharedStyles.spacing.xs)};
139
+ padding: 6px ${unsafeCSS(sharedStyles.spacing.md)};
140
+ background: ${sharedStyles.colors.background.card};
141
+ border: 1px solid ${sharedStyles.colors.border.default};
142
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
143
+ font-size: 13px;
144
+ font-weight: 500;
145
+ transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
146
+ animation: pillFadeIn 0.3s ${unsafeCSS(sharedStyles.easings.default)} both;
147
+ }
148
+
149
+ .service-pill:nth-child(1) { animation-delay: 0ms; }
150
+ .service-pill:nth-child(2) { animation-delay: 30ms; }
151
+ .service-pill:nth-child(3) { animation-delay: 60ms; }
152
+ .service-pill:nth-child(4) { animation-delay: 90ms; }
153
+ .service-pill:nth-child(5) { animation-delay: 120ms; }
154
+
155
+ @keyframes pillFadeIn {
156
+ from {
157
+ opacity: 0;
158
+ transform: scale(0.9);
159
+ }
160
+ to {
161
+ opacity: 1;
162
+ transform: scale(1);
163
+ }
164
+ }
165
+
166
+ .service-pill:hover {
167
+ border-color: ${sharedStyles.colors.border.muted};
168
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
169
+ transform: translateY(-1px);
170
+ }
171
+
172
+ .service-pill .status-dot {
173
+ width: 6px;
174
+ height: 6px;
175
+ border-radius: 50%;
176
+ flex-shrink: 0;
177
+ }
178
+
179
+ .service-pill .status-dot.operational {
180
+ box-shadow: 0 0 0 2px ${cssManager.bdTheme('rgba(34, 197, 94, 0.2)', 'rgba(34, 197, 94, 0.3)')};
181
+ }
182
+
183
+ .manage-button {
184
+ display: inline-flex;
185
+ align-items: center;
186
+ gap: ${unsafeCSS(sharedStyles.spacing.xs)};
187
+ padding: 6px ${unsafeCSS(sharedStyles.spacing.md)};
188
+ background: ${sharedStyles.colors.background.card};
189
+ border: 1px solid ${sharedStyles.colors.border.default};
190
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
191
+ font-size: 13px;
192
+ font-weight: 500;
193
+ cursor: pointer;
194
+ transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
195
+ color: ${sharedStyles.colors.text.secondary};
196
+ font-family: ${unsafeCSS(sharedStyles.fonts.base)};
197
+ }
198
+
199
+ .manage-button:hover {
200
+ border-color: ${sharedStyles.colors.border.muted};
201
+ color: ${sharedStyles.colors.text.primary};
202
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
203
+ transform: translateY(-1px);
204
+ }
205
+
206
+ .manage-button:active {
207
+ transform: translateY(0);
208
+ }
209
+
210
+ .expandable-section {
211
+ margin-top: ${unsafeCSS(sharedStyles.spacing.lg)};
212
+ overflow: hidden;
213
+ animation: expandIn 0.3s ${unsafeCSS(sharedStyles.easings.default)};
214
+ }
215
+
216
+ @keyframes expandIn {
217
+ from {
218
+ opacity: 0;
219
+ transform: translateY(-8px);
220
+ }
221
+ to {
222
+ opacity: 1;
223
+ transform: translateY(0);
224
+ }
225
+ }
226
+
227
+ .expandable-content {
228
+ padding: ${unsafeCSS(sharedStyles.spacing.lg)};
229
+ background: ${sharedStyles.colors.background.secondary};
230
+ border: 1px solid ${sharedStyles.colors.border.default};
231
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.lg)};
232
+ }
233
+
234
+ .assets-grid {
235
+ display: grid;
236
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
237
+ gap: ${unsafeCSS(sharedStyles.spacing.sm)};
238
+ margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
239
+ }
240
+
241
+ .asset-card {
242
+ display: flex;
243
+ align-items: center;
244
+ padding: ${unsafeCSS(sharedStyles.spacing.md)};
245
+ background: ${sharedStyles.colors.background.card};
246
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.base)};
247
+ cursor: pointer;
248
+ transition: all ${unsafeCSS(sharedStyles.durations.fast)} ${unsafeCSS(sharedStyles.easings.default)};
249
+ border: 1px solid ${sharedStyles.colors.border.default};
250
+ gap: ${unsafeCSS(sharedStyles.spacing.sm)};
251
+ animation: cardFadeIn 0.3s ${unsafeCSS(sharedStyles.easings.default)} both;
252
+ }
253
+
254
+ @keyframes cardFadeIn {
255
+ from {
256
+ opacity: 0;
257
+ transform: translateY(8px);
258
+ }
259
+ to {
260
+ opacity: 1;
261
+ transform: translateY(0);
262
+ }
263
+ }
264
+
265
+ .asset-card:hover {
266
+ border-color: ${sharedStyles.colors.border.muted};
267
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.sm)};
268
+ transform: translateY(-2px);
269
+ }
270
+
271
+ .asset-card.selected {
272
+ border-color: ${sharedStyles.colors.text.primary};
273
+ background: ${sharedStyles.colors.background.secondary};
39
274
  }
40
275
 
41
- .mainbox {
42
- margin: auto;
43
- max-width: 900px;
276
+ .asset-card.selected:hover {
277
+ box-shadow: ${unsafeCSS(sharedStyles.shadows.md)};
278
+ }
279
+
280
+ .asset-checkbox {
281
+ width: 16px;
282
+ height: 16px;
283
+ cursor: pointer;
284
+ accent-color: ${sharedStyles.colors.text.primary};
285
+ flex-shrink: 0;
286
+ }
287
+
288
+ .asset-info {
289
+ flex: 1;
290
+ min-width: 0;
291
+ }
292
+
293
+ .asset-name {
294
+ font-weight: 600;
295
+ font-size: 14px;
296
+ margin-bottom: ${unsafeCSS(sharedStyles.spacing.xs)};
297
+ overflow: hidden;
298
+ text-overflow: ellipsis;
299
+ white-space: nowrap;
300
+ }
301
+
302
+ .asset-description {
303
+ font-size: 13px;
304
+ color: ${sharedStyles.colors.text.secondary};
305
+ overflow: hidden;
306
+ text-overflow: ellipsis;
307
+ white-space: nowrap;
308
+ }
309
+
310
+ .asset-status {
311
+ display: flex;
312
+ align-items: center;
313
+ gap: ${unsafeCSS(sharedStyles.spacing.xs)};
314
+ flex-shrink: 0;
315
+ }
316
+
317
+ .status-indicator {
318
+ width: 8px;
319
+ height: 8px;
320
+ border-radius: ${unsafeCSS(sharedStyles.borderRadius.full)};
321
+ flex-shrink: 0;
322
+ }
323
+
324
+ .status-indicator.operational, .status-dot.operational {
325
+ background: ${sharedStyles.colors.status.operational};
326
+ }
327
+ .status-indicator.degraded, .status-dot.degraded {
328
+ background: ${sharedStyles.colors.status.degraded};
329
+ }
330
+ .status-indicator.partial_outage, .status-dot.partial_outage {
331
+ background: ${sharedStyles.colors.status.partial};
332
+ }
333
+ .status-indicator.major_outage, .status-dot.major_outage {
334
+ background: ${sharedStyles.colors.status.major};
335
+ }
336
+ .status-indicator.maintenance, .status-dot.maintenance {
337
+ background: ${sharedStyles.colors.status.maintenance};
338
+ }
339
+
340
+ .status-text {
341
+ font-size: 12px;
342
+ text-transform: capitalize;
343
+ color: ${sharedStyles.colors.text.secondary};
344
+ }
345
+
346
+ .loading-message,
347
+ .no-results {
348
+ grid-column: 1 / -1;
44
349
  text-align: center;
45
- height: 50px;
46
- border-radius: 3px;
47
- background: ${cssManager.bdTheme('#ffffff', '#333333')};;
350
+ padding: ${unsafeCSS(sharedStyles.spacing.xl)};
351
+ color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
352
+ font-size: 13px;
353
+ }
354
+
355
+ .summary {
356
+ text-align: right;
357
+ font-size: 12px;
358
+ margin-top: ${unsafeCSS(sharedStyles.spacing.md)};
359
+ color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
360
+ }
361
+
362
+ .no-services {
363
+ padding: ${unsafeCSS(sharedStyles.spacing.xl)};
364
+ text-align: center;
365
+ color: ${cssManager.bdTheme('#9ca3af', '#71717a')};
366
+ font-size: 13px;
367
+ }
368
+
369
+ @media (max-width: 640px) {
370
+ .container {
371
+ padding: 0 ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)} ${unsafeCSS(sharedStyles.spacing.md)};
372
+ }
373
+
374
+ .controls {
375
+ flex-direction: column;
376
+ align-items: stretch;
377
+ }
378
+
379
+ .search-input {
380
+ width: 100%;
381
+ }
382
+
383
+ .selected-services {
384
+ flex-direction: column;
385
+ align-items: stretch;
386
+ }
387
+
388
+ .service-pill {
389
+ width: auto;
390
+ }
391
+
392
+ .expandable-content {
393
+ padding: ${unsafeCSS(sharedStyles.spacing.md)};
394
+ }
395
+
396
+ .assets-grid {
397
+ grid-template-columns: 1fr;
398
+ }
399
+
400
+ .asset-card {
401
+ padding: ${unsafeCSS(sharedStyles.spacing.sm)};
402
+ }
48
403
  }
49
404
  `,
50
405
  ]
51
406
 
52
407
  public render(): TemplateResult {
408
+ const selectedServices = this.services.filter(s => s.selected);
409
+ const selectedCount = selectedServices.length;
410
+ const categories = this.getUniqueCategories();
411
+
53
412
  return html`
54
- <style>
413
+ <div class="container">
414
+ <uplinternal-miniheading>Monitored Assets</uplinternal-miniheading>
415
+
416
+ <div class="selected-services">
417
+ ${selectedCount === 0 ? html`
418
+ <span style="color: ${cssManager.bdTheme('#9ca3af', '#71717a')}; font-size: 13px;">
419
+ No services selected
420
+ </span>
421
+ ` : selectedCount > 5 && !this.expanded ? html`
422
+ ${selectedServices.slice(0, 4).map(service => html`
423
+ <div class="service-pill">
424
+ <span class="status-dot ${service.currentStatus}"></span>
425
+ <span>${service.displayName}</span>
426
+ </div>
427
+ `)}
428
+ <span style="color: ${cssManager.bdTheme('#6b7280', '#a1a1aa')}; font-size: 12px;">
429
+ +${selectedCount - 4} more
430
+ </span>
431
+ ` : selectedServices.map(service => html`
432
+ <div class="service-pill">
433
+ <span class="status-dot ${service.currentStatus}"></span>
434
+ <span>${service.displayName}</span>
435
+ </div>
436
+ `)}
437
+
438
+ <button
439
+ class="manage-button"
440
+ @click=${() => { this.expanded = !this.expanded; }}
441
+ >
442
+ ${this.expanded ? 'Close' : 'Manage Services'}
443
+ <svg
444
+ width="10"
445
+ height="6"
446
+ viewBox="0 0 10 6"
447
+ fill="none"
448
+ xmlns="http://www.w3.org/2000/svg"
449
+ style="transform: rotate(${this.expanded ? '180deg' : '0'}); transition: transform 0.2s;"
450
+ >
451
+ <path
452
+ d="M1 1L5 5L9 1"
453
+ stroke="currentColor"
454
+ stroke-width="1.5"
455
+ stroke-linecap="round"
456
+ stroke-linejoin="round"
457
+ />
458
+ </svg>
459
+ </button>
460
+ </div>
55
461
 
56
- </style>
57
- <uplinternal-miniheading>Monitored Assets</uplinternal-miniheading>
58
- <div class="mainbox">
59
- Hello!
462
+ ${this.expanded ? html`
463
+ <div class="expandable-section">
464
+ <div class="expandable-content">
465
+ <div class="controls">
466
+ <input
467
+ type="text"
468
+ class="search-input"
469
+ placeholder="Search services..."
470
+ .value=${this.filterText}
471
+ @input=${(e: Event) => {
472
+ this.filterText = (e.target as HTMLInputElement).value;
473
+ }}
474
+ />
475
+
476
+ <button
477
+ class="filter-button ${this.filterCategory === 'all' ? 'active' : ''}"
478
+ @click=${() => { this.filterCategory = 'all'; }}
479
+ >
480
+ All
481
+ </button>
482
+
483
+ ${categories.map(category => html`
484
+ <button
485
+ class="filter-button ${this.filterCategory === category ? 'active' : ''}"
486
+ @click=${() => { this.filterCategory = category; }}
487
+ >
488
+ ${category}
489
+ </button>
490
+ `)}
491
+
492
+ <button
493
+ class="filter-button ${this.showOnlySelected ? 'active' : ''}"
494
+ @click=${() => { this.showOnlySelected = !this.showOnlySelected; }}
495
+ >
496
+ ${this.showOnlySelected ? 'Show All' : 'Selected Only'}
497
+ </button>
498
+
499
+ <button
500
+ class="filter-button"
501
+ @click=${() => this.selectAll()}
502
+ >
503
+ Select All
504
+ </button>
505
+
506
+ <button
507
+ class="filter-button"
508
+ @click=${() => this.selectNone()}
509
+ >
510
+ Select None
511
+ </button>
512
+ </div>
513
+
514
+ <div class="assets-grid">
515
+ ${this.loading ? html`
516
+ <div class="loading-message">Loading services...</div>
517
+ ` : this.renderServiceGrid()}
518
+ </div>
519
+
520
+ <div class="summary">
521
+ ${selectedCount} of ${this.services.length} services selected
522
+ </div>
523
+ </div>
524
+ </div>
525
+ ` : ''}
60
526
  </div>
61
527
  `;
62
528
  }
529
+
530
+ private getFilteredServices(): IServiceStatus[] {
531
+ return this.services.filter(service => {
532
+ // Apply text filter
533
+ if (this.filterText && !service.displayName.toLowerCase().includes(this.filterText.toLowerCase()) &&
534
+ (!service.description || !service.description.toLowerCase().includes(this.filterText.toLowerCase()))) {
535
+ return false;
536
+ }
537
+
538
+ // Apply category filter
539
+ if (this.filterCategory !== 'all' && service.category !== this.filterCategory) {
540
+ return false;
541
+ }
542
+
543
+ // Apply selected filter
544
+ if (this.showOnlySelected && !service.selected) {
545
+ return false;
546
+ }
547
+
548
+ return true;
549
+ });
550
+ }
551
+
552
+ private getUniqueCategories(): string[] {
553
+ const categories = new Set<string>();
554
+ this.services.forEach(service => {
555
+ if (service.category) {
556
+ categories.add(service.category);
557
+ }
558
+ });
559
+ return Array.from(categories).sort();
560
+ }
561
+
562
+ private toggleService(serviceId: string) {
563
+ const service = this.services.find(s => s.id === serviceId);
564
+ if (service) {
565
+ service.selected = !service.selected;
566
+ this.requestUpdate();
567
+
568
+ this.dispatchEvent(new CustomEvent('selectionChanged', {
569
+ detail: {
570
+ serviceId,
571
+ selected: service.selected,
572
+ selectedServices: this.services.filter(s => s.selected).map(s => s.id)
573
+ },
574
+ bubbles: true,
575
+ composed: true
576
+ }));
577
+ }
578
+ }
579
+
580
+ private selectAll() {
581
+ const filteredServices = this.getFilteredServices();
582
+ filteredServices.forEach(service => {
583
+ service.selected = true;
584
+ });
585
+ this.requestUpdate();
586
+ this.emitSelectionUpdate();
587
+ }
588
+
589
+ private selectNone() {
590
+ const filteredServices = this.getFilteredServices();
591
+ filteredServices.forEach(service => {
592
+ service.selected = false;
593
+ });
594
+ this.requestUpdate();
595
+ this.emitSelectionUpdate();
596
+ }
597
+
598
+ private emitSelectionUpdate() {
599
+ this.dispatchEvent(new CustomEvent('selectionChanged', {
600
+ detail: {
601
+ selectedServices: this.services.filter(s => s.selected).map(s => s.id)
602
+ },
603
+ bubbles: true,
604
+ composed: true
605
+ }));
606
+ }
607
+
608
+ private renderServiceGrid(): TemplateResult {
609
+ const filteredServices = this.getFilteredServices();
610
+
611
+ if (filteredServices.length === 0) {
612
+ return html`<div class="no-results">No services found matching your criteria</div>`;
613
+ }
614
+
615
+ return html`${filteredServices.map(service => html`
616
+ <div
617
+ class="asset-card ${service.selected ? 'selected' : ''}"
618
+ @click=${() => this.toggleService(service.id)}
619
+ >
620
+ <input
621
+ type="checkbox"
622
+ class="asset-checkbox"
623
+ .checked=${service.selected}
624
+ @click=${(e: Event) => e.stopPropagation()}
625
+ @change=${(e: Event) => {
626
+ e.stopPropagation();
627
+ this.toggleService(service.id);
628
+ }}
629
+ />
630
+
631
+ <div class="asset-info">
632
+ <div class="asset-name">${service.displayName}</div>
633
+ ${service.description ? html`
634
+ <div class="asset-description">${service.description}</div>
635
+ ` : ''}
636
+ </div>
637
+
638
+ <div class="asset-status">
639
+ <div class="status-indicator ${service.currentStatus}"></div>
640
+ <div class="status-text">${service.currentStatus.replace(/_/g, ' ')}</div>
641
+ </div>
642
+ </div>
643
+ `)}`;
644
+ }
63
645
  }