@burdenoff/microfe-adaptercloud 2026.625.2 → 2026.628.1
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.
- package/dist/adaptercloud/AdapterCloudRoot.js +65 -121
- package/dist/adaptercloud/AdapterCloudRoot.js.map +1 -1
- package/dist/adaptercloud/AdapterCloudRoutes.js +70 -182
- package/dist/adaptercloud/AdapterCloudRoutes.js.map +1 -1
- package/dist/adaptercloud/components/DataState.js +42 -0
- package/dist/adaptercloud/components/DataState.js.map +1 -0
- package/dist/adaptercloud/components/SeverityBadge.js +12 -0
- package/dist/adaptercloud/components/SeverityBadge.js.map +1 -0
- package/dist/adaptercloud/components/StatusBadge.js +43 -44
- package/dist/adaptercloud/components/StatusBadge.js.map +1 -1
- package/dist/adaptercloud/context/AdapterCloudContext.js +22 -30
- package/dist/adaptercloud/context/AdapterCloudContext.js.map +1 -1
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js +397 -194
- package/dist/adaptercloud/hooks/useAdapterCloudApi.js.map +1 -1
- package/dist/adaptercloud/index.js +1 -0
- package/dist/adaptercloud/pages/OverviewPage.js +334 -0
- package/dist/adaptercloud/pages/OverviewPage.js.map +1 -0
- package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js +187 -0
- package/dist/adaptercloud/pages/adapters/AdapterDetailPage.js.map +1 -0
- package/dist/adaptercloud/pages/adapters/AdapterFormPage.js +278 -0
- package/dist/adaptercloud/pages/adapters/AdapterFormPage.js.map +1 -0
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js +172 -0
- package/dist/adaptercloud/pages/adapters/AdaptersListPage.js.map +1 -0
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js +247 -0
- package/dist/adaptercloud/pages/connections/ConnectionDetailPage.js.map +1 -0
- package/dist/adaptercloud/pages/connections/ConnectionFormPage.js +271 -0
- package/dist/adaptercloud/pages/connections/ConnectionFormPage.js.map +1 -0
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js +167 -0
- package/dist/adaptercloud/pages/connections/ConnectionsListPage.js.map +1 -0
- package/dist/adaptercloud/pages/costs/CostsPage.js +169 -0
- package/dist/adaptercloud/pages/costs/CostsPage.js.map +1 -0
- package/dist/adaptercloud/pages/drift/DriftListPage.js +132 -0
- package/dist/adaptercloud/pages/drift/DriftListPage.js.map +1 -0
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js +171 -0
- package/dist/adaptercloud/pages/policies/PoliciesListPage.js.map +1 -0
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js +185 -0
- package/dist/adaptercloud/pages/policies/PolicyDetailPage.js.map +1 -0
- package/dist/adaptercloud/pages/policies/PolicyFormPage.js +215 -0
- package/dist/adaptercloud/pages/policies/PolicyFormPage.js.map +1 -0
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js +207 -0
- package/dist/adaptercloud/pages/resources/ResourceDetailPage.js.map +1 -0
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js +182 -0
- package/dist/adaptercloud/pages/resources/ResourcesListPage.js.map +1 -0
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js +196 -0
- package/dist/adaptercloud/pages/sync-runs/SyncRunsListPage.js.map +1 -0
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js +258 -0
- package/dist/adaptercloud/pages/violations/ViolationsListPage.js.map +1 -0
- package/dist/adaptercloud/store/adaptercloudStore.js +1 -7
- package/dist/adaptercloud/store/adaptercloudStore.js.map +1 -1
- package/dist/adaptercloud/utils/domain.js +105 -0
- package/dist/adaptercloud/utils/domain.js.map +1 -0
- package/dist/adaptercloud/utils/formatters.js +1 -12
- package/dist/adaptercloud/utils/formatters.js.map +1 -1
- package/dist/index.js +5 -4
- package/package.json +1 -1
- package/dist/adaptercloud/components/InvestmentCard.js +0 -98
- package/dist/adaptercloud/components/InvestmentCard.js.map +0 -1
- package/dist/adaptercloud/components/RatingDisplay.js +0 -35
- package/dist/adaptercloud/components/RatingDisplay.js.map +0 -1
- package/dist/adaptercloud/mocks/data.js +0 -1517
- package/dist/adaptercloud/mocks/data.js.map +0 -1
- package/dist/adaptercloud/mocks/users.js +0 -34
- package/dist/adaptercloud/mocks/users.js.map +0 -1
- package/dist/adaptercloud/pages/HomePage.js +0 -934
- package/dist/adaptercloud/pages/HomePage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/AdminAnalyticsPage.js +0 -653
- package/dist/adaptercloud/pages/admin/AdminAnalyticsPage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/AdminDashboard.js +0 -665
- package/dist/adaptercloud/pages/admin/AdminDashboard.js.map +0 -1
- package/dist/adaptercloud/pages/admin/AdminFinancialPage.js +0 -426
- package/dist/adaptercloud/pages/admin/AdminFinancialPage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/AdminOrdersPage.js +0 -665
- package/dist/adaptercloud/pages/admin/AdminOrdersPage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/AdminProfilePage.js +0 -261
- package/dist/adaptercloud/pages/admin/AdminProfilePage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/AdminSettingsPage.js +0 -663
- package/dist/adaptercloud/pages/admin/AdminSettingsPage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/CartManagementPage.js +0 -874
- package/dist/adaptercloud/pages/admin/CartManagementPage.js.map +0 -1
- package/dist/adaptercloud/pages/admin/UserManagementPage.js +0 -898
- package/dist/adaptercloud/pages/admin/UserManagementPage.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/AnalyticsDashboard.js +0 -840
- package/dist/adaptercloud/pages/cart-owner/AnalyticsDashboard.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/BusinessSettingsPage.js +0 -714
- package/dist/adaptercloud/pages/cart-owner/BusinessSettingsPage.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/CartOwnerDashboard.js +0 -788
- package/dist/adaptercloud/pages/cart-owner/CartOwnerDashboard.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/CartOwnerProfilePage.js +0 -888
- package/dist/adaptercloud/pages/cart-owner/CartOwnerProfilePage.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/CartServiceConfigPage.js +0 -634
- package/dist/adaptercloud/pages/cart-owner/CartServiceConfigPage.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/InventoryManagementPage.js +0 -907
- package/dist/adaptercloud/pages/cart-owner/InventoryManagementPage.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/OrderManagementPage.js +0 -875
- package/dist/adaptercloud/pages/cart-owner/OrderManagementPage.js.map +0 -1
- package/dist/adaptercloud/pages/cart-owner/StaffManagementPage.js +0 -827
- package/dist/adaptercloud/pages/cart-owner/StaffManagementPage.js.map +0 -1
- package/dist/adaptercloud/pages/common/HelpSupportPage.js +0 -1329
- package/dist/adaptercloud/pages/common/HelpSupportPage.js.map +0 -1
- package/dist/adaptercloud/pages/common/NotificationCenterPage.js +0 -671
- package/dist/adaptercloud/pages/common/NotificationCenterPage.js.map +0 -1
- package/dist/adaptercloud/pages/common/SettingsPage.js +0 -982
- package/dist/adaptercloud/pages/common/SettingsPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/BookingDetailsPage.js +0 -596
- package/dist/adaptercloud/pages/customer/BookingDetailsPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/CartDetailPage.js +0 -623
- package/dist/adaptercloud/pages/customer/CartDetailPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/CartDiscoveryPage.js +0 -552
- package/dist/adaptercloud/pages/customer/CartDiscoveryPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/CheckoutPage.js +0 -534
- package/dist/adaptercloud/pages/customer/CheckoutPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/CustomerDashboard.js +0 -525
- package/dist/adaptercloud/pages/customer/CustomerDashboard.js.map +0 -1
- package/dist/adaptercloud/pages/customer/CustomerProfilePage.js +0 -623
- package/dist/adaptercloud/pages/customer/CustomerProfilePage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/DirectServiceBookingPage.js +0 -629
- package/dist/adaptercloud/pages/customer/DirectServiceBookingPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/FavoritesPage.js +0 -480
- package/dist/adaptercloud/pages/customer/FavoritesPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/OrderHistoryPage.js +0 -444
- package/dist/adaptercloud/pages/customer/OrderHistoryPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/OrderTrackingPage.js +0 -391
- package/dist/adaptercloud/pages/customer/OrderTrackingPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/PaymentMethodsPage.js +0 -504
- package/dist/adaptercloud/pages/customer/PaymentMethodsPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/ReviewsPage.js +0 -609
- package/dist/adaptercloud/pages/customer/ReviewsPage.js.map +0 -1
- package/dist/adaptercloud/pages/customer/SavedAddressesPage.js +0 -459
- package/dist/adaptercloud/pages/customer/SavedAddressesPage.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestmentDetailsPage.js +0 -851
- package/dist/adaptercloud/pages/investor/InvestmentDetailsPage.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestmentOpportunitiesPage.js +0 -942
- package/dist/adaptercloud/pages/investor/InvestmentOpportunitiesPage.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestorAnalyticsPage.js +0 -683
- package/dist/adaptercloud/pages/investor/InvestorAnalyticsPage.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestorDashboard.js +0 -452
- package/dist/adaptercloud/pages/investor/InvestorDashboard.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestorInvestmentsPage.js +0 -402
- package/dist/adaptercloud/pages/investor/InvestorInvestmentsPage.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestorPortfolioPage.js +0 -571
- package/dist/adaptercloud/pages/investor/InvestorPortfolioPage.js.map +0 -1
- package/dist/adaptercloud/pages/investor/InvestorProfilePage.js +0 -1067
- package/dist/adaptercloud/pages/investor/InvestorProfilePage.js.map +0 -1
- package/dist/adaptercloud/pages/worker/TaskDetailsPage.js +0 -526
- package/dist/adaptercloud/pages/worker/TaskDetailsPage.js.map +0 -1
- package/dist/adaptercloud/pages/worker/WorkerDashboard.js +0 -659
- package/dist/adaptercloud/pages/worker/WorkerDashboard.js.map +0 -1
- package/dist/adaptercloud/pages/worker/WorkerOrdersPage.js +0 -678
- package/dist/adaptercloud/pages/worker/WorkerOrdersPage.js.map +0 -1
- package/dist/adaptercloud/pages/worker/WorkerProfilePage.js +0 -689
- package/dist/adaptercloud/pages/worker/WorkerProfilePage.js.map +0 -1
- package/dist/adaptercloud/pages/worker/WorkerSchedulePage.js +0 -582
- package/dist/adaptercloud/pages/worker/WorkerSchedulePage.js.map +0 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
|
|
2
|
+
import { cn as t } from "../../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../../components/PageLayout.js";
|
|
4
|
+
import { policyEnforcementLabels as r, policyKindLabels as i } from "../../utils/domain.js";
|
|
5
|
+
import { StatusBadge as a } from "../../components/StatusBadge.js";
|
|
6
|
+
import { SeverityBadge as o } from "../../components/SeverityBadge.js";
|
|
7
|
+
import { DataState as s } from "../../components/DataState.js";
|
|
8
|
+
import { useArchivePolicy as c, usePolicy as l } from "../../hooks/useAdapterCloudApi.js";
|
|
9
|
+
import { formatDate as u, formatRelativeTime as d } from "../../utils/formatters.js";
|
|
10
|
+
import { useCallback as f } from "react";
|
|
11
|
+
import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
12
|
+
import { AlertCircle as m, Archive as h, ArrowLeft as g } from "lucide-react";
|
|
13
|
+
import { useParams as _ } from "react-router-dom";
|
|
14
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
15
|
+
import { GlassCard as b } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
//#region src/adaptercloud/pages/policies/PolicyDetailPage.tsx
|
|
17
|
+
function x() {
|
|
18
|
+
let { id: x } = _(), { navigateTo: S } = e(), { t: C } = p(), w = f((e, t) => {
|
|
19
|
+
let n = C(e);
|
|
20
|
+
return n === e ? t : n;
|
|
21
|
+
}, [C]), T = l(x), E = c(), D = T.data, O = f(() => {
|
|
22
|
+
x && E.mutate(x, { onSuccess: () => S("/policies") });
|
|
23
|
+
}, [
|
|
24
|
+
E,
|
|
25
|
+
x,
|
|
26
|
+
S
|
|
27
|
+
]);
|
|
28
|
+
return /* @__PURE__ */ v(n, {
|
|
29
|
+
title: D?.name ?? w("adaptercloud.policies.detailTitle", "Policy"),
|
|
30
|
+
description: D ? i[D.kind] : void 0,
|
|
31
|
+
actions: /* @__PURE__ */ y("div", {
|
|
32
|
+
className: "flex items-center gap-2",
|
|
33
|
+
children: [/* @__PURE__ */ y("button", {
|
|
34
|
+
type: "button",
|
|
35
|
+
onClick: () => S("/policies"),
|
|
36
|
+
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",
|
|
37
|
+
children: [/* @__PURE__ */ v(g, { className: "h-4 w-4" }), w("adaptercloud.common.back", "Back")]
|
|
38
|
+
}), D && /* @__PURE__ */ y("button", {
|
|
39
|
+
type: "button",
|
|
40
|
+
onClick: O,
|
|
41
|
+
disabled: E.isPending,
|
|
42
|
+
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-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50",
|
|
43
|
+
children: [/* @__PURE__ */ v(h, { className: "h-4 w-4" }), w("adaptercloud.common.archive", "Archive")]
|
|
44
|
+
})]
|
|
45
|
+
}),
|
|
46
|
+
children: /* @__PURE__ */ v(s, {
|
|
47
|
+
isLoading: T.isLoading,
|
|
48
|
+
isError: T.isError,
|
|
49
|
+
isEmpty: !T.isLoading && !D,
|
|
50
|
+
error: T.error,
|
|
51
|
+
onRetry: () => void T.refetch(),
|
|
52
|
+
emptyTitle: w("adaptercloud.policies.notFound", "Policy not found"),
|
|
53
|
+
children: D && /* @__PURE__ */ y("div", {
|
|
54
|
+
className: "space-y-6",
|
|
55
|
+
children: [
|
|
56
|
+
/* @__PURE__ */ y(b, {
|
|
57
|
+
className: "p-6",
|
|
58
|
+
children: [
|
|
59
|
+
/* @__PURE__ */ y("div", {
|
|
60
|
+
className: "mb-4 flex flex-wrap items-center gap-3",
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ v(o, { severity: D.severity }),
|
|
63
|
+
/* @__PURE__ */ v("span", {
|
|
64
|
+
className: "inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium text-text-secondary",
|
|
65
|
+
children: r[D.enforcement]
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ v("span", {
|
|
68
|
+
className: t("inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium", D.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-secondary"),
|
|
69
|
+
children: D.isActive ? w("adaptercloud.policies.active", "Active") : w("adaptercloud.policies.inactive", "Inactive")
|
|
70
|
+
})
|
|
71
|
+
]
|
|
72
|
+
}),
|
|
73
|
+
D.description && /* @__PURE__ */ v("p", {
|
|
74
|
+
className: "mb-4 text-sm text-text-secondary",
|
|
75
|
+
children: D.description
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ y("dl", {
|
|
78
|
+
className: "grid grid-cols-2 gap-4 text-sm sm:grid-cols-3",
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("dt", {
|
|
81
|
+
className: "text-text-secondary",
|
|
82
|
+
children: w("adaptercloud.policies.kind", "Kind")
|
|
83
|
+
}), /* @__PURE__ */ v("dd", {
|
|
84
|
+
className: "mt-0.5 font-medium text-text-primary",
|
|
85
|
+
children: i[D.kind]
|
|
86
|
+
})] }),
|
|
87
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("dt", {
|
|
88
|
+
className: "text-text-secondary",
|
|
89
|
+
children: w("adaptercloud.common.created", "Created")
|
|
90
|
+
}), /* @__PURE__ */ v("dd", {
|
|
91
|
+
className: "mt-0.5 font-medium text-text-primary",
|
|
92
|
+
children: u(D.createdAt)
|
|
93
|
+
})] }),
|
|
94
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("dt", {
|
|
95
|
+
className: "text-text-secondary",
|
|
96
|
+
children: w("adaptercloud.common.updated", "Updated")
|
|
97
|
+
}), /* @__PURE__ */ v("dd", {
|
|
98
|
+
className: "mt-0.5 font-medium text-text-primary",
|
|
99
|
+
children: u(D.updatedAt)
|
|
100
|
+
})] })
|
|
101
|
+
]
|
|
102
|
+
}),
|
|
103
|
+
D.appliesTo.length > 0 && /* @__PURE__ */ y("div", {
|
|
104
|
+
className: "mt-5",
|
|
105
|
+
children: [/* @__PURE__ */ v("h3", {
|
|
106
|
+
className: "mb-2 text-sm font-semibold text-text-primary",
|
|
107
|
+
children: w("adaptercloud.policies.appliesTo", "Applies To")
|
|
108
|
+
}), /* @__PURE__ */ v("div", {
|
|
109
|
+
className: "flex flex-wrap gap-1.5",
|
|
110
|
+
children: D.appliesTo.map((e) => /* @__PURE__ */ v("span", {
|
|
111
|
+
className: "inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 font-mono text-xs text-accent-blue",
|
|
112
|
+
children: e
|
|
113
|
+
}, e))
|
|
114
|
+
})]
|
|
115
|
+
}),
|
|
116
|
+
D.tags.length > 0 && /* @__PURE__ */ y("div", {
|
|
117
|
+
className: "mt-4",
|
|
118
|
+
children: [/* @__PURE__ */ v("h3", {
|
|
119
|
+
className: "mb-2 text-sm font-semibold text-text-primary",
|
|
120
|
+
children: w("adaptercloud.common.tags", "Tags")
|
|
121
|
+
}), /* @__PURE__ */ v("div", {
|
|
122
|
+
className: "flex flex-wrap gap-1.5",
|
|
123
|
+
children: D.tags.map((e) => /* @__PURE__ */ v("span", {
|
|
124
|
+
className: "inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary",
|
|
125
|
+
children: e
|
|
126
|
+
}, e))
|
|
127
|
+
})]
|
|
128
|
+
})
|
|
129
|
+
]
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ y(b, {
|
|
132
|
+
className: "p-6",
|
|
133
|
+
children: [/* @__PURE__ */ v("h3", {
|
|
134
|
+
className: "mb-3 text-sm font-semibold text-text-primary",
|
|
135
|
+
children: w("adaptercloud.policies.expression", "Expression")
|
|
136
|
+
}), /* @__PURE__ */ v("pre", {
|
|
137
|
+
className: "overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary",
|
|
138
|
+
children: JSON.stringify(D.expression, null, 2)
|
|
139
|
+
})]
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ y("section", { children: [/* @__PURE__ */ y("div", {
|
|
142
|
+
className: "mb-3 flex items-center justify-between",
|
|
143
|
+
children: [/* @__PURE__ */ v("h3", {
|
|
144
|
+
className: "text-base font-semibold text-text-primary",
|
|
145
|
+
children: w("adaptercloud.policies.violations", "Violations")
|
|
146
|
+
}), /* @__PURE__ */ v("button", {
|
|
147
|
+
type: "button",
|
|
148
|
+
onClick: () => S(`/violations?policyId=${D.id}`),
|
|
149
|
+
className: "text-sm font-medium text-primary hover:underline",
|
|
150
|
+
children: w("adaptercloud.common.viewAll", "View all")
|
|
151
|
+
})]
|
|
152
|
+
}), D.violations && D.violations.length > 0 ? /* @__PURE__ */ v("div", {
|
|
153
|
+
className: "divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface",
|
|
154
|
+
children: D.violations.slice(0, 10).map((e) => /* @__PURE__ */ y("button", {
|
|
155
|
+
type: "button",
|
|
156
|
+
onClick: () => S(`/violations?policyId=${D.id}`),
|
|
157
|
+
className: "flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50",
|
|
158
|
+
children: [
|
|
159
|
+
/* @__PURE__ */ v(m, { className: "mt-0.5 h-4 w-4 shrink-0 text-status-error-text" }),
|
|
160
|
+
/* @__PURE__ */ y("div", {
|
|
161
|
+
className: "min-w-0 flex-1",
|
|
162
|
+
children: [/* @__PURE__ */ v("p", {
|
|
163
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
164
|
+
children: e.message
|
|
165
|
+
}), /* @__PURE__ */ v("p", {
|
|
166
|
+
className: "mt-0.5 text-xs text-text-secondary",
|
|
167
|
+
children: d(e.detectedAt)
|
|
168
|
+
})]
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ v(a, { status: e.status })
|
|
171
|
+
]
|
|
172
|
+
}, e.id))
|
|
173
|
+
}) : /* @__PURE__ */ v("p", {
|
|
174
|
+
className: "rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary",
|
|
175
|
+
children: w("adaptercloud.policies.noViolations", "No violations recorded for this policy.")
|
|
176
|
+
})] })
|
|
177
|
+
]
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
//#endregion
|
|
183
|
+
export { x as PolicyDetailPage };
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=PolicyDetailPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PolicyDetailPage.js","names":[],"sources":["../../../../src/adaptercloud/pages/policies/PolicyDetailPage.tsx"],"sourcesContent":["/**\n * Policy Detail Page\n * @module adaptercloud/pages/policies\n *\n * One governance policy (`adaPolicy`) — its kind, enforcement, severity, JSON\n * expression, applies-to selector, and the violations it has produced. Supports\n * archiving via `adaArchivePolicy`.\n */\n\nimport { useCallback } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { ArrowLeft, Archive, AlertCircle } 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 { usePolicy, useArchivePolicy } from '../../hooks/useAdapterCloudApi';\nimport { policyKindLabels, policyEnforcementLabels } from '../../utils/domain';\nimport { formatDate, formatRelativeTime } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\n\nexport function PolicyDetailPage() {\n const { id } = useParams<{ id: string }>();\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 query = usePolicy(id);\n const archive = useArchivePolicy();\n const policy = query.data;\n\n const handleArchive = useCallback(() => {\n if (!id) return;\n archive.mutate(id, { onSuccess: () => navigateTo('/policies') });\n }, [archive, id, navigateTo]);\n\n return (\n <PageLayout\n title={policy?.name ?? t('adaptercloud.policies.detailTitle', 'Policy')}\n description={policy ? policyKindLabels[policy.kind] : undefined}\n actions={\n <div className=\"flex items-center gap-2\">\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.back', 'Back')}\n </button>\n {policy && (\n <button\n type=\"button\"\n onClick={handleArchive}\n disabled={archive.isPending}\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-status-error-text transition-colors hover:bg-status-error-bg-subtle disabled:opacity-50\"\n >\n <Archive className=\"h-4 w-4\" />\n {t('adaptercloud.common.archive', 'Archive')}\n </button>\n )}\n </div>\n }\n >\n <DataState\n isLoading={query.isLoading}\n isError={query.isError}\n isEmpty={!query.isLoading && !policy}\n error={query.error}\n onRetry={() => void query.refetch()}\n emptyTitle={t('adaptercloud.policies.notFound', 'Policy not found')}\n >\n {policy && (\n <div className=\"space-y-6\">\n <GlassCard className=\"p-6\">\n <div className=\"mb-4 flex flex-wrap items-center gap-3\">\n <SeverityBadge severity={policy.severity} />\n <span className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs font-medium 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.5 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 {policy.isActive\n ? t('adaptercloud.policies.active', 'Active')\n : t('adaptercloud.policies.inactive', 'Inactive')}\n </span>\n </div>\n\n {policy.description && (\n <p className=\"mb-4 text-sm text-text-secondary\">{policy.description}</p>\n )}\n\n <dl className=\"grid grid-cols-2 gap-4 text-sm sm:grid-cols-3\">\n <div>\n <dt className=\"text-text-secondary\">{t('adaptercloud.policies.kind', 'Kind')}</dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {policyKindLabels[policy.kind]}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.created', 'Created')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(policy.createdAt)}\n </dd>\n </div>\n <div>\n <dt className=\"text-text-secondary\">\n {t('adaptercloud.common.updated', 'Updated')}\n </dt>\n <dd className=\"mt-0.5 font-medium text-text-primary\">\n {formatDate(policy.updatedAt)}\n </dd>\n </div>\n </dl>\n\n {policy.appliesTo.length > 0 && (\n <div className=\"mt-5\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.policies.appliesTo', 'Applies To')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {policy.appliesTo.map((sel) => (\n <span\n key={sel}\n className=\"inline-flex items-center rounded-full bg-accent-blue-subtle px-2.5 py-0.5 font-mono text-xs text-accent-blue\"\n >\n {sel}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {policy.tags.length > 0 && (\n <div className=\"mt-4\">\n <h3 className=\"mb-2 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.common.tags', 'Tags')}\n </h3>\n <div className=\"flex flex-wrap gap-1.5\">\n {policy.tags.map((tag) => (\n <span\n key={tag}\n className=\"inline-flex items-center rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n </div>\n )}\n </GlassCard>\n\n {/* Expression */}\n <GlassCard className=\"p-6\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">\n {t('adaptercloud.policies.expression', 'Expression')}\n </h3>\n <pre className=\"overflow-auto rounded-lg bg-bg-sunken p-4 text-xs text-text-secondary\">\n {JSON.stringify(policy.expression, null, 2)}\n </pre>\n </GlassCard>\n\n {/* Violations */}\n <section>\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"text-base font-semibold text-text-primary\">\n {t('adaptercloud.policies.violations', 'Violations')}\n </h3>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/violations?policyId=${policy.id}`)}\n className=\"text-sm font-medium text-primary hover:underline\"\n >\n {t('adaptercloud.common.viewAll', 'View all')}\n </button>\n </div>\n {policy.violations && policy.violations.length > 0 ? (\n <div className=\"divide-y divide-border rounded-xl border border-border-subtle bg-bg-surface\">\n {policy.violations.slice(0, 10).map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => navigateTo(`/violations?policyId=${policy.id}`)}\n className=\"flex w-full items-start gap-3 px-4 py-3 text-left transition-colors hover:bg-accent/50\"\n >\n <AlertCircle className=\"mt-0.5 h-4 w-4 shrink-0 text-status-error-text\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {v.message}\n </p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatRelativeTime(v.detectedAt)}\n </p>\n </div>\n <StatusBadge status={v.status} />\n </button>\n ))}\n </div>\n ) : (\n <p className=\"rounded-xl border border-border-subtle bg-bg-surface px-4 py-6 text-center text-sm text-text-secondary\">\n {t('adaptercloud.policies.noViolations', 'No violations recorded for this policy.')}\n </p>\n )}\n </section>\n </div>\n )}\n </DataState>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import { useAdapterCloud as e } from "../../context/AdapterCloudContext.js";
|
|
2
|
+
import { PageLayout as t } from "../../components/PageLayout.js";
|
|
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";
|
|
10
|
+
//#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);
|
|
15
|
+
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."));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
let t;
|
|
22
|
+
try {
|
|
23
|
+
t = JSON.parse(L);
|
|
24
|
+
} catch {
|
|
25
|
+
B(y("adaptercloud.policies.form.invalidExpression", "Expression must be valid JSON."));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
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),
|
|
37
|
+
expression: t
|
|
38
|
+
}, {
|
|
39
|
+
onSuccess: (e) => _(`/policies/${e.id}`),
|
|
40
|
+
onError: (e) => B(e instanceof Error ? e.message : "Failed to create policy.")
|
|
41
|
+
});
|
|
42
|
+
}, [
|
|
43
|
+
b,
|
|
44
|
+
x,
|
|
45
|
+
C,
|
|
46
|
+
T,
|
|
47
|
+
D,
|
|
48
|
+
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", {
|
|
60
|
+
type: "button",
|
|
61
|
+
onClick: () => _("/policies"),
|
|
62
|
+
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")]
|
|
64
|
+
}),
|
|
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")
|
|
147
|
+
}),
|
|
148
|
+
/* @__PURE__ */ f("input", {
|
|
149
|
+
id: "ada-pol-applies",
|
|
150
|
+
type: "text",
|
|
151
|
+
value: j,
|
|
152
|
+
onChange: (e) => M(e.target.value),
|
|
153
|
+
className: H,
|
|
154
|
+
placeholder: "aws:ec2:instance, aws:s3:bucket"
|
|
155
|
+
}),
|
|
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")]
|
|
205
|
+
})
|
|
206
|
+
})
|
|
207
|
+
]
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
//#endregion
|
|
213
|
+
export { _ as PolicyFormPage };
|
|
214
|
+
|
|
215
|
+
//# sourceMappingURL=PolicyFormPage.js.map
|
|
@@ -0,0 +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 {\n policyKindLabels,\n policyEnforcementLabels,\n severityLabels,\n} 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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"}
|