@burdenoff/microfe-admin 2026.624.1 → 2026.626.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/admin/components/AccessDenied.js +2 -2
- package/dist/admin/components/AccessDenied.js.map +1 -1
- package/dist/admin/components/ActivityFeed.js +4 -4
- package/dist/admin/components/ActivityFeed.js.map +1 -1
- package/dist/admin/components/Charts.js.map +1 -1
- package/dist/admin/components/ConfirmDialog.js +1 -1
- package/dist/admin/components/ConfirmDialog.js.map +1 -1
- package/dist/admin/components/DataTable.js +5 -5
- package/dist/admin/components/DataTable.js.map +1 -1
- package/dist/admin/components/FormSheet.js +1 -1
- package/dist/admin/components/FormSheet.js.map +1 -1
- package/dist/admin/components/MetricCard.js +40 -37
- package/dist/admin/components/MetricCard.js.map +1 -1
- package/dist/admin/components/PageLayout.js +28 -22
- package/dist/admin/components/PageLayout.js.map +1 -1
- package/dist/admin/components/TenantSsoPanel.js +1 -1
- package/dist/admin/components/TenantSsoPanel.js.map +1 -1
- package/dist/admin/components/Toast.js +11 -11
- package/dist/admin/components/Toast.js.map +1 -1
- package/dist/admin/components/forms/SubscriptionForm.js +2 -2
- package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
- package/dist/admin/components/forms/TenantForm.js +1 -1
- package/dist/admin/components/forms/UserForm.js +1 -1
- package/dist/admin/components/ui.js +48 -41
- package/dist/admin/components/ui.js.map +1 -1
- package/dist/admin/constants/index.js +33 -24
- package/dist/admin/constants/index.js.map +1 -1
- package/dist/admin/pages/AuditLogsPage.js +94 -77
- package/dist/admin/pages/AuditLogsPage.js.map +1 -1
- package/dist/admin/pages/BillingPage.js +70 -18
- package/dist/admin/pages/BillingPage.js.map +1 -1
- package/dist/admin/pages/CommunicationsPage.js +98 -90
- package/dist/admin/pages/CommunicationsPage.js.map +1 -1
- package/dist/admin/pages/ContentPagesPage.js +276 -259
- package/dist/admin/pages/ContentPagesPage.js.map +1 -1
- package/dist/admin/pages/DashboardPage.js +114 -82
- package/dist/admin/pages/DashboardPage.js.map +1 -1
- package/dist/admin/pages/FeatureFlagsPage.js +94 -74
- package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
- package/dist/admin/pages/OrganizationsListPage.js +106 -86
- package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
- package/dist/admin/pages/ProductsPage.js +139 -128
- package/dist/admin/pages/ProductsPage.js.map +1 -1
- package/dist/admin/pages/ReferralsPage.js +74 -52
- package/dist/admin/pages/ReferralsPage.js.map +1 -1
- package/dist/admin/pages/RolesPermissionsPage.js +137 -116
- package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
- package/dist/admin/pages/SREPage.js +38 -32
- package/dist/admin/pages/SREPage.js.map +1 -1
- package/dist/admin/pages/SandboxSettingsPage.js +361 -322
- package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage.js +145 -130
- package/dist/admin/pages/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SyncFailuresPage.js +123 -118
- package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
- package/dist/admin/pages/TenantDetailPage.js +261 -218
- package/dist/admin/pages/TenantDetailPage.js.map +1 -1
- package/dist/admin/pages/TenantFormRoutePage.js +65 -61
- package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
- package/dist/admin/pages/TenantsListPage.js +88 -71
- package/dist/admin/pages/TenantsListPage.js.map +1 -1
- package/dist/admin/pages/UsersListPage.js +129 -112
- package/dist/admin/pages/UsersListPage.js.map +1 -1
- package/dist/admin/pages/WaitlistPage.js +80 -63
- package/dist/admin/pages/WaitlistPage.js.map +1 -1
- package/dist/admin/pages/WorkflowsPage.js +108 -100
- package/dist/admin/pages/WorkflowsPage.js.map +1 -1
- package/dist/admin/pages/WorkspacesListPage.js +62 -49
- package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
- package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
- package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
- package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
- package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
- package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
- package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
- package/package.json +2 -2
|
@@ -9,89 +9,96 @@ import { directActivityGraphqlRequest as o } from "../utils/directActivityGraphq
|
|
|
9
9
|
import { useEffect as s, useMemo as c, useState as l } from "react";
|
|
10
10
|
import { Activity as u, Link2 as d, RefreshCw as f, Users as p } from "lucide-react";
|
|
11
11
|
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
12
|
+
import { EmphasisPanel as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/admin/pages/ReferralsPage.tsx
|
|
13
|
-
function
|
|
14
|
-
let { canViewDashboard:
|
|
15
|
-
apiGatewayUrl:
|
|
16
|
-
authToken:
|
|
17
|
-
actorId:
|
|
14
|
+
function v() {
|
|
15
|
+
let { canViewDashboard: v, isLoading: y } = t(), { apiGatewayUrl: b, authToken: x, currentUser: S } = e(), C = v || S?.role === "PLATFORM_SUPERADMIN", [w, T] = l(null), [E, D] = l([]), [O, k] = l(null), [A, j] = l([]), [M, N] = l(!0), [P, F] = l(null), I = c(() => ({
|
|
16
|
+
apiGatewayUrl: b,
|
|
17
|
+
authToken: x,
|
|
18
|
+
actorId: S?.id,
|
|
18
19
|
actorType: "super_admin"
|
|
19
20
|
}), [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
]),
|
|
24
|
-
|
|
21
|
+
b,
|
|
22
|
+
x,
|
|
23
|
+
S?.id
|
|
24
|
+
]), L = async () => {
|
|
25
|
+
N(!0), F(null);
|
|
25
26
|
try {
|
|
26
27
|
let [e, t] = await Promise.all([o({
|
|
27
|
-
...
|
|
28
|
+
...I,
|
|
28
29
|
query: "query AdminAffiliateAnalytics {\n adminAffiliateAnalytics {\n totalCodes\n totalVisits\n totalJourneys\n totalSignups\n totalPaidConversions\n totalStorePurchases\n totalRevenueAmount\n totalRewardAmount\n topAffiliates {\n codeId\n codeValue\n ownerUserId\n visits\n signups\n paidConversions\n storePurchases\n revenueAmount\n rewardAmount\n }\n }\n }"
|
|
29
30
|
}), o({
|
|
30
|
-
...
|
|
31
|
+
...I,
|
|
31
32
|
query: "query AffiliateJourneys {\n affiliateJourneys(limit: 25, offset: 0) {\n items {\n id\n codeValue\n identifiedUserId\n productSlug\n currentPath\n geographyCountry\n mode\n status\n lastVisitedAt\n }\n }\n }"
|
|
32
33
|
})]);
|
|
33
|
-
|
|
34
|
+
T(e.adminAffiliateAnalytics), D(t.affiliateJourneys.items);
|
|
34
35
|
} catch (e) {
|
|
35
|
-
|
|
36
|
+
F(e instanceof Error ? e.message : "Failed to load referrals dashboard");
|
|
36
37
|
} finally {
|
|
37
|
-
|
|
38
|
+
N(!1);
|
|
38
39
|
}
|
|
39
40
|
};
|
|
40
41
|
s(() => {
|
|
41
|
-
|
|
42
|
-
}, [
|
|
43
|
-
if (!
|
|
44
|
-
|
|
42
|
+
L();
|
|
43
|
+
}, [I]), s(() => {
|
|
44
|
+
if (!O) {
|
|
45
|
+
j([]);
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
48
|
o({
|
|
48
|
-
...
|
|
49
|
+
...I,
|
|
49
50
|
query: "query AffiliateJourneyEvents($journeyId: ID!) {\n affiliateJourneyEvents(journeyId: $journeyId) {\n id\n type\n valueAmount\n currency\n occurredAt\n }\n }",
|
|
50
|
-
variables: { journeyId:
|
|
51
|
-
}).then((e) =>
|
|
52
|
-
|
|
51
|
+
variables: { journeyId: O }
|
|
52
|
+
}).then((e) => j(e.affiliateJourneyEvents)).catch((e) => {
|
|
53
|
+
F(e instanceof Error ? e.message : "Failed to load journey events");
|
|
53
54
|
});
|
|
54
|
-
}, [
|
|
55
|
-
let
|
|
55
|
+
}, [I, O]);
|
|
56
|
+
let R = w ? [
|
|
56
57
|
{
|
|
57
|
-
title: "
|
|
58
|
-
value:
|
|
59
|
-
icon: /* @__PURE__ */ m(
|
|
58
|
+
title: "Signups",
|
|
59
|
+
value: w.totalSignups,
|
|
60
|
+
icon: /* @__PURE__ */ m(p, { className: "h-5 w-5" }),
|
|
61
|
+
hero: !0
|
|
60
62
|
},
|
|
61
63
|
{
|
|
62
|
-
title: "
|
|
63
|
-
value:
|
|
64
|
-
icon: /* @__PURE__ */ m(
|
|
64
|
+
title: "Codes",
|
|
65
|
+
value: w.totalCodes,
|
|
66
|
+
icon: /* @__PURE__ */ m(d, { className: "h-5 w-5" }),
|
|
67
|
+
hero: !1
|
|
65
68
|
},
|
|
66
69
|
{
|
|
67
|
-
title: "
|
|
68
|
-
value:
|
|
69
|
-
icon: /* @__PURE__ */ m(
|
|
70
|
+
title: "Visits",
|
|
71
|
+
value: w.totalVisits,
|
|
72
|
+
icon: /* @__PURE__ */ m(u, { className: "h-5 w-5" }),
|
|
73
|
+
hero: !1
|
|
70
74
|
},
|
|
71
75
|
{
|
|
72
76
|
title: "Paid conversions",
|
|
73
|
-
value:
|
|
74
|
-
icon: /* @__PURE__ */ m(p, { className: "h-5 w-5" })
|
|
77
|
+
value: w.totalPaidConversions + w.totalStorePurchases,
|
|
78
|
+
icon: /* @__PURE__ */ m(p, { className: "h-5 w-5" }),
|
|
79
|
+
hero: !1
|
|
75
80
|
}
|
|
76
81
|
] : [];
|
|
77
82
|
return /* @__PURE__ */ m(a, {
|
|
78
|
-
allowed:
|
|
79
|
-
isLoading:
|
|
83
|
+
allowed: C,
|
|
84
|
+
isLoading: y,
|
|
80
85
|
pageTitle: "Referrals",
|
|
81
86
|
denyMessage: "You don't have permission to view referral analytics.",
|
|
82
87
|
children: /* @__PURE__ */ m(r, {
|
|
83
88
|
title: "Referrals",
|
|
89
|
+
gradientWord: "Referrals",
|
|
84
90
|
description: "Inspect top affiliates, journey drilldowns, conversions, and revenue attribution.",
|
|
85
91
|
actions: /* @__PURE__ */ h("button", {
|
|
86
92
|
type: "button",
|
|
87
|
-
onClick: () => void
|
|
93
|
+
onClick: () => void L(),
|
|
88
94
|
className: "inline-flex items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary",
|
|
89
95
|
children: [/* @__PURE__ */ m(f, { className: "h-4 w-4" }), "Refresh"]
|
|
90
96
|
}),
|
|
91
97
|
children: /* @__PURE__ */ h("div", {
|
|
92
98
|
className: "space-y-6",
|
|
93
99
|
children: [
|
|
94
|
-
|
|
100
|
+
/* @__PURE__ */ m(_, { children: "See how your referral and affiliate program is performing. This page attributes visits, signups, paid conversions and revenue back to each affiliate code, and lets you drill into an individual visitor's journey to understand what drove a conversion." }),
|
|
101
|
+
M ? /* @__PURE__ */ m("div", {
|
|
95
102
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
96
103
|
children: [
|
|
97
104
|
1,
|
|
@@ -99,9 +106,21 @@ function g() {
|
|
|
99
106
|
3,
|
|
100
107
|
4
|
|
101
108
|
].map((e) => /* @__PURE__ */ m("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
102
|
-
}) :
|
|
109
|
+
}) : w ? /* @__PURE__ */ m("div", {
|
|
103
110
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
104
|
-
children:
|
|
111
|
+
children: R.map((e) => e.hero ? /* @__PURE__ */ h(g, {
|
|
112
|
+
className: "p-4",
|
|
113
|
+
children: [/* @__PURE__ */ h("div", {
|
|
114
|
+
className: "mb-3 flex items-center justify-between text-text-secondary",
|
|
115
|
+
children: [/* @__PURE__ */ m("span", {
|
|
116
|
+
className: "text-sm font-medium",
|
|
117
|
+
children: e.title
|
|
118
|
+
}), e.icon]
|
|
119
|
+
}), /* @__PURE__ */ m("p", {
|
|
120
|
+
className: "text-2xl font-semibold text-text-primary",
|
|
121
|
+
children: e.value
|
|
122
|
+
})]
|
|
123
|
+
}, e.title) : /* @__PURE__ */ h("div", {
|
|
105
124
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
106
125
|
children: [/* @__PURE__ */ h("div", {
|
|
107
126
|
className: "mb-3 flex items-center justify-between text-text-secondary",
|
|
@@ -115,9 +134,9 @@ function g() {
|
|
|
115
134
|
})]
|
|
116
135
|
}, e.title))
|
|
117
136
|
}) : null,
|
|
118
|
-
|
|
137
|
+
P ? /* @__PURE__ */ m("div", {
|
|
119
138
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
120
|
-
children:
|
|
139
|
+
children: P
|
|
121
140
|
}) : null,
|
|
122
141
|
/* @__PURE__ */ h("div", {
|
|
123
142
|
className: "grid gap-6 xl:grid-cols-[1.15fr,1.85fr]",
|
|
@@ -126,9 +145,9 @@ function g() {
|
|
|
126
145
|
children: [/* @__PURE__ */ m("h2", {
|
|
127
146
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
128
147
|
children: "Top affiliates"
|
|
129
|
-
}),
|
|
148
|
+
}), w?.topAffiliates.length ? /* @__PURE__ */ m("div", {
|
|
130
149
|
className: "space-y-3",
|
|
131
|
-
children:
|
|
150
|
+
children: w.topAffiliates.map((e) => /* @__PURE__ */ m("div", {
|
|
132
151
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-3",
|
|
133
152
|
children: /* @__PURE__ */ h("div", {
|
|
134
153
|
className: "flex items-center justify-between gap-3",
|
|
@@ -151,6 +170,7 @@ function g() {
|
|
|
151
170
|
})
|
|
152
171
|
}, e.codeId))
|
|
153
172
|
}) : /* @__PURE__ */ m(n, {
|
|
173
|
+
illustration: "empty-data",
|
|
154
174
|
icon: /* @__PURE__ */ m(d, { className: "h-8 w-8" }),
|
|
155
175
|
title: "No affiliate performance yet",
|
|
156
176
|
description: "Top-performing affiliate codes will appear here once journeys are recorded."
|
|
@@ -162,7 +182,7 @@ function g() {
|
|
|
162
182
|
children: [/* @__PURE__ */ m("h2", {
|
|
163
183
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
164
184
|
children: "Recent journeys"
|
|
165
|
-
}),
|
|
185
|
+
}), E.length ? /* @__PURE__ */ m("div", {
|
|
166
186
|
className: "overflow-x-auto",
|
|
167
187
|
children: /* @__PURE__ */ h("table", {
|
|
168
188
|
className: "min-w-full text-sm",
|
|
@@ -194,9 +214,9 @@ function g() {
|
|
|
194
214
|
children: "Visited"
|
|
195
215
|
})
|
|
196
216
|
]
|
|
197
|
-
}) }), /* @__PURE__ */ m("tbody", { children:
|
|
217
|
+
}) }), /* @__PURE__ */ m("tbody", { children: E.map((e) => /* @__PURE__ */ h("tr", {
|
|
198
218
|
className: "cursor-pointer border-b border-border-subtle/70 text-text-primary",
|
|
199
|
-
onClick: () =>
|
|
219
|
+
onClick: () => k(e.id),
|
|
200
220
|
children: [
|
|
201
221
|
/* @__PURE__ */ m("td", {
|
|
202
222
|
className: "px-3 py-2 font-medium",
|
|
@@ -226,6 +246,7 @@ function g() {
|
|
|
226
246
|
}, e.id)) })]
|
|
227
247
|
})
|
|
228
248
|
}) : /* @__PURE__ */ m(n, {
|
|
249
|
+
illustration: "empty-search",
|
|
229
250
|
icon: /* @__PURE__ */ m(u, { className: "h-8 w-8" }),
|
|
230
251
|
title: "No journeys found",
|
|
231
252
|
description: "Journey-level drilldowns will appear once referral traffic flows through product touchpoints."
|
|
@@ -235,9 +256,9 @@ function g() {
|
|
|
235
256
|
children: [/* @__PURE__ */ m("h2", {
|
|
236
257
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
237
258
|
children: "Journey events"
|
|
238
|
-
}),
|
|
259
|
+
}), A.length ? /* @__PURE__ */ m("div", {
|
|
239
260
|
className: "space-y-3",
|
|
240
|
-
children:
|
|
261
|
+
children: A.map((e) => /* @__PURE__ */ m("div", {
|
|
241
262
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-3",
|
|
242
263
|
children: /* @__PURE__ */ h("div", {
|
|
243
264
|
className: "flex items-center justify-between gap-3",
|
|
@@ -254,6 +275,7 @@ function g() {
|
|
|
254
275
|
})
|
|
255
276
|
}, e.id))
|
|
256
277
|
}) : /* @__PURE__ */ m(n, {
|
|
278
|
+
illustration: "empty-generic",
|
|
257
279
|
icon: /* @__PURE__ */ m(u, { className: "h-8 w-8" }),
|
|
258
280
|
title: "Select a journey",
|
|
259
281
|
description: "Choose a journey from the table above to inspect its critical events."
|
|
@@ -267,6 +289,6 @@ function g() {
|
|
|
267
289
|
});
|
|
268
290
|
}
|
|
269
291
|
//#endregion
|
|
270
|
-
export {
|
|
292
|
+
export { v as ReferralsPage };
|
|
271
293
|
|
|
272
294
|
//# sourceMappingURL=ReferralsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReferralsPage.js","names":[],"sources":["../../../src/admin/pages/ReferralsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Activity, Link2, RefreshCw, Users } from 'lucide-react';\n\nimport { PageLayout, EmptyState, PermissionGuard } from '../components';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { useAdmin } from '../providers/AdminProvider';\nimport { directActivityGraphqlRequest } from '../utils/directActivityGraphql';\nimport { formatRelativeTime } from '../utils/formatters';\n\ntype ReferralSummary = {\n totalCodes: number;\n totalVisits: number;\n totalJourneys: number;\n totalSignups: number;\n totalPaidConversions: number;\n totalStorePurchases: number;\n totalRevenueAmount: number;\n totalRewardAmount: number;\n topAffiliates: Array<{\n codeId: string;\n codeValue: string;\n ownerUserId: string;\n visits: number;\n signups: number;\n paidConversions: number;\n storePurchases: number;\n revenueAmount: number;\n rewardAmount: number;\n }>;\n};\n\ntype ReferralJourney = {\n id: string;\n codeValue: string;\n identifiedUserId?: string | null;\n productSlug: string;\n currentPath?: string | null;\n geographyCountry?: string | null;\n mode?: string | null;\n status: string;\n lastVisitedAt: string;\n};\n\ntype ReferralEvent = {\n id: string;\n type: string;\n valueAmount?: number | null;\n currency?: string | null;\n occurredAt: string;\n};\n\nexport function ReferralsPage() {\n const { canViewDashboard, isLoading: permissionsLoading } = useAdminPermissions();\n const { apiGatewayUrl, authToken, currentUser } = useAdmin();\n const canViewReferrals = canViewDashboard || currentUser?.role === 'PLATFORM_SUPERADMIN';\n\n const [summary, setSummary] = useState<ReferralSummary | null>(null);\n const [journeys, setJourneys] = useState<ReferralJourney[]>([]);\n const [selectedJourneyId, setSelectedJourneyId] = useState<string | null>(null);\n const [selectedJourneyEvents, setSelectedJourneyEvents] = useState<ReferralEvent[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const requestContext = useMemo(\n () => ({\n apiGatewayUrl,\n authToken,\n actorId: currentUser?.id,\n actorType: 'super_admin' as const,\n }),\n [apiGatewayUrl, authToken, currentUser?.id]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [summaryData, journeyData] = await Promise.all([\n directActivityGraphqlRequest<{ adminAffiliateAnalytics: ReferralSummary }>({\n ...requestContext,\n query: `query AdminAffiliateAnalytics {\n adminAffiliateAnalytics {\n totalCodes\n totalVisits\n totalJourneys\n totalSignups\n totalPaidConversions\n totalStorePurchases\n totalRevenueAmount\n totalRewardAmount\n topAffiliates {\n codeId\n codeValue\n ownerUserId\n visits\n signups\n paidConversions\n storePurchases\n revenueAmount\n rewardAmount\n }\n }\n }`,\n }),\n directActivityGraphqlRequest<{ affiliateJourneys: { items: ReferralJourney[] } }>({\n ...requestContext,\n query: `query AffiliateJourneys {\n affiliateJourneys(limit: 25, offset: 0) {\n items {\n id\n codeValue\n identifiedUserId\n productSlug\n currentPath\n geographyCountry\n mode\n status\n lastVisitedAt\n }\n }\n }`,\n }),\n ]);\n\n setSummary(summaryData.adminAffiliateAnalytics);\n setJourneys(journeyData.affiliateJourneys.items);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError.message : 'Failed to load referrals dashboard'\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n useEffect(() => {\n if (!selectedJourneyId) {\n setSelectedJourneyEvents([]);\n return;\n }\n\n void directActivityGraphqlRequest<\n { affiliateJourneyEvents: ReferralEvent[] },\n { journeyId: string }\n >({\n ...requestContext,\n query: `query AffiliateJourneyEvents($journeyId: ID!) {\n affiliateJourneyEvents(journeyId: $journeyId) {\n id\n type\n valueAmount\n currency\n occurredAt\n }\n }`,\n variables: { journeyId: selectedJourneyId },\n })\n .then((response) => setSelectedJourneyEvents(response.affiliateJourneyEvents))\n .catch((loadError) => {\n setError(loadError instanceof Error ? loadError.message : 'Failed to load journey events');\n });\n }, [requestContext, selectedJourneyId]);\n\n const cards = summary\n ? [\n { title: 'Codes', value: summary.totalCodes, icon: <Link2 className=\"h-5 w-5\" /> },\n { title: 'Visits', value: summary.totalVisits, icon: <Activity className=\"h-5 w-5\" /> },\n { title: 'Signups', value: summary.totalSignups, icon: <Users className=\"h-5 w-5\" /> },\n {\n title: 'Paid conversions',\n value: summary.totalPaidConversions + summary.totalStorePurchases,\n icon: <Users className=\"h-5 w-5\" />,\n },\n ]\n : [];\n\n return (\n <PermissionGuard\n allowed={canViewReferrals}\n isLoading={permissionsLoading}\n pageTitle=\"Referrals\"\n denyMessage=\"You don't have permission to view referral analytics.\"\n >\n <PageLayout\n title=\"Referrals\"\n description=\"Inspect top affiliates, journey drilldowns, conversions, and revenue attribution.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n Refresh\n </button>\n }\n >\n <div className=\"space-y-6\">\n {loading ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {[1, 2, 3, 4].map((item) => (\n <div\n key={item}\n className=\"h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : summary ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {cards.map((card) => (\n <div key={card.title} className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <div className=\"mb-3 flex items-center justify-between text-text-secondary\">\n <span className=\"text-sm font-medium\">{card.title}</span>\n {card.icon}\n </div>\n <p className=\"text-2xl font-semibold text-text-primary\">{card.value}</p>\n </div>\n ))}\n </div>\n ) : null}\n\n {error ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n\n <div className=\"grid gap-6 xl:grid-cols-[1.15fr,1.85fr]\">\n <section className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Top affiliates</h2>\n {summary?.topAffiliates.length ? (\n <div className=\"space-y-3\">\n {summary.topAffiliates.map((affiliate) => (\n <div\n key={affiliate.codeId}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {affiliate.codeValue}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Owner: {affiliate.ownerUserId}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {affiliate.visits} visits\n </p>\n <p className=\"text-xs text-text-secondary\">\n {affiliate.paidConversions + affiliate.storePurchases} conversions\n </p>\n </div>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<Link2 className=\"h-8 w-8\" />}\n title=\"No affiliate performance yet\"\n description=\"Top-performing affiliate codes will appear here once journeys are recorded.\"\n />\n )}\n </section>\n\n <section className=\"space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Recent journeys</h2>\n {journeys.length ? (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-text-secondary\">\n <th className=\"px-3 py-2\">Code</th>\n <th className=\"px-3 py-2\">Product</th>\n <th className=\"px-3 py-2\">Path</th>\n <th className=\"px-3 py-2\">Country</th>\n <th className=\"px-3 py-2\">Status</th>\n <th className=\"px-3 py-2\">Visited</th>\n </tr>\n </thead>\n <tbody>\n {journeys.map((journey) => (\n <tr\n key={journey.id}\n className=\"cursor-pointer border-b border-border-subtle/70 text-text-primary\"\n onClick={() => setSelectedJourneyId(journey.id)}\n >\n <td className=\"px-3 py-2 font-medium\">{journey.codeValue}</td>\n <td className=\"px-3 py-2\">{journey.productSlug}</td>\n <td className=\"px-3 py-2\">{journey.currentPath || '-'}</td>\n <td className=\"px-3 py-2\">{journey.geographyCountry || '-'}</td>\n <td className=\"px-3 py-2\">{journey.status}</td>\n <td className=\"px-3 py-2\">\n {formatRelativeTime(journey.lastVisitedAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : (\n <EmptyState\n icon={<Activity className=\"h-8 w-8\" />}\n title=\"No journeys found\"\n description=\"Journey-level drilldowns will appear once referral traffic flows through product touchpoints.\"\n />\n )}\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Journey events</h2>\n {selectedJourneyEvents.length ? (\n <div className=\"space-y-3\">\n {selectedJourneyEvents.map((event) => (\n <div\n key={event.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{event.type}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatRelativeTime(event.occurredAt)}\n </p>\n </div>\n <p className=\"text-sm font-medium text-text-primary\">\n {event.valueAmount\n ? `${event.valueAmount} ${event.currency || 'USD'}`\n : '—'}\n </p>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n icon={<Activity className=\"h-8 w-8\" />}\n title=\"Select a journey\"\n description=\"Choose a journey from the table above to inspect its critical events.\"\n />\n )}\n </div>\n </section>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReferralsPage.js","names":[],"sources":["../../../src/admin/pages/ReferralsPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Activity, Link2, RefreshCw, Users } from 'lucide-react';\n\nimport { PageLayout, EmptyState, PermissionGuard } from '../components';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { useAdmin } from '../providers/AdminProvider';\nimport { directActivityGraphqlRequest } from '../utils/directActivityGraphql';\nimport { formatRelativeTime } from '../utils/formatters';\n\ntype ReferralSummary = {\n totalCodes: number;\n totalVisits: number;\n totalJourneys: number;\n totalSignups: number;\n totalPaidConversions: number;\n totalStorePurchases: number;\n totalRevenueAmount: number;\n totalRewardAmount: number;\n topAffiliates: Array<{\n codeId: string;\n codeValue: string;\n ownerUserId: string;\n visits: number;\n signups: number;\n paidConversions: number;\n storePurchases: number;\n revenueAmount: number;\n rewardAmount: number;\n }>;\n};\n\ntype ReferralJourney = {\n id: string;\n codeValue: string;\n identifiedUserId?: string | null;\n productSlug: string;\n currentPath?: string | null;\n geographyCountry?: string | null;\n mode?: string | null;\n status: string;\n lastVisitedAt: string;\n};\n\ntype ReferralEvent = {\n id: string;\n type: string;\n valueAmount?: number | null;\n currency?: string | null;\n occurredAt: string;\n};\n\nexport function ReferralsPage() {\n const { canViewDashboard, isLoading: permissionsLoading } = useAdminPermissions();\n const { apiGatewayUrl, authToken, currentUser } = useAdmin();\n const canViewReferrals = canViewDashboard || currentUser?.role === 'PLATFORM_SUPERADMIN';\n\n const [summary, setSummary] = useState<ReferralSummary | null>(null);\n const [journeys, setJourneys] = useState<ReferralJourney[]>([]);\n const [selectedJourneyId, setSelectedJourneyId] = useState<string | null>(null);\n const [selectedJourneyEvents, setSelectedJourneyEvents] = useState<ReferralEvent[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const requestContext = useMemo(\n () => ({\n apiGatewayUrl,\n authToken,\n actorId: currentUser?.id,\n actorType: 'super_admin' as const,\n }),\n [apiGatewayUrl, authToken, currentUser?.id]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [summaryData, journeyData] = await Promise.all([\n directActivityGraphqlRequest<{ adminAffiliateAnalytics: ReferralSummary }>({\n ...requestContext,\n query: `query AdminAffiliateAnalytics {\n adminAffiliateAnalytics {\n totalCodes\n totalVisits\n totalJourneys\n totalSignups\n totalPaidConversions\n totalStorePurchases\n totalRevenueAmount\n totalRewardAmount\n topAffiliates {\n codeId\n codeValue\n ownerUserId\n visits\n signups\n paidConversions\n storePurchases\n revenueAmount\n rewardAmount\n }\n }\n }`,\n }),\n directActivityGraphqlRequest<{ affiliateJourneys: { items: ReferralJourney[] } }>({\n ...requestContext,\n query: `query AffiliateJourneys {\n affiliateJourneys(limit: 25, offset: 0) {\n items {\n id\n codeValue\n identifiedUserId\n productSlug\n currentPath\n geographyCountry\n mode\n status\n lastVisitedAt\n }\n }\n }`,\n }),\n ]);\n\n setSummary(summaryData.adminAffiliateAnalytics);\n setJourneys(journeyData.affiliateJourneys.items);\n } catch (loadError) {\n setError(\n loadError instanceof Error ? loadError.message : 'Failed to load referrals dashboard'\n );\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n useEffect(() => {\n if (!selectedJourneyId) {\n setSelectedJourneyEvents([]);\n return;\n }\n\n void directActivityGraphqlRequest<\n { affiliateJourneyEvents: ReferralEvent[] },\n { journeyId: string }\n >({\n ...requestContext,\n query: `query AffiliateJourneyEvents($journeyId: ID!) {\n affiliateJourneyEvents(journeyId: $journeyId) {\n id\n type\n valueAmount\n currency\n occurredAt\n }\n }`,\n variables: { journeyId: selectedJourneyId },\n })\n .then((response) => setSelectedJourneyEvents(response.affiliateJourneyEvents))\n .catch((loadError) => {\n setError(loadError instanceof Error ? loadError.message : 'Failed to load journey events');\n });\n }, [requestContext, selectedJourneyId]);\n\n const cards = summary\n ? [\n // The signups total is the headline outcome — flagged as the single hero.\n {\n title: 'Signups',\n value: summary.totalSignups,\n icon: <Users className=\"h-5 w-5\" />,\n hero: true,\n },\n { title: 'Codes', value: summary.totalCodes, icon: <Link2 className=\"h-5 w-5\" />, hero: false },\n { title: 'Visits', value: summary.totalVisits, icon: <Activity className=\"h-5 w-5\" />, hero: false },\n {\n title: 'Paid conversions',\n value: summary.totalPaidConversions + summary.totalStorePurchases,\n icon: <Users className=\"h-5 w-5\" />,\n hero: false,\n },\n ]\n : [];\n\n return (\n <PermissionGuard\n allowed={canViewReferrals}\n isLoading={permissionsLoading}\n pageTitle=\"Referrals\"\n denyMessage=\"You don't have permission to view referral analytics.\"\n >\n <PageLayout\n title=\"Referrals\"\n gradientWord=\"Referrals\"\n description=\"Inspect top affiliates, journey drilldowns, conversions, and revenue attribution.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n Refresh\n </button>\n }\n >\n <div className=\"space-y-6\">\n <PagePurpose>\n See how your referral and affiliate program is performing. This page attributes\n visits, signups, paid conversions and revenue back to each affiliate code, and lets you\n drill into an individual visitor's journey to understand what drove a conversion.\n </PagePurpose>\n\n {loading ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {[1, 2, 3, 4].map((item) => (\n <div\n key={item}\n className=\"h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : summary ? (\n <div className=\"grid gap-4 md:grid-cols-2 xl:grid-cols-4\">\n {cards.map((card) =>\n card.hero ? (\n <EmphasisPanel key={card.title} className=\"p-4\">\n <div className=\"mb-3 flex items-center justify-between text-text-secondary\">\n <span className=\"text-sm font-medium\">{card.title}</span>\n {card.icon}\n </div>\n <p className=\"text-2xl font-semibold text-text-primary\">{card.value}</p>\n </EmphasisPanel>\n ) : (\n <div\n key={card.title}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\"\n >\n <div className=\"mb-3 flex items-center justify-between text-text-secondary\">\n <span className=\"text-sm font-medium\">{card.title}</span>\n {card.icon}\n </div>\n <p className=\"text-2xl font-semibold text-text-primary\">{card.value}</p>\n </div>\n )\n )}\n </div>\n ) : null}\n\n {error ? (\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text\">\n {error}\n </div>\n ) : null}\n\n <div className=\"grid gap-6 xl:grid-cols-[1.15fr,1.85fr]\">\n <section className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Top affiliates</h2>\n {summary?.topAffiliates.length ? (\n <div className=\"space-y-3\">\n {summary.topAffiliates.map((affiliate) => (\n <div\n key={affiliate.codeId}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">\n {affiliate.codeValue}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Owner: {affiliate.ownerUserId}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-semibold text-text-primary\">\n {affiliate.visits} visits\n </p>\n <p className=\"text-xs text-text-secondary\">\n {affiliate.paidConversions + affiliate.storePurchases} conversions\n </p>\n </div>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Link2 className=\"h-8 w-8\" />}\n title=\"No affiliate performance yet\"\n description=\"Top-performing affiliate codes will appear here once journeys are recorded.\"\n />\n )}\n </section>\n\n <section className=\"space-y-4\">\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Recent journeys</h2>\n {journeys.length ? (\n <div className=\"overflow-x-auto\">\n <table className=\"min-w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-text-secondary\">\n <th className=\"px-3 py-2\">Code</th>\n <th className=\"px-3 py-2\">Product</th>\n <th className=\"px-3 py-2\">Path</th>\n <th className=\"px-3 py-2\">Country</th>\n <th className=\"px-3 py-2\">Status</th>\n <th className=\"px-3 py-2\">Visited</th>\n </tr>\n </thead>\n <tbody>\n {journeys.map((journey) => (\n <tr\n key={journey.id}\n className=\"cursor-pointer border-b border-border-subtle/70 text-text-primary\"\n onClick={() => setSelectedJourneyId(journey.id)}\n >\n <td className=\"px-3 py-2 font-medium\">{journey.codeValue}</td>\n <td className=\"px-3 py-2\">{journey.productSlug}</td>\n <td className=\"px-3 py-2\">{journey.currentPath || '-'}</td>\n <td className=\"px-3 py-2\">{journey.geographyCountry || '-'}</td>\n <td className=\"px-3 py-2\">{journey.status}</td>\n <td className=\"px-3 py-2\">\n {formatRelativeTime(journey.lastVisitedAt)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-search\"\n icon={<Activity className=\"h-8 w-8\" />}\n title=\"No journeys found\"\n description=\"Journey-level drilldowns will appear once referral traffic flows through product touchpoints.\"\n />\n )}\n </div>\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Journey events</h2>\n {selectedJourneyEvents.length ? (\n <div className=\"space-y-3\">\n {selectedJourneyEvents.map((event) => (\n <div\n key={event.id}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{event.type}</p>\n <p className=\"text-xs text-text-secondary\">\n {formatRelativeTime(event.occurredAt)}\n </p>\n </div>\n <p className=\"text-sm font-medium text-text-primary\">\n {event.valueAmount\n ? `${event.valueAmount} ${event.currency || 'USD'}`\n : '—'}\n </p>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <EmptyState\n illustration=\"empty-generic\"\n icon={<Activity className=\"h-8 w-8\" />}\n title=\"Select a journey\"\n description=\"Choose a journey from the table above to inspect its critical events.\"\n />\n )}\n </div>\n </section>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,15 +7,17 @@ import { useGetResourceTypesQuery as s, useListRolesQuery as c } from "../../gen
|
|
|
7
7
|
import { useMemo as l, useState as u } from "react";
|
|
8
8
|
import { Pencil as d, Plus as f, Search as p, Shield as m, Trash2 as h } from "lucide-react";
|
|
9
9
|
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
10
|
+
import { EmphasisPanel as v, PagePurpose as y } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/admin/pages/RolesPermissionsPage.tsx
|
|
11
|
-
function
|
|
12
|
-
let [
|
|
13
|
-
if (!
|
|
14
|
-
let e =
|
|
15
|
-
return
|
|
16
|
-
}, [
|
|
12
|
+
function b() {
|
|
13
|
+
let [b, x] = u(""), { data: S, loading: C, error: w } = c({ variables: { scopeId: "global" } }), { data: T } = s(), E = l(() => S?.roles ?? [], [S]), D = l(() => {
|
|
14
|
+
if (!b.trim()) return E;
|
|
15
|
+
let e = b.toLowerCase();
|
|
16
|
+
return E.filter((t) => t.name.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e));
|
|
17
|
+
}, [E, b]);
|
|
17
18
|
return /* @__PURE__ */ g(i, {
|
|
18
19
|
title: "Roles & Permissions",
|
|
20
|
+
gradientWord: "Permissions",
|
|
19
21
|
description: "Manage platform roles and global permissions",
|
|
20
22
|
actions: /* @__PURE__ */ _(t, {
|
|
21
23
|
size: "sm",
|
|
@@ -23,123 +25,142 @@ function v() {
|
|
|
23
25
|
}),
|
|
24
26
|
children: /* @__PURE__ */ _("div", {
|
|
25
27
|
className: "space-y-4",
|
|
26
|
-
children: [
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
className: "
|
|
30
|
-
children: [/* @__PURE__ */ g(
|
|
31
|
-
|
|
32
|
-
className: "
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
children: [/* @__PURE__ */ g("span", {
|
|
58
|
-
className: "font-semibold text-text-primary",
|
|
59
|
-
children: t.name
|
|
60
|
-
}), t.isSystem && /* @__PURE__ */ g(e, {
|
|
61
|
-
variant: "secondary",
|
|
62
|
-
className: "h-4 px-1 text-[10px]",
|
|
63
|
-
children: "System"
|
|
64
|
-
})]
|
|
65
|
-
}), t.description && /* @__PURE__ */ g("span", {
|
|
66
|
-
className: "mt-0.5 line-clamp-1 text-xs text-text-secondary",
|
|
67
|
-
children: t.description
|
|
68
|
-
})]
|
|
28
|
+
children: [
|
|
29
|
+
/* @__PURE__ */ g(y, { children: "Roles bundle together the permissions a person can hold across the platform. Use this page to review what each role can do, create new roles for different kinds of operators, and keep least-privilege access tidy as your team grows." }),
|
|
30
|
+
/* @__PURE__ */ _(v, {
|
|
31
|
+
className: "flex items-center gap-4 p-5",
|
|
32
|
+
children: [/* @__PURE__ */ g("div", {
|
|
33
|
+
className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-action-primary-bg/15 text-action-primary-bg",
|
|
34
|
+
children: /* @__PURE__ */ g(m, { className: "h-6 w-6" })
|
|
35
|
+
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("div", {
|
|
36
|
+
className: "text-3xl font-bold tabular-nums text-text-primary",
|
|
37
|
+
children: E.length
|
|
38
|
+
}), /* @__PURE__ */ g("p", {
|
|
39
|
+
className: "text-sm text-text-secondary",
|
|
40
|
+
children: "roles defined platform-wide"
|
|
41
|
+
})] })]
|
|
42
|
+
}),
|
|
43
|
+
/* @__PURE__ */ _("div", {
|
|
44
|
+
className: "flex items-center justify-between",
|
|
45
|
+
children: [/* @__PURE__ */ _("div", {
|
|
46
|
+
className: "relative flex-1 max-w-sm",
|
|
47
|
+
children: [/* @__PURE__ */ g(p, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ g(r, {
|
|
48
|
+
placeholder: "Search roles...",
|
|
49
|
+
className: "pl-9",
|
|
50
|
+
value: b,
|
|
51
|
+
onChange: (e) => x(e.target.value)
|
|
52
|
+
})]
|
|
53
|
+
}), /* @__PURE__ */ g("div", {
|
|
54
|
+
className: "flex items-center gap-2",
|
|
55
|
+
children: /* @__PURE__ */ _(e, {
|
|
56
|
+
variant: "outline",
|
|
57
|
+
className: "h-8",
|
|
58
|
+
children: [T?.resourceTypes?.length ?? 0, " Resource Types"]
|
|
69
59
|
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
60
|
+
})]
|
|
61
|
+
}),
|
|
62
|
+
w ? /* @__PURE__ */ g(n, {
|
|
63
|
+
illustration: "empty-generic",
|
|
64
|
+
icon: /* @__PURE__ */ g(m, { className: "h-8 w-8 text-status-error-text" }),
|
|
65
|
+
title: "Error loading roles",
|
|
66
|
+
description: w.message
|
|
67
|
+
}) : /* @__PURE__ */ g(a, {
|
|
68
|
+
columns: [
|
|
69
|
+
{
|
|
70
|
+
key: "name",
|
|
71
|
+
header: "Role Name",
|
|
72
|
+
render: (t) => /* @__PURE__ */ _("div", {
|
|
73
|
+
className: "flex flex-col text-left",
|
|
74
|
+
children: [/* @__PURE__ */ _("div", {
|
|
75
|
+
className: "flex items-center gap-2",
|
|
76
|
+
children: [/* @__PURE__ */ g("span", {
|
|
77
|
+
className: "font-semibold text-text-primary",
|
|
78
|
+
children: t.name
|
|
79
|
+
}), t.isSystem && /* @__PURE__ */ g(e, {
|
|
80
|
+
variant: "secondary",
|
|
81
|
+
className: "h-4 px-1 text-[10px]",
|
|
82
|
+
children: "System"
|
|
83
|
+
})]
|
|
84
|
+
}), t.description && /* @__PURE__ */ g("span", {
|
|
85
|
+
className: "mt-0.5 line-clamp-1 text-xs text-text-secondary",
|
|
86
|
+
children: t.description
|
|
95
87
|
})]
|
|
96
|
-
}, t.id)) : /* @__PURE__ */ g("span", {
|
|
97
|
-
className: "text-xs italic text-text-secondary",
|
|
98
|
-
children: "No permissions defined"
|
|
99
88
|
})
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: "priority",
|
|
92
|
+
header: "Priority",
|
|
93
|
+
align: "center",
|
|
94
|
+
width: "80px",
|
|
95
|
+
render: (e) => /* @__PURE__ */ g("span", {
|
|
96
|
+
className: "rounded bg-bg-sunken px-2 py-0.5 text-xs font-medium",
|
|
97
|
+
children: e.priority
|
|
98
|
+
})
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
key: "permissions",
|
|
102
|
+
header: "Default Permissions",
|
|
103
|
+
render: (t) => /* @__PURE__ */ g("div", {
|
|
104
|
+
className: "flex max-w-md flex-wrap gap-1 text-left",
|
|
105
|
+
children: t.defaultPermissions && t.defaultPermissions.length > 0 ? t.defaultPermissions.map((t) => /* @__PURE__ */ _(e, {
|
|
106
|
+
variant: "outline",
|
|
107
|
+
className: "bg-bg-surface/50 px-1 py-0 font-normal text-[10px]",
|
|
108
|
+
children: [/* @__PURE__ */ _("span", {
|
|
109
|
+
className: "mr-1 text-text-secondary",
|
|
110
|
+
children: [t.resourceType, ":"]
|
|
111
|
+
}), /* @__PURE__ */ g("span", {
|
|
112
|
+
className: "font-medium",
|
|
113
|
+
children: t.level
|
|
114
|
+
})]
|
|
115
|
+
}, t.id)) : /* @__PURE__ */ g("span", {
|
|
116
|
+
className: "text-xs italic text-text-secondary",
|
|
117
|
+
children: "No permissions defined"
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
key: "actions",
|
|
123
|
+
header: "",
|
|
124
|
+
align: "right",
|
|
125
|
+
width: "50px",
|
|
126
|
+
render: (e) => /* @__PURE__ */ g(o, { actions: [{
|
|
127
|
+
key: "edit",
|
|
128
|
+
label: "Edit Role",
|
|
129
|
+
icon: /* @__PURE__ */ g(d, { className: "h-4 w-4" }),
|
|
130
|
+
onClick: () => console.log("Edit", e.id),
|
|
131
|
+
disabled: e.isSystem
|
|
132
|
+
}, {
|
|
133
|
+
key: "delete",
|
|
134
|
+
label: "Delete Role",
|
|
135
|
+
icon: /* @__PURE__ */ g(h, { className: "h-4 w-4" }),
|
|
136
|
+
onClick: () => console.log("Delete", e.id),
|
|
137
|
+
destructive: !0,
|
|
138
|
+
disabled: e.isSystem,
|
|
139
|
+
separator: !0
|
|
140
|
+
}] })
|
|
141
|
+
}
|
|
142
|
+
],
|
|
143
|
+
data: D,
|
|
144
|
+
keyExtractor: (e) => e.id,
|
|
145
|
+
loading: C,
|
|
146
|
+
emptyState: /* @__PURE__ */ g(n, {
|
|
147
|
+
illustration: "empty-people",
|
|
148
|
+
icon: /* @__PURE__ */ g(m, { className: "h-8 w-8" }),
|
|
149
|
+
title: "No roles found",
|
|
150
|
+
description: b ? `No roles match "${b}"` : "Roles and permissions will appear here when they are created",
|
|
151
|
+
action: b ? /* @__PURE__ */ g(t, {
|
|
152
|
+
variant: "outline",
|
|
153
|
+
size: "sm",
|
|
154
|
+
onClick: () => x(""),
|
|
155
|
+
children: "Clear Search"
|
|
156
|
+
}) : void 0
|
|
157
|
+
})
|
|
137
158
|
})
|
|
138
|
-
|
|
159
|
+
]
|
|
139
160
|
})
|
|
140
161
|
});
|
|
141
162
|
}
|
|
142
163
|
//#endregion
|
|
143
|
-
export {
|
|
164
|
+
export { b as RolesPermissionsPage };
|
|
144
165
|
|
|
145
166
|
//# sourceMappingURL=RolesPermissionsPage.js.map
|