@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.
Files changed (77) hide show
  1. package/dist/admin/AdminRoot.js +2 -2
  2. package/dist/admin/AdminRoot.js.map +1 -1
  3. package/dist/admin/AdminRoutes.js +130 -78
  4. package/dist/admin/AdminRoutes.js.map +1 -1
  5. package/dist/admin/components/AccessDenied.js +2 -2
  6. package/dist/admin/components/AccessDenied.js.map +1 -1
  7. package/dist/admin/components/ActivityFeed.js +4 -4
  8. package/dist/admin/components/ActivityFeed.js.map +1 -1
  9. package/dist/admin/components/Charts.js.map +1 -1
  10. package/dist/admin/components/ConfirmDialog.js +1 -1
  11. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  12. package/dist/admin/components/DataTable.js.map +1 -1
  13. package/dist/admin/components/FormSheet.js +1 -1
  14. package/dist/admin/components/FormSheet.js.map +1 -1
  15. package/dist/admin/components/TenantSsoPanel.js +1 -1
  16. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  17. package/dist/admin/components/Toast.js +11 -11
  18. package/dist/admin/components/Toast.js.map +1 -1
  19. package/dist/admin/components/forms/SubscriptionForm.js +2 -2
  20. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  21. package/dist/admin/components/forms/TenantForm.js +1 -1
  22. package/dist/admin/components/forms/UserForm.js +1 -1
  23. package/dist/admin/components/ui.js +3 -3
  24. package/dist/admin/components/ui.js.map +1 -1
  25. package/dist/admin/constants/featureFlags.js +12 -0
  26. package/dist/admin/constants/featureFlags.js.map +1 -0
  27. package/dist/admin/constants/index.js +33 -24
  28. package/dist/admin/constants/index.js.map +1 -1
  29. package/dist/admin/pages/AuditLogsPage.js +92 -77
  30. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  31. package/dist/admin/pages/BillingPage.js +95 -51
  32. package/dist/admin/pages/BillingPage.js.map +1 -1
  33. package/dist/admin/pages/CommunicationsPage.js +97 -92
  34. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  35. package/dist/admin/pages/ContentPagesPage.js +274 -259
  36. package/dist/admin/pages/ContentPagesPage.js.map +1 -1
  37. package/dist/admin/pages/DashboardPage.js +113 -84
  38. package/dist/admin/pages/DashboardPage.js.map +1 -1
  39. package/dist/admin/pages/FeatureFlagsPage.js +93 -75
  40. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  41. package/dist/admin/pages/OrganizationsListPage.js +105 -87
  42. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  43. package/dist/admin/pages/ProductsPage.js +138 -130
  44. package/dist/admin/pages/ProductsPage.js.map +1 -1
  45. package/dist/admin/pages/ReferralsPage.js +70 -52
  46. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  47. package/dist/admin/pages/RolesPermissionsPage.js +136 -118
  48. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  49. package/dist/admin/pages/SREPage.js +34 -32
  50. package/dist/admin/pages/SREPage.js.map +1 -1
  51. package/dist/admin/pages/SandboxSettingsPage.js +360 -322
  52. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  53. package/dist/admin/pages/SettingsPage.js +64 -61
  54. package/dist/admin/pages/SettingsPage.js.map +1 -1
  55. package/dist/admin/pages/SyncFailuresPage.js +120 -118
  56. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  57. package/dist/admin/pages/TenantDetailPage.js +286 -231
  58. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  59. package/dist/admin/pages/TenantFormRoutePage.js +78 -73
  60. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  61. package/dist/admin/pages/TenantsListPage.js +182 -155
  62. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  63. package/dist/admin/pages/UsersListPage.js +128 -113
  64. package/dist/admin/pages/UsersListPage.js.map +1 -1
  65. package/dist/admin/pages/WaitlistPage.js +78 -63
  66. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  67. package/dist/admin/pages/WorkflowsPage.js +107 -102
  68. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  69. package/dist/admin/pages/WorkspacesListPage.js +61 -49
  70. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  71. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
  72. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
  73. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
  74. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
  75. package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
  76. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
  77. 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 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, {
@@ -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 F(),
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
- 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", {
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
- }) : S ? /* @__PURE__ */ m("div", {
109
+ }) : w ? /* @__PURE__ */ m("div", {
104
110
  className: "grid gap-4 md:grid-cols-2 xl:grid-cols-4",
105
- 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", {
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
- M ? /* @__PURE__ */ m("div", {
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: M
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
- }), S?.topAffiliates.length ? /* @__PURE__ */ m("div", {
148
+ }), w?.topAffiliates.length ? /* @__PURE__ */ m("div", {
131
149
  className: "space-y-3",
132
- children: S.topAffiliates.map((e) => /* @__PURE__ */ m("div", {
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
- }), w.length ? /* @__PURE__ */ m("div", {
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: w.map((e) => /* @__PURE__ */ h("tr", {
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: () => D(e.id),
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
- }), O.length ? /* @__PURE__ */ m("div", {
259
+ }), A.length ? /* @__PURE__ */ m("div", {
242
260
  className: "space-y-3",
243
- children: O.map((e) => /* @__PURE__ */ m("div", {
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 { g as ReferralsPage };
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&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,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 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",
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: [/* @__PURE__ */ _("div", {
28
- className: "flex items-center justify-between",
29
- children: [/* @__PURE__ */ _("div", {
30
- className: "relative flex-1 max-w-sm",
31
- 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, {
32
- placeholder: "Search roles...",
33
- className: "pl-9",
34
- value: v,
35
- onChange: (e) => y(e.target.value)
36
- })]
37
- }), /* @__PURE__ */ g("div", {
38
- className: "flex items-center gap-2",
39
- children: /* @__PURE__ */ _(e, {
40
- variant: "outline",
41
- className: "h-8",
42
- children: [C?.resourceTypes?.length ?? 0, " Resource Types"]
43
- })
44
- })]
45
- }), S ? /* @__PURE__ */ g(n, {
46
- illustration: "empty-generic",
47
- icon: /* @__PURE__ */ g(m, { className: "h-8 w-8 text-status-error-text" }),
48
- title: "Error loading roles",
49
- description: S.message
50
- }) : /* @__PURE__ */ g(a, {
51
- columns: [
52
- {
53
- key: "name",
54
- header: "Role Name",
55
- render: (t) => /* @__PURE__ */ _("div", {
56
- className: "flex flex-col text-left",
57
- children: [/* @__PURE__ */ _("div", {
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
- key: "priority",
75
- header: "Priority",
76
- align: "center",
77
- width: "80px",
78
- render: (e) => /* @__PURE__ */ g("span", {
79
- className: "rounded bg-bg-sunken px-2 py-0.5 text-xs font-medium",
80
- children: e.priority
81
- })
82
- },
83
- {
84
- key: "permissions",
85
- header: "Default Permissions",
86
- render: (t) => /* @__PURE__ */ g("div", {
87
- className: "flex max-w-md flex-wrap gap-1 text-left",
88
- children: t.defaultPermissions && t.defaultPermissions.length > 0 ? t.defaultPermissions.map((t) => /* @__PURE__ */ _(e, {
89
- variant: "outline",
90
- className: "bg-bg-surface/50 px-1 py-0 font-normal text-[10px]",
91
- children: [/* @__PURE__ */ _("span", {
92
- className: "mr-1 text-text-secondary",
93
- children: [t.resourceType, ":"]
94
- }), /* @__PURE__ */ g("span", {
95
- className: "font-medium",
96
- 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
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
- key: "actions",
106
- header: "",
107
- align: "right",
108
- width: "50px",
109
- render: (e) => /* @__PURE__ */ g(o, { actions: [{
110
- key: "edit",
111
- label: "Edit Role",
112
- icon: /* @__PURE__ */ g(d, { className: "h-4 w-4" }),
113
- onClick: () => console.log("Edit", e.id),
114
- disabled: e.isSystem
115
- }, {
116
- key: "delete",
117
- label: "Delete Role",
118
- icon: /* @__PURE__ */ g(h, { className: "h-4 w-4" }),
119
- onClick: () => console.log("Delete", e.id),
120
- destructive: !0,
121
- disabled: e.isSystem,
122
- separator: !0
123
- }] })
124
- }
125
- ],
126
- data: T,
127
- keyExtractor: (e) => e.id,
128
- loading: x,
129
- emptyState: /* @__PURE__ */ g(n, {
130
- illustration: "empty-people",
131
- icon: /* @__PURE__ */ g(m, { className: "h-8 w-8" }),
132
- title: "No roles found",
133
- description: v ? `No roles match "${v}"` : "Roles and permissions will appear here when they are created",
134
- action: v ? /* @__PURE__ */ g(t, {
135
- variant: "outline",
136
- size: "sm",
137
- onClick: () => y(""),
138
- children: "Clear Search"
139
- }) : 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
+ })
140
158
  })
141
- })]
159
+ ]
142
160
  })
143
161
  });
144
162
  }
145
163
  //#endregion
146
- export { v as RolesPermissionsPage };
164
+ export { b as RolesPermissionsPage };
147
165
 
148
166
  //# sourceMappingURL=RolesPermissionsPage.js.map