@alepha/ui 0.15.3 → 0.15.5

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 (99) hide show
  1. package/dist/admin/AdminApiKeys-DsmGnHNh.js +3 -0
  2. package/dist/admin/AdminApiKeys-GMORg-1l.js +442 -0
  3. package/dist/admin/AdminApiKeys-GMORg-1l.js.map +1 -0
  4. package/dist/admin/AdminAudits-8SM96viT.js +3 -0
  5. package/dist/admin/{AdminAudits-Oh7iAfQa.js → AdminAudits-pkWrjq1Z.js} +2 -2
  6. package/dist/admin/{AdminAudits-Oh7iAfQa.js.map → AdminAudits-pkWrjq1Z.js.map} +1 -1
  7. package/dist/admin/AdminFiles-B56ocq4H.js +3 -0
  8. package/dist/admin/{AdminFiles-Cu8GHgQ3.js → AdminFiles-WeQbsCsl.js} +2 -2
  9. package/dist/admin/{AdminFiles-Cu8GHgQ3.js.map → AdminFiles-WeQbsCsl.js.map} +1 -1
  10. package/dist/admin/AdminJobs-B-q9iGO3.js +697 -0
  11. package/dist/admin/AdminJobs-B-q9iGO3.js.map +1 -0
  12. package/dist/admin/AdminJobs-CED1syCn.js +3 -0
  13. package/dist/admin/AdminLayout-BX4FIpXv.css +143 -0
  14. package/dist/admin/AdminLayout-BX4FIpXv.css.map +1 -0
  15. package/dist/admin/{AdminLayout-QJLIesuG.js → AdminLayout-BqZiXx4H.js} +3 -2
  16. package/dist/admin/AdminLayout-BqZiXx4H.js.map +1 -0
  17. package/dist/admin/AdminNotifications-B0B1rdc4.js +3 -0
  18. package/dist/admin/{AdminNotifications-CgYkBuG_.js → AdminNotifications-Ds5Un0NJ.js} +2 -2
  19. package/dist/admin/{AdminNotifications-CgYkBuG_.js.map → AdminNotifications-Ds5Un0NJ.js.map} +1 -1
  20. package/dist/admin/{AdminParameters-Cl-R0nXt.js → AdminParameters-BU3lATdJ.js} +1 -1
  21. package/dist/admin/{AdminParameters-hjNG_KXb.js → AdminParameters-CfDUpc78.js} +4 -4
  22. package/dist/admin/{AdminParameters-hjNG_KXb.js.map → AdminParameters-CfDUpc78.js.map} +1 -1
  23. package/dist/admin/AdminSessions-BDGK2MS6.js +3 -0
  24. package/dist/admin/{AdminSessions-Bey9cuy1.js → AdminSessions-DzIOxM3b.js} +2 -2
  25. package/dist/admin/{AdminSessions-Bey9cuy1.js.map → AdminSessions-DzIOxM3b.js.map} +1 -1
  26. package/dist/admin/{AdminUserAudits-C7AN9jx7.js → AdminUserAudits-CiUPN2BC.js} +2 -2
  27. package/dist/admin/{AdminUserAudits-C7AN9jx7.js.map → AdminUserAudits-CiUPN2BC.js.map} +1 -1
  28. package/dist/admin/{AdminUserAudits-Cp_ERd2g.js → AdminUserAudits-Cj79gENT.js} +1 -1
  29. package/dist/admin/{AdminUserCreate-BVIm4JdN.js → AdminUserCreate-BwQKr4xE.js} +2 -2
  30. package/dist/admin/{AdminUserCreate-BVIm4JdN.js.map → AdminUserCreate-BwQKr4xE.js.map} +1 -1
  31. package/dist/admin/{AdminUserCreate-C1aInRDk.js → AdminUserCreate-Cq-mUmBs.js} +1 -1
  32. package/dist/admin/{AdminUserDetails-Dcn3OwMC.js → AdminUserDetails-DRjVAPFd.js} +1 -1
  33. package/dist/admin/{AdminUserDetails-yM4x8JE6.js → AdminUserDetails-uqtC5aJ1.js} +2 -2
  34. package/dist/admin/{AdminUserDetails-yM4x8JE6.js.map → AdminUserDetails-uqtC5aJ1.js.map} +1 -1
  35. package/dist/admin/{AdminUserLayout-gb-nbggz.js → AdminUserLayout-CGzmHHby.js} +1 -1
  36. package/dist/admin/{AdminUserLayout-BnfBC1gD.js → AdminUserLayout-CiPay35T.js} +2 -2
  37. package/dist/admin/{AdminUserLayout-BnfBC1gD.js.map → AdminUserLayout-CiPay35T.js.map} +1 -1
  38. package/dist/admin/{AdminUserSessions-kmkXG-xf.js → AdminUserSessions-DAE8Nf1F.js} +2 -2
  39. package/dist/admin/{AdminUserSessions-kmkXG-xf.js.map → AdminUserSessions-DAE8Nf1F.js.map} +1 -1
  40. package/dist/admin/AdminUserSessions-DcdzuNZ9.js +3 -0
  41. package/dist/admin/AdminUserSettings-D7V6-ceX.js +3 -0
  42. package/dist/admin/{AdminUserSettings-DZ9iWhJW.js → AdminUserSettings-EbahaV2a.js} +2 -2
  43. package/dist/admin/{AdminUserSettings-DZ9iWhJW.js.map → AdminUserSettings-EbahaV2a.js.map} +1 -1
  44. package/dist/admin/AdminUsers-D9nyzGqQ.js +3 -0
  45. package/dist/admin/{AdminUsers-D6Y5K8Am.js → AdminUsers-Dcjh0KNW.js} +2 -2
  46. package/dist/admin/{AdminUsers-D6Y5K8Am.js.map → AdminUsers-Dcjh0KNW.js.map} +1 -1
  47. package/dist/admin/index.d.ts +39 -51
  48. package/dist/admin/index.d.ts.map +1 -1
  49. package/dist/admin/index.js +52 -169
  50. package/dist/admin/index.js.map +1 -1
  51. package/dist/auth/AuthLayout-BaD7RD2h.css +143 -0
  52. package/dist/auth/AuthLayout-BaD7RD2h.css.map +1 -0
  53. package/dist/auth/AuthLayout-Dj5K4SIN.js.map +1 -1
  54. package/dist/auth/index.d.ts +9 -1
  55. package/dist/auth/index.d.ts.map +1 -1
  56. package/dist/auth/index.js +1 -2
  57. package/dist/auth/index.js.map +1 -1
  58. package/dist/core/index.d.ts +13 -21
  59. package/dist/core/index.d.ts.map +1 -1
  60. package/dist/core/index.js +26 -38
  61. package/dist/core/index.js.map +1 -1
  62. package/dist/demo/{DemoLogin-S-b15cmE.js → DemoLogin-CvCG2WVh.js} +3 -1
  63. package/dist/demo/{DemoLogin-S-b15cmE.js.map → DemoLogin-CvCG2WVh.js.map} +1 -1
  64. package/dist/demo/{DemoRegister-B29MdAaZ.js → DemoRegister-CmeHbOAs.js} +3 -1
  65. package/dist/demo/{DemoRegister-B29MdAaZ.js.map → DemoRegister-CmeHbOAs.js.map} +1 -1
  66. package/dist/demo/{DemoResetPassword-CPTy88iK.js → DemoResetPassword-CKO5iA_6.js} +3 -1
  67. package/dist/demo/{DemoResetPassword-CPTy88iK.js.map → DemoResetPassword-CKO5iA_6.js.map} +1 -1
  68. package/dist/demo/index.js +3 -3
  69. package/package.json +3 -3
  70. package/src/admin/AdminRouter.ts +34 -0
  71. package/src/admin/components/AdminLayout.tsx +2 -0
  72. package/src/admin/components/jobs/AdminJobs.tsx +733 -119
  73. package/src/admin/components/keys/AdminApiKeys.tsx +537 -0
  74. package/src/admin/components/parameters/AdminParameters.tsx +2 -3
  75. package/src/admin/index.ts +3 -5
  76. package/src/auth/AuthRouter.ts +1 -2
  77. package/src/auth/components/AuthLayout.tsx +1 -0
  78. package/src/core/components/buttons/ActionButton.tsx +15 -2
  79. package/src/core/components/buttons/DarkModeButton.css +6 -0
  80. package/src/core/components/buttons/DarkModeButton.tsx +18 -71
  81. package/src/core/components/buttons/LanguageButton.tsx +2 -7
  82. package/src/core/components/buttons/ThemeButton.tsx +2 -6
  83. package/src/core/components/layout/AdminShell.tsx +17 -1
  84. package/src/core/components/layout/AppBar.tsx +5 -8
  85. package/src/core/index.ts +0 -1
  86. package/src/core/styles.css +1 -0
  87. package/src/demo/components/auth/DemoLogin.tsx +2 -0
  88. package/src/demo/components/auth/DemoRegister.tsx +2 -0
  89. package/src/demo/components/auth/DemoResetPassword.tsx +2 -0
  90. package/dist/admin/AdminAudits-BU-p1g7A.js +0 -3
  91. package/dist/admin/AdminFiles-Bg9feLFH.js +0 -3
  92. package/dist/admin/AdminLayout-BfeFXiul.js +0 -3
  93. package/dist/admin/AdminLayout-QJLIesuG.js.map +0 -1
  94. package/dist/admin/AdminNotifications-DmfGPqHe.js +0 -3
  95. package/dist/admin/AdminSessions-Cn4_jB04.js +0 -3
  96. package/dist/admin/AdminUserSessions-rvA0ztxn.js +0 -3
  97. package/dist/admin/AdminUserSettings-Dg-wTRzN.js +0 -3
  98. package/dist/admin/AdminUsers-RCaxccEW.js +0 -3
  99. package/src/admin/MainRouter.ts +0 -23
@@ -0,0 +1,3 @@
1
+ import { t as AdminApiKeys_default } from "./AdminApiKeys-GMORg-1l.js";
2
+
3
+ export { AdminApiKeys_default as default };
@@ -0,0 +1,442 @@
1
+ import { ActionButton, ClipboardButton, DataTable, Flex, Text, useDialog, useToast } from "@alepha/ui";
2
+ import { t } from "alepha";
3
+ import { IconCalendarOff, IconCheck, IconClock, IconKey, IconNetwork, IconShieldCheck, IconShieldOff, IconTrash, IconUser } from "@tabler/icons-react";
4
+ import { useRouter } from "alepha/react/router";
5
+ import { ActionIcon, Badge, Box, Code, Group, Paper, RingProgress, SimpleGrid, Stack, ThemeIcon, Tooltip } from "@mantine/core";
6
+ import { useClient } from "alepha/react";
7
+ import { useI18n } from "alepha/react/i18n";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { useCallback, useState } from "react";
10
+
11
+ //#region ../../src/admin/components/keys/AdminApiKeys.tsx
12
+ const getKeyStatus = (key) => {
13
+ if (key.revokedAt) return "revoked";
14
+ if (key.expiresAt && new Date(key.expiresAt) < /* @__PURE__ */ new Date()) return "expired";
15
+ return "active";
16
+ };
17
+ const getStatusColor = (status) => {
18
+ switch (status) {
19
+ case "active": return "teal";
20
+ case "revoked": return "red";
21
+ case "expired": return "orange";
22
+ }
23
+ };
24
+ const getStatusIcon = (status) => {
25
+ switch (status) {
26
+ case "active": return /* @__PURE__ */ jsx(IconShieldCheck, { size: 14 });
27
+ case "revoked": return /* @__PURE__ */ jsx(IconShieldOff, { size: 14 });
28
+ case "expired": return /* @__PURE__ */ jsx(IconCalendarOff, { size: 14 });
29
+ }
30
+ };
31
+ const formatKeyPreview = (prefix, suffix) => {
32
+ return `${prefix}...${suffix}`;
33
+ };
34
+ const StatsCards = ({ stats, loading }) => {
35
+ const activePercentage = stats.total > 0 ? Math.round(stats.active / stats.total * 100) : 0;
36
+ return /* @__PURE__ */ jsxs(SimpleGrid, {
37
+ cols: {
38
+ base: 2,
39
+ sm: 4
40
+ },
41
+ spacing: "md",
42
+ children: [
43
+ /* @__PURE__ */ jsx(Paper, {
44
+ p: "md",
45
+ radius: "md",
46
+ withBorder: true,
47
+ children: /* @__PURE__ */ jsxs(Group, {
48
+ justify: "space-between",
49
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
50
+ size: "xs",
51
+ c: "dimmed",
52
+ tt: "uppercase",
53
+ fw: 600,
54
+ children: "Total Keys"
55
+ }), /* @__PURE__ */ jsx(Text, {
56
+ size: "xl",
57
+ fw: 700,
58
+ ff: "monospace",
59
+ children: stats.total
60
+ })] }), /* @__PURE__ */ jsx(ThemeIcon, {
61
+ size: "lg",
62
+ radius: "md",
63
+ variant: "light",
64
+ color: "blue",
65
+ children: /* @__PURE__ */ jsx(IconKey, { size: 20 })
66
+ })]
67
+ })
68
+ }),
69
+ /* @__PURE__ */ jsx(Paper, {
70
+ p: "md",
71
+ radius: "md",
72
+ withBorder: true,
73
+ children: /* @__PURE__ */ jsxs(Group, {
74
+ justify: "space-between",
75
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
76
+ size: "xs",
77
+ c: "dimmed",
78
+ tt: "uppercase",
79
+ fw: 600,
80
+ children: "Active"
81
+ }), /* @__PURE__ */ jsx(Text, {
82
+ size: "xl",
83
+ fw: 700,
84
+ ff: "monospace",
85
+ c: "teal",
86
+ children: stats.active
87
+ })] }), /* @__PURE__ */ jsx(RingProgress, {
88
+ size: 48,
89
+ thickness: 4,
90
+ roundCaps: true,
91
+ sections: [{
92
+ value: activePercentage,
93
+ color: "teal"
94
+ }]
95
+ })]
96
+ })
97
+ }),
98
+ /* @__PURE__ */ jsx(Paper, {
99
+ p: "md",
100
+ radius: "md",
101
+ withBorder: true,
102
+ children: /* @__PURE__ */ jsxs(Group, {
103
+ justify: "space-between",
104
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
105
+ size: "xs",
106
+ c: "dimmed",
107
+ tt: "uppercase",
108
+ fw: 600,
109
+ children: "Revoked"
110
+ }), /* @__PURE__ */ jsx(Text, {
111
+ size: "xl",
112
+ fw: 700,
113
+ ff: "monospace",
114
+ c: "red",
115
+ children: stats.revoked
116
+ })] }), /* @__PURE__ */ jsx(ThemeIcon, {
117
+ size: "lg",
118
+ radius: "md",
119
+ variant: "light",
120
+ color: stats.revoked > 0 ? "red" : "gray",
121
+ children: /* @__PURE__ */ jsx(IconShieldOff, { size: 20 })
122
+ })]
123
+ })
124
+ }),
125
+ /* @__PURE__ */ jsx(Paper, {
126
+ p: "md",
127
+ radius: "md",
128
+ withBorder: true,
129
+ children: /* @__PURE__ */ jsxs(Group, {
130
+ justify: "space-between",
131
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
132
+ size: "xs",
133
+ c: "dimmed",
134
+ tt: "uppercase",
135
+ fw: 600,
136
+ children: "Never Used"
137
+ }), /* @__PURE__ */ jsx(Text, {
138
+ size: "xl",
139
+ fw: 700,
140
+ ff: "monospace",
141
+ c: "yellow",
142
+ children: stats.neverUsed
143
+ })] }), /* @__PURE__ */ jsx(ThemeIcon, {
144
+ size: "lg",
145
+ radius: "md",
146
+ variant: "light",
147
+ color: stats.neverUsed > 0 ? "yellow" : "gray",
148
+ children: /* @__PURE__ */ jsx(IconClock, { size: 20 })
149
+ })]
150
+ })
151
+ })
152
+ ]
153
+ });
154
+ };
155
+ const AdminApiKeys = () => {
156
+ const client = useClient();
157
+ const router = useRouter();
158
+ const { l } = useI18n();
159
+ const toast = useToast();
160
+ const dialog = useDialog();
161
+ const [stats, setStats] = useState({
162
+ total: 0,
163
+ active: 0,
164
+ revoked: 0,
165
+ expired: 0,
166
+ neverUsed: 0
167
+ });
168
+ const [refreshKey, setRefreshKey] = useState(0);
169
+ const [loading, setLoading] = useState(true);
170
+ const filters = t.object({
171
+ userId: t.optional(t.uuid()),
172
+ includeRevoked: t.optional(t.boolean())
173
+ });
174
+ const handleRevoke = useCallback(async (key) => {
175
+ if (!await dialog.confirm({
176
+ title: "Revoke API Key",
177
+ message: `Are you sure you want to revoke "${key.name}"? This action cannot be undone and will immediately invalidate the key.`,
178
+ confirmLabel: "Revoke Key",
179
+ confirmColor: "red"
180
+ })) return;
181
+ try {
182
+ await client.revokeApiKey({ params: { id: key.id } });
183
+ toast.success(`API key "${key.name}" has been revoked`);
184
+ setRefreshKey((k) => k + 1);
185
+ } catch (error) {
186
+ toast.danger(`Failed to revoke API key`);
187
+ }
188
+ }, [
189
+ client,
190
+ dialog,
191
+ toast
192
+ ]);
193
+ const updateStats = useCallback((keys) => {
194
+ const now = /* @__PURE__ */ new Date();
195
+ const newStats = {
196
+ total: keys.length,
197
+ active: 0,
198
+ revoked: 0,
199
+ expired: 0,
200
+ neverUsed: 0
201
+ };
202
+ for (const key of keys) {
203
+ if (key.revokedAt) newStats.revoked++;
204
+ else if (key.expiresAt && new Date(key.expiresAt) < now) newStats.expired++;
205
+ else newStats.active++;
206
+ if (!key.lastUsedAt) newStats.neverUsed++;
207
+ }
208
+ setStats(newStats);
209
+ setLoading(false);
210
+ }, []);
211
+ return /* @__PURE__ */ jsxs(Flex, {
212
+ flex: 1,
213
+ direction: "column",
214
+ gap: "md",
215
+ children: [/* @__PURE__ */ jsx(StatsCards, {
216
+ stats,
217
+ loading
218
+ }), /* @__PURE__ */ jsx(DataTable, {
219
+ submitOnInit: true,
220
+ defaultSize: 15,
221
+ typeFormProps: {
222
+ skipSubmitButton: true,
223
+ columns: 2
224
+ },
225
+ tableProps: {
226
+ horizontalSpacing: "sm",
227
+ verticalSpacing: "sm",
228
+ highlightOnHover: true
229
+ },
230
+ onFilterChange: (key, _value, form) => {
231
+ if (key === "userId" || key === "includeRevoked") return form.submit();
232
+ },
233
+ filters,
234
+ tableTrProps: (item) => {
235
+ const status = getKeyStatus(item);
236
+ if (status === "revoked") return {
237
+ opacity: .6,
238
+ style: {
239
+ textDecoration: "line-through",
240
+ textDecorationColor: "var(--mantine-color-red-5)"
241
+ }
242
+ };
243
+ if (status === "expired") return { opacity: .7 };
244
+ return {};
245
+ },
246
+ items: async (filters) => {
247
+ const response = await client.findApiKeys({ query: {
248
+ ...filters,
249
+ includeRevoked: filters.includeRevoked ?? true
250
+ } });
251
+ updateStats((await client.findApiKeys({ query: {
252
+ includeRevoked: true,
253
+ size: 100
254
+ } })).content);
255
+ return response;
256
+ },
257
+ columns: {
258
+ name: {
259
+ label: "Name",
260
+ value: (item) => /* @__PURE__ */ jsxs(Stack, {
261
+ gap: 2,
262
+ children: [/* @__PURE__ */ jsxs(Group, {
263
+ gap: "xs",
264
+ children: [/* @__PURE__ */ jsx(ThemeIcon, {
265
+ size: "xs",
266
+ radius: "sm",
267
+ variant: "light",
268
+ color: getStatusColor(getKeyStatus(item)),
269
+ children: /* @__PURE__ */ jsx(IconKey, { size: 10 })
270
+ }), /* @__PURE__ */ jsx(Text, {
271
+ size: "sm",
272
+ fw: 600,
273
+ children: item.name
274
+ })]
275
+ }), item.description && /* @__PURE__ */ jsx(Text, {
276
+ size: "xs",
277
+ c: "dimmed",
278
+ lineClamp: 1,
279
+ children: item.description
280
+ })]
281
+ })
282
+ },
283
+ token: {
284
+ label: "Key",
285
+ fit: true,
286
+ value: (item) => /* @__PURE__ */ jsxs(Group, {
287
+ gap: 4,
288
+ children: [/* @__PURE__ */ jsx(Code, {
289
+ ff: "monospace",
290
+ style: {
291
+ fontSize: 11,
292
+ letterSpacing: "0.5px"
293
+ },
294
+ children: formatKeyPreview(item.tokenPrefix, item.tokenSuffix)
295
+ }), /* @__PURE__ */ jsx(ClipboardButton, {
296
+ size: "xs",
297
+ variant: "subtle",
298
+ value: formatKeyPreview(item.tokenPrefix, item.tokenSuffix)
299
+ })]
300
+ })
301
+ },
302
+ status: {
303
+ label: "Status",
304
+ fit: true,
305
+ value: (item) => {
306
+ const status = getKeyStatus(item);
307
+ return /* @__PURE__ */ jsx(Badge, {
308
+ size: "sm",
309
+ variant: "light",
310
+ color: getStatusColor(status),
311
+ leftSection: getStatusIcon(status),
312
+ children: status.toUpperCase()
313
+ });
314
+ }
315
+ },
316
+ roles: {
317
+ label: "Roles",
318
+ value: (item) => /* @__PURE__ */ jsxs(Group, {
319
+ gap: 4,
320
+ wrap: "wrap",
321
+ children: [item.roles.length > 0 ? item.roles.slice(0, 3).map((role) => /* @__PURE__ */ jsx(Badge, {
322
+ size: "xs",
323
+ variant: "outline",
324
+ color: "gray",
325
+ children: role
326
+ }, role)) : /* @__PURE__ */ jsx(Text, {
327
+ size: "xs",
328
+ c: "dimmed",
329
+ children: "No roles"
330
+ }), item.roles.length > 3 && /* @__PURE__ */ jsx(Tooltip, {
331
+ label: item.roles.slice(3).join(", "),
332
+ children: /* @__PURE__ */ jsxs(Badge, {
333
+ size: "xs",
334
+ variant: "light",
335
+ color: "gray",
336
+ children: ["+", item.roles.length - 3]
337
+ })
338
+ })]
339
+ })
340
+ },
341
+ usage: {
342
+ label: "Usage",
343
+ fit: true,
344
+ value: (item) => /* @__PURE__ */ jsxs(Stack, {
345
+ gap: 2,
346
+ children: [/* @__PURE__ */ jsxs(Text, {
347
+ size: "xs",
348
+ ff: "monospace",
349
+ fw: 500,
350
+ children: [item.usageCount.toLocaleString(), " calls"]
351
+ }), item.lastUsedAt ? /* @__PURE__ */ jsxs(Group, {
352
+ gap: 4,
353
+ children: [/* @__PURE__ */ jsx(Text, {
354
+ size: "xs",
355
+ c: "dimmed",
356
+ children: l(item.lastUsedAt, { date: "fromNow" })
357
+ }), item.lastUsedIp && /* @__PURE__ */ jsx(Tooltip, {
358
+ label: `Last IP: ${item.lastUsedIp}`,
359
+ children: /* @__PURE__ */ jsx(IconNetwork, {
360
+ size: 12,
361
+ color: "var(--mantine-color-dimmed)"
362
+ })
363
+ })]
364
+ }) : /* @__PURE__ */ jsx(Text, {
365
+ size: "xs",
366
+ c: "yellow",
367
+ children: "Never used"
368
+ })]
369
+ })
370
+ },
371
+ userId: {
372
+ label: "Owner",
373
+ fit: true,
374
+ value: (item) => /* @__PURE__ */ jsx(ActionButton, {
375
+ variant: "subtle",
376
+ size: "xs",
377
+ href: router.path("adminUserDetails", { params: { userId: item.userId } }),
378
+ leftSection: /* @__PURE__ */ jsx(IconUser, { size: 12 }),
379
+ children: /* @__PURE__ */ jsx(Text, {
380
+ size: "xs",
381
+ ff: "monospace",
382
+ children: item.userId.slice(0, 8)
383
+ })
384
+ })
385
+ },
386
+ createdAt: {
387
+ label: "Created",
388
+ fit: true,
389
+ value: (item) => /* @__PURE__ */ jsx(Text, {
390
+ size: "xs",
391
+ c: "dimmed",
392
+ children: l(item.createdAt, { date: "fromNow" })
393
+ })
394
+ },
395
+ expiresAt: {
396
+ label: "Expires",
397
+ fit: true,
398
+ value: (item) => {
399
+ if (!item.expiresAt) return /* @__PURE__ */ jsx(Text, {
400
+ size: "xs",
401
+ c: "dimmed",
402
+ children: "Never"
403
+ });
404
+ return /* @__PURE__ */ jsx(Text, {
405
+ size: "xs",
406
+ c: new Date(item.expiresAt) < /* @__PURE__ */ new Date() ? "orange" : "dimmed",
407
+ children: l(item.expiresAt, { date: "fromNow" })
408
+ });
409
+ }
410
+ },
411
+ actions: {
412
+ label: "",
413
+ fit: true,
414
+ value: (item) => {
415
+ if (getKeyStatus(item) === "revoked") return /* @__PURE__ */ jsx(Tooltip, {
416
+ label: "Already revoked",
417
+ children: /* @__PURE__ */ jsx(IconCheck, {
418
+ size: 14,
419
+ color: "var(--mantine-color-dimmed)"
420
+ })
421
+ });
422
+ return /* @__PURE__ */ jsx(Tooltip, {
423
+ label: "Revoke key",
424
+ children: /* @__PURE__ */ jsx(ActionIcon, {
425
+ size: "sm",
426
+ variant: "subtle",
427
+ color: "red",
428
+ onClick: () => handleRevoke(item),
429
+ children: /* @__PURE__ */ jsx(IconTrash, { size: 14 })
430
+ })
431
+ });
432
+ }
433
+ }
434
+ }
435
+ }, refreshKey)]
436
+ });
437
+ };
438
+ var AdminApiKeys_default = AdminApiKeys;
439
+
440
+ //#endregion
441
+ export { AdminApiKeys_default as t };
442
+ //# sourceMappingURL=AdminApiKeys-GMORg-1l.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminApiKeys-GMORg-1l.js","names":[],"sources":["../../src/admin/components/keys/AdminApiKeys.tsx"],"sourcesContent":["import {\n ActionButton,\n ClipboardButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} from \"@alepha/ui\";\nimport {\n ActionIcon,\n Badge,\n Box,\n Code,\n Group,\n Paper,\n RingProgress,\n SimpleGrid,\n Stack,\n ThemeIcon,\n Tooltip,\n} from \"@mantine/core\";\nimport {\n IconCalendarOff,\n IconCheck,\n IconClock,\n IconKey,\n IconNetwork,\n IconShieldCheck,\n IconShieldOff,\n IconTrash,\n IconUser,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminApiKeyController } from \"alepha/api/keys\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport { useCallback, useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface ApiKeyResource {\n id: string;\n userId: string;\n name: string;\n description?: string;\n tokenPrefix: string;\n tokenSuffix: string;\n roles: string[];\n createdAt: string;\n lastUsedAt?: string;\n lastUsedIp?: string;\n expiresAt?: string;\n revokedAt?: string;\n usageCount: number;\n}\n\ninterface KeyStats {\n total: number;\n active: number;\n revoked: number;\n expired: number;\n neverUsed: number;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst getKeyStatus = (\n key: ApiKeyResource,\n): \"active\" | \"revoked\" | \"expired\" => {\n if (key.revokedAt) return \"revoked\";\n if (key.expiresAt && new Date(key.expiresAt) < new Date()) return \"expired\";\n return \"active\";\n};\n\nconst getStatusColor = (status: \"active\" | \"revoked\" | \"expired\") => {\n switch (status) {\n case \"active\":\n return \"teal\";\n case \"revoked\":\n return \"red\";\n case \"expired\":\n return \"orange\";\n }\n};\n\nconst getStatusIcon = (status: \"active\" | \"revoked\" | \"expired\") => {\n switch (status) {\n case \"active\":\n return <IconShieldCheck size={14} />;\n case \"revoked\":\n return <IconShieldOff size={14} />;\n case \"expired\":\n return <IconCalendarOff size={14} />;\n }\n};\n\nconst formatKeyPreview = (prefix: string, suffix: string) => {\n return `${prefix}...${suffix}`;\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Stats Cards\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface StatsCardsProps {\n stats: KeyStats;\n loading: boolean;\n}\n\nconst StatsCards = ({ stats, loading }: StatsCardsProps) => {\n const activePercentage =\n stats.total > 0 ? Math.round((stats.active / stats.total) * 100) : 0;\n\n return (\n <SimpleGrid cols={{ base: 2, sm: 4 }} spacing=\"md\">\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Total Keys\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\">\n {stats.total}\n </Text>\n </Box>\n <ThemeIcon size=\"lg\" radius=\"md\" variant=\"light\" color=\"blue\">\n <IconKey size={20} />\n </ThemeIcon>\n </Group>\n </Paper>\n\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Active\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\" c=\"teal\">\n {stats.active}\n </Text>\n </Box>\n <RingProgress\n size={48}\n thickness={4}\n roundCaps\n sections={[{ value: activePercentage, color: \"teal\" }]}\n />\n </Group>\n </Paper>\n\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Revoked\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\" c=\"red\">\n {stats.revoked}\n </Text>\n </Box>\n <ThemeIcon\n size=\"lg\"\n radius=\"md\"\n variant=\"light\"\n color={stats.revoked > 0 ? \"red\" : \"gray\"}\n >\n <IconShieldOff size={20} />\n </ThemeIcon>\n </Group>\n </Paper>\n\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Never Used\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\" c=\"yellow\">\n {stats.neverUsed}\n </Text>\n </Box>\n <ThemeIcon\n size=\"lg\"\n radius=\"md\"\n variant=\"light\"\n color={stats.neverUsed > 0 ? \"yellow\" : \"gray\"}\n >\n <IconClock size={20} />\n </ThemeIcon>\n </Group>\n </Paper>\n </SimpleGrid>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main Component\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst AdminApiKeys = () => {\n const client = useClient<AdminApiKeyController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const toast = useToast();\n const dialog = useDialog();\n\n const [stats, setStats] = useState<KeyStats>({\n total: 0,\n active: 0,\n revoked: 0,\n expired: 0,\n neverUsed: 0,\n });\n const [refreshKey, setRefreshKey] = useState(0);\n const [loading, setLoading] = useState(true);\n\n const filters = t.object({\n userId: t.optional(t.uuid()),\n includeRevoked: t.optional(t.boolean()),\n });\n\n const handleRevoke = useCallback(\n async (key: ApiKeyResource) => {\n const confirmed = await dialog.confirm({\n title: \"Revoke API Key\",\n message: `Are you sure you want to revoke \"${key.name}\"? This action cannot be undone and will immediately invalidate the key.`,\n confirmLabel: \"Revoke Key\",\n confirmColor: \"red\",\n });\n\n if (!confirmed) return;\n\n try {\n await client.revokeApiKey({ params: { id: key.id } });\n toast.success(`API key \"${key.name}\" has been revoked`);\n setRefreshKey((k) => k + 1);\n } catch (error) {\n toast.danger(`Failed to revoke API key`);\n }\n },\n [client, dialog, toast],\n );\n\n // Calculate stats from loaded data\n const updateStats = useCallback((keys: ApiKeyResource[]) => {\n const now = new Date();\n const newStats: KeyStats = {\n total: keys.length,\n active: 0,\n revoked: 0,\n expired: 0,\n neverUsed: 0,\n };\n\n for (const key of keys) {\n if (key.revokedAt) {\n newStats.revoked++;\n } else if (key.expiresAt && new Date(key.expiresAt) < now) {\n newStats.expired++;\n } else {\n newStats.active++;\n }\n\n if (!key.lastUsedAt) {\n newStats.neverUsed++;\n }\n }\n\n setStats(newStats);\n setLoading(false);\n }, []);\n\n return (\n <Flex flex={1} direction=\"column\" gap=\"md\">\n {/* Stats Header */}\n <StatsCards stats={stats} loading={loading} />\n\n {/* API Keys Table */}\n <DataTable<ApiKeyResource, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={15}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 2,\n }}\n tableProps={{\n horizontalSpacing: \"sm\",\n verticalSpacing: \"sm\",\n highlightOnHover: true,\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"userId\" || key === \"includeRevoked\") {\n return form.submit();\n }\n }}\n filters={filters}\n tableTrProps={(item) => {\n const status = getKeyStatus(item);\n if (status === \"revoked\") {\n return {\n opacity: 0.6,\n style: {\n textDecoration: \"line-through\",\n textDecorationColor: \"var(--mantine-color-red-5)\",\n },\n };\n }\n if (status === \"expired\") {\n return { opacity: 0.7 };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findApiKeys({\n query: {\n ...filters,\n includeRevoked: filters.includeRevoked ?? true,\n },\n });\n\n // Update stats with all keys (need to fetch all for accurate stats)\n const allKeys = await client.findApiKeys({\n query: { includeRevoked: true, size: 100 },\n });\n updateStats(allKeys.content as ApiKeyResource[]);\n\n return response as Page<ApiKeyResource>;\n }}\n columns={{\n name: {\n label: \"Name\",\n value: (item) => (\n <Stack gap={2}>\n <Group gap=\"xs\">\n <ThemeIcon\n size=\"xs\"\n radius=\"sm\"\n variant=\"light\"\n color={getStatusColor(getKeyStatus(item))}\n >\n <IconKey size={10} />\n </ThemeIcon>\n <Text size=\"sm\" fw={600}>\n {item.name}\n </Text>\n </Group>\n {item.description && (\n <Text size=\"xs\" c=\"dimmed\" lineClamp={1}>\n {item.description}\n </Text>\n )}\n </Stack>\n ),\n },\n token: {\n label: \"Key\",\n fit: true,\n value: (item) => (\n <Group gap={4}>\n <Code\n ff=\"monospace\"\n style={{\n fontSize: 11,\n letterSpacing: \"0.5px\",\n }}\n >\n {formatKeyPreview(item.tokenPrefix, item.tokenSuffix)}\n </Code>\n <ClipboardButton\n size=\"xs\"\n variant=\"subtle\"\n value={formatKeyPreview(item.tokenPrefix, item.tokenSuffix)}\n />\n </Group>\n ),\n },\n status: {\n label: \"Status\",\n fit: true,\n value: (item) => {\n const status = getKeyStatus(item);\n return (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={getStatusColor(status)}\n leftSection={getStatusIcon(status)}\n >\n {status.toUpperCase()}\n </Badge>\n );\n },\n },\n roles: {\n label: \"Roles\",\n value: (item) => (\n <Group gap={4} wrap=\"wrap\">\n {item.roles.length > 0 ? (\n item.roles.slice(0, 3).map((role) => (\n <Badge key={role} size=\"xs\" variant=\"outline\" color=\"gray\">\n {role}\n </Badge>\n ))\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n No roles\n </Text>\n )}\n {item.roles.length > 3 && (\n <Tooltip label={item.roles.slice(3).join(\", \")}>\n <Badge size=\"xs\" variant=\"light\" color=\"gray\">\n +{item.roles.length - 3}\n </Badge>\n </Tooltip>\n )}\n </Group>\n ),\n },\n usage: {\n label: \"Usage\",\n fit: true,\n value: (item) => (\n <Stack gap={2}>\n <Text size=\"xs\" ff=\"monospace\" fw={500}>\n {item.usageCount.toLocaleString()} calls\n </Text>\n {item.lastUsedAt ? (\n <Group gap={4}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.lastUsedAt, { date: \"fromNow\" })}\n </Text>\n {item.lastUsedIp && (\n <Tooltip label={`Last IP: ${item.lastUsedIp}`}>\n <IconNetwork\n size={12}\n color=\"var(--mantine-color-dimmed)\"\n />\n </Tooltip>\n )}\n </Group>\n ) : (\n <Text size=\"xs\" c=\"yellow\">\n Never used\n </Text>\n )}\n </Stack>\n ),\n },\n userId: {\n label: \"Owner\",\n fit: true,\n value: (item) => (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={router.path(\"adminUserDetails\", {\n params: { userId: item.userId },\n })}\n leftSection={<IconUser size={12} />}\n >\n <Text size=\"xs\" ff=\"monospace\">\n {item.userId.slice(0, 8)}\n </Text>\n </ActionButton>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n expiresAt: {\n label: \"Expires\",\n fit: true,\n value: (item) => {\n if (!item.expiresAt) {\n return (\n <Text size=\"xs\" c=\"dimmed\">\n Never\n </Text>\n );\n }\n\n const isExpired = new Date(item.expiresAt) < new Date();\n return (\n <Text size=\"xs\" c={isExpired ? \"orange\" : \"dimmed\"}>\n {l(item.expiresAt, { date: \"fromNow\" })}\n </Text>\n );\n },\n },\n actions: {\n label: \"\",\n fit: true,\n value: (item) => {\n const status = getKeyStatus(item);\n if (status === \"revoked\") {\n return (\n <Tooltip label=\"Already revoked\">\n <IconCheck size={14} color=\"var(--mantine-color-dimmed)\" />\n </Tooltip>\n );\n }\n\n return (\n <Tooltip label=\"Revoke key\">\n <ActionIcon\n size=\"sm\"\n variant=\"subtle\"\n color=\"red\"\n onClick={() => handleRevoke(item)}\n >\n <IconTrash size={14} />\n </ActionIcon>\n </Tooltip>\n );\n },\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminApiKeys;\n"],"mappings":";;;;;;;;;;;AAyEA,MAAM,gBACJ,QACqC;AACrC,KAAI,IAAI,UAAW,QAAO;AAC1B,KAAI,IAAI,aAAa,IAAI,KAAK,IAAI,UAAU,mBAAG,IAAI,MAAM,CAAE,QAAO;AAClE,QAAO;;AAGT,MAAM,kBAAkB,WAA6C;AACnE,SAAQ,QAAR;EACE,KAAK,SACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,KAAK,UACH,QAAO;;;AAIb,MAAM,iBAAiB,WAA6C;AAClE,SAAQ,QAAR;EACE,KAAK,SACH,QAAO,oBAAC,mBAAgB,MAAM,KAAM;EACtC,KAAK,UACH,QAAO,oBAAC,iBAAc,MAAM,KAAM;EACpC,KAAK,UACH,QAAO,oBAAC,mBAAgB,MAAM,KAAM;;;AAI1C,MAAM,oBAAoB,QAAgB,WAAmB;AAC3D,QAAO,GAAG,OAAO,KAAK;;AAYxB,MAAM,cAAc,EAAE,OAAO,cAA+B;CAC1D,MAAM,mBACJ,MAAM,QAAQ,IAAI,KAAK,MAAO,MAAM,SAAS,MAAM,QAAS,IAAI,GAAG;AAErE,QACE,qBAAC;EAAW,MAAM;GAAE,MAAM;GAAG,IAAI;GAAG;EAAE,SAAQ;;GAC5C,oBAAC;IAAM,GAAE;IAAK,QAAO;IAAK;cACxB,qBAAC;KAAM,SAAQ;gBACb,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;gBACzB,MAAM;OACF,IACH,EACN,oBAAC;MAAU,MAAK;MAAK,QAAO;MAAK,SAAQ;MAAQ,OAAM;gBACrD,oBAAC,WAAQ,MAAM,KAAM;OACX;MACN;KACF;GAER,oBAAC;IAAM,GAAE;IAAK,QAAO;IAAK;cACxB,qBAAC;KAAM,SAAQ;gBACb,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;MAAY,GAAE;gBACvC,MAAM;OACF,IACH,EACN,oBAAC;MACC,MAAM;MACN,WAAW;MACX;MACA,UAAU,CAAC;OAAE,OAAO;OAAkB,OAAO;OAAQ,CAAC;OACtD;MACI;KACF;GAER,oBAAC;IAAM,GAAE;IAAK,QAAO;IAAK;cACxB,qBAAC;KAAM,SAAQ;gBACb,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;MAAY,GAAE;gBACvC,MAAM;OACF,IACH,EACN,oBAAC;MACC,MAAK;MACL,QAAO;MACP,SAAQ;MACR,OAAO,MAAM,UAAU,IAAI,QAAQ;gBAEnC,oBAAC,iBAAc,MAAM,KAAM;OACjB;MACN;KACF;GAER,oBAAC;IAAM,GAAE;IAAK,QAAO;IAAK;cACxB,qBAAC;KAAM,SAAQ;gBACb,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;MAAY,GAAE;gBACvC,MAAM;OACF,IACH,EACN,oBAAC;MACC,MAAK;MACL,QAAO;MACP,SAAQ;MACR,OAAO,MAAM,YAAY,IAAI,WAAW;gBAExC,oBAAC,aAAU,MAAM,KAAM;OACb;MACN;KACF;;GACG;;AAQjB,MAAM,qBAAqB;CACzB,MAAM,SAAS,WAAkC;CACjD,MAAM,SAAS,WAAwB;CACvC,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,QAAQ,UAAU;CACxB,MAAM,SAAS,WAAW;CAE1B,MAAM,CAAC,OAAO,YAAY,SAAmB;EAC3C,OAAO;EACP,QAAQ;EACR,SAAS;EACT,SAAS;EACT,WAAW;EACZ,CAAC;CACF,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAE5C,MAAM,UAAU,EAAE,OAAO;EACvB,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC;EAC5B,gBAAgB,EAAE,SAAS,EAAE,SAAS,CAAC;EACxC,CAAC;CAEF,MAAM,eAAe,YACnB,OAAO,QAAwB;AAQ7B,MAAI,CAPc,MAAM,OAAO,QAAQ;GACrC,OAAO;GACP,SAAS,oCAAoC,IAAI,KAAK;GACtD,cAAc;GACd,cAAc;GACf,CAAC,CAEc;AAEhB,MAAI;AACF,SAAM,OAAO,aAAa,EAAE,QAAQ,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC;AACrD,SAAM,QAAQ,YAAY,IAAI,KAAK,oBAAoB;AACvD,kBAAe,MAAM,IAAI,EAAE;WACpB,OAAO;AACd,SAAM,OAAO,2BAA2B;;IAG5C;EAAC;EAAQ;EAAQ;EAAM,CACxB;CAGD,MAAM,cAAc,aAAa,SAA2B;EAC1D,MAAM,sBAAM,IAAI,MAAM;EACtB,MAAM,WAAqB;GACzB,OAAO,KAAK;GACZ,QAAQ;GACR,SAAS;GACT,SAAS;GACT,WAAW;GACZ;AAED,OAAK,MAAM,OAAO,MAAM;AACtB,OAAI,IAAI,UACN,UAAS;YACA,IAAI,aAAa,IAAI,KAAK,IAAI,UAAU,GAAG,IACpD,UAAS;OAET,UAAS;AAGX,OAAI,CAAC,IAAI,WACP,UAAS;;AAIb,WAAS,SAAS;AAClB,aAAW,MAAM;IAChB,EAAE,CAAC;AAEN,QACE,qBAAC;EAAK,MAAM;EAAG,WAAU;EAAS,KAAI;aAEpC,oBAAC;GAAkB;GAAgB;IAAW,EAG9C,oBAAC;GAEC;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IACjB,kBAAkB;IACnB;GACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,QAAI,QAAQ,YAAY,QAAQ,iBAC9B,QAAO,KAAK,QAAQ;;GAGf;GACT,eAAe,SAAS;IACtB,MAAM,SAAS,aAAa,KAAK;AACjC,QAAI,WAAW,UACb,QAAO;KACL,SAAS;KACT,OAAO;MACL,gBAAgB;MAChB,qBAAqB;MACtB;KACF;AAEH,QAAI,WAAW,UACb,QAAO,EAAE,SAAS,IAAK;AAEzB,WAAO,EAAE;;GAEX,OAAO,OAAO,YAAY;IACxB,MAAM,WAAW,MAAM,OAAO,YAAY,EACxC,OAAO;KACL,GAAG;KACH,gBAAgB,QAAQ,kBAAkB;KAC3C,EACF,CAAC;AAMF,iBAHgB,MAAM,OAAO,YAAY,EACvC,OAAO;KAAE,gBAAgB;KAAM,MAAM;KAAK,EAC3C,CAAC,EACkB,QAA4B;AAEhD,WAAO;;GAET,SAAS;IACP,MAAM;KACJ,OAAO;KACP,QAAQ,SACN,qBAAC;MAAM,KAAK;iBACV,qBAAC;OAAM,KAAI;kBACT,oBAAC;QACC,MAAK;QACL,QAAO;QACP,SAAQ;QACR,OAAO,eAAe,aAAa,KAAK,CAAC;kBAEzC,oBAAC,WAAQ,MAAM,KAAM;SACX,EACZ,oBAAC;QAAK,MAAK;QAAK,IAAI;kBACjB,KAAK;SACD;QACD,EACP,KAAK,eACJ,oBAAC;OAAK,MAAK;OAAK,GAAE;OAAS,WAAW;iBACnC,KAAK;QACD;OAEH;KAEX;IACD,OAAO;KACL,OAAO;KACP,KAAK;KACL,QAAQ,SACN,qBAAC;MAAM,KAAK;iBACV,oBAAC;OACC,IAAG;OACH,OAAO;QACL,UAAU;QACV,eAAe;QAChB;iBAEA,iBAAiB,KAAK,aAAa,KAAK,YAAY;QAChD,EACP,oBAAC;OACC,MAAK;OACL,SAAQ;OACR,OAAO,iBAAiB,KAAK,aAAa,KAAK,YAAY;QAC3D;OACI;KAEX;IACD,QAAQ;KACN,OAAO;KACP,KAAK;KACL,QAAQ,SAAS;MACf,MAAM,SAAS,aAAa,KAAK;AACjC,aACE,oBAAC;OACC,MAAK;OACL,SAAQ;OACR,OAAO,eAAe,OAAO;OAC7B,aAAa,cAAc,OAAO;iBAEjC,OAAO,aAAa;QACf;;KAGb;IACD,OAAO;KACL,OAAO;KACP,QAAQ,SACN,qBAAC;MAAM,KAAK;MAAG,MAAK;iBACjB,KAAK,MAAM,SAAS,IACnB,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,KAAK,SAC1B,oBAAC;OAAiB,MAAK;OAAK,SAAQ;OAAU,OAAM;iBACjD;SADS,KAEJ,CACR,GAEF,oBAAC;OAAK,MAAK;OAAK,GAAE;iBAAS;QAEpB,EAER,KAAK,MAAM,SAAS,KACnB,oBAAC;OAAQ,OAAO,KAAK,MAAM,MAAM,EAAE,CAAC,KAAK,KAAK;iBAC5C,qBAAC;QAAM,MAAK;QAAK,SAAQ;QAAQ,OAAM;mBAAO,KAC1C,KAAK,MAAM,SAAS;SAChB;QACA;OAEN;KAEX;IACD,OAAO;KACL,OAAO;KACP,KAAK;KACL,QAAQ,SACN,qBAAC;MAAM,KAAK;iBACV,qBAAC;OAAK,MAAK;OAAK,IAAG;OAAY,IAAI;kBAChC,KAAK,WAAW,gBAAgB,EAAC;QAC7B,EACN,KAAK,aACJ,qBAAC;OAAM,KAAK;kBACV,oBAAC;QAAK,MAAK;QAAK,GAAE;kBACf,EAAE,KAAK,YAAY,EAAE,MAAM,WAAW,CAAC;SACnC,EACN,KAAK,cACJ,oBAAC;QAAQ,OAAO,YAAY,KAAK;kBAC/B,oBAAC;SACC,MAAM;SACN,OAAM;UACN;SACM;QAEN,GAER,oBAAC;OAAK,MAAK;OAAK,GAAE;iBAAS;QAEpB;OAEH;KAEX;IACD,QAAQ;KACN,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MACC,SAAQ;MACR,MAAK;MACL,MAAM,OAAO,KAAK,oBAAoB,EACpC,QAAQ,EAAE,QAAQ,KAAK,QAAQ,EAChC,CAAC;MACF,aAAa,oBAAC,YAAS,MAAM,KAAM;gBAEnC,oBAAC;OAAK,MAAK;OAAK,IAAG;iBAChB,KAAK,OAAO,MAAM,GAAG,EAAE;QACnB;OACM;KAElB;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;OAClC;KAEV;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SAAS;AACf,UAAI,CAAC,KAAK,UACR,QACE,oBAAC;OAAK,MAAK;OAAK,GAAE;iBAAS;QAEpB;AAKX,aACE,oBAAC;OAAK,MAAK;OAAK,GAFA,IAAI,KAAK,KAAK,UAAU,mBAAG,IAAI,MAAM,GAEtB,WAAW;iBACvC,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;QAClC;;KAGZ;IACD,SAAS;KACP,OAAO;KACP,KAAK;KACL,QAAQ,SAAS;AAEf,UADe,aAAa,KAAK,KAClB,UACb,QACE,oBAAC;OAAQ,OAAM;iBACb,oBAAC;QAAU,MAAM;QAAI,OAAM;SAAgC;QACnD;AAId,aACE,oBAAC;OAAQ,OAAM;iBACb,oBAAC;QACC,MAAK;QACL,SAAQ;QACR,OAAM;QACN,eAAe,aAAa,KAAK;kBAEjC,oBAAC,aAAU,MAAM,KAAM;SACZ;QACL;;KAGf;IACF;KApPI,WAqPL;GACG;;AAIX,2BAAe"}
@@ -0,0 +1,3 @@
1
+ import { t as AdminAudits_default } from "./AdminAudits-pkWrjq1Z.js";
2
+
3
+ export { AdminAudits_default as default };
@@ -2,10 +2,10 @@ import { DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconAlertTriangle, IconCheck, IconInfoCircle, IconUser, IconX } from "@tabler/icons-react";
4
4
  import { useRouter } from "alepha/react/router";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
5
  import { Badge, Group, Stack, Tooltip } from "@mantine/core";
7
6
  import { useClient } from "alepha/react";
8
7
  import { useI18n } from "alepha/react/i18n";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region ../../src/admin/components/audits/AdminAudits.tsx
11
11
  const getSeverityColor = (severity) => {
@@ -213,4 +213,4 @@ var AdminAudits_default = AdminAudits;
213
213
 
214
214
  //#endregion
215
215
  export { AdminAudits_default as t };
216
- //# sourceMappingURL=AdminAudits-Oh7iAfQa.js.map
216
+ //# sourceMappingURL=AdminAudits-pkWrjq1Z.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminAudits-Oh7iAfQa.js","names":[],"sources":["../../src/admin/components/audits/AdminAudits.tsx"],"sourcesContent":["import { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Stack, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst getTypeColor = (type: string) => {\n switch (type) {\n case \"auth\":\n return \"blue\";\n case \"user\":\n return \"grape\";\n case \"security\":\n return \"red\";\n case \"system\":\n return \"orange\";\n case \"access\":\n return \"cyan\";\n case \"payment\":\n return \"green\";\n case \"order\":\n return \"teal\";\n default:\n return \"gray\";\n }\n};\n\nconst AdminAudits = (props: AdminAuditsProps) => {\n const client = useClient<AdminAuditController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n resourceType: t.optional(t.text()),\n search: t.optional(t.text()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={20}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n cursor: item.userId ? \"pointer\" : \"default\",\n opacity: item.success ? 1 : 0.85,\n },\n onClick: () => {\n if (item.userId) {\n router.push(\"adminUserDetails\", {\n params: { userId: item.userId },\n });\n }\n },\n })}\n items={async (query) => {\n const response = await client.findAudits({\n query: {\n ...query,\n userRealm: props.userRealmName,\n },\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color={getTypeColor(item.type)}>\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n user: {\n label: \"User\",\n fit: true,\n value: (item) =>\n item.userId ? (\n <Tooltip\n label={\n <Stack gap={2}>\n <Text size=\"xs\">{item.userEmail || \"No email\"}</Text>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userRealm || \"default\"}\n </Text>\n </Stack>\n }\n >\n <Group gap={4}>\n <IconUser size={12} />\n <Text size=\"xs\" lineClamp={1} maw={100}>\n {item.userEmail?.split(\"@\")[0] || item.userId.slice(0, 8)}\n </Text>\n </Group>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n System\n </Text>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={`${item.resourceType}: ${item.resourceId}`}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminAudits;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminAudits-pkWrjq1Z.js","names":[],"sources":["../../src/admin/components/audits/AdminAudits.tsx"],"sourcesContent":["import { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Stack, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst getTypeColor = (type: string) => {\n switch (type) {\n case \"auth\":\n return \"blue\";\n case \"user\":\n return \"grape\";\n case \"security\":\n return \"red\";\n case \"system\":\n return \"orange\";\n case \"access\":\n return \"cyan\";\n case \"payment\":\n return \"green\";\n case \"order\":\n return \"teal\";\n default:\n return \"gray\";\n }\n};\n\nconst AdminAudits = (props: AdminAuditsProps) => {\n const client = useClient<AdminAuditController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n resourceType: t.optional(t.text()),\n search: t.optional(t.text()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={20}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n cursor: item.userId ? \"pointer\" : \"default\",\n opacity: item.success ? 1 : 0.85,\n },\n onClick: () => {\n if (item.userId) {\n router.push(\"adminUserDetails\", {\n params: { userId: item.userId },\n });\n }\n },\n })}\n items={async (query) => {\n const response = await client.findAudits({\n query: {\n ...query,\n userRealm: props.userRealmName,\n },\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color={getTypeColor(item.type)}>\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n user: {\n label: \"User\",\n fit: true,\n value: (item) =>\n item.userId ? (\n <Tooltip\n label={\n <Stack gap={2}>\n <Text size=\"xs\">{item.userEmail || \"No email\"}</Text>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userRealm || \"default\"}\n </Text>\n </Stack>\n }\n >\n <Group gap={4}>\n <IconUser size={12} />\n <Text size=\"xs\" lineClamp={1} maw={100}>\n {item.userEmail?.split(\"@\")[0] || item.userId.slice(0, 8)}\n </Text>\n </Group>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n System\n </Text>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={`${item.resourceType}: ${item.resourceId}`}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminAudits;\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { t as AdminFiles_default } from "./AdminFiles-WeQbsCsl.js";
2
+
3
+ export { AdminFiles_default as default };
@@ -1,9 +1,9 @@
1
1
  import { DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
- import { jsx } from "react/jsx-runtime";
4
3
  import { Badge } from "@mantine/core";
5
4
  import { useClient } from "alepha/react";
6
5
  import { useI18n } from "alepha/react/i18n";
6
+ import { jsx } from "react/jsx-runtime";
7
7
  import { files } from "alepha/api/files";
8
8
 
9
9
  //#region ../../src/admin/components/files/AdminFiles.tsx
@@ -115,4 +115,4 @@ var AdminFiles_default = AdminFiles;
115
115
 
116
116
  //#endregion
117
117
  export { AdminFiles_default as t };
118
- //# sourceMappingURL=AdminFiles-Cu8GHgQ3.js.map
118
+ //# sourceMappingURL=AdminFiles-WeQbsCsl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminFiles-Cu8GHgQ3.js","names":[],"sources":["../../src/admin/components/files/AdminFiles.tsx"],"sourcesContent":["import { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport { type Page, t } from \"alepha\";\nimport { type FileController, type FileEntity, files } from \"alepha/api/files\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\n\nconst AdminFiles = () => {\n const client = useClient<FileController>();\n const { l } = useI18n();\n\n const filters = t.object({\n bucket: t.optional(t.string()),\n name: t.optional(\n t.string({\n $control: {\n query: t.pick(files.schema, [\"name\", \"bucket\", \"mimeType\"]),\n },\n }),\n ),\n });\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <Flex flex={1} direction={\"column\"}>\n <DataTable<FileEntity, typeof filters>\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"name\" || key === \"bucket\") {\n return form.submit();\n }\n }}\n filters={filters}\n items={async (filters) => {\n const response = await client.findFiles({\n query: filters,\n });\n\n return response as Page<FileEntity>;\n }}\n columns={{\n name: {\n label: \"Name\",\n value: (item) => (\n <Text size=\"sm\" fw={500} lineClamp={1}>\n {item.name}\n </Text>\n ),\n },\n bucket: {\n label: \"Bucket\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color=\"blue\">\n {item.bucket}\n </Badge>\n ),\n },\n mimeType: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.mimeType}\n </Text>\n ),\n },\n size: {\n label: \"Size\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {formatFileSize(item.size)}\n </Text>\n ),\n },\n creatorName: {\n label: \"Creator\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.creatorName || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminFiles;\n"],"mappings":";;;;;;;;;AAOA,MAAM,mBAAmB;CACvB,MAAM,SAAS,WAA2B;CAC1C,MAAM,EAAE,MAAM,SAAS;CAEvB,MAAM,UAAU,EAAE,OAAO;EACvB,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;EAC9B,MAAM,EAAE,SACN,EAAE,OAAO,EACP,UAAU,EACR,OAAO,EAAE,KAAK,MAAM,QAAQ;GAAC;GAAQ;GAAU;GAAW,CAAC,EAC5D,EACF,CAAC,CACH;EACF,CAAC;CAEF,MAAM,kBAAkB,UAAkB;AACxC,MAAI,UAAU,EAAG,QAAO;EACxB,MAAM,IAAI;EACV,MAAM,QAAQ;GAAC;GAAK;GAAM;GAAM;GAAK;EACrC,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,GAAG,MAAM;;AAGpE,QACE,oBAAC;EAAK,MAAM;EAAG,WAAW;YACxB,oBAAC;GACC;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IAClB;GACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,QAAI,QAAQ,UAAU,QAAQ,SAC5B,QAAO,KAAK,QAAQ;;GAGf;GACT,OAAO,OAAO,YAAY;AAKxB,WAJiB,MAAM,OAAO,UAAU,EACtC,OAAO,SACR,CAAC;;GAIJ,SAAS;IACP,MAAM;KACJ,OAAO;KACP,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,WAAW;gBACjC,KAAK;OACD;KAEV;IACD,QAAQ;KACN,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAM,MAAK;MAAK,SAAQ;MAAQ,OAAM;gBACpC,KAAK;OACA;KAEX;IACD,UAAU;KACR,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,KAAK;OACD;KAEV;IACD,MAAM;KACJ,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,eAAe,KAAK,KAAK;OACrB;KAEV;IACD,aAAa;KACX,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,KAAK,eAAe;OAChB;KAEV;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;OAClC;KAEV;IACF;IACD;GACG;;AAIX,yBAAe"}
1
+ {"version":3,"file":"AdminFiles-WeQbsCsl.js","names":[],"sources":["../../src/admin/components/files/AdminFiles.tsx"],"sourcesContent":["import { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport { type Page, t } from \"alepha\";\nimport { type FileController, type FileEntity, files } from \"alepha/api/files\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\n\nconst AdminFiles = () => {\n const client = useClient<FileController>();\n const { l } = useI18n();\n\n const filters = t.object({\n bucket: t.optional(t.string()),\n name: t.optional(\n t.string({\n $control: {\n query: t.pick(files.schema, [\"name\", \"bucket\", \"mimeType\"]),\n },\n }),\n ),\n });\n\n const formatFileSize = (bytes: number) => {\n if (bytes === 0) return \"0 B\";\n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;\n };\n\n return (\n <Flex flex={1} direction={\"column\"}>\n <DataTable<FileEntity, typeof filters>\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"name\" || key === \"bucket\") {\n return form.submit();\n }\n }}\n filters={filters}\n items={async (filters) => {\n const response = await client.findFiles({\n query: filters,\n });\n\n return response as Page<FileEntity>;\n }}\n columns={{\n name: {\n label: \"Name\",\n value: (item) => (\n <Text size=\"sm\" fw={500} lineClamp={1}>\n {item.name}\n </Text>\n ),\n },\n bucket: {\n label: \"Bucket\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color=\"blue\">\n {item.bucket}\n </Badge>\n ),\n },\n mimeType: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.mimeType}\n </Text>\n ),\n },\n size: {\n label: \"Size\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {formatFileSize(item.size)}\n </Text>\n ),\n },\n creatorName: {\n label: \"Creator\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.creatorName || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminFiles;\n"],"mappings":";;;;;;;;;AAOA,MAAM,mBAAmB;CACvB,MAAM,SAAS,WAA2B;CAC1C,MAAM,EAAE,MAAM,SAAS;CAEvB,MAAM,UAAU,EAAE,OAAO;EACvB,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC;EAC9B,MAAM,EAAE,SACN,EAAE,OAAO,EACP,UAAU,EACR,OAAO,EAAE,KAAK,MAAM,QAAQ;GAAC;GAAQ;GAAU;GAAW,CAAC,EAC5D,EACF,CAAC,CACH;EACF,CAAC;CAEF,MAAM,kBAAkB,UAAkB;AACxC,MAAI,UAAU,EAAG,QAAO;EACxB,MAAM,IAAI;EACV,MAAM,QAAQ;GAAC;GAAK;GAAM;GAAM;GAAK;EACrC,MAAM,IAAI,KAAK,MAAM,KAAK,IAAI,MAAM,GAAG,KAAK,IAAI,EAAE,CAAC;AACnD,SAAO,GAAG,OAAO,YAAY,QAAQ,KAAK,GAAG,QAAQ,EAAE,CAAC,CAAC,GAAG,MAAM;;AAGpE,QACE,oBAAC;EAAK,MAAM;EAAG,WAAW;YACxB,oBAAC;GACC;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IAClB;GACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,QAAI,QAAQ,UAAU,QAAQ,SAC5B,QAAO,KAAK,QAAQ;;GAGf;GACT,OAAO,OAAO,YAAY;AAKxB,WAJiB,MAAM,OAAO,UAAU,EACtC,OAAO,SACR,CAAC;;GAIJ,SAAS;IACP,MAAM;KACJ,OAAO;KACP,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,WAAW;gBACjC,KAAK;OACD;KAEV;IACD,QAAQ;KACN,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAM,MAAK;MAAK,SAAQ;MAAQ,OAAM;gBACpC,KAAK;OACA;KAEX;IACD,UAAU;KACR,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,KAAK;OACD;KAEV;IACD,MAAM;KACJ,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,eAAe,KAAK,KAAK;OACrB;KAEV;IACD,aAAa;KACX,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,KAAK,eAAe;OAChB;KAEV;IACD,WAAW;KACT,OAAO;KACP,KAAK;KACL,QAAQ,SACN,oBAAC;MAAK,MAAK;MAAK,GAAE;gBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;OAClC;KAEV;IACF;IACD;GACG;;AAIX,yBAAe"}