@littlebearapps/platform-admin-sdk 2.1.0 → 2.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 (115) hide show
  1. package/README.md +2 -5
  2. package/dist/templates.d.ts +1 -1
  3. package/dist/templates.js +121 -3
  4. package/package.json +1 -1
  5. package/templates/full/dashboard/src/components/notifications/NotificationDropdown.tsx +130 -0
  6. package/templates/full/dashboard/src/components/notifications/NotificationItem.tsx +264 -0
  7. package/templates/full/dashboard/src/components/patterns/PatternInfoButton.tsx +60 -0
  8. package/templates/full/dashboard/src/components/reports/FeatureUsageReport.tsx +339 -0
  9. package/templates/full/dashboard/src/components/search/SearchResultGroup.tsx +46 -0
  10. package/templates/full/dashboard/src/components/search/SearchResultItem.tsx +212 -0
  11. package/templates/full/dashboard/src/pages/api/patterns/[id]/approve.ts +49 -0
  12. package/templates/full/dashboard/src/pages/api/patterns/[id]/reject.ts +50 -0
  13. package/templates/full/dashboard/src/pages/api/reports/digests/stats.ts +38 -0
  14. package/templates/full/dashboard/src/pages/api/reports/digests.ts +39 -0
  15. package/templates/full/dashboard/src/pages/api/search/reindex/[type].ts +56 -0
  16. package/templates/full/dashboard/src/pages/api/test-reports/[id].ts +102 -0
  17. package/templates/full/dashboard/src/pages/feedback.astro +365 -0
  18. package/templates/full/dashboard/src/pages/kiosk.astro +206 -0
  19. package/templates/full/dashboard/src/pages/map.astro +561 -0
  20. package/templates/full/dashboard/src/pages/revenue.astro +72 -0
  21. package/templates/full/dashboard/src/pages/tests.astro +431 -0
  22. package/templates/full/scripts/ops/audit-cost-anomaly.ts +430 -0
  23. package/templates/full/scripts/ops/verify-account-total.ts +256 -0
  24. package/templates/full/tests/integration/feedback-schema.test.ts +361 -0
  25. package/templates/full/tests/integration/r2-archive.test.ts +108 -0
  26. package/templates/shared/.github/workflows/dependabot-automerge.yml +41 -0
  27. package/templates/shared/.github/workflows/validate-controls.yml +27 -0
  28. package/templates/shared/dashboard/src/components/Breadcrumbs.astro +101 -0
  29. package/templates/shared/dashboard/src/components/EmptyState.astro +46 -0
  30. package/templates/shared/dashboard/src/components/ErrorBoundary.astro +79 -0
  31. package/templates/shared/dashboard/src/components/LoadingSkeleton.astro +105 -0
  32. package/templates/shared/dashboard/src/components/PageShell.astro +72 -0
  33. package/templates/shared/dashboard/src/components/SkipLinks.astro +22 -0
  34. package/templates/shared/dashboard/src/components/Toast.astro +170 -0
  35. package/templates/shared/dashboard/src/components/ToastContainer.astro +156 -0
  36. package/templates/shared/dashboard/src/components/costs/ProviderCostsGrid.tsx +401 -0
  37. package/templates/shared/dashboard/src/components/costs/index.ts +4 -0
  38. package/templates/shared/dashboard/src/components/overview/AlertBanner.tsx +94 -0
  39. package/templates/shared/dashboard/src/components/overview/index.ts +9 -0
  40. package/templates/shared/dashboard/src/components/resources/CostChart.tsx +170 -0
  41. package/templates/shared/dashboard/src/components/resources/ProviderCard.tsx +272 -0
  42. package/templates/shared/dashboard/src/components/resources/ProviderDetail.tsx +293 -0
  43. package/templates/shared/dashboard/src/components/settings/SettingsCard.astro +102 -0
  44. package/templates/shared/dashboard/src/components/usage/AllowanceGauge.astro +170 -0
  45. package/templates/shared/dashboard/src/components/usage/AnomalyAlerts.astro +633 -0
  46. package/templates/shared/dashboard/src/components/usage/BillingCycleCountdown.astro +192 -0
  47. package/templates/shared/dashboard/src/components/usage/BurnRateHero.astro +539 -0
  48. package/templates/shared/dashboard/src/components/usage/CircuitBreakerEventLog.astro +542 -0
  49. package/templates/shared/dashboard/src/components/usage/CircuitBreakerPanel.tsx +292 -0
  50. package/templates/shared/dashboard/src/components/usage/CircuitBreakerStatus.astro +669 -0
  51. package/templates/shared/dashboard/src/components/usage/CompactThresholdBanner.astro +531 -0
  52. package/templates/shared/dashboard/src/components/usage/ComparisonModeSelector.astro +651 -0
  53. package/templates/shared/dashboard/src/components/usage/CostBreakdownChart.astro +381 -0
  54. package/templates/shared/dashboard/src/components/usage/CostBreakdownTable.astro +210 -0
  55. package/templates/shared/dashboard/src/components/usage/CostDataTable.astro +0 -0
  56. package/templates/shared/dashboard/src/components/usage/CostDonutChart.astro +311 -0
  57. package/templates/shared/dashboard/src/components/usage/DailyCostChart.astro +632 -0
  58. package/templates/shared/dashboard/src/components/usage/ExportButton.astro +114 -0
  59. package/templates/shared/dashboard/src/components/usage/FeatureBudgetsTable.astro +872 -0
  60. package/templates/shared/dashboard/src/components/usage/FilterBar.astro +190 -0
  61. package/templates/shared/dashboard/src/components/usage/FilterToggles.astro +175 -0
  62. package/templates/shared/dashboard/src/components/usage/GitHubUsageCard.astro +537 -0
  63. package/templates/shared/dashboard/src/components/usage/OverageCostCard.astro +212 -0
  64. package/templates/shared/dashboard/src/components/usage/PlanUtilizationCard.astro +193 -0
  65. package/templates/shared/dashboard/src/components/usage/ProjectCard.astro +640 -0
  66. package/templates/shared/dashboard/src/components/usage/ProjectCardsGrid.astro +272 -0
  67. package/templates/shared/dashboard/src/components/usage/ResourceSearch.astro +279 -0
  68. package/templates/shared/dashboard/src/components/usage/ServiceUtilizationList.astro +604 -0
  69. package/templates/shared/dashboard/src/components/usage/SparklineCard.astro +399 -0
  70. package/templates/shared/dashboard/src/components/usage/StatsHero.astro +600 -0
  71. package/templates/shared/dashboard/src/components/usage/TableFilters.astro +1033 -0
  72. package/templates/shared/dashboard/src/components/usage/ThresholdAlert.astro +271 -0
  73. package/templates/shared/dashboard/src/components/usage/ThresholdSettings.astro +618 -0
  74. package/templates/shared/dashboard/src/components/usage/TopSpenderCard.astro +170 -0
  75. package/templates/shared/dashboard/src/components/usage/UnifiedResourceTable.astro +1737 -0
  76. package/templates/shared/dashboard/src/components/usage/UsageCard.astro +135 -0
  77. package/templates/shared/dashboard/src/components/usage/UsageHealthBanner.astro +387 -0
  78. package/templates/shared/dashboard/src/components/usage/UtilizationBar.astro +159 -0
  79. package/templates/shared/dashboard/src/components/usage/WorkersBreakdownTable.astro +659 -0
  80. package/templates/shared/dashboard/src/components/usage/daily/CostChart.astro +461 -0
  81. package/templates/shared/dashboard/src/components/usage/daily/CostTable.astro +946 -0
  82. package/templates/shared/dashboard/src/components/usage/daily/DailyOverview.astro +1079 -0
  83. package/templates/shared/dashboard/src/components/usage/design-tokens.ts +187 -0
  84. package/templates/shared/dashboard/src/components/usage/filters/InlineDateRange.astro +285 -0
  85. package/templates/shared/dashboard/src/components/usage/filters/PeriodButtons.astro +157 -0
  86. package/templates/shared/dashboard/src/components/usage/filters/ProjectSelect.astro +284 -0
  87. package/templates/shared/dashboard/src/components/usage/scripts/ai-tab-controller.ts +419 -0
  88. package/templates/shared/dashboard/src/components/usage/scripts/constants.ts +60 -0
  89. package/templates/shared/dashboard/src/components/usage/scripts/formatters.ts +62 -0
  90. package/templates/shared/dashboard/src/components/usage/scripts/overview-controller.ts +1633 -0
  91. package/templates/shared/dashboard/src/components/usage/scripts/resource-table-builder.ts +294 -0
  92. package/templates/shared/dashboard/src/components/usage/scripts/tabs-filters-controller.ts +464 -0
  93. package/templates/shared/dashboard/src/components/usage/state/index.ts +55 -0
  94. package/templates/shared/dashboard/src/components/usage/state/usageActions.ts +439 -0
  95. package/templates/shared/dashboard/src/components/usage/state/usageStore.ts +376 -0
  96. package/templates/shared/dashboard/src/components/usage/types.ts +283 -0
  97. package/templates/shared/dashboard/src/components/usage/usage-colors.ts +292 -0
  98. package/templates/shared/dashboard/src/pages/api/usage/ai-models.ts +235 -0
  99. package/templates/shared/dashboard/src/pages/api/usage/billing-context.ts +296 -0
  100. package/templates/shared/scripts/test-telemetry-flow.ts +464 -0
  101. package/templates/shared/tests/e2e/usage-export.test.ts +784 -0
  102. package/templates/shared/tests/e2e/usage-mobile.test.ts +531 -0
  103. package/templates/standard/dashboard/src/components/errors/PriorityBadge.astro +27 -0
  104. package/templates/standard/dashboard/src/components/infrastructure/HealthchecksStatus.tsx +293 -0
  105. package/templates/standard/dashboard/src/components/infrastructure/InfrastructureTabs.tsx +268 -0
  106. package/templates/standard/dashboard/src/pages/analytics.astro +64 -0
  107. package/templates/standard/dashboard/src/pages/api/infrastructure/alerts.ts +85 -0
  108. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks/[id]/flips.ts +110 -0
  109. package/templates/standard/dashboard/src/pages/api/infrastructure/healthchecks.ts +101 -0
  110. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime/[id]/response-times.ts +121 -0
  111. package/templates/standard/dashboard/src/pages/api/infrastructure/uptime.ts +89 -0
  112. package/templates/standard/dashboard/src/pages/api/test/service-auth.ts +178 -0
  113. package/templates/standard/tests/integration/connectors.test.ts +241 -0
  114. package/templates/standard/tests/integration/github-monitor.test.ts +143 -0
  115. package/templates/standard/tests/integration/ingestion.test.ts +211 -0
@@ -0,0 +1,531 @@
1
+ ---
2
+ /**
3
+ * CompactThresholdBanner Component
4
+ *
5
+ * Compact threshold alert display with grouped horizontal bars and severity counts.
6
+ * Replaces text-heavy ThresholdAlert with visual summary.
7
+ */
8
+
9
+ interface ThresholdWarning {
10
+ resource: string;
11
+ resourceName: string;
12
+ metric: string;
13
+ current: number;
14
+ limit: number;
15
+ percentage: number;
16
+ level: 'normal' | 'warning' | 'high' | 'critical';
17
+ }
18
+
19
+ interface ThresholdAnalysis {
20
+ overallLevel: 'normal' | 'warning' | 'high' | 'critical';
21
+ warnings: ThresholdWarning[];
22
+ }
23
+
24
+ interface Props {
25
+ thresholds: ThresholdAnalysis;
26
+ collapsible?: boolean;
27
+ }
28
+
29
+ const { thresholds, collapsible = true } = Astro.props;
30
+
31
+ // Filter to only show warning, high, and critical levels
32
+ const alertWarnings = thresholds.warnings.filter(
33
+ (w) => w.level === 'warning' || w.level === 'high' || w.level === 'critical'
34
+ );
35
+
36
+ // Group by severity level
37
+ const criticalCount = alertWarnings.filter((w) => w.level === 'critical').length;
38
+ const highCount = alertWarnings.filter((w) => w.level === 'high').length;
39
+ const warningCount = alertWarnings.filter((w) => w.level === 'warning').length;
40
+
41
+ // If no alerts, don't render anything
42
+ if (alertWarnings.length === 0) {
43
+ return null;
44
+ }
45
+
46
+ // Determine banner style based on overall level
47
+ const bannerStyle =
48
+ thresholds.overallLevel === 'critical'
49
+ ? 'banner-critical'
50
+ : thresholds.overallLevel === 'high'
51
+ ? 'banner-high'
52
+ : 'banner-warning';
53
+
54
+ function formatNumber(num: number): string {
55
+ if (num >= 1_000_000_000) return `${(num / 1_000_000_000).toFixed(1)}B`;
56
+ if (num >= 1_000_000) return `${(num / 1_000_000).toFixed(1)}M`;
57
+ if (num >= 1_000) return `${(num / 1_000).toFixed(1)}K`;
58
+ return num.toLocaleString();
59
+ }
60
+
61
+ // Generate unique ID for this component instance
62
+ const componentId = `threshold-banner-${Math.random().toString(36).substr(2, 9)}`;
63
+ ---
64
+
65
+ <div class={`compact-threshold-banner ${bannerStyle}`} role="alert">
66
+ <div class="banner-summary">
67
+ <div class="severity-badges">
68
+ {
69
+ criticalCount > 0 && (
70
+ <span class="severity-badge badge-critical">
71
+ <span class="badge-count">{criticalCount}</span>
72
+ <span class="badge-label">Critical</span>
73
+ </span>
74
+ )
75
+ }
76
+ {
77
+ highCount > 0 && (
78
+ <span class="severity-badge badge-high">
79
+ <span class="badge-count">{highCount}</span>
80
+ <span class="badge-label">High</span>
81
+ </span>
82
+ )
83
+ }
84
+ {
85
+ warningCount > 0 && (
86
+ <span class="severity-badge badge-warning">
87
+ <span class="badge-count">{warningCount}</span>
88
+ <span class="badge-label">Warning</span>
89
+ </span>
90
+ )
91
+ }
92
+ </div>
93
+
94
+ <div class="threshold-bars">
95
+ {
96
+ alertWarnings.slice(0, 4).map((warning) => (
97
+ <div
98
+ class="mini-bar-container"
99
+ title={`${warning.resourceName}: ${warning.percentage.toFixed(0)}%`}
100
+ >
101
+ <div
102
+ class={`mini-bar bar-${warning.level}`}
103
+ style={`width: ${Math.min(warning.percentage, 100)}%`}
104
+ />
105
+ </div>
106
+ ))
107
+ }
108
+ {alertWarnings.length > 4 && <span class="more-count">+{alertWarnings.length - 4}</span>}
109
+ </div>
110
+
111
+ {
112
+ collapsible && (
113
+ <button
114
+ class="expand-button"
115
+ aria-expanded="false"
116
+ aria-controls={`${componentId}-details`}
117
+ data-toggle-id={componentId}
118
+ >
119
+ <span class="expand-icon">▼</span>
120
+ </button>
121
+ )
122
+ }
123
+ </div>
124
+
125
+ {
126
+ collapsible && (
127
+ <div
128
+ id={`${componentId}-details`}
129
+ class="banner-details"
130
+ data-details-id={componentId}
131
+ hidden
132
+ >
133
+ <div class="details-grid">
134
+ {alertWarnings.map((warning) => (
135
+ <div class={`detail-item detail-${warning.level}`}>
136
+ <div class="detail-header">
137
+ <span class={`detail-dot dot-${warning.level}`} />
138
+ <span class="detail-name">{warning.resourceName}</span>
139
+ <span class={`detail-level level-${warning.level}`}>
140
+ {warning.level.toUpperCase()}
141
+ </span>
142
+ </div>
143
+ <div class="detail-metric">{warning.metric}</div>
144
+ <div class="detail-progress">
145
+ <div class="detail-bar">
146
+ <div
147
+ class={`detail-fill fill-${warning.level}`}
148
+ style={`width: ${Math.min(warning.percentage, 100)}%`}
149
+ />
150
+ </div>
151
+ <span class="detail-values">
152
+ {formatNumber(warning.current)} / {formatNumber(warning.limit)}
153
+ </span>
154
+ <span class="detail-percent">{warning.percentage.toFixed(0)}%</span>
155
+ </div>
156
+ </div>
157
+ ))}
158
+ </div>
159
+ </div>
160
+ )
161
+ }
162
+ </div>
163
+
164
+ <style>
165
+ .compact-threshold-banner {
166
+ border-radius: 0.5rem;
167
+ border-width: 1px;
168
+ border-style: solid;
169
+ overflow: hidden;
170
+ margin-bottom: 1rem;
171
+ }
172
+
173
+ .banner-critical {
174
+ background-color: #fef2f2;
175
+ border-color: #fecaca;
176
+ }
177
+
178
+ .banner-high {
179
+ background-color: #fff7ed;
180
+ border-color: #fed7aa;
181
+ }
182
+
183
+ .banner-warning {
184
+ background-color: #fefce8;
185
+ border-color: #fef08a;
186
+ }
187
+
188
+ :global(.dark) .banner-critical {
189
+ background-color: rgba(239, 68, 68, 0.1);
190
+ border-color: rgba(239, 68, 68, 0.3);
191
+ }
192
+
193
+ :global(.dark) .banner-high {
194
+ background-color: rgba(249, 115, 22, 0.1);
195
+ border-color: rgba(249, 115, 22, 0.3);
196
+ }
197
+
198
+ :global(.dark) .banner-warning {
199
+ background-color: rgba(245, 158, 11, 0.1);
200
+ border-color: rgba(245, 158, 11, 0.3);
201
+ }
202
+
203
+ .banner-summary {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 1rem;
207
+ padding: 0.75rem 1rem;
208
+ }
209
+
210
+ .severity-badges {
211
+ display: flex;
212
+ gap: 0.5rem;
213
+ flex-shrink: 0;
214
+ }
215
+
216
+ .severity-badge {
217
+ display: inline-flex;
218
+ align-items: center;
219
+ gap: 0.25rem;
220
+ padding: 0.25rem 0.5rem;
221
+ border-radius: 9999px;
222
+ font-size: 0.75rem;
223
+ font-weight: 600;
224
+ }
225
+
226
+ .badge-critical {
227
+ background-color: #dc2626;
228
+ color: white;
229
+ }
230
+
231
+ .badge-high {
232
+ background-color: #ea580c;
233
+ color: white;
234
+ }
235
+
236
+ .badge-warning {
237
+ background-color: #ca8a04;
238
+ color: white;
239
+ }
240
+
241
+ .badge-count {
242
+ font-weight: 700;
243
+ }
244
+
245
+ .badge-label {
246
+ font-weight: 500;
247
+ }
248
+
249
+ .threshold-bars {
250
+ flex: 1;
251
+ display: flex;
252
+ align-items: center;
253
+ gap: 0.375rem;
254
+ min-width: 0;
255
+ }
256
+
257
+ .mini-bar-container {
258
+ flex: 1;
259
+ max-width: 80px;
260
+ height: 6px;
261
+ background-color: rgba(0, 0, 0, 0.1);
262
+ border-radius: 3px;
263
+ overflow: hidden;
264
+ cursor: help;
265
+ }
266
+
267
+ :global(.dark) .mini-bar-container {
268
+ background-color: rgba(255, 255, 255, 0.1);
269
+ }
270
+
271
+ .mini-bar {
272
+ height: 100%;
273
+ border-radius: 3px;
274
+ transition: width 0.3s ease;
275
+ }
276
+
277
+ .bar-critical {
278
+ background-color: #dc2626;
279
+ }
280
+
281
+ .bar-high {
282
+ background-color: #ea580c;
283
+ }
284
+
285
+ .bar-warning {
286
+ background-color: #ca8a04;
287
+ }
288
+
289
+ .more-count {
290
+ font-size: 0.625rem;
291
+ color: #6b7280;
292
+ flex-shrink: 0;
293
+ }
294
+
295
+ :global(.dark) .more-count {
296
+ color: #9ca3af;
297
+ }
298
+
299
+ .expand-button {
300
+ background: none;
301
+ border: none;
302
+ padding: 0.25rem;
303
+ cursor: pointer;
304
+ color: #6b7280;
305
+ transition: transform 0.2s ease;
306
+ flex-shrink: 0;
307
+ }
308
+
309
+ :global(.dark) .expand-button {
310
+ color: #9ca3af;
311
+ }
312
+
313
+ .expand-button:hover {
314
+ color: #374151;
315
+ }
316
+
317
+ :global(.dark) .expand-button:hover {
318
+ color: #d1d5db;
319
+ }
320
+
321
+ .expand-button[aria-expanded='true'] .expand-icon {
322
+ transform: rotate(180deg);
323
+ }
324
+
325
+ .expand-icon {
326
+ display: inline-block;
327
+ font-size: 0.625rem;
328
+ transition: transform 0.2s ease;
329
+ }
330
+
331
+ .banner-details {
332
+ padding: 0.75rem 1rem;
333
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
334
+ background-color: rgba(0, 0, 0, 0.02);
335
+ }
336
+
337
+ :global(.dark) .banner-details {
338
+ border-top-color: rgba(255, 255, 255, 0.1);
339
+ background-color: rgba(0, 0, 0, 0.2);
340
+ }
341
+
342
+ .details-grid {
343
+ display: grid;
344
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
345
+ gap: 0.75rem;
346
+ }
347
+
348
+ .detail-item {
349
+ padding: 0.5rem;
350
+ border-radius: 0.375rem;
351
+ background-color: white;
352
+ border: 1px solid #e5e7eb;
353
+ }
354
+
355
+ :global(.dark) .detail-item {
356
+ background-color: #1f2937;
357
+ border-color: #374151;
358
+ }
359
+
360
+ .detail-header {
361
+ display: flex;
362
+ align-items: center;
363
+ gap: 0.375rem;
364
+ margin-bottom: 0.25rem;
365
+ }
366
+
367
+ .detail-dot {
368
+ width: 8px;
369
+ height: 8px;
370
+ border-radius: 50%;
371
+ flex-shrink: 0;
372
+ }
373
+
374
+ .dot-critical {
375
+ background-color: #dc2626;
376
+ }
377
+
378
+ .dot-high {
379
+ background-color: #ea580c;
380
+ }
381
+
382
+ .dot-warning {
383
+ background-color: #ca8a04;
384
+ }
385
+
386
+ .detail-name {
387
+ flex: 1;
388
+ font-size: 0.75rem;
389
+ font-weight: 600;
390
+ color: #374151;
391
+ white-space: nowrap;
392
+ overflow: hidden;
393
+ text-overflow: ellipsis;
394
+ }
395
+
396
+ :global(.dark) .detail-name {
397
+ color: #d1d5db;
398
+ }
399
+
400
+ .detail-level {
401
+ font-size: 0.5rem;
402
+ font-weight: 700;
403
+ padding: 0.125rem 0.375rem;
404
+ border-radius: 9999px;
405
+ letter-spacing: 0.05em;
406
+ }
407
+
408
+ .level-critical {
409
+ background-color: #dc2626;
410
+ color: white;
411
+ }
412
+
413
+ .level-high {
414
+ background-color: #ea580c;
415
+ color: white;
416
+ }
417
+
418
+ .level-warning {
419
+ background-color: #ca8a04;
420
+ color: white;
421
+ }
422
+
423
+ .detail-metric {
424
+ font-size: 0.625rem;
425
+ color: #6b7280;
426
+ margin-bottom: 0.375rem;
427
+ }
428
+
429
+ :global(.dark) .detail-metric {
430
+ color: #9ca3af;
431
+ }
432
+
433
+ .detail-progress {
434
+ display: flex;
435
+ align-items: center;
436
+ gap: 0.5rem;
437
+ }
438
+
439
+ .detail-bar {
440
+ flex: 1;
441
+ height: 4px;
442
+ background-color: #e5e7eb;
443
+ border-radius: 2px;
444
+ overflow: hidden;
445
+ }
446
+
447
+ :global(.dark) .detail-bar {
448
+ background-color: #374151;
449
+ }
450
+
451
+ .detail-fill {
452
+ height: 100%;
453
+ border-radius: 2px;
454
+ transition: width 0.3s ease;
455
+ }
456
+
457
+ .fill-critical {
458
+ background-color: #dc2626;
459
+ }
460
+
461
+ .fill-high {
462
+ background-color: #ea580c;
463
+ }
464
+
465
+ .fill-warning {
466
+ background-color: #ca8a04;
467
+ }
468
+
469
+ .detail-values {
470
+ font-size: 0.625rem;
471
+ color: #6b7280;
472
+ font-family: monospace;
473
+ }
474
+
475
+ :global(.dark) .detail-values {
476
+ color: #9ca3af;
477
+ }
478
+
479
+ .detail-percent {
480
+ font-size: 0.625rem;
481
+ font-weight: 600;
482
+ color: #374151;
483
+ min-width: 2.5rem;
484
+ text-align: right;
485
+ }
486
+
487
+ :global(.dark) .detail-percent {
488
+ color: #d1d5db;
489
+ }
490
+
491
+ @media (max-width: 640px) {
492
+ .banner-summary {
493
+ flex-wrap: wrap;
494
+ }
495
+
496
+ .threshold-bars {
497
+ order: 3;
498
+ width: 100%;
499
+ flex: none;
500
+ margin-top: 0.5rem;
501
+ }
502
+
503
+ .mini-bar-container {
504
+ max-width: none;
505
+ }
506
+
507
+ .details-grid {
508
+ grid-template-columns: 1fr;
509
+ }
510
+ }
511
+ </style>
512
+
513
+ <script>
514
+ // Handle expand/collapse functionality
515
+ document.addEventListener('DOMContentLoaded', () => {
516
+ const expandButtons = document.querySelectorAll('.expand-button[data-toggle-id]');
517
+
518
+ expandButtons.forEach((button) => {
519
+ button.addEventListener('click', () => {
520
+ const toggleId = button.getAttribute('data-toggle-id');
521
+ const details = document.querySelector(`[data-details-id="${toggleId}"]`);
522
+
523
+ if (details) {
524
+ const isExpanded = button.getAttribute('aria-expanded') === 'true';
525
+ button.setAttribute('aria-expanded', String(!isExpanded));
526
+ details.hidden = isExpanded;
527
+ }
528
+ });
529
+ });
530
+ });
531
+ </script>