@burdenoff/microfe-adaptercloud 2026.706.1 → 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 (53) 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 +22 -14
  4. package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
  5. package/dist/adaptercloud/components/ConfirmDialog.js +27 -0
  6. package/dist/adaptercloud/components/ConfirmDialog.js.map +1 -0
  7. package/dist/adaptercloud/components/DataState.js +1 -1
  8. package/dist/adaptercloud/components/DataState.js.map +1 -1
  9. package/dist/adaptercloud/components/viz/BarChart.js +32 -0
  10. package/dist/adaptercloud/components/viz/BarChart.js.map +1 -0
  11. package/dist/adaptercloud/components/viz/Sparkline.js +50 -0
  12. package/dist/adaptercloud/components/viz/Sparkline.js.map +1 -0
  13. package/dist/adaptercloud/components/viz/StackedBar.js +1 -0
  14. package/dist/adaptercloud/components/viz/TopologyGraph.js +96 -0
  15. package/dist/adaptercloud/components/viz/TopologyGraph.js.map +1 -0
  16. package/dist/adaptercloud/components/viz/index.js +4 -0
  17. package/dist/adaptercloud/hooks/useAdapterCloudApi.js +71 -34
  18. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  19. package/dist/adaptercloud/pages/OverviewPage.js +159 -156
  20. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  21. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +179 -113
  22. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
  23. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +263 -179
  24. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
  25. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +2 -0
  26. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  27. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +141 -124
  28. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
  29. package/dist/adaptercloud/pages/connections/ConnectionFormPage.js +2 -0
  30. package/dist/adaptercloud/pages/connections/ConnectionFormPage.js.map +1 -1
  31. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +158 -93
  32. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  33. package/dist/adaptercloud/pages/costs/CostsPage.js +183 -109
  34. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  35. package/dist/adaptercloud/pages/drift/DriftListPage.js +172 -71
  36. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  37. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +179 -96
  38. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  39. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +109 -91
  40. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
  41. package/dist/adaptercloud/pages/policies/PolicyFormPage.js +211 -184
  42. package/dist/adaptercloud/pages/policies/PolicyFormPage.js.map +1 -1
  43. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +183 -127
  44. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
  45. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +121 -84
  46. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  47. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +236 -153
  48. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  49. package/dist/adaptercloud/pages/topology/TopologyPage.js +397 -0
  50. package/dist/adaptercloud/pages/topology/TopologyPage.js.map +1 -0
  51. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +192 -131
  52. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  53. package/package.json +1 -1
@@ -1,215 +1,242 @@
1
1
  import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
2
2
  import { PageLayout as t } from "../../components/PageLayout.js";
3
3
  import { policyEnforcementLabels as n, policyKindLabels as r, severityLabels as i } from "../../utils/domain.js";
4
- import { useCreatePolicy as a } from "../../hooks/useAdapterCloudApi.js";
5
- import { useCallback as o, useState as s } from "react";
6
- import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
- import { ArrowLeft as l, Loader2 as u, Save as d } from "lucide-react";
8
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
9
- import { GlassCard as m } from "@burdenoff/fe-libs/ui";
4
+ import { DataState as a } from "../../components/DataState.js";
5
+ import { useCreatePolicy as o, usePolicy as s, useUpdatePolicy as c } from "../../hooks/useAdapterCloudApi.js";
6
+ import { useCallback as l, useEffect as u, useState as d } from "react";
7
+ import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
+ import { ArrowLeft as p, Loader2 as m, Save as h } from "lucide-react";
9
+ import { useParams as g } from "react-router-dom";
10
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
11
+ import { GlassCard as y } from "@burdenoff/fe-libs/ui";
10
12
  //#region src/adaptercloud/pages/policies/PolicyFormPage.tsx
11
- var h = (e) => e.split(",").map((e) => e.trim()).filter(Boolean), g = "{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}";
12
- function _() {
13
- let { navigateTo: _ } = e(), { t: v } = c(), y = o((e, t) => {
14
- let n = v(e);
13
+ var b = (e) => e.split(",").map((e) => e.trim()).filter(Boolean), x = "{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}";
14
+ function S() {
15
+ let { id: S } = g(), C = !!S, { navigateTo: w } = e(), { t: T } = f(), E = l((e, t) => {
16
+ let n = T(e);
15
17
  return n === e ? t : n;
16
- }, [v]), b = a(), [x, S] = s(""), [C, w] = s(""), [T, E] = s("GUARDRAIL"), [D, O] = s("WARN"), [k, A] = s("MEDIUM"), [j, M] = s(""), [N, P] = s(""), [F, I] = s(!0), [L, R] = s(g), [z, B] = s(null), V = o((e) => {
17
- if (e.preventDefault(), B(null), !x.trim()) {
18
- B(y("adaptercloud.policies.form.nameRequired", "Name is required."));
18
+ }, [T]), D = s(C ? S : void 0), O = o(), k = c(), [A, j] = d(""), [M, N] = d(""), [P, F] = d("GUARDRAIL"), [I, L] = d("WARN"), [R, z] = d("MEDIUM"), [B, V] = d(""), [H, U] = d(""), [W, G] = d(!0), [K, q] = d(x), [J, Y] = d(null);
19
+ u(() => {
20
+ let e = D.data;
21
+ C && e && (j(e.name), N(e.description ?? ""), F(e.kind), L(e.enforcement), z(e.severity), V(e.appliesTo.join(", ")), U(e.tags.join(", ")), G(e.isActive), q(JSON.stringify(e.expression, null, 2)));
22
+ }, [C, D.data]);
23
+ let X = O.isPending || k.isPending, Z = l((e) => {
24
+ if (e.preventDefault(), Y(null), !A.trim()) {
25
+ Y(E("adaptercloud.policies.form.nameRequired", "Name is required."));
19
26
  return;
20
27
  }
21
28
  let t;
22
29
  try {
23
- t = JSON.parse(L);
30
+ t = JSON.parse(K);
24
31
  } catch {
25
- B(y("adaptercloud.policies.form.invalidExpression", "Expression must be valid JSON."));
32
+ Y(E("adaptercloud.policies.form.invalidExpression", "Expression must be valid JSON."));
26
33
  return;
27
34
  }
28
- b.mutate({
29
- name: x.trim(),
30
- description: C.trim() || void 0,
31
- kind: T,
32
- enforcement: D,
33
- severity: k,
34
- isActive: F,
35
- appliesTo: h(j),
36
- tags: h(N),
35
+ let n = {
36
+ name: A.trim(),
37
+ description: M.trim() || void 0,
38
+ kind: P,
39
+ enforcement: I,
40
+ severity: R,
41
+ isActive: W,
42
+ appliesTo: b(B),
43
+ tags: b(H),
37
44
  expression: t
45
+ }, r = (e) => Y(e instanceof Error ? e.message : "Failed to save policy.");
46
+ C && S ? k.mutate({
47
+ id: S,
48
+ input: n
38
49
  }, {
39
- onSuccess: (e) => _(`/policies/${e.id}`),
40
- onError: (e) => B(e instanceof Error ? e.message : "Failed to create policy.")
50
+ onSuccess: (e) => w(`/policies/${e.id}`),
51
+ onError: r
52
+ }) : O.mutate(n, {
53
+ onSuccess: (e) => w(`/policies/${e.id}`),
54
+ onError: r
41
55
  });
42
56
  }, [
43
- b,
44
- x,
45
- C,
46
- T,
47
- D,
57
+ O,
48
58
  k,
49
- F,
50
- j,
51
- N,
52
- L,
53
- _,
54
- y
55
- ]), H = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", U = "mb-1.5 block text-sm font-medium text-text-primary";
56
- return /* @__PURE__ */ f(t, {
57
- title: y("adaptercloud.policies.newTitle", "New Policy"),
58
- description: y("adaptercloud.policies.formSubtitle", "Put a governance rule into the system instead of a wiki."),
59
- actions: /* @__PURE__ */ p("button", {
59
+ S,
60
+ C,
61
+ A,
62
+ M,
63
+ P,
64
+ I,
65
+ R,
66
+ W,
67
+ B,
68
+ H,
69
+ K,
70
+ w,
71
+ E
72
+ ]), Q = "w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]", $ = "mb-1.5 block text-sm font-medium text-text-primary";
73
+ return /* @__PURE__ */ _(t, {
74
+ title: C ? E("adaptercloud.policies.editTitle", "Edit Policy") : E("adaptercloud.policies.newTitle", "New Policy"),
75
+ description: E("adaptercloud.policies.formSubtitle", "Put a governance rule into the system instead of a wiki."),
76
+ actions: /* @__PURE__ */ v("button", {
60
77
  type: "button",
61
- onClick: () => _("/policies"),
78
+ onClick: () => w(C && S ? `/policies/${S}` : "/policies"),
62
79
  className: "inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent",
63
- children: [/* @__PURE__ */ f(l, { className: "h-4 w-4" }), y("adaptercloud.common.cancel", "Cancel")]
80
+ children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), E("adaptercloud.common.cancel", "Cancel")]
64
81
  }),
65
- children: /* @__PURE__ */ f("form", {
66
- onSubmit: V,
67
- className: "max-w-2xl",
68
- children: /* @__PURE__ */ p(m, {
69
- className: "space-y-5 p-6",
70
- children: [
71
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("label", {
72
- htmlFor: "ada-pol-name",
73
- className: U,
74
- children: [y("adaptercloud.policies.form.name", "Name"), " *"]
75
- }), /* @__PURE__ */ f("input", {
76
- id: "ada-pol-name",
77
- type: "text",
78
- value: x,
79
- onChange: (e) => S(e.target.value),
80
- className: H,
81
- placeholder: "Require owner + env tags"
82
- })] }),
83
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
84
- htmlFor: "ada-pol-desc",
85
- className: U,
86
- children: y("adaptercloud.policies.form.description", "Description")
87
- }), /* @__PURE__ */ f("textarea", {
88
- id: "ada-pol-desc",
89
- value: C,
90
- onChange: (e) => w(e.target.value),
91
- rows: 2,
92
- className: H,
93
- placeholder: y("adaptercloud.policies.form.descriptionPlaceholder", "What this policy enforces and why.")
94
- })] }),
95
- /* @__PURE__ */ p("div", {
96
- className: "grid grid-cols-1 gap-5 sm:grid-cols-3",
97
- children: [
98
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
99
- htmlFor: "ada-pol-kind",
100
- className: U,
101
- children: y("adaptercloud.policies.form.kind", "Kind")
102
- }), /* @__PURE__ */ f("select", {
103
- id: "ada-pol-kind",
104
- value: T,
105
- onChange: (e) => E(e.target.value),
106
- className: H,
107
- children: Object.keys(r).map((e) => /* @__PURE__ */ f("option", {
108
- value: e,
109
- children: r[e]
110
- }, e))
111
- })] }),
112
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
113
- htmlFor: "ada-pol-enf",
114
- className: U,
115
- children: y("adaptercloud.policies.form.enforcement", "Enforcement")
116
- }), /* @__PURE__ */ f("select", {
117
- id: "ada-pol-enf",
118
- value: D,
119
- onChange: (e) => O(e.target.value),
120
- className: H,
121
- children: Object.keys(n).map((e) => /* @__PURE__ */ f("option", {
122
- value: e,
123
- children: n[e]
124
- }, e))
125
- })] }),
126
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
127
- htmlFor: "ada-pol-sev",
128
- className: U,
129
- children: y("adaptercloud.policies.form.severity", "Severity")
130
- }), /* @__PURE__ */ f("select", {
131
- id: "ada-pol-sev",
132
- value: k,
133
- onChange: (e) => A(e.target.value),
134
- className: H,
135
- children: Object.keys(i).map((e) => /* @__PURE__ */ f("option", {
136
- value: e,
137
- children: i[e]
138
- }, e))
139
- })] })
140
- ]
141
- }),
142
- /* @__PURE__ */ p("div", { children: [
143
- /* @__PURE__ */ f("label", {
144
- htmlFor: "ada-pol-applies",
145
- className: U,
146
- children: y("adaptercloud.policies.form.appliesTo", "Applies To")
82
+ children: /* @__PURE__ */ _(a, {
83
+ isLoading: C && D.isLoading,
84
+ isError: C && D.isError,
85
+ isEmpty: C && !D.isLoading && !D.data,
86
+ error: D.error,
87
+ onRetry: () => void D.refetch(),
88
+ emptyTitle: E("adaptercloud.policies.notFound", "Policy not found"),
89
+ children: /* @__PURE__ */ _("form", {
90
+ onSubmit: Z,
91
+ className: "max-w-2xl",
92
+ children: /* @__PURE__ */ v(y, {
93
+ className: "space-y-5 p-6",
94
+ children: [
95
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ v("label", {
96
+ htmlFor: "ada-pol-name",
97
+ className: $,
98
+ children: [E("adaptercloud.policies.form.name", "Name"), " *"]
99
+ }), /* @__PURE__ */ _("input", {
100
+ id: "ada-pol-name",
101
+ type: "text",
102
+ value: A,
103
+ onChange: (e) => j(e.target.value),
104
+ className: Q,
105
+ placeholder: "Require owner + env tags"
106
+ })] }),
107
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
108
+ htmlFor: "ada-pol-desc",
109
+ className: $,
110
+ children: E("adaptercloud.policies.form.description", "Description")
111
+ }), /* @__PURE__ */ _("textarea", {
112
+ id: "ada-pol-desc",
113
+ value: M,
114
+ onChange: (e) => N(e.target.value),
115
+ rows: 2,
116
+ className: Q,
117
+ placeholder: E("adaptercloud.policies.form.descriptionPlaceholder", "What this policy enforces and why.")
118
+ })] }),
119
+ /* @__PURE__ */ v("div", {
120
+ className: "grid grid-cols-1 gap-5 sm:grid-cols-3",
121
+ children: [
122
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
123
+ htmlFor: "ada-pol-kind",
124
+ className: $,
125
+ children: E("adaptercloud.policies.form.kind", "Kind")
126
+ }), /* @__PURE__ */ _("select", {
127
+ id: "ada-pol-kind",
128
+ value: P,
129
+ onChange: (e) => F(e.target.value),
130
+ className: Q,
131
+ children: Object.keys(r).map((e) => /* @__PURE__ */ _("option", {
132
+ value: e,
133
+ children: r[e]
134
+ }, e))
135
+ })] }),
136
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
137
+ htmlFor: "ada-pol-enf",
138
+ className: $,
139
+ children: E("adaptercloud.policies.form.enforcement", "Enforcement")
140
+ }), /* @__PURE__ */ _("select", {
141
+ id: "ada-pol-enf",
142
+ value: I,
143
+ onChange: (e) => L(e.target.value),
144
+ className: Q,
145
+ children: Object.keys(n).map((e) => /* @__PURE__ */ _("option", {
146
+ value: e,
147
+ children: n[e]
148
+ }, e))
149
+ })] }),
150
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
151
+ htmlFor: "ada-pol-sev",
152
+ className: $,
153
+ children: E("adaptercloud.policies.form.severity", "Severity")
154
+ }), /* @__PURE__ */ _("select", {
155
+ id: "ada-pol-sev",
156
+ value: R,
157
+ onChange: (e) => z(e.target.value),
158
+ className: Q,
159
+ children: Object.keys(i).map((e) => /* @__PURE__ */ _("option", {
160
+ value: e,
161
+ children: i[e]
162
+ }, e))
163
+ })] })
164
+ ]
147
165
  }),
148
- /* @__PURE__ */ f("input", {
149
- id: "ada-pol-applies",
166
+ /* @__PURE__ */ v("div", { children: [
167
+ /* @__PURE__ */ _("label", {
168
+ htmlFor: "ada-pol-applies",
169
+ className: $,
170
+ children: E("adaptercloud.policies.form.appliesTo", "Applies To")
171
+ }),
172
+ /* @__PURE__ */ _("input", {
173
+ id: "ada-pol-applies",
174
+ type: "text",
175
+ value: B,
176
+ onChange: (e) => V(e.target.value),
177
+ className: Q,
178
+ placeholder: "aws:ec2:instance, aws:s3:bucket"
179
+ }),
180
+ /* @__PURE__ */ _("p", {
181
+ className: "mt-1 text-xs text-text-secondary",
182
+ children: E("adaptercloud.common.commaSeparated", "Comma-separated.")
183
+ })
184
+ ] }),
185
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ v("label", {
186
+ htmlFor: "ada-pol-expr",
187
+ className: $,
188
+ children: [E("adaptercloud.policies.form.expression", "Expression (JSON)"), " *"]
189
+ }), /* @__PURE__ */ _("textarea", {
190
+ id: "ada-pol-expr",
191
+ value: K,
192
+ onChange: (e) => q(e.target.value),
193
+ rows: 8,
194
+ spellCheck: !1,
195
+ className: `${Q} font-mono`
196
+ })] }),
197
+ /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
198
+ htmlFor: "ada-pol-tags",
199
+ className: $,
200
+ children: E("adaptercloud.common.tags", "Tags")
201
+ }), /* @__PURE__ */ _("input", {
202
+ id: "ada-pol-tags",
150
203
  type: "text",
151
- value: j,
152
- onChange: (e) => M(e.target.value),
153
- className: H,
154
- placeholder: "aws:ec2:instance, aws:s3:bucket"
204
+ value: H,
205
+ onChange: (e) => U(e.target.value),
206
+ className: Q,
207
+ placeholder: "tagging, governance"
208
+ })] }),
209
+ /* @__PURE__ */ v("label", {
210
+ className: "flex items-center gap-2 text-sm text-text-primary",
211
+ children: [/* @__PURE__ */ _("input", {
212
+ type: "checkbox",
213
+ checked: W,
214
+ onChange: (e) => G(e.target.checked),
215
+ className: "h-4 w-4 rounded border-border-subtle"
216
+ }), E("adaptercloud.policies.form.activeImmediately", "Active immediately")]
155
217
  }),
156
- /* @__PURE__ */ f("p", {
157
- className: "mt-1 text-xs text-text-secondary",
158
- children: y("adaptercloud.common.commaSeparated", "Comma-separated.")
159
- })
160
- ] }),
161
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("label", {
162
- htmlFor: "ada-pol-expr",
163
- className: U,
164
- children: [y("adaptercloud.policies.form.expression", "Expression (JSON)"), " *"]
165
- }), /* @__PURE__ */ f("textarea", {
166
- id: "ada-pol-expr",
167
- value: L,
168
- onChange: (e) => R(e.target.value),
169
- rows: 8,
170
- spellCheck: !1,
171
- className: `${H} font-mono`
172
- })] }),
173
- /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("label", {
174
- htmlFor: "ada-pol-tags",
175
- className: U,
176
- children: y("adaptercloud.common.tags", "Tags")
177
- }), /* @__PURE__ */ f("input", {
178
- id: "ada-pol-tags",
179
- type: "text",
180
- value: N,
181
- onChange: (e) => P(e.target.value),
182
- className: H,
183
- placeholder: "tagging, governance"
184
- })] }),
185
- /* @__PURE__ */ p("label", {
186
- className: "flex items-center gap-2 text-sm text-text-primary",
187
- children: [/* @__PURE__ */ f("input", {
188
- type: "checkbox",
189
- checked: F,
190
- onChange: (e) => I(e.target.checked),
191
- className: "h-4 w-4 rounded border-border-subtle"
192
- }), y("adaptercloud.policies.form.activeImmediately", "Active immediately")]
193
- }),
194
- z && /* @__PURE__ */ f("p", {
195
- className: "rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
196
- children: z
197
- }),
198
- /* @__PURE__ */ f("div", {
199
- className: "flex justify-end gap-3 pt-2",
200
- children: /* @__PURE__ */ p("button", {
201
- type: "submit",
202
- disabled: b.isPending,
203
- className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50",
204
- children: [b.isPending ? /* @__PURE__ */ f(u, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ f(d, { className: "h-4 w-4" }), y("adaptercloud.policies.create", "Create policy")]
218
+ J && /* @__PURE__ */ _("p", {
219
+ role: "alert",
220
+ "aria-live": "polite",
221
+ className: "rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
222
+ children: J
223
+ }),
224
+ /* @__PURE__ */ _("div", {
225
+ className: "flex justify-end gap-3 pt-2",
226
+ children: /* @__PURE__ */ v("button", {
227
+ type: "submit",
228
+ disabled: X,
229
+ className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50",
230
+ children: [X ? /* @__PURE__ */ _(m, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ _(h, { className: "h-4 w-4" }), C ? E("adaptercloud.common.saveChanges", "Save changes") : E("adaptercloud.policies.create", "Create policy")]
231
+ })
205
232
  })
206
- })
207
- ]
233
+ ]
234
+ })
208
235
  })
209
236
  })
210
237
  });
211
238
  }
212
239
  //#endregion
213
- export { _ as PolicyFormPage };
240
+ export { S as PolicyFormPage };
214
241
 
215
242
  //# sourceMappingURL=PolicyFormPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PolicyFormPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PolicyFormPage.tsx"],"sourcesContent":["/**\n * Policy Create Form Page\n * @module adaptercloud/pages/policies\n *\n * Authors a new governance policy (`adaCreatePolicy`). The expression is a JSON\n * document validated client-side before submit.\n */\n\nimport { useCallback, useState } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useCreatePolicy } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels, severityLabels } from '../../utils/domain';\nimport type { AdaPolicyEnforcement, AdaPolicyKind, AdaSeverity } from '../../types';\n\nconst parseList = (value: string): string[] =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\nconst DEFAULT_EXPRESSION = `{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}`;\n\nexport function PolicyFormPage() {\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 create = useCreatePolicy();\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<AdaPolicyKind>('GUARDRAIL');\n const [enforcement, setEnforcement] = useState<AdaPolicyEnforcement>('WARN');\n const [severity, setSeverity] = useState<AdaSeverity>('MEDIUM');\n const [appliesTo, setAppliesTo] = useState('');\n const [tags, setTags] = useState('');\n const [isActive, setIsActive] = useState(true);\n const [expression, setExpression] = useState(DEFAULT_EXPRESSION);\n const [error, setError] = useState<string | null>(null);\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!name.trim()) {\n setError(t('adaptercloud.policies.form.nameRequired', 'Name is required.'));\n return;\n }\n\n let parsedExpression: unknown;\n try {\n parsedExpression = JSON.parse(expression);\n } catch {\n setError(\n t('adaptercloud.policies.form.invalidExpression', 'Expression must be valid JSON.')\n );\n return;\n }\n\n create.mutate(\n {\n name: name.trim(),\n description: description.trim() || undefined,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo: parseList(appliesTo),\n tags: parseList(tags),\n expression: parsedExpression,\n },\n {\n onSuccess: (p) => navigateTo(`/policies/${p.id}`),\n onError: (err) =>\n setError(err instanceof Error ? err.message : 'Failed to create policy.'),\n }\n );\n },\n [\n create,\n name,\n description,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo,\n tags,\n expression,\n navigateTo,\n t,\n ]\n );\n\n const fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n const labelClass = 'mb-1.5 block text-sm font-medium text-text-primary';\n\n return (\n <PageLayout\n title={t('adaptercloud.policies.newTitle', 'New Policy')}\n description={t(\n 'adaptercloud.policies.formSubtitle',\n 'Put a governance rule into the system instead of a wiki.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/policies')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl\">\n <GlassCard className=\"space-y-5 p-6\">\n <div>\n <label htmlFor=\"ada-pol-name\" className={labelClass}>\n {t('adaptercloud.policies.form.name', 'Name')} *\n </label>\n <input\n id=\"ada-pol-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className={fieldClass}\n placeholder=\"Require owner + env tags\"\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-desc\" className={labelClass}>\n {t('adaptercloud.policies.form.description', 'Description')}\n </label>\n <textarea\n id=\"ada-pol-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n placeholder={t(\n 'adaptercloud.policies.form.descriptionPlaceholder',\n 'What this policy enforces and why.'\n )}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"ada-pol-kind\" className={labelClass}>\n {t('adaptercloud.policies.form.kind', 'Kind')}\n </label>\n <select\n id=\"ada-pol-kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as AdaPolicyKind)}\n className={fieldClass}\n >\n {(Object.keys(policyKindLabels) as AdaPolicyKind[]).map((k) => (\n <option key={k} value={k}>\n {policyKindLabels[k]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-pol-enf\" className={labelClass}>\n {t('adaptercloud.policies.form.enforcement', 'Enforcement')}\n </label>\n <select\n id=\"ada-pol-enf\"\n value={enforcement}\n onChange={(e) => setEnforcement(e.target.value as AdaPolicyEnforcement)}\n className={fieldClass}\n >\n {(Object.keys(policyEnforcementLabels) as AdaPolicyEnforcement[]).map((en) => (\n <option key={en} value={en}>\n {policyEnforcementLabels[en]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-pol-sev\" className={labelClass}>\n {t('adaptercloud.policies.form.severity', 'Severity')}\n </label>\n <select\n id=\"ada-pol-sev\"\n value={severity}\n onChange={(e) => setSeverity(e.target.value as AdaSeverity)}\n className={fieldClass}\n >\n {(Object.keys(severityLabels) as AdaSeverity[]).map((s) => (\n <option key={s} value={s}>\n {severityLabels[s]}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-applies\" className={labelClass}>\n {t('adaptercloud.policies.form.appliesTo', 'Applies To')}\n </label>\n <input\n id=\"ada-pol-applies\"\n type=\"text\"\n value={appliesTo}\n onChange={(e) => setAppliesTo(e.target.value)}\n className={fieldClass}\n placeholder=\"aws:ec2:instance, aws:s3:bucket\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-expr\" className={labelClass}>\n {t('adaptercloud.policies.form.expression', 'Expression (JSON)')} *\n </label>\n <textarea\n id=\"ada-pol-expr\"\n value={expression}\n onChange={(e) => setExpression(e.target.value)}\n rows={8}\n spellCheck={false}\n className={`${fieldClass} font-mono`}\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-tags\" className={labelClass}>\n {t('adaptercloud.common.tags', 'Tags')}\n </label>\n <input\n id=\"ada-pol-tags\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n className={fieldClass}\n placeholder=\"tagging, governance\"\n />\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n {t('adaptercloud.policies.form.activeImmediately', 'Active immediately')}\n </label>\n\n {error && (\n <p className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {error}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={create.isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {create.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {t('adaptercloud.policies.create', 'Create policy')}\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"PolicyFormPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PolicyFormPage.tsx"],"sourcesContent":["/**\n * Policy Create / Edit Form Page\n * @module adaptercloud/pages/policies\n *\n * Authors a new governance policy (`adaCreatePolicy`) or edits an existing one\n * (`adaUpdatePolicy`). The presence of a route `:id` switches to edit mode and\n * pre-fills from `adaPolicy` (including the JSON expression). The expression is a\n * JSON document validated client-side before submit.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Loader2, Save } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { usePolicy, useCreatePolicy, useUpdatePolicy } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels, severityLabels } from '../../utils/domain';\nimport type { AdaPolicyEnforcement, AdaPolicyKind, AdaSeverity } from '../../types';\n\nconst parseList = (value: string): string[] =>\n value\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\nconst DEFAULT_EXPRESSION = `{\n \"match\": { \"resourceType\": \"*\" },\n \"require\": { \"tags\": [\"owner\", \"env\"] }\n}`;\n\nexport function PolicyFormPage() {\n const { id } = useParams<{ id: string }>();\n const isEdit = Boolean(id);\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 existing = usePolicy(isEdit ? id : undefined);\n const create = useCreatePolicy();\n const update = useUpdatePolicy();\n\n const [name, setName] = useState('');\n const [description, setDescription] = useState('');\n const [kind, setKind] = useState<AdaPolicyKind>('GUARDRAIL');\n const [enforcement, setEnforcement] = useState<AdaPolicyEnforcement>('WARN');\n const [severity, setSeverity] = useState<AdaSeverity>('MEDIUM');\n const [appliesTo, setAppliesTo] = useState('');\n const [tags, setTags] = useState('');\n const [isActive, setIsActive] = useState(true);\n const [expression, setExpression] = useState(DEFAULT_EXPRESSION);\n const [error, setError] = useState<string | null>(null);\n\n // Pre-fill in edit mode from the loaded policy.\n useEffect(() => {\n const p = existing.data;\n if (isEdit && p) {\n setName(p.name);\n setDescription(p.description ?? '');\n setKind(p.kind);\n setEnforcement(p.enforcement);\n setSeverity(p.severity);\n setAppliesTo(p.appliesTo.join(', '));\n setTags(p.tags.join(', '));\n setIsActive(p.isActive);\n setExpression(JSON.stringify(p.expression, null, 2));\n }\n }, [isEdit, existing.data]);\n\n const isPending = create.isPending || update.isPending;\n\n const handleSubmit = useCallback(\n (e: React.FormEvent) => {\n e.preventDefault();\n setError(null);\n\n if (!name.trim()) {\n setError(t('adaptercloud.policies.form.nameRequired', 'Name is required.'));\n return;\n }\n\n let parsedExpression: unknown;\n try {\n parsedExpression = JSON.parse(expression);\n } catch {\n setError(\n t('adaptercloud.policies.form.invalidExpression', 'Expression must be valid JSON.')\n );\n return;\n }\n\n const common = {\n name: name.trim(),\n description: description.trim() || undefined,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo: parseList(appliesTo),\n tags: parseList(tags),\n expression: parsedExpression,\n };\n\n const onError = (err: unknown) =>\n setError(err instanceof Error ? err.message : 'Failed to save policy.');\n\n if (isEdit && id) {\n update.mutate(\n { id, input: common },\n { onSuccess: (p) => navigateTo(`/policies/${p.id}`), onError }\n );\n } else {\n create.mutate(common, {\n onSuccess: (p) => navigateTo(`/policies/${p.id}`),\n onError,\n });\n }\n },\n [\n create,\n update,\n id,\n isEdit,\n name,\n description,\n kind,\n enforcement,\n severity,\n isActive,\n appliesTo,\n tags,\n expression,\n navigateTo,\n t,\n ]\n );\n\n const fieldClass =\n 'w-full rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]';\n const labelClass = 'mb-1.5 block text-sm font-medium text-text-primary';\n\n return (\n <PageLayout\n title={\n isEdit\n ? t('adaptercloud.policies.editTitle', 'Edit Policy')\n : t('adaptercloud.policies.newTitle', 'New Policy')\n }\n description={t(\n 'adaptercloud.policies.formSubtitle',\n 'Put a governance rule into the system instead of a wiki.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && id ? `/policies/${id}` : '/policies')}\n className=\"inline-flex items-center gap-2 rounded-lg border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-accent\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n {t('adaptercloud.common.cancel', 'Cancel')}\n </button>\n }\n >\n <DataState\n isLoading={isEdit && existing.isLoading}\n isError={isEdit && existing.isError}\n isEmpty={isEdit && !existing.isLoading && !existing.data}\n error={existing.error}\n onRetry={() => void existing.refetch()}\n emptyTitle={t('adaptercloud.policies.notFound', 'Policy not found')}\n >\n <form onSubmit={handleSubmit} className=\"max-w-2xl\">\n <GlassCard className=\"space-y-5 p-6\">\n <div>\n <label htmlFor=\"ada-pol-name\" className={labelClass}>\n {t('adaptercloud.policies.form.name', 'Name')} *\n </label>\n <input\n id=\"ada-pol-name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className={fieldClass}\n placeholder=\"Require owner + env tags\"\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-desc\" className={labelClass}>\n {t('adaptercloud.policies.form.description', 'Description')}\n </label>\n <textarea\n id=\"ada-pol-desc\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\n className={fieldClass}\n placeholder={t(\n 'adaptercloud.policies.form.descriptionPlaceholder',\n 'What this policy enforces and why.'\n )}\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-5 sm:grid-cols-3\">\n <div>\n <label htmlFor=\"ada-pol-kind\" className={labelClass}>\n {t('adaptercloud.policies.form.kind', 'Kind')}\n </label>\n <select\n id=\"ada-pol-kind\"\n value={kind}\n onChange={(e) => setKind(e.target.value as AdaPolicyKind)}\n className={fieldClass}\n >\n {(Object.keys(policyKindLabels) as AdaPolicyKind[]).map((k) => (\n <option key={k} value={k}>\n {policyKindLabels[k]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-pol-enf\" className={labelClass}>\n {t('adaptercloud.policies.form.enforcement', 'Enforcement')}\n </label>\n <select\n id=\"ada-pol-enf\"\n value={enforcement}\n onChange={(e) => setEnforcement(e.target.value as AdaPolicyEnforcement)}\n className={fieldClass}\n >\n {(Object.keys(policyEnforcementLabels) as AdaPolicyEnforcement[]).map((en) => (\n <option key={en} value={en}>\n {policyEnforcementLabels[en]}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label htmlFor=\"ada-pol-sev\" className={labelClass}>\n {t('adaptercloud.policies.form.severity', 'Severity')}\n </label>\n <select\n id=\"ada-pol-sev\"\n value={severity}\n onChange={(e) => setSeverity(e.target.value as AdaSeverity)}\n className={fieldClass}\n >\n {(Object.keys(severityLabels) as AdaSeverity[]).map((s) => (\n <option key={s} value={s}>\n {severityLabels[s]}\n </option>\n ))}\n </select>\n </div>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-applies\" className={labelClass}>\n {t('adaptercloud.policies.form.appliesTo', 'Applies To')}\n </label>\n <input\n id=\"ada-pol-applies\"\n type=\"text\"\n value={appliesTo}\n onChange={(e) => setAppliesTo(e.target.value)}\n className={fieldClass}\n placeholder=\"aws:ec2:instance, aws:s3:bucket\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {t('adaptercloud.common.commaSeparated', 'Comma-separated.')}\n </p>\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-expr\" className={labelClass}>\n {t('adaptercloud.policies.form.expression', 'Expression (JSON)')} *\n </label>\n <textarea\n id=\"ada-pol-expr\"\n value={expression}\n onChange={(e) => setExpression(e.target.value)}\n rows={8}\n spellCheck={false}\n className={`${fieldClass} font-mono`}\n />\n </div>\n\n <div>\n <label htmlFor=\"ada-pol-tags\" className={labelClass}>\n {t('adaptercloud.common.tags', 'Tags')}\n </label>\n <input\n id=\"ada-pol-tags\"\n type=\"text\"\n value={tags}\n onChange={(e) => setTags(e.target.value)}\n className={fieldClass}\n placeholder=\"tagging, governance\"\n />\n </div>\n\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"h-4 w-4 rounded border-border-subtle\"\n />\n {t('adaptercloud.policies.form.activeImmediately', 'Active immediately')}\n </label>\n\n {error && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {error}\n </p>\n )}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <button\n type=\"submit\"\n disabled={isPending}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n {isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit\n ? t('adaptercloud.common.saveChanges', 'Save changes')\n : t('adaptercloud.policies.create', 'Create policy')}\n </button>\n </div>\n </GlassCard>\n </form>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}