@echothink-ui/admin 0.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 (48) hide show
  1. package/README.md +5 -0
  2. package/dist/components/AdminHealthTemplate.d.ts +5 -0
  3. package/dist/components/AdminShell.d.ts +8 -0
  4. package/dist/components/AuditLogViewer.d.ts +9 -0
  5. package/dist/components/BillingUsagePanel.d.ts +7 -0
  6. package/dist/components/FeatureFlagPanel.d.ts +8 -0
  7. package/dist/components/IntegrationHealthTable.d.ts +6 -0
  8. package/dist/components/JobQueuePanel.d.ts +7 -0
  9. package/dist/components/PolicyConfigPanel.d.ts +9 -0
  10. package/dist/components/QueueDepthChart.d.ts +6 -0
  11. package/dist/components/RateLimitPanel.d.ts +7 -0
  12. package/dist/components/RuntimeLogViewer.d.ts +10 -0
  13. package/dist/components/ServiceStatusCard.d.ts +7 -0
  14. package/dist/components/SystemHealthDashboard.d.ts +8 -0
  15. package/dist/components/TenantSettingsPanel.d.ts +11 -0
  16. package/dist/components/WorkerPoolPanel.d.ts +7 -0
  17. package/dist/components/types.d.ts +114 -0
  18. package/dist/index.cjs +1835 -0
  19. package/dist/index.cjs.map +1 -0
  20. package/dist/index.css +1711 -0
  21. package/dist/index.css.map +1 -0
  22. package/dist/index.d.ts +19 -0
  23. package/dist/index.js +1812 -0
  24. package/dist/index.js.map +1 -0
  25. package/package.json +46 -0
  26. package/src/components/AdminHealthTemplate.tsx +25 -0
  27. package/src/components/AdminShell.tsx +48 -0
  28. package/src/components/AuditLogViewer.tsx +96 -0
  29. package/src/components/BillingUsagePanel.tsx +171 -0
  30. package/src/components/FeatureFlagPanel.tsx +178 -0
  31. package/src/components/IntegrationHealthTable.tsx +196 -0
  32. package/src/components/JobQueuePanel.tsx +171 -0
  33. package/src/components/PolicyConfigPanel.test.tsx +45 -0
  34. package/src/components/PolicyConfigPanel.tsx +131 -0
  35. package/src/components/QueueDepthChart.tsx +70 -0
  36. package/src/components/RateLimitPanel.test.tsx +29 -0
  37. package/src/components/RateLimitPanel.tsx +249 -0
  38. package/src/components/RuntimeLogViewer.test.tsx +60 -0
  39. package/src/components/RuntimeLogViewer.tsx +185 -0
  40. package/src/components/ServiceStatusCard.tsx +91 -0
  41. package/src/components/SystemHealthDashboard.tsx +214 -0
  42. package/src/components/TenantSettingsPanel.test.tsx +38 -0
  43. package/src/components/TenantSettingsPanel.tsx +44 -0
  44. package/src/components/WorkerPoolPanel.test.tsx +32 -0
  45. package/src/components/WorkerPoolPanel.tsx +281 -0
  46. package/src/components/types.ts +131 -0
  47. package/src/index.tsx +37 -0
  48. package/src/styles.css +2024 -0
package/dist/index.js ADDED
@@ -0,0 +1,1812 @@
1
+ // src/components/AdminShell.tsx
2
+ import { TemplateShell } from "@echothink-ui/layouts";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ function AdminShell({
5
+ sideNav,
6
+ header,
7
+ children,
8
+ className,
9
+ title: _title,
10
+ subtitle: _subtitle,
11
+ description: _description,
12
+ eyebrow: _eyebrow,
13
+ density: _density,
14
+ status: _status,
15
+ severity: _severity,
16
+ loading: _loading,
17
+ empty: _empty,
18
+ error: _error,
19
+ items: _items,
20
+ actions: _actions,
21
+ metadata: _metadata,
22
+ footer: _footer,
23
+ ...props
24
+ }) {
25
+ return /* @__PURE__ */ jsx(
26
+ TemplateShell,
27
+ {
28
+ ...props,
29
+ className: ["eth-admin-shell", className].filter(Boolean).join(" "),
30
+ "data-eth-component": "AdminShell",
31
+ canvas: /* @__PURE__ */ jsxs("div", { className: "eth-admin-shell__layout", children: [
32
+ /* @__PURE__ */ jsx("aside", { className: "eth-admin-shell__side-nav", children: sideNav }),
33
+ /* @__PURE__ */ jsxs("div", { className: "eth-admin-shell__workspace", children: [
34
+ header ? /* @__PURE__ */ jsx("header", { className: "eth-admin-shell__header", children: header }) : null,
35
+ /* @__PURE__ */ jsx("main", { className: "eth-admin-shell__main", children })
36
+ ] })
37
+ ] })
38
+ }
39
+ );
40
+ }
41
+
42
+ // src/components/SystemHealthDashboard.tsx
43
+ import {
44
+ Badge as Badge2,
45
+ Surface as Surface2
46
+ } from "@echothink-ui/core";
47
+ import { KPIBlock } from "@echothink-ui/charts";
48
+
49
+ // src/components/ServiceStatusCard.tsx
50
+ import { Badge, Surface } from "@echothink-ui/core";
51
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
52
+ var percentFormatter = new Intl.NumberFormat("en", { maximumFractionDigits: 2 });
53
+ function ServiceStatusCard({ service, className, ...props }) {
54
+ const statusSeverity2 = severityForStatus(service.status);
55
+ const statusText = statusLabel(service.status);
56
+ const metrics = [
57
+ {
58
+ label: "Latency",
59
+ value: service.latencyMs !== void 0 ? `${service.latencyMs} ms` : "Unavailable",
60
+ muted: service.latencyMs === void 0
61
+ },
62
+ {
63
+ label: "Uptime",
64
+ value: service.uptimePercent !== void 0 ? formatPercent(service.uptimePercent) : "Not reported",
65
+ muted: service.uptimePercent === void 0
66
+ },
67
+ {
68
+ label: service.incidentsCount !== void 0 ? "Open incidents" : "Last incident",
69
+ value: service.incidentsCount !== void 0 ? service.incidentsCount : service.lastIncidentAt ?? "None reported",
70
+ muted: service.incidentsCount !== void 0 ? service.incidentsCount === 0 : !service.lastIncidentAt
71
+ }
72
+ ];
73
+ return /* @__PURE__ */ jsx2(
74
+ Surface,
75
+ {
76
+ ...props,
77
+ title: /* @__PURE__ */ jsxs2("span", { className: "eth-admin-service-status-card__title", children: [
78
+ /* @__PURE__ */ jsx2("span", { children: service.name }),
79
+ /* @__PURE__ */ jsx2(Badge, { "aria-label": `Service status: ${statusText}`, severity: statusSeverity2, children: statusText })
80
+ ] }),
81
+ description: statusDescription(service.status),
82
+ className: [
83
+ "eth-admin-service-status-card",
84
+ `eth-admin-service-status-card--${service.status}`,
85
+ className
86
+ ].filter(Boolean).join(" "),
87
+ "data-eth-component": "ServiceStatusCard",
88
+ children: /* @__PURE__ */ jsx2("dl", { className: "eth-admin-service-status-card__metrics", children: metrics.map((metric) => /* @__PURE__ */ jsxs2(
89
+ "div",
90
+ {
91
+ className: metric.muted ? "eth-admin-service-status-card__metric--muted" : void 0,
92
+ children: [
93
+ /* @__PURE__ */ jsx2("dt", { children: metric.label }),
94
+ /* @__PURE__ */ jsx2("dd", { children: metric.value })
95
+ ]
96
+ },
97
+ metric.label
98
+ )) })
99
+ }
100
+ );
101
+ }
102
+ function severityForStatus(status) {
103
+ if (status === "healthy") return "success";
104
+ if (status === "degraded") return "warning";
105
+ return "danger";
106
+ }
107
+ function statusLabel(status) {
108
+ if (status === "healthy") return "Healthy";
109
+ if (status === "degraded") return "Degraded";
110
+ return "Down";
111
+ }
112
+ function statusDescription(status) {
113
+ if (status === "healthy") return "Operational within expected thresholds";
114
+ if (status === "degraded") return "Elevated latency or partial failures detected";
115
+ return "Service unavailable or actively failing";
116
+ }
117
+ function formatPercent(value) {
118
+ return `${percentFormatter.format(value)}%`;
119
+ }
120
+
121
+ // src/components/SystemHealthDashboard.tsx
122
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
123
+ function SystemHealthDashboard({
124
+ services,
125
+ incidents = [],
126
+ metrics = [],
127
+ title,
128
+ description,
129
+ severity,
130
+ status,
131
+ className,
132
+ ...props
133
+ }) {
134
+ const serviceCount = services.length;
135
+ const healthyCount = services.filter((service) => service.status === "healthy").length;
136
+ const degradedCount = services.filter((service) => service.status === "degraded").length;
137
+ const downCount = services.filter((service) => service.status === "down").length;
138
+ const criticalIncidentCount = incidents.filter(
139
+ (incident) => incident.severity === "critical" || incident.severity === "error"
140
+ ).length;
141
+ const openIncidentCount = incidents.length;
142
+ const defaultSeverity = severityForHealth(
143
+ downCount,
144
+ degradedCount,
145
+ criticalIncidentCount,
146
+ openIncidentCount
147
+ );
148
+ const defaultStatus = statusForHealth(serviceCount, downCount, degradedCount);
149
+ const computedMetrics = metrics.length ? metrics : [
150
+ {
151
+ label: "Healthy services",
152
+ value: serviceCount ? `${healthyCount} / ${serviceCount}` : "0",
153
+ trend: serviceHealthTrend(healthyCount, degradedCount, downCount)
154
+ },
155
+ {
156
+ label: "Open incidents",
157
+ value: openIncidentCount,
158
+ trend: incidentTrend(criticalIncidentCount, openIncidentCount)
159
+ },
160
+ {
161
+ label: "Avg latency",
162
+ value: average(
163
+ services.map((service) => service.latencyMs).filter((value) => value !== void 0)
164
+ ),
165
+ trend: serviceCount ? `${serviceCount} monitored` : void 0
166
+ }
167
+ ];
168
+ return /* @__PURE__ */ jsxs3(
169
+ Surface2,
170
+ {
171
+ ...props,
172
+ title: title ?? "System health",
173
+ description: description ?? healthSummary(serviceCount, healthyCount, degradedCount, downCount, openIncidentCount),
174
+ severity: severity ?? defaultSeverity,
175
+ status: status ?? defaultStatus,
176
+ className: ["eth-admin-system-health", className].filter(Boolean).join(" "),
177
+ "data-eth-component": "SystemHealthDashboard",
178
+ children: [
179
+ /* @__PURE__ */ jsx3("div", { className: "eth-admin-system-health__metrics", children: computedMetrics.map((metric) => /* @__PURE__ */ jsx3(
180
+ KPIBlock,
181
+ {
182
+ title: metric.label,
183
+ value: metric.value,
184
+ status: metric.trend
185
+ },
186
+ String(metric.label)
187
+ )) }),
188
+ /* @__PURE__ */ jsx3("div", { className: "eth-admin-system-health__services", children: services.length ? services.map((service) => {
189
+ const serviceIncidents = incidents.filter(
190
+ (incident) => incident.title.toLowerCase().includes(service.name.toLowerCase())
191
+ );
192
+ return /* @__PURE__ */ jsx3(
193
+ ServiceStatusCard,
194
+ {
195
+ service: {
196
+ name: service.name,
197
+ status: service.status,
198
+ latencyMs: service.latencyMs,
199
+ uptimePercent: service.status === "healthy" ? 99.9 : void 0,
200
+ incidentsCount: service.incidentsCount ?? serviceIncidents.length,
201
+ lastIncidentAt: serviceIncidents[0]?.startedAt
202
+ }
203
+ },
204
+ service.id
205
+ );
206
+ }) : /* @__PURE__ */ jsx3("p", { className: "eth-admin-system-health__empty", children: "No services are currently registered." }) }),
207
+ incidents.length ? /* @__PURE__ */ jsxs3("section", { className: "eth-admin-system-health__incidents", children: [
208
+ /* @__PURE__ */ jsx3("h3", { children: "Incidents" }),
209
+ /* @__PURE__ */ jsx3("ul", { children: incidents.map((incident) => /* @__PURE__ */ jsxs3("li", { children: [
210
+ /* @__PURE__ */ jsx3(Badge2, { severity: severityForIncident(incident.severity), children: incident.severity }),
211
+ /* @__PURE__ */ jsx3("span", { children: incident.title }),
212
+ /* @__PURE__ */ jsx3("time", { children: incident.startedAt })
213
+ ] }, incident.id)) })
214
+ ] }) : null
215
+ ]
216
+ }
217
+ );
218
+ }
219
+ function average(values) {
220
+ if (!values.length) return "0 ms";
221
+ return `${Math.round(values.reduce((sum, value) => sum + value, 0) / values.length)} ms`;
222
+ }
223
+ function healthSummary(serviceCount, healthyCount, degradedCount, downCount, openIncidentCount) {
224
+ if (!serviceCount) return "No services registered for this tenant.";
225
+ const exceptions = [];
226
+ if (degradedCount) exceptions.push(`${degradedCount} degraded`);
227
+ if (downCount) exceptions.push(`${downCount} down`);
228
+ if (openIncidentCount) {
229
+ exceptions.push(`${openIncidentCount} open ${pluralize(openIncidentCount, "incident")}`);
230
+ }
231
+ const serviceSummary = `${healthyCount} of ${serviceCount} ${pluralize(serviceCount, "service")} healthy`;
232
+ return exceptions.length ? `${serviceSummary}; ${exceptions.join(", ")}.` : `${serviceSummary}; no open incidents.`;
233
+ }
234
+ function serviceHealthTrend(healthyCount, degradedCount, downCount) {
235
+ if (downCount) {
236
+ return /* @__PURE__ */ jsx3(Badge2, { severity: "danger", children: `${downCount} down` });
237
+ }
238
+ if (degradedCount) {
239
+ return /* @__PURE__ */ jsx3(Badge2, { severity: "warning", children: `${degradedCount} degraded` });
240
+ }
241
+ if (healthyCount) {
242
+ return /* @__PURE__ */ jsx3(Badge2, { severity: "success", children: "Nominal" });
243
+ }
244
+ return void 0;
245
+ }
246
+ function incidentTrend(criticalIncidentCount, openIncidentCount) {
247
+ if (criticalIncidentCount) {
248
+ return /* @__PURE__ */ jsx3(Badge2, { severity: "danger", children: `${criticalIncidentCount} critical` });
249
+ }
250
+ if (openIncidentCount) {
251
+ return /* @__PURE__ */ jsx3(Badge2, { severity: "warning", children: "Investigating" });
252
+ }
253
+ return /* @__PURE__ */ jsx3(Badge2, { severity: "success", children: "Clear" });
254
+ }
255
+ function severityForHealth(downCount, degradedCount, criticalIncidentCount, openIncidentCount) {
256
+ if (downCount || criticalIncidentCount) return "danger";
257
+ if (degradedCount || openIncidentCount) return "warning";
258
+ return void 0;
259
+ }
260
+ function statusForHealth(serviceCount, downCount, degradedCount) {
261
+ if (!serviceCount) return "inactive";
262
+ if (downCount) return "failed";
263
+ if (degradedCount) return "warning";
264
+ return "active";
265
+ }
266
+ function pluralize(count, singular) {
267
+ return count === 1 ? singular : `${singular}s`;
268
+ }
269
+ function severityForIncident(severity) {
270
+ if (severity === "critical" || severity === "error") return "danger";
271
+ if (severity === "warning") return "warning";
272
+ return "info";
273
+ }
274
+
275
+ // src/components/WorkerPoolPanel.tsx
276
+ import * as React from "react";
277
+ import {
278
+ ActionGroup,
279
+ Badge as Badge3,
280
+ Button
281
+ } from "@echothink-ui/core";
282
+ import { DataTable } from "@echothink-ui/data";
283
+ import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
284
+ function WorkerPoolPanel({
285
+ pools,
286
+ onScale,
287
+ title = "Worker pools",
288
+ subtitle,
289
+ description,
290
+ actions,
291
+ density = "compact",
292
+ className,
293
+ "aria-label": ariaLabel,
294
+ "aria-labelledby": ariaLabelledBy,
295
+ eyebrow: _eyebrow,
296
+ status: _status,
297
+ severity: _severity,
298
+ loading: _loading,
299
+ empty: _empty,
300
+ error: _error,
301
+ items: _items,
302
+ metadata: _metadata,
303
+ footer: _footer,
304
+ ...sectionProps
305
+ }) {
306
+ const headingId = React.useId();
307
+ const rows = pools.map((pool) => {
308
+ const percent = pool.total > 0 ? pool.active / pool.total * 100 : 0;
309
+ const cappedPercent = Math.min(100, Math.max(0, percent));
310
+ return {
311
+ ...pool,
312
+ cappedPercent,
313
+ capacityLabel: pool.total > 0 ? `${formatNumber(pool.active)} / ${formatNumber(pool.total)} active` : `${formatNumber(pool.active)} active`,
314
+ percent,
315
+ percentLabel: pool.total > 0 ? formatPercent2(percent) : "No capacity",
316
+ state: workerPoolState(pool, percent)
317
+ };
318
+ });
319
+ const totals = pools.reduce(
320
+ (summary, pool) => ({
321
+ active: summary.active + pool.active,
322
+ failed: summary.failed + pool.failed,
323
+ idle: summary.idle + pool.idle,
324
+ total: summary.total + pool.total
325
+ }),
326
+ { active: 0, failed: 0, idle: 0, total: 0 }
327
+ );
328
+ const utilization = totals.total > 0 ? totals.active / totals.total * 100 : 0;
329
+ const statusSummary = workerPoolStatusSummary(pools.length, totals.failed, utilization);
330
+ const supportingText = subtitle ?? description;
331
+ const columns = [
332
+ {
333
+ key: "name",
334
+ header: "Pool",
335
+ width: "26%",
336
+ render: (row) => /* @__PURE__ */ jsxs4("div", { className: "eth-admin-worker-pool__pool", children: [
337
+ /* @__PURE__ */ jsx4("strong", { children: row.name }),
338
+ /* @__PURE__ */ jsx4("span", { children: row.state.label })
339
+ ] })
340
+ },
341
+ {
342
+ key: "active",
343
+ header: "Capacity",
344
+ width: "34%",
345
+ render: (row) => /* @__PURE__ */ jsxs4("div", { className: "eth-admin-worker-pool__capacity", children: [
346
+ /* @__PURE__ */ jsxs4("div", { className: "eth-admin-worker-pool__capacity-copy", children: [
347
+ /* @__PURE__ */ jsx4("span", { children: row.capacityLabel }),
348
+ /* @__PURE__ */ jsx4("strong", { children: row.percentLabel })
349
+ ] }),
350
+ /* @__PURE__ */ jsx4(
351
+ "span",
352
+ {
353
+ className: "eth-admin-worker-pool__meter",
354
+ role: "progressbar",
355
+ "aria-label": `${row.name} capacity`,
356
+ "aria-valuemin": 0,
357
+ "aria-valuemax": 100,
358
+ "aria-valuenow": Math.round(row.cappedPercent),
359
+ "aria-valuetext": `${formatNumber(row.active)} of ${formatNumber(
360
+ row.total
361
+ )} workers active`,
362
+ style: {
363
+ "--eth-admin-worker-pool-capacity": `${row.cappedPercent}%`
364
+ },
365
+ children: /* @__PURE__ */ jsx4(
366
+ "span",
367
+ {
368
+ className: [
369
+ "eth-admin-worker-pool__meter-fill",
370
+ `eth-admin-worker-pool__meter-fill--${row.state.tone}`
371
+ ].join(" ")
372
+ }
373
+ )
374
+ }
375
+ )
376
+ ] })
377
+ },
378
+ {
379
+ key: "idle",
380
+ header: "Idle",
381
+ align: "end",
382
+ width: "12%",
383
+ render: (row) => /* @__PURE__ */ jsx4("span", { className: "eth-admin-worker-pool__number", children: formatNumber(row.idle) })
384
+ },
385
+ {
386
+ key: "failed",
387
+ header: "Failed",
388
+ align: "end",
389
+ width: "12%",
390
+ render: (row) => /* @__PURE__ */ jsx4(Badge3, { severity: row.failed > 0 ? "danger" : "success", children: formatNumber(row.failed) })
391
+ },
392
+ {
393
+ key: "scale",
394
+ header: "Scale",
395
+ align: "end",
396
+ width: "10rem",
397
+ render: (row) => /* @__PURE__ */ jsxs4(
398
+ "div",
399
+ {
400
+ className: "eth-admin-worker-pool__scale",
401
+ role: "group",
402
+ "aria-label": `${row.name} scale controls`,
403
+ children: [
404
+ /* @__PURE__ */ jsx4(
405
+ Button,
406
+ {
407
+ type: "button",
408
+ intent: "ghost",
409
+ density: "compact",
410
+ className: "eth-admin-worker-pool__scale-button",
411
+ disabled: !onScale,
412
+ "aria-label": `Scale ${row.name} down by 1`,
413
+ onClick: () => onScale?.(row.id, -1),
414
+ children: "-1"
415
+ }
416
+ ),
417
+ /* @__PURE__ */ jsx4(
418
+ Button,
419
+ {
420
+ type: "button",
421
+ intent: "ghost",
422
+ density: "compact",
423
+ className: "eth-admin-worker-pool__scale-button",
424
+ disabled: !onScale,
425
+ "aria-label": `Scale ${row.name} up by 1`,
426
+ onClick: () => onScale?.(row.id, 1),
427
+ children: "+1"
428
+ }
429
+ )
430
+ ]
431
+ }
432
+ )
433
+ }
434
+ ];
435
+ return /* @__PURE__ */ jsxs4(
436
+ "section",
437
+ {
438
+ ...sectionProps,
439
+ "aria-label": ariaLabel,
440
+ "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : headingId),
441
+ className: ["eth-admin-worker-pool", className].filter(Boolean).join(" "),
442
+ "data-eth-component": "WorkerPoolPanel",
443
+ children: [
444
+ /* @__PURE__ */ jsxs4("header", { children: [
445
+ /* @__PURE__ */ jsxs4("div", { className: "eth-admin-worker-pool__heading", children: [
446
+ /* @__PURE__ */ jsx4("h3", { id: headingId, children: title }),
447
+ supportingText ? /* @__PURE__ */ jsx4("p", { children: supportingText }) : null
448
+ ] }),
449
+ /* @__PURE__ */ jsxs4("div", { className: "eth-admin-worker-pool__header-actions", children: [
450
+ /* @__PURE__ */ jsx4(Badge3, { severity: statusSummary.severity, children: statusSummary.label }),
451
+ /* @__PURE__ */ jsx4(ActionGroup, { actions })
452
+ ] })
453
+ ] }),
454
+ /* @__PURE__ */ jsxs4("dl", { className: "eth-admin-worker-pool__summary", "aria-label": "Worker pool totals", children: [
455
+ /* @__PURE__ */ jsxs4("div", { children: [
456
+ /* @__PURE__ */ jsx4("dt", { children: "Tracked pools" }),
457
+ /* @__PURE__ */ jsx4("dd", { children: formatPoolCount(pools.length) })
458
+ ] }),
459
+ /* @__PURE__ */ jsxs4("div", { children: [
460
+ /* @__PURE__ */ jsx4("dt", { children: "Active capacity" }),
461
+ /* @__PURE__ */ jsxs4("dd", { children: [
462
+ formatNumber(totals.active),
463
+ " / ",
464
+ formatNumber(totals.total)
465
+ ] })
466
+ ] }),
467
+ /* @__PURE__ */ jsxs4("div", { children: [
468
+ /* @__PURE__ */ jsx4("dt", { children: "Failures" }),
469
+ /* @__PURE__ */ jsx4("dd", { children: formatFailureCount(totals.failed) })
470
+ ] })
471
+ ] }),
472
+ /* @__PURE__ */ jsx4(
473
+ DataTable,
474
+ {
475
+ rows,
476
+ columns,
477
+ rowKey: "id",
478
+ density,
479
+ className: "eth-admin-worker-pool__table",
480
+ emptyState: /* @__PURE__ */ jsx4("p", { className: "eth-admin-worker-pool__empty", children: "No worker pools are configured." })
481
+ }
482
+ )
483
+ ]
484
+ }
485
+ );
486
+ }
487
+ function workerPoolState(pool, percent) {
488
+ if (pool.failed > 0) return { label: "Failure requires review", tone: "danger" };
489
+ if (pool.total <= 0) return { label: "No capacity", tone: "neutral" };
490
+ if (percent >= 100) return { label: "At capacity", tone: "warning" };
491
+ if (pool.idle > 0) return { label: "Capacity available", tone: "normal" };
492
+ return { label: "Active", tone: "normal" };
493
+ }
494
+ function workerPoolStatusSummary(poolCount, failedCount, utilization) {
495
+ if (poolCount === 0) return { label: "No pools", severity: "neutral" };
496
+ if (failedCount === 1) return { label: "Failure in pool", severity: "danger" };
497
+ if (failedCount > 1) {
498
+ return { label: `${formatNumber(failedCount)} failures`, severity: "danger" };
499
+ }
500
+ if (utilization >= 90) return { label: "Capacity tight", severity: "warning" };
501
+ return { label: "Operational", severity: "success" };
502
+ }
503
+ function formatNumber(value) {
504
+ return new Intl.NumberFormat("en-US").format(value);
505
+ }
506
+ function formatPercent2(value) {
507
+ return `${value.toLocaleString("en-US", { maximumFractionDigits: 0 })}%`;
508
+ }
509
+ function formatPoolCount(value) {
510
+ return `${formatNumber(value)} ${value === 1 ? "pool" : "pools"}`;
511
+ }
512
+ function formatFailureCount(value) {
513
+ return `${formatNumber(value)} failed`;
514
+ }
515
+
516
+ // src/components/JobQueuePanel.tsx
517
+ import * as React2 from "react";
518
+ import {
519
+ ActionGroup as ActionGroup2,
520
+ Badge as Badge4
521
+ } from "@echothink-ui/core";
522
+ import { DataTable as DataTable2 } from "@echothink-ui/data";
523
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
524
+ function JobQueuePanel({
525
+ queues,
526
+ actions,
527
+ title = "Job queues",
528
+ subtitle,
529
+ description,
530
+ density = "compact",
531
+ className,
532
+ "aria-labelledby": ariaLabelledBy,
533
+ eyebrow: _eyebrow,
534
+ status: _status,
535
+ severity: _severity,
536
+ loading: _loading,
537
+ empty: _empty,
538
+ error: _error,
539
+ items: _items,
540
+ metadata: _metadata,
541
+ footer: _footer,
542
+ ...props
543
+ }) {
544
+ const headingId = React2.useId();
545
+ const rows = queues.map((queue) => ({ ...queue }));
546
+ const subtitleContent = subtitle ?? description;
547
+ const totals = queues.reduce(
548
+ (summary, queue) => ({
549
+ depth: summary.depth + queue.depth,
550
+ processing: summary.processing + queue.processing,
551
+ failed: summary.failed + queue.failed
552
+ }),
553
+ { depth: 0, processing: 0, failed: 0 }
554
+ );
555
+ const columns = [
556
+ {
557
+ key: "name",
558
+ header: "Queue",
559
+ width: "34%",
560
+ render: (row) => /* @__PURE__ */ jsxs5("div", { className: "eth-admin-job-queue__queue", children: [
561
+ /* @__PURE__ */ jsx5("strong", { children: row.name }),
562
+ /* @__PURE__ */ jsx5("span", { children: queueStateLabel(row) })
563
+ ] })
564
+ },
565
+ {
566
+ key: "depth",
567
+ header: "Depth",
568
+ align: "end",
569
+ width: "16%",
570
+ render: (row) => /* @__PURE__ */ jsx5("span", { className: "eth-admin-job-queue__number", children: formatCount(row.depth) })
571
+ },
572
+ {
573
+ key: "processing",
574
+ header: "Processing",
575
+ align: "end",
576
+ width: "18%",
577
+ render: (row) => /* @__PURE__ */ jsx5("span", { className: "eth-admin-job-queue__number", children: formatCount(row.processing) })
578
+ },
579
+ {
580
+ key: "failed",
581
+ header: "Failed",
582
+ align: "end",
583
+ width: "14%",
584
+ render: (row) => /* @__PURE__ */ jsx5(Badge4, { severity: row.failed > 0 ? "danger" : "success", children: formatCount(row.failed) })
585
+ },
586
+ {
587
+ key: "retryRate",
588
+ header: "Retry rate",
589
+ align: "end",
590
+ width: "18%",
591
+ render: (row) => row.retryRate === void 0 ? /* @__PURE__ */ jsx5("span", { className: "eth-admin-job-queue__muted", children: "n/a" }) : /* @__PURE__ */ jsx5(Badge4, { severity: retryRateSeverity(row.retryRate), children: formatRetryRate(row.retryRate) })
592
+ }
593
+ ];
594
+ const statusSeverity2 = queues.length === 0 ? "neutral" : totals.failed > 0 ? "danger" : totals.processing > 0 ? "info" : "success";
595
+ const statusLabel3 = queues.length === 0 ? "No queues" : totals.failed > 0 ? "Action needed" : "Operational";
596
+ return /* @__PURE__ */ jsxs5(
597
+ "section",
598
+ {
599
+ ...props,
600
+ className: ["eth-admin-job-queue", className].filter(Boolean).join(" "),
601
+ "data-eth-component": "JobQueuePanel",
602
+ "aria-labelledby": ariaLabelledBy ?? headingId,
603
+ children: [
604
+ /* @__PURE__ */ jsxs5("header", { children: [
605
+ /* @__PURE__ */ jsxs5("div", { className: "eth-admin-job-queue__heading", children: [
606
+ /* @__PURE__ */ jsx5("h3", { id: headingId, children: title }),
607
+ subtitleContent ? /* @__PURE__ */ jsx5("div", { className: "eth-admin-job-queue__subtitle", children: subtitleContent }) : null
608
+ ] }),
609
+ /* @__PURE__ */ jsxs5("div", { className: "eth-admin-job-queue__header-actions", children: [
610
+ /* @__PURE__ */ jsx5(Badge4, { severity: statusSeverity2, children: statusLabel3 }),
611
+ /* @__PURE__ */ jsx5(ActionGroup2, { actions })
612
+ ] })
613
+ ] }),
614
+ /* @__PURE__ */ jsxs5("dl", { className: "eth-admin-job-queue__summary", "aria-label": "Queue totals", children: [
615
+ /* @__PURE__ */ jsxs5("div", { children: [
616
+ /* @__PURE__ */ jsx5("dt", { children: "Total depth" }),
617
+ /* @__PURE__ */ jsx5("dd", { children: formatCount(totals.depth) })
618
+ ] }),
619
+ /* @__PURE__ */ jsxs5("div", { children: [
620
+ /* @__PURE__ */ jsx5("dt", { children: "Processing" }),
621
+ /* @__PURE__ */ jsx5("dd", { children: formatCount(totals.processing) })
622
+ ] }),
623
+ /* @__PURE__ */ jsxs5("div", { children: [
624
+ /* @__PURE__ */ jsx5("dt", { children: "Failed" }),
625
+ /* @__PURE__ */ jsx5("dd", { children: formatCount(totals.failed) })
626
+ ] })
627
+ ] }),
628
+ /* @__PURE__ */ jsx5(DataTable2, { rows, columns, density })
629
+ ]
630
+ }
631
+ );
632
+ }
633
+ function queueStateLabel(queue) {
634
+ if (queue.failed > 0) return "Failure requires review";
635
+ if (queue.processing > 0) return "Workers active";
636
+ if (queue.depth > 0) return "Waiting for capacity";
637
+ return "Idle";
638
+ }
639
+ function retryRateSeverity(retryRate) {
640
+ if (retryRate >= 1) return "danger";
641
+ if (retryRate > 0.1) return "warning";
642
+ if (retryRate > 0) return "info";
643
+ return "success";
644
+ }
645
+ function formatCount(value) {
646
+ return new Intl.NumberFormat("en-US").format(value);
647
+ }
648
+ function formatRetryRate(value) {
649
+ return `${value.toLocaleString("en-US", { maximumFractionDigits: 2 })}%`;
650
+ }
651
+
652
+ // src/components/QueueDepthChart.tsx
653
+ import { ChartBlock } from "@echothink-ui/charts";
654
+ import { jsx as jsx6 } from "react/jsx-runtime";
655
+ var depthFormatter = new Intl.NumberFormat("en-US", { maximumFractionDigits: 1 });
656
+ var queueDepthSeries = [{ key: "depth", label: "Queue depth", color: "#0f62fe" }];
657
+ function QueueDepthChart({
658
+ points,
659
+ title,
660
+ metadata,
661
+ className,
662
+ ...props
663
+ }) {
664
+ const data = points.map((point, index) => {
665
+ const row = {
666
+ label: point.label ?? point.timestamp ?? String(index + 1),
667
+ depth: point.depth
668
+ };
669
+ return point.timestamp ? { ...row, timestamp: point.timestamp } : row;
670
+ });
671
+ return /* @__PURE__ */ jsx6(
672
+ ChartBlock,
673
+ {
674
+ ...props,
675
+ className: ["eth-admin-queue-depth-chart", className].filter(Boolean).join(" "),
676
+ title: title ?? "Queue depth",
677
+ metadata: metadata ?? queueDepthMetadata(points),
678
+ chartType: "line",
679
+ data,
680
+ valueKey: "depth",
681
+ series: queueDepthSeries,
682
+ "data-eth-component": "QueueDepthChart"
683
+ }
684
+ );
685
+ }
686
+ function queueDepthMetadata(points) {
687
+ const depths = points.map((point) => point.depth).filter(Number.isFinite);
688
+ if (!depths.length) return [];
689
+ const current = depths.at(-1) ?? 0;
690
+ const previous = depths.at(-2);
691
+ const peak = Math.max(...depths);
692
+ const average2 = depths.reduce((total, depth) => total + depth, 0) / depths.length;
693
+ return [
694
+ { label: "Current depth", value: formatDepth(current) },
695
+ { label: "Peak", value: formatDepth(peak) },
696
+ { label: "Average", value: formatDepth(average2) },
697
+ {
698
+ label: "Latest change",
699
+ value: previous === void 0 ? "n/a" : formatDelta(current - previous)
700
+ }
701
+ ];
702
+ }
703
+ function formatDepth(value) {
704
+ return depthFormatter.format(value);
705
+ }
706
+ function formatDelta(value) {
707
+ if (value === 0) return "No change";
708
+ return `${value > 0 ? "+" : ""}${formatDepth(value)}`;
709
+ }
710
+
711
+ // src/components/RuntimeLogViewer.tsx
712
+ import * as React3 from "react";
713
+ import { Pause } from "@carbon/icons-react";
714
+ import { Badge as Badge5, Button as Button2 } from "@echothink-ui/core";
715
+ import { DataTable as DataTable3 } from "@echothink-ui/data";
716
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
717
+ function RuntimeLogViewer({
718
+ entries,
719
+ filters,
720
+ onPause,
721
+ streaming = false,
722
+ className,
723
+ title = "Runtime logs",
724
+ subtitle,
725
+ description,
726
+ density = "compact",
727
+ "aria-label": ariaLabel,
728
+ "aria-labelledby": ariaLabelledBy
729
+ }) {
730
+ const headingId = React3.useId();
731
+ const warningCount = countLevel(entries, "warning");
732
+ const errorCount = countLevel(entries, "error");
733
+ const latestTimestamp = entries[entries.length - 1]?.timestamp ?? "No activity";
734
+ const supportingText = subtitle ?? description ?? `${formatEntryCount(entries.length)} buffered from runtime stream.`;
735
+ const hasToolbar = Boolean(filters || streaming && onPause);
736
+ const rows = entries.map((entry) => ({ ...entry }));
737
+ const columns = [
738
+ {
739
+ key: "timestamp",
740
+ header: "Time",
741
+ width: "7rem",
742
+ render: (row) => /* @__PURE__ */ jsx7("time", { className: "eth-admin-runtime-log__timestamp", dateTime: row.timestamp, children: row.timestamp })
743
+ },
744
+ {
745
+ key: "level",
746
+ header: "Level",
747
+ width: "7rem",
748
+ render: (row) => /* @__PURE__ */ jsx7(Badge5, { severity: levelSeverity(row.level), children: formatLevel(row.level) })
749
+ },
750
+ {
751
+ key: "source",
752
+ header: "Source",
753
+ width: "11rem",
754
+ render: (row) => /* @__PURE__ */ jsx7("span", { className: "eth-admin-runtime-log__source", children: row.source ?? "system" })
755
+ },
756
+ {
757
+ key: "message",
758
+ header: "Message",
759
+ render: (row) => /* @__PURE__ */ jsx7(
760
+ "span",
761
+ {
762
+ className: [
763
+ "eth-admin-runtime-log__message",
764
+ `eth-admin-runtime-log__message--${row.level ?? "info"}`
765
+ ].join(" "),
766
+ children: row.message
767
+ }
768
+ )
769
+ }
770
+ ];
771
+ return /* @__PURE__ */ jsxs6(
772
+ "section",
773
+ {
774
+ role: "region",
775
+ "aria-label": ariaLabel,
776
+ "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : headingId),
777
+ className: [
778
+ "eth-admin-runtime-log",
779
+ streaming ? "eth-admin-runtime-log--streaming" : "eth-admin-runtime-log--paused",
780
+ className
781
+ ].filter(Boolean).join(" "),
782
+ "data-eth-component": "RuntimeLogViewer",
783
+ children: [
784
+ /* @__PURE__ */ jsxs6("header", { className: "eth-admin-runtime-log__header", children: [
785
+ /* @__PURE__ */ jsxs6("div", { className: "eth-admin-runtime-log__heading", children: [
786
+ /* @__PURE__ */ jsx7("h3", { id: headingId, children: title }),
787
+ /* @__PURE__ */ jsx7("p", { children: supportingText })
788
+ ] }),
789
+ /* @__PURE__ */ jsxs6("div", { className: "eth-admin-runtime-log__status", "aria-live": "polite", children: [
790
+ /* @__PURE__ */ jsx7(Badge5, { severity: streaming ? "success" : "neutral", children: streaming ? "Streaming" : "Paused" }),
791
+ /* @__PURE__ */ jsx7("span", { children: errorCount ? formatLevelCount(errorCount, "error") : "No errors" })
792
+ ] })
793
+ ] }),
794
+ hasToolbar ? /* @__PURE__ */ jsxs6(
795
+ "div",
796
+ {
797
+ className: "eth-admin-runtime-log__toolbar",
798
+ role: "toolbar",
799
+ "aria-label": "Runtime log controls",
800
+ children: [
801
+ filters ? /* @__PURE__ */ jsx7("div", { className: "eth-admin-runtime-log__filters", children: filters }) : null,
802
+ streaming && onPause ? /* @__PURE__ */ jsx7(
803
+ Button2,
804
+ {
805
+ type: "button",
806
+ intent: "secondary",
807
+ density: "compact",
808
+ icon: /* @__PURE__ */ jsx7(Pause, { size: 16 }),
809
+ "aria-label": "Pause runtime stream",
810
+ onClick: onPause,
811
+ children: "Pause stream"
812
+ }
813
+ ) : null
814
+ ]
815
+ }
816
+ ) : null,
817
+ /* @__PURE__ */ jsxs6("dl", { className: "eth-admin-runtime-log__summary", "aria-label": "Runtime log summary", children: [
818
+ /* @__PURE__ */ jsxs6("div", { children: [
819
+ /* @__PURE__ */ jsx7("dt", { children: "Entries" }),
820
+ /* @__PURE__ */ jsx7("dd", { children: formatEntryCount(entries.length) })
821
+ ] }),
822
+ /* @__PURE__ */ jsxs6("div", { children: [
823
+ /* @__PURE__ */ jsx7("dt", { children: "Warnings" }),
824
+ /* @__PURE__ */ jsx7("dd", { children: formatLevelCount(warningCount, "warning") })
825
+ ] }),
826
+ /* @__PURE__ */ jsxs6("div", { children: [
827
+ /* @__PURE__ */ jsx7("dt", { children: "Errors" }),
828
+ /* @__PURE__ */ jsx7("dd", { children: formatLevelCount(errorCount, "error") })
829
+ ] }),
830
+ /* @__PURE__ */ jsxs6("div", { children: [
831
+ /* @__PURE__ */ jsx7("dt", { children: "Latest" }),
832
+ /* @__PURE__ */ jsx7("dd", { children: latestTimestamp })
833
+ ] })
834
+ ] }),
835
+ /* @__PURE__ */ jsx7("div", { className: "eth-admin-runtime-log__table-shell", "aria-live": streaming ? "polite" : "off", children: /* @__PURE__ */ jsx7(
836
+ DataTable3,
837
+ {
838
+ rows,
839
+ columns,
840
+ density,
841
+ rowKey: "id",
842
+ className: "eth-admin-runtime-log__table",
843
+ emptyState: /* @__PURE__ */ jsxs6("div", { className: "eth-admin-runtime-log__empty", role: "status", children: [
844
+ /* @__PURE__ */ jsx7("h4", { children: "No runtime logs" }),
845
+ /* @__PURE__ */ jsx7("p", { children: "Events will appear here when the stream receives output." })
846
+ ] })
847
+ }
848
+ ) })
849
+ ]
850
+ }
851
+ );
852
+ }
853
+ function countLevel(entries, level) {
854
+ return entries.filter((entry) => entry.level === level).length;
855
+ }
856
+ function formatEntryCount(count) {
857
+ return `${count} ${count === 1 ? "entry" : "entries"}`;
858
+ }
859
+ function formatLevelCount(count, label) {
860
+ return `${count} ${count === 1 ? label : `${label}s`}`;
861
+ }
862
+ function formatLevel(level) {
863
+ return (level ?? "info").toUpperCase();
864
+ }
865
+ function levelSeverity(level) {
866
+ if (level === "error") return "danger";
867
+ if (level === "warning") return "warning";
868
+ if (level === "debug") return "neutral";
869
+ return "info";
870
+ }
871
+
872
+ // src/components/AuditLogViewer.tsx
873
+ import * as React4 from "react";
874
+ import { Badge as Badge6, Button as Button3 } from "@echothink-ui/core";
875
+ import { DataTable as DataTable4 } from "@echothink-ui/data";
876
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
877
+ var exportFormats = [
878
+ { format: "csv", label: "CSV" },
879
+ { format: "json", label: "JSON" },
880
+ { format: "xlsx", label: "XLSX" }
881
+ ];
882
+ function AuditLogViewer({
883
+ entries,
884
+ filters,
885
+ onExport,
886
+ className,
887
+ "aria-label": ariaLabel,
888
+ "aria-labelledby": ariaLabelledBy
889
+ }) {
890
+ const headingId = React4.useId();
891
+ const rows = entries.map((entry) => ({ ...entry }));
892
+ const columns = [
893
+ { key: "timestamp", header: "Time", width: "7rem" },
894
+ {
895
+ key: "actor",
896
+ header: "Actor",
897
+ width: "9rem",
898
+ render: (row) => /* @__PURE__ */ jsx8("span", { className: "eth-admin-audit-log__actor", children: row.actor })
899
+ },
900
+ { key: "action", header: "Action", width: "12rem" },
901
+ { key: "resource", header: "Resource", width: "12rem" },
902
+ {
903
+ key: "ip",
904
+ header: "IP",
905
+ width: "9rem",
906
+ render: (row) => /* @__PURE__ */ jsx8("span", { className: "eth-admin-audit-log__mono", children: row.ip ?? "n/a" })
907
+ },
908
+ {
909
+ key: "outcome",
910
+ header: "Outcome",
911
+ width: "8rem",
912
+ render: (row) => /* @__PURE__ */ jsx8(Badge6, { severity: outcomeSeverity(row.outcome), children: row.outcome })
913
+ }
914
+ ];
915
+ return /* @__PURE__ */ jsxs7(
916
+ "section",
917
+ {
918
+ "aria-label": ariaLabel,
919
+ "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : headingId),
920
+ className: ["eth-admin-audit-log", className].filter(Boolean).join(" "),
921
+ "data-eth-component": "AuditLogViewer",
922
+ children: [
923
+ /* @__PURE__ */ jsxs7("header", { children: [
924
+ /* @__PURE__ */ jsxs7("div", { className: "eth-admin-audit-log__heading", children: [
925
+ /* @__PURE__ */ jsx8("h3", { id: headingId, children: "Audit log" }),
926
+ /* @__PURE__ */ jsx8("span", { children: formatEntryCount2(entries.length) })
927
+ ] }),
928
+ /* @__PURE__ */ jsxs7("div", { className: "eth-admin-audit-log__toolbar", children: [
929
+ filters ? /* @__PURE__ */ jsx8("div", { className: "eth-admin-audit-log__filters", children: filters }) : null,
930
+ /* @__PURE__ */ jsx8("div", { className: "eth-admin-audit-log__exports", "aria-label": "Export audit log", children: exportFormats.map(({ format, label }) => /* @__PURE__ */ jsx8(
931
+ Button3,
932
+ {
933
+ type: "button",
934
+ intent: "tertiary",
935
+ density: "compact",
936
+ disabled: !onExport,
937
+ onClick: () => onExport?.(format),
938
+ children: label
939
+ },
940
+ format
941
+ )) })
942
+ ] })
943
+ ] }),
944
+ /* @__PURE__ */ jsx8(DataTable4, { rows, columns, density: "compact", rowKey: "id" })
945
+ ]
946
+ }
947
+ );
948
+ }
949
+ function formatEntryCount2(count) {
950
+ return `${count} ${count === 1 ? "event" : "events"}`;
951
+ }
952
+ function outcomeSeverity(outcome) {
953
+ return outcome === "success" ? "success" : outcome === "failure" ? "danger" : "neutral";
954
+ }
955
+
956
+ // src/components/PolicyConfigPanel.tsx
957
+ import { useId as useId5 } from "react";
958
+ import {
959
+ Badge as Badge7,
960
+ Button as Button4,
961
+ FormField,
962
+ Surface as Surface3,
963
+ Tag,
964
+ TextInput,
965
+ Textarea
966
+ } from "@echothink-ui/core";
967
+ import { RuleBuilder } from "@echothink-ui/forms";
968
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
969
+ function PolicyConfigPanel({
970
+ policy,
971
+ schema,
972
+ onChange,
973
+ onSave,
974
+ title,
975
+ description,
976
+ density = "default",
977
+ className,
978
+ ...props
979
+ }) {
980
+ const generatedId = useId5().replace(/[^a-zA-Z0-9_-]/g, "");
981
+ const policyDomId = `eth-admin-policy-${String(policy.id ?? generatedId).replace(
982
+ /[^a-zA-Z0-9_-]/g,
983
+ "-"
984
+ )}`;
985
+ const update = (patch) => onChange?.({ ...policy, ...patch });
986
+ const schemaFields = Object.keys(schema.properties ?? {});
987
+ const variables = policy.variables?.length ? policy.variables : schemaFields;
988
+ const rules = policy.rules ?? [];
989
+ const visibleVariables = variables.slice(0, 5);
990
+ const hiddenVariableCount = Math.max(0, variables.length - visibleVariables.length);
991
+ return /* @__PURE__ */ jsxs8(
992
+ Surface3,
993
+ {
994
+ ...props,
995
+ title: title ?? "Policy configuration",
996
+ description: description ?? schema.description ?? "Configure policy metadata and conditional rules for governed operations.",
997
+ density,
998
+ className: ["eth-admin-policy-config", className].filter(Boolean).join(" "),
999
+ "data-eth-component": "PolicyConfigPanel",
1000
+ children: [
1001
+ /* @__PURE__ */ jsxs8("div", { className: "eth-admin-policy-config__layout", children: [
1002
+ /* @__PURE__ */ jsxs8("div", { className: "eth-admin-policy-config__fields", children: [
1003
+ /* @__PURE__ */ jsx9(FormField, { id: `${policyDomId}-name`, label: "Policy name", required: true, children: /* @__PURE__ */ jsx9(
1004
+ TextInput,
1005
+ {
1006
+ id: `${policyDomId}-name`,
1007
+ density,
1008
+ value: policy.name ?? "",
1009
+ onChange: (event) => update({ name: event.currentTarget.value })
1010
+ }
1011
+ ) }),
1012
+ /* @__PURE__ */ jsx9(FormField, { id: `${policyDomId}-description`, label: "Description", children: /* @__PURE__ */ jsx9(
1013
+ Textarea,
1014
+ {
1015
+ id: `${policyDomId}-description`,
1016
+ rows: 4,
1017
+ value: policy.description ?? "",
1018
+ onChange: (event) => update({ description: event.currentTarget.value })
1019
+ }
1020
+ ) })
1021
+ ] }),
1022
+ /* @__PURE__ */ jsxs8("aside", { className: "eth-admin-policy-config__summary", "aria-label": "Policy summary", children: [
1023
+ /* @__PURE__ */ jsxs8("div", { className: "eth-admin-policy-config__summary-header", children: [
1024
+ /* @__PURE__ */ jsx9("h3", { children: "Policy scope" }),
1025
+ /* @__PURE__ */ jsx9(Badge7, { severity: rules.length ? "success" : "warning", children: rules.length ? "Configured" : "Needs rules" })
1026
+ ] }),
1027
+ /* @__PURE__ */ jsxs8("dl", { className: "eth-admin-policy-config__summary-grid", children: [
1028
+ /* @__PURE__ */ jsxs8("div", { children: [
1029
+ /* @__PURE__ */ jsx9("dt", { children: "Rules" }),
1030
+ /* @__PURE__ */ jsx9("dd", { children: rules.length })
1031
+ ] }),
1032
+ /* @__PURE__ */ jsxs8("div", { children: [
1033
+ /* @__PURE__ */ jsx9("dt", { children: "Variables" }),
1034
+ /* @__PURE__ */ jsx9("dd", { children: variables.length })
1035
+ ] }),
1036
+ /* @__PURE__ */ jsxs8("div", { children: [
1037
+ /* @__PURE__ */ jsx9("dt", { children: "Schema fields" }),
1038
+ /* @__PURE__ */ jsx9("dd", { children: schemaFields.length })
1039
+ ] })
1040
+ ] }),
1041
+ /* @__PURE__ */ jsxs8("div", { className: "eth-admin-policy-config__variables", children: [
1042
+ /* @__PURE__ */ jsx9("span", { children: "Rule variables" }),
1043
+ variables.length ? /* @__PURE__ */ jsxs8("div", { className: "eth-admin-policy-config__variable-list", children: [
1044
+ visibleVariables.map((variable) => /* @__PURE__ */ jsx9(Tag, { children: variable }, variable)),
1045
+ hiddenVariableCount ? /* @__PURE__ */ jsxs8(Badge7, { severity: "neutral", children: [
1046
+ "+",
1047
+ hiddenVariableCount,
1048
+ " more"
1049
+ ] }) : null
1050
+ ] }) : /* @__PURE__ */ jsx9("p", { children: "No variables available." })
1051
+ ] })
1052
+ ] })
1053
+ ] }),
1054
+ /* @__PURE__ */ jsx9(
1055
+ RuleBuilder,
1056
+ {
1057
+ className: "eth-admin-policy-config__rules",
1058
+ rules,
1059
+ variables,
1060
+ onChange: (nextRules) => update({ rules: nextRules })
1061
+ }
1062
+ ),
1063
+ /* @__PURE__ */ jsx9("div", { className: "eth-admin-policy-config__actions", children: /* @__PURE__ */ jsx9(Button4, { type: "button", disabled: !onSave, onClick: () => onSave?.(policy), children: "Save policy" }) })
1064
+ ]
1065
+ }
1066
+ );
1067
+ }
1068
+
1069
+ // src/components/BillingUsagePanel.tsx
1070
+ import { Badge as Badge8, Surface as Surface4 } from "@echothink-ui/core";
1071
+ import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
1072
+ function BillingUsagePanel({ metrics, period, title, className, ...props }) {
1073
+ const entries = metrics.map((metric) => {
1074
+ const percent = metric.limit > 0 ? metric.current / metric.limit * 100 : 0;
1075
+ const roundedPercent = Math.round(percent);
1076
+ const cappedPercent = Math.min(100, Math.max(0, percent));
1077
+ const state = usageState(percent, metric.limit);
1078
+ return {
1079
+ metric,
1080
+ percent,
1081
+ roundedPercent,
1082
+ cappedPercent,
1083
+ state,
1084
+ trendLabel: formatTrend(metric.trend),
1085
+ usageLabel: metric.limit > 0 ? `${formatNumber2(metric.current)} / ${formatNumber2(metric.limit)} ${formatUnit(metric.unit, metric.limit)}` : `${formatNumber2(metric.current)} ${formatUnit(metric.unit, metric.current)}`
1086
+ };
1087
+ });
1088
+ const highestUsage = entries.reduce((highest, entry) => {
1089
+ if (!highest || entry.percent > highest.percent) return entry;
1090
+ return highest;
1091
+ }, void 0);
1092
+ const overLimitCount = entries.filter((entry) => entry.percent >= 100).length;
1093
+ const watchedCount = entries.filter((entry) => entry.percent >= 80 && entry.percent < 100).length;
1094
+ const quotaStatus = getQuotaStatus(overLimitCount, watchedCount);
1095
+ return /* @__PURE__ */ jsx10(
1096
+ Surface4,
1097
+ {
1098
+ ...props,
1099
+ title: title ?? "Billing usage",
1100
+ subtitle: period,
1101
+ className: ["eth-admin-billing-usage", className].filter(Boolean).join(" "),
1102
+ "data-eth-component": "BillingUsagePanel",
1103
+ children: entries.length ? /* @__PURE__ */ jsxs9(Fragment, { children: [
1104
+ /* @__PURE__ */ jsxs9("dl", { className: "eth-admin-billing-usage__summary", "aria-label": "Billing usage summary", children: [
1105
+ /* @__PURE__ */ jsxs9("div", { children: [
1106
+ /* @__PURE__ */ jsx10("dt", { children: "Tracked quotas" }),
1107
+ /* @__PURE__ */ jsx10("dd", { children: entries.length })
1108
+ ] }),
1109
+ /* @__PURE__ */ jsxs9("div", { children: [
1110
+ /* @__PURE__ */ jsx10("dt", { children: "Highest usage" }),
1111
+ /* @__PURE__ */ jsx10("dd", { children: highestUsage ? `${highestUsage.metric.label} ${highestUsage.roundedPercent}%` : "n/a" })
1112
+ ] }),
1113
+ /* @__PURE__ */ jsxs9("div", { children: [
1114
+ /* @__PURE__ */ jsx10("dt", { children: "Quota status" }),
1115
+ /* @__PURE__ */ jsx10("dd", { children: /* @__PURE__ */ jsx10(Badge8, { severity: quotaStatus.severity, children: quotaStatus.label }) })
1116
+ ] })
1117
+ ] }),
1118
+ /* @__PURE__ */ jsx10("div", { className: "eth-admin-billing-usage__metrics", children: entries.map(({ metric, roundedPercent, cappedPercent, state, trendLabel, usageLabel }) => /* @__PURE__ */ jsxs9("section", { className: "eth-admin-billing-usage__metric", children: [
1119
+ /* @__PURE__ */ jsxs9("div", { className: "eth-admin-billing-usage__metric-header", children: [
1120
+ /* @__PURE__ */ jsxs9("div", { children: [
1121
+ /* @__PURE__ */ jsx10("h3", { children: metric.label }),
1122
+ /* @__PURE__ */ jsx10("p", { children: usageLabel })
1123
+ ] }),
1124
+ /* @__PURE__ */ jsx10(Badge8, { severity: state.severity, children: state.label })
1125
+ ] }),
1126
+ /* @__PURE__ */ jsx10(
1127
+ "div",
1128
+ {
1129
+ className: "eth-admin-billing-usage__meter",
1130
+ role: "progressbar",
1131
+ "aria-label": `${metric.label} quota usage`,
1132
+ "aria-valuemin": 0,
1133
+ "aria-valuemax": 100,
1134
+ "aria-valuenow": Math.round(cappedPercent),
1135
+ "aria-valuetext": metric.limit > 0 ? `${roundedPercent}% of quota used` : "No quota limit configured",
1136
+ children: /* @__PURE__ */ jsx10(
1137
+ "span",
1138
+ {
1139
+ className: `eth-admin-billing-usage__meter-fill eth-admin-billing-usage__meter-fill--${state.tone}`,
1140
+ style: {
1141
+ "--eth-admin-billing-usage-percent": `${cappedPercent}%`
1142
+ }
1143
+ }
1144
+ )
1145
+ }
1146
+ ),
1147
+ /* @__PURE__ */ jsxs9("dl", { className: "eth-admin-billing-usage__metric-details", children: [
1148
+ /* @__PURE__ */ jsxs9("div", { children: [
1149
+ /* @__PURE__ */ jsx10("dt", { children: "Utilization" }),
1150
+ /* @__PURE__ */ jsx10("dd", { children: metric.limit > 0 ? `${roundedPercent}%` : "No limit" })
1151
+ ] }),
1152
+ /* @__PURE__ */ jsxs9("div", { children: [
1153
+ /* @__PURE__ */ jsx10("dt", { children: "Limit" }),
1154
+ /* @__PURE__ */ jsx10("dd", { children: metric.limit > 0 ? `${formatNumber2(metric.limit)} ${formatUnit(metric.unit, metric.limit)}` : "Unlimited" })
1155
+ ] }),
1156
+ trendLabel ? /* @__PURE__ */ jsxs9("div", { children: [
1157
+ /* @__PURE__ */ jsx10("dt", { children: "Change" }),
1158
+ /* @__PURE__ */ jsx10("dd", { children: trendLabel })
1159
+ ] }) : null
1160
+ ] })
1161
+ ] }, `${metric.label}-${metric.unit}`)) })
1162
+ ] }) : /* @__PURE__ */ jsx10("p", { className: "eth-admin-billing-usage__empty", children: "No usage metrics are available for this period." })
1163
+ }
1164
+ );
1165
+ }
1166
+ function usageState(percent, limit) {
1167
+ if (limit <= 0) return { label: "No limit", severity: "neutral", tone: "neutral" };
1168
+ if (percent >= 100) return { label: "Over limit", severity: "danger", tone: "danger" };
1169
+ if (percent >= 80) return { label: "Watch", severity: "warning", tone: "warning" };
1170
+ return { label: "Within limit", severity: "success", tone: "normal" };
1171
+ }
1172
+ function getQuotaStatus(overLimitCount, watchedCount) {
1173
+ if (overLimitCount > 0) return { label: `${overLimitCount} over limit`, severity: "danger" };
1174
+ if (watchedCount > 0) return { label: `${watchedCount} nearing limit`, severity: "warning" };
1175
+ return { label: "Within limits", severity: "success" };
1176
+ }
1177
+ function formatNumber2(value) {
1178
+ return new Intl.NumberFormat("en-US", {
1179
+ maximumFractionDigits: value % 1 === 0 ? 0 : 2
1180
+ }).format(value);
1181
+ }
1182
+ function formatUnit(unit, value) {
1183
+ if (unit.toLowerCase() === "user") return value === 1 ? "user" : "users";
1184
+ return unit;
1185
+ }
1186
+ function formatTrend(trend) {
1187
+ if (trend === void 0) return void 0;
1188
+ if (trend === 0) return "Unchanged";
1189
+ return `${trend > 0 ? "+" : ""}${trend}% vs previous period`;
1190
+ }
1191
+
1192
+ // src/components/TenantSettingsPanel.tsx
1193
+ import { ConfigForm } from "@echothink-ui/forms";
1194
+ import { jsx as jsx11 } from "react/jsx-runtime";
1195
+ function TenantSettingsPanel({
1196
+ sections,
1197
+ values,
1198
+ onChange,
1199
+ onSubmit,
1200
+ submitLabel = "Save settings",
1201
+ title,
1202
+ description,
1203
+ className,
1204
+ ...props
1205
+ }) {
1206
+ const effectiveDescription = description === void 0 ? "Manage tenant identity, regional placement, and governance defaults." : description;
1207
+ return /* @__PURE__ */ jsx11(
1208
+ ConfigForm,
1209
+ {
1210
+ ...props,
1211
+ title: title ?? "Tenant settings",
1212
+ description: effectiveDescription,
1213
+ className: ["eth-admin-tenant-settings", className].filter(Boolean).join(" "),
1214
+ sections,
1215
+ values,
1216
+ onChange,
1217
+ onSubmit,
1218
+ submitLabel,
1219
+ "data-eth-component": "TenantSettingsPanel"
1220
+ }
1221
+ );
1222
+ }
1223
+
1224
+ // src/components/FeatureFlagPanel.tsx
1225
+ import * as React5 from "react";
1226
+ import {
1227
+ ActionGroup as ActionGroup3,
1228
+ Badge as Badge9,
1229
+ NumberInput,
1230
+ Toggle
1231
+ } from "@echothink-ui/core";
1232
+ import { DataTable as DataTable5 } from "@echothink-ui/data";
1233
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1234
+ function FeatureFlagPanel({
1235
+ flags,
1236
+ onToggle,
1237
+ onUpdate,
1238
+ className,
1239
+ title = "Feature flags",
1240
+ subtitle,
1241
+ description,
1242
+ actions,
1243
+ density = "compact",
1244
+ "aria-label": ariaLabel,
1245
+ "aria-labelledby": ariaLabelledBy,
1246
+ eyebrow: _eyebrow,
1247
+ status: _status,
1248
+ severity: _severity,
1249
+ loading: _loading,
1250
+ empty: _empty,
1251
+ error: _error,
1252
+ items: _items,
1253
+ metadata: _metadata,
1254
+ footer: _footer,
1255
+ ...sectionProps
1256
+ }) {
1257
+ const headingId = React5.useId();
1258
+ const enabledCount = flags.filter((flag) => flag.enabled).length;
1259
+ const disabledCount = flags.length - enabledCount;
1260
+ const rows = flags.map((flag) => ({ ...flag }));
1261
+ const columns = [
1262
+ {
1263
+ key: "name",
1264
+ header: "Flag",
1265
+ width: "34%",
1266
+ render: (row) => /* @__PURE__ */ jsx12("span", { className: "eth-admin-feature-flag__name", children: row.name })
1267
+ },
1268
+ {
1269
+ key: "enabled",
1270
+ header: "Status",
1271
+ width: "13rem",
1272
+ render: (row) => /* @__PURE__ */ jsxs10("div", { className: "eth-admin-feature-flag__status", children: [
1273
+ /* @__PURE__ */ jsx12(
1274
+ Toggle,
1275
+ {
1276
+ label: `${row.name} is ${row.enabled ? "enabled" : "disabled"}`,
1277
+ hideLabel: true,
1278
+ onLabel: "",
1279
+ offLabel: "",
1280
+ checked: row.enabled,
1281
+ readOnly: !onToggle,
1282
+ density,
1283
+ onChange: () => onToggle?.(row.id)
1284
+ }
1285
+ ),
1286
+ /* @__PURE__ */ jsx12(Badge9, { severity: row.enabled ? "success" : "neutral", children: row.enabled ? "Enabled" : "Disabled" })
1287
+ ] })
1288
+ },
1289
+ {
1290
+ key: "rolloutPercent",
1291
+ header: "Rollout",
1292
+ width: "18rem",
1293
+ render: (row) => /* @__PURE__ */ jsxs10("div", { className: "eth-admin-feature-flag__rollout", children: [
1294
+ /* @__PURE__ */ jsx12(
1295
+ NumberInput,
1296
+ {
1297
+ "aria-label": `${row.name} rollout percent`,
1298
+ className: "eth-admin-feature-flag__rollout-input",
1299
+ density,
1300
+ hideLabel: true,
1301
+ min: 0,
1302
+ max: 100,
1303
+ step: 1,
1304
+ readOnly: !onUpdate,
1305
+ value: normalizeRolloutPercent(row.rolloutPercent),
1306
+ onChange: (event) => onUpdate?.(row.id, {
1307
+ rolloutPercent: normalizeRolloutPercent(Number(event.currentTarget.value))
1308
+ })
1309
+ }
1310
+ ),
1311
+ /* @__PURE__ */ jsx12("span", { className: "eth-admin-feature-flag__rollout-unit", "aria-hidden": "true", children: "%" }),
1312
+ /* @__PURE__ */ jsx12("span", { className: "eth-admin-feature-flag__meter", "aria-hidden": "true", children: /* @__PURE__ */ jsx12(
1313
+ "span",
1314
+ {
1315
+ style: {
1316
+ "--eth-admin-feature-flag-rollout": `${normalizeRolloutPercent(row.rolloutPercent)}%`
1317
+ }
1318
+ }
1319
+ ) })
1320
+ ] })
1321
+ },
1322
+ {
1323
+ key: "environments",
1324
+ header: "Environments",
1325
+ render: (row) => row.environments?.length ? /* @__PURE__ */ jsx12("div", { className: "eth-admin-feature-flag__environments", children: row.environments.map((environment) => /* @__PURE__ */ jsx12(Badge9, { severity: "neutral", children: environment }, environment)) }) : /* @__PURE__ */ jsx12(Badge9, { severity: "neutral", children: "All environments" })
1326
+ }
1327
+ ];
1328
+ const supportingText = subtitle ?? description;
1329
+ return /* @__PURE__ */ jsxs10(
1330
+ "section",
1331
+ {
1332
+ ...sectionProps,
1333
+ "aria-label": ariaLabel,
1334
+ "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : headingId),
1335
+ className: ["eth-admin-feature-flag", className].filter(Boolean).join(" "),
1336
+ "data-eth-component": "FeatureFlagPanel",
1337
+ children: [
1338
+ /* @__PURE__ */ jsxs10("header", { children: [
1339
+ /* @__PURE__ */ jsxs10("div", { className: "eth-admin-feature-flag__heading", children: [
1340
+ /* @__PURE__ */ jsx12("h3", { id: headingId, children: title }),
1341
+ supportingText ? /* @__PURE__ */ jsx12("p", { children: supportingText }) : null
1342
+ ] }),
1343
+ /* @__PURE__ */ jsxs10("div", { className: "eth-admin-feature-flag__header-actions", children: [
1344
+ /* @__PURE__ */ jsxs10(
1345
+ "div",
1346
+ {
1347
+ className: "eth-admin-feature-flag__summary",
1348
+ "aria-label": `${flags.length} feature flags`,
1349
+ children: [
1350
+ /* @__PURE__ */ jsxs10(Badge9, { severity: "success", children: [
1351
+ enabledCount,
1352
+ " enabled"
1353
+ ] }),
1354
+ /* @__PURE__ */ jsxs10(Badge9, { severity: "neutral", children: [
1355
+ disabledCount,
1356
+ " disabled"
1357
+ ] })
1358
+ ]
1359
+ }
1360
+ ),
1361
+ /* @__PURE__ */ jsx12(ActionGroup3, { actions })
1362
+ ] })
1363
+ ] }),
1364
+ /* @__PURE__ */ jsx12(
1365
+ DataTable5,
1366
+ {
1367
+ rows,
1368
+ columns,
1369
+ density,
1370
+ rowKey: "id",
1371
+ className: "eth-admin-feature-flag__table",
1372
+ emptyState: /* @__PURE__ */ jsx12("p", { className: "eth-admin-feature-flag__empty", children: "No feature flags configured." })
1373
+ }
1374
+ )
1375
+ ]
1376
+ }
1377
+ );
1378
+ }
1379
+ function normalizeRolloutPercent(value) {
1380
+ const numericValue = value ?? 0;
1381
+ if (!Number.isFinite(numericValue)) return 0;
1382
+ return Math.min(100, Math.max(0, Math.round(numericValue)));
1383
+ }
1384
+
1385
+ // src/components/RateLimitPanel.tsx
1386
+ import * as React6 from "react";
1387
+ import {
1388
+ ActionGroup as ActionGroup4,
1389
+ Badge as Badge10,
1390
+ NumberInput as NumberInput2
1391
+ } from "@echothink-ui/core";
1392
+ import { DataTable as DataTable6 } from "@echothink-ui/data";
1393
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1394
+ function RateLimitPanel({
1395
+ limits,
1396
+ onUpdate,
1397
+ title = "Rate limits",
1398
+ subtitle,
1399
+ description,
1400
+ actions,
1401
+ density = "compact",
1402
+ className,
1403
+ "aria-label": ariaLabel,
1404
+ "aria-labelledby": ariaLabelledBy,
1405
+ eyebrow: _eyebrow,
1406
+ status: _status,
1407
+ severity: _severity,
1408
+ loading: _loading,
1409
+ empty: _empty,
1410
+ error: _error,
1411
+ items: _items,
1412
+ metadata: _metadata,
1413
+ footer: _footer,
1414
+ ...sectionProps
1415
+ }) {
1416
+ const headingId = React6.useId();
1417
+ const rows = limits.map((limit) => {
1418
+ const percent = limit.limit > 0 ? limit.current / limit.limit * 100 : 0;
1419
+ const cappedPercent = Math.min(100, Math.max(0, percent));
1420
+ return {
1421
+ ...limit,
1422
+ cappedPercent,
1423
+ percent,
1424
+ percentLabel: limit.limit > 0 ? formatPercent3(percent) : "No limit",
1425
+ state: rateLimitState(percent, limit.limit),
1426
+ usageLabel: limit.limit > 0 ? `${formatNumber3(limit.current)} / ${formatNumber3(limit.limit)}` : `${formatNumber3(limit.current)} requests`
1427
+ };
1428
+ });
1429
+ const highestUsage = rows.reduce((highest, row) => {
1430
+ if (row.limit <= 0) return highest;
1431
+ if (!highest || row.percent > highest.percent) return row;
1432
+ return highest;
1433
+ }, void 0);
1434
+ const exceededCount = rows.filter((row) => row.limit > 0 && row.percent >= 100).length;
1435
+ const nearLimitCount = rows.filter(
1436
+ (row) => row.limit > 0 && row.percent >= 80 && row.percent < 100
1437
+ ).length;
1438
+ const statusSummary = rateLimitStatusSummary(limits.length, exceededCount, nearLimitCount);
1439
+ const supportingText = subtitle ?? description;
1440
+ const columns = [
1441
+ {
1442
+ key: "name",
1443
+ header: "Limit",
1444
+ width: "28%",
1445
+ render: (row) => /* @__PURE__ */ jsxs11("div", { className: "eth-admin-rate-limit__limit", children: [
1446
+ /* @__PURE__ */ jsx13("strong", { children: row.name }),
1447
+ /* @__PURE__ */ jsx13("span", { children: row.state.label })
1448
+ ] })
1449
+ },
1450
+ {
1451
+ key: "current",
1452
+ header: "Usage",
1453
+ width: "34%",
1454
+ render: (row) => /* @__PURE__ */ jsxs11("div", { className: "eth-admin-rate-limit__usage", children: [
1455
+ /* @__PURE__ */ jsxs11("div", { className: "eth-admin-rate-limit__usage-copy", children: [
1456
+ /* @__PURE__ */ jsx13("span", { children: row.usageLabel }),
1457
+ /* @__PURE__ */ jsx13("strong", { children: row.percentLabel })
1458
+ ] }),
1459
+ /* @__PURE__ */ jsx13(
1460
+ "span",
1461
+ {
1462
+ className: "eth-admin-rate-limit__meter",
1463
+ role: "progressbar",
1464
+ "aria-label": `${row.name} usage`,
1465
+ "aria-valuemin": 0,
1466
+ "aria-valuemax": 100,
1467
+ "aria-valuenow": Math.round(row.cappedPercent),
1468
+ "aria-valuetext": row.limit > 0 ? `${formatPercent3(row.percent)} used, ${formatNumber3(row.current)} of ${formatNumber3(
1469
+ row.limit
1470
+ )} requests` : "No configured limit",
1471
+ style: {
1472
+ "--eth-admin-rate-limit-usage": `${row.cappedPercent}%`
1473
+ },
1474
+ children: /* @__PURE__ */ jsx13(
1475
+ "span",
1476
+ {
1477
+ className: [
1478
+ "eth-admin-rate-limit__meter-fill",
1479
+ `eth-admin-rate-limit__meter-fill--${row.state.tone}`
1480
+ ].join(" ")
1481
+ }
1482
+ )
1483
+ }
1484
+ )
1485
+ ] })
1486
+ },
1487
+ {
1488
+ key: "windowSeconds",
1489
+ header: "Window",
1490
+ width: "8rem",
1491
+ render: (row) => /* @__PURE__ */ jsx13("span", { className: "eth-admin-rate-limit__number", children: formatWindow(row.windowSeconds) })
1492
+ },
1493
+ {
1494
+ key: "limit",
1495
+ header: "Configured limit",
1496
+ width: "13rem",
1497
+ render: (row) => /* @__PURE__ */ jsx13(
1498
+ NumberInput2,
1499
+ {
1500
+ "aria-label": `${row.name} configured limit`,
1501
+ className: "eth-admin-rate-limit__limit-input",
1502
+ density,
1503
+ hideLabel: true,
1504
+ min: 0,
1505
+ step: 1,
1506
+ readOnly: !onUpdate,
1507
+ value: row.limit,
1508
+ onChange: (event) => {
1509
+ const nextLimit = Number(event.currentTarget.value);
1510
+ if (Number.isFinite(nextLimit)) onUpdate?.(row.id, { limit: nextLimit });
1511
+ }
1512
+ }
1513
+ )
1514
+ }
1515
+ ];
1516
+ return /* @__PURE__ */ jsxs11(
1517
+ "section",
1518
+ {
1519
+ ...sectionProps,
1520
+ "aria-label": ariaLabel,
1521
+ "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : headingId),
1522
+ className: ["eth-admin-rate-limit", className].filter(Boolean).join(" "),
1523
+ "data-eth-component": "RateLimitPanel",
1524
+ children: [
1525
+ /* @__PURE__ */ jsxs11("header", { children: [
1526
+ /* @__PURE__ */ jsxs11("div", { className: "eth-admin-rate-limit__heading", children: [
1527
+ /* @__PURE__ */ jsx13("h3", { id: headingId, children: title }),
1528
+ supportingText ? /* @__PURE__ */ jsx13("p", { children: supportingText }) : null
1529
+ ] }),
1530
+ /* @__PURE__ */ jsxs11("div", { className: "eth-admin-rate-limit__header-actions", children: [
1531
+ /* @__PURE__ */ jsx13(Badge10, { severity: statusSummary.severity, children: statusSummary.label }),
1532
+ /* @__PURE__ */ jsx13(ActionGroup4, { actions })
1533
+ ] })
1534
+ ] }),
1535
+ /* @__PURE__ */ jsxs11("dl", { className: "eth-admin-rate-limit__summary", "aria-label": "Rate limit totals", children: [
1536
+ /* @__PURE__ */ jsxs11("div", { children: [
1537
+ /* @__PURE__ */ jsx13("dt", { children: "Tracked limits" }),
1538
+ /* @__PURE__ */ jsx13("dd", { children: formatNumber3(limits.length) })
1539
+ ] }),
1540
+ /* @__PURE__ */ jsxs11("div", { children: [
1541
+ /* @__PURE__ */ jsx13("dt", { children: "Highest usage" }),
1542
+ /* @__PURE__ */ jsx13("dd", { children: highestUsage ? `${highestUsage.name} ${formatPercent3(highestUsage.percent)}` : "n/a" })
1543
+ ] }),
1544
+ /* @__PURE__ */ jsxs11("div", { children: [
1545
+ /* @__PURE__ */ jsx13("dt", { children: "Needs review" }),
1546
+ /* @__PURE__ */ jsx13("dd", { children: formatNumber3(exceededCount + nearLimitCount) })
1547
+ ] })
1548
+ ] }),
1549
+ /* @__PURE__ */ jsx13(
1550
+ DataTable6,
1551
+ {
1552
+ rows,
1553
+ columns,
1554
+ rowKey: "id",
1555
+ density,
1556
+ className: "eth-admin-rate-limit__table",
1557
+ emptyState: /* @__PURE__ */ jsx13("p", { className: "eth-admin-rate-limit__empty", children: "No rate limits configured." })
1558
+ }
1559
+ )
1560
+ ]
1561
+ }
1562
+ );
1563
+ }
1564
+ function rateLimitState(percent, limit) {
1565
+ if (limit <= 0) return { label: "No limit", severity: "neutral", tone: "neutral" };
1566
+ if (percent >= 100) return { label: "Exceeded", severity: "danger", tone: "danger" };
1567
+ if (percent >= 80) return { label: "Near limit", severity: "warning", tone: "warning" };
1568
+ return { label: "Within limit", severity: "success", tone: "normal" };
1569
+ }
1570
+ function rateLimitStatusSummary(totalCount, exceededCount, nearLimitCount) {
1571
+ if (totalCount === 0) return { label: "No limits", severity: "neutral" };
1572
+ if (exceededCount > 0) {
1573
+ return { label: `${formatNumber3(exceededCount)} exceeded`, severity: "danger" };
1574
+ }
1575
+ if (nearLimitCount > 0) {
1576
+ return { label: `${formatNumber3(nearLimitCount)} near limit`, severity: "warning" };
1577
+ }
1578
+ return { label: "Within limits", severity: "success" };
1579
+ }
1580
+ function formatWindow(seconds) {
1581
+ return `${formatNumber3(seconds)}s`;
1582
+ }
1583
+ function formatPercent3(value) {
1584
+ return `${Math.round(value).toLocaleString("en-US")}%`;
1585
+ }
1586
+ function formatNumber3(value) {
1587
+ return new Intl.NumberFormat("en-US").format(value);
1588
+ }
1589
+
1590
+ // src/components/IntegrationHealthTable.tsx
1591
+ import * as React7 from "react";
1592
+ import {
1593
+ ActionGroup as ActionGroup5,
1594
+ Badge as Badge11
1595
+ } from "@echothink-ui/core";
1596
+ import { DataTable as DataTable7 } from "@echothink-ui/data";
1597
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1598
+ function IntegrationHealthTable({
1599
+ integrations,
1600
+ className,
1601
+ title = "Integration health",
1602
+ subtitle,
1603
+ description,
1604
+ actions,
1605
+ density = "compact",
1606
+ "aria-label": ariaLabel,
1607
+ "aria-labelledby": ariaLabelledBy,
1608
+ eyebrow: _eyebrow,
1609
+ status: _status,
1610
+ severity: _severity,
1611
+ loading: _loading,
1612
+ empty: _empty,
1613
+ error: _error,
1614
+ items: _items,
1615
+ metadata: _metadata,
1616
+ footer: _footer,
1617
+ ...sectionProps
1618
+ }) {
1619
+ const headingId = React7.useId();
1620
+ const incidentCount = integrations.reduce(
1621
+ (sum, integration) => sum + (integration.incidentsCount ?? 0),
1622
+ 0
1623
+ );
1624
+ const impairedCount = integrations.filter(
1625
+ (integration) => integration.status !== "healthy"
1626
+ ).length;
1627
+ const healthyCount = integrations.length - impairedCount;
1628
+ const rows = integrations.map((integration) => ({ ...integration }));
1629
+ const columns = [
1630
+ {
1631
+ key: "name",
1632
+ header: "Integration",
1633
+ width: "32%",
1634
+ render: (row) => /* @__PURE__ */ jsxs12("span", { className: "eth-admin-integration-health__name", children: [
1635
+ /* @__PURE__ */ jsx14("strong", { children: row.name }),
1636
+ /* @__PURE__ */ jsx14("span", { children: row.id })
1637
+ ] })
1638
+ },
1639
+ {
1640
+ key: "status",
1641
+ header: "Status",
1642
+ width: "9rem",
1643
+ render: (row) => /* @__PURE__ */ jsx14(Badge11, { severity: statusSeverity(row.status), children: statusLabel2(row.status) })
1644
+ },
1645
+ {
1646
+ key: "latencyMs",
1647
+ header: "Latency",
1648
+ width: "8rem",
1649
+ align: "end",
1650
+ render: (row) => /* @__PURE__ */ jsx14("span", { className: latencyMetricClassName(row.latencyMs), children: formatLatency(row.latencyMs) })
1651
+ },
1652
+ {
1653
+ key: "incidentsCount",
1654
+ header: "Incidents",
1655
+ width: "8rem",
1656
+ align: "end",
1657
+ render: (row) => /* @__PURE__ */ jsx14(Badge11, { severity: incidentSeverity(row.incidentsCount), children: formatIncidentCount(row.incidentsCount) })
1658
+ },
1659
+ {
1660
+ key: "lastCheckedAt",
1661
+ header: "Last checked",
1662
+ width: "10rem",
1663
+ render: (row) => /* @__PURE__ */ jsx14("span", { className: "eth-admin-integration-health__checked", children: row.lastCheckedAt ?? "Not checked" })
1664
+ }
1665
+ ];
1666
+ const supportingText = subtitle ?? description;
1667
+ return /* @__PURE__ */ jsxs12(
1668
+ "section",
1669
+ {
1670
+ ...sectionProps,
1671
+ "aria-label": ariaLabel,
1672
+ "aria-labelledby": ariaLabelledBy ?? (ariaLabel ? void 0 : headingId),
1673
+ className: ["eth-admin-integration-health", className].filter(Boolean).join(" "),
1674
+ "data-eth-component": "IntegrationHealthTable",
1675
+ children: [
1676
+ /* @__PURE__ */ jsxs12("header", { children: [
1677
+ /* @__PURE__ */ jsxs12("div", { className: "eth-admin-integration-health__heading", children: [
1678
+ /* @__PURE__ */ jsx14("h3", { id: headingId, children: title }),
1679
+ supportingText ? /* @__PURE__ */ jsx14("p", { children: supportingText }) : null
1680
+ ] }),
1681
+ /* @__PURE__ */ jsxs12("div", { className: "eth-admin-integration-health__header-actions", children: [
1682
+ /* @__PURE__ */ jsxs12(
1683
+ "div",
1684
+ {
1685
+ className: "eth-admin-integration-health__summary",
1686
+ "aria-label": summaryLabel(rows.length, incidentCount, healthyCount),
1687
+ children: [
1688
+ /* @__PURE__ */ jsx14(Badge11, { severity: !rows.length ? "neutral" : impairedCount ? "warning" : "success", children: formatHealthySummary(rows.length, healthyCount) }),
1689
+ /* @__PURE__ */ jsxs12(Badge11, { severity: incidentCount ? "danger" : "neutral", children: [
1690
+ incidentCount,
1691
+ " ",
1692
+ incidentCount === 1 ? "incident" : "incidents"
1693
+ ] })
1694
+ ]
1695
+ }
1696
+ ),
1697
+ /* @__PURE__ */ jsx14(ActionGroup5, { actions })
1698
+ ] })
1699
+ ] }),
1700
+ /* @__PURE__ */ jsx14(
1701
+ DataTable7,
1702
+ {
1703
+ rows,
1704
+ columns,
1705
+ density,
1706
+ rowKey: "id",
1707
+ className: "eth-admin-integration-health__table",
1708
+ emptyState: /* @__PURE__ */ jsx14("p", { className: "eth-admin-integration-health__empty", children: "No integrations are reporting." })
1709
+ }
1710
+ )
1711
+ ]
1712
+ }
1713
+ );
1714
+ }
1715
+ function statusSeverity(status) {
1716
+ if (status === "healthy") return "success";
1717
+ if (status === "degraded") return "warning";
1718
+ return "danger";
1719
+ }
1720
+ function statusLabel2(status) {
1721
+ if (status === "down") return "Down";
1722
+ if (status === "degraded") return "Degraded";
1723
+ return "Healthy";
1724
+ }
1725
+ function latencySeverity(latencyMs) {
1726
+ if (latencyMs === void 0) return null;
1727
+ if (latencyMs >= 1e3) return "danger";
1728
+ if (latencyMs >= 500) return "warning";
1729
+ return null;
1730
+ }
1731
+ function latencyMetricClassName(latencyMs) {
1732
+ const severity = latencySeverity(latencyMs);
1733
+ return [
1734
+ "eth-admin-integration-health__metric",
1735
+ severity ? `eth-admin-integration-health__metric--${severity}` : void 0
1736
+ ].filter(Boolean).join(" ");
1737
+ }
1738
+ function incidentSeverity(count) {
1739
+ if (!count) return "success";
1740
+ return count > 1 ? "danger" : "warning";
1741
+ }
1742
+ function formatLatency(latencyMs) {
1743
+ return latencyMs === void 0 ? "n/a" : `${latencyMs} ms`;
1744
+ }
1745
+ function formatIncidentCount(count) {
1746
+ return String(count ?? 0);
1747
+ }
1748
+ function formatHealthySummary(total, healthyCount) {
1749
+ return total ? `${healthyCount}/${total} healthy` : "0 integrations";
1750
+ }
1751
+ function summaryLabel(total, incidentCount, healthyCount) {
1752
+ if (!total) return `No integrations reporting, ${incidentCount} open incidents`;
1753
+ return `${healthyCount} of ${total} integrations healthy, ${incidentCount} open ${incidentCount === 1 ? "incident" : "incidents"}`;
1754
+ }
1755
+
1756
+ // src/components/AdminHealthTemplate.tsx
1757
+ import { jsx as jsx15 } from "react/jsx-runtime";
1758
+ function AdminHealthTemplate({ services, incidents, metrics, ...props }) {
1759
+ return /* @__PURE__ */ jsx15(
1760
+ SystemHealthDashboard,
1761
+ {
1762
+ ...props,
1763
+ services: services ?? [
1764
+ { id: "api", name: "API Gateway", status: "healthy", latencyMs: 42 },
1765
+ { id: "workers", name: "Worker fleet", status: "degraded", latencyMs: 110, incidentsCount: 1 },
1766
+ { id: "storage", name: "Storage", status: "healthy", latencyMs: 24 }
1767
+ ],
1768
+ incidents,
1769
+ metrics,
1770
+ "data-eth-component": "AdminHealthTemplate"
1771
+ }
1772
+ );
1773
+ }
1774
+
1775
+ // src/index.tsx
1776
+ var AdminComponentNames = [
1777
+ "AdminShell",
1778
+ "SystemHealthDashboard",
1779
+ "ServiceStatusCard",
1780
+ "WorkerPoolPanel",
1781
+ "JobQueuePanel",
1782
+ "QueueDepthChart",
1783
+ "RuntimeLogViewer",
1784
+ "AuditLogViewer",
1785
+ "PolicyConfigPanel",
1786
+ "BillingUsagePanel",
1787
+ "TenantSettingsPanel",
1788
+ "FeatureFlagPanel",
1789
+ "RateLimitPanel",
1790
+ "IntegrationHealthTable",
1791
+ "AdminHealthTemplate"
1792
+ ];
1793
+ export {
1794
+ AdminComponentNames,
1795
+ AdminHealthTemplate,
1796
+ AdminShell,
1797
+ AuditLogViewer,
1798
+ BillingUsagePanel,
1799
+ FeatureFlagPanel,
1800
+ IntegrationHealthTable,
1801
+ JobQueuePanel,
1802
+ PolicyConfigPanel,
1803
+ QueueDepthChart,
1804
+ RateLimitPanel,
1805
+ RuntimeLogViewer,
1806
+ ServiceStatusCard,
1807
+ SystemHealthDashboard,
1808
+ TenantSettingsPanel,
1809
+ WorkerPoolPanel,
1810
+ severityForStatus
1811
+ };
1812
+ //# sourceMappingURL=index.js.map