@alepha/ui 0.16.2 → 0.17.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 (175) hide show
  1. package/dist/admin/{AdminApiKeys-CoTOTfgU.js → AdminApiKeys-CF_qOO3u.js} +20 -20
  2. package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +1 -0
  3. package/dist/admin/{AdminAudits-BmsxFbDa.js → AdminAudits-BQno3hZG.js} +7 -8
  4. package/dist/admin/AdminAudits-BQno3hZG.js.map +1 -0
  5. package/dist/admin/{AdminFiles-BBB8knca.js → AdminFiles-kvuUaASF.js} +3 -5
  6. package/dist/admin/{AdminFiles-BBB8knca.js.map → AdminFiles-kvuUaASF.js.map} +1 -1
  7. package/dist/admin/AdminJobDashboard-CrPxp0W1.js +485 -0
  8. package/dist/admin/AdminJobDashboard-CrPxp0W1.js.map +1 -0
  9. package/dist/admin/AdminJobExecutions-D-b4Zt7W.js +678 -0
  10. package/dist/admin/AdminJobExecutions-D-b4Zt7W.js.map +1 -0
  11. package/dist/admin/AdminJobRegistry-CNX5cpDx.js +301 -0
  12. package/dist/admin/AdminJobRegistry-CNX5cpDx.js.map +1 -0
  13. package/dist/admin/{AdminLayout-CsjvpeD1.js → AdminLayout-e-ZP5nWw.js} +1 -1
  14. package/dist/admin/{AdminLayout-CsjvpeD1.js.map → AdminLayout-e-ZP5nWw.js.map} +1 -1
  15. package/dist/admin/{AdminNotifications-LwR6RKrx.js → AdminNotifications-DeHJFf6W.js} +3 -5
  16. package/dist/admin/{AdminNotifications-LwR6RKrx.js.map → AdminNotifications-DeHJFf6W.js.map} +1 -1
  17. package/dist/admin/{AdminParameters-B_83Vie9.js → AdminParameters-iQE8o7a7.js} +43 -36
  18. package/dist/admin/AdminParameters-iQE8o7a7.js.map +1 -0
  19. package/dist/admin/{AdminSessions-CWnPosdd.js → AdminSessions-oKJCbd7w.js} +5 -7
  20. package/dist/admin/AdminSessions-oKJCbd7w.js.map +1 -0
  21. package/dist/admin/{AdminUserAudits-nHv636E_.js → AdminUserAudits-BNCEle_E.js} +6 -8
  22. package/dist/admin/AdminUserAudits-BNCEle_E.js.map +1 -0
  23. package/dist/admin/{AdminUserCreate-CjYD3Kjc.js → AdminUserCreate-CgqeFwCt.js} +6 -7
  24. package/dist/admin/AdminUserCreate-CgqeFwCt.js.map +1 -0
  25. package/dist/admin/{AdminUserDetails-Ccq-LsZ0.js → AdminUserDetails-DDe1A1GP.js} +30 -29
  26. package/dist/admin/AdminUserDetails-DDe1A1GP.js.map +1 -0
  27. package/dist/admin/{AdminUserLayout-7s41DiF_.js → AdminUserLayout-HAlobhWf.js} +18 -16
  28. package/dist/admin/AdminUserLayout-HAlobhWf.js.map +1 -0
  29. package/dist/admin/{AdminUserSessions-Ds3ODq_d.js → AdminUserSessions-Bq1LnVLf.js} +5 -7
  30. package/dist/admin/AdminUserSessions-Bq1LnVLf.js.map +1 -0
  31. package/dist/admin/{AdminUserSettings-CGh4gROo.js → AdminUserSettings-BRsBZoxV.js} +10 -10
  32. package/dist/admin/AdminUserSettings-BRsBZoxV.js.map +1 -0
  33. package/dist/admin/{AdminUsers-CvPiBzQK.js → AdminUsers-D71kIOSn.js} +6 -8
  34. package/dist/admin/AdminUsers-D71kIOSn.js.map +1 -0
  35. package/dist/admin/index.d.ts +7 -83
  36. package/dist/admin/index.d.ts.map +1 -1
  37. package/dist/admin/index.js +49 -70
  38. package/dist/admin/index.js.map +1 -1
  39. package/dist/auth/{Login-DS_OqA0G.js → Login-BS_FYTy0.js} +13 -8
  40. package/dist/auth/Login-BS_FYTy0.js.map +1 -0
  41. package/dist/auth/{Profile-Di7N7HZL.js → Profile-CjDsW378.js} +16 -10
  42. package/dist/auth/Profile-CjDsW378.js.map +1 -0
  43. package/dist/auth/{Register-BRR2_gux.js → Register-C5eqzAaD.js} +21 -12
  44. package/dist/auth/Register-C5eqzAaD.js.map +1 -0
  45. package/dist/auth/{ResetPassword-oQu72lod.js → ResetPassword-XifinVao.js} +14 -8
  46. package/dist/auth/ResetPassword-XifinVao.js.map +1 -0
  47. package/dist/auth/{VerifyEmail-DC6HPZjd.js → VerifyEmail-DTgbeJOO.js} +6 -4
  48. package/dist/auth/VerifyEmail-DTgbeJOO.js.map +1 -0
  49. package/dist/auth/index.d.ts +4 -0
  50. package/dist/auth/index.d.ts.map +1 -1
  51. package/dist/auth/index.js +15 -14
  52. package/dist/auth/index.js.map +1 -1
  53. package/dist/core/index.d.ts +37 -26
  54. package/dist/core/index.d.ts.map +1 -1
  55. package/dist/core/index.js +444 -193
  56. package/dist/core/index.js.map +1 -1
  57. package/dist/demo/DemoDataTable-lnBKWBf8.js +362 -0
  58. package/dist/demo/DemoDataTable-lnBKWBf8.js.map +1 -0
  59. package/dist/demo/{DemoHome-DpRrPlBC.js → DemoHome-CUMZsYaH.js} +6 -7
  60. package/dist/demo/DemoHome-CUMZsYaH.js.map +1 -0
  61. package/dist/demo/{DemoJsonViewer-zeucGKHV.js → DemoJsonViewer-_uokbGaW.js} +17 -19
  62. package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +1 -0
  63. package/dist/demo/{DemoLayout-PhgbAAiQ.js → DemoLayout-DHVoacE6.js} +2 -4
  64. package/dist/demo/{DemoLayout-PhgbAAiQ.js.map → DemoLayout-DHVoacE6.js.map} +1 -1
  65. package/dist/demo/{DemoLogin-DSzP0Lkv.js → DemoLogin-DjJ9314c.js} +22 -17
  66. package/dist/demo/DemoLogin-DjJ9314c.js.map +1 -0
  67. package/dist/demo/{DemoRegister-DavFBsCz.js → DemoRegister-DzkJ5M83.js} +34 -25
  68. package/dist/demo/DemoRegister-DzkJ5M83.js.map +1 -0
  69. package/dist/demo/{DemoResetPassword-BS2rIAQK.js → DemoResetPassword-DWh4_BpQ.js} +27 -21
  70. package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +1 -0
  71. package/dist/demo/{DemoSidebar-zNkUmHRl.js → DemoSidebar-C1csnGhX.js} +2 -2
  72. package/dist/demo/{DemoSidebar-zNkUmHRl.js.map → DemoSidebar-C1csnGhX.js.map} +1 -1
  73. package/dist/demo/{DemoTypeForm-B9q7oT0b.js → DemoTypeForm-CWz6fJrJ.js} +2 -2
  74. package/dist/demo/{DemoTypeForm-B9q7oT0b.js.map → DemoTypeForm-CWz6fJrJ.js.map} +1 -1
  75. package/dist/demo/{DemoVerifyEmail-Bi4SdWz0.js → DemoVerifyEmail-DbU_tCj8.js} +13 -11
  76. package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +1 -0
  77. package/dist/demo/{IconGoogle-CTeZyrek.js → IconGoogle-Ch1m3Uzl.js} +1 -1
  78. package/dist/demo/{IconGoogle-CTeZyrek.js.map → IconGoogle-Ch1m3Uzl.js.map} +1 -1
  79. package/dist/demo/{Showcase-C9btr_SJ.js → Showcase-BzoXNlCn.js} +10 -10
  80. package/dist/demo/Showcase-BzoXNlCn.js.map +1 -0
  81. package/dist/demo/index.d.ts +1 -68
  82. package/dist/demo/index.d.ts.map +1 -1
  83. package/dist/demo/index.js +11 -15
  84. package/dist/demo/index.js.map +1 -1
  85. package/dist/json/index.js +2 -2
  86. package/dist/json/index.js.map +1 -1
  87. package/package.json +9 -5
  88. package/src/admin/AdminRouter.ts +36 -5
  89. package/src/admin/components/audits/AdminAudits.tsx +5 -5
  90. package/src/admin/components/jobs/AdminJobDashboard.tsx +455 -0
  91. package/src/admin/components/jobs/AdminJobExecutions.tsx +693 -0
  92. package/src/admin/components/jobs/AdminJobRegistry.tsx +325 -0
  93. package/src/admin/components/keys/AdminApiKeys.tsx +28 -31
  94. package/src/admin/components/parameters/AdminParameters.tsx +3 -3
  95. package/src/admin/components/parameters/ParameterDetails.tsx +34 -29
  96. package/src/admin/components/parameters/ParameterEmptyState.tsx +5 -5
  97. package/src/admin/components/parameters/ParameterHistory.tsx +11 -19
  98. package/src/admin/components/parameters/ParameterTree.tsx +16 -18
  99. package/src/admin/components/sessions/AdminSessions.tsx +3 -3
  100. package/src/admin/components/shared/AdminResourceHeader.tsx +20 -16
  101. package/src/admin/components/users/AdminUserAudits.tsx +5 -5
  102. package/src/admin/components/users/AdminUserCreate.tsx +3 -3
  103. package/src/admin/components/users/AdminUserDetails.tsx +51 -53
  104. package/src/admin/components/users/AdminUserLayout.tsx +7 -7
  105. package/src/admin/components/users/AdminUserSessions.tsx +3 -3
  106. package/src/admin/components/users/AdminUserSettings.tsx +9 -9
  107. package/src/admin/components/users/AdminUsers.tsx +5 -5
  108. package/src/admin/components/verifications/AdminVerifications.tsx +3 -3
  109. package/src/admin/index.ts +0 -24
  110. package/src/auth/components/Login.tsx +13 -13
  111. package/src/auth/components/Profile.tsx +17 -26
  112. package/src/auth/components/Register.tsx +21 -31
  113. package/src/auth/components/ResetPassword.tsx +13 -22
  114. package/src/auth/components/VerifyEmail.tsx +5 -5
  115. package/src/auth/components/buttons/UserButton.tsx +14 -4
  116. package/src/core/components/buttons/ActionButton.tsx +9 -2
  117. package/src/core/components/data/ErrorViewer.tsx +15 -15
  118. package/src/core/components/dialogs/AlertDialog.tsx +3 -3
  119. package/src/core/components/dialogs/ConfirmDialog.tsx +3 -3
  120. package/src/core/components/dialogs/PromptDialog.tsx +3 -3
  121. package/src/core/components/form/Control.tsx +9 -0
  122. package/src/core/components/form/ControlArray.tsx +6 -7
  123. package/src/core/components/form/ControlObject.tsx +3 -3
  124. package/src/core/components/form/ControlQueryBuilder.tsx +20 -22
  125. package/src/core/components/form/ControlSelect.tsx +4 -0
  126. package/src/core/components/form/TypeForm.tsx +7 -0
  127. package/src/core/components/layout/Breadcrumb.tsx +6 -6
  128. package/src/core/components/layout/Omnibar.tsx +2 -1
  129. package/src/core/components/layout/Sidebar.tsx +5 -1
  130. package/src/core/components/table/ColumnPicker.tsx +47 -31
  131. package/src/core/components/table/DataTable.tsx +277 -201
  132. package/src/core/components/table/DataTableFilters.tsx +8 -0
  133. package/src/core/components/table/DataTableToolbar.tsx +98 -5
  134. package/src/core/components/table/FilterPicker.tsx +28 -26
  135. package/src/core/components/table/types.ts +52 -37
  136. package/src/core/components/table/useTableSelection.ts +83 -0
  137. package/src/core/styles.css +1 -0
  138. package/src/core/utils/parseInput.ts +1 -0
  139. package/src/demo/components/DemoHome.tsx +5 -5
  140. package/src/demo/components/core/DemoDataTable.tsx +209 -5
  141. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  142. package/src/demo/components/shared/MacWindow.tsx +7 -7
  143. package/src/demo/components/shared/Showcase.tsx +3 -3
  144. package/src/demo/index.ts +0 -11
  145. package/src/json/components/JsonViewer.tsx +3 -3
  146. package/dist/admin/AdminApiKeys-CoTOTfgU.js.map +0 -1
  147. package/dist/admin/AdminAudits-BmsxFbDa.js.map +0 -1
  148. package/dist/admin/AdminJobs-C604joTz.js +0 -698
  149. package/dist/admin/AdminJobs-C604joTz.js.map +0 -1
  150. package/dist/admin/AdminParameters-B_83Vie9.js.map +0 -1
  151. package/dist/admin/AdminSessions-CWnPosdd.js.map +0 -1
  152. package/dist/admin/AdminUserAudits-nHv636E_.js.map +0 -1
  153. package/dist/admin/AdminUserCreate-CjYD3Kjc.js.map +0 -1
  154. package/dist/admin/AdminUserDetails-Ccq-LsZ0.js.map +0 -1
  155. package/dist/admin/AdminUserLayout-7s41DiF_.js.map +0 -1
  156. package/dist/admin/AdminUserSessions-Ds3ODq_d.js.map +0 -1
  157. package/dist/admin/AdminUserSettings-CGh4gROo.js.map +0 -1
  158. package/dist/admin/AdminUsers-CvPiBzQK.js.map +0 -1
  159. package/dist/admin/rolldown-runtime-CjeV3_4I.js +0 -18
  160. package/dist/auth/Login-DS_OqA0G.js.map +0 -1
  161. package/dist/auth/Profile-Di7N7HZL.js.map +0 -1
  162. package/dist/auth/Register-BRR2_gux.js.map +0 -1
  163. package/dist/auth/ResetPassword-oQu72lod.js.map +0 -1
  164. package/dist/auth/VerifyEmail-DC6HPZjd.js.map +0 -1
  165. package/dist/demo/DemoDataTable-DCsJq8v5.js +0 -149
  166. package/dist/demo/DemoDataTable-DCsJq8v5.js.map +0 -1
  167. package/dist/demo/DemoHome-DpRrPlBC.js.map +0 -1
  168. package/dist/demo/DemoJsonViewer-zeucGKHV.js.map +0 -1
  169. package/dist/demo/DemoLogin-DSzP0Lkv.js.map +0 -1
  170. package/dist/demo/DemoRegister-DavFBsCz.js.map +0 -1
  171. package/dist/demo/DemoResetPassword-BS2rIAQK.js.map +0 -1
  172. package/dist/demo/DemoVerifyEmail-Bi4SdWz0.js.map +0 -1
  173. package/dist/demo/Showcase-C9btr_SJ.js.map +0 -1
  174. package/dist/demo/rolldown-runtime-CjeV3_4I.js +0 -18
  175. package/src/admin/components/jobs/AdminJobs.tsx +0 -772
@@ -0,0 +1,325 @@
1
+ import { DataTable, Flex, Text, useDialog, useToast } from "@alepha/ui";
2
+ import { Badge } from "@mantine/core";
3
+ import {
4
+ IconCircleCheck,
5
+ IconCircleX,
6
+ IconPlayerPlay,
7
+ } from "@tabler/icons-react";
8
+ import { t } from "alepha";
9
+ import type {
10
+ AdminJobController,
11
+ JobCronInfo,
12
+ JobFailure,
13
+ JobQueueDepth,
14
+ JobRegistration,
15
+ } from "alepha/api/jobs";
16
+ import { useClient } from "alepha/react";
17
+ import { useI18n } from "alepha/react/i18n";
18
+ import { useCallback, useEffect, useState } from "react";
19
+
20
+ // ─────────────────────────────────────────────────────────────────────────────
21
+
22
+ const getTypeColor = (type: string) => {
23
+ switch (type) {
24
+ case "cron":
25
+ return "violet";
26
+ case "push":
27
+ return "blue";
28
+ case "both":
29
+ return "teal";
30
+ default:
31
+ return "gray";
32
+ }
33
+ };
34
+
35
+ const registryFilters = t.object({
36
+ type: t.optional(t.enum(["cron", "push", "both"])),
37
+ });
38
+
39
+ // ─────────────────────────────────────────────────────────────────────────────
40
+
41
+ const AdminJobRegistry = () => {
42
+ const client = useClient<AdminJobController>();
43
+ const { l } = useI18n();
44
+ const toast = useToast();
45
+ const dialog = useDialog();
46
+ const [refreshKey, setRefreshKey] = useState(0);
47
+
48
+ // Extra data for enriched panels
49
+ const [cronMap, setCronMap] = useState<Map<string, JobCronInfo>>(new Map());
50
+ const [queueMap, setQueueMap] = useState<Map<string, JobQueueDepth>>(
51
+ new Map(),
52
+ );
53
+ const [failureMap, setFailureMap] = useState<Map<string, JobFailure>>(
54
+ new Map(),
55
+ );
56
+
57
+ const loadExtraData = useCallback(async () => {
58
+ try {
59
+ const [cronData, queueData, failureData] = await Promise.all([
60
+ client.getCronJobs(),
61
+ client.getQueueDepth(),
62
+ client.getTopFailures(),
63
+ ]);
64
+ setCronMap(new Map(cronData.map((c) => [c.name, c])));
65
+ setQueueMap(new Map(queueData.map((q) => [q.jobName, q])));
66
+ setFailureMap(new Map(failureData.map((f) => [f.jobName, f])));
67
+ } catch {
68
+ // non-critical
69
+ }
70
+ }, [client]);
71
+
72
+ useEffect(() => {
73
+ loadExtraData();
74
+ }, [loadExtraData, refreshKey]);
75
+
76
+ const handleTriggerJob = useCallback(
77
+ async (name: string) => {
78
+ const confirmed = await dialog.confirm({
79
+ title: "Trigger Job",
80
+ message: `Are you sure you want to trigger "${name}" manually?`,
81
+ confirmLabel: "Trigger",
82
+ confirmColor: "blue",
83
+ });
84
+
85
+ if (!confirmed) return;
86
+
87
+ return client.triggerJob({ body: { name } }).then(() => {
88
+ toast.success(`Job "${name}" triggered`);
89
+ setRefreshKey((k) => k + 1);
90
+ });
91
+ },
92
+ [client, dialog, toast],
93
+ );
94
+
95
+ return (
96
+ <Flex flex={1} direction="column" gap="md">
97
+ <DataTable<JobRegistration, typeof registryFilters>
98
+ key={`registry-${refreshKey}`}
99
+ submitOnInit
100
+ typeFormProps={{
101
+ skipSubmitButton: true,
102
+ columns: 1,
103
+ }}
104
+ tableProps={{
105
+ horizontalSpacing: "sm",
106
+ verticalSpacing: "sm",
107
+ highlightOnHover: true,
108
+ }}
109
+ onFilterChange={(_key, _value, form) => form.submit()}
110
+ filters={registryFilters}
111
+ items={async (filters) => {
112
+ const items = await client.getRegistry();
113
+ const filtered = filters.type
114
+ ? items.filter((i) => i.type === filters.type)
115
+ : items;
116
+ return { content: filtered };
117
+ }}
118
+ columns={{
119
+ name: {
120
+ label: "Name",
121
+ value: (item) => (
122
+ <Text size="sm" fw={500} ff="monospace">
123
+ {item.name}
124
+ </Text>
125
+ ),
126
+ },
127
+ type: {
128
+ label: "Type",
129
+ fit: true,
130
+ value: (item) => (
131
+ <Badge size="sm" variant="light" color={getTypeColor(item.type)}>
132
+ {item.type}
133
+ </Badge>
134
+ ),
135
+ },
136
+ priority: {
137
+ label: "Priority",
138
+ fit: true,
139
+ value: (item) => (
140
+ <Text size="sm" tt="capitalize">
141
+ {item.priority}
142
+ </Text>
143
+ ),
144
+ },
145
+ concurrency: {
146
+ label: "Concurrency",
147
+ fit: true,
148
+ value: (item) => (
149
+ <Text size="sm" ff="monospace">
150
+ {item.concurrency}
151
+ </Text>
152
+ ),
153
+ },
154
+ queue: {
155
+ label: "Queue",
156
+ fit: true,
157
+ value: (item) => {
158
+ const q = queueMap.get(item.name);
159
+ if (
160
+ !q ||
161
+ q.pending + q.running + q.scheduled + q.retrying + q.dead === 0
162
+ ) {
163
+ return (
164
+ <Text size="xs" c="dimmed">
165
+
166
+ </Text>
167
+ );
168
+ }
169
+ return (
170
+ <Flex gap={4}>
171
+ {q.running > 0 && (
172
+ <Badge size="xs" variant="light" color="blue">
173
+ {q.running} run
174
+ </Badge>
175
+ )}
176
+ {q.pending > 0 && (
177
+ <Badge size="xs" variant="light" color="gray">
178
+ {q.pending} pen
179
+ </Badge>
180
+ )}
181
+ {q.retrying > 0 && (
182
+ <Badge size="xs" variant="light" color="yellow">
183
+ {q.retrying} retry
184
+ </Badge>
185
+ )}
186
+ {q.dead > 0 && (
187
+ <Badge size="xs" variant="light" color="red">
188
+ {q.dead} dead
189
+ </Badge>
190
+ )}
191
+ </Flex>
192
+ );
193
+ },
194
+ },
195
+ actions: {
196
+ label: "",
197
+ fit: true,
198
+ actions: (item) => [
199
+ {
200
+ tooltip: "Trigger",
201
+ color: "blue",
202
+ icon: IconPlayerPlay,
203
+ onClick: () => handleTriggerJob(item.name),
204
+ },
205
+ ],
206
+ },
207
+ }}
208
+ panel={(item) => {
209
+ const cron = cronMap.get(item.name);
210
+ const failure = failureMap.get(item.name);
211
+
212
+ return (
213
+ <Flex direction="column" gap="sm" p="sm">
214
+ {/* Config */}
215
+ <Flex gap="lg" wrap="wrap">
216
+ {item.cron && (
217
+ <PanelField label="Cron" value={item.cron} monospace />
218
+ )}
219
+ {item.timeout && (
220
+ <PanelField label="Timeout" value={item.timeout} />
221
+ )}
222
+ {item.retry && (
223
+ <PanelField
224
+ label="Retry"
225
+ value={`${item.retry.retries}x${item.retry.hasBackoff ? " (backoff)" : ""}`}
226
+ />
227
+ )}
228
+ {item.batch && (
229
+ <PanelField
230
+ label="Batch"
231
+ value={`${item.batch.size} / ${item.batch.window}`}
232
+ />
233
+ )}
234
+ <PanelField
235
+ label="Schema"
236
+ value={item.hasSchema ? "Yes" : "No"}
237
+ />
238
+ </Flex>
239
+
240
+ {/* Last cron execution */}
241
+ {cron?.lastExecution && (
242
+ <Flex gap="lg" wrap="wrap" align="center">
243
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
244
+ Last Run
245
+ </Text>
246
+ <Flex align="center" gap={4}>
247
+ {cron.lastExecution.status === "completed" ? (
248
+ <IconCircleCheck
249
+ size={14}
250
+ color="var(--mantine-color-teal-6)"
251
+ />
252
+ ) : (
253
+ <IconCircleX
254
+ size={14}
255
+ color="var(--mantine-color-red-6)"
256
+ />
257
+ )}
258
+ <Text size="xs" tt="capitalize">
259
+ {cron.lastExecution.status}
260
+ </Text>
261
+ </Flex>
262
+ {cron.lastExecution.startedAt && (
263
+ <Text size="xs" c="dimmed">
264
+ {l(cron.lastExecution.startedAt, { date: "fromNow" })}
265
+ </Text>
266
+ )}
267
+ {cron.lastExecution.error && (
268
+ <Text size="xs" c="red" lineClamp={1}>
269
+ {cron.lastExecution.error}
270
+ </Text>
271
+ )}
272
+ </Flex>
273
+ )}
274
+
275
+ {/* Failures */}
276
+ {failure && (
277
+ <Flex gap="lg" wrap="wrap" align="center">
278
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
279
+ Failures (7d)
280
+ </Text>
281
+ <Badge size="xs" variant="light" color="red">
282
+ {failure.failures}
283
+ </Badge>
284
+ {failure.lastError && (
285
+ <Text
286
+ size="xs"
287
+ c="dimmed"
288
+ lineClamp={1}
289
+ style={{ maxWidth: 400 }}
290
+ >
291
+ {failure.lastError}
292
+ </Text>
293
+ )}
294
+ </Flex>
295
+ )}
296
+ </Flex>
297
+ );
298
+ }}
299
+ />
300
+ </Flex>
301
+ );
302
+ };
303
+
304
+ // ─────────────────────────────────────────────────────────────────────────────
305
+
306
+ const PanelField = ({
307
+ label,
308
+ value,
309
+ monospace,
310
+ }: {
311
+ label: string;
312
+ value: string;
313
+ monospace?: boolean;
314
+ }) => (
315
+ <Flex direction="column" gap={2}>
316
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
317
+ {label}
318
+ </Text>
319
+ <Text size="sm" ff={monospace ? "monospace" : undefined}>
320
+ {value}
321
+ </Text>
322
+ </Flex>
323
+ );
324
+
325
+ export default AdminJobRegistry;
@@ -10,13 +10,10 @@ import {
10
10
  import {
11
11
  ActionIcon,
12
12
  Badge,
13
- Box,
14
13
  Code,
15
- Group,
16
14
  Paper,
17
15
  RingProgress,
18
16
  SimpleGrid,
19
- Stack,
20
17
  ThemeIcon,
21
18
  Tooltip,
22
19
  } from "@mantine/core";
@@ -121,50 +118,50 @@ const StatsCards = ({ stats, loading }: StatsCardsProps) => {
121
118
  return (
122
119
  <SimpleGrid cols={{ base: 2, sm: 4 }} spacing="md">
123
120
  <Paper p="md" radius="md" withBorder>
124
- <Group justify="space-between">
125
- <Box>
121
+ <Flex justify="space-between">
122
+ <Flex>
126
123
  <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
127
124
  Total Keys
128
125
  </Text>
129
126
  <Text size="xl" fw={700} ff="monospace">
130
127
  {stats.total}
131
128
  </Text>
132
- </Box>
129
+ </Flex>
133
130
  <ThemeIcon size="lg" radius="md" variant="light" color="blue">
134
131
  <IconKey size={20} />
135
132
  </ThemeIcon>
136
- </Group>
133
+ </Flex>
137
134
  </Paper>
138
135
 
139
136
  <Paper p="md" radius="md" withBorder>
140
- <Group justify="space-between">
141
- <Box>
137
+ <Flex justify="space-between">
138
+ <Flex>
142
139
  <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
143
140
  Active
144
141
  </Text>
145
142
  <Text size="xl" fw={700} ff="monospace" c="teal">
146
143
  {stats.active}
147
144
  </Text>
148
- </Box>
145
+ </Flex>
149
146
  <RingProgress
150
147
  size={48}
151
148
  thickness={4}
152
149
  roundCaps
153
150
  sections={[{ value: activePercentage, color: "teal" }]}
154
151
  />
155
- </Group>
152
+ </Flex>
156
153
  </Paper>
157
154
 
158
155
  <Paper p="md" radius="md" withBorder>
159
- <Group justify="space-between">
160
- <Box>
156
+ <Flex justify="space-between">
157
+ <Flex>
161
158
  <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
162
159
  Revoked
163
160
  </Text>
164
161
  <Text size="xl" fw={700} ff="monospace" c="red">
165
162
  {stats.revoked}
166
163
  </Text>
167
- </Box>
164
+ </Flex>
168
165
  <ThemeIcon
169
166
  size="lg"
170
167
  radius="md"
@@ -173,19 +170,19 @@ const StatsCards = ({ stats, loading }: StatsCardsProps) => {
173
170
  >
174
171
  <IconShieldOff size={20} />
175
172
  </ThemeIcon>
176
- </Group>
173
+ </Flex>
177
174
  </Paper>
178
175
 
179
176
  <Paper p="md" radius="md" withBorder>
180
- <Group justify="space-between">
181
- <Box>
177
+ <Flex justify="space-between">
178
+ <Flex>
182
179
  <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
183
180
  Never Used
184
181
  </Text>
185
182
  <Text size="xl" fw={700} ff="monospace" c="yellow">
186
183
  {stats.neverUsed}
187
184
  </Text>
188
- </Box>
185
+ </Flex>
189
186
  <ThemeIcon
190
187
  size="lg"
191
188
  radius="md"
@@ -194,7 +191,7 @@ const StatsCards = ({ stats, loading }: StatsCardsProps) => {
194
191
  >
195
192
  <IconClock size={20} />
196
193
  </ThemeIcon>
197
- </Group>
194
+ </Flex>
198
195
  </Paper>
199
196
  </SimpleGrid>
200
197
  );
@@ -338,8 +335,8 @@ const AdminApiKeys = () => {
338
335
  name: {
339
336
  label: "Name",
340
337
  value: (item) => (
341
- <Stack gap={2}>
342
- <Group gap="xs">
338
+ <Flex direction="column" gap={2}>
339
+ <Flex gap="xs">
343
340
  <ThemeIcon
344
341
  size="xs"
345
342
  radius="sm"
@@ -351,20 +348,20 @@ const AdminApiKeys = () => {
351
348
  <Text size="sm" fw={600}>
352
349
  {item.name}
353
350
  </Text>
354
- </Group>
351
+ </Flex>
355
352
  {item.description && (
356
353
  <Text size="xs" c="dimmed" lineClamp={1}>
357
354
  {item.description}
358
355
  </Text>
359
356
  )}
360
- </Stack>
357
+ </Flex>
361
358
  ),
362
359
  },
363
360
  token: {
364
361
  label: "Key",
365
362
  fit: true,
366
363
  value: (item) => (
367
- <Group gap={4}>
364
+ <Flex gap={4}>
368
365
  <Code
369
366
  ff="monospace"
370
367
  style={{
@@ -379,7 +376,7 @@ const AdminApiKeys = () => {
379
376
  variant="subtle"
380
377
  value={formatKeyPreview(item.tokenPrefix, item.tokenSuffix)}
381
378
  />
382
- </Group>
379
+ </Flex>
383
380
  ),
384
381
  },
385
382
  status: {
@@ -402,7 +399,7 @@ const AdminApiKeys = () => {
402
399
  roles: {
403
400
  label: "Roles",
404
401
  value: (item) => (
405
- <Group gap={4} wrap="wrap">
402
+ <Flex gap={4} wrap="wrap">
406
403
  {item.roles.length > 0 ? (
407
404
  item.roles.slice(0, 3).map((role) => (
408
405
  <Badge key={role} size="xs" variant="outline" color="gray">
@@ -421,19 +418,19 @@ const AdminApiKeys = () => {
421
418
  </Badge>
422
419
  </Tooltip>
423
420
  )}
424
- </Group>
421
+ </Flex>
425
422
  ),
426
423
  },
427
424
  usage: {
428
425
  label: "Usage",
429
426
  fit: true,
430
427
  value: (item) => (
431
- <Stack gap={2}>
428
+ <Flex direction="column" gap={2}>
432
429
  <Text size="xs" ff="monospace" fw={500}>
433
430
  {item.usageCount.toLocaleString()} calls
434
431
  </Text>
435
432
  {item.lastUsedAt ? (
436
- <Group gap={4}>
433
+ <Flex gap={4}>
437
434
  <Text size="xs" c="dimmed">
438
435
  {l(item.lastUsedAt, { date: "fromNow" })}
439
436
  </Text>
@@ -445,13 +442,13 @@ const AdminApiKeys = () => {
445
442
  />
446
443
  </Tooltip>
447
444
  )}
448
- </Group>
445
+ </Flex>
449
446
  ) : (
450
447
  <Text size="xs" c="yellow">
451
448
  Never used
452
449
  </Text>
453
450
  )}
454
- </Stack>
451
+ </Flex>
455
452
  ),
456
453
  },
457
454
  userId: {
@@ -1,5 +1,5 @@
1
1
  import { Text, useToast } from "@alepha/ui";
2
- import { Card, Flex, Stack } from "@mantine/core";
2
+ import { Card, Flex } from "@mantine/core";
3
3
  import { IconSettings } from "@tabler/icons-react";
4
4
  import type {
5
5
  AdminParameterController,
@@ -153,7 +153,7 @@ const AdminParameters = ({
153
153
  if (treeData.length === 0) {
154
154
  return (
155
155
  <Flex flex={1} justify="center" align="center">
156
- <Stack align="center" gap="xs">
156
+ <Flex direction="column" align="center" gap="xs">
157
157
  <IconSettings
158
158
  size={48}
159
159
  stroke={1.5}
@@ -164,7 +164,7 @@ const AdminParameters = ({
164
164
  Define parameters using the $parameter primitive to manage dynamic
165
165
  application settings. Parameters will appear here once created.
166
166
  </Text>
167
- </Stack>
167
+ </Flex>
168
168
  </Flex>
169
169
  );
170
170
  }