@burdenoff/microfe-admin 2026.625.1 → 2026.712.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/AdminRoot.js +2 -2
- package/dist/admin/AdminRoot.js.map +1 -1
- package/dist/admin/AdminRoutes.js +130 -78
- package/dist/admin/AdminRoutes.js.map +1 -1
- 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.map +1 -1
- package/dist/admin/components/FormSheet.js +1 -1
- package/dist/admin/components/FormSheet.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 +3 -3
- package/dist/admin/components/ui.js.map +1 -1
- package/dist/admin/constants/featureFlags.js +12 -0
- package/dist/admin/constants/featureFlags.js.map +1 -0
- package/dist/admin/constants/index.js +33 -24
- package/dist/admin/constants/index.js.map +1 -1
- package/dist/admin/pages/AuditLogsPage.js +92 -77
- package/dist/admin/pages/AuditLogsPage.js.map +1 -1
- package/dist/admin/pages/BillingPage.js +95 -51
- package/dist/admin/pages/BillingPage.js.map +1 -1
- package/dist/admin/pages/CommunicationsPage.js +97 -92
- package/dist/admin/pages/CommunicationsPage.js.map +1 -1
- package/dist/admin/pages/ContentPagesPage.js +274 -259
- package/dist/admin/pages/ContentPagesPage.js.map +1 -1
- package/dist/admin/pages/DashboardPage.js +113 -84
- package/dist/admin/pages/DashboardPage.js.map +1 -1
- package/dist/admin/pages/FeatureFlagsPage.js +93 -75
- package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
- package/dist/admin/pages/OrganizationsListPage.js +105 -87
- package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
- package/dist/admin/pages/ProductsPage.js +138 -130
- package/dist/admin/pages/ProductsPage.js.map +1 -1
- package/dist/admin/pages/ReferralsPage.js +70 -52
- package/dist/admin/pages/ReferralsPage.js.map +1 -1
- package/dist/admin/pages/RolesPermissionsPage.js +136 -118
- package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
- package/dist/admin/pages/SREPage.js +34 -32
- package/dist/admin/pages/SREPage.js.map +1 -1
- package/dist/admin/pages/SandboxSettingsPage.js +360 -322
- package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
- package/dist/admin/pages/SettingsPage.js +64 -61
- package/dist/admin/pages/SettingsPage.js.map +1 -1
- package/dist/admin/pages/SyncFailuresPage.js +120 -118
- package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
- package/dist/admin/pages/TenantDetailPage.js +286 -231
- package/dist/admin/pages/TenantDetailPage.js.map +1 -1
- package/dist/admin/pages/TenantFormRoutePage.js +78 -73
- package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
- package/dist/admin/pages/TenantsListPage.js +182 -155
- package/dist/admin/pages/TenantsListPage.js.map +1 -1
- package/dist/admin/pages/UsersListPage.js +128 -113
- package/dist/admin/pages/UsersListPage.js.map +1 -1
- package/dist/admin/pages/WaitlistPage.js +78 -63
- package/dist/admin/pages/WaitlistPage.js.map +1 -1
- package/dist/admin/pages/WorkflowsPage.js +107 -102
- package/dist/admin/pages/WorkflowsPage.js.map +1 -1
- package/dist/admin/pages/WorkspacesListPage.js +61 -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,74 +9,79 @@ 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, {
|
|
@@ -85,14 +90,15 @@ function g() {
|
|
|
85
90
|
description: "Inspect top affiliates, journey drilldowns, conversions, and revenue attribution.",
|
|
86
91
|
actions: /* @__PURE__ */ h("button", {
|
|
87
92
|
type: "button",
|
|
88
|
-
onClick: () => void
|
|
93
|
+
onClick: () => void L(),
|
|
89
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",
|
|
90
95
|
children: [/* @__PURE__ */ m(f, { className: "h-4 w-4" }), "Refresh"]
|
|
91
96
|
}),
|
|
92
97
|
children: /* @__PURE__ */ h("div", {
|
|
93
98
|
className: "space-y-6",
|
|
94
99
|
children: [
|
|
95
|
-
|
|
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", {
|
|
96
102
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
97
103
|
children: [
|
|
98
104
|
1,
|
|
@@ -100,9 +106,21 @@ function g() {
|
|
|
100
106
|
3,
|
|
101
107
|
4
|
|
102
108
|
].map((e) => /* @__PURE__ */ m("div", { className: "h-28 animate-pulse rounded-lg border border-border-subtle bg-bg-surface" }, e))
|
|
103
|
-
}) :
|
|
109
|
+
}) : w ? /* @__PURE__ */ m("div", {
|
|
104
110
|
className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
|
|
105
|
-
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", {
|
|
106
124
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
|
|
107
125
|
children: [/* @__PURE__ */ h("div", {
|
|
108
126
|
className: "mb-3 flex items-center justify-between text-text-secondary",
|
|
@@ -116,9 +134,9 @@ function g() {
|
|
|
116
134
|
})]
|
|
117
135
|
}, e.title))
|
|
118
136
|
}) : null,
|
|
119
|
-
|
|
137
|
+
P ? /* @__PURE__ */ m("div", {
|
|
120
138
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm text-status-error-text",
|
|
121
|
-
children:
|
|
139
|
+
children: P
|
|
122
140
|
}) : null,
|
|
123
141
|
/* @__PURE__ */ h("div", {
|
|
124
142
|
className: "grid gap-6 xl:grid-cols-[1.15fr,1.85fr]",
|
|
@@ -127,9 +145,9 @@ function g() {
|
|
|
127
145
|
children: [/* @__PURE__ */ m("h2", {
|
|
128
146
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
129
147
|
children: "Top affiliates"
|
|
130
|
-
}),
|
|
148
|
+
}), w?.topAffiliates.length ? /* @__PURE__ */ m("div", {
|
|
131
149
|
className: "space-y-3",
|
|
132
|
-
children:
|
|
150
|
+
children: w.topAffiliates.map((e) => /* @__PURE__ */ m("div", {
|
|
133
151
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-3",
|
|
134
152
|
children: /* @__PURE__ */ h("div", {
|
|
135
153
|
className: "flex items-center justify-between gap-3",
|
|
@@ -164,7 +182,7 @@ function g() {
|
|
|
164
182
|
children: [/* @__PURE__ */ m("h2", {
|
|
165
183
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
166
184
|
children: "Recent journeys"
|
|
167
|
-
}),
|
|
185
|
+
}), E.length ? /* @__PURE__ */ m("div", {
|
|
168
186
|
className: "overflow-x-auto",
|
|
169
187
|
children: /* @__PURE__ */ h("table", {
|
|
170
188
|
className: "min-w-full text-sm",
|
|
@@ -196,9 +214,9 @@ function g() {
|
|
|
196
214
|
children: "Visited"
|
|
197
215
|
})
|
|
198
216
|
]
|
|
199
|
-
}) }), /* @__PURE__ */ m("tbody", { children:
|
|
217
|
+
}) }), /* @__PURE__ */ m("tbody", { children: E.map((e) => /* @__PURE__ */ h("tr", {
|
|
200
218
|
className: "cursor-pointer border-b border-border-subtle/70 text-text-primary",
|
|
201
|
-
onClick: () =>
|
|
219
|
+
onClick: () => k(e.id),
|
|
202
220
|
children: [
|
|
203
221
|
/* @__PURE__ */ m("td", {
|
|
204
222
|
className: "px-3 py-2 font-medium",
|
|
@@ -238,9 +256,9 @@ function g() {
|
|
|
238
256
|
children: [/* @__PURE__ */ m("h2", {
|
|
239
257
|
className: "mb-4 text-lg font-semibold text-text-primary",
|
|
240
258
|
children: "Journey events"
|
|
241
|
-
}),
|
|
259
|
+
}), A.length ? /* @__PURE__ */ m("div", {
|
|
242
260
|
className: "space-y-3",
|
|
243
|
-
children:
|
|
261
|
+
children: A.map((e) => /* @__PURE__ */ m("div", {
|
|
244
262
|
className: "rounded-lg border border-border-subtle bg-bg-surface p-3",
|
|
245
263
|
children: /* @__PURE__ */ h("div", {
|
|
246
264
|
className: "flex items-center justify-between gap-3",
|
|
@@ -271,6 +289,6 @@ function g() {
|
|
|
271
289
|
});
|
|
272
290
|
}
|
|
273
291
|
//#endregion
|
|
274
|
-
export {
|
|
292
|
+
export { v as ReferralsPage };
|
|
275
293
|
|
|
276
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 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 {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 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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"}
|
|
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 {\n title: 'Codes',\n value: summary.totalCodes,\n icon: <Link2 className=\"h-5 w-5\" />,\n hero: false,\n },\n {\n title: 'Visits',\n value: summary.totalVisits,\n icon: <Activity className=\"h-5 w-5\" />,\n hero: false,\n },\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 visits,\n signups, paid conversions and revenue back to each affiliate code, and lets you drill\n 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,13 +7,14 @@ 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",
|
|
19
20
|
gradientWord: "Permissions",
|
|
@@ -24,125 +25,142 @@ function v() {
|
|
|
24
25
|
}),
|
|
25
26
|
children: /* @__PURE__ */ _("div", {
|
|
26
27
|
className: "space-y-4",
|
|
27
|
-
children: [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
className: "
|
|
31
|
-
children: [/* @__PURE__ */ g(
|
|
32
|
-
|
|
33
|
-
className: "
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
className: "
|
|
57
|
-
children: [
|
|
58
|
-
className: "flex items-center gap-2",
|
|
59
|
-
children: [/* @__PURE__ */ g("span", {
|
|
60
|
-
className: "font-semibold text-text-primary",
|
|
61
|
-
children: t.name
|
|
62
|
-
}), t.isSystem && /* @__PURE__ */ g(e, {
|
|
63
|
-
variant: "secondary",
|
|
64
|
-
className: "h-4 px-1 text-[10px]",
|
|
65
|
-
children: "System"
|
|
66
|
-
})]
|
|
67
|
-
}), t.description && /* @__PURE__ */ g("span", {
|
|
68
|
-
className: "mt-0.5 line-clamp-1 text-xs text-text-secondary",
|
|
69
|
-
children: t.description
|
|
70
|
-
})]
|
|
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"]
|
|
71
59
|
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
97
87
|
})]
|
|
98
|
-
}, t.id)) : /* @__PURE__ */ g("span", {
|
|
99
|
-
className: "text-xs italic text-text-secondary",
|
|
100
|
-
children: "No permissions defined"
|
|
101
88
|
})
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
})
|
|
140
158
|
})
|
|
141
|
-
|
|
159
|
+
]
|
|
142
160
|
})
|
|
143
161
|
});
|
|
144
162
|
}
|
|
145
163
|
//#endregion
|
|
146
|
-
export {
|
|
164
|
+
export { b as RolesPermissionsPage };
|
|
147
165
|
|
|
148
166
|
//# sourceMappingURL=RolesPermissionsPage.js.map
|