@alepha/ui 0.15.4 → 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 +24 -36
  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,697 @@
1
+ import { ActionButton, DataTable, Flex, Text, useDialog, useToast } from "@alepha/ui";
2
+ import { t } from "alepha";
3
+ import { IconAlertTriangle, IconCircleCheck, IconCircleX, IconClock, IconPlayerPlay, IconRefresh, IconTerminal2 } from "@tabler/icons-react";
4
+ import { ActionIcon, Badge, Box, Card, Group, Paper, Progress, RingProgress, ScrollArea, SimpleGrid, Skeleton, Stack, Tabs, ThemeIcon, Tooltip, useMantineTheme } from "@mantine/core";
5
+ import { useClient } from "alepha/react";
6
+ import { useI18n } from "alepha/react/i18n";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ import { jobExecutions } from "alepha/api/jobs";
9
+ import { useCallback, useEffect, useState } from "react";
10
+
11
+ //#region ../../src/admin/components/jobs/AdminJobs.tsx
12
+ const formatDuration = (start, end) => {
13
+ const startTime = new Date(start).getTime();
14
+ const duration = (end ? new Date(end).getTime() : Date.now()) - startTime;
15
+ if (duration < 1e3) return `${duration}ms`;
16
+ if (duration < 6e4) return `${(duration / 1e3).toFixed(1)}s`;
17
+ if (duration < 36e5) return `${Math.floor(duration / 6e4)}m ${Math.floor(duration % 6e4 / 1e3)}s`;
18
+ return `${Math.floor(duration / 36e5)}h ${Math.floor(duration % 36e5 / 6e4)}m`;
19
+ };
20
+ const getStatusColor = (status) => {
21
+ switch (status) {
22
+ case "COMPLETED": return "teal";
23
+ case "FAILED": return "red";
24
+ case "STARTED": return "blue";
25
+ default: return "gray";
26
+ }
27
+ };
28
+ const getStatusIcon = (status, size = 14) => {
29
+ switch (status) {
30
+ case "COMPLETED": return /* @__PURE__ */ jsx(IconCircleCheck, { size });
31
+ case "FAILED": return /* @__PURE__ */ jsx(IconCircleX, { size });
32
+ case "STARTED": return /* @__PURE__ */ jsx(IconPlayerPlay, { size });
33
+ default: return /* @__PURE__ */ jsx(IconClock, { size });
34
+ }
35
+ };
36
+ const getLogLevelColor = (level) => {
37
+ switch (level) {
38
+ case "ERROR": return "red";
39
+ case "WARN": return "yellow";
40
+ case "INFO": return "blue";
41
+ case "DEBUG": return "gray";
42
+ case "TRACE": return "dimmed";
43
+ default: return "dimmed";
44
+ }
45
+ };
46
+ const JobCard = (props) => {
47
+ const { job, stats, isTriggering, onTrigger, onSelect, isSelected } = props;
48
+ const theme = useMantineTheme();
49
+ stats && stats.total > 0 && stats.completed / stats.total * 100;
50
+ return /* @__PURE__ */ jsxs(Card, {
51
+ p: "md",
52
+ radius: "md",
53
+ withBorder: true,
54
+ onClick: () => onSelect(job),
55
+ style: {
56
+ cursor: "pointer",
57
+ borderColor: isSelected ? theme.colors.blue[6] : void 0,
58
+ backgroundColor: isSelected ? "var(--mantine-color-blue-light)" : void 0,
59
+ transition: "all 150ms ease"
60
+ },
61
+ children: [/* @__PURE__ */ jsxs(Group, {
62
+ justify: "space-between",
63
+ mb: "xs",
64
+ children: [/* @__PURE__ */ jsxs(Group, {
65
+ gap: "xs",
66
+ children: [/* @__PURE__ */ jsx(ThemeIcon, {
67
+ size: "sm",
68
+ radius: "sm",
69
+ variant: "light",
70
+ color: stats?.lastStatus ? getStatusColor(stats.lastStatus) : "gray",
71
+ children: /* @__PURE__ */ jsx(IconTerminal2, { size: 14 })
72
+ }), /* @__PURE__ */ jsx(Text, {
73
+ size: "sm",
74
+ fw: 600,
75
+ ff: "monospace",
76
+ children: job
77
+ })]
78
+ }), /* @__PURE__ */ jsx(Tooltip, {
79
+ label: "Trigger job manually",
80
+ position: "left",
81
+ children: /* @__PURE__ */ jsx(ActionIcon, {
82
+ size: "sm",
83
+ variant: "light",
84
+ color: "blue",
85
+ loading: isTriggering,
86
+ onClick: (e) => {
87
+ e.stopPropagation();
88
+ onTrigger(job);
89
+ },
90
+ children: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 12 })
91
+ })
92
+ })]
93
+ }), stats ? /* @__PURE__ */ jsxs(Fragment, { children: [
94
+ /* @__PURE__ */ jsxs(Group, {
95
+ gap: "lg",
96
+ mb: "xs",
97
+ children: [
98
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
99
+ size: "xs",
100
+ c: "dimmed",
101
+ tt: "uppercase",
102
+ fw: 500,
103
+ children: "Total"
104
+ }), /* @__PURE__ */ jsx(Text, {
105
+ size: "lg",
106
+ fw: 700,
107
+ ff: "monospace",
108
+ children: stats.total
109
+ })] }),
110
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
111
+ size: "xs",
112
+ c: "dimmed",
113
+ tt: "uppercase",
114
+ fw: 500,
115
+ children: "Success"
116
+ }), /* @__PURE__ */ jsx(Text, {
117
+ size: "lg",
118
+ fw: 700,
119
+ ff: "monospace",
120
+ c: "teal",
121
+ children: stats.completed
122
+ })] }),
123
+ /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
124
+ size: "xs",
125
+ c: "dimmed",
126
+ tt: "uppercase",
127
+ fw: 500,
128
+ children: "Failed"
129
+ }), /* @__PURE__ */ jsx(Text, {
130
+ size: "lg",
131
+ fw: 700,
132
+ ff: "monospace",
133
+ c: "red",
134
+ children: stats.failed
135
+ })] })
136
+ ]
137
+ }),
138
+ /* @__PURE__ */ jsxs(Progress.Root, {
139
+ size: "sm",
140
+ radius: "xs",
141
+ children: [
142
+ /* @__PURE__ */ jsx(Tooltip, {
143
+ label: `${stats.completed} completed`,
144
+ children: /* @__PURE__ */ jsx(Progress.Section, {
145
+ value: stats.completed / Math.max(stats.total, 1) * 100,
146
+ color: "teal"
147
+ })
148
+ }),
149
+ /* @__PURE__ */ jsx(Tooltip, {
150
+ label: `${stats.failed} failed`,
151
+ children: /* @__PURE__ */ jsx(Progress.Section, {
152
+ value: stats.failed / Math.max(stats.total, 1) * 100,
153
+ color: "red"
154
+ })
155
+ }),
156
+ /* @__PURE__ */ jsx(Tooltip, {
157
+ label: `${stats.running} running`,
158
+ children: /* @__PURE__ */ jsx(Progress.Section, {
159
+ value: stats.running / Math.max(stats.total, 1) * 100,
160
+ color: "blue"
161
+ })
162
+ })
163
+ ]
164
+ }),
165
+ stats.lastRun && /* @__PURE__ */ jsxs(Text, {
166
+ size: "xs",
167
+ c: "dimmed",
168
+ mt: "xs",
169
+ children: [
170
+ "Last run: ",
171
+ formatDuration(stats.lastRun, /* @__PURE__ */ new Date()),
172
+ " ago"
173
+ ]
174
+ })
175
+ ] }) : /* @__PURE__ */ jsxs(Stack, {
176
+ gap: "xs",
177
+ children: [/* @__PURE__ */ jsx(Skeleton, {
178
+ height: 8,
179
+ radius: "xl"
180
+ }), /* @__PURE__ */ jsx(Skeleton, {
181
+ height: 8,
182
+ width: "70%",
183
+ radius: "xl"
184
+ })]
185
+ })]
186
+ });
187
+ };
188
+ const ExecutionLogViewer = (props) => {
189
+ const { logs, error } = props;
190
+ if (!logs?.length && !error) return /* @__PURE__ */ jsx(Box, {
191
+ p: "md",
192
+ children: /* @__PURE__ */ jsx(Text, {
193
+ size: "sm",
194
+ c: "dimmed",
195
+ ta: "center",
196
+ children: "No logs available"
197
+ })
198
+ });
199
+ return /* @__PURE__ */ jsx(ScrollArea, {
200
+ h: 300,
201
+ type: "auto",
202
+ children: /* @__PURE__ */ jsxs(Box, {
203
+ p: "md",
204
+ style: {
205
+ fontFamily: "var(--mantine-font-family-monospace)",
206
+ fontSize: "12px",
207
+ lineHeight: 1.6
208
+ },
209
+ children: [error && /* @__PURE__ */ jsx(Paper, {
210
+ p: "sm",
211
+ mb: "md",
212
+ bg: "var(--mantine-color-red-light)",
213
+ radius: "sm",
214
+ children: /* @__PURE__ */ jsxs(Group, {
215
+ gap: "xs",
216
+ align: "flex-start",
217
+ children: [/* @__PURE__ */ jsx(IconAlertTriangle, {
218
+ size: 14,
219
+ color: "var(--mantine-color-red-filled)"
220
+ }), /* @__PURE__ */ jsx(Text, {
221
+ size: "xs",
222
+ c: "red",
223
+ style: {
224
+ whiteSpace: "pre-wrap",
225
+ wordBreak: "break-word"
226
+ },
227
+ children: error
228
+ })]
229
+ })
230
+ }), logs?.map((log, index) => /* @__PURE__ */ jsxs(Group, {
231
+ gap: "sm",
232
+ align: "flex-start",
233
+ mb: 4,
234
+ wrap: "nowrap",
235
+ children: [
236
+ /* @__PURE__ */ jsx(Text, {
237
+ size: "xs",
238
+ c: "dimmed",
239
+ style: {
240
+ minWidth: 80,
241
+ flexShrink: 0
242
+ },
243
+ children: new Date(log.timestamp).toLocaleTimeString()
244
+ }),
245
+ /* @__PURE__ */ jsx(Badge, {
246
+ size: "xs",
247
+ variant: "light",
248
+ color: getLogLevelColor(log.level),
249
+ style: { minWidth: 50 },
250
+ children: log.level
251
+ }),
252
+ /* @__PURE__ */ jsx(Text, {
253
+ size: "xs",
254
+ c: "dimmed",
255
+ style: {
256
+ minWidth: 100,
257
+ flexShrink: 0
258
+ },
259
+ children: log.module
260
+ }),
261
+ /* @__PURE__ */ jsx(Text, {
262
+ size: "xs",
263
+ style: { wordBreak: "break-word" },
264
+ children: log.message
265
+ })
266
+ ]
267
+ }, index))]
268
+ })
269
+ });
270
+ };
271
+ const AdminJobs = () => {
272
+ const client = useClient();
273
+ const { l } = useI18n();
274
+ const toast = useToast();
275
+ const dialog = useDialog();
276
+ const [jobs, setJobs] = useState([]);
277
+ const [jobStats, setJobStats] = useState(/* @__PURE__ */ new Map());
278
+ const [selectedJob, setSelectedJob] = useState(null);
279
+ const [triggeringJobs, setTriggeringJobs] = useState(/* @__PURE__ */ new Set());
280
+ const [refreshKey, setRefreshKey] = useState(0);
281
+ const [loading, setLoading] = useState(true);
282
+ const [activeTab, setActiveTab] = useState("overview");
283
+ useEffect(() => {
284
+ const loadJobs = async () => {
285
+ try {
286
+ const jobList = await client.getJobs();
287
+ setJobs(jobList);
288
+ const statsMap = /* @__PURE__ */ new Map();
289
+ for (const job of jobList) {
290
+ const items = (await client.getJobExecutions({ query: {
291
+ job,
292
+ size: 100
293
+ } })).content || [];
294
+ const completed = items.filter((e) => e.status === "COMPLETED").length;
295
+ const failed = items.filter((e) => e.status === "FAILED").length;
296
+ const running = items.filter((e) => e.status === "STARTED").length;
297
+ const completedItems = items.filter((e) => e.status === "COMPLETED" && e.finishedAt);
298
+ const avgDuration = completedItems.length > 0 ? completedItems.reduce((acc, e) => {
299
+ return acc + (new Date(e.finishedAt).getTime() - new Date(e.createdAt).getTime());
300
+ }, 0) / completedItems.length : 0;
301
+ const lastItem = items[0];
302
+ statsMap.set(job, {
303
+ name: job,
304
+ total: items.length,
305
+ completed,
306
+ failed,
307
+ running,
308
+ avgDuration,
309
+ lastRun: lastItem?.createdAt ? new Date(lastItem.createdAt) : void 0,
310
+ lastStatus: lastItem?.status
311
+ });
312
+ }
313
+ setJobStats(statsMap);
314
+ } catch (error) {
315
+ toast.danger("Failed to load jobs");
316
+ } finally {
317
+ setLoading(false);
318
+ }
319
+ };
320
+ loadJobs();
321
+ }, [refreshKey]);
322
+ const handleTriggerJob = useCallback(async (job) => {
323
+ if (!await dialog.confirm({
324
+ title: "Trigger Job",
325
+ message: `Are you sure you want to trigger "${job}" manually?`,
326
+ confirmLabel: "Trigger",
327
+ confirmColor: "blue"
328
+ })) return;
329
+ setTriggeringJobs((prev) => new Set(prev).add(job));
330
+ try {
331
+ await client.triggerJob({ body: { name: job } });
332
+ toast.success(`Job "${job}" triggered successfully`);
333
+ setRefreshKey((k) => k + 1);
334
+ } catch (error) {
335
+ toast.danger(`Failed to trigger job "${job}"`);
336
+ } finally {
337
+ setTriggeringJobs((prev) => {
338
+ const next = new Set(prev);
339
+ next.delete(job);
340
+ return next;
341
+ });
342
+ }
343
+ }, [
344
+ client,
345
+ dialog,
346
+ toast
347
+ ]);
348
+ const filters = t.object({
349
+ job: t.optional(t.string({ $control: { query: t.pick(jobExecutions.schema, ["job"]) } })),
350
+ status: t.optional(t.enum([
351
+ "STARTED",
352
+ "FAILED",
353
+ "COMPLETED"
354
+ ]))
355
+ });
356
+ const globalStats = {
357
+ total: Array.from(jobStats.values()).reduce((acc, s) => acc + s.total, 0),
358
+ completed: Array.from(jobStats.values()).reduce((acc, s) => acc + s.completed, 0),
359
+ failed: Array.from(jobStats.values()).reduce((acc, s) => acc + s.failed, 0),
360
+ running: Array.from(jobStats.values()).reduce((acc, s) => acc + s.running, 0)
361
+ };
362
+ const successRate = globalStats.total > 0 ? Math.round(globalStats.completed / globalStats.total * 100) : 0;
363
+ return /* @__PURE__ */ jsxs(Flex, {
364
+ flex: 1,
365
+ direction: "column",
366
+ gap: "md",
367
+ children: [/* @__PURE__ */ jsxs(SimpleGrid, {
368
+ cols: {
369
+ base: 2,
370
+ sm: 4
371
+ },
372
+ spacing: "md",
373
+ children: [
374
+ /* @__PURE__ */ jsx(Paper, {
375
+ p: "md",
376
+ radius: "md",
377
+ withBorder: true,
378
+ children: /* @__PURE__ */ jsxs(Group, {
379
+ justify: "space-between",
380
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
381
+ size: "xs",
382
+ c: "dimmed",
383
+ tt: "uppercase",
384
+ fw: 600,
385
+ children: "Total Jobs"
386
+ }), /* @__PURE__ */ jsx(Text, {
387
+ size: "xl",
388
+ fw: 700,
389
+ ff: "monospace",
390
+ children: jobs.length
391
+ })] }), /* @__PURE__ */ jsx(ThemeIcon, {
392
+ size: "lg",
393
+ radius: "md",
394
+ variant: "light",
395
+ color: "blue",
396
+ children: /* @__PURE__ */ jsx(IconTerminal2, { size: 20 })
397
+ })]
398
+ })
399
+ }),
400
+ /* @__PURE__ */ jsx(Paper, {
401
+ p: "md",
402
+ radius: "md",
403
+ withBorder: true,
404
+ children: /* @__PURE__ */ jsxs(Group, {
405
+ justify: "space-between",
406
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
407
+ size: "xs",
408
+ c: "dimmed",
409
+ tt: "uppercase",
410
+ fw: 600,
411
+ children: "Executions"
412
+ }), /* @__PURE__ */ jsx(Text, {
413
+ size: "xl",
414
+ fw: 700,
415
+ ff: "monospace",
416
+ children: globalStats.total
417
+ })] }), /* @__PURE__ */ jsx(ThemeIcon, {
418
+ size: "lg",
419
+ radius: "md",
420
+ variant: "light",
421
+ color: "gray",
422
+ children: /* @__PURE__ */ jsx(IconClock, { size: 20 })
423
+ })]
424
+ })
425
+ }),
426
+ /* @__PURE__ */ jsx(Paper, {
427
+ p: "md",
428
+ radius: "md",
429
+ withBorder: true,
430
+ children: /* @__PURE__ */ jsxs(Group, {
431
+ justify: "space-between",
432
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
433
+ size: "xs",
434
+ c: "dimmed",
435
+ tt: "uppercase",
436
+ fw: 600,
437
+ children: "Success Rate"
438
+ }), /* @__PURE__ */ jsxs(Text, {
439
+ size: "xl",
440
+ fw: 700,
441
+ ff: "monospace",
442
+ c: successRate >= 90 ? "teal" : successRate >= 70 ? "yellow" : "red",
443
+ children: [successRate, "%"]
444
+ })] }), /* @__PURE__ */ jsx(RingProgress, {
445
+ size: 48,
446
+ thickness: 4,
447
+ roundCaps: true,
448
+ sections: [{
449
+ value: successRate,
450
+ color: successRate >= 90 ? "teal" : successRate >= 70 ? "yellow" : "red"
451
+ }]
452
+ })]
453
+ })
454
+ }),
455
+ /* @__PURE__ */ jsx(Paper, {
456
+ p: "md",
457
+ radius: "md",
458
+ withBorder: true,
459
+ children: /* @__PURE__ */ jsxs(Group, {
460
+ justify: "space-between",
461
+ children: [/* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text, {
462
+ size: "xs",
463
+ c: "dimmed",
464
+ tt: "uppercase",
465
+ fw: 600,
466
+ children: "Running Now"
467
+ }), /* @__PURE__ */ jsx(Text, {
468
+ size: "xl",
469
+ fw: 700,
470
+ ff: "monospace",
471
+ c: "blue",
472
+ children: globalStats.running
473
+ })] }), /* @__PURE__ */ jsx(ThemeIcon, {
474
+ size: "lg",
475
+ radius: "md",
476
+ variant: "light",
477
+ color: globalStats.running > 0 ? "blue" : "gray",
478
+ children: /* @__PURE__ */ jsx(IconPlayerPlay, { size: 20 })
479
+ })]
480
+ })
481
+ })
482
+ ]
483
+ }), /* @__PURE__ */ jsxs(Tabs, {
484
+ value: activeTab,
485
+ onChange: setActiveTab,
486
+ children: [
487
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [/* @__PURE__ */ jsx(Tabs.Tab, {
488
+ value: "overview",
489
+ leftSection: /* @__PURE__ */ jsx(IconTerminal2, { size: 14 }),
490
+ children: "Jobs Overview"
491
+ }), /* @__PURE__ */ jsx(Tabs.Tab, {
492
+ value: "executions",
493
+ leftSection: /* @__PURE__ */ jsx(IconClock, { size: 14 }),
494
+ children: "Execution History"
495
+ })] }),
496
+ /* @__PURE__ */ jsxs(Tabs.Panel, {
497
+ value: "overview",
498
+ pt: "md",
499
+ children: [/* @__PURE__ */ jsxs(Group, {
500
+ justify: "space-between",
501
+ mb: "md",
502
+ children: [/* @__PURE__ */ jsxs(Text, {
503
+ size: "sm",
504
+ c: "dimmed",
505
+ children: [
506
+ jobs.length,
507
+ " registered job",
508
+ jobs.length !== 1 ? "s" : ""
509
+ ]
510
+ }), /* @__PURE__ */ jsx(ActionButton, {
511
+ size: "xs",
512
+ variant: "light",
513
+ leftSection: /* @__PURE__ */ jsx(IconRefresh, { size: 14 }),
514
+ onClick: () => setRefreshKey((k) => k + 1),
515
+ children: "Refresh"
516
+ })]
517
+ }), loading ? /* @__PURE__ */ jsx(SimpleGrid, {
518
+ cols: {
519
+ base: 1,
520
+ sm: 2,
521
+ lg: 3
522
+ },
523
+ spacing: "md",
524
+ children: [
525
+ 1,
526
+ 2,
527
+ 3
528
+ ].map((i) => /* @__PURE__ */ jsx(Skeleton, {
529
+ height: 150,
530
+ radius: "md"
531
+ }, i))
532
+ }) : jobs.length === 0 ? /* @__PURE__ */ jsxs(Paper, {
533
+ p: "xl",
534
+ radius: "md",
535
+ withBorder: true,
536
+ ta: "center",
537
+ children: [
538
+ /* @__PURE__ */ jsx(IconTerminal2, {
539
+ size: 48,
540
+ color: "var(--mantine-color-dimmed)"
541
+ }),
542
+ /* @__PURE__ */ jsx(Text, {
543
+ size: "lg",
544
+ fw: 500,
545
+ mt: "md",
546
+ children: "No jobs registered"
547
+ }),
548
+ /* @__PURE__ */ jsx(Text, {
549
+ size: "sm",
550
+ c: "dimmed",
551
+ mt: "xs",
552
+ children: "Jobs will appear here once they are defined using $job primitive"
553
+ })
554
+ ]
555
+ }) : /* @__PURE__ */ jsx(SimpleGrid, {
556
+ cols: {
557
+ base: 1,
558
+ sm: 2,
559
+ lg: 3
560
+ },
561
+ spacing: "md",
562
+ children: jobs.map((job) => /* @__PURE__ */ jsx(JobCard, {
563
+ job,
564
+ stats: jobStats.get(job),
565
+ isTriggering: triggeringJobs.has(job),
566
+ onTrigger: handleTriggerJob,
567
+ onSelect: setSelectedJob,
568
+ isSelected: selectedJob === job
569
+ }, job))
570
+ })]
571
+ }),
572
+ /* @__PURE__ */ jsx(Tabs.Panel, {
573
+ value: "executions",
574
+ pt: "md",
575
+ children: /* @__PURE__ */ jsx(DataTable, {
576
+ submitOnInit: true,
577
+ defaultSize: 15,
578
+ typeFormProps: {
579
+ skipSubmitButton: true,
580
+ columns: 3
581
+ },
582
+ tableProps: {
583
+ horizontalSpacing: "sm",
584
+ verticalSpacing: "sm",
585
+ highlightOnHover: true
586
+ },
587
+ onFilterChange: (key, _value, form) => {
588
+ if (key === "job" || key === "status") return form.submit();
589
+ },
590
+ filters,
591
+ items: async (filters) => {
592
+ return await client.getJobExecutions({ query: {
593
+ ...filters,
594
+ job: selectedJob || filters.job
595
+ } });
596
+ },
597
+ columns: {
598
+ job: {
599
+ label: "Job",
600
+ value: (item) => /* @__PURE__ */ jsx(Text, {
601
+ size: "sm",
602
+ fw: 500,
603
+ ff: "monospace",
604
+ children: item.job
605
+ })
606
+ },
607
+ status: {
608
+ label: "Status",
609
+ fit: true,
610
+ value: (item) => /* @__PURE__ */ jsx(Badge, {
611
+ size: "sm",
612
+ variant: "light",
613
+ color: getStatusColor(item.status),
614
+ leftSection: getStatusIcon(item.status, 12),
615
+ children: item.status
616
+ })
617
+ },
618
+ duration: {
619
+ label: "Duration",
620
+ fit: true,
621
+ value: (item) => /* @__PURE__ */ jsx(Text, {
622
+ size: "xs",
623
+ c: "dimmed",
624
+ ff: "monospace",
625
+ children: formatDuration(item.createdAt, item.finishedAt)
626
+ })
627
+ },
628
+ logs: {
629
+ label: "Logs",
630
+ fit: true,
631
+ value: (item) => {
632
+ const logCount = item.logs?.length || 0;
633
+ const errorCount = item.logs?.filter((log) => log.level === "ERROR").length || 0;
634
+ return /* @__PURE__ */ jsxs(Group, {
635
+ gap: 4,
636
+ children: [/* @__PURE__ */ jsxs(Badge, {
637
+ size: "xs",
638
+ variant: "light",
639
+ color: "gray",
640
+ children: [logCount, " logs"]
641
+ }), errorCount > 0 && /* @__PURE__ */ jsxs(Badge, {
642
+ size: "xs",
643
+ variant: "light",
644
+ color: "red",
645
+ children: [errorCount, " errors"]
646
+ })]
647
+ });
648
+ }
649
+ },
650
+ error: {
651
+ label: "Error",
652
+ value: (item) => item.error ? /* @__PURE__ */ jsx(Tooltip, {
653
+ label: item.error,
654
+ multiline: true,
655
+ w: 300,
656
+ children: /* @__PURE__ */ jsx(Text, {
657
+ size: "xs",
658
+ c: "red",
659
+ lineClamp: 1,
660
+ children: item.error
661
+ })
662
+ }) : /* @__PURE__ */ jsx(Text, {
663
+ size: "xs",
664
+ c: "dimmed",
665
+ children: "—"
666
+ })
667
+ },
668
+ createdAt: {
669
+ label: "Started",
670
+ fit: true,
671
+ value: (item) => /* @__PURE__ */ jsx(Text, {
672
+ size: "xs",
673
+ c: "dimmed",
674
+ children: l(item.createdAt, { date: "fromNow" })
675
+ })
676
+ }
677
+ },
678
+ panel: (item) => /* @__PURE__ */ jsx(Box, {
679
+ bg: "var(--mantine-color-dark-7)",
680
+ p: 0,
681
+ children: /* @__PURE__ */ jsx(ExecutionLogViewer, {
682
+ logs: item.logs,
683
+ error: item.error
684
+ })
685
+ }),
686
+ canPanel: (item) => Boolean(item.logs?.length || item.error)
687
+ }, refreshKey)
688
+ })
689
+ ]
690
+ })]
691
+ });
692
+ };
693
+ var AdminJobs_default = AdminJobs;
694
+
695
+ //#endregion
696
+ export { AdminJobs_default as t };
697
+ //# sourceMappingURL=AdminJobs-B-q9iGO3.js.map