@burdenoff/microfe-adaptercloud 2026.629.1 → 2026.706.2

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 (50) hide show
  1. package/dist/adaptercloud/AdapterCloudRoot.js +39 -33
  2. package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
  3. package/dist/adaptercloud/AdapterCloudRoutes.js +5 -1
  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/hooks/useAdapterCloudApi.js +246 -220
  10. package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
  11. package/dist/adaptercloud/lib/toast.js +36 -0
  12. package/dist/adaptercloud/lib/toast.js.map +1 -0
  13. package/dist/adaptercloud/pages/OverviewPage.js +159 -156
  14. package/dist/adaptercloud/pages/OverviewPage.js.map +1 -1
  15. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +102 -89
  16. package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -1
  17. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +2 -0
  18. package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -1
  19. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +2 -0
  20. package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -1
  21. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +141 -124
  22. package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -1
  23. package/dist/adaptercloud/pages/connections/ConnectionFormPage.js +2 -0
  24. package/dist/adaptercloud/pages/connections/ConnectionFormPage.js.map +1 -1
  25. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +1 -0
  26. package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -1
  27. package/dist/adaptercloud/pages/costs/CostsPage.js +112 -87
  28. package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -1
  29. package/dist/adaptercloud/pages/drift/DriftListPage.js +81 -59
  30. package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -1
  31. package/dist/adaptercloud/pages/policies/PoliciesListPage.js +2 -0
  32. package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -1
  33. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +109 -91
  34. package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -1
  35. package/dist/adaptercloud/pages/policies/PolicyFormPage.js +211 -184
  36. package/dist/adaptercloud/pages/policies/PolicyFormPage.js.map +1 -1
  37. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +153 -111
  38. package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -1
  39. package/dist/adaptercloud/pages/resources/ResourcesListPage.js +111 -83
  40. package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -1
  41. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +108 -86
  42. package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -1
  43. package/dist/adaptercloud/pages/violations/ViolationsListPage.js +11 -3
  44. package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -1
  45. package/dist/adaptercloud/utils/domain.js +1 -0
  46. package/dist/adaptercloud/utils/domain.js.map +1 -1
  47. package/dist/adaptercloud/utils/formatters.js +3 -15
  48. package/dist/adaptercloud/utils/formatters.js.map +1 -1
  49. package/dist/index.js +4 -4
  50. package/package.json +2 -1
@@ -6,127 +6,149 @@ import { SeverityBadge as a } from "../../components/SeverityBadge.js";
6
6
  import { DataState as o } from "../../components/DataState.js";
7
7
  import { useDriftRecords as s, useRemediateDrift as c } from "../../hooks/useAdapterCloudApi.js";
8
8
  import { formatDate 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 { GitCompareArrows as p, Loader2 as m, Wrench 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";
9
+ import { ConfirmDialog as u } from "../../components/ConfirmDialog.js";
10
+ import { useCallback as d, useState as f } from "react";
11
+ import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ import { GitCompareArrows as m, Loader2 as h, Wrench as g } from "lucide-react";
13
+ import { jsx as _, jsxs as v } from "react/jsx-runtime";
14
+ import { GlassCard as y } from "@burdenoff/fe-libs/ui";
14
15
  //#region src/adaptercloud/pages/drift/DriftListPage.tsx
15
- function y() {
16
- let { navigateTo: y } = e(), { t: b } = f(), x = u((e, t) => {
17
- let n = b(e);
16
+ function b() {
17
+ let { navigateTo: b } = e(), { t: x } = p(), S = d((e, t) => {
18
+ let n = x(e);
18
19
  return n === e ? t : n;
19
- }, [b]), [S, C] = d("DETECTED"), [w, T] = d(null), E = s({ status: S || void 0 }), D = c(), O = [...E.data ?? []].sort((e, t) => r[t.severity] - r[e.severity]), k = u((e) => {
20
- T(null), D.mutate(e, { onError: (e) => T(e instanceof Error ? e.message : "Failed to remediate drift.") });
21
- }, [D]);
22
- return /* @__PURE__ */ _(t, {
23
- title: x("adaptercloud.drift.title", "Drift"),
24
- description: x("adaptercloud.drift.subtitle", "Divergences between observed and desired state, surfaced during reconciliation."),
20
+ }, [x]), [C, w] = f("DETECTED"), [T, E] = f(null), [D, O] = f(null), k = s({ status: C || void 0 }), A = c(), j = [...k.data ?? []].sort((e, t) => r[t.severity] - r[e.severity]), M = A.isPending ? A.variables : void 0, N = d((e) => {
21
+ E(null), A.mutate(e, {
22
+ onSuccess: () => O(null),
23
+ onError: (e) => E(e instanceof Error ? e.message : "Failed to remediate drift.")
24
+ });
25
+ }, [A]);
26
+ return /* @__PURE__ */ v(t, {
27
+ title: S("adaptercloud.drift.title", "Drift"),
28
+ description: S("adaptercloud.drift.subtitle", "Divergences between observed and desired state, surfaced during reconciliation."),
25
29
  children: [
26
- /* @__PURE__ */ g("div", {
30
+ /* @__PURE__ */ _("div", {
27
31
  className: "mb-6 flex flex-wrap gap-3",
28
- children: /* @__PURE__ */ _("select", {
29
- value: S,
30
- onChange: (e) => C(e.target.value),
32
+ children: /* @__PURE__ */ v("select", {
33
+ value: C,
34
+ onChange: (e) => w(e.target.value),
35
+ "aria-label": S("adaptercloud.drift.filterByStatus", "Filter by status"),
31
36
  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)]",
32
- children: [/* @__PURE__ */ g("option", {
37
+ children: [/* @__PURE__ */ _("option", {
33
38
  value: "",
34
- children: x("adaptercloud.drift.allStatuses", "All statuses")
35
- }), Object.keys(n).map((e) => /* @__PURE__ */ g("option", {
39
+ children: S("adaptercloud.drift.allStatuses", "All statuses")
40
+ }), Object.keys(n).map((e) => /* @__PURE__ */ _("option", {
36
41
  value: e,
37
42
  children: n[e]
38
43
  }, e))]
39
44
  })
40
45
  }),
41
- w && /* @__PURE__ */ g("p", {
46
+ T && /* @__PURE__ */ _("p", {
47
+ role: "alert",
48
+ "aria-live": "polite",
42
49
  className: "mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text",
43
- children: w
50
+ children: T
44
51
  }),
45
- /* @__PURE__ */ g(o, {
46
- isLoading: E.isLoading,
47
- isError: E.isError,
48
- isEmpty: O.length === 0,
49
- error: E.error,
50
- onRetry: () => void E.refetch(),
51
- emptyTitle: x("adaptercloud.drift.emptyTitle", "No drift"),
52
- emptyDescription: x("adaptercloud.drift.emptyDescription", "Nothing matches this filter — observed state matches desired state."),
53
- emptyIcon: /* @__PURE__ */ g(p, { className: "h-10 w-10" }),
54
- children: /* @__PURE__ */ g("div", {
52
+ /* @__PURE__ */ _(o, {
53
+ isLoading: k.isLoading,
54
+ isError: k.isError,
55
+ isEmpty: j.length === 0,
56
+ error: k.error,
57
+ onRetry: () => void k.refetch(),
58
+ emptyTitle: S("adaptercloud.drift.emptyTitle", "No drift"),
59
+ emptyDescription: S("adaptercloud.drift.emptyDescription", "Nothing matches this filter — observed state matches desired state."),
60
+ emptyIcon: /* @__PURE__ */ _(m, { className: "h-10 w-10" }),
61
+ children: /* @__PURE__ */ _("div", {
55
62
  className: "space-y-3",
56
- children: O.map((e) => {
63
+ children: j.map((e) => {
57
64
  let t = e.status === "DETECTED" || e.status === "ACKNOWLEDGED";
58
- return /* @__PURE__ */ g(v, {
65
+ return /* @__PURE__ */ _(y, {
59
66
  className: "p-5",
60
- children: /* @__PURE__ */ _("div", {
67
+ children: /* @__PURE__ */ v("div", {
61
68
  className: "flex flex-wrap items-start justify-between gap-3",
62
- children: [/* @__PURE__ */ _("div", {
69
+ children: [/* @__PURE__ */ v("div", {
63
70
  className: "min-w-0 flex-1",
64
71
  children: [
65
- /* @__PURE__ */ _("div", {
72
+ /* @__PURE__ */ v("div", {
66
73
  className: "mb-1.5 flex flex-wrap items-center gap-2",
67
- children: [/* @__PURE__ */ g(a, { severity: e.severity }), /* @__PURE__ */ g(i, {
74
+ children: [/* @__PURE__ */ _(a, { severity: e.severity }), /* @__PURE__ */ _(i, {
68
75
  status: e.status,
69
76
  label: n[e.status]
70
77
  })]
71
78
  }),
72
- /* @__PURE__ */ g("p", {
79
+ /* @__PURE__ */ _("p", {
73
80
  className: "break-all font-mono text-sm font-medium text-text-primary",
74
81
  children: e.attributePath
75
82
  }),
76
- /* @__PURE__ */ _("div", {
83
+ /* @__PURE__ */ v("div", {
77
84
  className: "mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2",
78
- children: [/* @__PURE__ */ _("div", {
85
+ children: [/* @__PURE__ */ v("div", {
79
86
  className: "rounded-lg bg-bg-sunken p-3",
80
- children: [/* @__PURE__ */ g("p", {
87
+ children: [/* @__PURE__ */ _("p", {
81
88
  className: "mb-1 text-xs font-semibold text-text-secondary",
82
- children: x("adaptercloud.drift.expected", "Expected")
83
- }), /* @__PURE__ */ g("pre", {
89
+ children: S("adaptercloud.drift.expected", "Expected")
90
+ }), /* @__PURE__ */ _("pre", {
84
91
  className: "overflow-auto text-xs text-text-primary",
85
92
  children: JSON.stringify(e.expectedValue, null, 2)
86
93
  })]
87
- }), /* @__PURE__ */ _("div", {
94
+ }), /* @__PURE__ */ v("div", {
88
95
  className: "rounded-lg bg-bg-sunken p-3",
89
- children: [/* @__PURE__ */ g("p", {
96
+ children: [/* @__PURE__ */ _("p", {
90
97
  className: "mb-1 text-xs font-semibold text-text-secondary",
91
- children: x("adaptercloud.drift.observed", "Observed")
92
- }), /* @__PURE__ */ g("pre", {
98
+ children: S("adaptercloud.drift.observed", "Observed")
99
+ }), /* @__PURE__ */ _("pre", {
93
100
  className: "overflow-auto text-xs text-text-primary",
94
101
  children: JSON.stringify(e.observedValue, null, 2)
95
102
  })]
96
103
  })]
97
104
  }),
98
- /* @__PURE__ */ _("p", {
105
+ /* @__PURE__ */ v("p", {
99
106
  className: "mt-2 text-xs text-text-secondary",
100
107
  children: [
101
- x("adaptercloud.drift.detected", "Detected"),
108
+ S("adaptercloud.drift.detected", "Detected"),
102
109
  " ",
103
110
  l(e.detectedAt),
104
111
  " · ",
105
- /* @__PURE__ */ g("button", {
112
+ /* @__PURE__ */ _("button", {
106
113
  type: "button",
107
- onClick: () => y(`/resources/${e.resourceId}`),
114
+ onClick: () => b(`/resources/${e.resourceId}`),
108
115
  className: "hover:underline",
109
- children: x("adaptercloud.drift.viewResource", "View resource")
116
+ children: S("adaptercloud.drift.viewResource", "View resource")
110
117
  })
111
118
  ]
112
119
  })
113
120
  ]
114
- }), t && /* @__PURE__ */ _("button", {
121
+ }), t && /* @__PURE__ */ v("button", {
115
122
  type: "button",
116
- disabled: D.isPending,
117
- onClick: () => k(e.id),
123
+ disabled: M === e.id,
124
+ onClick: () => {
125
+ E(null), O(e);
126
+ },
118
127
  className: "inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50",
119
- children: [D.isPending ? /* @__PURE__ */ g(m, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ g(h, { className: "h-3.5 w-3.5" }), x("adaptercloud.drift.remediate", "Remediate")]
128
+ children: [M === e.id ? /* @__PURE__ */ _(h, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ _(g, { className: "h-3.5 w-3.5" }), S("adaptercloud.drift.remediate", "Remediate")]
120
129
  })]
121
130
  })
122
131
  }, e.id);
123
132
  })
124
133
  })
134
+ }),
135
+ /* @__PURE__ */ _(u, {
136
+ open: !!D,
137
+ onOpenChange: (e) => {
138
+ e || O(null);
139
+ },
140
+ title: S("adaptercloud.drift.remediateTitle", "Mark this drift remediated?"),
141
+ description: S("adaptercloud.drift.remediatePrompt", "This records the divergence as remediated. Confirm only if the observed state now matches the desired state."),
142
+ confirmLabel: S("adaptercloud.drift.remediate", "Remediate"),
143
+ cancelLabel: S("adaptercloud.common.cancel", "Cancel"),
144
+ onConfirm: () => D && N(D.id),
145
+ isPending: !!D && M === D?.id,
146
+ tone: "primary"
125
147
  })
126
148
  ]
127
149
  });
128
150
  }
129
151
  //#endregion
130
- export { y as DriftListPage };
152
+ export { b as DriftListPage };
131
153
 
132
154
  //# sourceMappingURL=DriftListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DriftListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/drift/DriftListPage.tsx"],"sourcesContent":["/**\n * Drift Board Page\n * @module adaptercloud/pages/drift\n *\n * Divergences between observed and desired state (`adaListDriftRecords`).\n * Filters by status; each detected/acknowledged record can be remediated via\n * `adaRemediateDrift`.\n */\n\nimport { useState, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { GitCompareArrows, Wrench, Loader2 } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { SeverityBadge } from '../../components/SeverityBadge';\nimport { DataState } from '../../components/DataState';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useDriftRecords, useRemediateDrift } from '../../hooks/useAdapterCloudApi';\nimport { driftStatusLabels, severityRank } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport type { AdaDriftStatus } from '../../types';\n\nexport function DriftListPage() {\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 [status, setStatus] = useState<AdaDriftStatus | ''>('DETECTED');\n const [actionError, setActionError] = useState<string | null>(null);\n\n const query = useDriftRecords({ status: status || undefined });\n const remediate = useRemediateDrift();\n\n const items = [...(query.data ?? [])].sort(\n (a, b) => severityRank[b.severity] - severityRank[a.severity]\n );\n\n const handleRemediate = useCallback(\n (id: string) => {\n setActionError(null);\n remediate.mutate(id, {\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Failed to remediate drift.'),\n });\n },\n [remediate]\n );\n\n return (\n <PageLayout\n title={t('adaptercloud.drift.title', 'Drift')}\n description={t(\n 'adaptercloud.drift.subtitle',\n 'Divergences between observed and desired state, surfaced during reconciliation.'\n )}\n >\n <div className=\"mb-6 flex flex-wrap gap-3\">\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as AdaDriftStatus | '')}\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.drift.allStatuses', 'All statuses')}</option>\n {(Object.keys(driftStatusLabels) as AdaDriftStatus[]).map((s) => (\n <option key={s} value={s}>\n {driftStatusLabels[s]}\n </option>\n ))}\n </select>\n </div>\n\n {actionError && (\n <p className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\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.drift.emptyTitle', 'No drift')}\n emptyDescription={t(\n 'adaptercloud.drift.emptyDescription',\n 'Nothing matches this filter — observed state matches desired state.'\n )}\n emptyIcon={<GitCompareArrows className=\"h-10 w-10\" />}\n >\n <div className=\"space-y-3\">\n {items.map((d) => {\n const canRemediate = d.status === 'DETECTED' || d.status === 'ACKNOWLEDGED';\n return (\n <GlassCard key={d.id} className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"mb-1.5 flex flex-wrap items-center gap-2\">\n <SeverityBadge severity={d.severity} />\n <StatusBadge status={d.status} label={driftStatusLabels[d.status]} />\n </div>\n <p className=\"break-all font-mono text-sm font-medium text-text-primary\">\n {d.attributePath}\n </p>\n <div className=\"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div className=\"rounded-lg bg-bg-sunken p-3\">\n <p className=\"mb-1 text-xs font-semibold text-text-secondary\">\n {t('adaptercloud.drift.expected', 'Expected')}\n </p>\n <pre className=\"overflow-auto text-xs text-text-primary\">\n {JSON.stringify(d.expectedValue, null, 2)}\n </pre>\n </div>\n <div className=\"rounded-lg bg-bg-sunken p-3\">\n <p className=\"mb-1 text-xs font-semibold text-text-secondary\">\n {t('adaptercloud.drift.observed', 'Observed')}\n </p>\n <pre className=\"overflow-auto text-xs text-text-primary\">\n {JSON.stringify(d.observedValue, null, 2)}\n </pre>\n </div>\n </div>\n <p className=\"mt-2 text-xs text-text-secondary\">\n {t('adaptercloud.drift.detected', 'Detected')} {formatDate(d.detectedAt)}\n {' · '}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/resources/${d.resourceId}`)}\n className=\"hover:underline\"\n >\n {t('adaptercloud.drift.viewResource', 'View resource')}\n </button>\n </p>\n </div>\n\n {canRemediate && (\n <button\n type=\"button\"\n disabled={remediate.isPending}\n onClick={() => handleRemediate(d.id)}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50\"\n >\n {remediate.isPending ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Wrench className=\"h-3.5 w-3.5\" />\n )}\n {t('adaptercloud.drift.remediate', 'Remediate')}\n </button>\n )}\n </div>\n </GlassCard>\n );\n })}\n </div>\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DriftListPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/drift/DriftListPage.tsx"],"sourcesContent":["/**\n * Drift Board Page\n * @module adaptercloud/pages/drift\n *\n * Divergences between observed and desired state (`adaListDriftRecords`).\n * Filters by status; each detected/acknowledged record can be remediated via\n * `adaRemediateDrift`.\n */\n\nimport { useState, useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { GitCompareArrows, Wrench, Loader2 } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { SeverityBadge } from '../../components/SeverityBadge';\nimport { DataState } from '../../components/DataState';\nimport { ConfirmDialog } from '../../components/ConfirmDialog';\nimport { useAdapterCloud } from '../../context/AdapterCloudContext';\nimport { useDriftRecords, useRemediateDrift } from '../../hooks/useAdapterCloudApi';\nimport { driftStatusLabels, severityRank } from '../../utils/domain';\nimport { formatDate } from '../../utils/formatters';\nimport type { AdaDriftRecord, AdaDriftStatus } from '../../types';\n\nexport function DriftListPage() {\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 [status, setStatus] = useState<AdaDriftStatus | ''>('DETECTED');\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmTarget, setConfirmTarget] = useState<AdaDriftRecord | null>(null);\n\n const query = useDriftRecords({ status: status || undefined });\n const remediate = useRemediateDrift();\n\n // Note: this severity sort only reorders the current server result set.\n // adaListDriftRecords returns a single capped array (no pagination), so the\n // ordering is global across what is loaded, not a partial-page reshuffle.\n const items = [...(query.data ?? [])].sort(\n (a, b) => severityRank[b.severity] - severityRank[a.severity]\n );\n\n // Per-id pending: React Query exposes the in-flight mutation argument via\n // `variables`, so only the acted row spins/disables — not the whole list.\n const pendingId = remediate.isPending ? remediate.variables : undefined;\n\n const handleRemediate = useCallback(\n (id: string) => {\n setActionError(null);\n remediate.mutate(id, {\n onSuccess: () => setConfirmTarget(null),\n onError: (err) =>\n setActionError(err instanceof Error ? err.message : 'Failed to remediate drift.'),\n });\n },\n [remediate]\n );\n\n return (\n <PageLayout\n title={t('adaptercloud.drift.title', 'Drift')}\n description={t(\n 'adaptercloud.drift.subtitle',\n 'Divergences between observed and desired state, surfaced during reconciliation.'\n )}\n >\n <div className=\"mb-6 flex flex-wrap gap-3\">\n <select\n value={status}\n onChange={(e) => setStatus(e.target.value as AdaDriftStatus | '')}\n aria-label={t('adaptercloud.drift.filterByStatus', 'Filter by status')}\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.drift.allStatuses', 'All statuses')}</option>\n {(Object.keys(driftStatusLabels) as AdaDriftStatus[]).map((s) => (\n <option key={s} value={s}>\n {driftStatusLabels[s]}\n </option>\n ))}\n </select>\n </div>\n\n {actionError && (\n <p\n role=\"alert\"\n aria-live=\"polite\"\n className=\"mb-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\"\n >\n {actionError}\n </p>\n )}\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.drift.emptyTitle', 'No drift')}\n emptyDescription={t(\n 'adaptercloud.drift.emptyDescription',\n 'Nothing matches this filter — observed state matches desired state.'\n )}\n emptyIcon={<GitCompareArrows className=\"h-10 w-10\" />}\n >\n <div className=\"space-y-3\">\n {items.map((d) => {\n const canRemediate = d.status === 'DETECTED' || d.status === 'ACKNOWLEDGED';\n return (\n <GlassCard key={d.id} className=\"p-5\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0 flex-1\">\n <div className=\"mb-1.5 flex flex-wrap items-center gap-2\">\n <SeverityBadge severity={d.severity} />\n <StatusBadge status={d.status} label={driftStatusLabels[d.status]} />\n </div>\n <p className=\"break-all font-mono text-sm font-medium text-text-primary\">\n {d.attributePath}\n </p>\n <div className=\"mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2\">\n <div className=\"rounded-lg bg-bg-sunken p-3\">\n <p className=\"mb-1 text-xs font-semibold text-text-secondary\">\n {t('adaptercloud.drift.expected', 'Expected')}\n </p>\n <pre className=\"overflow-auto text-xs text-text-primary\">\n {JSON.stringify(d.expectedValue, null, 2)}\n </pre>\n </div>\n <div className=\"rounded-lg bg-bg-sunken p-3\">\n <p className=\"mb-1 text-xs font-semibold text-text-secondary\">\n {t('adaptercloud.drift.observed', 'Observed')}\n </p>\n <pre className=\"overflow-auto text-xs text-text-primary\">\n {JSON.stringify(d.observedValue, null, 2)}\n </pre>\n </div>\n </div>\n <p className=\"mt-2 text-xs text-text-secondary\">\n {t('adaptercloud.drift.detected', 'Detected')} {formatDate(d.detectedAt)}\n {' · '}\n <button\n type=\"button\"\n onClick={() => navigateTo(`/resources/${d.resourceId}`)}\n className=\"hover:underline\"\n >\n {t('adaptercloud.drift.viewResource', 'View resource')}\n </button>\n </p>\n </div>\n\n {canRemediate && (\n <button\n type=\"button\"\n disabled={pendingId === d.id}\n onClick={() => {\n setActionError(null);\n setConfirmTarget(d);\n }}\n className=\"inline-flex shrink-0 items-center gap-1.5 rounded-lg border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle disabled:opacity-50\"\n >\n {pendingId === d.id ? (\n <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <Wrench className=\"h-3.5 w-3.5\" />\n )}\n {t('adaptercloud.drift.remediate', 'Remediate')}\n </button>\n )}\n </div>\n </GlassCard>\n );\n })}\n </div>\n </DataState>\n\n <ConfirmDialog\n open={Boolean(confirmTarget)}\n onOpenChange={(open) => {\n if (!open) setConfirmTarget(null);\n }}\n title={t('adaptercloud.drift.remediateTitle', 'Mark this drift remediated?')}\n description={t(\n 'adaptercloud.drift.remediatePrompt',\n 'This records the divergence as remediated. Confirm only if the observed state now matches the desired state.'\n )}\n confirmLabel={t('adaptercloud.drift.remediate', 'Remediate')}\n cancelLabel={t('adaptercloud.common.cancel', 'Cancel')}\n onConfirm={() => confirmTarget && handleRemediate(confirmTarget.id)}\n isPending={Boolean(confirmTarget) && pendingId === confirmTarget?.id}\n tone=\"primary\"\n />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -41,6 +41,7 @@ function b() {
41
41
  onChange: (e) => {
42
42
  w(e.target.value), O(0);
43
43
  },
44
+ "aria-label": S("adaptercloud.policies.filterByKind", "Filter by kind"),
44
45
  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)]",
45
46
  children: [/* @__PURE__ */ g("option", {
46
47
  value: "",
@@ -54,6 +55,7 @@ function b() {
54
55
  onChange: (e) => {
55
56
  E(e.target.value), O(0);
56
57
  },
58
+ "aria-label": S("adaptercloud.policies.filterByActive", "Filter by active state"),
57
59
  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)]",
58
60
  children: [
59
61
  /* @__PURE__ */ g("option", {
@@ -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 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 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":";;;;;;;;;;;;;;AAsBA,IAAM,IAAY;AAIlB,SAAgB,IAAmB;CACjC,IAAM,EAAE,kBAAe,GAAiB,EAClC,EAAE,GAAG,MAAc,GAAS,EAC5B,IAAI,GACP,GAAa,MAA6B;EACzC,IAAM,IAAQ,EAAU,EAAI;AAC5B,SAAO,MAAU,IAAM,IAAW;IAEpC,CAAC,EAAU,CACZ,EAEK,CAAC,GAAM,KAAW,EAA6B,GAAG,EAClD,CAAC,GAAQ,KAAa,EAAuB,MAAM,EACnD,CAAC,GAAQ,KAAa,EAAS,EAAE,EAEjC,IAAQ,EAAY;EACxB,MAAM,KAAQ,KAAA;EACd,UAAU,MAAW,QAAQ,KAAA,IAAY,MAAW;EACpD,YAAY;GAAE,OAAO;GAAW;GAAQ;EACzC,CAAC,EAEI,IAAQ,EAAM,MAAM,SAAS,EAAE,EAC/B,IAAU,EAAM,MAAM,SAAS,WAAW;AAEhD,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,+BAA+B,WAAW;EACnD,aAAa,EACX,kCACA,4GACD;EACD,SACE,kBAAC,UAAD;GACE,MAAK;GACL,eAAe,EAAW,gBAAgB;GAC1C,WAAU;aAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAE,6BAA6B,aAAa,CACtC;;YAdb,CAiBE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,OAAO;IACP,WAAW,MAAM;AAEf,KADA,EAAQ,EAAE,OAAO,MAA4B,EAC7C,EAAU,EAAE;;IAEd,WAAU;cANZ,CAQE,kBAAC,UAAD;KAAQ,OAAM;eAAI,EAAE,kCAAkC,YAAY;KAAU,CAAA,EAC1E,OAAO,KAAK,EAAiB,CAAqB,KAAK,MACvD,kBAAC,UAAD;KAAgB,OAAO;eACpB,EAAiB;KACX,EAFI,EAEJ,CACT,CACK;OACT,kBAAC,UAAD;IACE,OAAO;IACP,WAAW,MAAM;AAEf,KADA,EAAU,EAAE,OAAO,MAAsB,EACzC,EAAU,EAAE;;IAEd,WAAU;cANZ;KAQE,kBAAC,UAAD;MAAQ,OAAM;gBAAO,EAAE,mCAAmC,MAAM;MAAU,CAAA;KAC1E,kBAAC,UAAD;MAAQ,OAAM;gBAAU,EAAE,gCAAgC,SAAS;MAAU,CAAA;KAC7E,kBAAC,UAAD;MAAQ,OAAM;gBAAY,EAAE,kCAAkC,WAAW;MAAU,CAAA;KAC5E;MACL;MAEN,kBAAC,GAAD;GACE,WAAW,EAAM;GACjB,SAAS,EAAM;GACf,SAAS,EAAM,WAAW;GAC1B,OAAO,EAAM;GACb,eAAe,KAAK,EAAM,SAAS;GACnC,YAAY,EAAE,oCAAoC,kBAAkB;GACpE,kBAAkB,EAChB,0CACA,yEACD;GACD,WAAW,kBAAC,GAAD,EAAY,WAAU,aAAc,CAAA;GAC/C,aACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,gBAAgB;IAC1C,WAAU;cAET,EAAE,6BAA6B,aAAa;IACtC,CAAA;aAnBb,CAsBE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAM,KAAK,MACV,kBAAC,GAAD;KAEE,MAAK;KACL,UAAU;KACV,eAAe,EAAW,aAAa,EAAO,KAAK;KACnD,YAAY,MAAM;AAChB,OAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,gBAAgB,EAClB,EAAW,aAAa,EAAO,KAAK;;KAGxC,WAAU;eAXZ;MAaE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAO;SACL,CAAA,EACL,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAiB,EAAO;SACvB,CAAA,CACA;WACN,kBAAC,GAAD,EAAe,UAAU,EAAO,UAAY,CAAA,CACxC;;MAEL,EAAO,eACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAO;OACN,CAAA;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAwB,EAAO;QAC3B,CAAA,EACP,kBAAC,QAAD;QACE,WAAW,EACT,+EACA,EAAO,WACH,yDACA,mCACL;kBANH,CAQE,kBAAC,QAAD,EACE,WAAW,EACT,4BACA,EAAO,WAAW,2BAA2B,oBAC9C,EACD,CAAA,EACD,EAAO,WACJ,EAAE,gCAAgC,SAAS,GAC3C,EAAE,kCAAkC,WAAW,CAC9C;UACH;;MAEN,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAE,+BAA+B,UAAU;QAAC;QAAE,EAAmB,EAAO,UAAU;QACjF;;MACM;OAzDL,EAAO,GAyDF,CACZ;IACE,CAAA,GAEJ,IAAS,KAAK,MACd,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,MAAW;MACrB,eAAe,GAAW,MAAM,KAAK,IAAI,GAAG,IAAI,EAAU,CAAC;MAC3D,WAAU;gBAJZ,CAME,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA,EAClC,EAAE,4BAA4B,WAAW,CACnC;;KACT,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG,EAAE,+BAA+B,UAAU;OAAC;OAAE,IAAS;OAAE;OAAE,IAAS,EAAM;OACtE;;KACP,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,CAAC;MACX,eAAe,GAAW,MAAM,IAAI,EAAU;MAC9C,WAAU;gBAJZ,CAMG,EAAE,4BAA4B,OAAO,EACtC,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA,CAC7B;;KACL;MAEE;KACD"}
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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"}