@flanksource/clicky-ui 0.3.37 → 0.3.39

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 (54) hide show
  1. package/README.md +36 -0
  2. package/dist/data/TimeseriesCoreBars.cjs +10 -36
  3. package/dist/data/TimeseriesCoreBars.cjs.map +1 -1
  4. package/dist/data/TimeseriesCoreBars.d.ts +1 -1
  5. package/dist/data/TimeseriesCoreBars.d.ts.map +1 -1
  6. package/dist/data/TimeseriesCoreBars.js +8 -34
  7. package/dist/data/TimeseriesCoreBars.js.map +1 -1
  8. package/dist/data/TimeseriesGauge.cjs +12 -36
  9. package/dist/data/TimeseriesGauge.cjs.map +1 -1
  10. package/dist/data/TimeseriesGauge.d.ts +14 -4
  11. package/dist/data/TimeseriesGauge.d.ts.map +1 -1
  12. package/dist/data/TimeseriesGauge.js +10 -34
  13. package/dist/data/TimeseriesGauge.js.map +1 -1
  14. package/dist/data/TimeseriesPanel.cjs +82 -39
  15. package/dist/data/TimeseriesPanel.cjs.map +1 -1
  16. package/dist/data/TimeseriesPanel.d.ts +1 -1
  17. package/dist/data/TimeseriesPanel.d.ts.map +1 -1
  18. package/dist/data/TimeseriesPanel.js +82 -39
  19. package/dist/data/TimeseriesPanel.js.map +1 -1
  20. package/dist/data/TimeseriesPanel.model.cjs +1 -0
  21. package/dist/data/TimeseriesPanel.model.cjs.map +1 -1
  22. package/dist/data/TimeseriesPanel.model.d.ts +38 -2
  23. package/dist/data/TimeseriesPanel.model.d.ts.map +1 -1
  24. package/dist/data/TimeseriesPanel.model.js +1 -0
  25. package/dist/data/TimeseriesPanel.model.js.map +1 -1
  26. package/dist/data/WorkloadCard.cjs +67 -31
  27. package/dist/data/WorkloadCard.cjs.map +1 -1
  28. package/dist/data/WorkloadCard.d.ts +19 -6
  29. package/dist/data/WorkloadCard.d.ts.map +1 -1
  30. package/dist/data/WorkloadCard.js +69 -33
  31. package/dist/data/WorkloadCard.js.map +1 -1
  32. package/dist/data/WorkloadCard.model.cjs +16 -10
  33. package/dist/data/WorkloadCard.model.cjs.map +1 -1
  34. package/dist/data/WorkloadCard.model.d.ts +29 -1
  35. package/dist/data/WorkloadCard.model.d.ts.map +1 -1
  36. package/dist/data/WorkloadCard.model.js +17 -11
  37. package/dist/data/WorkloadCard.model.js.map +1 -1
  38. package/dist/data/timeseries-query.cjs +51 -0
  39. package/dist/data/timeseries-query.cjs.map +1 -0
  40. package/dist/data/timeseries-query.d.ts +31 -0
  41. package/dist/data/timeseries-query.d.ts.map +1 -0
  42. package/dist/data/timeseries-query.js +51 -0
  43. package/dist/data/timeseries-query.js.map +1 -0
  44. package/dist/data/version-info.cjs +3 -3
  45. package/dist/data/version-info.js +3 -3
  46. package/dist/data.cjs +1 -0
  47. package/dist/data.cjs.map +1 -1
  48. package/dist/data.d.ts +2 -2
  49. package/dist/data.d.ts.map +1 -1
  50. package/dist/data.js +3 -2
  51. package/dist/index.cjs +1 -0
  52. package/dist/index.cjs.map +1 -1
  53. package/dist/index.js +2 -1
  54. package/package.json +1 -1
@@ -1,12 +1,20 @@
1
+ import { ReactNode } from 'react';
1
2
  import { TimeseriesResponse } from './TimeseriesPanel';
2
3
  import { WorkloadCardMetrics, WorkloadCardWorkload } from './WorkloadCard.model';
3
4
  export interface WorkloadCardProps {
4
5
  workload: WorkloadCardWorkload;
6
+ /**
7
+ * CPU/memory/disk series. Each is requested as `baseUrl + id` via `fetcher`,
8
+ * or loaded by its own `load` function (then `id` is the cache key and must
9
+ * be unique per data source).
10
+ */
5
11
  metrics: WorkloadCardMetrics;
6
12
  baseUrl?: string;
7
13
  range?: string;
8
14
  refreshMs?: number;
9
15
  expandable?: boolean;
16
+ /** Controls rendered beside status and history, owned by the host domain. */
17
+ headerActions?: ReactNode;
10
18
  size?: WorkloadCardSize;
11
19
  variant?: WorkloadCardVariant;
12
20
  fetcher?: (url: string) => Promise<TimeseriesResponse>;
@@ -15,11 +23,16 @@ export interface WorkloadCardProps {
15
23
  export type WorkloadCardSize = "sm" | "md" | "lg";
16
24
  export type WorkloadCardVariant = "default" | "outline" | "subtle" | "elevated" | "ghost";
17
25
  /**
18
- * WorkloadCard is a compact resource card for Kubernetes-style workloads. It
19
- * shows workload identity, status, replica/age metadata, and CPU/memory/disk
20
- * utilization backed by the existing time-series primitives: CPU and memory as
21
- * core-style bars (memory at one bar per GB) and disk as a linear progress bar.
22
- * Callers own the metric ids; the card only composes the supplied series.
26
+ * WorkloadCard is a compact resource card for a workload: a Kubernetes
27
+ * resource (`kind`) or anything else (a free-form `type`, `icon` and
28
+ * `metadata`). It shows identity, status, subtitle metadata, and
29
+ * CPU/memory/disk utilization backed by the existing time-series primitives:
30
+ * CPU and memory as core-style bars (memory at one bar per GiB) and disk as a
31
+ * linear progress bar. Callers own the metric series (URL ids or `load`
32
+ * functions); the card only composes them.
33
+ *
34
+ * Like the timeseries widgets it renders, it needs a react-query
35
+ * `QueryClientProvider` above it.
23
36
  */
24
- export declare function WorkloadCard({ workload, metrics, baseUrl, range, refreshMs, expandable, size, variant, fetcher, className, }: WorkloadCardProps): import("react").JSX.Element;
37
+ export declare function WorkloadCard({ workload, metrics, baseUrl, range, refreshMs, expandable, headerActions, size, variant, fetcher, className, }: WorkloadCardProps): import("react").JSX.Element;
25
38
  //# sourceMappingURL=WorkloadCard.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkloadCard.d.ts","sourceRoot":"","sources":["../../src/data/WorkloadCard.tsx"],"names":[],"mappings":"AAeA,OAAO,EAEL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAOL,KAAK,mBAAmB,EAExB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,OAAO,EAAE,mBAAmB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,SAAS,GACT,QAAQ,GACR,UAAU,GACV,OAAO,CAAC;AAuJZ;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,OAAO,EACP,KAAY,EACZ,SAAgB,EAChB,UAAiB,EACjB,IAAW,EACX,OAAmB,EACnB,OAAO,EACP,SAAS,GACV,EAAE,iBAAiB,+BAwOnB"}
1
+ {"version":3,"file":"WorkloadCard.d.ts","sourceRoot":"","sources":["../../src/data/WorkloadCard.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAe1D,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EAOL,KAAK,mBAAmB,EAExB,KAAK,oBAAoB,EAC1B,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,oBAAoB,CAAC;IAC/B;;;;OAIG;IACH,OAAO,EAAE,mBAAmB,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAC3B,SAAS,GACT,SAAS,GACT,QAAQ,GACR,UAAU,GACV,OAAO,CAAC;AA2MZ;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,OAAO,EACP,OAAO,EACP,KAAY,EACZ,SAAgB,EAChB,UAAiB,EACjB,aAAa,EACb,IAAW,EACX,OAAmB,EACnB,OAAO,EACP,SAAS,GACV,EAAE,iBAAiB,+BA2OnB"}
@@ -1,4 +1,4 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { useState, useMemo } from "react";
3
3
  import { K8SPod, K8SStatefulset, K8SDeployment, K8SIngress, K8SService } from "@flanksource/icons/mi";
4
4
  import { cn } from "../lib/utils.js";
@@ -9,7 +9,8 @@ import { Icon } from "./Icon.js";
9
9
  import { TimeseriesCoreBars } from "./TimeseriesCoreBars.js";
10
10
  import { TimeseriesGauge } from "./TimeseriesGauge.js";
11
11
  import { TimeseriesPanel } from "./TimeseriesPanel.js";
12
- import { workloadKindLabel, workloadStatusLabel, formatReplicaCounts, workloadStatusTone } from "./WorkloadCard.model.js";
12
+ import { workloadTypeLabel, workloadStatusLabel, workloadStatusTone, formatReplicaCounts } from "./WorkloadCard.model.js";
13
+ import { UiBox } from "../icons/components/UiBox.js";
13
14
  import { UiChip } from "../icons/components/UiChip.js";
14
15
  import { UiMemoryStick } from "../icons/components/UiMemoryStick.js";
15
16
  import { UiHardDrive } from "../icons/components/UiHardDrive.js";
@@ -77,7 +78,7 @@ function metricGridClass(count) {
77
78
  if (count === 2) return "grid-cols-2";
78
79
  return "grid-cols-3";
79
80
  }
80
- const MEMORY_BAR_UNIT = { perBar: 1024 ** 3, label: "GB", barLabel: "GB" };
81
+ const MEMORY_BAR_UNIT = { perBar: 1024 ** 3, label: "GiB", barLabel: "GiB" };
81
82
  function WorkloadIcon({
82
83
  icon,
83
84
  title,
@@ -90,36 +91,69 @@ function WorkloadIcon({
90
91
  }
91
92
  function metricSeries(metric) {
92
93
  const series = [
93
- {
94
- id: metric.value.id,
95
- label: metric.valueLabel ?? "usage",
96
- ...metric.value.transform ? { transform: metric.value.transform } : {}
97
- }
94
+ { ...metric.value, label: metric.valueLabel ?? "usage" }
98
95
  ];
99
96
  if (typeof metric.max === "object") {
100
- series.push({
101
- id: metric.max.id,
102
- label: metric.maxLabel ?? "limit",
103
- ...metric.max.transform ? { transform: metric.max.transform } : {}
104
- });
97
+ series.push({ ...metric.max, label: metric.maxLabel ?? "limit" });
105
98
  }
106
99
  return series;
107
100
  }
101
+ function metricReferenceLines(metric) {
102
+ if (typeof metric.max !== "number") return [];
103
+ return [{ value: metric.max, label: metric.maxLabel ?? "capacity" }];
104
+ }
108
105
  function hasMetric(metrics) {
109
106
  return Boolean(metrics.cpu || metrics.memory || metrics.disk);
110
107
  }
111
- function HeaderMeta({ children }) {
112
- return /* @__PURE__ */ jsxs("span", { className: "inline-flex min-w-0 items-center gap-1", children: [
113
- /* @__PURE__ */ jsx(
108
+ function HeaderMeta({
109
+ separator,
110
+ title,
111
+ children
112
+ }) {
113
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex min-w-0 items-center gap-1", title, children: [
114
+ separator ? /* @__PURE__ */ jsx(
114
115
  "span",
115
116
  {
116
117
  className: "h-1 w-1 shrink-0 rounded-full bg-muted-foreground/40",
117
118
  "aria-hidden": true
118
119
  }
119
- ),
120
+ ) : null,
120
121
  /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children })
121
122
  ] });
122
123
  }
124
+ function headerMetaItems(workload, typeLabel) {
125
+ var _a;
126
+ const items = [];
127
+ if (typeLabel) items.push({ key: "type", content: typeLabel });
128
+ if (workload.namespace) {
129
+ items.push({ key: "namespace", content: workload.namespace });
130
+ }
131
+ if (workload.role) items.push({ key: "role", content: workload.role });
132
+ const replicaText = formatReplicaCounts(workload.replicas);
133
+ if (replicaText) items.push({ key: "replicas", content: replicaText });
134
+ if (workload.createdAt !== void 0) {
135
+ items.push({
136
+ key: "age",
137
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
138
+ "age ",
139
+ /* @__PURE__ */ jsx(Timestamp, { value: workload.createdAt, format: "relative" })
140
+ ] })
141
+ });
142
+ }
143
+ (_a = workload.metadata) == null ? void 0 : _a.forEach((item, i) => {
144
+ const plain = typeof item.value === "string" || typeof item.value === "number";
145
+ items.push({
146
+ key: `meta-${i}`,
147
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
148
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70", children: item.label }),
149
+ " ",
150
+ item.value
151
+ ] }),
152
+ title: plain ? `${item.label}: ${item.value}` : item.label
153
+ });
154
+ });
155
+ return items;
156
+ }
123
157
  function WorkloadCard({
124
158
  workload,
125
159
  metrics,
@@ -127,6 +161,7 @@ function WorkloadCard({
127
161
  range = "1h",
128
162
  refreshMs = 5e3,
129
163
  expandable = true,
164
+ headerActions,
130
165
  size = "md",
131
166
  variant = "default",
132
167
  fetcher,
@@ -134,10 +169,10 @@ function WorkloadCard({
134
169
  }) {
135
170
  var _a;
136
171
  const [expanded, setExpanded] = useState(false);
137
- const kindLabel = workloadKindLabel(workload.kind);
172
+ const typeLabel = workloadTypeLabel(workload);
138
173
  const statusLabel = workloadStatusLabel(workload.status);
139
- const replicaText = formatReplicaCounts(workload.replicas);
140
- const workloadIcon = workload.icon ?? KIND_ICONS[workload.kind];
174
+ const metaItems = headerMetaItems(workload, typeLabel);
175
+ const workloadIcon = workload.icon ?? (workload.kind ? KIND_ICONS[workload.kind] : UiBox);
141
176
  const sizeClasses = SIZE_CLASSES[size];
142
177
  const panelMetrics = useMemo(() => {
143
178
  const list = [];
@@ -204,7 +239,7 @@ function WorkloadCard({
204
239
  WorkloadIcon,
205
240
  {
206
241
  icon: workloadIcon,
207
- title: kindLabel,
242
+ title: typeLabel ?? workload.name,
208
243
  className: cn("shrink-0 text-muted-foreground", sizeClasses.icon)
209
244
  }
210
245
  ),
@@ -219,25 +254,24 @@ function WorkloadCard({
219
254
  ]
220
255
  }
221
256
  ),
222
- /* @__PURE__ */ jsxs(
257
+ metaItems.length > 0 ? /* @__PURE__ */ jsx(
223
258
  "div",
224
259
  {
225
260
  className: cn(
226
261
  "flex flex-wrap items-center text-muted-foreground",
227
262
  sizeClasses.meta
228
263
  ),
229
- children: [
230
- /* @__PURE__ */ jsx("span", { children: kindLabel }),
231
- workload.namespace ? /* @__PURE__ */ jsx(HeaderMeta, { children: workload.namespace }) : null,
232
- workload.role ? /* @__PURE__ */ jsx(HeaderMeta, { children: workload.role }) : null,
233
- replicaText ? /* @__PURE__ */ jsx(HeaderMeta, { children: replicaText }) : null,
234
- workload.createdAt !== void 0 ? /* @__PURE__ */ jsxs(HeaderMeta, { children: [
235
- "age ",
236
- /* @__PURE__ */ jsx(Timestamp, { value: workload.createdAt, format: "relative" })
237
- ] }) : null
238
- ]
264
+ children: metaItems.map((item, i) => /* @__PURE__ */ jsx(
265
+ HeaderMeta,
266
+ {
267
+ separator: i > 0,
268
+ title: item.title,
269
+ children: item.content
270
+ },
271
+ item.key
272
+ ))
239
273
  }
240
- )
274
+ ) : null
241
275
  ] }),
242
276
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
243
277
  statusLabel ? /* @__PURE__ */ jsx("span", { title: (_a = workload.status) == null ? void 0 : _a.message, children: /* @__PURE__ */ jsx(
@@ -249,6 +283,7 @@ function WorkloadCard({
249
283
  children: statusLabel
250
284
  }
251
285
  ) }) : null,
286
+ headerActions,
252
287
  canExpand ? /* @__PURE__ */ jsx(
253
288
  "button",
254
289
  {
@@ -353,6 +388,7 @@ function WorkloadCard({
353
388
  title,
354
389
  icon,
355
390
  series: metricSeries(metric),
391
+ referenceLines: metricReferenceLines(metric),
356
392
  ...unit ? { unit } : {},
357
393
  ...metricProps,
358
394
  expandable: false
@@ -1 +1 @@
1
- {"version":3,"file":"WorkloadCard.js","sources":["../../src/data/WorkloadCard.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport {\n K8SDeployment,\n K8SIngress,\n K8SPod,\n K8SService,\n K8SStatefulset,\n} from \"@flanksource/icons/mi\";\nimport { cn } from \"../lib/utils\";\nimport { Modal } from \"../overlay/Modal\";\nimport { Badge } from \"./Badge\";\nimport { Timestamp } from \"./cells/Timestamp\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { TimeseriesCoreBars } from \"./TimeseriesCoreBars\";\nimport { TimeseriesGauge } from \"./TimeseriesGauge\";\nimport {\n TimeseriesPanel,\n type TimeseriesResponse,\n type TimeseriesSeries,\n} from \"./TimeseriesPanel\";\nimport { UiChip, UiFullscreen, UiHardDrive, UiMemoryStick } from \"../icons\";\nimport {\n formatReplicaCounts,\n workloadKindLabel,\n workloadStatusLabel,\n workloadStatusTone,\n type WorkloadCardIcon,\n type WorkloadCardKind,\n type WorkloadCardMetrics,\n type WorkloadCardResourceMetric,\n type WorkloadCardWorkload,\n} from \"./WorkloadCard.model\";\n\nexport interface WorkloadCardProps {\n workload: WorkloadCardWorkload;\n metrics: WorkloadCardMetrics;\n baseUrl?: string;\n range?: string;\n refreshMs?: number;\n expandable?: boolean;\n size?: WorkloadCardSize;\n variant?: WorkloadCardVariant;\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nexport type WorkloadCardSize = \"sm\" | \"md\" | \"lg\";\nexport type WorkloadCardVariant =\n | \"default\"\n | \"outline\"\n | \"subtle\"\n | \"elevated\"\n | \"ghost\";\n\ntype MetricKey = keyof WorkloadCardMetrics;\n\ntype PanelMetric = {\n key: MetricKey;\n title: string;\n unit?: string;\n icon: WorkloadCardIcon;\n metric: WorkloadCardResourceMetric;\n};\n\nconst KIND_ICONS: Record<WorkloadCardKind, StaticIconComponent> = {\n service: K8SService,\n ingress: K8SIngress,\n deployment: K8SDeployment,\n statefulset: K8SStatefulset,\n pod: K8SPod,\n};\n\ntype WorkloadCardSizeClasses = {\n root: string;\n header: string;\n titleGap: string;\n icon: string;\n name: string;\n meta: string;\n statusSize: \"xxs\" | \"xs\" | \"sm\";\n action: string;\n actionIcon: number;\n metricGrid: string;\n metricVariant: \"default\" | \"cell\";\n empty: string;\n};\n\nconst SIZE_CLASSES: Record<WorkloadCardSize, WorkloadCardSizeClasses> = {\n sm: {\n root: \"p-2\",\n header: \"mb-2 gap-2\",\n titleGap: \"gap-1\",\n icon: \"h-3.5 w-3.5\",\n name: \"text-xs\",\n meta: \"mt-0.5 gap-x-1.5 gap-y-0.5 text-[11px] leading-4\",\n statusSize: \"xxs\",\n action: \"h-5 w-5\",\n actionIcon: 12,\n metricGrid: \"grid-cols-1 gap-1.5\",\n metricVariant: \"cell\",\n empty: \"px-2 py-3 text-[11px]\",\n },\n md: {\n root: \"p-3\",\n header: \"mb-3 gap-2\",\n titleGap: \"gap-1.5\",\n icon: \"h-4 w-4\",\n name: \"text-sm\",\n meta: \"mt-0.5 gap-x-2 gap-y-0.5 text-xs\",\n statusSize: \"xs\",\n action: \"h-6 w-6\",\n actionIcon: 14,\n metricGrid: \"gap-3\",\n metricVariant: \"default\",\n empty: \"px-3 py-4 text-xs\",\n },\n lg: {\n root: \"p-4\",\n header: \"mb-4 gap-3\",\n titleGap: \"gap-2\",\n icon: \"h-5 w-5\",\n name: \"text-base\",\n meta: \"mt-1 gap-x-2 gap-y-1 text-xs\",\n statusSize: \"sm\",\n action: \"h-7 w-7\",\n actionIcon: 16,\n metricGrid: \"gap-4\",\n metricVariant: \"default\",\n empty: \"px-4 py-6 text-sm\",\n },\n};\n\nconst VARIANT_CLASSES: Record<WorkloadCardVariant, string> = {\n default: \"border-border bg-card text-card-foreground\",\n outline: \"border-border bg-background text-foreground\",\n subtle: \"border-transparent bg-muted/35 text-foreground\",\n elevated: \"border-border/70 bg-card text-card-foreground shadow-sm\",\n ghost: \"border-transparent bg-transparent text-foreground\",\n};\n\nfunction metricGridClass(count: number): string {\n if (count <= 1) return \"grid-cols-1\";\n if (count === 2) return \"grid-cols-2\";\n return \"grid-cols-3\";\n}\n\n// Memory renders as cores-style bars at one bar per gigabyte, matching CPU; bytes\n// are fed raw and TimeseriesCoreBars divides by perBar so captions read \"x GB\".\nconst MEMORY_BAR_UNIT = { perBar: 1024 ** 3, label: \"GB\", barLabel: \"GB\" };\n\nfunction WorkloadIcon({\n icon,\n title,\n className,\n}: {\n icon: WorkloadCardIcon;\n title: string;\n className?: string;\n}) {\n if (typeof icon === \"string\") {\n return (\n <Icon name={icon} title={title} {...(className ? { className } : {})} />\n );\n }\n return (\n <Icon icon={icon} title={title} {...(className ? { className } : {})} />\n );\n}\n\nfunction metricSeries(metric: WorkloadCardResourceMetric): TimeseriesSeries[] {\n const series: TimeseriesSeries[] = [\n {\n id: metric.value.id,\n label: metric.valueLabel ?? \"usage\",\n ...(metric.value.transform ? { transform: metric.value.transform } : {}),\n },\n ];\n if (typeof metric.max === \"object\") {\n series.push({\n id: metric.max.id,\n label: metric.maxLabel ?? \"limit\",\n ...(metric.max.transform ? { transform: metric.max.transform } : {}),\n });\n }\n return series;\n}\n\nfunction hasMetric(metrics: WorkloadCardMetrics): boolean {\n return Boolean(metrics.cpu || metrics.memory || metrics.disk);\n}\n\nfunction HeaderMeta({ children }: { children: ReactNode }) {\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1\">\n <span\n className=\"h-1 w-1 shrink-0 rounded-full bg-muted-foreground/40\"\n aria-hidden\n />\n <span className=\"min-w-0 truncate\">{children}</span>\n </span>\n );\n}\n\n/**\n * WorkloadCard is a compact resource card for Kubernetes-style workloads. It\n * shows workload identity, status, replica/age metadata, and CPU/memory/disk\n * utilization backed by the existing time-series primitives: CPU and memory as\n * core-style bars (memory at one bar per GB) and disk as a linear progress bar.\n * Callers own the metric ids; the card only composes the supplied series.\n */\nexport function WorkloadCard({\n workload,\n metrics,\n baseUrl,\n range = \"1h\",\n refreshMs = 5000,\n expandable = true,\n size = \"md\",\n variant = \"default\",\n fetcher,\n className,\n}: WorkloadCardProps) {\n const [expanded, setExpanded] = useState(false);\n const kindLabel = workloadKindLabel(workload.kind);\n const statusLabel = workloadStatusLabel(workload.status);\n const replicaText = formatReplicaCounts(workload.replicas);\n const workloadIcon = workload.icon ?? KIND_ICONS[workload.kind];\n const sizeClasses = SIZE_CLASSES[size];\n\n const panelMetrics = useMemo(() => {\n const list: PanelMetric[] = [];\n if (metrics.cpu) {\n list.push({\n key: \"cpu\",\n title: metrics.cpu.title ?? \"CPU\",\n unit: metrics.cpu.unit ?? \"short\",\n icon: metrics.cpu.icon ?? UiChip,\n metric: metrics.cpu,\n });\n }\n if (metrics.memory) {\n list.push({\n key: \"memory\",\n title: metrics.memory.title ?? \"Memory\",\n unit: metrics.memory.unit ?? \"bytes\",\n icon: metrics.memory.icon ?? UiMemoryStick,\n metric: metrics.memory,\n });\n }\n if (metrics.disk) {\n list.push({\n key: \"disk\",\n title: metrics.disk.title ?? \"Disk\",\n unit: metrics.disk.unit ?? \"bytes\",\n icon: metrics.disk.icon ?? UiHardDrive,\n metric: metrics.disk,\n });\n }\n return list;\n }, [metrics]);\n\n const metricProps = {\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n range,\n refreshMs,\n ...(fetcher !== undefined ? { fetcher } : {}),\n };\n const canExpand = expandable && hasMetric(metrics);\n\n return (\n <div\n className={cn(\n \"w-full rounded-md border\",\n VARIANT_CLASSES[variant],\n sizeClasses.root,\n className,\n )}\n data-size={size}\n data-variant={variant}\n >\n <div\n className={cn(\"flex items-start justify-between\", sizeClasses.header)}\n >\n <div className=\"min-w-0\">\n <div\n className={cn(\"flex min-w-0 items-center\", sizeClasses.titleGap)}\n >\n <WorkloadIcon\n icon={workloadIcon}\n title={kindLabel}\n className={cn(\"shrink-0 text-muted-foreground\", sizeClasses.icon)}\n />\n <span\n className={cn(\"min-w-0 truncate font-medium\", sizeClasses.name)}\n title={workload.name}\n >\n {workload.name}\n </span>\n </div>\n <div\n className={cn(\n \"flex flex-wrap items-center text-muted-foreground\",\n sizeClasses.meta,\n )}\n >\n <span>{kindLabel}</span>\n {workload.namespace ? (\n <HeaderMeta>{workload.namespace}</HeaderMeta>\n ) : null}\n {workload.role ? <HeaderMeta>{workload.role}</HeaderMeta> : null}\n {replicaText ? <HeaderMeta>{replicaText}</HeaderMeta> : null}\n {workload.createdAt !== undefined ? (\n <HeaderMeta>\n age <Timestamp value={workload.createdAt} format=\"relative\" />\n </HeaderMeta>\n ) : null}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {statusLabel ? (\n <span title={workload.status?.message}>\n <Badge\n tone={workloadStatusTone(workload.status)}\n variant=\"soft\"\n size={sizeClasses.statusSize}\n >\n {statusLabel}\n </Badge>\n </span>\n ) : null}\n {canExpand ? (\n <button\n type=\"button\"\n aria-label={`Open ${workload.name} history`}\n title=\"History\"\n onClick={() => setExpanded(true)}\n className={cn(\n \"inline-flex items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\",\n sizeClasses.action,\n )}\n >\n <Icon\n icon={UiFullscreen}\n width={sizeClasses.actionIcon}\n height={sizeClasses.actionIcon}\n />\n </button>\n ) : null}\n </div>\n </div>\n\n {hasMetric(metrics) ? (\n <div\n className={cn(\n \"grid items-start gap-3\",\n sizeClasses.metricVariant === \"cell\"\n ? sizeClasses.metricGrid\n : cn(\n metricGridClass(panelMetrics.length),\n sizeClasses.metricGrid,\n ),\n )}\n >\n {metrics.cpu ? (\n <TimeseriesCoreBars\n title={metrics.cpu.title ?? \"CPU\"}\n icon={metrics.cpu.icon ?? UiChip}\n value={metrics.cpu.value}\n {...(metrics.cpu.max !== undefined\n ? { max: metrics.cpu.max }\n : {})}\n {...(metrics.cpu.thresholds\n ? { thresholds: metrics.cpu.thresholds }\n : {})}\n {...(metrics.cpu.orientation\n ? { orientation: metrics.cpu.orientation }\n : {})}\n {...(metrics.cpu.showValue !== undefined\n ? { showValue: metrics.cpu.showValue }\n : {})}\n variant={sizeClasses.metricVariant}\n {...metricProps}\n />\n ) : null}\n {metrics.memory ? (\n <TimeseriesCoreBars\n title={metrics.memory.title ?? \"Memory\"}\n icon={metrics.memory.icon ?? UiMemoryStick}\n value={metrics.memory.value}\n unit={MEMORY_BAR_UNIT}\n variant={sizeClasses.metricVariant}\n {...(metrics.memory.max !== undefined\n ? { max: metrics.memory.max }\n : {})}\n {...(metrics.memory.thresholds\n ? { thresholds: metrics.memory.thresholds }\n : {})}\n {...metricProps}\n />\n ) : null}\n {metrics.disk ? (\n <TimeseriesGauge\n title={metrics.disk.title ?? \"Disk\"}\n icon={metrics.disk.icon ?? UiHardDrive}\n value={metrics.disk.value}\n unit={metrics.disk.unit ?? \"bytes\"}\n shape=\"linear\"\n expandable={false}\n centerDisplay={metrics.disk.centerDisplay ?? \"value\"}\n variant={sizeClasses.metricVariant}\n {...(metrics.disk.max !== undefined\n ? { max: metrics.disk.max }\n : {})}\n {...(metrics.disk.thresholds\n ? { thresholds: metrics.disk.thresholds }\n : {})}\n {...metricProps}\n />\n ) : null}\n </div>\n ) : (\n <div\n className={cn(\n \"rounded-md border border-dashed border-border text-center text-muted-foreground\",\n sizeClasses.empty,\n )}\n >\n No metrics configured\n </div>\n )}\n\n <Modal\n open={expanded}\n onClose={() => setExpanded(false)}\n title={`${workload.name} history`}\n size=\"xl\"\n >\n <div className=\"flex flex-col gap-3\">\n {panelMetrics.map(({ key, title, unit, icon, metric }) => (\n <TimeseriesPanel\n key={key}\n title={title}\n icon={icon}\n series={metricSeries(metric)}\n {...(unit ? { unit } : {})}\n {...metricProps}\n expandable={false}\n />\n ))}\n </div>\n </Modal>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WorkloadCard.js","sources":["../../src/data/WorkloadCard.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport {\n K8SDeployment,\n K8SIngress,\n K8SPod,\n K8SService,\n K8SStatefulset,\n} from \"@flanksource/icons/mi\";\nimport { cn } from \"../lib/utils\";\nimport { Modal } from \"../overlay/Modal\";\nimport { Badge } from \"./Badge\";\nimport { Timestamp } from \"./cells/Timestamp\";\nimport { Icon, type StaticIconComponent } from \"./Icon\";\nimport { TimeseriesCoreBars } from \"./TimeseriesCoreBars\";\nimport { TimeseriesGauge } from \"./TimeseriesGauge\";\nimport {\n TimeseriesPanel,\n type TimeseriesReferenceLine,\n type TimeseriesResponse,\n type TimeseriesSeries,\n} from \"./TimeseriesPanel\";\nimport {\n UiBox,\n UiChip,\n UiFullscreen,\n UiHardDrive,\n UiMemoryStick,\n} from \"../icons\";\nimport {\n formatReplicaCounts,\n workloadStatusLabel,\n workloadStatusTone,\n workloadTypeLabel,\n type WorkloadCardIcon,\n type WorkloadCardKind,\n type WorkloadCardMetrics,\n type WorkloadCardResourceMetric,\n type WorkloadCardWorkload,\n} from \"./WorkloadCard.model\";\n\nexport interface WorkloadCardProps {\n workload: WorkloadCardWorkload;\n /**\n * CPU/memory/disk series. Each is requested as `baseUrl + id` via `fetcher`,\n * or loaded by its own `load` function (then `id` is the cache key and must\n * be unique per data source).\n */\n metrics: WorkloadCardMetrics;\n baseUrl?: string;\n range?: string;\n refreshMs?: number;\n expandable?: boolean;\n /** Controls rendered beside status and history, owned by the host domain. */\n headerActions?: ReactNode;\n size?: WorkloadCardSize;\n variant?: WorkloadCardVariant;\n fetcher?: (url: string) => Promise<TimeseriesResponse>;\n className?: string;\n}\n\nexport type WorkloadCardSize = \"sm\" | \"md\" | \"lg\";\nexport type WorkloadCardVariant =\n | \"default\"\n | \"outline\"\n | \"subtle\"\n | \"elevated\"\n | \"ghost\";\n\ntype MetricKey = keyof WorkloadCardMetrics;\n\ntype PanelMetric = {\n key: MetricKey;\n title: string;\n unit?: string;\n icon: WorkloadCardIcon;\n metric: WorkloadCardResourceMetric;\n};\n\nconst KIND_ICONS: Record<WorkloadCardKind, StaticIconComponent> = {\n service: K8SService,\n ingress: K8SIngress,\n deployment: K8SDeployment,\n statefulset: K8SStatefulset,\n pod: K8SPod,\n};\n\ntype WorkloadCardSizeClasses = {\n root: string;\n header: string;\n titleGap: string;\n icon: string;\n name: string;\n meta: string;\n statusSize: \"xxs\" | \"xs\" | \"sm\";\n action: string;\n actionIcon: number;\n metricGrid: string;\n metricVariant: \"default\" | \"cell\";\n empty: string;\n};\n\nconst SIZE_CLASSES: Record<WorkloadCardSize, WorkloadCardSizeClasses> = {\n sm: {\n root: \"p-2\",\n header: \"mb-2 gap-2\",\n titleGap: \"gap-1\",\n icon: \"h-3.5 w-3.5\",\n name: \"text-xs\",\n meta: \"mt-0.5 gap-x-1.5 gap-y-0.5 text-[11px] leading-4\",\n statusSize: \"xxs\",\n action: \"h-5 w-5\",\n actionIcon: 12,\n metricGrid: \"grid-cols-1 gap-1.5\",\n metricVariant: \"cell\",\n empty: \"px-2 py-3 text-[11px]\",\n },\n md: {\n root: \"p-3\",\n header: \"mb-3 gap-2\",\n titleGap: \"gap-1.5\",\n icon: \"h-4 w-4\",\n name: \"text-sm\",\n meta: \"mt-0.5 gap-x-2 gap-y-0.5 text-xs\",\n statusSize: \"xs\",\n action: \"h-6 w-6\",\n actionIcon: 14,\n metricGrid: \"gap-3\",\n metricVariant: \"default\",\n empty: \"px-3 py-4 text-xs\",\n },\n lg: {\n root: \"p-4\",\n header: \"mb-4 gap-3\",\n titleGap: \"gap-2\",\n icon: \"h-5 w-5\",\n name: \"text-base\",\n meta: \"mt-1 gap-x-2 gap-y-1 text-xs\",\n statusSize: \"sm\",\n action: \"h-7 w-7\",\n actionIcon: 16,\n metricGrid: \"gap-4\",\n metricVariant: \"default\",\n empty: \"px-4 py-6 text-sm\",\n },\n};\n\nconst VARIANT_CLASSES: Record<WorkloadCardVariant, string> = {\n default: \"border-border bg-card text-card-foreground\",\n outline: \"border-border bg-background text-foreground\",\n subtle: \"border-transparent bg-muted/35 text-foreground\",\n elevated: \"border-border/70 bg-card text-card-foreground shadow-sm\",\n ghost: \"border-transparent bg-transparent text-foreground\",\n};\n\nfunction metricGridClass(count: number): string {\n if (count <= 1) return \"grid-cols-1\";\n if (count === 2) return \"grid-cols-2\";\n return \"grid-cols-3\";\n}\n\n// Memory renders as cores-style bars at one bar per gibibyte, matching CPU; bytes\n// are fed raw and TimeseriesCoreBars divides by perBar so captions read \"x GiB\".\nconst MEMORY_BAR_UNIT = { perBar: 1024 ** 3, label: \"GiB\", barLabel: \"GiB\" };\n\nfunction WorkloadIcon({\n icon,\n title,\n className,\n}: {\n icon: WorkloadCardIcon;\n title: string;\n className?: string;\n}) {\n if (typeof icon === \"string\") {\n return (\n <Icon name={icon} title={title} {...(className ? { className } : {})} />\n );\n }\n return (\n <Icon icon={icon} title={title} {...(className ? { className } : {})} />\n );\n}\n\nfunction metricSeries(metric: WorkloadCardResourceMetric): TimeseriesSeries[] {\n const series: TimeseriesSeries[] = [\n { ...metric.value, label: metric.valueLabel ?? \"usage\" },\n ];\n if (typeof metric.max === \"object\") {\n series.push({ ...metric.max, label: metric.maxLabel ?? \"limit\" });\n }\n return series;\n}\n\n// A fixed numeric max has no series to plot, so the history chart draws it as\n// a flat capacity line instead.\nfunction metricReferenceLines(\n metric: WorkloadCardResourceMetric,\n): TimeseriesReferenceLine[] {\n if (typeof metric.max !== \"number\") return [];\n return [{ value: metric.max, label: metric.maxLabel ?? \"capacity\" }];\n}\n\nfunction hasMetric(metrics: WorkloadCardMetrics): boolean {\n return Boolean(metrics.cpu || metrics.memory || metrics.disk);\n}\n\ntype HeaderMetaItem = { key: string; content: ReactNode; title?: string };\n\nfunction HeaderMeta({\n separator,\n title,\n children,\n}: {\n separator: boolean;\n title?: string | undefined;\n children: ReactNode;\n}) {\n return (\n <span className=\"inline-flex min-w-0 items-center gap-1\" title={title}>\n {separator ? (\n <span\n className=\"h-1 w-1 shrink-0 rounded-full bg-muted-foreground/40\"\n aria-hidden\n />\n ) : null}\n <span className=\"min-w-0 truncate\">{children}</span>\n </span>\n );\n}\n\nfunction headerMetaItems(\n workload: WorkloadCardWorkload,\n typeLabel: string | undefined,\n): HeaderMetaItem[] {\n const items: HeaderMetaItem[] = [];\n if (typeLabel) items.push({ key: \"type\", content: typeLabel });\n if (workload.namespace) {\n items.push({ key: \"namespace\", content: workload.namespace });\n }\n if (workload.role) items.push({ key: \"role\", content: workload.role });\n const replicaText = formatReplicaCounts(workload.replicas);\n if (replicaText) items.push({ key: \"replicas\", content: replicaText });\n if (workload.createdAt !== undefined) {\n items.push({\n key: \"age\",\n content: (\n <>\n age <Timestamp value={workload.createdAt} format=\"relative\" />\n </>\n ),\n });\n }\n workload.metadata?.forEach((item, i) => {\n const plain =\n typeof item.value === \"string\" || typeof item.value === \"number\";\n items.push({\n key: `meta-${i}`,\n content: (\n <>\n <span className=\"text-muted-foreground/70\">{item.label}</span>{\" \"}\n {item.value}\n </>\n ),\n title: plain ? `${item.label}: ${item.value}` : item.label,\n });\n });\n return items;\n}\n\n/**\n * WorkloadCard is a compact resource card for a workload: a Kubernetes\n * resource (`kind`) or anything else (a free-form `type`, `icon` and\n * `metadata`). It shows identity, status, subtitle metadata, and\n * CPU/memory/disk utilization backed by the existing time-series primitives:\n * CPU and memory as core-style bars (memory at one bar per GiB) and disk as a\n * linear progress bar. Callers own the metric series (URL ids or `load`\n * functions); the card only composes them.\n *\n * Like the timeseries widgets it renders, it needs a react-query\n * `QueryClientProvider` above it.\n */\nexport function WorkloadCard({\n workload,\n metrics,\n baseUrl,\n range = \"1h\",\n refreshMs = 5000,\n expandable = true,\n headerActions,\n size = \"md\",\n variant = \"default\",\n fetcher,\n className,\n}: WorkloadCardProps) {\n const [expanded, setExpanded] = useState(false);\n const typeLabel = workloadTypeLabel(workload);\n const statusLabel = workloadStatusLabel(workload.status);\n const metaItems = headerMetaItems(workload, typeLabel);\n const workloadIcon =\n workload.icon ?? (workload.kind ? KIND_ICONS[workload.kind] : UiBox);\n const sizeClasses = SIZE_CLASSES[size];\n\n const panelMetrics = useMemo(() => {\n const list: PanelMetric[] = [];\n if (metrics.cpu) {\n list.push({\n key: \"cpu\",\n title: metrics.cpu.title ?? \"CPU\",\n unit: metrics.cpu.unit ?? \"short\",\n icon: metrics.cpu.icon ?? UiChip,\n metric: metrics.cpu,\n });\n }\n if (metrics.memory) {\n list.push({\n key: \"memory\",\n title: metrics.memory.title ?? \"Memory\",\n unit: metrics.memory.unit ?? \"bytes\",\n icon: metrics.memory.icon ?? UiMemoryStick,\n metric: metrics.memory,\n });\n }\n if (metrics.disk) {\n list.push({\n key: \"disk\",\n title: metrics.disk.title ?? \"Disk\",\n unit: metrics.disk.unit ?? \"bytes\",\n icon: metrics.disk.icon ?? UiHardDrive,\n metric: metrics.disk,\n });\n }\n return list;\n }, [metrics]);\n\n const metricProps = {\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n range,\n refreshMs,\n ...(fetcher !== undefined ? { fetcher } : {}),\n };\n const canExpand = expandable && hasMetric(metrics);\n\n return (\n <div\n className={cn(\n \"w-full rounded-md border\",\n VARIANT_CLASSES[variant],\n sizeClasses.root,\n className,\n )}\n data-size={size}\n data-variant={variant}\n >\n <div\n className={cn(\"flex items-start justify-between\", sizeClasses.header)}\n >\n <div className=\"min-w-0\">\n <div\n className={cn(\"flex min-w-0 items-center\", sizeClasses.titleGap)}\n >\n <WorkloadIcon\n icon={workloadIcon}\n title={typeLabel ?? workload.name}\n className={cn(\"shrink-0 text-muted-foreground\", sizeClasses.icon)}\n />\n <span\n className={cn(\"min-w-0 truncate font-medium\", sizeClasses.name)}\n title={workload.name}\n >\n {workload.name}\n </span>\n </div>\n {metaItems.length > 0 ? (\n <div\n className={cn(\n \"flex flex-wrap items-center text-muted-foreground\",\n sizeClasses.meta,\n )}\n >\n {metaItems.map((item, i) => (\n <HeaderMeta\n key={item.key}\n separator={i > 0}\n title={item.title}\n >\n {item.content}\n </HeaderMeta>\n ))}\n </div>\n ) : null}\n </div>\n <div className=\"flex shrink-0 items-center gap-1\">\n {statusLabel ? (\n <span title={workload.status?.message}>\n <Badge\n tone={workloadStatusTone(workload.status)}\n variant=\"soft\"\n size={sizeClasses.statusSize}\n >\n {statusLabel}\n </Badge>\n </span>\n ) : null}\n {headerActions}\n {canExpand ? (\n <button\n type=\"button\"\n aria-label={`Open ${workload.name} history`}\n title=\"History\"\n onClick={() => setExpanded(true)}\n className={cn(\n \"inline-flex items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground\",\n sizeClasses.action,\n )}\n >\n <Icon\n icon={UiFullscreen}\n width={sizeClasses.actionIcon}\n height={sizeClasses.actionIcon}\n />\n </button>\n ) : null}\n </div>\n </div>\n\n {hasMetric(metrics) ? (\n <div\n className={cn(\n \"grid items-start gap-3\",\n sizeClasses.metricVariant === \"cell\"\n ? sizeClasses.metricGrid\n : cn(\n metricGridClass(panelMetrics.length),\n sizeClasses.metricGrid,\n ),\n )}\n >\n {metrics.cpu ? (\n <TimeseriesCoreBars\n title={metrics.cpu.title ?? \"CPU\"}\n icon={metrics.cpu.icon ?? UiChip}\n value={metrics.cpu.value}\n {...(metrics.cpu.max !== undefined\n ? { max: metrics.cpu.max }\n : {})}\n {...(metrics.cpu.thresholds\n ? { thresholds: metrics.cpu.thresholds }\n : {})}\n {...(metrics.cpu.orientation\n ? { orientation: metrics.cpu.orientation }\n : {})}\n {...(metrics.cpu.showValue !== undefined\n ? { showValue: metrics.cpu.showValue }\n : {})}\n variant={sizeClasses.metricVariant}\n {...metricProps}\n />\n ) : null}\n {metrics.memory ? (\n <TimeseriesCoreBars\n title={metrics.memory.title ?? \"Memory\"}\n icon={metrics.memory.icon ?? UiMemoryStick}\n value={metrics.memory.value}\n unit={MEMORY_BAR_UNIT}\n variant={sizeClasses.metricVariant}\n {...(metrics.memory.max !== undefined\n ? { max: metrics.memory.max }\n : {})}\n {...(metrics.memory.thresholds\n ? { thresholds: metrics.memory.thresholds }\n : {})}\n {...metricProps}\n />\n ) : null}\n {metrics.disk ? (\n <TimeseriesGauge\n title={metrics.disk.title ?? \"Disk\"}\n icon={metrics.disk.icon ?? UiHardDrive}\n value={metrics.disk.value}\n unit={metrics.disk.unit ?? \"bytes\"}\n shape=\"linear\"\n expandable={false}\n centerDisplay={metrics.disk.centerDisplay ?? \"value\"}\n variant={sizeClasses.metricVariant}\n {...(metrics.disk.max !== undefined\n ? { max: metrics.disk.max }\n : {})}\n {...(metrics.disk.thresholds\n ? { thresholds: metrics.disk.thresholds }\n : {})}\n {...metricProps}\n />\n ) : null}\n </div>\n ) : (\n <div\n className={cn(\n \"rounded-md border border-dashed border-border text-center text-muted-foreground\",\n sizeClasses.empty,\n )}\n >\n No metrics configured\n </div>\n )}\n\n <Modal\n open={expanded}\n onClose={() => setExpanded(false)}\n title={`${workload.name} history`}\n size=\"xl\"\n >\n <div className=\"flex flex-col gap-3\">\n {panelMetrics.map(({ key, title, unit, icon, metric }) => (\n <TimeseriesPanel\n key={key}\n title={title}\n icon={icon}\n series={metricSeries(metric)}\n referenceLines={metricReferenceLines(metric)}\n {...(unit ? { unit } : {})}\n {...metricProps}\n expandable={false}\n />\n ))}\n </div>\n </Modal>\n </div>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AA8EA,MAAM,aAA4D;AAAA,EAChE,SAAS;AAAA,EACT,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,KAAK;AACP;AAiBA,MAAM,eAAkE;AAAA,EACtE,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,EAAA;AAAA,EAET,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,EAAA;AAAA,EAET,IAAI;AAAA,IACF,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,MAAM;AAAA,IACN,MAAM;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,OAAO;AAAA,EAAA;AAEX;AAEA,MAAM,kBAAuD;AAAA,EAC3D,SAAS;AAAA,EACT,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AACT;AAEA,SAAS,gBAAgB,OAAuB;AAC9C,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,UAAU,EAAG,QAAO;AACxB,SAAO;AACT;AAIA,MAAM,kBAAkB,EAAE,QAAQ,QAAQ,GAAG,OAAO,OAAO,UAAU,MAAA;AAErE,SAAS,aAAa;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,OAAO,SAAS,UAAU;AAC5B,WACE,oBAAC,MAAA,EAAK,MAAM,MAAM,OAAe,GAAI,YAAY,EAAE,UAAA,IAAc,CAAA,EAAC,CAAI;AAAA,EAE1E;AACA,SACE,oBAAC,MAAA,EAAK,MAAY,OAAe,GAAI,YAAY,EAAE,cAAc,CAAA,GAAK;AAE1E;AAEA,SAAS,aAAa,QAAwD;AAC5E,QAAM,SAA6B;AAAA,IACjC,EAAE,GAAG,OAAO,OAAO,OAAO,OAAO,cAAc,QAAA;AAAA,EAAQ;AAEzD,MAAI,OAAO,OAAO,QAAQ,UAAU;AAClC,WAAO,KAAK,EAAE,GAAG,OAAO,KAAK,OAAO,OAAO,YAAY,SAAS;AAAA,EAClE;AACA,SAAO;AACT;AAIA,SAAS,qBACP,QAC2B;AAC3B,MAAI,OAAO,OAAO,QAAQ,iBAAiB,CAAA;AAC3C,SAAO,CAAC,EAAE,OAAO,OAAO,KAAK,OAAO,OAAO,YAAY,YAAY;AACrE;AAEA,SAAS,UAAU,SAAuC;AACxD,SAAO,QAAQ,QAAQ,OAAO,QAAQ,UAAU,QAAQ,IAAI;AAC9D;AAIA,SAAS,WAAW;AAAA,EAClB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SACE,qBAAC,QAAA,EAAK,WAAU,0CAAyC,OACtD,UAAA;AAAA,IAAA,YACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,WAAU;AAAA,QACV,eAAW;AAAA,MAAA;AAAA,IAAA,IAEX;AAAA,IACJ,oBAAC,QAAA,EAAK,WAAU,oBAAoB,SAAA,CAAS;AAAA,EAAA,GAC/C;AAEJ;AAEA,SAAS,gBACP,UACA,WACkB;;AAClB,QAAM,QAA0B,CAAA;AAChC,MAAI,iBAAiB,KAAK,EAAE,KAAK,QAAQ,SAAS,WAAW;AAC7D,MAAI,SAAS,WAAW;AACtB,UAAM,KAAK,EAAE,KAAK,aAAa,SAAS,SAAS,WAAW;AAAA,EAC9D;AACA,MAAI,SAAS,KAAM,OAAM,KAAK,EAAE,KAAK,QAAQ,SAAS,SAAS,MAAM;AACrE,QAAM,cAAc,oBAAoB,SAAS,QAAQ;AACzD,MAAI,mBAAmB,KAAK,EAAE,KAAK,YAAY,SAAS,aAAa;AACrE,MAAI,SAAS,cAAc,QAAW;AACpC,UAAM,KAAK;AAAA,MACT,KAAK;AAAA,MACL,SACE,qBAAA,UAAA,EAAE,UAAA;AAAA,QAAA;AAAA,4BACK,WAAA,EAAU,OAAO,SAAS,WAAW,QAAO,WAAA,CAAW;AAAA,MAAA,EAAA,CAC9D;AAAA,IAAA,CAEH;AAAA,EACH;AACA,iBAAS,aAAT,mBAAmB,QAAQ,CAAC,MAAM,MAAM;AACtC,UAAM,QACJ,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,UAAU;AAC1D,UAAM,KAAK;AAAA,MACT,KAAK,QAAQ,CAAC;AAAA,MACd,SACE,qBAAA,UAAA,EACE,UAAA;AAAA,QAAA,oBAAC,QAAA,EAAK,WAAU,4BAA4B,UAAA,KAAK,OAAM;AAAA,QAAQ;AAAA,QAC9D,KAAK;AAAA,MAAA,GACR;AAAA,MAEF,OAAO,QAAQ,GAAG,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK,KAAK;AAAA,IAAA,CACtD;AAAA,EACH;AACA,SAAO;AACT;AAcO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,aAAa;AAAA,EACb;AAAA,EACA,OAAO;AAAA,EACP,UAAU;AAAA,EACV;AAAA,EACA;AACF,GAAsB;;AACpB,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,QAAM,YAAY,kBAAkB,QAAQ;AAC5C,QAAM,cAAc,oBAAoB,SAAS,MAAM;AACvD,QAAM,YAAY,gBAAgB,UAAU,SAAS;AACrD,QAAM,eACJ,SAAS,SAAS,SAAS,OAAO,WAAW,SAAS,IAAI,IAAI;AAChE,QAAM,cAAc,aAAa,IAAI;AAErC,QAAM,eAAe,QAAQ,MAAM;AACjC,UAAM,OAAsB,CAAA;AAC5B,QAAI,QAAQ,KAAK;AACf,WAAK,KAAK;AAAA,QACR,KAAK;AAAA,QACL,OAAO,QAAQ,IAAI,SAAS;AAAA,QAC5B,MAAM,QAAQ,IAAI,QAAQ;AAAA,QAC1B,MAAM,QAAQ,IAAI,QAAQ;AAAA,QAC1B,QAAQ,QAAQ;AAAA,MAAA,CACjB;AAAA,IACH;AACA,QAAI,QAAQ,QAAQ;AAClB,WAAK,KAAK;AAAA,QACR,KAAK;AAAA,QACL,OAAO,QAAQ,OAAO,SAAS;AAAA,QAC/B,MAAM,QAAQ,OAAO,QAAQ;AAAA,QAC7B,MAAM,QAAQ,OAAO,QAAQ;AAAA,QAC7B,QAAQ,QAAQ;AAAA,MAAA,CACjB;AAAA,IACH;AACA,QAAI,QAAQ,MAAM;AAChB,WAAK,KAAK;AAAA,QACR,KAAK;AAAA,QACL,OAAO,QAAQ,KAAK,SAAS;AAAA,QAC7B,MAAM,QAAQ,KAAK,QAAQ;AAAA,QAC3B,MAAM,QAAQ,KAAK,QAAQ;AAAA,QAC3B,QAAQ,QAAQ;AAAA,MAAA,CACjB;AAAA,IACH;AACA,WAAO;AAAA,EACT,GAAG,CAAC,OAAO,CAAC;AAEZ,QAAM,cAAc;AAAA,IAClB,GAAI,YAAY,SAAY,EAAE,QAAA,IAAY,CAAA;AAAA,IAC1C;AAAA,IACA;AAAA,IACA,GAAI,YAAY,SAAY,EAAE,YAAY,CAAA;AAAA,EAAC;AAE7C,QAAM,YAAY,cAAc,UAAU,OAAO;AAEjD,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,OAAO;AAAA,QACvB,YAAY;AAAA,QACZ;AAAA,MAAA;AAAA,MAEF,aAAW;AAAA,MACX,gBAAc;AAAA,MAEd,UAAA;AAAA,QAAA;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW,GAAG,oCAAoC,YAAY,MAAM;AAAA,YAEpE,UAAA;AAAA,cAAA,qBAAC,OAAA,EAAI,WAAU,WACb,UAAA;AAAA,gBAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAW,GAAG,6BAA6B,YAAY,QAAQ;AAAA,oBAE/D,UAAA;AAAA,sBAAA;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,MAAM;AAAA,0BACN,OAAO,aAAa,SAAS;AAAA,0BAC7B,WAAW,GAAG,kCAAkC,YAAY,IAAI;AAAA,wBAAA;AAAA,sBAAA;AAAA,sBAElE;AAAA,wBAAC;AAAA,wBAAA;AAAA,0BACC,WAAW,GAAG,gCAAgC,YAAY,IAAI;AAAA,0BAC9D,OAAO,SAAS;AAAA,0BAEf,UAAA,SAAS;AAAA,wBAAA;AAAA,sBAAA;AAAA,oBACZ;AAAA,kBAAA;AAAA,gBAAA;AAAA,gBAED,UAAU,SAAS,IAClB;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,WAAW;AAAA,sBACT;AAAA,sBACA,YAAY;AAAA,oBAAA;AAAA,oBAGb,UAAA,UAAU,IAAI,CAAC,MAAM,MACpB;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBAEC,WAAW,IAAI;AAAA,wBACf,OAAO,KAAK;AAAA,wBAEX,UAAA,KAAK;AAAA,sBAAA;AAAA,sBAJD,KAAK;AAAA,oBAAA,CAMb;AAAA,kBAAA;AAAA,gBAAA,IAED;AAAA,cAAA,GACN;AAAA,cACA,qBAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,gBAAA,cACC,oBAAC,QAAA,EAAK,QAAO,cAAS,WAAT,mBAAiB,SAC5B,UAAA;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAM,mBAAmB,SAAS,MAAM;AAAA,oBACxC,SAAQ;AAAA,oBACR,MAAM,YAAY;AAAA,oBAEjB,UAAA;AAAA,kBAAA;AAAA,gBAAA,GAEL,IACE;AAAA,gBACH;AAAA,gBACA,YACC;AAAA,kBAAC;AAAA,kBAAA;AAAA,oBACC,MAAK;AAAA,oBACL,cAAY,QAAQ,SAAS,IAAI;AAAA,oBACjC,OAAM;AAAA,oBACN,SAAS,MAAM,YAAY,IAAI;AAAA,oBAC/B,WAAW;AAAA,sBACT;AAAA,sBACA,YAAY;AAAA,oBAAA;AAAA,oBAGd,UAAA;AAAA,sBAAC;AAAA,sBAAA;AAAA,wBACC,MAAM;AAAA,wBACN,OAAO,YAAY;AAAA,wBACnB,QAAQ,YAAY;AAAA,sBAAA;AAAA,oBAAA;AAAA,kBACtB;AAAA,gBAAA,IAEA;AAAA,cAAA,EAAA,CACN;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAGD,UAAU,OAAO,IAChB;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,YAAY,kBAAkB,SAC1B,YAAY,aACZ;AAAA,gBACE,gBAAgB,aAAa,MAAM;AAAA,gBACnC,YAAY;AAAA,cAAA;AAAA,YACd;AAAA,YAGL,UAAA;AAAA,cAAA,QAAQ,MACP;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO,QAAQ,IAAI,SAAS;AAAA,kBAC5B,MAAM,QAAQ,IAAI,QAAQ;AAAA,kBAC1B,OAAO,QAAQ,IAAI;AAAA,kBAClB,GAAI,QAAQ,IAAI,QAAQ,SACrB,EAAE,KAAK,QAAQ,IAAI,IAAA,IACnB,CAAA;AAAA,kBACH,GAAI,QAAQ,IAAI,aACb,EAAE,YAAY,QAAQ,IAAI,WAAA,IAC1B,CAAA;AAAA,kBACH,GAAI,QAAQ,IAAI,cACb,EAAE,aAAa,QAAQ,IAAI,YAAA,IAC3B,CAAA;AAAA,kBACH,GAAI,QAAQ,IAAI,cAAc,SAC3B,EAAE,WAAW,QAAQ,IAAI,UAAA,IACzB,CAAA;AAAA,kBACJ,SAAS,YAAY;AAAA,kBACpB,GAAG;AAAA,gBAAA;AAAA,cAAA,IAEJ;AAAA,cACH,QAAQ,SACP;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO,QAAQ,OAAO,SAAS;AAAA,kBAC/B,MAAM,QAAQ,OAAO,QAAQ;AAAA,kBAC7B,OAAO,QAAQ,OAAO;AAAA,kBACtB,MAAM;AAAA,kBACN,SAAS,YAAY;AAAA,kBACpB,GAAI,QAAQ,OAAO,QAAQ,SACxB,EAAE,KAAK,QAAQ,OAAO,IAAA,IACtB,CAAA;AAAA,kBACH,GAAI,QAAQ,OAAO,aAChB,EAAE,YAAY,QAAQ,OAAO,WAAA,IAC7B,CAAA;AAAA,kBACH,GAAG;AAAA,gBAAA;AAAA,cAAA,IAEJ;AAAA,cACH,QAAQ,OACP;AAAA,gBAAC;AAAA,gBAAA;AAAA,kBACC,OAAO,QAAQ,KAAK,SAAS;AAAA,kBAC7B,MAAM,QAAQ,KAAK,QAAQ;AAAA,kBAC3B,OAAO,QAAQ,KAAK;AAAA,kBACpB,MAAM,QAAQ,KAAK,QAAQ;AAAA,kBAC3B,OAAM;AAAA,kBACN,YAAY;AAAA,kBACZ,eAAe,QAAQ,KAAK,iBAAiB;AAAA,kBAC7C,SAAS,YAAY;AAAA,kBACpB,GAAI,QAAQ,KAAK,QAAQ,SACtB,EAAE,KAAK,QAAQ,KAAK,IAAA,IACpB,CAAA;AAAA,kBACH,GAAI,QAAQ,KAAK,aACd,EAAE,YAAY,QAAQ,KAAK,WAAA,IAC3B,CAAA;AAAA,kBACH,GAAG;AAAA,gBAAA;AAAA,cAAA,IAEJ;AAAA,YAAA;AAAA,UAAA;AAAA,QAAA,IAGN;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,WAAW;AAAA,cACT;AAAA,cACA,YAAY;AAAA,YAAA;AAAA,YAEf,UAAA;AAAA,UAAA;AAAA,QAAA;AAAA,QAKH;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAM;AAAA,YACN,SAAS,MAAM,YAAY,KAAK;AAAA,YAChC,OAAO,GAAG,SAAS,IAAI;AAAA,YACvB,MAAK;AAAA,YAEL,UAAA,oBAAC,OAAA,EAAI,WAAU,uBACZ,UAAA,aAAa,IAAI,CAAC,EAAE,KAAK,OAAO,MAAM,MAAM,aAC3C;AAAA,cAAC;AAAA,cAAA;AAAA,gBAEC;AAAA,gBACA;AAAA,gBACA,QAAQ,aAAa,MAAM;AAAA,gBAC3B,gBAAgB,qBAAqB,MAAM;AAAA,gBAC1C,GAAI,OAAO,EAAE,KAAA,IAAS,CAAA;AAAA,gBACtB,GAAG;AAAA,gBACJ,YAAY;AAAA,cAAA;AAAA,cAPP;AAAA,YAAA,CASR,EAAA,CACH;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAGN;"}
@@ -14,21 +14,26 @@ function workloadKindLabel(kind) {
14
14
  return "Pod";
15
15
  }
16
16
  }
17
+ function workloadTypeLabel(workload) {
18
+ if (workload.type) return workload.type;
19
+ return workload.kind ? workloadKindLabel(workload.kind) : void 0;
20
+ }
17
21
  function workloadStatusLabel(status) {
18
22
  return (status == null ? void 0 : status.label) ?? (status == null ? void 0 : status.code) ?? (status == null ? void 0 : status.health);
19
23
  }
20
24
  function workloadStatusTone(status) {
25
+ var _a;
21
26
  if (status == null ? void 0 : status.tone) return status.tone;
22
- const token = [status == null ? void 0 : status.label, status == null ? void 0 : status.code, status == null ? void 0 : status.health].filter(Boolean).join(" ").toLowerCase();
23
- if (!token) return "neutral";
24
- if (/(unhealthy|error|failed|failure|crash|down|unavailable)/.test(token))
25
- return "danger";
26
- if (/(warning|warn|degraded|pending|progress|starting|reconcil)/.test(token))
27
- return "warning";
28
- if (/(healthy|ready|running|available|ok|success)/.test(token))
29
- return "success";
30
- if (/(info|unknown)/.test(token)) return "info";
31
- return "neutral";
27
+ switch ((_a = status == null ? void 0 : status.health) == null ? void 0 : _a.trim().toLowerCase()) {
28
+ case "healthy":
29
+ return "success";
30
+ case "warning":
31
+ return "warning";
32
+ case "unhealthy":
33
+ return "danger";
34
+ default:
35
+ return "neutral";
36
+ }
32
37
  }
33
38
  function formatReplicaCounts(replicas) {
34
39
  if (!replicas) return void 0;
@@ -48,4 +53,5 @@ exports.formatReplicaCounts = formatReplicaCounts;
48
53
  exports.workloadKindLabel = workloadKindLabel;
49
54
  exports.workloadStatusLabel = workloadStatusLabel;
50
55
  exports.workloadStatusTone = workloadStatusTone;
56
+ exports.workloadTypeLabel = workloadTypeLabel;
51
57
  //# sourceMappingURL=WorkloadCard.model.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkloadCard.model.cjs","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type { ProgressBarsOrientation } from \"./ProgressBars\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\n\nexport type WorkloadCardKind =\n | \"service\"\n | \"ingress\"\n | \"deployment\"\n | \"statefulset\"\n | \"pod\";\n\nexport type WorkloadCardIcon = string | StaticIconComponent;\n\nexport interface WorkloadCardReplicas {\n ready?: number;\n desired?: number;\n available?: number;\n}\n\nexport interface WorkloadCardStatus {\n label?: string;\n code?: string;\n health?: string;\n message?: string;\n tone?: BadgeTone;\n}\n\nexport interface WorkloadCardWorkload {\n kind: WorkloadCardKind;\n name: string;\n namespace?: string;\n role?: string;\n createdAt?: string | number | Date;\n replicas?: WorkloadCardReplicas;\n status?: WorkloadCardStatus;\n icon?: WorkloadCardIcon;\n}\n\nexport interface WorkloadCardResourceMetric {\n title?: string;\n value: GaugeSeries;\n max?: GaugeSeries | number;\n valueLabel?: string;\n maxLabel?: string;\n unit?: string;\n icon?: WorkloadCardIcon;\n thresholds?: [warning: number, danger: number];\n centerDisplay?: \"value\" | \"percent\";\n}\n\nexport interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {\n orientation?: ProgressBarsOrientation;\n showValue?: boolean;\n}\n\nexport interface WorkloadCardMetrics {\n cpu?: WorkloadCardCpuMetric;\n memory?: WorkloadCardResourceMetric;\n disk?: WorkloadCardResourceMetric;\n}\n\nexport function workloadKindLabel(kind: WorkloadCardKind): string {\n switch (kind) {\n case \"statefulset\":\n return \"StatefulSet\";\n case \"deployment\":\n return \"Deployment\";\n case \"service\":\n return \"Service\";\n case \"ingress\":\n return \"Ingress\";\n case \"pod\":\n return \"Pod\";\n }\n}\n\nexport function workloadStatusLabel(\n status: WorkloadCardStatus | undefined,\n): string | undefined {\n return status?.label ?? status?.code ?? status?.health;\n}\n\nexport function workloadStatusTone(\n status: WorkloadCardStatus | undefined,\n): BadgeTone {\n if (status?.tone) return status.tone;\n const token = [status?.label, status?.code, status?.health]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n if (!token) return \"neutral\";\n if (/(unhealthy|error|failed|failure|crash|down|unavailable)/.test(token))\n return \"danger\";\n if (/(warning|warn|degraded|pending|progress|starting|reconcil)/.test(token))\n return \"warning\";\n if (/(healthy|ready|running|available|ok|success)/.test(token))\n return \"success\";\n if (/(info|unknown)/.test(token)) return \"info\";\n return \"neutral\";\n}\n\nexport function formatReplicaCounts(\n replicas: WorkloadCardReplicas | undefined,\n): string | undefined {\n if (!replicas) return undefined;\n if (replicas.ready !== undefined && replicas.desired !== undefined) {\n return `${replicas.ready}/${replicas.desired} ready`;\n }\n if (replicas.available !== undefined && replicas.desired !== undefined) {\n return `${replicas.available}/${replicas.desired} available`;\n }\n if (replicas.ready !== undefined) return `${replicas.ready} ready`;\n if (replicas.desired !== undefined) return `${replicas.desired} desired`;\n if (replicas.available !== undefined)\n return `${replicas.available} available`;\n return undefined;\n}\n"],"names":[],"mappings":";;AA8DO,SAAS,kBAAkB,MAAgC;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAEO,SAAS,oBACd,QACoB;AACpB,UAAO,iCAAQ,WAAS,iCAAQ,UAAQ,iCAAQ;AAClD;AAEO,SAAS,mBACd,QACW;AACX,MAAI,iCAAQ,KAAM,QAAO,OAAO;AAChC,QAAM,QAAQ,CAAC,iCAAQ,OAAO,iCAAQ,MAAM,iCAAQ,MAAM,EACvD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAA;AACH,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,0DAA0D,KAAK,KAAK;AACtE,WAAO;AACT,MAAI,6DAA6D,KAAK,KAAK;AACzE,WAAO;AACT,MAAI,+CAA+C,KAAK,KAAK;AAC3D,WAAO;AACT,MAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,SAAO;AACT;AAEO,SAAS,oBACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,UAAU,UAAa,SAAS,YAAY,QAAW;AAClE,WAAO,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,SAAS,cAAc,UAAa,SAAS,YAAY,QAAW;AACtE,WAAO,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO;AAAA,EAClD;AACA,MAAI,SAAS,UAAU,OAAW,QAAO,GAAG,SAAS,KAAK;AAC1D,MAAI,SAAS,YAAY,OAAW,QAAO,GAAG,SAAS,OAAO;AAC9D,MAAI,SAAS,cAAc;AACzB,WAAO,GAAG,SAAS,SAAS;AAC9B,SAAO;AACT;;;;;"}
1
+ {"version":3,"file":"WorkloadCard.model.cjs","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type { ProgressBarsOrientation } from \"./ProgressBars\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\n\nexport type WorkloadCardKind =\n | \"service\"\n | \"ingress\"\n | \"deployment\"\n | \"statefulset\"\n | \"pod\";\n\nexport type WorkloadCardIcon = string | StaticIconComponent;\n\nexport interface WorkloadCardReplicas {\n ready?: number;\n desired?: number;\n available?: number;\n}\n\nexport interface WorkloadCardStatus {\n /** Badge text; falls back to `code`, then `health`. Never affects the tone. */\n label?: string;\n code?: string;\n /** Drives the tone: healthy → success, warning → warning, unhealthy → danger. */\n health?: string;\n /** Hover text for the status badge. */\n message?: string;\n /** Explicit badge tone; overrides the `health` mapping. */\n tone?: BadgeTone;\n}\n\n/** One caller-defined fact shown in the card's subtitle row, e.g. a region. */\nexport interface WorkloadCardMetadataItem {\n label: string;\n value: ReactNode;\n}\n\n/**\n * The workload a card describes. Kubernetes workloads set `kind` (which picks\n * the icon and type label); anything else sets a free-form `type` label and\n * its own `icon`, and describes itself through `metadata`.\n */\nexport interface WorkloadCardWorkload {\n /** Kubernetes kind; picks the default icon and type label. */\n kind?: WorkloadCardKind;\n /** Display label for the workload type, e.g. \"EC2 instance\". Overrides the kind label. */\n type?: string;\n name: string;\n namespace?: string;\n role?: string;\n createdAt?: string | number | Date;\n replicas?: WorkloadCardReplicas;\n status?: WorkloadCardStatus;\n icon?: WorkloadCardIcon;\n /** Extra label/value facts rendered after the built-ins in the subtitle row. */\n metadata?: ReadonlyArray<WorkloadCardMetadataItem>;\n}\n\nexport interface WorkloadCardResourceMetric {\n title?: string;\n value: GaugeSeries;\n max?: GaugeSeries | number;\n valueLabel?: string;\n maxLabel?: string;\n unit?: string;\n icon?: WorkloadCardIcon;\n thresholds?: [warning: number, danger: number];\n centerDisplay?: \"value\" | \"percent\";\n}\n\nexport interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {\n orientation?: ProgressBarsOrientation;\n showValue?: boolean;\n}\n\nexport interface WorkloadCardMetrics {\n cpu?: WorkloadCardCpuMetric;\n memory?: WorkloadCardResourceMetric;\n disk?: WorkloadCardResourceMetric;\n}\n\nexport function workloadKindLabel(kind: WorkloadCardKind): string {\n switch (kind) {\n case \"statefulset\":\n return \"StatefulSet\";\n case \"deployment\":\n return \"Deployment\";\n case \"service\":\n return \"Service\";\n case \"ingress\":\n return \"Ingress\";\n case \"pod\":\n return \"Pod\";\n }\n}\n\n/** The type label shown in the subtitle: `type`, else the kind's label. */\nexport function workloadTypeLabel(\n workload: Pick<WorkloadCardWorkload, \"kind\" | \"type\">,\n): string | undefined {\n if (workload.type) return workload.type;\n return workload.kind ? workloadKindLabel(workload.kind) : undefined;\n}\n\nexport function workloadStatusLabel(\n status: WorkloadCardStatus | undefined,\n): string | undefined {\n return status?.label ?? status?.code ?? status?.health;\n}\n\n/**\n * The status badge tone: an explicit `tone`, else a fixed mapping of `health`\n * (healthy → success, warning → warning, unhealthy → danger, anything else →\n * neutral). Free-text `label`/`code` never affect the tone — substring guesses\n * misfire (\"NotReady\" contains \"ready\", \"Broken\" contains \"ok\").\n */\nexport function workloadStatusTone(\n status: WorkloadCardStatus | undefined,\n): BadgeTone {\n if (status?.tone) return status.tone;\n switch (status?.health?.trim().toLowerCase()) {\n case \"healthy\":\n return \"success\";\n case \"warning\":\n return \"warning\";\n case \"unhealthy\":\n return \"danger\";\n default:\n return \"neutral\";\n }\n}\n\nexport function formatReplicaCounts(\n replicas: WorkloadCardReplicas | undefined,\n): string | undefined {\n if (!replicas) return undefined;\n if (replicas.ready !== undefined && replicas.desired !== undefined) {\n return `${replicas.ready}/${replicas.desired} ready`;\n }\n if (replicas.available !== undefined && replicas.desired !== undefined) {\n return `${replicas.available}/${replicas.desired} available`;\n }\n if (replicas.ready !== undefined) return `${replicas.ready} ready`;\n if (replicas.desired !== undefined) return `${replicas.desired} desired`;\n if (replicas.available !== undefined)\n return `${replicas.available} available`;\n return undefined;\n}\n"],"names":[],"mappings":";;AAmFO,SAAS,kBAAkB,MAAgC;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAGO,SAAS,kBACd,UACoB;AACpB,MAAI,SAAS,KAAM,QAAO,SAAS;AACnC,SAAO,SAAS,OAAO,kBAAkB,SAAS,IAAI,IAAI;AAC5D;AAEO,SAAS,oBACd,QACoB;AACpB,UAAO,iCAAQ,WAAS,iCAAQ,UAAQ,iCAAQ;AAClD;AAQO,SAAS,mBACd,QACW;;AACX,MAAI,iCAAQ,KAAM,QAAO,OAAO;AAChC,WAAQ,sCAAQ,WAAR,mBAAgB,OAAO,eAAY;AAAA,IACzC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EAAA;AAEb;AAEO,SAAS,oBACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,UAAU,UAAa,SAAS,YAAY,QAAW;AAClE,WAAO,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,SAAS,cAAc,UAAa,SAAS,YAAY,QAAW;AACtE,WAAO,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO;AAAA,EAClD;AACA,MAAI,SAAS,UAAU,OAAW,QAAO,GAAG,SAAS,KAAK;AAC1D,MAAI,SAAS,YAAY,OAAW,QAAO,GAAG,SAAS,OAAO;AAC9D,MAAI,SAAS,cAAc;AACzB,WAAO,GAAG,SAAS,SAAS;AAC9B,SAAO;AACT;;;;;;"}
@@ -1,3 +1,4 @@
1
+ import { ReactNode } from 'react';
1
2
  import { BadgeTone } from './Badge';
2
3
  import { StaticIconComponent } from './Icon';
3
4
  import { ProgressBarsOrientation } from './ProgressBars';
@@ -10,14 +11,31 @@ export interface WorkloadCardReplicas {
10
11
  available?: number;
11
12
  }
12
13
  export interface WorkloadCardStatus {
14
+ /** Badge text; falls back to `code`, then `health`. Never affects the tone. */
13
15
  label?: string;
14
16
  code?: string;
17
+ /** Drives the tone: healthy → success, warning → warning, unhealthy → danger. */
15
18
  health?: string;
19
+ /** Hover text for the status badge. */
16
20
  message?: string;
21
+ /** Explicit badge tone; overrides the `health` mapping. */
17
22
  tone?: BadgeTone;
18
23
  }
24
+ /** One caller-defined fact shown in the card's subtitle row, e.g. a region. */
25
+ export interface WorkloadCardMetadataItem {
26
+ label: string;
27
+ value: ReactNode;
28
+ }
29
+ /**
30
+ * The workload a card describes. Kubernetes workloads set `kind` (which picks
31
+ * the icon and type label); anything else sets a free-form `type` label and
32
+ * its own `icon`, and describes itself through `metadata`.
33
+ */
19
34
  export interface WorkloadCardWorkload {
20
- kind: WorkloadCardKind;
35
+ /** Kubernetes kind; picks the default icon and type label. */
36
+ kind?: WorkloadCardKind;
37
+ /** Display label for the workload type, e.g. "EC2 instance". Overrides the kind label. */
38
+ type?: string;
21
39
  name: string;
22
40
  namespace?: string;
23
41
  role?: string;
@@ -25,6 +43,8 @@ export interface WorkloadCardWorkload {
25
43
  replicas?: WorkloadCardReplicas;
26
44
  status?: WorkloadCardStatus;
27
45
  icon?: WorkloadCardIcon;
46
+ /** Extra label/value facts rendered after the built-ins in the subtitle row. */
47
+ metadata?: ReadonlyArray<WorkloadCardMetadataItem>;
28
48
  }
29
49
  export interface WorkloadCardResourceMetric {
30
50
  title?: string;
@@ -47,7 +67,15 @@ export interface WorkloadCardMetrics {
47
67
  disk?: WorkloadCardResourceMetric;
48
68
  }
49
69
  export declare function workloadKindLabel(kind: WorkloadCardKind): string;
70
+ /** The type label shown in the subtitle: `type`, else the kind's label. */
71
+ export declare function workloadTypeLabel(workload: Pick<WorkloadCardWorkload, "kind" | "type">): string | undefined;
50
72
  export declare function workloadStatusLabel(status: WorkloadCardStatus | undefined): string | undefined;
73
+ /**
74
+ * The status badge tone: an explicit `tone`, else a fixed mapping of `health`
75
+ * (healthy → success, warning → warning, unhealthy → danger, anything else →
76
+ * neutral). Free-text `label`/`code` never affect the tone — substring guesses
77
+ * misfire ("NotReady" contains "ready", "Broken" contains "ok").
78
+ */
51
79
  export declare function workloadStatusTone(status: WorkloadCardStatus | undefined): BadgeTone;
52
80
  export declare function formatReplicaCounts(replicas: WorkloadCardReplicas | undefined): string | undefined;
53
81
  //# sourceMappingURL=WorkloadCard.model.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkloadCard.model.d.ts","sourceRoot":"","sources":["../../src/data/WorkloadCard.model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,SAAS,GACT,YAAY,GACZ,aAAa,GACb,KAAK,CAAC;AAEV,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,mBAAmB,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,IAAI,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/C,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CACrC;AAED,MAAM,WAAW,qBAAsB,SAAQ,0BAA0B;IACvE,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,EAAE,qBAAqB,CAAC;IAC5B,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,IAAI,CAAC,EAAE,0BAA0B,CAAC;CACnC;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAahE;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACrC,SAAS,CAeX;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,oBAAoB,GAAG,SAAS,GACzC,MAAM,GAAG,SAAS,CAapB"}
1
+ {"version":3,"file":"WorkloadCard.model.d.ts","sourceRoot":"","sources":["../../src/data/WorkloadCard.model.ts"],"names":[],"mappings":"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"}
@@ -12,21 +12,26 @@ function workloadKindLabel(kind) {
12
12
  return "Pod";
13
13
  }
14
14
  }
15
+ function workloadTypeLabel(workload) {
16
+ if (workload.type) return workload.type;
17
+ return workload.kind ? workloadKindLabel(workload.kind) : void 0;
18
+ }
15
19
  function workloadStatusLabel(status) {
16
20
  return (status == null ? void 0 : status.label) ?? (status == null ? void 0 : status.code) ?? (status == null ? void 0 : status.health);
17
21
  }
18
22
  function workloadStatusTone(status) {
23
+ var _a;
19
24
  if (status == null ? void 0 : status.tone) return status.tone;
20
- const token = [status == null ? void 0 : status.label, status == null ? void 0 : status.code, status == null ? void 0 : status.health].filter(Boolean).join(" ").toLowerCase();
21
- if (!token) return "neutral";
22
- if (/(unhealthy|error|failed|failure|crash|down|unavailable)/.test(token))
23
- return "danger";
24
- if (/(warning|warn|degraded|pending|progress|starting|reconcil)/.test(token))
25
- return "warning";
26
- if (/(healthy|ready|running|available|ok|success)/.test(token))
27
- return "success";
28
- if (/(info|unknown)/.test(token)) return "info";
29
- return "neutral";
25
+ switch ((_a = status == null ? void 0 : status.health) == null ? void 0 : _a.trim().toLowerCase()) {
26
+ case "healthy":
27
+ return "success";
28
+ case "warning":
29
+ return "warning";
30
+ case "unhealthy":
31
+ return "danger";
32
+ default:
33
+ return "neutral";
34
+ }
30
35
  }
31
36
  function formatReplicaCounts(replicas) {
32
37
  if (!replicas) return void 0;
@@ -46,6 +51,7 @@ export {
46
51
  formatReplicaCounts,
47
52
  workloadKindLabel,
48
53
  workloadStatusLabel,
49
- workloadStatusTone
54
+ workloadStatusTone,
55
+ workloadTypeLabel
50
56
  };
51
57
  //# sourceMappingURL=WorkloadCard.model.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkloadCard.model.js","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type { ProgressBarsOrientation } from \"./ProgressBars\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\n\nexport type WorkloadCardKind =\n | \"service\"\n | \"ingress\"\n | \"deployment\"\n | \"statefulset\"\n | \"pod\";\n\nexport type WorkloadCardIcon = string | StaticIconComponent;\n\nexport interface WorkloadCardReplicas {\n ready?: number;\n desired?: number;\n available?: number;\n}\n\nexport interface WorkloadCardStatus {\n label?: string;\n code?: string;\n health?: string;\n message?: string;\n tone?: BadgeTone;\n}\n\nexport interface WorkloadCardWorkload {\n kind: WorkloadCardKind;\n name: string;\n namespace?: string;\n role?: string;\n createdAt?: string | number | Date;\n replicas?: WorkloadCardReplicas;\n status?: WorkloadCardStatus;\n icon?: WorkloadCardIcon;\n}\n\nexport interface WorkloadCardResourceMetric {\n title?: string;\n value: GaugeSeries;\n max?: GaugeSeries | number;\n valueLabel?: string;\n maxLabel?: string;\n unit?: string;\n icon?: WorkloadCardIcon;\n thresholds?: [warning: number, danger: number];\n centerDisplay?: \"value\" | \"percent\";\n}\n\nexport interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {\n orientation?: ProgressBarsOrientation;\n showValue?: boolean;\n}\n\nexport interface WorkloadCardMetrics {\n cpu?: WorkloadCardCpuMetric;\n memory?: WorkloadCardResourceMetric;\n disk?: WorkloadCardResourceMetric;\n}\n\nexport function workloadKindLabel(kind: WorkloadCardKind): string {\n switch (kind) {\n case \"statefulset\":\n return \"StatefulSet\";\n case \"deployment\":\n return \"Deployment\";\n case \"service\":\n return \"Service\";\n case \"ingress\":\n return \"Ingress\";\n case \"pod\":\n return \"Pod\";\n }\n}\n\nexport function workloadStatusLabel(\n status: WorkloadCardStatus | undefined,\n): string | undefined {\n return status?.label ?? status?.code ?? status?.health;\n}\n\nexport function workloadStatusTone(\n status: WorkloadCardStatus | undefined,\n): BadgeTone {\n if (status?.tone) return status.tone;\n const token = [status?.label, status?.code, status?.health]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n if (!token) return \"neutral\";\n if (/(unhealthy|error|failed|failure|crash|down|unavailable)/.test(token))\n return \"danger\";\n if (/(warning|warn|degraded|pending|progress|starting|reconcil)/.test(token))\n return \"warning\";\n if (/(healthy|ready|running|available|ok|success)/.test(token))\n return \"success\";\n if (/(info|unknown)/.test(token)) return \"info\";\n return \"neutral\";\n}\n\nexport function formatReplicaCounts(\n replicas: WorkloadCardReplicas | undefined,\n): string | undefined {\n if (!replicas) return undefined;\n if (replicas.ready !== undefined && replicas.desired !== undefined) {\n return `${replicas.ready}/${replicas.desired} ready`;\n }\n if (replicas.available !== undefined && replicas.desired !== undefined) {\n return `${replicas.available}/${replicas.desired} available`;\n }\n if (replicas.ready !== undefined) return `${replicas.ready} ready`;\n if (replicas.desired !== undefined) return `${replicas.desired} desired`;\n if (replicas.available !== undefined)\n return `${replicas.available} available`;\n return undefined;\n}\n"],"names":[],"mappings":"AA8DO,SAAS,kBAAkB,MAAgC;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAEO,SAAS,oBACd,QACoB;AACpB,UAAO,iCAAQ,WAAS,iCAAQ,UAAQ,iCAAQ;AAClD;AAEO,SAAS,mBACd,QACW;AACX,MAAI,iCAAQ,KAAM,QAAO,OAAO;AAChC,QAAM,QAAQ,CAAC,iCAAQ,OAAO,iCAAQ,MAAM,iCAAQ,MAAM,EACvD,OAAO,OAAO,EACd,KAAK,GAAG,EACR,YAAA;AACH,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,0DAA0D,KAAK,KAAK;AACtE,WAAO;AACT,MAAI,6DAA6D,KAAK,KAAK;AACzE,WAAO;AACT,MAAI,+CAA+C,KAAK,KAAK;AAC3D,WAAO;AACT,MAAI,iBAAiB,KAAK,KAAK,EAAG,QAAO;AACzC,SAAO;AACT;AAEO,SAAS,oBACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,UAAU,UAAa,SAAS,YAAY,QAAW;AAClE,WAAO,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,SAAS,cAAc,UAAa,SAAS,YAAY,QAAW;AACtE,WAAO,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO;AAAA,EAClD;AACA,MAAI,SAAS,UAAU,OAAW,QAAO,GAAG,SAAS,KAAK;AAC1D,MAAI,SAAS,YAAY,OAAW,QAAO,GAAG,SAAS,OAAO;AAC9D,MAAI,SAAS,cAAc;AACzB,WAAO,GAAG,SAAS,SAAS;AAC9B,SAAO;AACT;"}
1
+ {"version":3,"file":"WorkloadCard.model.js","sources":["../../src/data/WorkloadCard.model.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport type { BadgeTone } from \"./Badge\";\nimport type { StaticIconComponent } from \"./Icon\";\nimport type { ProgressBarsOrientation } from \"./ProgressBars\";\nimport type { GaugeSeries } from \"./TimeseriesGauge\";\n\nexport type WorkloadCardKind =\n | \"service\"\n | \"ingress\"\n | \"deployment\"\n | \"statefulset\"\n | \"pod\";\n\nexport type WorkloadCardIcon = string | StaticIconComponent;\n\nexport interface WorkloadCardReplicas {\n ready?: number;\n desired?: number;\n available?: number;\n}\n\nexport interface WorkloadCardStatus {\n /** Badge text; falls back to `code`, then `health`. Never affects the tone. */\n label?: string;\n code?: string;\n /** Drives the tone: healthy → success, warning → warning, unhealthy → danger. */\n health?: string;\n /** Hover text for the status badge. */\n message?: string;\n /** Explicit badge tone; overrides the `health` mapping. */\n tone?: BadgeTone;\n}\n\n/** One caller-defined fact shown in the card's subtitle row, e.g. a region. */\nexport interface WorkloadCardMetadataItem {\n label: string;\n value: ReactNode;\n}\n\n/**\n * The workload a card describes. Kubernetes workloads set `kind` (which picks\n * the icon and type label); anything else sets a free-form `type` label and\n * its own `icon`, and describes itself through `metadata`.\n */\nexport interface WorkloadCardWorkload {\n /** Kubernetes kind; picks the default icon and type label. */\n kind?: WorkloadCardKind;\n /** Display label for the workload type, e.g. \"EC2 instance\". Overrides the kind label. */\n type?: string;\n name: string;\n namespace?: string;\n role?: string;\n createdAt?: string | number | Date;\n replicas?: WorkloadCardReplicas;\n status?: WorkloadCardStatus;\n icon?: WorkloadCardIcon;\n /** Extra label/value facts rendered after the built-ins in the subtitle row. */\n metadata?: ReadonlyArray<WorkloadCardMetadataItem>;\n}\n\nexport interface WorkloadCardResourceMetric {\n title?: string;\n value: GaugeSeries;\n max?: GaugeSeries | number;\n valueLabel?: string;\n maxLabel?: string;\n unit?: string;\n icon?: WorkloadCardIcon;\n thresholds?: [warning: number, danger: number];\n centerDisplay?: \"value\" | \"percent\";\n}\n\nexport interface WorkloadCardCpuMetric extends WorkloadCardResourceMetric {\n orientation?: ProgressBarsOrientation;\n showValue?: boolean;\n}\n\nexport interface WorkloadCardMetrics {\n cpu?: WorkloadCardCpuMetric;\n memory?: WorkloadCardResourceMetric;\n disk?: WorkloadCardResourceMetric;\n}\n\nexport function workloadKindLabel(kind: WorkloadCardKind): string {\n switch (kind) {\n case \"statefulset\":\n return \"StatefulSet\";\n case \"deployment\":\n return \"Deployment\";\n case \"service\":\n return \"Service\";\n case \"ingress\":\n return \"Ingress\";\n case \"pod\":\n return \"Pod\";\n }\n}\n\n/** The type label shown in the subtitle: `type`, else the kind's label. */\nexport function workloadTypeLabel(\n workload: Pick<WorkloadCardWorkload, \"kind\" | \"type\">,\n): string | undefined {\n if (workload.type) return workload.type;\n return workload.kind ? workloadKindLabel(workload.kind) : undefined;\n}\n\nexport function workloadStatusLabel(\n status: WorkloadCardStatus | undefined,\n): string | undefined {\n return status?.label ?? status?.code ?? status?.health;\n}\n\n/**\n * The status badge tone: an explicit `tone`, else a fixed mapping of `health`\n * (healthy → success, warning → warning, unhealthy → danger, anything else →\n * neutral). Free-text `label`/`code` never affect the tone — substring guesses\n * misfire (\"NotReady\" contains \"ready\", \"Broken\" contains \"ok\").\n */\nexport function workloadStatusTone(\n status: WorkloadCardStatus | undefined,\n): BadgeTone {\n if (status?.tone) return status.tone;\n switch (status?.health?.trim().toLowerCase()) {\n case \"healthy\":\n return \"success\";\n case \"warning\":\n return \"warning\";\n case \"unhealthy\":\n return \"danger\";\n default:\n return \"neutral\";\n }\n}\n\nexport function formatReplicaCounts(\n replicas: WorkloadCardReplicas | undefined,\n): string | undefined {\n if (!replicas) return undefined;\n if (replicas.ready !== undefined && replicas.desired !== undefined) {\n return `${replicas.ready}/${replicas.desired} ready`;\n }\n if (replicas.available !== undefined && replicas.desired !== undefined) {\n return `${replicas.available}/${replicas.desired} available`;\n }\n if (replicas.ready !== undefined) return `${replicas.ready} ready`;\n if (replicas.desired !== undefined) return `${replicas.desired} desired`;\n if (replicas.available !== undefined)\n return `${replicas.available} available`;\n return undefined;\n}\n"],"names":[],"mappings":"AAmFO,SAAS,kBAAkB,MAAgC;AAChE,UAAQ,MAAA;AAAA,IACN,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,EAAA;AAEb;AAGO,SAAS,kBACd,UACoB;AACpB,MAAI,SAAS,KAAM,QAAO,SAAS;AACnC,SAAO,SAAS,OAAO,kBAAkB,SAAS,IAAI,IAAI;AAC5D;AAEO,SAAS,oBACd,QACoB;AACpB,UAAO,iCAAQ,WAAS,iCAAQ,UAAQ,iCAAQ;AAClD;AAQO,SAAS,mBACd,QACW;AArCN;AAsCL,MAAI,iCAAQ,KAAM,QAAO,OAAO;AAChC,WAAQ,sCAAQ,WAAR,mBAAgB,OAAO,eAAY;AAAA,IACzC,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EAAA;AAEb;AAEO,SAAS,oBACd,UACoB;AACpB,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,SAAS,UAAU,UAAa,SAAS,YAAY,QAAW;AAClE,WAAO,GAAG,SAAS,KAAK,IAAI,SAAS,OAAO;AAAA,EAC9C;AACA,MAAI,SAAS,cAAc,UAAa,SAAS,YAAY,QAAW;AACtE,WAAO,GAAG,SAAS,SAAS,IAAI,SAAS,OAAO;AAAA,EAClD;AACA,MAAI,SAAS,UAAU,OAAW,QAAO,GAAG,SAAS,KAAK;AAC1D,MAAI,SAAS,YAAY,OAAW,QAAO,GAAG,SAAS,OAAO;AAC9D,MAAI,SAAS,cAAc;AACzB,WAAO,GAAG,SAAS,SAAS;AAC9B,SAAO;AACT;"}