@burdenoff/microfe-admin 2026.805.1 → 2026.829.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/AdminRoutes.js +28 -24
- package/dist/admin/AdminRoutes.js.map +1 -1
- package/dist/admin/hooks/useAdminPermissions.js +1 -0
- package/dist/admin/hooks/useAdminPermissions.js.map +1 -1
- package/dist/admin/pages/ExploreConversationsPage.js +419 -0
- package/dist/admin/pages/ExploreConversationsPage.js.map +1 -0
- package/dist/admin/pages/index.js +1 -0
- package/package.json +1 -1
|
@@ -9,14 +9,14 @@ import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
|
9
9
|
import { FeatureGate as d } from "@burdenoff/fe-libs/shared/feature-flags";
|
|
10
10
|
import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
11
|
//#region src/admin/AdminRoutes.tsx
|
|
12
|
-
var p = a(() => import("./pages/TenantsListPage.js").then((e) => ({ default: e.TenantsListPage }))), m = a(() => import("./pages/TenantDetailPage.js").then((e) => ({ default: e.TenantDetailPage }))), h = a(() => import("./pages/TenantFormRoutePage.js").then((e) => ({ default: e.TenantFormRoutePage }))), g = a(() => import("./pages/UsersListPage.js").then((e) => ({ default: e.UsersListPage }))), _ = a(() => import("./pages/BillingPage.js").then((e) => ({ default: e.BillingPage }))), v = a(() => import("./pages/SettingsPage.js").then((e) => ({ default: e.SettingsPage }))), y = a(() => import("./pages/CommunicationsPage.js").then((e) => ({ default: e.CommunicationsPage }))), b = a(() => import("./pages/SREPage.js").then((e) => ({ default: e.SREPage }))), x = a(() => import("./pages/SyncFailuresPage.js").then((e) => ({ default: e.SyncFailuresPage }))), S = a(() => import("./pages/AuditLogsPage.js").then((e) => ({ default: e.AuditLogsPage }))), C = a(() => import("./pages/SandboxSettingsPage.js").then((e) => ({ default: e.SandboxSettingsPage }))),
|
|
13
|
-
function
|
|
12
|
+
var p = a(() => import("./pages/TenantsListPage.js").then((e) => ({ default: e.TenantsListPage }))), m = a(() => import("./pages/TenantDetailPage.js").then((e) => ({ default: e.TenantDetailPage }))), h = a(() => import("./pages/TenantFormRoutePage.js").then((e) => ({ default: e.TenantFormRoutePage }))), g = a(() => import("./pages/UsersListPage.js").then((e) => ({ default: e.UsersListPage }))), _ = a(() => import("./pages/BillingPage.js").then((e) => ({ default: e.BillingPage }))), v = a(() => import("./pages/SettingsPage.js").then((e) => ({ default: e.SettingsPage }))), y = a(() => import("./pages/CommunicationsPage.js").then((e) => ({ default: e.CommunicationsPage }))), b = a(() => import("./pages/SREPage.js").then((e) => ({ default: e.SREPage }))), x = a(() => import("./pages/SyncFailuresPage.js").then((e) => ({ default: e.SyncFailuresPage }))), S = a(() => import("./pages/AuditLogsPage.js").then((e) => ({ default: e.AuditLogsPage }))), C = a(() => import("./pages/ExploreConversationsPage.js").then((e) => ({ default: e.ExploreConversationsPage }))), w = a(() => import("./pages/SandboxSettingsPage.js").then((e) => ({ default: e.SandboxSettingsPage }))), T = a(() => import("./pages/OrganizationsListPage.js").then((e) => ({ default: e.OrganizationsListPage }))), E = a(() => import("./pages/WorkspacesListPage.js").then((e) => ({ default: e.WorkspacesListPage }))), D = a(() => import("./pages/FeatureFlagsPage.js").then((e) => ({ default: e.FeatureFlagsPage }))), O = a(() => import("./pages/RolesPermissionsPage.js").then((e) => ({ default: e.RolesPermissionsPage }))), k = a(() => import("./pages/WorkflowsPage.js").then((e) => ({ default: e.WorkflowsPage }))), A = a(() => import("./pages/ProductsPage.js").then((e) => ({ default: e.ProductsPage }))), j = a(() => import("./pages/ContentPagesPage.js").then((e) => ({ default: e.ContentPagesPage }))), M = a(() => import("./pages/WaitlistPage.js").then((e) => ({ default: e.WaitlistPage }))), N = a(() => import("./pages/ReferralsPage.js").then((e) => ({ default: e.ReferralsPage })));
|
|
13
|
+
function P() {
|
|
14
14
|
return /* @__PURE__ */ l("div", {
|
|
15
15
|
className: "flex h-64 items-center justify-center",
|
|
16
16
|
children: /* @__PURE__ */ l("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent" })
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
|
-
function
|
|
19
|
+
function F() {
|
|
20
20
|
let { t: e } = f();
|
|
21
21
|
return /* @__PURE__ */ l("div", {
|
|
22
22
|
className: "flex min-h-[400px] items-center justify-center p-8 text-center",
|
|
@@ -29,21 +29,21 @@ function P() {
|
|
|
29
29
|
})] })
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
|
-
function
|
|
32
|
+
function I(e, t, n) {
|
|
33
33
|
return /* @__PURE__ */ l(d, {
|
|
34
34
|
flag: e,
|
|
35
35
|
defaultValue: n,
|
|
36
|
-
fallback: /* @__PURE__ */ l(
|
|
36
|
+
fallback: /* @__PURE__ */ l(F, {}),
|
|
37
37
|
children: t
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
function
|
|
41
|
-
return e.reduceRight((e, t) =>
|
|
40
|
+
function L(e, t) {
|
|
41
|
+
return e.reduceRight((e, t) => I(t.flag, e, t.defaultValue), t);
|
|
42
42
|
}
|
|
43
|
-
function
|
|
43
|
+
function R() {
|
|
44
44
|
let { basePath: t } = e(), a = t || "/admin";
|
|
45
45
|
return /* @__PURE__ */ l(i, {
|
|
46
|
-
fallback: /* @__PURE__ */ l(
|
|
46
|
+
fallback: /* @__PURE__ */ l(P, {}),
|
|
47
47
|
children: /* @__PURE__ */ u(c, { children: [
|
|
48
48
|
/* @__PURE__ */ l(s, {
|
|
49
49
|
index: !0,
|
|
@@ -58,7 +58,7 @@ function L() {
|
|
|
58
58
|
}),
|
|
59
59
|
/* @__PURE__ */ l(s, {
|
|
60
60
|
path: "tenants",
|
|
61
|
-
element:
|
|
61
|
+
element: L([{
|
|
62
62
|
flag: r.management,
|
|
63
63
|
defaultValue: !0
|
|
64
64
|
}, {
|
|
@@ -68,7 +68,7 @@ function L() {
|
|
|
68
68
|
}),
|
|
69
69
|
/* @__PURE__ */ l(s, {
|
|
70
70
|
path: "tenants/new",
|
|
71
|
-
element:
|
|
71
|
+
element: L([{
|
|
72
72
|
flag: r.management,
|
|
73
73
|
defaultValue: !0
|
|
74
74
|
}, {
|
|
@@ -78,7 +78,7 @@ function L() {
|
|
|
78
78
|
}),
|
|
79
79
|
/* @__PURE__ */ l(s, {
|
|
80
80
|
path: "tenants/:tenantId",
|
|
81
|
-
element:
|
|
81
|
+
element: L([{
|
|
82
82
|
flag: r.management,
|
|
83
83
|
defaultValue: !0
|
|
84
84
|
}, {
|
|
@@ -88,7 +88,7 @@ function L() {
|
|
|
88
88
|
}),
|
|
89
89
|
/* @__PURE__ */ l(s, {
|
|
90
90
|
path: "tenants/:tenantId/edit",
|
|
91
|
-
element:
|
|
91
|
+
element: L([{
|
|
92
92
|
flag: r.management,
|
|
93
93
|
defaultValue: !0
|
|
94
94
|
}, {
|
|
@@ -98,11 +98,11 @@ function L() {
|
|
|
98
98
|
}),
|
|
99
99
|
/* @__PURE__ */ l(s, {
|
|
100
100
|
path: "organizations",
|
|
101
|
-
element: /* @__PURE__ */ l(
|
|
101
|
+
element: /* @__PURE__ */ l(T, {})
|
|
102
102
|
}),
|
|
103
103
|
/* @__PURE__ */ l(s, {
|
|
104
104
|
path: "workspaces",
|
|
105
|
-
element: /* @__PURE__ */ l(
|
|
105
|
+
element: /* @__PURE__ */ l(E, {})
|
|
106
106
|
}),
|
|
107
107
|
/* @__PURE__ */ l(s, {
|
|
108
108
|
path: "users",
|
|
@@ -114,15 +114,15 @@ function L() {
|
|
|
114
114
|
}),
|
|
115
115
|
/* @__PURE__ */ l(s, {
|
|
116
116
|
path: "roles",
|
|
117
|
-
element: /* @__PURE__ */ l(
|
|
117
|
+
element: /* @__PURE__ */ l(O, {})
|
|
118
118
|
}),
|
|
119
119
|
/* @__PURE__ */ l(s, {
|
|
120
120
|
path: "feature-flags",
|
|
121
|
-
element: /* @__PURE__ */ l(
|
|
121
|
+
element: /* @__PURE__ */ l(D, {})
|
|
122
122
|
}),
|
|
123
123
|
/* @__PURE__ */ l(s, {
|
|
124
124
|
path: "workflows",
|
|
125
|
-
element: /* @__PURE__ */ l(
|
|
125
|
+
element: /* @__PURE__ */ l(k, {})
|
|
126
126
|
}),
|
|
127
127
|
/* @__PURE__ */ l(s, {
|
|
128
128
|
path: "billing",
|
|
@@ -163,25 +163,29 @@ function L() {
|
|
|
163
163
|
path: "audit",
|
|
164
164
|
element: /* @__PURE__ */ l(S, {})
|
|
165
165
|
}),
|
|
166
|
+
/* @__PURE__ */ l(s, {
|
|
167
|
+
path: "explore",
|
|
168
|
+
element: /* @__PURE__ */ l(C, {})
|
|
169
|
+
}),
|
|
166
170
|
/* @__PURE__ */ l(s, {
|
|
167
171
|
path: "referrals",
|
|
168
|
-
element: /* @__PURE__ */ l(
|
|
172
|
+
element: /* @__PURE__ */ l(N, {})
|
|
169
173
|
}),
|
|
170
174
|
/* @__PURE__ */ l(s, {
|
|
171
175
|
path: "sandbox",
|
|
172
|
-
element: /* @__PURE__ */ l(
|
|
176
|
+
element: /* @__PURE__ */ l(w, {})
|
|
173
177
|
}),
|
|
174
178
|
/* @__PURE__ */ l(s, {
|
|
175
179
|
path: "products",
|
|
176
|
-
element: /* @__PURE__ */ l(
|
|
180
|
+
element: /* @__PURE__ */ l(A, {})
|
|
177
181
|
}),
|
|
178
182
|
/* @__PURE__ */ l(s, {
|
|
179
183
|
path: "content",
|
|
180
|
-
element: /* @__PURE__ */ l(
|
|
184
|
+
element: /* @__PURE__ */ l(j, {})
|
|
181
185
|
}),
|
|
182
186
|
/* @__PURE__ */ l(s, {
|
|
183
187
|
path: "waitlist",
|
|
184
|
-
element: /* @__PURE__ */ l(
|
|
188
|
+
element: /* @__PURE__ */ l(M, {})
|
|
185
189
|
}),
|
|
186
190
|
/* @__PURE__ */ l(s, {
|
|
187
191
|
path: "settings",
|
|
@@ -198,6 +202,6 @@ function L() {
|
|
|
198
202
|
});
|
|
199
203
|
}
|
|
200
204
|
//#endregion
|
|
201
|
-
export {
|
|
205
|
+
export { R as AdminRoutes };
|
|
202
206
|
|
|
203
207
|
//# sourceMappingURL=AdminRoutes.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminRoutes.js","names":[],"sources":["../../src/admin/AdminRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { Navigate, Route, Routes } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { DashboardPage } from './pages';\nimport { useAdmin } from './providers/AdminProvider';\nimport { TENANT_FEATURE_FLAGS } from './constants/featureFlags';\nimport { tWithFallback } from './utils/i18n';\n\n// Lazy loaded pages\nconst TenantsListPage = lazy(() =>\n import('./pages/TenantsListPage').then((m) => ({ default: m.TenantsListPage }))\n);\nconst TenantDetailPage = lazy(() =>\n import('./pages/TenantDetailPage').then((m) => ({ default: m.TenantDetailPage }))\n);\nconst TenantFormRoutePage = lazy(() =>\n import('./pages/TenantFormRoutePage').then((m) => ({ default: m.TenantFormRoutePage }))\n);\nconst UsersListPage = lazy(() =>\n import('./pages/UsersListPage').then((m) => ({ default: m.UsersListPage }))\n);\nconst BillingPage = lazy(() =>\n import('./pages/BillingPage').then((m) => ({ default: m.BillingPage }))\n);\nconst SettingsPage = lazy(() =>\n import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage }))\n);\nconst CommunicationsPage = lazy(() =>\n import('./pages/CommunicationsPage').then((m) => ({ default: m.CommunicationsPage }))\n);\nconst SREPage = lazy(() => import('./pages/SREPage').then((m) => ({ default: m.SREPage })));\nconst SyncFailuresPage = lazy(() =>\n import('./pages/SyncFailuresPage').then((m) => ({ default: m.SyncFailuresPage }))\n);\nconst AuditLogsPage = lazy(() =>\n import('./pages/AuditLogsPage').then((m) => ({ default: m.AuditLogsPage }))\n);\nconst SandboxSettingsPage = lazy(() =>\n import('./pages/SandboxSettingsPage').then((m) => ({ default: m.SandboxSettingsPage }))\n);\nconst OrganizationsListPage = lazy(() =>\n import('./pages/OrganizationsListPage').then((m) => ({ default: m.OrganizationsListPage }))\n);\nconst WorkspacesListPage = lazy(() =>\n import('./pages/WorkspacesListPage').then((m) => ({ default: m.WorkspacesListPage }))\n);\nconst FeatureFlagsPage = lazy(() =>\n import('./pages/FeatureFlagsPage').then((m) => ({ default: m.FeatureFlagsPage }))\n);\nconst RolesPermissionsPage = lazy(() =>\n import('./pages/RolesPermissionsPage').then((m) => ({ default: m.RolesPermissionsPage }))\n);\nconst WorkflowsPage = lazy(() =>\n import('./pages/WorkflowsPage').then((m) => ({ default: m.WorkflowsPage }))\n);\nconst ProductsPage = lazy(() =>\n import('./pages/ProductsPage').then((m) => ({ default: m.ProductsPage }))\n);\nconst ContentPagesPage = lazy(() =>\n import('./pages/ContentPagesPage').then((m) => ({ default: m.ContentPagesPage }))\n);\nconst WaitlistPage = lazy(() =>\n import('./pages/WaitlistPage').then((m) => ({ default: m.WaitlistPage }))\n);\nconst ReferralsPage = lazy(() =>\n import('./pages/ReferralsPage').then((m) => ({ default: m.ReferralsPage }))\n);\n\n// Loading fallback component\nfunction PageLoader() {\n return (\n <div className=\"flex h-64 items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n}\n\nfunction FeatureDisabledFallback() {\n const { t } = useI18n();\n\n return (\n <div className=\"flex min-h-[400px] items-center justify-center p-8 text-center\">\n <div>\n <h2 className=\"mb-2 text-lg font-semibold text-foreground\">\n {tWithFallback(\n t,\n 'admin.routes.tenantFeatureDisabledTitle',\n 'Tenant management unavailable'\n )}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'admin.routes.tenantFeatureDisabled',\n 'This feature is currently disabled.'\n )}\n </p>\n </div>\n </div>\n );\n}\n\nfunction gated(flag: string, element: ReactNode, defaultValue: boolean): ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nfunction gatedAll(\n gates: Array<{ flag: string; defaultValue: boolean }>,\n element: ReactElement\n): ReactElement {\n return gates.reduceRight((child, gate) => gated(gate.flag, child, gate.defaultValue), element);\n}\n\n/**\n * Internal router for the Admin microfrontend.\n * This component handles routing within the microfrontend using React Router v7.\n *\n * Routes:\n * - / → Dashboard (redirect)\n * - /dashboard → Dashboard\n * - /tenants → Tenants list\n * - /tenants/new → Create new tenant\n * - /tenants/:tenantId → Tenant detail\n * - /tenants/:tenantId/edit → Edit tenant\n * - /users → Users list\n * - /users/:userId → User detail\n * - /billing → Billing overview\n * - /billing/subscriptions → Subscriptions list\n * - /billing/invoices → Invoices list\n * - /billing/plans → Plans management\n * - /support → Support overview\n * - /support/tickets → Tickets list\n * - /support/tickets/:ticketId → Ticket detail\n * - /products → Products management\n * - /communications → Communications center\n * - /sre → SRE Monitoring\n * - /audit → Audit logs\n * - /sandbox → Sandbox settings\n * - /settings → Settings\n */\nexport function AdminRoutes() {\n const { basePath } = useAdmin();\n const base = basePath || '/admin';\n\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Default redirect to dashboard */}\n <Route index element={<Navigate to={`${base}/dashboard`} replace />} />\n\n {/* Dashboard - eager loaded for fast initial render */}\n <Route path=\"dashboard\" element={<DashboardPage />} />\n\n {/* Tenants */}\n <Route\n path=\"tenants\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.list, defaultValue: true },\n ],\n <TenantsListPage />\n )}\n />\n <Route\n path=\"tenants/new\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.create, defaultValue: true },\n ],\n <TenantFormRoutePage mode=\"create\" />\n )}\n />\n <Route\n path=\"tenants/:tenantId\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.detail, defaultValue: true },\n ],\n <TenantDetailPage />\n )}\n />\n <Route\n path=\"tenants/:tenantId/edit\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.edit, defaultValue: true },\n ],\n <TenantFormRoutePage mode=\"edit\" />\n )}\n />\n\n {/* Organizations */}\n <Route path=\"organizations\" element={<OrganizationsListPage />} />\n\n {/* Workspaces */}\n <Route path=\"workspaces\" element={<WorkspacesListPage />} />\n\n {/* Users */}\n <Route path=\"users\" element={<UsersListPage />} />\n <Route path=\"users/:userId\" element={<UsersListPage />} />\n\n {/* Roles & Permissions */}\n <Route path=\"roles\" element={<RolesPermissionsPage />} />\n\n {/* Feature Flags */}\n <Route path=\"feature-flags\" element={<FeatureFlagsPage />} />\n\n {/* Workflows */}\n <Route path=\"workflows\" element={<WorkflowsPage />} />\n\n {/* Billing */}\n <Route path=\"billing\" element={<BillingPage />} />\n <Route path=\"billing/subscriptions\" element={<BillingPage />} />\n <Route path=\"billing/invoices\" element={<BillingPage />} />\n <Route path=\"billing/plans\" element={<BillingPage />} />\n\n {/* Support — redirects to shell-level /support/* route (microfe-support MFE) */}\n <Route path=\"support/*\" element={<Navigate to=\"/support\" replace />} />\n\n {/* Communications */}\n <Route path=\"communications\" element={<CommunicationsPage />} />\n\n {/* SRE Monitoring */}\n <Route path=\"sre\" element={<SREPage />} />\n\n {/* Sync Failures (cross-layer sync outbox) */}\n <Route path=\"sync-failures\" element={<SyncFailuresPage />} />\n\n {/* Audit Logs */}\n <Route path=\"audit\" element={<AuditLogsPage />} />\n\n {/* Referrals */}\n <Route path=\"referrals\" element={<ReferralsPage />} />\n\n {/* Sandbox */}\n <Route path=\"sandbox\" element={<SandboxSettingsPage />} />\n\n {/* Products */}\n <Route path=\"products\" element={<ProductsPage />} />\n\n {/* Content (blog / case studies / changelog) */}\n <Route path=\"content\" element={<ContentPagesPage />} />\n\n {/* Waitlist */}\n <Route path=\"waitlist\" element={<WaitlistPage />} />\n\n {/* Settings */}\n <Route path=\"settings\" element={<SettingsPage />} />\n\n {/* Catch-all route - redirect to dashboard (absolute path to prevent infinite loops) */}\n <Route path=\"*\" element={<Navigate to={`${base}/dashboard`} replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminRoutes.js","names":[],"sources":["../../src/admin/AdminRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { Navigate, Route, Routes } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { DashboardPage } from './pages';\nimport { useAdmin } from './providers/AdminProvider';\nimport { TENANT_FEATURE_FLAGS } from './constants/featureFlags';\nimport { tWithFallback } from './utils/i18n';\n\n// Lazy loaded pages\nconst TenantsListPage = lazy(() =>\n import('./pages/TenantsListPage').then((m) => ({ default: m.TenantsListPage }))\n);\nconst TenantDetailPage = lazy(() =>\n import('./pages/TenantDetailPage').then((m) => ({ default: m.TenantDetailPage }))\n);\nconst TenantFormRoutePage = lazy(() =>\n import('./pages/TenantFormRoutePage').then((m) => ({ default: m.TenantFormRoutePage }))\n);\nconst UsersListPage = lazy(() =>\n import('./pages/UsersListPage').then((m) => ({ default: m.UsersListPage }))\n);\nconst BillingPage = lazy(() =>\n import('./pages/BillingPage').then((m) => ({ default: m.BillingPage }))\n);\nconst SettingsPage = lazy(() =>\n import('./pages/SettingsPage').then((m) => ({ default: m.SettingsPage }))\n);\nconst CommunicationsPage = lazy(() =>\n import('./pages/CommunicationsPage').then((m) => ({ default: m.CommunicationsPage }))\n);\nconst SREPage = lazy(() => import('./pages/SREPage').then((m) => ({ default: m.SREPage })));\nconst SyncFailuresPage = lazy(() =>\n import('./pages/SyncFailuresPage').then((m) => ({ default: m.SyncFailuresPage }))\n);\nconst AuditLogsPage = lazy(() =>\n import('./pages/AuditLogsPage').then((m) => ({ default: m.AuditLogsPage }))\n);\nconst ExploreConversationsPage = lazy(() =>\n import('./pages/ExploreConversationsPage').then((m) => ({\n default: m.ExploreConversationsPage,\n }))\n);\nconst SandboxSettingsPage = lazy(() =>\n import('./pages/SandboxSettingsPage').then((m) => ({ default: m.SandboxSettingsPage }))\n);\nconst OrganizationsListPage = lazy(() =>\n import('./pages/OrganizationsListPage').then((m) => ({ default: m.OrganizationsListPage }))\n);\nconst WorkspacesListPage = lazy(() =>\n import('./pages/WorkspacesListPage').then((m) => ({ default: m.WorkspacesListPage }))\n);\nconst FeatureFlagsPage = lazy(() =>\n import('./pages/FeatureFlagsPage').then((m) => ({ default: m.FeatureFlagsPage }))\n);\nconst RolesPermissionsPage = lazy(() =>\n import('./pages/RolesPermissionsPage').then((m) => ({ default: m.RolesPermissionsPage }))\n);\nconst WorkflowsPage = lazy(() =>\n import('./pages/WorkflowsPage').then((m) => ({ default: m.WorkflowsPage }))\n);\nconst ProductsPage = lazy(() =>\n import('./pages/ProductsPage').then((m) => ({ default: m.ProductsPage }))\n);\nconst ContentPagesPage = lazy(() =>\n import('./pages/ContentPagesPage').then((m) => ({ default: m.ContentPagesPage }))\n);\nconst WaitlistPage = lazy(() =>\n import('./pages/WaitlistPage').then((m) => ({ default: m.WaitlistPage }))\n);\nconst ReferralsPage = lazy(() =>\n import('./pages/ReferralsPage').then((m) => ({ default: m.ReferralsPage }))\n);\n\n// Loading fallback component\nfunction PageLoader() {\n return (\n <div className=\"flex h-64 items-center justify-center\">\n <div className=\"h-8 w-8 animate-spin rounded-full border-4 border-primary border-t-transparent\" />\n </div>\n );\n}\n\nfunction FeatureDisabledFallback() {\n const { t } = useI18n();\n\n return (\n <div className=\"flex min-h-[400px] items-center justify-center p-8 text-center\">\n <div>\n <h2 className=\"mb-2 text-lg font-semibold text-foreground\">\n {tWithFallback(\n t,\n 'admin.routes.tenantFeatureDisabledTitle',\n 'Tenant management unavailable'\n )}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tWithFallback(\n t,\n 'admin.routes.tenantFeatureDisabled',\n 'This feature is currently disabled.'\n )}\n </p>\n </div>\n </div>\n );\n}\n\nfunction gated(flag: string, element: ReactNode, defaultValue: boolean): ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nfunction gatedAll(\n gates: Array<{ flag: string; defaultValue: boolean }>,\n element: ReactElement\n): ReactElement {\n return gates.reduceRight((child, gate) => gated(gate.flag, child, gate.defaultValue), element);\n}\n\n/**\n * Internal router for the Admin microfrontend.\n * This component handles routing within the microfrontend using React Router v7.\n *\n * Routes:\n * - / → Dashboard (redirect)\n * - /dashboard → Dashboard\n * - /tenants → Tenants list\n * - /tenants/new → Create new tenant\n * - /tenants/:tenantId → Tenant detail\n * - /tenants/:tenantId/edit → Edit tenant\n * - /users → Users list\n * - /users/:userId → User detail\n * - /billing → Billing overview\n * - /billing/subscriptions → Subscriptions list\n * - /billing/invoices → Invoices list\n * - /billing/plans → Plans management\n * - /support → Support overview\n * - /support/tickets → Tickets list\n * - /support/tickets/:ticketId → Ticket detail\n * - /products → Products management\n * - /communications → Communications center\n * - /sre → SRE Monitoring\n * - /audit → Audit logs\n * - /sandbox → Sandbox settings\n * - /settings → Settings\n */\nexport function AdminRoutes() {\n const { basePath } = useAdmin();\n const base = basePath || '/admin';\n\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Default redirect to dashboard */}\n <Route index element={<Navigate to={`${base}/dashboard`} replace />} />\n\n {/* Dashboard - eager loaded for fast initial render */}\n <Route path=\"dashboard\" element={<DashboardPage />} />\n\n {/* Tenants */}\n <Route\n path=\"tenants\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.list, defaultValue: true },\n ],\n <TenantsListPage />\n )}\n />\n <Route\n path=\"tenants/new\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.create, defaultValue: true },\n ],\n <TenantFormRoutePage mode=\"create\" />\n )}\n />\n <Route\n path=\"tenants/:tenantId\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.detail, defaultValue: true },\n ],\n <TenantDetailPage />\n )}\n />\n <Route\n path=\"tenants/:tenantId/edit\"\n element={gatedAll(\n [\n { flag: TENANT_FEATURE_FLAGS.management, defaultValue: true },\n { flag: TENANT_FEATURE_FLAGS.edit, defaultValue: true },\n ],\n <TenantFormRoutePage mode=\"edit\" />\n )}\n />\n\n {/* Organizations */}\n <Route path=\"organizations\" element={<OrganizationsListPage />} />\n\n {/* Workspaces */}\n <Route path=\"workspaces\" element={<WorkspacesListPage />} />\n\n {/* Users */}\n <Route path=\"users\" element={<UsersListPage />} />\n <Route path=\"users/:userId\" element={<UsersListPage />} />\n\n {/* Roles & Permissions */}\n <Route path=\"roles\" element={<RolesPermissionsPage />} />\n\n {/* Feature Flags */}\n <Route path=\"feature-flags\" element={<FeatureFlagsPage />} />\n\n {/* Workflows */}\n <Route path=\"workflows\" element={<WorkflowsPage />} />\n\n {/* Billing */}\n <Route path=\"billing\" element={<BillingPage />} />\n <Route path=\"billing/subscriptions\" element={<BillingPage />} />\n <Route path=\"billing/invoices\" element={<BillingPage />} />\n <Route path=\"billing/plans\" element={<BillingPage />} />\n\n {/* Support — redirects to shell-level /support/* route (microfe-support MFE) */}\n <Route path=\"support/*\" element={<Navigate to=\"/support\" replace />} />\n\n {/* Communications */}\n <Route path=\"communications\" element={<CommunicationsPage />} />\n\n {/* SRE Monitoring */}\n <Route path=\"sre\" element={<SREPage />} />\n\n {/* Sync Failures (cross-layer sync outbox) */}\n <Route path=\"sync-failures\" element={<SyncFailuresPage />} />\n\n {/* Audit Logs */}\n <Route path=\"audit\" element={<AuditLogsPage />} />\n <Route path=\"explore\" element={<ExploreConversationsPage />} />\n\n {/* Referrals */}\n <Route path=\"referrals\" element={<ReferralsPage />} />\n\n {/* Sandbox */}\n <Route path=\"sandbox\" element={<SandboxSettingsPage />} />\n\n {/* Products */}\n <Route path=\"products\" element={<ProductsPage />} />\n\n {/* Content (blog / case studies / changelog) */}\n <Route path=\"content\" element={<ContentPagesPage />} />\n\n {/* Waitlist */}\n <Route path=\"waitlist\" element={<WaitlistPage />} />\n\n {/* Settings */}\n <Route path=\"settings\" element={<SettingsPage />} />\n\n {/* Catch-all route - redirect to dashboard (absolute path to prevent infinite loops) */}\n <Route path=\"*\" element={<Navigate to={`${base}/dashboard`} replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useAdminPermissions.js","names":[],"sources":["../../../src/admin/hooks/useAdminPermissions.ts"],"sourcesContent":["import { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useAuth } from '@burdenoff/fe-libs/shared/providers/shell/AuthProvider';\nimport { ADMIN_PERMISSIONS } from '../constants/permissions';\n\ninterface JwtAccessClaims {\n role: string | null;\n actorType: string | null;\n}\n\nfunction decodeJwtAccessClaims(token: string | undefined): JwtAccessClaims {\n if (!token) return { role: null, actorType: null };\n const parts = token.split('.');\n if (parts.length !== 3) return { role: null, actorType: null };\n try {\n const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const normalized = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const payload = JSON.parse(atob(normalized)) as Record<string, unknown>;\n return {\n role: typeof payload.role === 'string' ? payload.role : null,\n actorType:\n typeof payload.actor_type === 'string'\n ? payload.actor_type\n : typeof payload.actorType === 'string'\n ? payload.actorType\n : null,\n };\n } catch {\n return { role: null, actorType: null };\n }\n}\n\n/**\n * Hook for admin console RBAC permissions.\n *\n * SUPER_ADMIN role / actor type (verified from profile or JWT) bypasses all\n * permission checks.\n * Other users require granular permissions from the global RBAC service.\n */\nexport function useAdminPermissions() {\n const { hasPermission, isLoading } = usePermissions();\n const auth = useAuth();\n\n const jwtClaims = decodeJwtAccessClaims(auth.tokens?.accessToken);\n const userActorType =\n typeof auth.user?.actorType === 'string'\n ? auth.user.actorType\n : typeof auth.user?.actor_type === 'string'\n ? auth.user.actor_type\n : null;\n const isSuperAdmin =\n auth.user?.role === 'SUPER_ADMIN' ||\n auth.user?.role === 'super_admin' ||\n userActorType === 'super_admin' ||\n jwtClaims.role === 'SUPER_ADMIN' ||\n jwtClaims.role === 'super_admin' ||\n jwtClaims.actorType === 'super_admin';\n\n const check = (permission: string) => isSuperAdmin || hasPermission(permission);\n\n // True if the user holds any admin-level permission (read or admin grants)\n const hasAnyAdminAccess =\n isSuperAdmin ||\n hasPermission(ADMIN_PERMISSIONS.TENANT_READ) ||\n hasPermission(ADMIN_PERMISSIONS.TENANT_ADMIN) ||\n hasPermission(ADMIN_PERMISSIONS.USER_READ) ||\n hasPermission(ADMIN_PERMISSIONS.USER_ADMIN) ||\n hasPermission(ADMIN_PERMISSIONS.BILLING_READ) ||\n hasPermission(ADMIN_PERMISSIONS.NOTIFICATION_READ) ||\n hasPermission(ADMIN_PERMISSIONS.RBAC_AUDIT_READ) ||\n hasPermission(ADMIN_PERMISSIONS.WORKSPACE_ADMIN);\n\n return {\n // Tenant management\n canViewTenants: check(ADMIN_PERMISSIONS.TENANT_READ),\n canManageTenants: check(ADMIN_PERMISSIONS.TENANT_WRITE),\n canDeleteTenants: check(ADMIN_PERMISSIONS.TENANT_DELETE),\n isTenantAdmin: check(ADMIN_PERMISSIONS.TENANT_ADMIN),\n\n // User management\n canViewUsers: check(ADMIN_PERMISSIONS.USER_READ),\n canUpdateUsers: check(ADMIN_PERMISSIONS.USER_UPDATE),\n canDeleteUsers: check(ADMIN_PERMISSIONS.USER_DELETE),\n isUserAdmin: check(ADMIN_PERMISSIONS.USER_ADMIN),\n\n // Billing\n canViewBilling: check(ADMIN_PERMISSIONS.BILLING_READ),\n\n // Communications\n canViewCommunications: check(ADMIN_PERMISSIONS.NOTIFICATION_READ),\n\n // Audit\n canViewAuditLogs: check(ADMIN_PERMISSIONS.RBAC_AUDIT_READ),\n\n // Platform settings / SRE\n canManageSettings: check(ADMIN_PERMISSIONS.TENANT_ADMIN),\n canViewSRE: check(ADMIN_PERMISSIONS.TENANT_ADMIN),\n\n // Cross-layer sync outbox monitoring (super-admin only)\n canViewSyncFailures: isSuperAdmin || hasPermission(ADMIN_PERMISSIONS.SYNC_OUTBOX_READ),\n canManageSyncFailures: isSuperAdmin || hasPermission(ADMIN_PERMISSIONS.SYNC_OUTBOX_MANAGE),\n\n // Workspace / Sandbox\n canManageSandbox: check(ADMIN_PERMISSIONS.WORKSPACE_ADMIN),\n\n // Products\n canViewProducts: hasAnyAdminAccess,\n canManageProducts: check(ADMIN_PERMISSIONS.PRODUCT_WRITE),\n\n // Waitlist\n canViewWaitlist: hasAnyAdminAccess,\n canManageWaitlist:\n isSuperAdmin ||\n hasPermission(ADMIN_PERMISSIONS.WAITLIST_MANAGE) ||\n hasPermission(ADMIN_PERMISSIONS.TENANT_ADMIN),\n\n // General pages — any admin role can access\n canViewDashboard: hasAnyAdminAccess,\n\n isLoading: isSuperAdmin ? false : isLoading,\n };\n}\n"],"mappings":";;;;AASA,SAAS,EAAsB,GAA4C;AACzE,KAAI,CAAC,EAAO,QAAO;EAAE,MAAM;EAAM,WAAW;EAAM;CAClD,IAAM,IAAQ,EAAM,MAAM,IAAI;AAC9B,KAAI,EAAM,WAAW,EAAG,QAAO;EAAE,MAAM;EAAM,WAAW;EAAM;AAC9D,KAAI;EACF,IAAM,IAAS,EAAM,GAAG,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI,EACvD,IAAa,EAAO,OAAO,KAAK,KAAK,EAAO,SAAS,EAAE,GAAG,GAAG,IAAI,EACjE,IAAU,KAAK,MAAM,KAAK,EAAW,CAAC;AAC5C,SAAO;GACL,MAAM,OAAO,EAAQ,QAAS,WAAW,EAAQ,OAAO;GACxD,WACE,OAAO,EAAQ,cAAe,WAC1B,EAAQ,aACR,OAAO,EAAQ,aAAc,WAC3B,EAAQ,YACR;GACT;SACK;AACN,SAAO;GAAE,MAAM;GAAM,WAAW;GAAM;;;AAW1C,SAAgB,IAAsB;CACpC,IAAM,EAAE,kBAAe,iBAAc,GAAgB,EAC/C,IAAO,GAAS,EAEhB,IAAY,EAAsB,EAAK,QAAQ,YAAY,EAC3D,IACJ,OAAO,EAAK,MAAM,aAAc,WAC5B,EAAK,KAAK,YACV,OAAO,EAAK,MAAM,cAAe,WAC/B,EAAK,KAAK,aACV,MACF,IACJ,EAAK,MAAM,SAAS,iBACpB,EAAK,MAAM,SAAS,iBACpB,MAAkB,iBAClB,EAAU,SAAS,iBACnB,EAAU,SAAS,iBACnB,EAAU,cAAc,eAEpB,KAAS,MAAuB,KAAgB,EAAc,EAAW,EAGzE,IACJ,KACA,EAAc,EAAkB,YAAY,IAC5C,EAAc,EAAkB,aAAa,IAC7C,EAAc,EAAkB,UAAU,IAC1C,EAAc,EAAkB,WAAW,IAC3C,EAAc,EAAkB,aAAa,IAC7C,EAAc,EAAkB,kBAAkB,IAClD,EAAc,EAAkB,gBAAgB,IAChD,EAAc,EAAkB,gBAAgB;AAElD,QAAO;EAEL,gBAAgB,EAAM,EAAkB,YAAY;EACpD,kBAAkB,EAAM,EAAkB,aAAa;EACvD,kBAAkB,EAAM,EAAkB,cAAc;EACxD,eAAe,EAAM,EAAkB,aAAa;EAGpD,cAAc,EAAM,EAAkB,UAAU;EAChD,gBAAgB,EAAM,EAAkB,YAAY;EACpD,gBAAgB,EAAM,EAAkB,YAAY;EACpD,aAAa,EAAM,EAAkB,WAAW;EAGhD,gBAAgB,EAAM,EAAkB,aAAa;EAGrD,uBAAuB,EAAM,EAAkB,kBAAkB;EAGjE,kBAAkB,EAAM,EAAkB,gBAAgB;EAG1D,mBAAmB,EAAM,EAAkB,aAAa;EACxD,YAAY,EAAM,EAAkB,aAAa;EAGjD,qBAAqB,KAAgB,EAAc,EAAkB,iBAAiB;EACtF,uBAAuB,KAAgB,EAAc,EAAkB,mBAAmB;EAG1F,kBAAkB,EAAM,EAAkB,gBAAgB;EAG1D,iBAAiB;EACjB,mBAAmB,EAAM,EAAkB,cAAc;EAGzD,iBAAiB;EACjB,mBACE,KACA,EAAc,EAAkB,gBAAgB,IAChD,EAAc,EAAkB,aAAa;EAG/C,kBAAkB;
|
|
1
|
+
{"version":3,"file":"useAdminPermissions.js","names":[],"sources":["../../../src/admin/hooks/useAdminPermissions.ts"],"sourcesContent":["import { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useAuth } from '@burdenoff/fe-libs/shared/providers/shell/AuthProvider';\nimport { ADMIN_PERMISSIONS } from '../constants/permissions';\n\ninterface JwtAccessClaims {\n role: string | null;\n actorType: string | null;\n}\n\nfunction decodeJwtAccessClaims(token: string | undefined): JwtAccessClaims {\n if (!token) return { role: null, actorType: null };\n const parts = token.split('.');\n if (parts.length !== 3) return { role: null, actorType: null };\n try {\n const base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/');\n const normalized = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');\n const payload = JSON.parse(atob(normalized)) as Record<string, unknown>;\n return {\n role: typeof payload.role === 'string' ? payload.role : null,\n actorType:\n typeof payload.actor_type === 'string'\n ? payload.actor_type\n : typeof payload.actorType === 'string'\n ? payload.actorType\n : null,\n };\n } catch {\n return { role: null, actorType: null };\n }\n}\n\n/**\n * Hook for admin console RBAC permissions.\n *\n * SUPER_ADMIN role / actor type (verified from profile or JWT) bypasses all\n * permission checks.\n * Other users require granular permissions from the global RBAC service.\n */\nexport function useAdminPermissions() {\n const { hasPermission, isLoading } = usePermissions();\n const auth = useAuth();\n\n const jwtClaims = decodeJwtAccessClaims(auth.tokens?.accessToken);\n const userActorType =\n typeof auth.user?.actorType === 'string'\n ? auth.user.actorType\n : typeof auth.user?.actor_type === 'string'\n ? auth.user.actor_type\n : null;\n const isSuperAdmin =\n auth.user?.role === 'SUPER_ADMIN' ||\n auth.user?.role === 'super_admin' ||\n userActorType === 'super_admin' ||\n jwtClaims.role === 'SUPER_ADMIN' ||\n jwtClaims.role === 'super_admin' ||\n jwtClaims.actorType === 'super_admin';\n\n const check = (permission: string) => isSuperAdmin || hasPermission(permission);\n\n // True if the user holds any admin-level permission (read or admin grants)\n const hasAnyAdminAccess =\n isSuperAdmin ||\n hasPermission(ADMIN_PERMISSIONS.TENANT_READ) ||\n hasPermission(ADMIN_PERMISSIONS.TENANT_ADMIN) ||\n hasPermission(ADMIN_PERMISSIONS.USER_READ) ||\n hasPermission(ADMIN_PERMISSIONS.USER_ADMIN) ||\n hasPermission(ADMIN_PERMISSIONS.BILLING_READ) ||\n hasPermission(ADMIN_PERMISSIONS.NOTIFICATION_READ) ||\n hasPermission(ADMIN_PERMISSIONS.RBAC_AUDIT_READ) ||\n hasPermission(ADMIN_PERMISSIONS.WORKSPACE_ADMIN);\n\n return {\n // Tenant management\n canViewTenants: check(ADMIN_PERMISSIONS.TENANT_READ),\n canManageTenants: check(ADMIN_PERMISSIONS.TENANT_WRITE),\n canDeleteTenants: check(ADMIN_PERMISSIONS.TENANT_DELETE),\n isTenantAdmin: check(ADMIN_PERMISSIONS.TENANT_ADMIN),\n\n // User management\n canViewUsers: check(ADMIN_PERMISSIONS.USER_READ),\n canUpdateUsers: check(ADMIN_PERMISSIONS.USER_UPDATE),\n canDeleteUsers: check(ADMIN_PERMISSIONS.USER_DELETE),\n isUserAdmin: check(ADMIN_PERMISSIONS.USER_ADMIN),\n\n // Billing\n canViewBilling: check(ADMIN_PERMISSIONS.BILLING_READ),\n\n // Communications\n canViewCommunications: check(ADMIN_PERMISSIONS.NOTIFICATION_READ),\n\n // Audit\n canViewAuditLogs: check(ADMIN_PERMISSIONS.RBAC_AUDIT_READ),\n\n // Platform settings / SRE\n canManageSettings: check(ADMIN_PERMISSIONS.TENANT_ADMIN),\n canViewSRE: check(ADMIN_PERMISSIONS.TENANT_ADMIN),\n\n // Cross-layer sync outbox monitoring (super-admin only)\n canViewSyncFailures: isSuperAdmin || hasPermission(ADMIN_PERMISSIONS.SYNC_OUTBOX_READ),\n canManageSyncFailures: isSuperAdmin || hasPermission(ADMIN_PERMISSIONS.SYNC_OUTBOX_MANAGE),\n\n // Workspace / Sandbox\n canManageSandbox: check(ADMIN_PERMISSIONS.WORKSPACE_ADMIN),\n\n // Products\n canViewProducts: hasAnyAdminAccess,\n canManageProducts: check(ADMIN_PERMISSIONS.PRODUCT_WRITE),\n\n // Waitlist\n canViewWaitlist: hasAnyAdminAccess,\n canManageWaitlist:\n isSuperAdmin ||\n hasPermission(ADMIN_PERMISSIONS.WAITLIST_MANAGE) ||\n hasPermission(ADMIN_PERMISSIONS.TENANT_ADMIN),\n\n // General pages — any admin role can access\n canViewDashboard: hasAnyAdminAccess,\n\n /**\n * Explore conversations: superadmin only, matching the\n * `@rbac(actorTypes: [\"super_admin\"])` on the backing fields. Exposed as its own flag\n * rather than reusing a permission, because the backend gate is actor-type based and a\n * permission grant would not open it — the two must agree or the page renders and then\n * every query comes back FORBIDDEN.\n */\n canViewExploreConversations: isSuperAdmin,\n\n isLoading: isSuperAdmin ? false : isLoading,\n };\n}\n"],"mappings":";;;;AASA,SAAS,EAAsB,GAA4C;AACzE,KAAI,CAAC,EAAO,QAAO;EAAE,MAAM;EAAM,WAAW;EAAM;CAClD,IAAM,IAAQ,EAAM,MAAM,IAAI;AAC9B,KAAI,EAAM,WAAW,EAAG,QAAO;EAAE,MAAM;EAAM,WAAW;EAAM;AAC9D,KAAI;EACF,IAAM,IAAS,EAAM,GAAG,QAAQ,MAAM,IAAI,CAAC,QAAQ,MAAM,IAAI,EACvD,IAAa,EAAO,OAAO,KAAK,KAAK,EAAO,SAAS,EAAE,GAAG,GAAG,IAAI,EACjE,IAAU,KAAK,MAAM,KAAK,EAAW,CAAC;AAC5C,SAAO;GACL,MAAM,OAAO,EAAQ,QAAS,WAAW,EAAQ,OAAO;GACxD,WACE,OAAO,EAAQ,cAAe,WAC1B,EAAQ,aACR,OAAO,EAAQ,aAAc,WAC3B,EAAQ,YACR;GACT;SACK;AACN,SAAO;GAAE,MAAM;GAAM,WAAW;GAAM;;;AAW1C,SAAgB,IAAsB;CACpC,IAAM,EAAE,kBAAe,iBAAc,GAAgB,EAC/C,IAAO,GAAS,EAEhB,IAAY,EAAsB,EAAK,QAAQ,YAAY,EAC3D,IACJ,OAAO,EAAK,MAAM,aAAc,WAC5B,EAAK,KAAK,YACV,OAAO,EAAK,MAAM,cAAe,WAC/B,EAAK,KAAK,aACV,MACF,IACJ,EAAK,MAAM,SAAS,iBACpB,EAAK,MAAM,SAAS,iBACpB,MAAkB,iBAClB,EAAU,SAAS,iBACnB,EAAU,SAAS,iBACnB,EAAU,cAAc,eAEpB,KAAS,MAAuB,KAAgB,EAAc,EAAW,EAGzE,IACJ,KACA,EAAc,EAAkB,YAAY,IAC5C,EAAc,EAAkB,aAAa,IAC7C,EAAc,EAAkB,UAAU,IAC1C,EAAc,EAAkB,WAAW,IAC3C,EAAc,EAAkB,aAAa,IAC7C,EAAc,EAAkB,kBAAkB,IAClD,EAAc,EAAkB,gBAAgB,IAChD,EAAc,EAAkB,gBAAgB;AAElD,QAAO;EAEL,gBAAgB,EAAM,EAAkB,YAAY;EACpD,kBAAkB,EAAM,EAAkB,aAAa;EACvD,kBAAkB,EAAM,EAAkB,cAAc;EACxD,eAAe,EAAM,EAAkB,aAAa;EAGpD,cAAc,EAAM,EAAkB,UAAU;EAChD,gBAAgB,EAAM,EAAkB,YAAY;EACpD,gBAAgB,EAAM,EAAkB,YAAY;EACpD,aAAa,EAAM,EAAkB,WAAW;EAGhD,gBAAgB,EAAM,EAAkB,aAAa;EAGrD,uBAAuB,EAAM,EAAkB,kBAAkB;EAGjE,kBAAkB,EAAM,EAAkB,gBAAgB;EAG1D,mBAAmB,EAAM,EAAkB,aAAa;EACxD,YAAY,EAAM,EAAkB,aAAa;EAGjD,qBAAqB,KAAgB,EAAc,EAAkB,iBAAiB;EACtF,uBAAuB,KAAgB,EAAc,EAAkB,mBAAmB;EAG1F,kBAAkB,EAAM,EAAkB,gBAAgB;EAG1D,iBAAiB;EACjB,mBAAmB,EAAM,EAAkB,cAAc;EAGzD,iBAAiB;EACjB,mBACE,KACA,EAAc,EAAkB,gBAAgB,IAChD,EAAc,EAAkB,aAAa;EAG/C,kBAAkB;EASlB,6BAA6B;EAE7B,WAAW,IAAe,KAAQ;EACnC"}
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { useAdminPermissions as e } from "../hooks/useAdminPermissions.js";
|
|
2
|
+
import { Badge as t, EmptyState as n, Input as r } from "../components/ui.js";
|
|
3
|
+
import { PageLayout as ee } from "../components/PageLayout.js";
|
|
4
|
+
import { formatNumber as i, formatRelativeTime as a } from "../utils/formatters.js";
|
|
5
|
+
import { DataTable as o, Pagination as te } from "../components/DataTable.js";
|
|
6
|
+
import { PermissionGuard as s } from "../components/PermissionGuard.js";
|
|
7
|
+
import "../components/index.js";
|
|
8
|
+
import { useCallback as c, useMemo as l, useState as u } from "react";
|
|
9
|
+
import { MessageSquare as d, MousePointerClick as f, TriangleAlert as p } from "lucide-react";
|
|
10
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
11
|
+
import { EmphasisPanel as ne, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
12
|
+
import { gql as _ } from "@apollo/client";
|
|
13
|
+
import { useQuery as v } from "@apollo/client/react";
|
|
14
|
+
//#region src/admin/pages/ExploreConversationsPage.tsx
|
|
15
|
+
var y = 25, b = _`
|
|
16
|
+
query ExploreAdminStats($filter: ExploreAdminFilter) {
|
|
17
|
+
exploreAdminStats(filter: $filter) {
|
|
18
|
+
conversations
|
|
19
|
+
messages
|
|
20
|
+
unanswered
|
|
21
|
+
clicks
|
|
22
|
+
byProduct {
|
|
23
|
+
product
|
|
24
|
+
answerCount
|
|
25
|
+
clickCount
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
`, x = _`
|
|
30
|
+
query ExploreAdminConversations(
|
|
31
|
+
$filter: ExploreAdminFilter
|
|
32
|
+
$sort: ExploreAdminSort
|
|
33
|
+
$limit: Int
|
|
34
|
+
$offset: Int
|
|
35
|
+
) {
|
|
36
|
+
exploreAdminConversations(filter: $filter, sort: $sort, limit: $limit, offset: $offset) {
|
|
37
|
+
totalCount
|
|
38
|
+
hasMore
|
|
39
|
+
items {
|
|
40
|
+
id
|
|
41
|
+
productSlug
|
|
42
|
+
ipHash
|
|
43
|
+
locale
|
|
44
|
+
turnCount
|
|
45
|
+
clickCount
|
|
46
|
+
hasUnanswered
|
|
47
|
+
createdAt
|
|
48
|
+
lastMessageAt
|
|
49
|
+
messages {
|
|
50
|
+
id
|
|
51
|
+
role
|
|
52
|
+
status
|
|
53
|
+
content
|
|
54
|
+
answered
|
|
55
|
+
createdAt
|
|
56
|
+
references {
|
|
57
|
+
url
|
|
58
|
+
title
|
|
59
|
+
product
|
|
60
|
+
}
|
|
61
|
+
ctas {
|
|
62
|
+
kind
|
|
63
|
+
label
|
|
64
|
+
url
|
|
65
|
+
}
|
|
66
|
+
clicks {
|
|
67
|
+
id
|
|
68
|
+
kind
|
|
69
|
+
url
|
|
70
|
+
product
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
`;
|
|
77
|
+
function S(e, t = !1) {
|
|
78
|
+
if (e) return (/* @__PURE__ */ new Date(`${e}T${t ? "23:59:59" : "00:00:00"}`)).toISOString();
|
|
79
|
+
}
|
|
80
|
+
function C() {
|
|
81
|
+
let { canViewExploreConversations: _, isLoading: C } = e(), [w, T] = u(1), [E, D] = u(""), [O, k] = u(""), [A, j] = u(""), [M, N] = u(""), [P, F] = u(""), [I, L] = u(!1), [R, z] = u(!1), [B, V] = u("NEWEST"), [H, U] = u(null), W = l(() => ({
|
|
82
|
+
productSlug: E || void 0,
|
|
83
|
+
ipHash: O || void 0,
|
|
84
|
+
from: S(A),
|
|
85
|
+
to: S(M, !0),
|
|
86
|
+
search: P || void 0,
|
|
87
|
+
unansweredOnly: I || void 0,
|
|
88
|
+
convertedOnly: R || void 0
|
|
89
|
+
}), [
|
|
90
|
+
E,
|
|
91
|
+
O,
|
|
92
|
+
A,
|
|
93
|
+
M,
|
|
94
|
+
P,
|
|
95
|
+
I,
|
|
96
|
+
R
|
|
97
|
+
]), G = C || !_, { data: K } = v(b, {
|
|
98
|
+
variables: { filter: W },
|
|
99
|
+
fetchPolicy: "cache-and-network",
|
|
100
|
+
skip: G
|
|
101
|
+
}), { data: q, loading: J, error: Y } = v(x, {
|
|
102
|
+
variables: {
|
|
103
|
+
filter: W,
|
|
104
|
+
sort: B,
|
|
105
|
+
limit: y,
|
|
106
|
+
offset: (w - 1) * y
|
|
107
|
+
},
|
|
108
|
+
fetchPolicy: "cache-and-network",
|
|
109
|
+
skip: G
|
|
110
|
+
}), X = K?.exploreAdminStats, Z = q?.exploreAdminConversations?.items ?? [], Q = q?.exploreAdminConversations?.totalCount ?? 0, re = Math.ceil(Q / y), $ = c((e) => (t) => {
|
|
111
|
+
e(t), T(1);
|
|
112
|
+
}, []), ie = l(() => [
|
|
113
|
+
{
|
|
114
|
+
key: "product",
|
|
115
|
+
header: "Product",
|
|
116
|
+
width: "140px",
|
|
117
|
+
render: (e) => /* @__PURE__ */ m("span", {
|
|
118
|
+
className: "text-sm font-medium",
|
|
119
|
+
children: e.productSlug ?? "—"
|
|
120
|
+
})
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
key: "question",
|
|
124
|
+
header: "First question",
|
|
125
|
+
render: (e) => /* @__PURE__ */ m("span", {
|
|
126
|
+
className: "line-clamp-2 text-sm text-text-secondary",
|
|
127
|
+
children: e.messages.find((e) => e.role === "USER")?.content ?? "—"
|
|
128
|
+
})
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
key: "turns",
|
|
132
|
+
header: "Turns",
|
|
133
|
+
width: "80px",
|
|
134
|
+
align: "right",
|
|
135
|
+
render: (e) => i(e.turnCount)
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
key: "clicks",
|
|
139
|
+
header: "Clicks",
|
|
140
|
+
width: "90px",
|
|
141
|
+
align: "right",
|
|
142
|
+
render: (e) => e.clickCount > 0 ? /* @__PURE__ */ m(t, {
|
|
143
|
+
variant: "success",
|
|
144
|
+
children: e.clickCount
|
|
145
|
+
}) : /* @__PURE__ */ m("span", {
|
|
146
|
+
className: "text-text-tertiary",
|
|
147
|
+
children: "0"
|
|
148
|
+
})
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
key: "answered",
|
|
152
|
+
header: "Gaps",
|
|
153
|
+
width: "90px",
|
|
154
|
+
render: (e) => e.hasUnanswered ? /* @__PURE__ */ m(t, {
|
|
155
|
+
variant: "warning",
|
|
156
|
+
children: "unanswered"
|
|
157
|
+
}) : null
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
key: "ip",
|
|
161
|
+
header: "Visitor",
|
|
162
|
+
width: "110px",
|
|
163
|
+
render: (e) => /* @__PURE__ */ m("button", {
|
|
164
|
+
type: "button",
|
|
165
|
+
title: "Filter to this visitor's sessions",
|
|
166
|
+
className: "font-mono text-xs text-text-tertiary underline-offset-2 hover:underline",
|
|
167
|
+
onClick: (t) => {
|
|
168
|
+
t.stopPropagation(), k(e.ipHash), T(1);
|
|
169
|
+
},
|
|
170
|
+
children: e.ipHash.slice(0, 8)
|
|
171
|
+
})
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
key: "when",
|
|
175
|
+
header: "Started",
|
|
176
|
+
width: "130px",
|
|
177
|
+
render: (e) => /* @__PURE__ */ m("span", {
|
|
178
|
+
className: "text-sm text-text-tertiary",
|
|
179
|
+
children: a(e.createdAt)
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
], []);
|
|
183
|
+
return /* @__PURE__ */ m(s, {
|
|
184
|
+
allowed: _,
|
|
185
|
+
isLoading: C,
|
|
186
|
+
pageTitle: "Explore Conversations",
|
|
187
|
+
denyMessage: "Explore conversations are restricted to super admins.",
|
|
188
|
+
children: /* @__PURE__ */ h(ee, {
|
|
189
|
+
title: "Explore Conversations",
|
|
190
|
+
gradientWord: "Conversations",
|
|
191
|
+
description: "Every question asked of the public /explore assistant, what it answered, and where it sent people.",
|
|
192
|
+
children: [
|
|
193
|
+
/* @__PURE__ */ m(g, { children: "Visitors are anonymous. The only handle here is a salted hash of their IP, which groups one visitor's sessions without exposing an address." }),
|
|
194
|
+
X ? /* @__PURE__ */ m(ne, { children: /* @__PURE__ */ h("div", {
|
|
195
|
+
className: "grid grid-cols-2 gap-4 sm:grid-cols-4",
|
|
196
|
+
children: [
|
|
197
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
198
|
+
className: "text-2xl font-semibold",
|
|
199
|
+
children: i(X.conversations)
|
|
200
|
+
}), /* @__PURE__ */ m("p", {
|
|
201
|
+
className: "text-xs text-text-tertiary",
|
|
202
|
+
children: "Conversations"
|
|
203
|
+
})] }),
|
|
204
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
205
|
+
className: "text-2xl font-semibold",
|
|
206
|
+
children: i(X.messages)
|
|
207
|
+
}), /* @__PURE__ */ m("p", {
|
|
208
|
+
className: "text-xs text-text-tertiary",
|
|
209
|
+
children: "Messages"
|
|
210
|
+
})] }),
|
|
211
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
212
|
+
className: "text-2xl font-semibold",
|
|
213
|
+
children: i(X.clicks)
|
|
214
|
+
}), /* @__PURE__ */ m("p", {
|
|
215
|
+
className: "text-xs text-text-tertiary",
|
|
216
|
+
children: "Links followed"
|
|
217
|
+
})] }),
|
|
218
|
+
/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
|
|
219
|
+
className: "text-2xl font-semibold",
|
|
220
|
+
children: i(X.unanswered)
|
|
221
|
+
}), /* @__PURE__ */ m("p", {
|
|
222
|
+
className: "text-xs text-text-tertiary",
|
|
223
|
+
children: "Couldn't answer"
|
|
224
|
+
})] })
|
|
225
|
+
]
|
|
226
|
+
}) }) : null,
|
|
227
|
+
X && X.byProduct.length > 0 ? /* @__PURE__ */ h("div", {
|
|
228
|
+
className: "rounded-xl border border-border-seam p-4",
|
|
229
|
+
children: [/* @__PURE__ */ m("p", {
|
|
230
|
+
className: "mb-3 text-sm font-semibold",
|
|
231
|
+
children: "Where the assistant sends people"
|
|
232
|
+
}), /* @__PURE__ */ m("div", {
|
|
233
|
+
className: "space-y-1.5",
|
|
234
|
+
children: X.byProduct.slice(0, 12).map((e) => /* @__PURE__ */ h("div", {
|
|
235
|
+
className: "flex items-center gap-3 text-sm",
|
|
236
|
+
children: [
|
|
237
|
+
/* @__PURE__ */ m("span", {
|
|
238
|
+
className: "w-40 shrink-0 truncate",
|
|
239
|
+
children: e.product
|
|
240
|
+
}),
|
|
241
|
+
/* @__PURE__ */ h("span", {
|
|
242
|
+
className: "text-text-tertiary",
|
|
243
|
+
children: [i(e.answerCount), " cited"]
|
|
244
|
+
}),
|
|
245
|
+
/* @__PURE__ */ m(f, { className: "h-3 w-3 text-text-tertiary" }),
|
|
246
|
+
/* @__PURE__ */ h("span", {
|
|
247
|
+
className: "text-text-tertiary",
|
|
248
|
+
children: [i(e.clickCount), " followed"]
|
|
249
|
+
}),
|
|
250
|
+
e.answerCount > 0 && e.clickCount === 0 ? /* @__PURE__ */ m(t, {
|
|
251
|
+
variant: "warning",
|
|
252
|
+
children: "no conversions"
|
|
253
|
+
}) : null
|
|
254
|
+
]
|
|
255
|
+
}, e.product))
|
|
256
|
+
})]
|
|
257
|
+
}) : null,
|
|
258
|
+
/* @__PURE__ */ h("div", {
|
|
259
|
+
className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-4",
|
|
260
|
+
children: [
|
|
261
|
+
/* @__PURE__ */ m(r, {
|
|
262
|
+
placeholder: "Search message text…",
|
|
263
|
+
value: P,
|
|
264
|
+
onChange: (e) => $(F)(e.target.value)
|
|
265
|
+
}),
|
|
266
|
+
/* @__PURE__ */ m(r, {
|
|
267
|
+
placeholder: "Product slug (e.g. vibecontrols)",
|
|
268
|
+
value: E,
|
|
269
|
+
onChange: (e) => $(D)(e.target.value)
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ m(r, {
|
|
272
|
+
type: "date",
|
|
273
|
+
"aria-label": "From date",
|
|
274
|
+
value: A,
|
|
275
|
+
onChange: (e) => $(j)(e.target.value)
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ m(r, {
|
|
278
|
+
type: "date",
|
|
279
|
+
"aria-label": "To date",
|
|
280
|
+
value: M,
|
|
281
|
+
onChange: (e) => $(N)(e.target.value)
|
|
282
|
+
})
|
|
283
|
+
]
|
|
284
|
+
}),
|
|
285
|
+
/* @__PURE__ */ h("div", {
|
|
286
|
+
className: "flex flex-wrap items-center gap-3 text-sm",
|
|
287
|
+
children: [
|
|
288
|
+
/* @__PURE__ */ h("label", {
|
|
289
|
+
className: "flex items-center gap-1.5",
|
|
290
|
+
children: [
|
|
291
|
+
/* @__PURE__ */ m("input", {
|
|
292
|
+
type: "checkbox",
|
|
293
|
+
checked: I,
|
|
294
|
+
onChange: (e) => $(L)(e.target.checked)
|
|
295
|
+
}),
|
|
296
|
+
/* @__PURE__ */ m(p, { className: "h-3.5 w-3.5" }),
|
|
297
|
+
"Only unanswered"
|
|
298
|
+
]
|
|
299
|
+
}),
|
|
300
|
+
/* @__PURE__ */ h("label", {
|
|
301
|
+
className: "flex items-center gap-1.5",
|
|
302
|
+
children: [
|
|
303
|
+
/* @__PURE__ */ m("input", {
|
|
304
|
+
type: "checkbox",
|
|
305
|
+
checked: R,
|
|
306
|
+
onChange: (e) => $(z)(e.target.checked)
|
|
307
|
+
}),
|
|
308
|
+
/* @__PURE__ */ m(f, { className: "h-3.5 w-3.5" }),
|
|
309
|
+
"Only converted"
|
|
310
|
+
]
|
|
311
|
+
}),
|
|
312
|
+
/* @__PURE__ */ h("label", {
|
|
313
|
+
className: "flex items-center gap-1.5",
|
|
314
|
+
children: ["Sort", /* @__PURE__ */ h("select", {
|
|
315
|
+
className: "rounded-md border border-border-seam bg-bg-surface px-2 py-1",
|
|
316
|
+
value: B,
|
|
317
|
+
onChange: (e) => $(V)(e.target.value),
|
|
318
|
+
children: [
|
|
319
|
+
/* @__PURE__ */ m("option", {
|
|
320
|
+
value: "NEWEST",
|
|
321
|
+
children: "Newest"
|
|
322
|
+
}),
|
|
323
|
+
/* @__PURE__ */ m("option", {
|
|
324
|
+
value: "OLDEST",
|
|
325
|
+
children: "Oldest"
|
|
326
|
+
}),
|
|
327
|
+
/* @__PURE__ */ m("option", {
|
|
328
|
+
value: "MOST_TURNS",
|
|
329
|
+
children: "Most turns"
|
|
330
|
+
}),
|
|
331
|
+
/* @__PURE__ */ m("option", {
|
|
332
|
+
value: "MOST_CLICKS",
|
|
333
|
+
children: "Most clicks"
|
|
334
|
+
})
|
|
335
|
+
]
|
|
336
|
+
})]
|
|
337
|
+
}),
|
|
338
|
+
O ? /* @__PURE__ */ h("button", {
|
|
339
|
+
type: "button",
|
|
340
|
+
className: "underline underline-offset-2",
|
|
341
|
+
onClick: () => $(k)(""),
|
|
342
|
+
children: [
|
|
343
|
+
"Clear visitor filter (",
|
|
344
|
+
O.slice(0, 8),
|
|
345
|
+
")"
|
|
346
|
+
]
|
|
347
|
+
}) : null
|
|
348
|
+
]
|
|
349
|
+
}),
|
|
350
|
+
Y ? /* @__PURE__ */ m("div", {
|
|
351
|
+
className: "rounded-lg border border-status-error-border bg-status-error-bg p-3 text-sm text-status-error-text",
|
|
352
|
+
children: Y.message
|
|
353
|
+
}) : null,
|
|
354
|
+
/* @__PURE__ */ m(o, {
|
|
355
|
+
columns: ie,
|
|
356
|
+
data: Z,
|
|
357
|
+
keyExtractor: (e) => e.id,
|
|
358
|
+
loading: J,
|
|
359
|
+
onRowClick: (e) => U((t) => t === e.id ? null : e.id),
|
|
360
|
+
emptyState: /* @__PURE__ */ m(n, {
|
|
361
|
+
icon: /* @__PURE__ */ m(d, { className: "h-8 w-8" }),
|
|
362
|
+
title: "No conversations",
|
|
363
|
+
description: "Nothing matches these filters yet."
|
|
364
|
+
})
|
|
365
|
+
}),
|
|
366
|
+
H ? /* @__PURE__ */ h("div", {
|
|
367
|
+
className: "rounded-xl border border-border-seam p-4",
|
|
368
|
+
children: [/* @__PURE__ */ m("p", {
|
|
369
|
+
className: "mb-3 text-sm font-semibold",
|
|
370
|
+
children: "Transcript"
|
|
371
|
+
}), /* @__PURE__ */ m("div", {
|
|
372
|
+
className: "space-y-3",
|
|
373
|
+
children: (Z.find((e) => e.id === H)?.messages ?? []).map((e) => /* @__PURE__ */ h("div", {
|
|
374
|
+
className: "text-sm",
|
|
375
|
+
children: [
|
|
376
|
+
/* @__PURE__ */ h("div", {
|
|
377
|
+
className: "flex items-center gap-2",
|
|
378
|
+
children: [
|
|
379
|
+
/* @__PURE__ */ m(t, {
|
|
380
|
+
variant: e.role === "USER" ? "default" : "secondary",
|
|
381
|
+
children: e.role
|
|
382
|
+
}),
|
|
383
|
+
e.role === "ASSISTANT" && !e.answered ? /* @__PURE__ */ m(t, {
|
|
384
|
+
variant: "warning",
|
|
385
|
+
children: "couldn't answer"
|
|
386
|
+
}) : null,
|
|
387
|
+
/* @__PURE__ */ m("span", {
|
|
388
|
+
className: "text-xs text-text-tertiary",
|
|
389
|
+
children: a(e.createdAt)
|
|
390
|
+
})
|
|
391
|
+
]
|
|
392
|
+
}),
|
|
393
|
+
/* @__PURE__ */ m("p", {
|
|
394
|
+
className: "mt-1 whitespace-pre-wrap break-words text-text-secondary",
|
|
395
|
+
children: e.content
|
|
396
|
+
}),
|
|
397
|
+
e.clicks.length > 0 ? /* @__PURE__ */ h("p", {
|
|
398
|
+
className: "mt-1 text-xs text-status-success-text",
|
|
399
|
+
children: ["Followed: ", e.clicks.map((e) => e.url).join(", ")]
|
|
400
|
+
}) : null
|
|
401
|
+
]
|
|
402
|
+
}, e.id))
|
|
403
|
+
})]
|
|
404
|
+
}) : null,
|
|
405
|
+
Q > y ? /* @__PURE__ */ m(te, {
|
|
406
|
+
page: w,
|
|
407
|
+
totalPages: re,
|
|
408
|
+
totalItems: Q,
|
|
409
|
+
pageSize: y,
|
|
410
|
+
onPageChange: T
|
|
411
|
+
}) : null
|
|
412
|
+
]
|
|
413
|
+
})
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
//#endregion
|
|
417
|
+
export { C as ExploreConversationsPage };
|
|
418
|
+
|
|
419
|
+
//# sourceMappingURL=ExploreConversationsPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExploreConversationsPage.js","names":[],"sources":["../../../src/admin/pages/ExploreConversationsPage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { MessageSquare, MousePointerClick, TriangleAlert } from 'lucide-react';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\n\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n EmptyState,\n Badge,\n PermissionGuard,\n} from '../components';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { formatRelativeTime, formatNumber } from '../utils/formatters';\n\nconst PAGE_SIZE = 25;\n\n/**\n * Operations are declared inline rather than in `src/operations/global/*.graphql`.\n *\n * Codegen runs in CI against the COMMITTED `graphql/global.graphql` snapshot, and that\n * snapshot can only be refreshed by `schema:pull` against a local gateway with the new\n * subgraph composed. Putting these in an operations file would therefore fail CI codegen\n * until the schema has propagated to the dev Hive target. Inline documents are not scanned\n * by codegen, so the page builds today and can be migrated to generated hooks — with no\n * change to the queries themselves — once the snapshot catches up.\n */\nconst EXPLORE_ADMIN_STATS = gql`\n query ExploreAdminStats($filter: ExploreAdminFilter) {\n exploreAdminStats(filter: $filter) {\n conversations\n messages\n unanswered\n clicks\n byProduct {\n product\n answerCount\n clickCount\n }\n }\n }\n`;\n\nconst EXPLORE_ADMIN_CONVERSATIONS = gql`\n query ExploreAdminConversations(\n $filter: ExploreAdminFilter\n $sort: ExploreAdminSort\n $limit: Int\n $offset: Int\n ) {\n exploreAdminConversations(filter: $filter, sort: $sort, limit: $limit, offset: $offset) {\n totalCount\n hasMore\n items {\n id\n productSlug\n ipHash\n locale\n turnCount\n clickCount\n hasUnanswered\n createdAt\n lastMessageAt\n messages {\n id\n role\n status\n content\n answered\n createdAt\n references {\n url\n title\n product\n }\n ctas {\n kind\n label\n url\n }\n clicks {\n id\n kind\n url\n product\n }\n }\n }\n }\n }\n`;\n\ntype SortOption = 'NEWEST' | 'OLDEST' | 'MOST_TURNS' | 'MOST_CLICKS';\n\ninterface AdminClick {\n id: string;\n kind: string;\n url: string;\n product?: string | null;\n}\n\ninterface AdminMessage {\n id: string;\n role: string;\n status: string;\n content: string;\n answered: boolean;\n createdAt: string;\n references: Array<{ url: string; title?: string | null; product?: string | null }>;\n ctas: Array<{ kind: string; label: string; url: string }>;\n clicks: AdminClick[];\n}\n\ninterface AdminProductStat {\n product: string;\n answerCount: number;\n clickCount: number;\n}\n\ninterface AdminStats {\n conversations: number;\n messages: number;\n unanswered: number;\n clicks: number;\n byProduct: AdminProductStat[];\n}\n\ninterface AdminConversation {\n id: string;\n productSlug?: string | null;\n ipHash: string;\n locale?: string | null;\n turnCount: number;\n clickCount: number;\n hasUnanswered: boolean;\n createdAt: string;\n lastMessageAt?: string | null;\n messages: AdminMessage[];\n}\n\n/**\n * Result shapes for the inline documents above.\n *\n * Hand-written because these operations are deliberately not run through codegen (see the\n * note on the documents). They mirror the schema exactly; when the snapshot catches up and\n * the queries move to generated hooks, these go away.\n */\ninterface StatsResult {\n exploreAdminStats: AdminStats;\n}\n\ninterface ConversationsResult {\n exploreAdminConversations: {\n totalCount: number;\n hasMore: boolean;\n items: AdminConversation[];\n };\n}\n\n/** `datetime-local`-free: the fleet has no date picker, so two native date inputs it is. */\nfunction toDate(value: string, endOfDay = false): string | undefined {\n if (!value) return undefined;\n return new Date(`${value}T${endOfDay ? '23:59:59' : '00:00:00'}`).toISOString();\n}\n\n/**\n * Explore conversations — the superadmin view of the public `/explore` AI chat.\n *\n * Answers three questions the product could not answer before: what are visitors asking,\n * which products is the assistant routing them to, and which questions are we failing to\n * answer at all.\n *\n * Visitors are anonymous and stay that way. The only handle shown is the salted `ipHash`\n * already stored server-side, which groups one visitor's sessions without this console ever\n * handling an IP address.\n */\nexport function ExploreConversationsPage() {\n const { canViewExploreConversations, isLoading: permissionsLoading } = useAdminPermissions();\n\n const [page, setPage] = useState(1);\n const [productSlug, setProductSlug] = useState('');\n const [ipHash, setIpHash] = useState('');\n const [from, setFrom] = useState('');\n const [to, setTo] = useState('');\n const [search, setSearch] = useState('');\n const [unansweredOnly, setUnansweredOnly] = useState(false);\n const [convertedOnly, setConvertedOnly] = useState(false);\n const [sort, setSort] = useState<SortOption>('NEWEST');\n const [expanded, setExpanded] = useState<string | null>(null);\n\n const filter = useMemo(\n () => ({\n productSlug: productSlug || undefined,\n ipHash: ipHash || undefined,\n from: toDate(from),\n to: toDate(to, true),\n search: search || undefined,\n unansweredOnly: unansweredOnly || undefined,\n convertedOnly: convertedOnly || undefined,\n }),\n [productSlug, ipHash, from, to, search, unansweredOnly, convertedOnly]\n );\n\n // The whole app sits behind AdminGuard, which is an @burdenoff.com / ADMIN gate rather\n // than a super_admin one — and it is client-side. The real boundary is the\n // `@rbac(actorTypes: [\"super_admin\"])` on these fields; this check only avoids showing a\n // page that would come back FORBIDDEN.\n const skip = permissionsLoading || !canViewExploreConversations;\n\n const { data: statsData } = useQuery<StatsResult>(EXPLORE_ADMIN_STATS, {\n variables: { filter },\n fetchPolicy: 'cache-and-network',\n skip,\n });\n\n const { data, loading, error } = useQuery<ConversationsResult>(EXPLORE_ADMIN_CONVERSATIONS, {\n variables: { filter, sort, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE },\n fetchPolicy: 'cache-and-network',\n skip,\n });\n\n const stats = statsData?.exploreAdminStats;\n const conversations: AdminConversation[] = data?.exploreAdminConversations?.items ?? [];\n const total: number = data?.exploreAdminConversations?.totalCount ?? 0;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n\n const resetToFirstPage = useCallback(<T,>(setter: (value: T) => void) => {\n return (value: T) => {\n setter(value);\n setPage(1);\n };\n }, []);\n\n const columns = useMemo(\n () => [\n {\n key: 'product',\n header: 'Product',\n width: '140px',\n render: (row: AdminConversation) => (\n <span className=\"text-sm font-medium\">{row.productSlug ?? '—'}</span>\n ),\n },\n {\n key: 'question',\n header: 'First question',\n render: (row: AdminConversation) => {\n const first = row.messages.find((message) => message.role === 'USER');\n return (\n <span className=\"line-clamp-2 text-sm text-text-secondary\">\n {first?.content ?? '—'}\n </span>\n );\n },\n },\n {\n key: 'turns',\n header: 'Turns',\n width: '80px',\n align: 'right' as const,\n render: (row: AdminConversation) => formatNumber(row.turnCount),\n },\n {\n key: 'clicks',\n header: 'Clicks',\n width: '90px',\n align: 'right' as const,\n render: (row: AdminConversation) =>\n row.clickCount > 0 ? (\n <Badge variant=\"success\">{row.clickCount}</Badge>\n ) : (\n <span className=\"text-text-tertiary\">0</span>\n ),\n },\n {\n key: 'answered',\n header: 'Gaps',\n width: '90px',\n render: (row: AdminConversation) =>\n row.hasUnanswered ? <Badge variant=\"warning\">unanswered</Badge> : null,\n },\n {\n key: 'ip',\n header: 'Visitor',\n width: '110px',\n render: (row: AdminConversation) => (\n <button\n type=\"button\"\n title=\"Filter to this visitor's sessions\"\n className=\"font-mono text-xs text-text-tertiary underline-offset-2 hover:underline\"\n onClick={(event) => {\n event.stopPropagation();\n setIpHash(row.ipHash);\n setPage(1);\n }}\n >\n {row.ipHash.slice(0, 8)}\n </button>\n ),\n },\n {\n key: 'when',\n header: 'Started',\n width: '130px',\n render: (row: AdminConversation) => (\n <span className=\"text-sm text-text-tertiary\">{formatRelativeTime(row.createdAt)}</span>\n ),\n },\n ],\n []\n );\n\n return (\n <PermissionGuard\n allowed={canViewExploreConversations}\n isLoading={permissionsLoading}\n pageTitle=\"Explore Conversations\"\n denyMessage=\"Explore conversations are restricted to super admins.\"\n >\n <PageLayout\n title=\"Explore Conversations\"\n gradientWord=\"Conversations\"\n description=\"Every question asked of the public /explore assistant, what it answered, and where it sent people.\"\n >\n <PagePurpose>\n Visitors are anonymous. The only handle here is a salted hash of their IP, which groups\n one visitor's sessions without exposing an address.\n </PagePurpose>\n\n {stats ? (\n <EmphasisPanel>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <div>\n <p className=\"text-2xl font-semibold\">{formatNumber(stats.conversations)}</p>\n <p className=\"text-xs text-text-tertiary\">Conversations</p>\n </div>\n <div>\n <p className=\"text-2xl font-semibold\">{formatNumber(stats.messages)}</p>\n <p className=\"text-xs text-text-tertiary\">Messages</p>\n </div>\n <div>\n <p className=\"text-2xl font-semibold\">{formatNumber(stats.clicks)}</p>\n <p className=\"text-xs text-text-tertiary\">Links followed</p>\n </div>\n <div>\n <p className=\"text-2xl font-semibold\">{formatNumber(stats.unanswered)}</p>\n <p className=\"text-xs text-text-tertiary\">Couldn't answer</p>\n </div>\n </div>\n </EmphasisPanel>\n ) : null}\n\n {stats && stats.byProduct.length > 0 ? (\n <div className=\"rounded-xl border border-border-seam p-4\">\n <p className=\"mb-3 text-sm font-semibold\">Where the assistant sends people</p>\n <div className=\"space-y-1.5\">\n {stats.byProduct.slice(0, 12).map((row: AdminProductStat) => (\n <div key={row.product} className=\"flex items-center gap-3 text-sm\">\n <span className=\"w-40 shrink-0 truncate\">{row.product}</span>\n <span className=\"text-text-tertiary\">{formatNumber(row.answerCount)} cited</span>\n <MousePointerClick className=\"h-3 w-3 text-text-tertiary\" />\n <span className=\"text-text-tertiary\">\n {formatNumber(row.clickCount)} followed\n </span>\n {row.answerCount > 0 && row.clickCount === 0 ? (\n <Badge variant=\"warning\">no conversions</Badge>\n ) : null}\n </div>\n ))}\n </div>\n </div>\n ) : null}\n\n {/* Filters */}\n <div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-4\">\n <Input\n placeholder=\"Search message text…\"\n value={search}\n onChange={(event) => resetToFirstPage(setSearch)(event.target.value)}\n />\n <Input\n placeholder=\"Product slug (e.g. vibecontrols)\"\n value={productSlug}\n onChange={(event) => resetToFirstPage(setProductSlug)(event.target.value)}\n />\n <Input\n type=\"date\"\n aria-label=\"From date\"\n value={from}\n onChange={(event) => resetToFirstPage(setFrom)(event.target.value)}\n />\n <Input\n type=\"date\"\n aria-label=\"To date\"\n value={to}\n onChange={(event) => resetToFirstPage(setTo)(event.target.value)}\n />\n </div>\n\n <div className=\"flex flex-wrap items-center gap-3 text-sm\">\n <label className=\"flex items-center gap-1.5\">\n <input\n type=\"checkbox\"\n checked={unansweredOnly}\n onChange={(event) => resetToFirstPage(setUnansweredOnly)(event.target.checked)}\n />\n <TriangleAlert className=\"h-3.5 w-3.5\" />\n Only unanswered\n </label>\n <label className=\"flex items-center gap-1.5\">\n <input\n type=\"checkbox\"\n checked={convertedOnly}\n onChange={(event) => resetToFirstPage(setConvertedOnly)(event.target.checked)}\n />\n <MousePointerClick className=\"h-3.5 w-3.5\" />\n Only converted\n </label>\n <label className=\"flex items-center gap-1.5\">\n Sort\n <select\n className=\"rounded-md border border-border-seam bg-bg-surface px-2 py-1\"\n value={sort}\n onChange={(event) => resetToFirstPage(setSort)(event.target.value as SortOption)}\n >\n <option value=\"NEWEST\">Newest</option>\n <option value=\"OLDEST\">Oldest</option>\n <option value=\"MOST_TURNS\">Most turns</option>\n <option value=\"MOST_CLICKS\">Most clicks</option>\n </select>\n </label>\n {ipHash ? (\n <button\n type=\"button\"\n className=\"underline underline-offset-2\"\n onClick={() => resetToFirstPage(setIpHash)('')}\n >\n Clear visitor filter ({ipHash.slice(0, 8)})\n </button>\n ) : null}\n </div>\n\n {error ? (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg p-3 text-sm text-status-error-text\">\n {error.message}\n </div>\n ) : null}\n\n <DataTable\n columns={columns}\n data={conversations}\n keyExtractor={(row: AdminConversation) => row.id}\n loading={loading}\n onRowClick={(row: AdminConversation) =>\n setExpanded((current) => (current === row.id ? null : row.id))\n }\n emptyState={\n <EmptyState\n icon={<MessageSquare className=\"h-8 w-8\" />}\n title=\"No conversations\"\n description=\"Nothing matches these filters yet.\"\n />\n }\n />\n\n {expanded ? (\n <div className=\"rounded-xl border border-border-seam p-4\">\n <p className=\"mb-3 text-sm font-semibold\">Transcript</p>\n <div className=\"space-y-3\">\n {(conversations.find((row) => row.id === expanded)?.messages ?? []).map((message) => (\n <div key={message.id} className=\"text-sm\">\n <div className=\"flex items-center gap-2\">\n <Badge variant={message.role === 'USER' ? 'default' : 'secondary'}>\n {message.role}\n </Badge>\n {message.role === 'ASSISTANT' && !message.answered ? (\n <Badge variant=\"warning\">couldn't answer</Badge>\n ) : null}\n <span className=\"text-xs text-text-tertiary\">\n {formatRelativeTime(message.createdAt)}\n </span>\n </div>\n <p className=\"mt-1 whitespace-pre-wrap break-words text-text-secondary\">\n {message.content}\n </p>\n {message.clicks.length > 0 ? (\n <p className=\"mt-1 text-xs text-status-success-text\">\n Followed: {message.clicks.map((click) => click.url).join(', ')}\n </p>\n ) : null}\n </div>\n ))}\n </div>\n </div>\n ) : null}\n\n {total > PAGE_SIZE ? (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n />\n ) : null}\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|