@burdenoff/microfe-admin 2026.520.1 → 2026.529.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/pages/AuditLogsPage.js +96 -81
- package/dist/admin/pages/AuditLogsPage.js.map +1 -1
- package/dist/admin/pages/DashboardPage.js +2 -2
- package/dist/admin/pages/DashboardPage.js.map +1 -1
- package/dist/admin/pages/OrganizationsListPage.js +1 -1
- package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
- package/dist/admin/pages/WorkspacesListPage.js +1 -1
- package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
- package/dist/generated/global-operations.js +4 -4
- package/dist/generated/global-operations.js.map +1 -1
- package/package.json +2 -2
- package/dist/admin/AdminRoot.d.ts +0 -31
- package/dist/admin/AdminRoot.d.ts.map +0 -1
- package/dist/admin/AdminRoutes.d.ts +0 -29
- package/dist/admin/AdminRoutes.d.ts.map +0 -1
- package/dist/admin/components/AccessDenied.d.ts +0 -8
- package/dist/admin/components/AccessDenied.d.ts.map +0 -1
- package/dist/admin/components/ActivityFeed.d.ts +0 -20
- package/dist/admin/components/ActivityFeed.d.ts.map +0 -1
- package/dist/admin/components/Charts.d.ts +0 -33
- package/dist/admin/components/Charts.d.ts.map +0 -1
- package/dist/admin/components/ConfirmDialog.d.ts +0 -45
- package/dist/admin/components/ConfirmDialog.d.ts.map +0 -1
- package/dist/admin/components/DataTable.d.ts +0 -64
- package/dist/admin/components/DataTable.d.ts.map +0 -1
- package/dist/admin/components/FormSheet.d.ts +0 -79
- package/dist/admin/components/FormSheet.d.ts.map +0 -1
- package/dist/admin/components/MetricCard.d.ts +0 -40
- package/dist/admin/components/MetricCard.d.ts.map +0 -1
- package/dist/admin/components/PageLayout.d.ts +0 -34
- package/dist/admin/components/PageLayout.d.ts.map +0 -1
- package/dist/admin/components/PermissionGuard.d.ts +0 -12
- package/dist/admin/components/PermissionGuard.d.ts.map +0 -1
- package/dist/admin/components/RowActions.d.ts +0 -41
- package/dist/admin/components/RowActions.d.ts.map +0 -1
- package/dist/admin/components/StatusBadge.d.ts +0 -35
- package/dist/admin/components/StatusBadge.d.ts.map +0 -1
- package/dist/admin/components/TenantSsoPanel.d.ts +0 -7
- package/dist/admin/components/TenantSsoPanel.d.ts.map +0 -1
- package/dist/admin/components/Toast.d.ts +0 -26
- package/dist/admin/components/Toast.d.ts.map +0 -1
- package/dist/admin/components/forms/SubscriptionForm.d.ts +0 -57
- package/dist/admin/components/forms/SubscriptionForm.d.ts.map +0 -1
- package/dist/admin/components/forms/TenantForm.d.ts +0 -68
- package/dist/admin/components/forms/TenantForm.d.ts.map +0 -1
- package/dist/admin/components/forms/TicketForm.d.ts +0 -67
- package/dist/admin/components/forms/TicketForm.d.ts.map +0 -1
- package/dist/admin/components/forms/UserForm.d.ts +0 -57
- package/dist/admin/components/forms/UserForm.d.ts.map +0 -1
- package/dist/admin/components/forms/index.d.ts +0 -5
- package/dist/admin/components/forms/index.d.ts.map +0 -1
- package/dist/admin/components/index.d.ts +0 -15
- package/dist/admin/components/index.d.ts.map +0 -1
- package/dist/admin/components/ui.d.ts +0 -52
- package/dist/admin/components/ui.d.ts.map +0 -1
- package/dist/admin/constants/index.d.ts +0 -75
- package/dist/admin/constants/index.d.ts.map +0 -1
- package/dist/admin/constants/permissions.d.ts +0 -29
- package/dist/admin/constants/permissions.d.ts.map +0 -1
- package/dist/admin/hooks/useAdminPermissions.d.ts +0 -32
- package/dist/admin/hooks/useAdminPermissions.d.ts.map +0 -1
- package/dist/admin/index.d.ts +0 -31
- package/dist/admin/index.d.ts.map +0 -1
- package/dist/admin/pages/AuditLogsPage.d.ts +0 -5
- package/dist/admin/pages/AuditLogsPage.d.ts.map +0 -1
- package/dist/admin/pages/BillingPage.d.ts +0 -2
- package/dist/admin/pages/BillingPage.d.ts.map +0 -1
- package/dist/admin/pages/CommunicationsPage.d.ts +0 -2
- package/dist/admin/pages/CommunicationsPage.d.ts.map +0 -1
- package/dist/admin/pages/DashboardPage.d.ts +0 -5
- package/dist/admin/pages/DashboardPage.d.ts.map +0 -1
- package/dist/admin/pages/FeatureFlagsPage.d.ts +0 -2
- package/dist/admin/pages/FeatureFlagsPage.d.ts.map +0 -1
- package/dist/admin/pages/OrganizationsListPage.d.ts +0 -2
- package/dist/admin/pages/OrganizationsListPage.d.ts.map +0 -1
- package/dist/admin/pages/ProductsPage.d.ts +0 -5
- package/dist/admin/pages/ProductsPage.d.ts.map +0 -1
- package/dist/admin/pages/ReferralsPage.d.ts +0 -2
- package/dist/admin/pages/ReferralsPage.d.ts.map +0 -1
- package/dist/admin/pages/RolesPermissionsPage.d.ts +0 -2
- package/dist/admin/pages/RolesPermissionsPage.d.ts.map +0 -1
- package/dist/admin/pages/SREPage.d.ts +0 -5
- package/dist/admin/pages/SREPage.d.ts.map +0 -1
- package/dist/admin/pages/SandboxSettingsPage.d.ts +0 -7
- package/dist/admin/pages/SandboxSettingsPage.d.ts.map +0 -1
- package/dist/admin/pages/SettingsPage.d.ts +0 -5
- package/dist/admin/pages/SettingsPage.d.ts.map +0 -1
- package/dist/admin/pages/SyncFailuresPage.d.ts +0 -14
- package/dist/admin/pages/SyncFailuresPage.d.ts.map +0 -1
- package/dist/admin/pages/TenantDetailPage.d.ts +0 -5
- package/dist/admin/pages/TenantDetailPage.d.ts.map +0 -1
- package/dist/admin/pages/TenantFormRoutePage.d.ts +0 -6
- package/dist/admin/pages/TenantFormRoutePage.d.ts.map +0 -1
- package/dist/admin/pages/TenantsListPage.d.ts +0 -5
- package/dist/admin/pages/TenantsListPage.d.ts.map +0 -1
- package/dist/admin/pages/UsersListPage.d.ts +0 -5
- package/dist/admin/pages/UsersListPage.d.ts.map +0 -1
- package/dist/admin/pages/WaitlistPage.d.ts +0 -2
- package/dist/admin/pages/WaitlistPage.d.ts.map +0 -1
- package/dist/admin/pages/WorkflowsPage.d.ts +0 -2
- package/dist/admin/pages/WorkflowsPage.d.ts.map +0 -1
- package/dist/admin/pages/WorkspacesListPage.d.ts +0 -2
- package/dist/admin/pages/WorkspacesListPage.d.ts.map +0 -1
- package/dist/admin/pages/index.d.ts +0 -21
- package/dist/admin/pages/index.d.ts.map +0 -1
- package/dist/admin/providers/AdminProvider.d.ts +0 -13
- package/dist/admin/providers/AdminProvider.d.ts.map +0 -1
- package/dist/admin/schemas/index.d.ts +0 -409
- package/dist/admin/schemas/index.d.ts.map +0 -1
- package/dist/admin/store/adminStore.d.ts +0 -28
- package/dist/admin/store/adminStore.d.ts.map +0 -1
- package/dist/admin/types/index.d.ts +0 -325
- package/dist/admin/types/index.d.ts.map +0 -1
- package/dist/admin/utils/cn.d.ts +0 -7
- package/dist/admin/utils/cn.d.ts.map +0 -1
- package/dist/admin/utils/directActivityGraphql.d.ts +0 -11
- package/dist/admin/utils/directActivityGraphql.d.ts.map +0 -1
- package/dist/admin/utils/formatters.d.ts +0 -33
- package/dist/admin/utils/formatters.d.ts.map +0 -1
- package/dist/admin/utils/gatewayGraphql.d.ts +0 -9
- package/dist/admin/utils/gatewayGraphql.d.ts.map +0 -1
- package/dist/admin/utils/i18n.d.ts +0 -6
- package/dist/admin/utils/i18n.d.ts.map +0 -1
- package/dist/admin/utils/mappers.d.ts +0 -15
- package/dist/admin/utils/mappers.d.ts.map +0 -1
- package/dist/admin/utils/navigation.d.ts +0 -9
- package/dist/admin/utils/navigation.d.ts.map +0 -1
- package/dist/admin/utils/tenantEvents.d.ts +0 -11
- package/dist/admin/utils/tenantEvents.d.ts.map +0 -1
- package/dist/dev/main.d.ts +0 -2
- package/dist/dev/main.d.ts.map +0 -1
- package/dist/generated/global-operations.d.ts +0 -3001
- package/dist/generated/global-operations.d.ts.map +0 -1
- package/dist/generated/global-types.d.ts +0 -22700
- package/dist/generated/global-types.d.ts.map +0 -1
- package/dist/generated/wspace-operations.d.ts +0 -132
- package/dist/generated/wspace-operations.d.ts.map +0 -1
- package/dist/generated/wspace-types.d.ts +0 -57851
- package/dist/generated/wspace-types.d.ts.map +0 -1
- package/dist/index.d.ts +0 -6
- package/dist/index.d.ts.map +0 -1
|
@@ -1,42 +1,43 @@
|
|
|
1
1
|
import { useAdminPermissions as e } from "../hooks/useAdminPermissions.js";
|
|
2
|
-
import {
|
|
3
|
-
import { PageLayout as
|
|
4
|
-
import { formatRelativeTime as
|
|
5
|
-
import { DataTable as
|
|
6
|
-
import { PermissionGuard as
|
|
2
|
+
import { EmptyState as t, Input as n } from "../components/ui.js";
|
|
3
|
+
import { PageLayout as r } from "../components/PageLayout.js";
|
|
4
|
+
import { formatRelativeTime as i } from "../utils/formatters.js";
|
|
5
|
+
import { DataTable as a, Pagination as o } from "../components/DataTable.js";
|
|
6
|
+
import { PermissionGuard as s } from "../components/PermissionGuard.js";
|
|
7
7
|
import "../components/index.js";
|
|
8
|
-
import { useListAuditLogsQuery as
|
|
9
|
-
import { useCallback as
|
|
10
|
-
import { Database as
|
|
11
|
-
import { jsx as
|
|
8
|
+
import { useListAuditLogsQuery as c } from "../../generated/global-operations.js";
|
|
9
|
+
import { useCallback as l, useMemo as u, useState as d } from "react";
|
|
10
|
+
import { Database as f, FileText as p, Key as m, Search as h, Settings as g, Shield as _, User as v } from "lucide-react";
|
|
11
|
+
import { jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
12
|
+
import { SaveToFilesButton as x } from "@burdenoff/fe-libs/storage";
|
|
12
13
|
//#region src/admin/pages/AuditLogsPage.tsx
|
|
13
|
-
var
|
|
14
|
-
auth: /* @__PURE__ */
|
|
15
|
-
user: /* @__PURE__ */
|
|
16
|
-
settings: /* @__PURE__ */
|
|
17
|
-
rbac: /* @__PURE__ */
|
|
18
|
-
data: /* @__PURE__ */
|
|
19
|
-
api: /* @__PURE__ */
|
|
14
|
+
var S = 20, C = {
|
|
15
|
+
auth: /* @__PURE__ */ y(v, { className: "h-3 w-3" }),
|
|
16
|
+
user: /* @__PURE__ */ y(v, { className: "h-3 w-3" }),
|
|
17
|
+
settings: /* @__PURE__ */ y(g, { className: "h-3 w-3" }),
|
|
18
|
+
rbac: /* @__PURE__ */ y(_, { className: "h-3 w-3" }),
|
|
19
|
+
data: /* @__PURE__ */ y(f, { className: "h-3 w-3" }),
|
|
20
|
+
api: /* @__PURE__ */ y(m, { className: "h-3 w-3" })
|
|
20
21
|
};
|
|
21
|
-
function
|
|
22
|
-
return
|
|
22
|
+
function w(e) {
|
|
23
|
+
return C[e.split(".")[0]] ?? /* @__PURE__ */ y(p, { className: "h-3 w-3" });
|
|
23
24
|
}
|
|
24
|
-
function
|
|
25
|
-
let { canViewAuditLogs:
|
|
25
|
+
function T() {
|
|
26
|
+
let { canViewAuditLogs: f, isLoading: m } = e(), [g, _] = d(1), [v, C] = d(""), [T, E] = d(""), D = m || !f, { data: O, loading: k, error: A } = c({
|
|
26
27
|
variables: {
|
|
27
|
-
resource:
|
|
28
|
-
limit:
|
|
29
|
-
offset: (
|
|
28
|
+
resource: T || void 0,
|
|
29
|
+
limit: S,
|
|
30
|
+
offset: (g - 1) * S
|
|
30
31
|
},
|
|
31
32
|
fetchPolicy: "cache-and-network",
|
|
32
|
-
skip:
|
|
33
|
-
}),
|
|
34
|
-
if (!
|
|
35
|
-
let e =
|
|
36
|
-
return
|
|
37
|
-
}, [
|
|
38
|
-
let e =
|
|
39
|
-
if (e.length === 0) return;
|
|
33
|
+
skip: D
|
|
34
|
+
}), j = u(() => (O?.auditLogs?.edges ?? []).map((e) => e.node), [O]), M = u(() => {
|
|
35
|
+
if (!v) return j;
|
|
36
|
+
let e = v.toLowerCase();
|
|
37
|
+
return j.filter((t) => t.action.toLowerCase().includes(e) || t.actorId.toLowerCase().includes(e) || t.resource.toLowerCase().includes(e) || t.tenantId.toLowerCase().includes(e));
|
|
38
|
+
}, [j, v]), N = O?.auditLogs?.totalCount ?? 0, P = Math.ceil(N / S), F = l(() => {
|
|
39
|
+
let e = M;
|
|
40
|
+
if (e.length === 0) return null;
|
|
40
41
|
let t = [
|
|
41
42
|
"id",
|
|
42
43
|
"action",
|
|
@@ -67,61 +68,75 @@ function E() {
|
|
|
67
68
|
n(e.changes),
|
|
68
69
|
n(e.metadata),
|
|
69
70
|
n(e.createdAt)
|
|
70
|
-
].join(","))]
|
|
71
|
-
|
|
72
|
-
}, [
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
].join(","))];
|
|
72
|
+
return new Blob([r.join("\n")], { type: "text/csv;charset=utf-8;" });
|
|
73
|
+
}, [M]), I = l(() => {
|
|
74
|
+
let e = F();
|
|
75
|
+
if (!e) return;
|
|
76
|
+
let t = URL.createObjectURL(e), n = document.createElement("a"), r = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
77
|
+
n.href = t, n.setAttribute("download", `audit-logs-${r}.csv`), document.body.appendChild(n), n.click(), document.body.removeChild(n), URL.revokeObjectURL(t);
|
|
78
|
+
}, [F]);
|
|
79
|
+
return /* @__PURE__ */ y(s, {
|
|
80
|
+
allowed: f,
|
|
81
|
+
isLoading: m,
|
|
76
82
|
pageTitle: "Audit Logs",
|
|
77
83
|
denyMessage: "You don't have permission to view audit logs.",
|
|
78
|
-
children: /* @__PURE__ */
|
|
84
|
+
children: /* @__PURE__ */ y(r, {
|
|
79
85
|
title: "Audit Logs",
|
|
80
|
-
description: `Security and compliance audit trail${
|
|
81
|
-
actions: /* @__PURE__ */
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
description: `Security and compliance audit trail${N ? ` · ${N} total` : ""}`,
|
|
87
|
+
actions: /* @__PURE__ */ y(x, {
|
|
88
|
+
label: "Export CSV",
|
|
89
|
+
disabled: k || M.length === 0,
|
|
90
|
+
testId: "audit-logs-export",
|
|
91
|
+
defaultFolderPath: "/Admin/Audit Logs",
|
|
92
|
+
source: {
|
|
93
|
+
kind: "blob",
|
|
94
|
+
filename: `audit-logs-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}.csv`,
|
|
95
|
+
mimeType: "text/csv",
|
|
96
|
+
getBlob: async () => {
|
|
97
|
+
let e = F();
|
|
98
|
+
return e ? { blob: e } : null;
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
onDownload: I
|
|
87
102
|
}),
|
|
88
|
-
children: /* @__PURE__ */
|
|
103
|
+
children: /* @__PURE__ */ b("div", {
|
|
89
104
|
className: "space-y-4",
|
|
90
105
|
children: [
|
|
91
|
-
/* @__PURE__ */
|
|
106
|
+
/* @__PURE__ */ b("div", {
|
|
92
107
|
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
93
|
-
children: [/* @__PURE__ */
|
|
108
|
+
children: [/* @__PURE__ */ b("div", {
|
|
94
109
|
className: "relative max-w-sm flex-1",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
110
|
+
children: [/* @__PURE__ */ y(h, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ y(n, {
|
|
96
111
|
placeholder: "Search by action, actor, or resource...",
|
|
97
|
-
value:
|
|
98
|
-
onChange: (e) =>
|
|
112
|
+
value: v,
|
|
113
|
+
onChange: (e) => C(e.target.value),
|
|
99
114
|
className: "pl-9"
|
|
100
115
|
})]
|
|
101
|
-
}), /* @__PURE__ */
|
|
116
|
+
}), /* @__PURE__ */ y(n, {
|
|
102
117
|
placeholder: "Filter by resource (e.g. tenant, user)...",
|
|
103
|
-
value:
|
|
118
|
+
value: T,
|
|
104
119
|
onChange: (e) => {
|
|
105
|
-
|
|
120
|
+
_(1), E(e.target.value);
|
|
106
121
|
},
|
|
107
122
|
className: "max-w-xs"
|
|
108
123
|
})]
|
|
109
124
|
}),
|
|
110
|
-
|
|
125
|
+
A && /* @__PURE__ */ b("div", {
|
|
111
126
|
className: "rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive",
|
|
112
|
-
children: ["Failed to load audit logs: ",
|
|
127
|
+
children: ["Failed to load audit logs: ", A.message]
|
|
113
128
|
}),
|
|
114
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ y(a, {
|
|
115
130
|
columns: [
|
|
116
131
|
{
|
|
117
132
|
key: "action",
|
|
118
133
|
header: "Action",
|
|
119
|
-
render: (e) => /* @__PURE__ */
|
|
134
|
+
render: (e) => /* @__PURE__ */ b("div", {
|
|
120
135
|
className: "flex items-center gap-2",
|
|
121
|
-
children: [/* @__PURE__ */
|
|
136
|
+
children: [/* @__PURE__ */ y("span", {
|
|
122
137
|
className: "text-muted-foreground",
|
|
123
|
-
children:
|
|
124
|
-
}), /* @__PURE__ */
|
|
138
|
+
children: w(e.action)
|
|
139
|
+
}), /* @__PURE__ */ y("span", {
|
|
125
140
|
className: "font-mono text-sm font-medium text-foreground",
|
|
126
141
|
children: e.action
|
|
127
142
|
})]
|
|
@@ -131,12 +146,12 @@ function E() {
|
|
|
131
146
|
key: "actorId",
|
|
132
147
|
header: "Actor",
|
|
133
148
|
width: "180px",
|
|
134
|
-
render: (e) => /* @__PURE__ */
|
|
149
|
+
render: (e) => /* @__PURE__ */ b("div", {
|
|
135
150
|
className: "min-w-0",
|
|
136
|
-
children: [/* @__PURE__ */
|
|
151
|
+
children: [/* @__PURE__ */ y("div", {
|
|
137
152
|
className: "truncate text-sm",
|
|
138
153
|
children: e.actorId
|
|
139
|
-
}), /* @__PURE__ */
|
|
154
|
+
}), /* @__PURE__ */ y("div", {
|
|
140
155
|
className: "text-xs text-muted-foreground capitalize",
|
|
141
156
|
children: e.actorType
|
|
142
157
|
})]
|
|
@@ -146,12 +161,12 @@ function E() {
|
|
|
146
161
|
key: "resource",
|
|
147
162
|
header: "Resource",
|
|
148
163
|
width: "150px",
|
|
149
|
-
render: (e) => /* @__PURE__ */
|
|
164
|
+
render: (e) => /* @__PURE__ */ b("div", {
|
|
150
165
|
className: "min-w-0",
|
|
151
|
-
children: [/* @__PURE__ */
|
|
166
|
+
children: [/* @__PURE__ */ y("div", {
|
|
152
167
|
className: "truncate text-sm",
|
|
153
168
|
children: e.resource
|
|
154
|
-
}), e.resourceId && /* @__PURE__ */
|
|
169
|
+
}), e.resourceId && /* @__PURE__ */ b("div", {
|
|
155
170
|
className: "truncate text-xs text-muted-foreground",
|
|
156
171
|
children: ["#", String(e.resourceId).slice(-8)]
|
|
157
172
|
})]
|
|
@@ -161,7 +176,7 @@ function E() {
|
|
|
161
176
|
key: "tenantId",
|
|
162
177
|
header: "Tenant",
|
|
163
178
|
width: "130px",
|
|
164
|
-
render: (e) => /* @__PURE__ */
|
|
179
|
+
render: (e) => /* @__PURE__ */ y("span", {
|
|
165
180
|
className: "text-xs text-muted-foreground",
|
|
166
181
|
children: e.tenantId ? `#${e.tenantId.slice(-8)}` : "—"
|
|
167
182
|
})
|
|
@@ -170,7 +185,7 @@ function E() {
|
|
|
170
185
|
key: "ipAddress",
|
|
171
186
|
header: "IP",
|
|
172
187
|
width: "120px",
|
|
173
|
-
render: (e) => /* @__PURE__ */
|
|
188
|
+
render: (e) => /* @__PURE__ */ y("span", {
|
|
174
189
|
className: "font-mono text-xs text-muted-foreground",
|
|
175
190
|
children: e.ipAddress ?? "—"
|
|
176
191
|
})
|
|
@@ -179,24 +194,24 @@ function E() {
|
|
|
179
194
|
key: "createdAt",
|
|
180
195
|
header: "Time",
|
|
181
196
|
width: "110px",
|
|
182
|
-
render: (e) =>
|
|
197
|
+
render: (e) => i(e.createdAt)
|
|
183
198
|
}
|
|
184
199
|
],
|
|
185
|
-
data:
|
|
200
|
+
data: M,
|
|
186
201
|
keyExtractor: (e) => e.id,
|
|
187
|
-
loading:
|
|
188
|
-
emptyState: /* @__PURE__ */
|
|
189
|
-
icon: /* @__PURE__ */
|
|
202
|
+
loading: k,
|
|
203
|
+
emptyState: /* @__PURE__ */ y(t, {
|
|
204
|
+
icon: /* @__PURE__ */ y(p, { className: "h-8 w-8" }),
|
|
190
205
|
title: "No audit logs found",
|
|
191
206
|
description: "Audit events will appear here when actions are performed"
|
|
192
207
|
})
|
|
193
208
|
}),
|
|
194
|
-
|
|
195
|
-
page:
|
|
196
|
-
totalPages:
|
|
197
|
-
totalItems:
|
|
198
|
-
pageSize:
|
|
199
|
-
onPageChange:
|
|
209
|
+
N > S && /* @__PURE__ */ y(o, {
|
|
210
|
+
page: g,
|
|
211
|
+
totalPages: P,
|
|
212
|
+
totalItems: N,
|
|
213
|
+
pageSize: S,
|
|
214
|
+
onPageChange: _
|
|
200
215
|
})
|
|
201
216
|
]
|
|
202
217
|
})
|
|
@@ -204,6 +219,6 @@ function E() {
|
|
|
204
219
|
});
|
|
205
220
|
}
|
|
206
221
|
//#endregion
|
|
207
|
-
export {
|
|
222
|
+
export { T as AuditLogsPage };
|
|
208
223
|
|
|
209
224
|
//# sourceMappingURL=AuditLogsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuditLogsPage.js","names":[],"sources":["../../../src/admin/pages/AuditLogsPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { FileText, Search, Download, User, Settings, Shield, Database, Key } from 'lucide-react';\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n EmptyState,\n Button,\n PermissionGuard,\n} from '../components';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { useListAuditLogsQuery } from '../../generated/global-operations';\n\nconst PAGE_SIZE = 20;\n\nconst ACTION_ICONS: Record<string, React.ReactNode> = {\n auth: <User className=\"h-3 w-3\" />,\n user: <User className=\"h-3 w-3\" />,\n settings: <Settings className=\"h-3 w-3\" />,\n rbac: <Shield className=\"h-3 w-3\" />,\n data: <Database className=\"h-3 w-3\" />,\n api: <Key className=\"h-3 w-3\" />,\n};\n\nfunction getActionIcon(action: string) {\n const prefix = action.split('.')[0];\n return ACTION_ICONS[prefix] ?? <FileText className=\"h-3 w-3\" />;\n}\n\n/**\n * Audit logs page — connected to real backend\n */\nexport function AuditLogsPage() {\n const { canViewAuditLogs, isLoading: permissionsLoading } = useAdminPermissions();\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [resourceFilter, setResourceFilter] = useState('');\n\n const skipQueries = permissionsLoading || !canViewAuditLogs;\n\n const { data, loading, error } = useListAuditLogsQuery({\n variables: {\n resource: resourceFilter || undefined,\n limit: PAGE_SIZE,\n offset: (page - 1) * PAGE_SIZE,\n },\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n\n const auditLogs = useMemo(() => {\n const edges = data?.auditLogs?.edges ?? [];\n return edges.map((e: (typeof edges)[number]) => e.node);\n }, [data]);\n\n type AuditLogNode = (typeof auditLogs)[number];\n\n const filteredLogs = useMemo(() => {\n if (!search) return auditLogs;\n const q = search.toLowerCase();\n return auditLogs.filter(\n (log: AuditLogNode) =>\n log.action.toLowerCase().includes(q) ||\n log.actorId.toLowerCase().includes(q) ||\n log.resource.toLowerCase().includes(q) ||\n log.tenantId.toLowerCase().includes(q)\n );\n }, [auditLogs, search]);\n\n const total = data?.auditLogs?.totalCount ?? 0;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n\n const exportToCsv = useCallback(() => {\n const rows = filteredLogs;\n if (rows.length === 0) return;\n\n const headers = [\n 'id',\n 'action',\n 'actorId',\n 'actorType',\n 'resource',\n 'resourceId',\n 'tenantId',\n 'ipAddress',\n 'userAgent',\n 'changes',\n 'metadata',\n 'createdAt',\n ];\n\n const escape = (value: unknown): string => {\n if (value === null || value === undefined) return '';\n const str = typeof value === 'object' ? JSON.stringify(value) : String(value);\n // RFC 4180: wrap in quotes if it contains quote, comma, or newline; double internal quotes\n if (/[\",\\n\\r]/.test(str)) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n };\n\n const csvLines = [\n headers.join(','),\n ...rows.map((log: AuditLogNode) =>\n [\n escape(log.id),\n escape(log.action),\n escape(log.actorId),\n escape(log.actorType),\n escape(log.resource),\n escape(log.resourceId),\n escape(log.tenantId),\n escape(log.ipAddress),\n escape((log as { userAgent?: unknown }).userAgent),\n escape((log as { changes?: unknown }).changes),\n escape((log as { metadata?: unknown }).metadata),\n escape(log.createdAt),\n ].join(',')\n ),\n ];\n\n const csv = csvLines.join('\\n');\n const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n link.href = url;\n link.setAttribute('download', `audit-logs-${stamp}.csv`);\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }, [filteredLogs]);\n\n const columns = [\n {\n key: 'action',\n header: 'Action',\n render: (log: (typeof auditLogs)[number]) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground\">{getActionIcon(log.action)}</span>\n <span className=\"font-mono text-sm font-medium text-foreground\">{log.action}</span>\n </div>\n ),\n },\n {\n key: 'actorId',\n header: 'Actor',\n width: '180px',\n render: (log: (typeof auditLogs)[number]) => (\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm\">{log.actorId}</div>\n <div className=\"text-xs text-muted-foreground capitalize\">{log.actorType}</div>\n </div>\n ),\n },\n {\n key: 'resource',\n header: 'Resource',\n width: '150px',\n render: (log: (typeof auditLogs)[number]) => (\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm\">{log.resource}</div>\n {log.resourceId && (\n <div className=\"truncate text-xs text-muted-foreground\">\n #{String(log.resourceId).slice(-8)}\n </div>\n )}\n </div>\n ),\n },\n {\n key: 'tenantId',\n header: 'Tenant',\n width: '130px',\n render: (log: (typeof auditLogs)[number]) => (\n <span className=\"text-xs text-muted-foreground\">\n {log.tenantId ? `#${log.tenantId.slice(-8)}` : '—'}\n </span>\n ),\n },\n {\n key: 'ipAddress',\n header: 'IP',\n width: '120px',\n render: (log: (typeof auditLogs)[number]) => (\n <span className=\"font-mono text-xs text-muted-foreground\">{log.ipAddress ?? '—'}</span>\n ),\n },\n {\n key: 'createdAt',\n header: 'Time',\n width: '110px',\n render: (log: (typeof auditLogs)[number]) => formatRelativeTime(log.createdAt),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canViewAuditLogs}\n isLoading={permissionsLoading}\n pageTitle=\"Audit Logs\"\n denyMessage=\"You don't have permission to view audit logs.\"\n >\n <PageLayout\n title=\"Audit Logs\"\n description={`Security and compliance audit trail${total ? ` · ${total} total` : ''}`}\n actions={\n <Button\n variant=\"outline\"\n onClick={exportToCsv}\n disabled={loading || filteredLogs.length === 0}\n data-testid=\"audit-logs-export-csv\"\n >\n <Download className=\"mr-2 h-4 w-4\" />\n Export CSV\n </Button>\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder=\"Search by action, actor, or resource...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <Input\n placeholder=\"Filter by resource (e.g. tenant, user)...\"\n value={resourceFilter}\n onChange={(e) => {\n setPage(1);\n setResourceFilter(e.target.value);\n }}\n className=\"max-w-xs\"\n />\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive\">\n Failed to load audit logs: {error.message}\n </div>\n )}\n\n <DataTable\n columns={columns}\n data={filteredLogs}\n keyExtractor={(l) => l.id}\n loading={loading}\n emptyState={\n <EmptyState\n icon={<FileText className=\"h-8 w-8\" />}\n title=\"No audit logs found\"\n description=\"Audit events will appear here when actions are performed\"\n />\n }\n />\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 )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AuditLogsPage.js","names":[],"sources":["../../../src/admin/pages/AuditLogsPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback } from 'react';\nimport { FileText, Search, Download, User, Settings, Shield, Database, Key } from 'lucide-react';\nimport { SaveToFilesButton } from '@burdenoff/fe-libs/storage';\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n EmptyState,\n Button,\n PermissionGuard,\n} from '../components';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport { formatRelativeTime } from '../utils/formatters';\nimport { useListAuditLogsQuery } from '../../generated/global-operations';\n\nconst PAGE_SIZE = 20;\n\nconst ACTION_ICONS: Record<string, React.ReactNode> = {\n auth: <User className=\"h-3 w-3\" />,\n user: <User className=\"h-3 w-3\" />,\n settings: <Settings className=\"h-3 w-3\" />,\n rbac: <Shield className=\"h-3 w-3\" />,\n data: <Database className=\"h-3 w-3\" />,\n api: <Key className=\"h-3 w-3\" />,\n};\n\nfunction getActionIcon(action: string) {\n const prefix = action.split('.')[0];\n return ACTION_ICONS[prefix] ?? <FileText className=\"h-3 w-3\" />;\n}\n\n/**\n * Audit logs page — connected to real backend\n */\nexport function AuditLogsPage() {\n const { canViewAuditLogs, isLoading: permissionsLoading } = useAdminPermissions();\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [resourceFilter, setResourceFilter] = useState('');\n\n const skipQueries = permissionsLoading || !canViewAuditLogs;\n\n const { data, loading, error } = useListAuditLogsQuery({\n variables: {\n resource: resourceFilter || undefined,\n limit: PAGE_SIZE,\n offset: (page - 1) * PAGE_SIZE,\n },\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n\n const auditLogs = useMemo(() => {\n const edges = data?.auditLogs?.edges ?? [];\n return edges.map((e: (typeof edges)[number]) => e.node);\n }, [data]);\n\n type AuditLogNode = (typeof auditLogs)[number];\n\n const filteredLogs = useMemo(() => {\n if (!search) return auditLogs;\n const q = search.toLowerCase();\n return auditLogs.filter(\n (log: AuditLogNode) =>\n log.action.toLowerCase().includes(q) ||\n log.actorId.toLowerCase().includes(q) ||\n log.resource.toLowerCase().includes(q) ||\n log.tenantId.toLowerCase().includes(q)\n );\n }, [auditLogs, search]);\n\n const total = data?.auditLogs?.totalCount ?? 0;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n\n const buildAuditCsvBlob = useCallback((): Blob | null => {\n const rows = filteredLogs;\n if (rows.length === 0) return null;\n\n const headers = [\n 'id',\n 'action',\n 'actorId',\n 'actorType',\n 'resource',\n 'resourceId',\n 'tenantId',\n 'ipAddress',\n 'userAgent',\n 'changes',\n 'metadata',\n 'createdAt',\n ];\n\n const escape = (value: unknown): string => {\n if (value === null || value === undefined) return '';\n const str = typeof value === 'object' ? JSON.stringify(value) : String(value);\n // RFC 4180: wrap in quotes if it contains quote, comma, or newline; double internal quotes\n if (/[\",\\n\\r]/.test(str)) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n };\n\n const csvLines = [\n headers.join(','),\n ...rows.map((log: AuditLogNode) =>\n [\n escape(log.id),\n escape(log.action),\n escape(log.actorId),\n escape(log.actorType),\n escape(log.resource),\n escape(log.resourceId),\n escape(log.tenantId),\n escape(log.ipAddress),\n escape((log as { userAgent?: unknown }).userAgent),\n escape((log as { changes?: unknown }).changes),\n escape((log as { metadata?: unknown }).metadata),\n escape(log.createdAt),\n ].join(',')\n ),\n ];\n\n return new Blob([csvLines.join('\\n')], { type: 'text/csv;charset=utf-8;' });\n }, [filteredLogs]);\n\n const exportToCsv = useCallback(() => {\n const blob = buildAuditCsvBlob();\n if (!blob) return;\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n const stamp = new Date().toISOString().replace(/[:.]/g, '-');\n link.href = url;\n link.setAttribute('download', `audit-logs-${stamp}.csv`);\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n }, [buildAuditCsvBlob]);\n\n const columns = [\n {\n key: 'action',\n header: 'Action',\n render: (log: (typeof auditLogs)[number]) => (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-muted-foreground\">{getActionIcon(log.action)}</span>\n <span className=\"font-mono text-sm font-medium text-foreground\">{log.action}</span>\n </div>\n ),\n },\n {\n key: 'actorId',\n header: 'Actor',\n width: '180px',\n render: (log: (typeof auditLogs)[number]) => (\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm\">{log.actorId}</div>\n <div className=\"text-xs text-muted-foreground capitalize\">{log.actorType}</div>\n </div>\n ),\n },\n {\n key: 'resource',\n header: 'Resource',\n width: '150px',\n render: (log: (typeof auditLogs)[number]) => (\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm\">{log.resource}</div>\n {log.resourceId && (\n <div className=\"truncate text-xs text-muted-foreground\">\n #{String(log.resourceId).slice(-8)}\n </div>\n )}\n </div>\n ),\n },\n {\n key: 'tenantId',\n header: 'Tenant',\n width: '130px',\n render: (log: (typeof auditLogs)[number]) => (\n <span className=\"text-xs text-muted-foreground\">\n {log.tenantId ? `#${log.tenantId.slice(-8)}` : '—'}\n </span>\n ),\n },\n {\n key: 'ipAddress',\n header: 'IP',\n width: '120px',\n render: (log: (typeof auditLogs)[number]) => (\n <span className=\"font-mono text-xs text-muted-foreground\">{log.ipAddress ?? '—'}</span>\n ),\n },\n {\n key: 'createdAt',\n header: 'Time',\n width: '110px',\n render: (log: (typeof auditLogs)[number]) => formatRelativeTime(log.createdAt),\n },\n ];\n\n return (\n <PermissionGuard\n allowed={canViewAuditLogs}\n isLoading={permissionsLoading}\n pageTitle=\"Audit Logs\"\n denyMessage=\"You don't have permission to view audit logs.\"\n >\n <PageLayout\n title=\"Audit Logs\"\n description={`Security and compliance audit trail${total ? ` · ${total} total` : ''}`}\n actions={\n <SaveToFilesButton\n label=\"Export CSV\"\n disabled={loading || filteredLogs.length === 0}\n testId=\"audit-logs-export\"\n defaultFolderPath=\"/Admin/Audit Logs\"\n source={{\n kind: 'blob',\n filename: `audit-logs-${new Date().toISOString().replace(/[:.]/g, '-')}.csv`,\n mimeType: 'text/csv',\n getBlob: async () => {\n const blob = buildAuditCsvBlob();\n return blob ? { blob } : null;\n },\n }}\n onDownload={exportToCsv}\n />\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder=\"Search by action, actor, or resource...\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n className=\"pl-9\"\n />\n </div>\n <Input\n placeholder=\"Filter by resource (e.g. tenant, user)...\"\n value={resourceFilter}\n onChange={(e) => {\n setPage(1);\n setResourceFilter(e.target.value);\n }}\n className=\"max-w-xs\"\n />\n </div>\n\n {error && (\n <div className=\"rounded-md border border-destructive/50 bg-destructive/10 p-4 text-sm text-destructive\">\n Failed to load audit logs: {error.message}\n </div>\n )}\n\n <DataTable\n columns={columns}\n data={filteredLogs}\n keyExtractor={(l) => l.id}\n loading={loading}\n emptyState={\n <EmptyState\n icon={<FileText className=\"h-8 w-8\" />}\n title=\"No audit logs found\"\n description=\"Audit events will appear here when actions are performed\"\n />\n }\n />\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 )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -34,8 +34,8 @@ function O() {
|
|
|
34
34
|
skip: N
|
|
35
35
|
}), W = {
|
|
36
36
|
tenants: P?.tenants?.total ?? 0,
|
|
37
|
-
organizations: I?.
|
|
38
|
-
workspaces: R?.
|
|
37
|
+
organizations: I?.allOrganizations?.total ?? 0,
|
|
38
|
+
workspaces: R?.allWorkspaces?.total ?? 0,
|
|
39
39
|
users: B?.userCount ?? 0
|
|
40
40
|
}, G = g(() => (H?.activityLogs?.nodes ?? []).map(u), [H]), K = [
|
|
41
41
|
{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/admin/pages/DashboardPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Building2,\n Users,\n Layout,\n Briefcase,\n Plus,\n UserPlus,\n FileText,\n HelpCircle,\n} from 'lucide-react';\nimport { useAdmin } from '../providers/AdminProvider';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n MetricCard,\n MetricsGrid,\n ActivityFeed,\n QuickLinks,\n PermissionGuard,\n} from '../components';\nimport { joinPath } from '../utils/navigation';\nimport { mapActivityLogToUi } from '../utils/mappers';\nimport type { Activity } from '../types';\nimport {\n useGetDashboardTenantCountQuery,\n useGetDashboardOrganizationCountQuery,\n useGetDashboardActiveWorkspaceCountQuery,\n useGetDashboardUserCountQuery,\n useGetRecentActivityLogsQuery,\n} from '../../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\n/**\n * Dashboard page — platform overview with real-time metrics\n */\nexport function DashboardPage() {\n const { canViewDashboard, isLoading: permissionsLoading } = useAdminPermissions();\n const { basePath = '' } = useAdmin();\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n\n const skipQueries = permissionsLoading || !canViewDashboard;\n\n const { data: tenantData, loading: tenantLoading } = useGetDashboardTenantCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: orgData, loading: orgLoading } = useGetDashboardOrganizationCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: workspaceData, loading: workspaceLoading } =\n useGetDashboardActiveWorkspaceCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: userData, loading: userLoading } = useGetDashboardUserCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: activityData, loading: activityLoading } = useGetRecentActivityLogsQuery({\n variables: { limit: 10 },\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n\n const metrics = {\n tenants: tenantData?.tenants?.total ?? 0,\n organizations: orgData?.
|
|
1
|
+
{"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/admin/pages/DashboardPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport {\n Building2,\n Users,\n Layout,\n Briefcase,\n Plus,\n UserPlus,\n FileText,\n HelpCircle,\n} from 'lucide-react';\nimport { useAdmin } from '../providers/AdminProvider';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n MetricCard,\n MetricsGrid,\n ActivityFeed,\n QuickLinks,\n PermissionGuard,\n} from '../components';\nimport { joinPath } from '../utils/navigation';\nimport { mapActivityLogToUi } from '../utils/mappers';\nimport type { Activity } from '../types';\nimport {\n useGetDashboardTenantCountQuery,\n useGetDashboardOrganizationCountQuery,\n useGetDashboardActiveWorkspaceCountQuery,\n useGetDashboardUserCountQuery,\n useGetRecentActivityLogsQuery,\n} from '../../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { tWithFallback } from '../utils/i18n';\n\n/**\n * Dashboard page — platform overview with real-time metrics\n */\nexport function DashboardPage() {\n const { canViewDashboard, isLoading: permissionsLoading } = useAdminPermissions();\n const { basePath = '' } = useAdmin();\n const { t } = useI18n();\n const tr = (key: string, fallback: string, params?: Record<string, string | number>): string =>\n tWithFallback(t, key, fallback, params);\n\n const skipQueries = permissionsLoading || !canViewDashboard;\n\n const { data: tenantData, loading: tenantLoading } = useGetDashboardTenantCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: orgData, loading: orgLoading } = useGetDashboardOrganizationCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: workspaceData, loading: workspaceLoading } =\n useGetDashboardActiveWorkspaceCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: userData, loading: userLoading } = useGetDashboardUserCountQuery({\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n const { data: activityData, loading: activityLoading } = useGetRecentActivityLogsQuery({\n variables: { limit: 10 },\n fetchPolicy: 'cache-and-network',\n skip: skipQueries,\n });\n\n const metrics = {\n tenants: tenantData?.tenants?.total ?? 0,\n organizations: orgData?.allOrganizations?.total ?? 0,\n workspaces: workspaceData?.allWorkspaces?.total ?? 0,\n users: userData?.userCount ?? 0,\n };\n\n const recentActivity: Activity[] = useMemo(\n () => (activityData?.activityLogs?.nodes ?? []).map(mapActivityLogToUi),\n [activityData]\n );\n\n const quickLinks = [\n {\n to: joinPath(basePath, 'tenants'),\n label: tr('admin.dashboard.quickLinks.addTenant', 'Add Tenant'),\n icon: <Plus className=\"h-4 w-4\" />,\n description: tr('admin.dashboard.quickLinks.addTenantDescription', 'Create a new tenant'),\n },\n {\n to: joinPath(basePath, 'users'),\n label: tr('admin.dashboard.quickLinks.inviteUser', 'Invite User'),\n icon: <UserPlus className=\"h-4 w-4\" />,\n description: tr('admin.dashboard.quickLinks.inviteUserDescription', 'Invite a new user'),\n },\n {\n to: joinPath(basePath, 'audit'),\n label: tr('admin.dashboard.quickLinks.auditLogs', 'Audit Logs'),\n icon: <FileText className=\"h-4 w-4\" />,\n description: tr(\n 'admin.dashboard.quickLinks.auditLogsDescription',\n 'View security audit trail'\n ),\n },\n {\n to: joinPath(basePath, 'support'),\n label: tr('admin.dashboard.quickLinks.support', 'Support'),\n icon: <HelpCircle className=\"h-4 w-4\" />,\n description: tr('admin.dashboard.quickLinks.supportDescription', 'Manage support tickets'),\n },\n ];\n\n const isLoading = tenantLoading || orgLoading || workspaceLoading || userLoading;\n\n return (\n <PermissionGuard\n allowed={canViewDashboard}\n isLoading={permissionsLoading}\n pageTitle={tr('admin.dashboard.title', 'Dashboard')}\n denyMessage={tr(\n 'admin.dashboard.denyMessage',\n \"You don't have permission to view the admin dashboard.\"\n )}\n >\n <PageLayout\n title={tr('admin.dashboard.title', 'Dashboard')}\n description={tr('admin.dashboard.description', 'Platform overview and key metrics')}\n >\n <div className=\"space-y-6\">\n {/* Metrics Grid */}\n <MetricsGrid>\n <MetricCard\n title={tr('admin.dashboard.metrics.totalTenants', 'Total Tenants')}\n value={metrics.tenants}\n loading={isLoading}\n icon={<Building2 className=\"h-5 w-5 text-blue-500\" />}\n />\n <MetricCard\n title={tr('admin.dashboard.metrics.organizations', 'Organizations')}\n value={metrics.organizations}\n loading={isLoading}\n icon={<Briefcase className=\"h-5 w-5 text-purple-500\" />}\n />\n <MetricCard\n title={tr('admin.dashboard.metrics.activeWorkspaces', 'Active Workspaces')}\n value={metrics.workspaces}\n loading={isLoading}\n icon={<Layout className=\"h-5 w-5 text-green-500\" />}\n />\n <MetricCard\n title={tr('admin.dashboard.metrics.totalUsers', 'Total Users')}\n value={metrics.users}\n loading={isLoading}\n icon={<Users className=\"h-5 w-5 text-orange-500\" />}\n />\n </MetricsGrid>\n\n {/* Content Area */}\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <ActivityFeed activities={recentActivity} loading={activityLoading} />\n </div>\n <div className=\"space-y-4\">\n <QuickLinks links={quickLinks} />\n </div>\n </div>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ function p() {
|
|
|
15
15
|
id: e,
|
|
16
16
|
input: { status: t === "ACTIVE" ? "INACTIVE" : "ACTIVE" }
|
|
17
17
|
} }), _();
|
|
18
|
-
}, b = h?.
|
|
18
|
+
}, b = h?.allOrganizations?.items || [];
|
|
19
19
|
return /* @__PURE__ */ d(r, {
|
|
20
20
|
title: "Organizations",
|
|
21
21
|
description: "Manage and monitor platform organizations",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OrganizationsListPage.js","names":[],"sources":["../../../src/admin/pages/OrganizationsListPage.tsx"],"sourcesContent":["import { Building, Search, Plus, Loader2 } from 'lucide-react';\nimport { PageLayout, Input, Button, EmptyState } from '../components';\nimport {\n useListOrganizationsQuery,\n useUpdateOrganizationMutation,\n} from '../../generated/global-operations';\nimport { useState } from 'react';\n\nexport function OrganizationsListPage() {\n const [searchTerm, setSearchTerm] = useState('');\n const { data, loading, refetch } = useListOrganizationsQuery({\n variables: {\n filter: searchTerm ? { search: searchTerm } : undefined,\n limit: 50,\n },\n });\n\n const [updateOrganization] = useUpdateOrganizationMutation();\n\n const handleToggleStatus = async (id: string, currentStatus: string) => {\n const newStatus = currentStatus === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE';\n await updateOrganization({\n variables: {\n id,\n input: { status: newStatus },\n },\n });\n refetch();\n };\n\n const organizations = data?.
|
|
1
|
+
{"version":3,"file":"OrganizationsListPage.js","names":[],"sources":["../../../src/admin/pages/OrganizationsListPage.tsx"],"sourcesContent":["import { Building, Search, Plus, Loader2 } from 'lucide-react';\nimport { PageLayout, Input, Button, EmptyState } from '../components';\nimport {\n useListOrganizationsQuery,\n useUpdateOrganizationMutation,\n} from '../../generated/global-operations';\nimport { useState } from 'react';\n\nexport function OrganizationsListPage() {\n const [searchTerm, setSearchTerm] = useState('');\n const { data, loading, refetch } = useListOrganizationsQuery({\n variables: {\n filter: searchTerm ? { search: searchTerm } : undefined,\n limit: 50,\n },\n });\n\n const [updateOrganization] = useUpdateOrganizationMutation();\n\n const handleToggleStatus = async (id: string, currentStatus: string) => {\n const newStatus = currentStatus === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE';\n await updateOrganization({\n variables: {\n id,\n input: { status: newStatus },\n },\n });\n refetch();\n };\n\n const organizations = data?.allOrganizations?.items || [];\n\n return (\n <PageLayout\n title=\"Organizations\"\n description=\"Manage and monitor platform organizations\"\n actions={\n <Button>\n <Plus className=\"mr-2 h-4 w-4\" />\n Create Organization\n </Button>\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input\n placeholder=\"Search organizations...\"\n className=\"pl-9\"\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n </div>\n\n {loading ? (\n <div className=\"flex h-64 items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n ) : organizations.length > 0 ? (\n <div className=\"rounded-md border\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b bg-muted/50 text-left\">\n <th className=\"p-4 font-medium\">Name</th>\n <th className=\"p-4 font-medium\">Slug</th>\n <th className=\"p-4 font-medium\">Status</th>\n <th className=\"p-4 font-medium\">Created At</th>\n <th className=\"p-4 font-medium text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {organizations.map((org: any) => (\n <tr key={org.id} className=\"border-b transition-colors hover:bg-muted/50\">\n <td className=\"p-4 font-medium\">{org.name}</td>\n <td className=\"p-4 text-muted-foreground\">{org.slug}</td>\n <td className=\"p-4\">\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-semibold ${\n org.status === 'ACTIVE'\n ? 'bg-green-100 text-green-800'\n : 'bg-red-100 text-red-800'\n }`}\n >\n {org.status}\n </span>\n </td>\n <td className=\"p-4 text-muted-foreground\">\n {new Date(org.createdAt).toLocaleDateString()}\n </td>\n <td className=\"p-4 text-right\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => handleToggleStatus(org.id, org.status)}\n >\n Toggle Status\n </Button>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n ) : (\n <EmptyState\n icon={<Building className=\"h-8 w-8\" />}\n title=\"No organizations found\"\n description=\"Organizations will appear here when they are created\"\n />\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,IAAwB;CACtC,IAAM,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,EAAE,SAAM,YAAS,eAAY,EAA0B,EAC3D,WAAW;EACT,QAAQ,IAAa,EAAE,QAAQ,GAAY,GAAG,KAAA;EAC9C,OAAO;EACR,EACF,CAAC,EAEI,CAAC,KAAsB,GAA+B,EAEtD,IAAqB,OAAO,GAAY,MAA0B;AAQtE,EANA,MAAM,EAAmB,EACvB,WAAW;GACT;GACA,OAAO,EAAE,QAJK,MAAkB,WAAW,aAAa,UAI5B;GAC7B,EACF,CAAC,EACF,GAAS;IAGL,IAAgB,GAAM,kBAAkB,SAAS,EAAE;AAEzD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,sBAE1B,EAAA,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,0EAA2E,CAAA,EAC7F,kBAAC,GAAD;MACE,aAAY;MACZ,WAAU;MACV,OAAO;MACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;MAC9C,CAAA,CACE;;IACF,CAAA,EAEL,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;IAC9D,CAAA,GACJ,EAAc,SAAS,IACzB,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,MAAD;QAAI,WAAU;kBAAkB;QAAS,CAAA;OACzC,kBAAC,MAAD;QAAI,WAAU;kBAAkB;QAAS,CAAA;OACzC,kBAAC,MAAD;QAAI,WAAU;kBAAkB;QAAW,CAAA;OAC3C,kBAAC,MAAD;QAAI,WAAU;kBAAkB;QAAe,CAAA;OAC/C,kBAAC,MAAD;QAAI,WAAU;kBAA6B;QAAY,CAAA;OACpD;SACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAc,KAAK,MAClB,kBAAC,MAAD;MAAiB,WAAU;gBAA3B;OACE,kBAAC,MAAD;QAAI,WAAU;kBAAmB,EAAI;QAAU,CAAA;OAC/C,kBAAC,MAAD;QAAI,WAAU;kBAA6B,EAAI;QAAU,CAAA;OACzD,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,QAAD;SACE,WAAW,6EACT,EAAI,WAAW,WACX,gCACA;mBAGL,EAAI;SACA,CAAA;QACJ,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACX,IAAI,KAAK,EAAI,UAAU,CAAC,oBAAoB;QAC1C,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,GAAD;SACE,SAAQ;SACR,MAAK;SACL,eAAe,EAAmB,EAAI,IAAI,EAAI,OAAO;mBACtD;SAEQ,CAAA;QACN,CAAA;OACF;QA1BI,EAAI,GA0BR,CACL,EACI,CAAA,CACF;;IACJ,CAAA,GAEN,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;IACtC,OAAM;IACN,aAAY;IACZ,CAAA,CAEA;;EACK,CAAA"}
|
|
@@ -19,7 +19,7 @@ function g() {
|
|
|
19
19
|
let [c, g] = l(1), { data: _, loading: v, error: y } = s({ variables: {
|
|
20
20
|
limit: 20,
|
|
21
21
|
offset: (c - 1) * 20
|
|
22
|
-
} }), b = _?.
|
|
22
|
+
} }), b = _?.allWorkspaces.items ?? [], x = _?.allWorkspaces.total ?? 0, S = Math.ceil(x / 20);
|
|
23
23
|
return /* @__PURE__ */ p(i, {
|
|
24
24
|
title: "Workspaces",
|
|
25
25
|
description: "Manage and monitor platform workspaces",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkspacesListPage.js","names":[],"sources":["../../../src/admin/pages/WorkspacesListPage.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Layout, Search, Plus } from 'lucide-react';\nimport { PageLayout, Input, Button, DataTable, Pagination } from '../components';\nimport { useListWorkspacesQuery } from '../../generated/global-operations';\nimport { WorkspaceStatus } from '../../generated/global-types';\nimport { Badge } from '../components/ui';\nimport { cn } from '../utils/cn';\n\nconst STATUS_STYLING: Record<WorkspaceStatus, string> = {\n [WorkspaceStatus.Active]: 'bg-success/10 text-success border-success/20',\n [WorkspaceStatus.Suspended]: 'bg-destructive/10 text-destructive border-destructive/20',\n [WorkspaceStatus.Archived]: 'bg-muted text-muted-foreground border-muted',\n [WorkspaceStatus.Deleted]: 'bg-destructive/10 text-destructive border-destructive/20',\n};\n\nexport function WorkspacesListPage() {\n const [page, setPage] = useState(1);\n const pageSize = 20;\n\n const { data, loading, error } = useListWorkspacesQuery({\n variables: {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n },\n });\n\n const workspaces = data?.
|
|
1
|
+
{"version":3,"file":"WorkspacesListPage.js","names":[],"sources":["../../../src/admin/pages/WorkspacesListPage.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { Layout, Search, Plus } from 'lucide-react';\nimport { PageLayout, Input, Button, DataTable, Pagination } from '../components';\nimport { useListWorkspacesQuery } from '../../generated/global-operations';\nimport { WorkspaceStatus } from '../../generated/global-types';\nimport { Badge } from '../components/ui';\nimport { cn } from '../utils/cn';\n\nconst STATUS_STYLING: Record<WorkspaceStatus, string> = {\n [WorkspaceStatus.Active]: 'bg-success/10 text-success border-success/20',\n [WorkspaceStatus.Suspended]: 'bg-destructive/10 text-destructive border-destructive/20',\n [WorkspaceStatus.Archived]: 'bg-muted text-muted-foreground border-muted',\n [WorkspaceStatus.Deleted]: 'bg-destructive/10 text-destructive border-destructive/20',\n};\n\nexport function WorkspacesListPage() {\n const [page, setPage] = useState(1);\n const pageSize = 20;\n\n const { data, loading, error } = useListWorkspacesQuery({\n variables: {\n limit: pageSize,\n offset: (page - 1) * pageSize,\n },\n });\n\n const workspaces = data?.allWorkspaces.items ?? [];\n const total = data?.allWorkspaces.total ?? 0;\n const totalPages = Math.ceil(total / pageSize);\n\n const columns = [\n {\n key: 'name',\n header: 'Workspace Name',\n render: (w: any) => <div className=\"font-medium text-foreground\">{w.name}</div>,\n },\n {\n key: 'id',\n header: 'ID',\n render: (w: any) => <code className=\"text-xs text-muted-foreground\">{w.id}</code>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (w: any) => (\n <Badge className={cn(STATUS_STYLING[w.status as WorkspaceStatus])} variant=\"outline\">\n {w.status}\n </Badge>\n ),\n },\n ];\n\n return (\n <PageLayout\n title=\"Workspaces\"\n description=\"Manage and monitor platform workspaces\"\n actions={\n <Button>\n <Plus className=\"mr-2 h-4 w-4\" />\n Create Workspace\n </Button>\n }\n >\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground\" />\n <Input placeholder=\"Search workspaces...\" className=\"pl-9\" />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n data={workspaces}\n keyExtractor={(w) => w.id}\n loading={loading}\n emptyState={\n <div className=\"flex flex-col items-center justify-center py-12 text-center\">\n <Layout className=\"h-12 w-12 text-muted-foreground/50 mb-4\" />\n <h3 className=\"text-lg font-medium\">No workspaces found</h3>\n <p className=\"text-muted-foreground\">\n Workspaces will appear here when they are created.\n </p>\n </div>\n }\n />\n\n {total > pageSize && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={pageSize}\n onPageChange={setPage}\n />\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAQA,IAAM,IAAkD;EACrD,EAAgB,SAAS;EACzB,EAAgB,YAAY;EAC5B,EAAgB,WAAW;EAC3B,EAAgB,UAAU;CAC5B;AAED,SAAgB,IAAqB;CACnC,IAAM,CAAC,GAAM,KAAW,EAAS,EAAE,EAG7B,EAAE,SAAM,YAAS,aAAU,EAAuB,EACtD,WAAW;EACT,OAAO;EACP,SAAS,IAAO,KAAK;EACtB,EACF,CAAC,EAEI,IAAa,GAAM,cAAc,SAAS,EAAE,EAC5C,IAAQ,GAAM,cAAc,SAAS,GACrC,IAAa,KAAK,KAAK,IAAQ,GAAS;AAwB9C,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,mBAE1B,EAAA,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,0EAA2E,CAAA,EAC7F,kBAAC,GAAD;OAAO,aAAY;OAAuB,WAAU;OAAS,CAAA,CACzD;;KACF,CAAA;IAEN,kBAAC,GAAD;KACW,SA1CD;MACd;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MAAW,kBAAC,OAAD;QAAK,WAAU;kBAA+B,EAAE;QAAW,CAAA;OAChF;MACD;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MAAW,kBAAC,QAAD;QAAM,WAAU;kBAAiC,EAAE;QAAU,CAAA;OAClF;MACD;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MACP,kBAAC,GAAD;QAAO,WAAW,EAAG,EAAe,EAAE,QAA2B;QAAE,SAAQ;kBACxE,EAAE;QACG,CAAA;OAEX;MACF;KAuBO,MAAM;KACN,eAAe,MAAM,EAAE;KACd;KACT,YACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAQ,WAAU,2CAA4C,CAAA;OAC9D,kBAAC,MAAD;QAAI,WAAU;kBAAsB;QAAwB,CAAA;OAC5D,kBAAC,KAAD;QAAG,WAAU;kBAAwB;QAEjC,CAAA;OACA;;KAER,CAAA;IAED,IAAQ,MACP,kBAAC,GAAD;KACQ;KACM;KACZ,YAAY;KACF;KACV,cAAc;KACd,CAAA;IAEA;;EACK,CAAA"}
|
|
@@ -455,7 +455,7 @@ function F(e) {
|
|
|
455
455
|
}
|
|
456
456
|
var I = e`
|
|
457
457
|
query GetDashboardOrganizationCount {
|
|
458
|
-
|
|
458
|
+
allOrganizations(limit: 1, offset: 0) {
|
|
459
459
|
total
|
|
460
460
|
}
|
|
461
461
|
}
|
|
@@ -469,7 +469,7 @@ function L(e) {
|
|
|
469
469
|
}
|
|
470
470
|
var R = e`
|
|
471
471
|
query GetDashboardActiveWorkspaceCount {
|
|
472
|
-
|
|
472
|
+
allWorkspaces(filter: {status: ACTIVE}, limit: 1, offset: 0) {
|
|
473
473
|
total
|
|
474
474
|
}
|
|
475
475
|
}
|
|
@@ -620,7 +620,7 @@ function J(e) {
|
|
|
620
620
|
}
|
|
621
621
|
var Y = e`
|
|
622
622
|
query ListOrganizations($filter: OrganizationFilterInput, $limit: Int, $offset: Int) {
|
|
623
|
-
|
|
623
|
+
allOrganizations(filter: $filter, limit: $limit, offset: $offset) {
|
|
624
624
|
items {
|
|
625
625
|
id
|
|
626
626
|
name
|
|
@@ -1006,7 +1006,7 @@ function we(e) {
|
|
|
1006
1006
|
}
|
|
1007
1007
|
var Te = e`
|
|
1008
1008
|
query ListWorkspaces($limit: Int, $offset: Int) {
|
|
1009
|
-
|
|
1009
|
+
allWorkspaces(limit: $limit, offset: $offset) {
|
|
1010
1010
|
items {
|
|
1011
1011
|
id
|
|
1012
1012
|
name
|