@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.
Files changed (76) hide show
  1. package/dist/admin/components/AccessDenied.js +2 -2
  2. package/dist/admin/components/AccessDenied.js.map +1 -1
  3. package/dist/admin/components/ActivityFeed.js +4 -4
  4. package/dist/admin/components/ActivityFeed.js.map +1 -1
  5. package/dist/admin/components/Charts.js.map +1 -1
  6. package/dist/admin/components/ConfirmDialog.js +1 -1
  7. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  8. package/dist/admin/components/DataTable.js +5 -5
  9. package/dist/admin/components/DataTable.js.map +1 -1
  10. package/dist/admin/components/FormSheet.js +1 -1
  11. package/dist/admin/components/FormSheet.js.map +1 -1
  12. package/dist/admin/components/MetricCard.js +40 -37
  13. package/dist/admin/components/MetricCard.js.map +1 -1
  14. package/dist/admin/components/PageLayout.js +28 -22
  15. package/dist/admin/components/PageLayout.js.map +1 -1
  16. package/dist/admin/components/TenantSsoPanel.js +1 -1
  17. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  18. package/dist/admin/components/Toast.js +11 -11
  19. package/dist/admin/components/Toast.js.map +1 -1
  20. package/dist/admin/components/forms/SubscriptionForm.js +2 -2
  21. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  22. package/dist/admin/components/forms/TenantForm.js +1 -1
  23. package/dist/admin/components/forms/UserForm.js +1 -1
  24. package/dist/admin/components/ui.js +48 -41
  25. package/dist/admin/components/ui.js.map +1 -1
  26. package/dist/admin/constants/index.js +33 -24
  27. package/dist/admin/constants/index.js.map +1 -1
  28. package/dist/admin/pages/AuditLogsPage.js +94 -77
  29. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  30. package/dist/admin/pages/BillingPage.js +70 -18
  31. package/dist/admin/pages/BillingPage.js.map +1 -1
  32. package/dist/admin/pages/CommunicationsPage.js +98 -90
  33. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  34. package/dist/admin/pages/ContentPagesPage.js +276 -259
  35. package/dist/admin/pages/ContentPagesPage.js.map +1 -1
  36. package/dist/admin/pages/DashboardPage.js +114 -82
  37. package/dist/admin/pages/DashboardPage.js.map +1 -1
  38. package/dist/admin/pages/FeatureFlagsPage.js +94 -74
  39. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  40. package/dist/admin/pages/OrganizationsListPage.js +106 -86
  41. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  42. package/dist/admin/pages/ProductsPage.js +139 -128
  43. package/dist/admin/pages/ProductsPage.js.map +1 -1
  44. package/dist/admin/pages/ReferralsPage.js +74 -52
  45. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  46. package/dist/admin/pages/RolesPermissionsPage.js +137 -116
  47. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  48. package/dist/admin/pages/SREPage.js +38 -32
  49. package/dist/admin/pages/SREPage.js.map +1 -1
  50. package/dist/admin/pages/SandboxSettingsPage.js +361 -322
  51. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  52. package/dist/admin/pages/SettingsPage.js +145 -130
  53. package/dist/admin/pages/SettingsPage.js.map +1 -1
  54. package/dist/admin/pages/SyncFailuresPage.js +123 -118
  55. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  56. package/dist/admin/pages/TenantDetailPage.js +261 -218
  57. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  58. package/dist/admin/pages/TenantFormRoutePage.js +65 -61
  59. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  60. package/dist/admin/pages/TenantsListPage.js +88 -71
  61. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  62. package/dist/admin/pages/UsersListPage.js +129 -112
  63. package/dist/admin/pages/UsersListPage.js.map +1 -1
  64. package/dist/admin/pages/WaitlistPage.js +80 -63
  65. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  66. package/dist/admin/pages/WorkflowsPage.js +108 -100
  67. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  68. package/dist/admin/pages/WorkspacesListPage.js +62 -49
  69. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  70. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
  71. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
  72. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
  73. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
  74. package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
  75. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
  76. 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 g() {
14
- let { canViewDashboard: g, isLoading: _ } = t(), { apiGatewayUrl: v, authToken: y, currentUser: b } = e(), x = g || b?.role === "PLATFORM_SUPERADMIN", [S, C] = l(null), [w, T] = l([]), [E, D] = l(null), [O, k] = l([]), [A, j] = l(!0), [M, N] = l(null), P = c(() => ({
15
- apiGatewayUrl: v,
16
- authToken: y,
17
- actorId: b?.id,
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
- v,
21
- y,
22
- b?.id
23
- ]), F = async () => {
24
- j(!0), N(null);
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
- ...P,
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
- ...P,
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
- C(e.adminAffiliateAnalytics), T(t.affiliateJourneys.items);
34
+ T(e.adminAffiliateAnalytics), D(t.affiliateJourneys.items);
34
35
  } catch (e) {
35
- N(e instanceof Error ? e.message : "Failed to load referrals dashboard");
36
+ F(e instanceof Error ? e.message : "Failed to load referrals dashboard");
36
37
  } finally {
37
- j(!1);
38
+ N(!1);
38
39
  }
39
40
  };
40
41
  s(() => {
41
- F();
42
- }, [P]), s(() => {
43
- if (!E) {
44
- k([]);
42
+ L();
43
+ }, [I]), s(() => {
44
+ if (!O) {
45
+ j([]);
45
46
  return;
46
47
  }
47
48
  o({
48
- ...P,
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: E }
51
- }).then((e) => k(e.affiliateJourneyEvents)).catch((e) => {
52
- N(e instanceof Error ? e.message : "Failed to load journey events");
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
- }, [P, E]);
55
- let I = S ? [
55
+ }, [I, O]);
56
+ let R = w ? [
56
57
  {
57
- title: "Codes",
58
- value: S.totalCodes,
59
- icon: /* @__PURE__ */ m(d, { className: "h-5 w-5" })
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: "Visits",
63
- value: S.totalVisits,
64
- icon: /* @__PURE__ */ m(u, { className: "h-5 w-5" })
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: "Signups",
68
- value: S.totalSignups,
69
- icon: /* @__PURE__ */ m(p, { className: "h-5 w-5" })
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: S.totalPaidConversions + S.totalStorePurchases,
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: x,
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 F(),
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
- A ? /* @__PURE__ */ m("div", {
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
- }) : S ? /* @__PURE__ */ m("div", {
109
+ }) : w ? /* @__PURE__ */ m("div", {
103
110
  className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
104
- children: I.map((e) => /* @__PURE__ */ h("div", {
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
- M ? /* @__PURE__ */ m("div", {
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: M
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
- }), S?.topAffiliates.length ? /* @__PURE__ */ m("div", {
148
+ }), w?.topAffiliates.length ? /* @__PURE__ */ m("div", {
130
149
  className: "space-y-3",
131
- children: S.topAffiliates.map((e) => /* @__PURE__ */ m("div", {
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
- }), w.length ? /* @__PURE__ */ m("div", {
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: w.map((e) => /* @__PURE__ */ h("tr", {
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: () => D(e.id),
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
- }), O.length ? /* @__PURE__ */ m("div", {
259
+ }), A.length ? /* @__PURE__ */ m("div", {
239
260
  className: "space-y-3",
240
- children: O.map((e) => /* @__PURE__ */ m("div", {
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 { g as ReferralsPage };
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&apos;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 v() {
12
- let [v, y] = u(""), { data: b, loading: x, error: S } = c({ variables: { scopeId: "global" } }), { data: C } = s(), w = l(() => b?.roles ?? [], [b]), T = l(() => {
13
- if (!v.trim()) return w;
14
- let e = v.toLowerCase();
15
- return w.filter((t) => t.name.toLowerCase().includes(e) || t.description?.toLowerCase().includes(e));
16
- }, [w, v]);
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: [/* @__PURE__ */ _("div", {
27
- className: "flex items-center justify-between",
28
- children: [/* @__PURE__ */ _("div", {
29
- className: "relative flex-1 max-w-sm",
30
- 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, {
31
- placeholder: "Search roles...",
32
- className: "pl-9",
33
- value: v,
34
- onChange: (e) => y(e.target.value)
35
- })]
36
- }), /* @__PURE__ */ g("div", {
37
- className: "flex items-center gap-2",
38
- children: /* @__PURE__ */ _(e, {
39
- variant: "outline",
40
- className: "h-8",
41
- children: [C?.resourceTypes?.length ?? 0, " Resource Types"]
42
- })
43
- })]
44
- }), S ? /* @__PURE__ */ g(n, {
45
- icon: /* @__PURE__ */ g(m, { className: "h-8 w-8 text-status-error-text" }),
46
- title: "Error loading roles",
47
- description: S.message
48
- }) : /* @__PURE__ */ g(a, {
49
- columns: [
50
- {
51
- key: "name",
52
- header: "Role Name",
53
- render: (t) => /* @__PURE__ */ _("div", {
54
- className: "flex flex-col text-left",
55
- children: [/* @__PURE__ */ _("div", {
56
- className: "flex items-center gap-2",
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
- key: "priority",
73
- header: "Priority",
74
- align: "center",
75
- width: "80px",
76
- render: (e) => /* @__PURE__ */ g("span", {
77
- className: "rounded bg-bg-sunken px-2 py-0.5 text-xs font-medium",
78
- children: e.priority
79
- })
80
- },
81
- {
82
- key: "permissions",
83
- header: "Default Permissions",
84
- render: (t) => /* @__PURE__ */ g("div", {
85
- className: "flex max-w-md flex-wrap gap-1 text-left",
86
- children: t.defaultPermissions && t.defaultPermissions.length > 0 ? t.defaultPermissions.map((t) => /* @__PURE__ */ _(e, {
87
- variant: "outline",
88
- className: "bg-bg-surface/50 px-1 py-0 font-normal text-[10px]",
89
- children: [/* @__PURE__ */ _("span", {
90
- className: "mr-1 text-text-secondary",
91
- children: [t.resourceType, ":"]
92
- }), /* @__PURE__ */ g("span", {
93
- className: "font-medium",
94
- children: t.level
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
- key: "actions",
104
- header: "",
105
- align: "right",
106
- width: "50px",
107
- render: (e) => /* @__PURE__ */ g(o, { actions: [{
108
- key: "edit",
109
- label: "Edit Role",
110
- icon: /* @__PURE__ */ g(d, { className: "h-4 w-4" }),
111
- onClick: () => console.log("Edit", e.id),
112
- disabled: e.isSystem
113
- }, {
114
- key: "delete",
115
- label: "Delete Role",
116
- icon: /* @__PURE__ */ g(h, { className: "h-4 w-4" }),
117
- onClick: () => console.log("Delete", e.id),
118
- destructive: !0,
119
- disabled: e.isSystem,
120
- separator: !0
121
- }] })
122
- }
123
- ],
124
- data: T,
125
- keyExtractor: (e) => e.id,
126
- loading: x,
127
- emptyState: /* @__PURE__ */ g(n, {
128
- icon: /* @__PURE__ */ g(m, { className: "h-8 w-8" }),
129
- title: "No roles found",
130
- description: v ? `No roles match "${v}"` : "Roles and permissions will appear here when they are created",
131
- action: v ? /* @__PURE__ */ g(t, {
132
- variant: "outline",
133
- size: "sm",
134
- onClick: () => y(""),
135
- children: "Clear Search"
136
- }) : void 0
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 { v as RolesPermissionsPage };
164
+ export { b as RolesPermissionsPage };
144
165
 
145
166
  //# sourceMappingURL=RolesPermissionsPage.js.map