@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.
Files changed (141) hide show
  1. package/dist/admin/pages/AuditLogsPage.js +96 -81
  2. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  3. package/dist/admin/pages/DashboardPage.js +2 -2
  4. package/dist/admin/pages/DashboardPage.js.map +1 -1
  5. package/dist/admin/pages/OrganizationsListPage.js +1 -1
  6. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  7. package/dist/admin/pages/WorkspacesListPage.js +1 -1
  8. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  9. package/dist/generated/global-operations.js +4 -4
  10. package/dist/generated/global-operations.js.map +1 -1
  11. package/package.json +2 -2
  12. package/dist/admin/AdminRoot.d.ts +0 -31
  13. package/dist/admin/AdminRoot.d.ts.map +0 -1
  14. package/dist/admin/AdminRoutes.d.ts +0 -29
  15. package/dist/admin/AdminRoutes.d.ts.map +0 -1
  16. package/dist/admin/components/AccessDenied.d.ts +0 -8
  17. package/dist/admin/components/AccessDenied.d.ts.map +0 -1
  18. package/dist/admin/components/ActivityFeed.d.ts +0 -20
  19. package/dist/admin/components/ActivityFeed.d.ts.map +0 -1
  20. package/dist/admin/components/Charts.d.ts +0 -33
  21. package/dist/admin/components/Charts.d.ts.map +0 -1
  22. package/dist/admin/components/ConfirmDialog.d.ts +0 -45
  23. package/dist/admin/components/ConfirmDialog.d.ts.map +0 -1
  24. package/dist/admin/components/DataTable.d.ts +0 -64
  25. package/dist/admin/components/DataTable.d.ts.map +0 -1
  26. package/dist/admin/components/FormSheet.d.ts +0 -79
  27. package/dist/admin/components/FormSheet.d.ts.map +0 -1
  28. package/dist/admin/components/MetricCard.d.ts +0 -40
  29. package/dist/admin/components/MetricCard.d.ts.map +0 -1
  30. package/dist/admin/components/PageLayout.d.ts +0 -34
  31. package/dist/admin/components/PageLayout.d.ts.map +0 -1
  32. package/dist/admin/components/PermissionGuard.d.ts +0 -12
  33. package/dist/admin/components/PermissionGuard.d.ts.map +0 -1
  34. package/dist/admin/components/RowActions.d.ts +0 -41
  35. package/dist/admin/components/RowActions.d.ts.map +0 -1
  36. package/dist/admin/components/StatusBadge.d.ts +0 -35
  37. package/dist/admin/components/StatusBadge.d.ts.map +0 -1
  38. package/dist/admin/components/TenantSsoPanel.d.ts +0 -7
  39. package/dist/admin/components/TenantSsoPanel.d.ts.map +0 -1
  40. package/dist/admin/components/Toast.d.ts +0 -26
  41. package/dist/admin/components/Toast.d.ts.map +0 -1
  42. package/dist/admin/components/forms/SubscriptionForm.d.ts +0 -57
  43. package/dist/admin/components/forms/SubscriptionForm.d.ts.map +0 -1
  44. package/dist/admin/components/forms/TenantForm.d.ts +0 -68
  45. package/dist/admin/components/forms/TenantForm.d.ts.map +0 -1
  46. package/dist/admin/components/forms/TicketForm.d.ts +0 -67
  47. package/dist/admin/components/forms/TicketForm.d.ts.map +0 -1
  48. package/dist/admin/components/forms/UserForm.d.ts +0 -57
  49. package/dist/admin/components/forms/UserForm.d.ts.map +0 -1
  50. package/dist/admin/components/forms/index.d.ts +0 -5
  51. package/dist/admin/components/forms/index.d.ts.map +0 -1
  52. package/dist/admin/components/index.d.ts +0 -15
  53. package/dist/admin/components/index.d.ts.map +0 -1
  54. package/dist/admin/components/ui.d.ts +0 -52
  55. package/dist/admin/components/ui.d.ts.map +0 -1
  56. package/dist/admin/constants/index.d.ts +0 -75
  57. package/dist/admin/constants/index.d.ts.map +0 -1
  58. package/dist/admin/constants/permissions.d.ts +0 -29
  59. package/dist/admin/constants/permissions.d.ts.map +0 -1
  60. package/dist/admin/hooks/useAdminPermissions.d.ts +0 -32
  61. package/dist/admin/hooks/useAdminPermissions.d.ts.map +0 -1
  62. package/dist/admin/index.d.ts +0 -31
  63. package/dist/admin/index.d.ts.map +0 -1
  64. package/dist/admin/pages/AuditLogsPage.d.ts +0 -5
  65. package/dist/admin/pages/AuditLogsPage.d.ts.map +0 -1
  66. package/dist/admin/pages/BillingPage.d.ts +0 -2
  67. package/dist/admin/pages/BillingPage.d.ts.map +0 -1
  68. package/dist/admin/pages/CommunicationsPage.d.ts +0 -2
  69. package/dist/admin/pages/CommunicationsPage.d.ts.map +0 -1
  70. package/dist/admin/pages/DashboardPage.d.ts +0 -5
  71. package/dist/admin/pages/DashboardPage.d.ts.map +0 -1
  72. package/dist/admin/pages/FeatureFlagsPage.d.ts +0 -2
  73. package/dist/admin/pages/FeatureFlagsPage.d.ts.map +0 -1
  74. package/dist/admin/pages/OrganizationsListPage.d.ts +0 -2
  75. package/dist/admin/pages/OrganizationsListPage.d.ts.map +0 -1
  76. package/dist/admin/pages/ProductsPage.d.ts +0 -5
  77. package/dist/admin/pages/ProductsPage.d.ts.map +0 -1
  78. package/dist/admin/pages/ReferralsPage.d.ts +0 -2
  79. package/dist/admin/pages/ReferralsPage.d.ts.map +0 -1
  80. package/dist/admin/pages/RolesPermissionsPage.d.ts +0 -2
  81. package/dist/admin/pages/RolesPermissionsPage.d.ts.map +0 -1
  82. package/dist/admin/pages/SREPage.d.ts +0 -5
  83. package/dist/admin/pages/SREPage.d.ts.map +0 -1
  84. package/dist/admin/pages/SandboxSettingsPage.d.ts +0 -7
  85. package/dist/admin/pages/SandboxSettingsPage.d.ts.map +0 -1
  86. package/dist/admin/pages/SettingsPage.d.ts +0 -5
  87. package/dist/admin/pages/SettingsPage.d.ts.map +0 -1
  88. package/dist/admin/pages/SyncFailuresPage.d.ts +0 -14
  89. package/dist/admin/pages/SyncFailuresPage.d.ts.map +0 -1
  90. package/dist/admin/pages/TenantDetailPage.d.ts +0 -5
  91. package/dist/admin/pages/TenantDetailPage.d.ts.map +0 -1
  92. package/dist/admin/pages/TenantFormRoutePage.d.ts +0 -6
  93. package/dist/admin/pages/TenantFormRoutePage.d.ts.map +0 -1
  94. package/dist/admin/pages/TenantsListPage.d.ts +0 -5
  95. package/dist/admin/pages/TenantsListPage.d.ts.map +0 -1
  96. package/dist/admin/pages/UsersListPage.d.ts +0 -5
  97. package/dist/admin/pages/UsersListPage.d.ts.map +0 -1
  98. package/dist/admin/pages/WaitlistPage.d.ts +0 -2
  99. package/dist/admin/pages/WaitlistPage.d.ts.map +0 -1
  100. package/dist/admin/pages/WorkflowsPage.d.ts +0 -2
  101. package/dist/admin/pages/WorkflowsPage.d.ts.map +0 -1
  102. package/dist/admin/pages/WorkspacesListPage.d.ts +0 -2
  103. package/dist/admin/pages/WorkspacesListPage.d.ts.map +0 -1
  104. package/dist/admin/pages/index.d.ts +0 -21
  105. package/dist/admin/pages/index.d.ts.map +0 -1
  106. package/dist/admin/providers/AdminProvider.d.ts +0 -13
  107. package/dist/admin/providers/AdminProvider.d.ts.map +0 -1
  108. package/dist/admin/schemas/index.d.ts +0 -409
  109. package/dist/admin/schemas/index.d.ts.map +0 -1
  110. package/dist/admin/store/adminStore.d.ts +0 -28
  111. package/dist/admin/store/adminStore.d.ts.map +0 -1
  112. package/dist/admin/types/index.d.ts +0 -325
  113. package/dist/admin/types/index.d.ts.map +0 -1
  114. package/dist/admin/utils/cn.d.ts +0 -7
  115. package/dist/admin/utils/cn.d.ts.map +0 -1
  116. package/dist/admin/utils/directActivityGraphql.d.ts +0 -11
  117. package/dist/admin/utils/directActivityGraphql.d.ts.map +0 -1
  118. package/dist/admin/utils/formatters.d.ts +0 -33
  119. package/dist/admin/utils/formatters.d.ts.map +0 -1
  120. package/dist/admin/utils/gatewayGraphql.d.ts +0 -9
  121. package/dist/admin/utils/gatewayGraphql.d.ts.map +0 -1
  122. package/dist/admin/utils/i18n.d.ts +0 -6
  123. package/dist/admin/utils/i18n.d.ts.map +0 -1
  124. package/dist/admin/utils/mappers.d.ts +0 -15
  125. package/dist/admin/utils/mappers.d.ts.map +0 -1
  126. package/dist/admin/utils/navigation.d.ts +0 -9
  127. package/dist/admin/utils/navigation.d.ts.map +0 -1
  128. package/dist/admin/utils/tenantEvents.d.ts +0 -11
  129. package/dist/admin/utils/tenantEvents.d.ts.map +0 -1
  130. package/dist/dev/main.d.ts +0 -2
  131. package/dist/dev/main.d.ts.map +0 -1
  132. package/dist/generated/global-operations.d.ts +0 -3001
  133. package/dist/generated/global-operations.d.ts.map +0 -1
  134. package/dist/generated/global-types.d.ts +0 -22700
  135. package/dist/generated/global-types.d.ts.map +0 -1
  136. package/dist/generated/wspace-operations.d.ts +0 -132
  137. package/dist/generated/wspace-operations.d.ts.map +0 -1
  138. package/dist/generated/wspace-types.d.ts +0 -57851
  139. package/dist/generated/wspace-types.d.ts.map +0 -1
  140. package/dist/index.d.ts +0 -6
  141. package/dist/index.d.ts.map +0 -1
@@ -1,42 +1,43 @@
1
1
  import { useAdminPermissions as e } from "../hooks/useAdminPermissions.js";
2
- import { Button as t, EmptyState as n, Input as r } from "../components/ui.js";
3
- import { PageLayout as i } from "../components/PageLayout.js";
4
- import { formatRelativeTime as a } from "../utils/formatters.js";
5
- import { DataTable as o, Pagination as s } from "../components/DataTable.js";
6
- import { PermissionGuard as c } from "../components/PermissionGuard.js";
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 l } from "../../generated/global-operations.js";
9
- import { useCallback as u, useMemo as d, useState as f } from "react";
10
- import { Database as p, Download as m, FileText as h, Key as g, Search as _, Settings as v, Shield as y, User as b } from "lucide-react";
11
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
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 C = 20, w = {
14
- auth: /* @__PURE__ */ x(b, { className: "h-3 w-3" }),
15
- user: /* @__PURE__ */ x(b, { className: "h-3 w-3" }),
16
- settings: /* @__PURE__ */ x(v, { className: "h-3 w-3" }),
17
- rbac: /* @__PURE__ */ x(y, { className: "h-3 w-3" }),
18
- data: /* @__PURE__ */ x(p, { className: "h-3 w-3" }),
19
- api: /* @__PURE__ */ x(g, { className: "h-3 w-3" })
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 T(e) {
22
- return w[e.split(".")[0]] ?? /* @__PURE__ */ x(h, { className: "h-3 w-3" });
22
+ function w(e) {
23
+ return C[e.split(".")[0]] ?? /* @__PURE__ */ y(p, { className: "h-3 w-3" });
23
24
  }
24
- function E() {
25
- let { canViewAuditLogs: p, isLoading: g } = e(), [v, y] = f(1), [b, w] = f(""), [E, D] = f(""), O = g || !p, { data: k, loading: A, error: j } = l({
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: E || void 0,
28
- limit: C,
29
- offset: (v - 1) * C
28
+ resource: T || void 0,
29
+ limit: S,
30
+ offset: (g - 1) * S
30
31
  },
31
32
  fetchPolicy: "cache-and-network",
32
- skip: O
33
- }), M = d(() => (k?.auditLogs?.edges ?? []).map((e) => e.node), [k]), N = d(() => {
34
- if (!b) return M;
35
- let e = b.toLowerCase();
36
- return M.filter((t) => t.action.toLowerCase().includes(e) || t.actorId.toLowerCase().includes(e) || t.resource.toLowerCase().includes(e) || t.tenantId.toLowerCase().includes(e));
37
- }, [M, b]), P = k?.auditLogs?.totalCount ?? 0, F = Math.ceil(P / C), I = u(() => {
38
- let e = N;
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(","))].join("\n"), i = new Blob([r], { type: "text/csv;charset=utf-8;" }), a = URL.createObjectURL(i), o = document.createElement("a"), s = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
71
- o.href = a, o.setAttribute("download", `audit-logs-${s}.csv`), document.body.appendChild(o), o.click(), document.body.removeChild(o), URL.revokeObjectURL(a);
72
- }, [N]);
73
- return /* @__PURE__ */ x(c, {
74
- allowed: p,
75
- isLoading: g,
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__ */ x(i, {
84
+ children: /* @__PURE__ */ y(r, {
79
85
  title: "Audit Logs",
80
- description: `Security and compliance audit trail${P ? ` · ${P} total` : ""}`,
81
- actions: /* @__PURE__ */ S(t, {
82
- variant: "outline",
83
- onClick: I,
84
- disabled: A || N.length === 0,
85
- "data-testid": "audit-logs-export-csv",
86
- children: [/* @__PURE__ */ x(m, { className: "mr-2 h-4 w-4" }), "Export CSV"]
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__ */ S("div", {
103
+ children: /* @__PURE__ */ b("div", {
89
104
  className: "space-y-4",
90
105
  children: [
91
- /* @__PURE__ */ S("div", {
106
+ /* @__PURE__ */ b("div", {
92
107
  className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
93
- children: [/* @__PURE__ */ S("div", {
108
+ children: [/* @__PURE__ */ b("div", {
94
109
  className: "relative max-w-sm flex-1",
95
- children: [/* @__PURE__ */ x(_, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" }), /* @__PURE__ */ x(r, {
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: b,
98
- onChange: (e) => w(e.target.value),
112
+ value: v,
113
+ onChange: (e) => C(e.target.value),
99
114
  className: "pl-9"
100
115
  })]
101
- }), /* @__PURE__ */ x(r, {
116
+ }), /* @__PURE__ */ y(n, {
102
117
  placeholder: "Filter by resource (e.g. tenant, user)...",
103
- value: E,
118
+ value: T,
104
119
  onChange: (e) => {
105
- y(1), D(e.target.value);
120
+ _(1), E(e.target.value);
106
121
  },
107
122
  className: "max-w-xs"
108
123
  })]
109
124
  }),
110
- j && /* @__PURE__ */ S("div", {
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: ", j.message]
127
+ children: ["Failed to load audit logs: ", A.message]
113
128
  }),
114
- /* @__PURE__ */ x(o, {
129
+ /* @__PURE__ */ y(a, {
115
130
  columns: [
116
131
  {
117
132
  key: "action",
118
133
  header: "Action",
119
- render: (e) => /* @__PURE__ */ S("div", {
134
+ render: (e) => /* @__PURE__ */ b("div", {
120
135
  className: "flex items-center gap-2",
121
- children: [/* @__PURE__ */ x("span", {
136
+ children: [/* @__PURE__ */ y("span", {
122
137
  className: "text-muted-foreground",
123
- children: T(e.action)
124
- }), /* @__PURE__ */ x("span", {
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__ */ S("div", {
149
+ render: (e) => /* @__PURE__ */ b("div", {
135
150
  className: "min-w-0",
136
- children: [/* @__PURE__ */ x("div", {
151
+ children: [/* @__PURE__ */ y("div", {
137
152
  className: "truncate text-sm",
138
153
  children: e.actorId
139
- }), /* @__PURE__ */ x("div", {
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__ */ S("div", {
164
+ render: (e) => /* @__PURE__ */ b("div", {
150
165
  className: "min-w-0",
151
- children: [/* @__PURE__ */ x("div", {
166
+ children: [/* @__PURE__ */ y("div", {
152
167
  className: "truncate text-sm",
153
168
  children: e.resource
154
- }), e.resourceId && /* @__PURE__ */ S("div", {
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__ */ x("span", {
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__ */ x("span", {
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) => a(e.createdAt)
197
+ render: (e) => i(e.createdAt)
183
198
  }
184
199
  ],
185
- data: N,
200
+ data: M,
186
201
  keyExtractor: (e) => e.id,
187
- loading: A,
188
- emptyState: /* @__PURE__ */ x(n, {
189
- icon: /* @__PURE__ */ x(h, { className: "h-8 w-8" }),
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
- P > C && /* @__PURE__ */ x(s, {
195
- page: v,
196
- totalPages: F,
197
- totalItems: P,
198
- pageSize: C,
199
- onPageChange: y
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 { E as AuditLogsPage };
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?.organizations?.total ?? 0,
38
- workspaces: R?.workspaces?.total ?? 0,
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?.organizations?.total ?? 0,\n workspaces: workspaceData?.workspaces?.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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"}
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?.organizations?.items || [];
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?.organizations?.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,eAAe,SAAS,EAAE;AAEtD,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"}
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 = _?.workspaces.items ?? [], x = _?.workspaces.total ?? 0, S = Math.ceil(x / 20);
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?.workspaces.items ?? [];\n const total = data?.workspaces.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,WAAW,SAAS,EAAE,EACzC,IAAQ,GAAM,WAAW,SAAS,GAClC,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"}
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
- organizations(limit: 1, offset: 0) {
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
- workspaces(filter: {status: ACTIVE}, limit: 1, offset: 0) {
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
- organizations(filter: $filter, limit: $limit, offset: $offset) {
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
- workspaces(limit: $limit, offset: $offset) {
1009
+ allWorkspaces(limit: $limit, offset: $offset) {
1010
1010
  items {
1011
1011
  id
1012
1012
  name