@burdenoff/microfe-adaptercloud 2026.706.2 → 2026.706.3

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 (40) hide show
  1. package/dist/adaptercloud/AdapterCloudRoot.js +52 -44
  2. package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
  3. package/dist/adaptercloud/AdapterCloudRoutes.js +18 -14
  4. package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
  5. package/dist/adaptercloud/components/ConfirmDialog.js.map +1 -1
  6. package/dist/adaptercloud/components/viz/BarChart.js +32 -0
  7. package/dist/adaptercloud/components/viz/BarChart.js.map +1 -0
  8. package/dist/adaptercloud/components/viz/Sparkline.js +50 -0
  9. package/dist/adaptercloud/components/viz/Sparkline.js.map +1 -0
  10. package/dist/adaptercloud/components/viz/StackedBar.js +1 -0
  11. package/dist/adaptercloud/components/viz/TopologyGraph.js +96 -0
  12. package/dist/adaptercloud/components/viz/TopologyGraph.js.map +1 -0
  13. package/dist/adaptercloud/components/viz/index.js +4 -0
  14. package/dist/adaptercloud/hooks/useAdapterCloudApi.js +22 -4
  15. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  16. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  17. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +176 -123
  18. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
  19. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +261 -179
  20. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
  21. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  22. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +158 -94
  23. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  24. package/dist/adaptercloud/pages/costs/CostsPage.js +160 -111
  25. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  26. package/dist/adaptercloud/pages/drift/DriftListPage.js +163 -84
  27. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  28. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +177 -96
  29. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  30. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +145 -131
  31. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
  32. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +10 -1
  33. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  34. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +226 -165
  35. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  36. package/dist/adaptercloud/pages/topology/TopologyPage.js +397 -0
  37. package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -0
  38. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +184 -131
  39. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  40. package/package.json +1 -1
@@ -4,163 +4,244 @@ import { PageLayout as n } from "../../components/PageLayout.js";
4
4
  import { policyEnforcementLabels as r, policyKindLabels as i } from "../../utils/domain.js";
5
5
  import { SeverityBadge as a } from "../../components/SeverityBadge.js";
6
6
  import { DataState as o } from "../../components/DataState.js";
7
- import { usePolicies as s } from "../../hooks/useAdapterCloudApi.js";
8
- import { formatRelativeTime as c } from "../../utils/formatters.js";
9
- import { useCallback as l, useState as u } from "react";
10
- import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
11
- import { ChevronLeft as f, ChevronRight as p, Plus as m, ScrollText as h } from "lucide-react";
12
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
13
- import { GlassCard as v } from "@burdenoff/fe-libs/ui";
7
+ import { usePolicies as s, useUpdatePolicy as c } from "../../hooks/useAdapterCloudApi.js";
8
+ import { formatRelativeTime as l } from "../../utils/formatters.js";
9
+ import { useCallback as u, useState as d } from "react";
10
+ import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
11
+ import { ChevronLeft as p, ChevronRight as m, Plus as h, ScrollText as g } from "lucide-react";
12
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
13
+ import { GlassCard as y } from "@burdenoff/fe-libs/ui";
14
14
  //#region src/adaptercloud/pages/policies/PoliciesListPage.tsx
15
- var y = 20;
16
- function b() {
17
- let { navigateTo: b } = e(), { t: x } = d(), S = l((e, t) => {
15
+ var b = 20, x = "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium", S = {
16
+ SECURITY: "bg-accent-blue-subtle text-accent-blue",
17
+ GUARDRAIL: "bg-accent-blue-subtle text-accent-blue",
18
+ COMPLIANCE: "bg-accent-blue-subtle text-accent-blue",
19
+ COST: "bg-status-success-bg-subtle text-status-success-text",
20
+ NAMING: "bg-bg-sunken text-text-secondary",
21
+ TAGGING: "bg-bg-sunken text-text-secondary"
22
+ }, C = {
23
+ BLOCK: "bg-status-error-bg-subtle text-status-error-text",
24
+ WARN: "bg-status-warning-bg-subtle text-status-warning-text",
25
+ ADVISORY: "bg-bg-sunken text-text-secondary"
26
+ };
27
+ function w({ kind: e }) {
28
+ return /* @__PURE__ */ _("span", {
29
+ className: t(x, S[e]),
30
+ children: i[e]
31
+ });
32
+ }
33
+ function T({ enforcement: e }) {
34
+ return /* @__PURE__ */ _("span", {
35
+ className: t(x, C[e]),
36
+ children: r[e]
37
+ });
38
+ }
39
+ function E({ label: e, children: t }) {
40
+ return /* @__PURE__ */ v("div", {
41
+ className: "flex flex-col gap-1",
42
+ children: [/* @__PURE__ */ _("span", {
43
+ className: "text-xs font-semibold uppercase tracking-wide text-text-secondary",
44
+ children: e
45
+ }), t]
46
+ });
47
+ }
48
+ function D() {
49
+ let { navigateTo: r } = e(), { t: x } = f(), S = u((e, t) => {
18
50
  let n = x(e);
19
51
  return n === e ? t : n;
20
- }, [x]), [C, w] = u(""), [T, E] = u("all"), [D, O] = u(0), k = s({
52
+ }, [x]), [C, D] = d(""), [O, k] = d("all"), [A, j] = d(0), [M, N] = d({}), P = s({
21
53
  kind: C || void 0,
22
- isActive: T === "all" ? void 0 : T === "active",
54
+ isActive: O === "all" ? void 0 : O === "active",
23
55
  pagination: {
24
- limit: y,
25
- offset: D
56
+ limit: b,
57
+ offset: A
26
58
  }
27
- }), A = k.data?.items ?? [], j = k.data?.pageInfo.hasMore ?? !1;
28
- return /* @__PURE__ */ _(n, {
59
+ }), F = c(), I = P.data?.items ?? [], L = P.data?.pageInfo.hasMore ?? !1, R = u((e) => {
60
+ let t = !(M[e.id] ?? e.isActive);
61
+ N((n) => ({
62
+ ...n,
63
+ [e.id]: t
64
+ })), F.mutate({
65
+ id: e.id,
66
+ input: { isActive: t }
67
+ }, { onSettled: () => N((t) => {
68
+ let n = { ...t };
69
+ return delete n[e.id], n;
70
+ }) });
71
+ }, [F, M]);
72
+ return /* @__PURE__ */ v(n, {
29
73
  title: S("adaptercloud.policies.title", "Policies"),
30
74
  description: S("adaptercloud.policies.subtitle", "Governance rules that evaluate the inventory — guardrail, tagging, security, cost, compliance and naming."),
31
- actions: /* @__PURE__ */ _("button", {
75
+ actions: /* @__PURE__ */ v("button", {
32
76
  type: "button",
33
- onClick: () => b("/policies/new"),
77
+ onClick: () => r("/policies/new"),
34
78
  className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
35
- children: [/* @__PURE__ */ g(m, { className: "h-4 w-4" }), S("adaptercloud.policies.new", "New policy")]
79
+ children: [/* @__PURE__ */ _(h, { className: "h-4 w-4" }), S("adaptercloud.policies.new", "New policy")]
36
80
  }),
37
- children: [/* @__PURE__ */ _("div", {
81
+ children: [/* @__PURE__ */ v("div", {
38
82
  className: "mb-6 flex flex-wrap gap-3",
39
- children: [/* @__PURE__ */ _("select", {
83
+ children: [/* @__PURE__ */ v("select", {
40
84
  value: C,
41
85
  onChange: (e) => {
42
- w(e.target.value), O(0);
86
+ D(e.target.value), j(0);
43
87
  },
44
88
  "aria-label": S("adaptercloud.policies.filterByKind", "Filter by kind"),
45
89
  className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
46
- children: [/* @__PURE__ */ g("option", {
90
+ children: [/* @__PURE__ */ _("option", {
47
91
  value: "",
48
92
  children: S("adaptercloud.policies.allKinds", "All kinds")
49
- }), Object.keys(i).map((e) => /* @__PURE__ */ g("option", {
93
+ }), Object.keys(i).map((e) => /* @__PURE__ */ _("option", {
50
94
  value: e,
51
95
  children: i[e]
52
96
  }, e))]
53
- }), /* @__PURE__ */ _("select", {
54
- value: T,
97
+ }), /* @__PURE__ */ v("select", {
98
+ value: O,
55
99
  onChange: (e) => {
56
- E(e.target.value), O(0);
100
+ k(e.target.value), j(0);
57
101
  },
58
102
  "aria-label": S("adaptercloud.policies.filterByActive", "Filter by active state"),
59
103
  className: "rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
60
104
  children: [
61
- /* @__PURE__ */ g("option", {
105
+ /* @__PURE__ */ _("option", {
62
106
  value: "all",
63
107
  children: S("adaptercloud.policies.allStates", "All")
64
108
  }),
65
- /* @__PURE__ */ g("option", {
109
+ /* @__PURE__ */ _("option", {
66
110
  value: "active",
67
111
  children: S("adaptercloud.policies.active", "Active")
68
112
  }),
69
- /* @__PURE__ */ g("option", {
113
+ /* @__PURE__ */ _("option", {
70
114
  value: "inactive",
71
115
  children: S("adaptercloud.policies.inactive", "Inactive")
72
116
  })
73
117
  ]
74
118
  })]
75
- }), /* @__PURE__ */ _(o, {
76
- isLoading: k.isLoading,
77
- isError: k.isError,
78
- isEmpty: A.length === 0,
79
- error: k.error,
80
- onRetry: () => void k.refetch(),
119
+ }), /* @__PURE__ */ v(o, {
120
+ isLoading: P.isLoading,
121
+ isError: P.isError,
122
+ isEmpty: I.length === 0,
123
+ error: P.error,
124
+ onRetry: () => void P.refetch(),
81
125
  emptyTitle: S("adaptercloud.policies.emptyTitle", "No policies yet"),
82
126
  emptyDescription: S("adaptercloud.policies.emptyDescription", "Author your first governance policy to put compliance into the system."),
83
- emptyIcon: /* @__PURE__ */ g(h, { className: "h-10 w-10" }),
84
- emptyAction: /* @__PURE__ */ g("button", {
127
+ emptyIcon: /* @__PURE__ */ _(g, { className: "h-10 w-10" }),
128
+ emptyAction: /* @__PURE__ */ _("button", {
85
129
  type: "button",
86
- onClick: () => b("/policies/new"),
130
+ onClick: () => r("/policies/new"),
87
131
  className: "rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90",
88
132
  children: S("adaptercloud.policies.new", "New policy")
89
133
  }),
90
- children: [/* @__PURE__ */ g("div", {
91
- className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
92
- children: A.map((e) => /* @__PURE__ */ _(v, {
93
- role: "button",
94
- tabIndex: 0,
95
- onClick: () => b(`/policies/${e.id}`),
96
- onKeyDown: (t) => {
97
- (t.key === "Enter" || t.key === " ") && (t.preventDefault(), b(`/policies/${e.id}`));
98
- },
99
- className: "cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
100
- children: [
101
- /* @__PURE__ */ _("div", {
102
- className: "flex items-start justify-between gap-2",
103
- children: [/* @__PURE__ */ _("div", {
104
- className: "min-w-0",
105
- children: [/* @__PURE__ */ g("h3", {
106
- className: "truncate text-base font-semibold text-text-primary",
107
- children: e.name
108
- }), /* @__PURE__ */ g("p", {
109
- className: "mt-0.5 text-xs text-text-secondary",
110
- children: i[e.kind]
111
- })]
112
- }), /* @__PURE__ */ g(a, { severity: e.severity })]
113
- }),
114
- e.description && /* @__PURE__ */ g("p", {
115
- className: "mt-3 line-clamp-2 text-sm text-text-secondary",
116
- children: e.description
117
- }),
118
- /* @__PURE__ */ _("div", {
119
- className: "mt-4 flex items-center gap-2",
120
- children: [/* @__PURE__ */ g("span", {
121
- className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
122
- children: r[e.enforcement]
123
- }), /* @__PURE__ */ _("span", {
124
- className: t("inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium", e.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-secondary"),
125
- children: [/* @__PURE__ */ g("span", { className: t("h-1.5 w-1.5 rounded-full", e.isActive ? "bg-status-success-text" : "bg-text-secondary") }), e.isActive ? S("adaptercloud.policies.active", "Active") : S("adaptercloud.policies.inactive", "Inactive")]
126
- })]
127
- }),
128
- /* @__PURE__ */ _("p", {
129
- className: "mt-4 text-xs text-text-secondary",
134
+ children: [/* @__PURE__ */ _("div", {
135
+ className: "space-y-3",
136
+ children: I.map((e) => {
137
+ let n = M[e.id] ?? e.isActive, i = e.id in M;
138
+ return /* @__PURE__ */ v(y, {
139
+ role: "button",
140
+ tabIndex: 0,
141
+ onClick: () => r(`/policies/${e.id}`),
142
+ onKeyDown: (t) => {
143
+ (t.key === "Enter" || t.key === " ") && (t.preventDefault(), r(`/policies/${e.id}`));
144
+ },
145
+ className: "cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]",
146
+ children: [/* @__PURE__ */ v("div", {
147
+ className: "flex flex-col gap-4 lg:flex-row lg:items-center lg:gap-6",
148
+ children: [
149
+ /* @__PURE__ */ v("div", {
150
+ className: "min-w-0 lg:flex-1",
151
+ children: [/* @__PURE__ */ _("h3", {
152
+ className: "truncate text-base font-semibold text-text-primary",
153
+ children: e.name
154
+ }), e.description && /* @__PURE__ */ _("p", {
155
+ className: "mt-0.5 line-clamp-1 text-sm text-text-secondary",
156
+ children: e.description
157
+ })]
158
+ }),
159
+ /* @__PURE__ */ _(E, {
160
+ label: S("adaptercloud.policies.kind", "Kind"),
161
+ children: /* @__PURE__ */ _(w, { kind: e.kind })
162
+ }),
163
+ /* @__PURE__ */ _(E, {
164
+ label: S("adaptercloud.policies.enforcement", "Enforcement"),
165
+ children: /* @__PURE__ */ _(T, { enforcement: e.enforcement })
166
+ }),
167
+ /* @__PURE__ */ _(E, {
168
+ label: S("adaptercloud.policies.severity", "Severity"),
169
+ children: /* @__PURE__ */ _(a, { severity: e.severity })
170
+ }),
171
+ /* @__PURE__ */ _(E, {
172
+ label: S("adaptercloud.policies.activeLabel", "Active"),
173
+ children: /* @__PURE__ */ v("div", {
174
+ className: "flex items-center gap-2",
175
+ children: [/* @__PURE__ */ _("button", {
176
+ type: "button",
177
+ role: "switch",
178
+ "aria-checked": n,
179
+ "aria-label": n ? S("adaptercloud.policies.deactivate", "Deactivate policy") : S("adaptercloud.policies.activate", "Activate policy"),
180
+ disabled: i,
181
+ onClick: (t) => {
182
+ t.stopPropagation(), R(e);
183
+ },
184
+ className: t("relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:opacity-60", n ? "border-transparent bg-action-primary-bg" : "border-border-subtle bg-bg-sunken"),
185
+ children: /* @__PURE__ */ _("span", {
186
+ "aria-hidden": "true",
187
+ className: t("inline-block h-3.5 w-3.5 transform rounded-full shadow-sm transition-transform", n ? "translate-x-4 bg-action-primary-text" : "translate-x-0.5 bg-text-secondary")
188
+ })
189
+ }), /* @__PURE__ */ _("span", {
190
+ className: "text-xs text-text-secondary",
191
+ children: n ? S("adaptercloud.policies.active", "Active") : S("adaptercloud.policies.inactive", "Inactive")
192
+ })]
193
+ })
194
+ }),
195
+ e.appliesTo.length > 0 && /* @__PURE__ */ _(E, {
196
+ label: S("adaptercloud.policies.appliesTo", "Applies to"),
197
+ children: /* @__PURE__ */ v("div", {
198
+ className: "flex flex-wrap items-center gap-1.5",
199
+ children: [e.appliesTo.slice(0, 2).map((e) => /* @__PURE__ */ _("span", {
200
+ className: "inline-flex items-center rounded-full bg-accent-blue-subtle px-2 py-0.5 font-mono text-xs text-accent-blue",
201
+ children: e
202
+ }, e)), e.appliesTo.length > 2 && /* @__PURE__ */ v("span", {
203
+ className: "inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary",
204
+ children: ["+", e.appliesTo.length - 2]
205
+ })]
206
+ })
207
+ })
208
+ ]
209
+ }), /* @__PURE__ */ v("p", {
210
+ className: "mt-3 text-xs text-text-secondary",
130
211
  children: [
131
212
  S("adaptercloud.common.updated", "Updated"),
132
213
  " ",
133
- c(e.updatedAt)
214
+ l(e.updatedAt)
134
215
  ]
135
- })
136
- ]
137
- }, e.id))
138
- }), (D > 0 || j) && /* @__PURE__ */ _("div", {
216
+ })]
217
+ }, e.id);
218
+ })
219
+ }), (A > 0 || L) && /* @__PURE__ */ v("div", {
139
220
  className: "mt-6 flex items-center justify-between",
140
221
  children: [
141
- /* @__PURE__ */ _("button", {
222
+ /* @__PURE__ */ v("button", {
142
223
  type: "button",
143
- disabled: D === 0,
144
- onClick: () => O((e) => Math.max(0, e - y)),
224
+ disabled: A === 0,
225
+ onClick: () => j((e) => Math.max(0, e - b)),
145
226
  className: "inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
146
- children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), S("adaptercloud.common.prev", "Previous")]
227
+ children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), S("adaptercloud.common.prev", "Previous")]
147
228
  }),
148
- /* @__PURE__ */ _("span", {
229
+ /* @__PURE__ */ v("span", {
149
230
  className: "text-sm text-text-secondary",
150
231
  children: [
151
232
  S("adaptercloud.common.showing", "Showing"),
152
233
  " ",
153
- D + 1,
234
+ A + 1,
154
235
  "–",
155
- D + A.length
236
+ A + I.length
156
237
  ]
157
238
  }),
158
- /* @__PURE__ */ _("button", {
239
+ /* @__PURE__ */ v("button", {
159
240
  type: "button",
160
- disabled: !j,
161
- onClick: () => O((e) => e + y),
241
+ disabled: !L,
242
+ onClick: () => j((e) => e + b),
162
243
  className: "inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50",
163
- children: [S("adaptercloud.common.next", "Next"), /* @__PURE__ */ g(p, { className: "h-4 w-4" })]
244
+ children: [S("adaptercloud.common.next", "Next"), /* @__PURE__ */ _(m, { className: "h-4 w-4" })]
164
245
  })
165
246
  ]
166
247
  })]
@@ -168,6 +249,6 @@ function b() {
168
249
  });
169
250
  }
170
251
  //#endregion
171
- export { b as PoliciesListPage };
252
+ export { D as PoliciesListPage };
172
253
 
173
254
  //# sourceMappingURL=PoliciesListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PoliciesListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PoliciesListPage.tsx"],"sourcesContent":["/**\n * Policies List Page\n * @module adaptercloud/pages/policies\n *\n * Governance rules evaluated against the inventory (`adaListPolicies`). Filters\n * by kind and active flag; offset pagination.\n */\n\nimport { useState, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ScrollText, Plus, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { SeverityBadge } from '../../components/SeverityBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { usePolicies } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels } from '../../utils/domain';\nimport { formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { AdaPolicyKind } from '../../types';\n\nconst PAGE_SIZE = 20;\n\ntype ActiveFilter = 'all' | 'active' | 'inactive';\n\nexport function PoliciesListPage() {\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const [kind, setKind] = useState<AdaPolicyKind | ''>('');\n const [active, setActive] = useState<ActiveFilter>('all');\n const [offset, setOffset] = useState(0);\n\n const query = usePolicies({\n kind: kind || undefined,\n isActive: active === 'all' ? undefined : active === 'active',\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title={t('adaptercloud.policies.title', 'Policies')}\n description={t(\n 'adaptercloud.policies.subtitle',\n 'Governance rules that evaluate the inventory — guardrail, tagging, security, cost, compliance and naming.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/policies/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" />\n {t('adaptercloud.policies.new', 'New policy')}\n </button>\n }\n >\n <div className=\"mb-6 flex flex-wrap gap-3\">\n <select\n value={kind}\n onChange={(e) => {\n setKind(e.target.value as AdaPolicyKind | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.policies.filterByKind', 'Filter by kind')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.policies.allKinds', 'All kinds')}</option>\n {(Object.keys(policyKindLabels) as AdaPolicyKind[]).map((k) => (\n <option key={k} value={k}>\n {policyKindLabels[k]}\n </option>\n ))}\n </select>\n <select\n value={active}\n onChange={(e) => {\n setActive(e.target.value as ActiveFilter);\n setOffset(0);\n }}\n aria-label={t('adaptercloud.policies.filterByActive', 'Filter by active state')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"all\">{t('adaptercloud.policies.allStates', 'All')}</option>\n <option value=\"active\">{t('adaptercloud.policies.active', 'Active')}</option>\n <option value=\"inactive\">{t('adaptercloud.policies.inactive', 'Inactive')}</option>\n </select>\n </div>\n\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={items.length === 0}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.policies.emptyTitle', 'No policies yet')}\n emptyDescription={t(\n 'adaptercloud.policies.emptyDescription',\n 'Author your first governance policy to put compliance into the system.'\n )}\n emptyIcon={<ScrollText className=\"h-10 w-10\" />}\n emptyAction={\n <button\n type=\"button\"\n onClick={() => navigateTo('/policies/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n {t('adaptercloud.policies.new', 'New policy')}\n </button>\n }\n >\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {items.map((policy) => (\n <GlassCard\n key={policy.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigateTo(`/policies/${policy.id}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigateTo(`/policies/${policy.id}`);\n }\n }}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {policy.name}\n </h3>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {policyKindLabels[policy.kind]}\n </p>\n </div>\n <SeverityBadge severity={policy.severity} />\n </div>\n\n {policy.description && (\n <p className=\"mt-3 line-clamp-2 text-sm text-text-secondary\">\n {policy.description}\n </p>\n )}\n\n <div className=\"mt-4 flex items-center gap-2\">\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {policyEnforcementLabels[policy.enforcement]}\n </span>\n <span\n className={cn(\n 'inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium',\n policy.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-secondary'\n )}\n >\n <span\n className={cn(\n 'h-1.5 w-1.5 rounded-full',\n policy.isActive ? 'bg-status-success-text' : 'bg-text-secondary'\n )}\n />\n {policy.isActive\n ? t('adaptercloud.policies.active', 'Active')\n : t('adaptercloud.policies.inactive', 'Inactive')}\n </span>\n </div>\n\n <p className=\"mt-4 text-xs text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')} {formatRelativeTime(policy.updatedAt)}\n </p>\n </GlassCard>\n ))}\n </div>\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.prev', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PoliciesListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PoliciesListPage.tsx"],"sourcesContent":["/**\n * Policies List Page\n * @module adaptercloud/pages/policies\n *\n * Governance rules evaluated against the inventory (`adaListPolicies`). Filters\n * by kind and active flag; offset pagination. Each row surfaces color-coded\n * KIND / ENFORCEMENT / SEVERITY badges, an applies-to chip set, and an inline\n * Active toggle wired to `adaUpdatePolicy` (optimistic, toasts on success).\n */\n\nimport { useState, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ScrollText, Plus, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { SeverityBadge } from '../../components/SeverityBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { usePolicies, useUpdatePolicy } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels } from '../../utils/domain';\nimport { formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { AdaPolicy, AdaPolicyEnforcement, AdaPolicyKind } from '../../types';\n\nconst PAGE_SIZE = 20;\n\ntype ActiveFilter = 'all' | 'active' | 'inactive';\n\n// ---------------------------------------------------------------------------\n// Color-coded governance badges (semantic tokens only), mirroring SeverityBadge.\n// ---------------------------------------------------------------------------\n\nconst badgeBase = 'inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium';\n\n/** Kind → semantic token. Security/Guardrail/Compliance read as governance\n * (accent-blue), Cost as spend (success/green), the rest as neutral. */\nconst policyKindBadgeClass: Record<AdaPolicyKind, string> = {\n SECURITY: 'bg-accent-blue-subtle text-accent-blue',\n GUARDRAIL: 'bg-accent-blue-subtle text-accent-blue',\n COMPLIANCE: 'bg-accent-blue-subtle text-accent-blue',\n COST: 'bg-status-success-bg-subtle text-status-success-text',\n NAMING: 'bg-bg-sunken text-text-secondary',\n TAGGING: 'bg-bg-sunken text-text-secondary',\n};\n\n/** Enforcement → semantic token. Block = error, Warn = warning, Advisory = neutral. */\nconst policyEnforcementBadgeClass: Record<AdaPolicyEnforcement, string> = {\n BLOCK: 'bg-status-error-bg-subtle text-status-error-text',\n WARN: 'bg-status-warning-bg-subtle text-status-warning-text',\n ADVISORY: 'bg-bg-sunken text-text-secondary',\n};\n\nfunction KindBadge({ kind }: { kind: AdaPolicyKind }) {\n return <span className={cn(badgeBase, policyKindBadgeClass[kind])}>{policyKindLabels[kind]}</span>;\n}\n\nfunction EnforcementBadge({ enforcement }: { enforcement: AdaPolicyEnforcement }) {\n return (\n <span className={cn(badgeBase, policyEnforcementBadgeClass[enforcement])}>\n {policyEnforcementLabels[enforcement]}\n </span>\n );\n}\n\n/** Labeled meta column (eyebrow + value) used across each policy row. */\nfunction MetaCol({ label, children }: { label: string; children: React.ReactNode }) {\n return (\n <div className=\"flex flex-col gap-1\">\n <span className=\"text-xs font-semibold uppercase tracking-wide text-text-secondary\">\n {label}\n </span>\n {children}\n </div>\n );\n}\n\nexport function PoliciesListPage() {\n const { navigateTo } = useAdapterCloud();\n const { t: translate } = useI18n();\n const t = useCallback(\n (key: string, fallback: string): string => {\n const value = translate(key);\n return value === key ? fallback : value;\n },\n [translate]\n );\n\n const [kind, setKind] = useState<AdaPolicyKind | ''>('');\n const [active, setActive] = useState<ActiveFilter>('all');\n const [offset, setOffset] = useState(0);\n // Local optimistic override of isActive per policy id while the mutation is in\n // flight, so the toggle flips instantly instead of waiting for the refetch.\n const [pendingActive, setPendingActive] = useState<Record<string, boolean>>({});\n\n const query = usePolicies({\n kind: kind || undefined,\n isActive: active === 'all' ? undefined : active === 'active',\n pagination: { limit: PAGE_SIZE, offset },\n });\n const update = useUpdatePolicy();\n\n const items = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n const toggleActive = useCallback(\n (policy: AdaPolicy) => {\n const next = !(pendingActive[policy.id] ?? policy.isActive);\n setPendingActive((m) => ({ ...m, [policy.id]: next }));\n update.mutate(\n { id: policy.id, input: { isActive: next } },\n {\n onSettled: () =>\n setPendingActive((m) => {\n const copy = { ...m };\n delete copy[policy.id];\n return copy;\n }),\n }\n );\n },\n [update, pendingActive]\n );\n\n return (\n <PageLayout\n title={t('adaptercloud.policies.title', 'Policies')}\n description={t(\n 'adaptercloud.policies.subtitle',\n 'Governance rules that evaluate the inventory — guardrail, tagging, security, cost, compliance and naming.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/policies/new')}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n <Plus className=\"h-4 w-4\" />\n {t('adaptercloud.policies.new', 'New policy')}\n </button>\n }\n >\n <div className=\"mb-6 flex flex-wrap gap-3\">\n <select\n value={kind}\n onChange={(e) => {\n setKind(e.target.value as AdaPolicyKind | '');\n setOffset(0);\n }}\n aria-label={t('adaptercloud.policies.filterByKind', 'Filter by kind')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"\">{t('adaptercloud.policies.allKinds', 'All kinds')}</option>\n {(Object.keys(policyKindLabels) as AdaPolicyKind[]).map((k) => (\n <option key={k} value={k}>\n {policyKindLabels[k]}\n </option>\n ))}\n </select>\n <select\n value={active}\n onChange={(e) => {\n setActive(e.target.value as ActiveFilter);\n setOffset(0);\n }}\n aria-label={t('adaptercloud.policies.filterByActive', 'Filter by active state')}\n className=\"rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"all\">{t('adaptercloud.policies.allStates', 'All')}</option>\n <option value=\"active\">{t('adaptercloud.policies.active', 'Active')}</option>\n <option value=\"inactive\">{t('adaptercloud.policies.inactive', 'Inactive')}</option>\n </select>\n </div>\n\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={items.length === 0}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.policies.emptyTitle', 'No policies yet')}\n emptyDescription={t(\n 'adaptercloud.policies.emptyDescription',\n 'Author your first governance policy to put compliance into the system.'\n )}\n emptyIcon={<ScrollText className=\"h-10 w-10\" />}\n emptyAction={\n <button\n type=\"button\"\n onClick={() => navigateTo('/policies/new')}\n className=\"rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90\"\n >\n {t('adaptercloud.policies.new', 'New policy')}\n </button>\n }\n >\n <div className=\"space-y-3\">\n {items.map((policy) => {\n const isActiveNow = pendingActive[policy.id] ?? policy.isActive;\n const isToggling = policy.id in pendingActive;\n return (\n <GlassCard\n key={policy.id}\n role=\"button\"\n tabIndex={0}\n onClick={() => navigateTo(`/policies/${policy.id}`)}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n navigateTo(`/policies/${policy.id}`);\n }\n }}\n className=\"cursor-pointer p-5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <div className=\"flex flex-col gap-4 lg:flex-row lg:items-center lg:gap-6\">\n <div className=\"min-w-0 lg:flex-1\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {policy.name}\n </h3>\n {policy.description && (\n <p className=\"mt-0.5 line-clamp-1 text-sm text-text-secondary\">\n {policy.description}\n </p>\n )}\n </div>\n\n <MetaCol label={t('adaptercloud.policies.kind', 'Kind')}>\n <KindBadge kind={policy.kind} />\n </MetaCol>\n\n <MetaCol label={t('adaptercloud.policies.enforcement', 'Enforcement')}>\n <EnforcementBadge enforcement={policy.enforcement} />\n </MetaCol>\n\n <MetaCol label={t('adaptercloud.policies.severity', 'Severity')}>\n <SeverityBadge severity={policy.severity} />\n </MetaCol>\n\n <MetaCol label={t('adaptercloud.policies.activeLabel', 'Active')}>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isActiveNow}\n aria-label={\n isActiveNow\n ? t('adaptercloud.policies.deactivate', 'Deactivate policy')\n : t('adaptercloud.policies.activate', 'Activate policy')\n }\n disabled={isToggling}\n onClick={(e) => {\n e.stopPropagation();\n toggleActive(policy);\n }}\n className={cn(\n 'relative inline-flex h-5 w-9 shrink-0 items-center rounded-full border transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:opacity-60',\n isActiveNow\n ? 'border-transparent bg-action-primary-bg'\n : 'border-border-subtle bg-bg-sunken'\n )}\n >\n <span\n aria-hidden=\"true\"\n className={cn(\n 'inline-block h-3.5 w-3.5 transform rounded-full shadow-sm transition-transform',\n isActiveNow\n ? 'translate-x-4 bg-action-primary-text'\n : 'translate-x-0.5 bg-text-secondary'\n )}\n />\n </button>\n <span className=\"text-xs text-text-secondary\">\n {isActiveNow\n ? t('adaptercloud.policies.active', 'Active')\n : t('adaptercloud.policies.inactive', 'Inactive')}\n </span>\n </div>\n </MetaCol>\n\n {policy.appliesTo.length > 0 && (\n <MetaCol label={t('adaptercloud.policies.appliesTo', 'Applies to')}>\n <div className=\"flex flex-wrap items-center gap-1.5\">\n {policy.appliesTo.slice(0, 2).map((sel) => (\n <span\n key={sel}\n className=\"inline-flex items-center rounded-full bg-accent-blue-subtle px-2 py-0.5 font-mono text-xs text-accent-blue\"\n >\n {sel}\n </span>\n ))}\n {policy.appliesTo.length > 2 && (\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n +{policy.appliesTo.length - 2}\n </span>\n )}\n </div>\n </MetaCol>\n )}\n </div>\n\n <p className=\"mt-3 text-xs text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')} {formatRelativeTime(policy.updatedAt)}\n </p>\n </GlassCard>\n );\n })}\n </div>\n\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-between\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.prev', 'Previous')}\n </button>\n <span className=\"text-sm text-text-secondary\">\n {t('adaptercloud.common.showing', 'Showing')} {offset + 1}–{offset + items.length}\n </span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {t('adaptercloud.common.next', 'Next')}\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}