@alepha/ui 0.18.3 → 0.19.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 (181) hide show
  1. package/dist/admin/{AdminApiKeys-Dy_k-4Vd.js → AdminApiKeys-C2ze85eD.js} +3 -4
  2. package/dist/admin/{AdminApiKeys-Dy_k-4Vd.js.map → AdminApiKeys-C2ze85eD.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-CKiFMSSU.js → AdminAudits-BIj81e4k.js} +3 -4
  4. package/dist/admin/{AdminAudits-CKiFMSSU.js.map → AdminAudits-BIj81e4k.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-PhC_dZqo.js → AdminDashboard-PMVzrwSu.js} +3 -4
  6. package/dist/admin/{AdminDashboard-PhC_dZqo.js.map → AdminDashboard-PMVzrwSu.js.map} +1 -1
  7. package/dist/admin/AdminFiles-Bq03BLt-.js +189 -0
  8. package/dist/admin/AdminFiles-Bq03BLt-.js.map +1 -0
  9. package/dist/admin/{AdminJobExecutions-D9E-CS-U.js → AdminJobs-D1_QGCDy.js} +401 -358
  10. package/dist/admin/AdminJobs-D1_QGCDy.js.map +1 -0
  11. package/dist/admin/{AdminLayout-I6TlUMPc.js → AdminLayout-BNiwiw2D.js} +8 -25
  12. package/dist/admin/AdminLayout-BNiwiw2D.js.map +1 -0
  13. package/dist/admin/{AdminNotifications-ZPHCYrv7.js → AdminNotifications-DSKQtUfn.js} +85 -124
  14. package/dist/admin/AdminNotifications-DSKQtUfn.js.map +1 -0
  15. package/dist/admin/{AdminParameters-CqgvhRsb.js → AdminParameters-CoB7EhyM.js} +3 -12
  16. package/dist/admin/{AdminParameters-CqgvhRsb.js.map → AdminParameters-CoB7EhyM.js.map} +1 -1
  17. package/dist/admin/{AdminSessions-Bz5NRuoW.js → AdminSessions-DFbFcrJQ.js} +3 -4
  18. package/dist/admin/{AdminSessions-Bz5NRuoW.js.map → AdminSessions-DFbFcrJQ.js.map} +1 -1
  19. package/dist/admin/{AdminUserLayout-lXT6I0Qq.js → AdminUserLayout-fSfi3KMm.js} +72 -111
  20. package/dist/admin/AdminUserLayout-fSfi3KMm.js.map +1 -0
  21. package/dist/admin/{AdminUserProfile-vFBLoJ3h.js → AdminUserProfile-_C-h8vUK.js} +7 -6
  22. package/dist/admin/AdminUserProfile-_C-h8vUK.js.map +1 -0
  23. package/dist/admin/{AdminUserSessions-CT_YDim0.js → AdminUserSessions-KpJHIeQo.js} +3 -4
  24. package/dist/admin/{AdminUserSessions-CT_YDim0.js.map → AdminUserSessions-KpJHIeQo.js.map} +1 -1
  25. package/dist/admin/{AdminUsers-D1UfGya9.js → AdminUsers-DcVrzdQP.js} +4 -4
  26. package/dist/admin/AdminUsers-DcVrzdQP.js.map +1 -0
  27. package/dist/admin/{AuthLayout-_frhdgOO.js → AuthLayout-CazfLzcf.js} +3 -4
  28. package/dist/admin/{AuthLayout-_frhdgOO.js.map → AuthLayout-CazfLzcf.js.map} +1 -1
  29. package/dist/{demo/IconGoogle-CSQLPYwX.js → admin/IconGoogle-8Nkx6yax.js} +2 -4
  30. package/dist/admin/{IconGoogle-Ch1m3Uzl.js.map → IconGoogle-8Nkx6yax.js.map} +1 -1
  31. package/dist/admin/{Login-xtNmQtGh.js → Login-CaMjUrDP.js} +5 -6
  32. package/dist/{auth/Login-BA1E8IZl.js.map → admin/Login-CaMjUrDP.js.map} +1 -1
  33. package/dist/admin/{Profile-_AtPUwAP.js → Profile-Ca4fZX15.js} +3 -5
  34. package/dist/{demo/Profile-DS5q4vOh.js.map → admin/Profile-Ca4fZX15.js.map} +1 -1
  35. package/dist/admin/{Register-JcCjHUUn.js → Register-C5DyKWPO.js} +5 -6
  36. package/dist/{demo/Register-B4hLBeEv.js.map → admin/Register-C5DyKWPO.js.map} +1 -1
  37. package/dist/admin/{ResetPassword-CwGBPLJO.js → ResetPassword-BA5sAgXo.js} +4 -5
  38. package/dist/{auth/ResetPassword-DCtGcneA.js.map → admin/ResetPassword-BA5sAgXo.js.map} +1 -1
  39. package/dist/admin/{VerifyEmail-hNxWejWf.js → VerifyEmail-DKNXROj_.js} +4 -5
  40. package/dist/{auth/VerifyEmail-DkH7NBfn.js.map → admin/VerifyEmail-DKNXROj_.js.map} +1 -1
  41. package/dist/admin/adminUserAtom-BLNc7XbT.js +11 -0
  42. package/dist/admin/adminUserAtom-BLNc7XbT.js.map +1 -0
  43. package/dist/admin/{core-CYaRQ8O-.js → core-CJCEx18C.js} +132 -86
  44. package/dist/admin/core-CJCEx18C.js.map +1 -0
  45. package/dist/admin/index.d.ts +80 -13
  46. package/dist/admin/index.d.ts.map +1 -1
  47. package/dist/admin/index.js +38 -68
  48. package/dist/admin/index.js.map +1 -1
  49. package/dist/admin/rolldown-runtime-CiIaOW0V.js +13 -0
  50. package/dist/{demo/AuthLayout-Brri4A-L.js → auth/AuthLayout-vXPcCVzp.js} +3 -4
  51. package/dist/auth/{AuthLayout-AvLlcLjS.js.map → AuthLayout-vXPcCVzp.js.map} +1 -1
  52. package/dist/{admin/IconGoogle-Ch1m3Uzl.js → auth/IconGoogle-8Nkx6yax.js} +2 -4
  53. package/dist/auth/{IconGoogle-Ch1m3Uzl.js.map → IconGoogle-8Nkx6yax.js.map} +1 -1
  54. package/dist/auth/{Login-BA1E8IZl.js → Login-Dg08QR20.js} +5 -6
  55. package/dist/{demo/Login-C12N4oGs.js.map → auth/Login-Dg08QR20.js.map} +1 -1
  56. package/dist/{demo/Profile-DS5q4vOh.js → auth/Profile-Bb5O1yeh.js} +3 -5
  57. package/dist/auth/{Profile-YcWdeuFz.js.map → Profile-Bb5O1yeh.js.map} +1 -1
  58. package/dist/auth/{Register-CPhEO5MG.js → Register-B2AN71NC.js} +5 -6
  59. package/dist/{admin/Register-JcCjHUUn.js.map → auth/Register-B2AN71NC.js.map} +1 -1
  60. package/dist/{demo/ResetPassword-D8g9ha1N.js → auth/ResetPassword-BLxwzbDj.js} +4 -5
  61. package/dist/{admin/ResetPassword-CwGBPLJO.js.map → auth/ResetPassword-BLxwzbDj.js.map} +1 -1
  62. package/dist/auth/{VerifyEmail-DkH7NBfn.js → VerifyEmail-CSDOk3Zm.js} +4 -5
  63. package/dist/{admin/VerifyEmail-hNxWejWf.js.map → auth/VerifyEmail-CSDOk3Zm.js.map} +1 -1
  64. package/dist/auth/{core-D5jIAVF2.js → core-DuGkjPiU.js} +23 -54
  65. package/dist/auth/core-DuGkjPiU.js.map +1 -0
  66. package/dist/auth/index.d.ts +20 -6
  67. package/dist/auth/index.d.ts.map +1 -1
  68. package/dist/auth/index.js +13 -18
  69. package/dist/auth/index.js.map +1 -1
  70. package/dist/auth/rolldown-runtime-CiIaOW0V.js +13 -0
  71. package/dist/core/index.d.ts +78 -20
  72. package/dist/core/index.d.ts.map +1 -1
  73. package/dist/core/index.js +130 -98
  74. package/dist/core/index.js.map +1 -1
  75. package/dist/{auth/AuthLayout-AvLlcLjS.js → demo/AuthLayout-DPsOOG4u.js} +3 -4
  76. package/dist/demo/{AuthLayout-Brri4A-L.js.map → AuthLayout-DPsOOG4u.js.map} +1 -1
  77. package/dist/demo/{DemoButton-wiCxZZ_L.js → DemoButton-wzcqGk4u.js} +4 -5
  78. package/dist/demo/{DemoButton-wiCxZZ_L.js.map → DemoButton-wzcqGk4u.js.map} +1 -1
  79. package/dist/demo/{DemoControlSelect-D7ILObVg.js → DemoControlSelect-CMWvQ6Gm.js} +4 -5
  80. package/dist/demo/{DemoControlSelect-D7ILObVg.js.map → DemoControlSelect-CMWvQ6Gm.js.map} +1 -1
  81. package/dist/demo/{DemoDataTable-DZ5Y8pFX.js → DemoDataTable-CHsAP3e2.js} +4 -5
  82. package/dist/demo/{DemoDataTable-DZ5Y8pFX.js.map → DemoDataTable-CHsAP3e2.js.map} +1 -1
  83. package/dist/demo/{DemoDialog-CUWdLHim.js → DemoDialog-Co2IePxX.js} +3 -4
  84. package/dist/demo/{DemoDialog-CUWdLHim.js.map → DemoDialog-Co2IePxX.js.map} +1 -1
  85. package/dist/demo/{DemoFlex-a8OhMMvq.js → DemoFlex-OEwQt5do.js} +4 -5
  86. package/dist/demo/{DemoFlex-a8OhMMvq.js.map → DemoFlex-OEwQt5do.js.map} +1 -1
  87. package/dist/demo/DemoHeading-Db-XkQIK.js +69 -0
  88. package/dist/demo/DemoHeading-Db-XkQIK.js.map +1 -0
  89. package/dist/demo/{DemoHome-D_De3UiT.js → DemoHome-Cyp29ygy.js} +4 -5
  90. package/dist/demo/{DemoHome-D_De3UiT.js.map → DemoHome-Cyp29ygy.js.map} +1 -1
  91. package/dist/demo/{DemoJsonViewer-B50s9aGM.js → DemoJsonViewer-DXtCeMzH.js} +4 -5
  92. package/dist/demo/{DemoJsonViewer-B50s9aGM.js.map → DemoJsonViewer-DXtCeMzH.js.map} +1 -1
  93. package/dist/demo/{DemoLayout-CHU8WTwO.js → DemoLayout-hh9VmZQP.js} +4 -5
  94. package/dist/demo/DemoLayout-hh9VmZQP.js.map +1 -0
  95. package/dist/demo/{DemoLogin-BBlrWpml.js → DemoLogin-DX7mnmkh.js} +15 -11
  96. package/dist/demo/{DemoLogin-BBlrWpml.js.map → DemoLogin-DX7mnmkh.js.map} +1 -1
  97. package/dist/demo/{DemoRegister-BuNE3_-f.js → DemoRegister-DVcZl04m.js} +15 -11
  98. package/dist/demo/{DemoRegister-BuNE3_-f.js.map → DemoRegister-DVcZl04m.js.map} +1 -1
  99. package/dist/demo/{DemoResetPassword-D_IjjjOJ.js → DemoResetPassword-CPENlZH5.js} +15 -11
  100. package/dist/demo/{DemoResetPassword-D_IjjjOJ.js.map → DemoResetPassword-CPENlZH5.js.map} +1 -1
  101. package/dist/demo/{DemoSidebar-Giy2HRBD.js → DemoSidebar-CGu7DZeM.js} +4 -5
  102. package/dist/demo/{DemoSidebar-Giy2HRBD.js.map → DemoSidebar-CGu7DZeM.js.map} +1 -1
  103. package/dist/demo/{DemoText-ubcw-vog.js → DemoText-DYUJ7bY_.js} +4 -5
  104. package/dist/demo/{DemoText-ubcw-vog.js.map → DemoText-DYUJ7bY_.js.map} +1 -1
  105. package/dist/demo/{DemoToast-9die_dYT.js → DemoToast-CgdnZNvx.js} +3 -4
  106. package/dist/demo/{DemoToast-9die_dYT.js.map → DemoToast-CgdnZNvx.js.map} +1 -1
  107. package/dist/demo/{DemoTypeForm-D_d6OVKL.js → DemoTypeForm-Pims-cGa.js} +4 -5
  108. package/dist/demo/{DemoTypeForm-D_d6OVKL.js.map → DemoTypeForm-Pims-cGa.js.map} +1 -1
  109. package/dist/demo/{DemoVerifyEmail-B43KlF4F.js → DemoVerifyEmail-C7B3xxch.js} +10 -11
  110. package/dist/demo/{DemoVerifyEmail-B43KlF4F.js.map → DemoVerifyEmail-C7B3xxch.js.map} +1 -1
  111. package/dist/{auth/IconGoogle-Ch1m3Uzl.js → demo/IconGoogle-CwQy4G9y.js} +2 -4
  112. package/dist/demo/{IconGoogle-CSQLPYwX.js.map → IconGoogle-CwQy4G9y.js.map} +1 -1
  113. package/dist/demo/{Login-C12N4oGs.js → Login-pwMF4TUj.js} +5 -6
  114. package/dist/{admin/Login-xtNmQtGh.js.map → demo/Login-pwMF4TUj.js.map} +1 -1
  115. package/dist/{auth/Profile-YcWdeuFz.js → demo/Profile-BliZapZS.js} +3 -5
  116. package/dist/{admin/Profile-_AtPUwAP.js.map → demo/Profile-BliZapZS.js.map} +1 -1
  117. package/dist/demo/{Register-B4hLBeEv.js → Register-CiwAT7Hy.js} +5 -6
  118. package/dist/{auth/Register-CPhEO5MG.js.map → demo/Register-CiwAT7Hy.js.map} +1 -1
  119. package/dist/{auth/ResetPassword-DCtGcneA.js → demo/ResetPassword-l9Vg4JE-.js} +4 -5
  120. package/dist/demo/{ResetPassword-D8g9ha1N.js.map → ResetPassword-l9Vg4JE-.js.map} +1 -1
  121. package/dist/demo/{Showcase-D6Fxt4X4.js → Showcase-CX6bDgwe.js} +3 -5
  122. package/dist/demo/{Showcase-D6Fxt4X4.js.map → Showcase-CX6bDgwe.js.map} +1 -1
  123. package/dist/demo/{VerifyEmail-BjDo0cZA.js → VerifyEmail-CAB-OS7i.js} +4 -5
  124. package/dist/demo/{VerifyEmail-BjDo0cZA.js.map → VerifyEmail-CAB-OS7i.js.map} +1 -1
  125. package/dist/demo/{auth-ByVTreDl.js → auth-uegJAdKu.js} +18 -35
  126. package/dist/demo/{auth-ByVTreDl.js.map → auth-uegJAdKu.js.map} +1 -1
  127. package/dist/demo/{core-DFgB3yU4.js → core-B4LVHzPn.js} +132 -93
  128. package/dist/demo/core-B4LVHzPn.js.map +1 -0
  129. package/dist/demo/index.js +20 -23
  130. package/dist/demo/index.js.map +1 -1
  131. package/dist/demo/rolldown-runtime-CiIaOW0V.js +13 -0
  132. package/package.json +17 -20
  133. package/src/admin/AdminRouter.tsx +23 -38
  134. package/src/admin/atoms/adminUserAtom.ts +7 -0
  135. package/src/admin/components/AdminLayout.tsx +2 -14
  136. package/src/admin/components/files/AdminFiles.tsx +123 -1
  137. package/src/admin/components/jobs/{AdminJobExecutions.tsx → AdminJobs.tsx} +450 -317
  138. package/src/admin/components/notifications/AdminNotifications.tsx +11 -25
  139. package/src/admin/components/users/AdminUserLayout.tsx +84 -127
  140. package/src/admin/components/users/AdminUserProfile.tsx +5 -2
  141. package/src/admin/components/users/AdminUsers.tsx +1 -1
  142. package/src/core/components/Flex.tsx +24 -0
  143. package/src/core/components/Section.tsx +109 -0
  144. package/src/core/components/SectionHeader.tsx +106 -0
  145. package/src/core/components/buttons/ActionButton.tsx +1 -0
  146. package/src/core/components/dialogs/PromptDialog.tsx +1 -1
  147. package/src/core/components/layout/Breadcrumb.tsx +2 -2
  148. package/src/core/components/layout/DashboardShell.tsx +1 -1
  149. package/src/core/index.ts +4 -1
  150. package/src/core/services/DialogService.tsx +2 -2
  151. package/src/core/styles.css +2 -1
  152. package/src/core/table/components/DataTable.tsx +5 -2
  153. package/src/demo/DemoRouter.ts +1 -1
  154. package/src/demo/components/auth/DemoLogin.tsx +5 -0
  155. package/src/demo/components/auth/DemoRegister.tsx +5 -0
  156. package/src/demo/components/auth/DemoResetPassword.tsx +5 -0
  157. package/src/demo/components/core/DemoHeading.tsx +56 -3
  158. package/dist/admin/AdminFiles-DFTjijGp.js +0 -111
  159. package/dist/admin/AdminFiles-DFTjijGp.js.map +0 -1
  160. package/dist/admin/AdminJobDashboard-BL8gGPDp.js +0 -354
  161. package/dist/admin/AdminJobDashboard-BL8gGPDp.js.map +0 -1
  162. package/dist/admin/AdminJobExecutions-D9E-CS-U.js.map +0 -1
  163. package/dist/admin/AdminJobRegistry-Ci9ue1zC.js +0 -270
  164. package/dist/admin/AdminJobRegistry-Ci9ue1zC.js.map +0 -1
  165. package/dist/admin/AdminLayout-I6TlUMPc.js.map +0 -1
  166. package/dist/admin/AdminNotifications-ZPHCYrv7.js.map +0 -1
  167. package/dist/admin/AdminUserLayout-lXT6I0Qq.js.map +0 -1
  168. package/dist/admin/AdminUserProfile-vFBLoJ3h.js.map +0 -1
  169. package/dist/admin/AdminUsers-D1UfGya9.js.map +0 -1
  170. package/dist/admin/core-CYaRQ8O-.js.map +0 -1
  171. package/dist/admin/rolldown-runtime-CjeV3_4I.js +0 -18
  172. package/dist/auth/core-D5jIAVF2.js.map +0 -1
  173. package/dist/auth/rolldown-runtime-CjeV3_4I.js +0 -18
  174. package/dist/demo/DemoHeading-C13OVDfS.js +0 -18
  175. package/dist/demo/DemoHeading-C13OVDfS.js.map +0 -1
  176. package/dist/demo/DemoLayout-CHU8WTwO.js.map +0 -1
  177. package/dist/demo/core-DFgB3yU4.js.map +0 -1
  178. package/dist/demo/rolldown-runtime-CjeV3_4I.js +0 -18
  179. package/src/admin/components/jobs/AdminJobDashboard.tsx +0 -349
  180. package/src/admin/components/jobs/AdminJobRegistry.tsx +0 -301
  181. package/src/core/components/Heading.tsx +0 -19
@@ -1,18 +0,0 @@
1
- //#region \0rolldown/runtime.js
2
- var __defProp = Object.defineProperty;
3
- var __exportAll = (all, no_symbols) => {
4
- let target = {};
5
- for (var name in all) {
6
- __defProp(target, name, {
7
- get: all[name],
8
- enumerable: true
9
- });
10
- }
11
- if (!no_symbols) {
12
- __defProp(target, Symbol.toStringTag, { value: "Module" });
13
- }
14
- return target;
15
- };
16
-
17
- //#endregion
18
- export { __exportAll as t };
@@ -1,349 +0,0 @@
1
- import { ActionButton, Flex, StatCards, Text, useToast } from "@alepha/ui";
2
- import { AreaChart, BarChart, DonutChart } from "@mantine/charts";
3
- import { Paper, SimpleGrid, Table } from "@mantine/core";
4
- import {
5
- IconAlertTriangle,
6
- IconCircleCheck,
7
- IconPlayerPlay,
8
- IconRefresh,
9
- IconTerminal2,
10
- } from "@tabler/icons-react";
11
- import type {
12
- AdminJobController,
13
- JobActivityPoint,
14
- JobFailure,
15
- JobQueueDepth,
16
- JobStats,
17
- } from "alepha/api/jobs";
18
- import { useClient } from "alepha/react";
19
- import { useI18n } from "alepha/react/i18n";
20
- import { useCallback, useEffect, useState } from "react";
21
-
22
- // ─────────────────────────────────────────────────────────────────────────────
23
-
24
- const formatDuration = (
25
- start: Date | string,
26
- end?: Date | string | null,
27
- ): string => {
28
- const startTime = new Date(start).getTime();
29
- const endTime = end ? new Date(end).getTime() : Date.now();
30
- const duration = endTime - startTime;
31
-
32
- if (duration < 1000) return `${duration}ms`;
33
- if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`;
34
- if (duration < 3600000)
35
- return `${Math.floor(duration / 60000)}m ${Math.floor((duration % 60000) / 1000)}s`;
36
- return `${Math.floor(duration / 3600000)}h ${Math.floor((duration % 3600000) / 60000)}m`;
37
- };
38
-
39
- // ─────────────────────────────────────────────────────────────────────────────
40
-
41
- interface RecentExecution {
42
- id: string;
43
- jobName: string;
44
- status: string;
45
- startedAt?: string;
46
- completedAt?: string;
47
- }
48
-
49
- // ─────────────────────────────────────────────────────────────────────────────
50
-
51
- const AdminJobDashboard = () => {
52
- const client = useClient<AdminJobController>();
53
- const { l } = useI18n();
54
- const toast = useToast();
55
-
56
- const [stats, setStats] = useState<JobStats | null>(null);
57
- const [recent, setRecent] = useState<RecentExecution[]>([]);
58
- const [failures, setFailures] = useState<JobFailure[]>([]);
59
- const [activity, setActivity] = useState<JobActivityPoint[]>([]);
60
- const [queueDepth, setQueueDepth] = useState<JobQueueDepth[]>([]);
61
-
62
- const loadData = useCallback(async () => {
63
- try {
64
- const [statsData, recentData, failureData, activityData, queueData] =
65
- await Promise.all([
66
- client.getJobStats(),
67
- client.findJobExecutions({ query: { sort: "-createdAt", size: 10 } }),
68
- client.getJobTopFailures(),
69
- client.getJobActivity({ query: { days: 14 } }),
70
- client.getJobQueueDepth(),
71
- ]);
72
- setStats(statsData);
73
- setRecent(recentData.content as RecentExecution[]);
74
- setFailures(failureData);
75
- setActivity(activityData);
76
- setQueueDepth(queueData);
77
- } catch {
78
- toast.danger("Failed to load dashboard data");
79
- }
80
- }, [client, toast]);
81
-
82
- useEffect(() => {
83
- loadData();
84
- }, [loadData]);
85
-
86
- // Prepare chart data
87
- const activityChartData = activity.map((point) => ({
88
- date: new Date(point.date).toLocaleDateString("en-US", {
89
- month: "short",
90
- day: "numeric",
91
- }),
92
- completed: point.completed,
93
- failed: point.failed,
94
- }));
95
-
96
- const queueChartData = queueDepth
97
- .filter(
98
- (q) => q.pending + q.running + q.scheduled + q.retrying + q.dead > 0,
99
- )
100
- .map((q) => ({
101
- job:
102
- q.jobName.length > 20
103
- ? `...${q.jobName.slice(q.jobName.length - 18)}`
104
- : q.jobName,
105
- pending: q.pending,
106
- running: q.running,
107
- scheduled: q.scheduled,
108
- retrying: q.retrying,
109
- dead: q.dead,
110
- }));
111
-
112
- const statusDonutData = stats
113
- ? [
114
- { name: "Running", value: stats.running, color: "blue" },
115
- { name: "Pending", value: stats.pending, color: "gray" },
116
- { name: "Scheduled", value: stats.scheduled, color: "violet" },
117
- { name: "Retrying", value: stats.retrying, color: "yellow" },
118
- { name: "Dead", value: stats.dead, color: "red" },
119
- ].filter((d) => d.value > 0)
120
- : [];
121
-
122
- return (
123
- <Flex flex={1} direction="column" gap="md" p="md">
124
- <Flex justify="space-between" align="center">
125
- <Text size="lg" fw={600}>
126
- Jobs Dashboard
127
- </Text>
128
- <ActionButton
129
- tooltip="Refresh"
130
- variant="light"
131
- size="sm"
132
- icon={IconRefresh}
133
- onClick={loadData}
134
- />
135
- </Flex>
136
-
137
- {/* Stats Cards */}
138
- {stats && (
139
- <StatCards
140
- items={[
141
- {
142
- label: "Registered",
143
- value: stats.registered,
144
- icon: IconTerminal2,
145
- },
146
- {
147
- label: "Running",
148
- value: stats.running,
149
- icon: IconPlayerPlay,
150
- },
151
- {
152
- label: "Completed 24h",
153
- value: stats.completed24h,
154
- icon: IconCircleCheck,
155
- },
156
- {
157
- label: "Failed 24h",
158
- value: stats.failed24h,
159
- icon: IconAlertTriangle,
160
- },
161
- ]}
162
- />
163
- )}
164
-
165
- {/* Charts Row */}
166
- <SimpleGrid cols={{ base: 1, md: 2 }} spacing="md">
167
- {/* Activity Timeline */}
168
- <Paper p="md" radius="md" withBorder>
169
- <Text size="sm" fw={600} mb="sm">
170
- Activity (14d)
171
- </Text>
172
- {activityChartData.length > 0 ? (
173
- <AreaChart
174
- h={220}
175
- data={activityChartData}
176
- dataKey="date"
177
- series={[
178
- { name: "completed", label: "Completed", color: "teal.6" },
179
- { name: "failed", label: "Failed", color: "red.6" },
180
- ]}
181
- curveType="monotone"
182
- withGradient
183
- withTooltip
184
- withDots={false}
185
- />
186
- ) : (
187
- <Flex h={220} align="center" justify="center">
188
- <Text size="sm" c="dimmed">
189
- No activity data
190
- </Text>
191
- </Flex>
192
- )}
193
- </Paper>
194
-
195
- {/* Current Status Donut */}
196
- <Paper p="md" radius="md" withBorder>
197
- <Text size="sm" fw={600} mb="sm">
198
- Active Executions
199
- </Text>
200
- {statusDonutData.length > 0 ? (
201
- <DonutChart
202
- h={220}
203
- data={statusDonutData}
204
- withTooltip
205
- tooltipDataSource="segment"
206
- chartLabel={String(
207
- statusDonutData.reduce((sum, d) => sum + d.value, 0),
208
- )}
209
- />
210
- ) : (
211
- <Flex h={220} align="center" justify="center">
212
- <Text size="sm" c="dimmed">
213
- No active executions
214
- </Text>
215
- </Flex>
216
- )}
217
- </Paper>
218
- </SimpleGrid>
219
-
220
- {/* Queue Depth Chart */}
221
- {queueChartData.length > 0 && (
222
- <Paper p="md" radius="md" withBorder>
223
- <Text size="sm" fw={600} mb="sm">
224
- Queue Depth by Job
225
- </Text>
226
- <BarChart
227
- h={200}
228
- data={queueChartData}
229
- dataKey="job"
230
- type="stacked"
231
- series={[
232
- { name: "running", label: "Running", color: "blue.6" },
233
- { name: "pending", label: "Pending", color: "gray.5" },
234
- { name: "scheduled", label: "Scheduled", color: "violet.5" },
235
- { name: "retrying", label: "Retrying", color: "yellow.5" },
236
- { name: "dead", label: "Dead", color: "red.6" },
237
- ]}
238
- withTooltip
239
- withLegend
240
- />
241
- </Paper>
242
- )}
243
-
244
- {/* Recent Activity + Top Failures side by side */}
245
- <SimpleGrid cols={{ base: 1, md: 2 }} spacing="md">
246
- {/* Recent Activity */}
247
- <Paper p="md" radius="md" withBorder>
248
- <Text size="sm" fw={600} mb="sm">
249
- Recent Executions
250
- </Text>
251
- <Table highlightOnHover>
252
- <Table.Thead>
253
- <Table.Tr>
254
- <Table.Th>Job</Table.Th>
255
- <Table.Th>Status</Table.Th>
256
- <Table.Th>Duration</Table.Th>
257
- </Table.Tr>
258
- </Table.Thead>
259
- <Table.Tbody>
260
- {recent.map((exec) => (
261
- <Table.Tr key={exec.id}>
262
- <Table.Td>
263
- <Text size="xs" fw={500} ff="monospace" lineClamp={1}>
264
- {exec.jobName}
265
- </Text>
266
- </Table.Td>
267
- <Table.Td>
268
- <Text size="xs" ff="monospace">
269
- {exec.status}
270
- </Text>
271
- </Table.Td>
272
- <Table.Td>
273
- <Text size="xs" c="dimmed" ff="monospace">
274
- {exec.startedAt &&
275
- (exec.completedAt || exec.status === "running")
276
- ? formatDuration(exec.startedAt, exec.completedAt)
277
- : "\u2014"}
278
- </Text>
279
- </Table.Td>
280
- </Table.Tr>
281
- ))}
282
- {recent.length === 0 && (
283
- <Table.Tr>
284
- <Table.Td colSpan={3}>
285
- <Text size="sm" c="dimmed" ta="center">
286
- No recent executions
287
- </Text>
288
- </Table.Td>
289
- </Table.Tr>
290
- )}
291
- </Table.Tbody>
292
- </Table>
293
- </Paper>
294
-
295
- {/* Top Failures (7d) */}
296
- <Paper p="md" radius="md" withBorder>
297
- <Text size="sm" fw={600} mb="sm">
298
- Top Failures (7d)
299
- </Text>
300
- {failures.length > 0 ? (
301
- <Table>
302
- <Table.Thead>
303
- <Table.Tr>
304
- <Table.Th>Job</Table.Th>
305
- <Table.Th>Count</Table.Th>
306
- <Table.Th>Last Error</Table.Th>
307
- </Table.Tr>
308
- </Table.Thead>
309
- <Table.Tbody>
310
- {failures.map((f) => (
311
- <Table.Tr key={f.jobName}>
312
- <Table.Td>
313
- <Text size="xs" fw={500} ff="monospace" lineClamp={1}>
314
- {f.jobName}
315
- </Text>
316
- </Table.Td>
317
- <Table.Td>
318
- <Text size="xs" fw={600} ff="monospace">
319
- {f.failures}
320
- </Text>
321
- </Table.Td>
322
- <Table.Td>
323
- <Text
324
- size="xs"
325
- c="dimmed"
326
- lineClamp={1}
327
- style={{ maxWidth: 200 }}
328
- >
329
- {f.lastError ?? "\u2014"}
330
- </Text>
331
- </Table.Td>
332
- </Table.Tr>
333
- ))}
334
- </Table.Tbody>
335
- </Table>
336
- ) : (
337
- <Flex h={100} align="center" justify="center">
338
- <Text size="sm" c="dimmed">
339
- No failures in the last 7 days
340
- </Text>
341
- </Flex>
342
- )}
343
- </Paper>
344
- </SimpleGrid>
345
- </Flex>
346
- );
347
- };
348
-
349
- export default AdminJobDashboard;
@@ -1,301 +0,0 @@
1
- import type { DetailListItem } from "@alepha/ui";
2
- import {
3
- DataTable,
4
- DetailList,
5
- Flex,
6
- Text,
7
- useDialog,
8
- useToast,
9
- } from "@alepha/ui";
10
- import { Badge } from "@mantine/core";
11
- import {
12
- IconCircleCheck,
13
- IconCircleX,
14
- IconPlayerPlay,
15
- } from "@tabler/icons-react";
16
- import { t } from "alepha";
17
- import type {
18
- AdminJobController,
19
- JobCronInfo,
20
- JobFailure,
21
- JobQueueDepth,
22
- JobRegistration,
23
- } from "alepha/api/jobs";
24
- import { useClient } from "alepha/react";
25
- import { useI18n } from "alepha/react/i18n";
26
- import { useCallback, useEffect, useState } from "react";
27
-
28
- // ─────────────────────────────────────────────────────────────────────────────
29
-
30
- const registryFilters = t.object({
31
- type: t.optional(t.enum(["cron", "push", "both"])),
32
- });
33
-
34
- // ─────────────────────────────────────────────────────────────────────────────
35
-
36
- const AdminJobRegistry = () => {
37
- const client = useClient<AdminJobController>();
38
- const { l } = useI18n();
39
- const toast = useToast();
40
- const dialog = useDialog();
41
- const [refreshKey, setRefreshKey] = useState(0);
42
-
43
- // Extra data for enriched panels
44
- const [cronMap, setCronMap] = useState<Map<string, JobCronInfo>>(new Map());
45
- const [queueMap, setQueueMap] = useState<Map<string, JobQueueDepth>>(
46
- new Map(),
47
- );
48
- const [failureMap, setFailureMap] = useState<Map<string, JobFailure>>(
49
- new Map(),
50
- );
51
-
52
- const loadExtraData = useCallback(async () => {
53
- try {
54
- const [cronData, queueData, failureData] = await Promise.all([
55
- client.getCronJobs(),
56
- client.getJobQueueDepth(),
57
- client.getJobTopFailures(),
58
- ]);
59
- setCronMap(new Map(cronData.map((c) => [c.name, c])));
60
- setQueueMap(new Map(queueData.map((q) => [q.jobName, q])));
61
- setFailureMap(new Map(failureData.map((f) => [f.jobName, f])));
62
- } catch {
63
- // non-critical
64
- }
65
- }, [client]);
66
-
67
- useEffect(() => {
68
- loadExtraData();
69
- }, [loadExtraData, refreshKey]);
70
-
71
- const handleTriggerJob = useCallback(
72
- async (name: string) => {
73
- const confirmed = await dialog.confirm({
74
- title: "Trigger Job",
75
- message: `Are you sure you want to trigger "${name}" manually?`,
76
- confirmLabel: "Trigger",
77
- confirmColor: "blue",
78
- });
79
-
80
- if (!confirmed) return;
81
-
82
- return client.triggerJob({ body: { name } }).then(() => {
83
- toast.success(`Job "${name}" triggered`);
84
- setRefreshKey((k) => k + 1);
85
- });
86
- },
87
- [client, dialog, toast],
88
- );
89
-
90
- return (
91
- <Flex p="md" flex={1} direction="column" gap="md">
92
- <DataTable<JobRegistration, typeof registryFilters>
93
- key={`registry-${refreshKey}`}
94
- submitOnInit
95
- typeFormProps={{
96
- skipSubmitButton: true,
97
- columns: 1,
98
- }}
99
- tableProps={{
100
- horizontalSpacing: "sm",
101
- verticalSpacing: "sm",
102
- }}
103
- onFilterChange={(_key, _value, form) => form.submit()}
104
- filters={registryFilters}
105
- items={async (filters) => {
106
- const items = await client.getJobRegistry();
107
- const filtered = filters.type
108
- ? items.filter((i) => i.type === filters.type)
109
- : items;
110
- return { content: filtered };
111
- }}
112
- columns={{
113
- name: {
114
- label: "Name",
115
- value: (item) => (
116
- <Text size="sm" fw={500} ff="monospace">
117
- {item.name}
118
- </Text>
119
- ),
120
- },
121
- type: {
122
- label: "Type",
123
- value: (item) => (
124
- <Badge size="sm" variant="default">
125
- {item.type}
126
- </Badge>
127
- ),
128
- },
129
- priority: {
130
- label: "Priority",
131
- value: (item) => (
132
- <Text size="sm" tt="capitalize">
133
- {item.priority}
134
- </Text>
135
- ),
136
- },
137
- concurrency: {
138
- label: "Concurrency",
139
- value: (item) => (
140
- <Text size="sm" ff="monospace">
141
- {item.concurrency}
142
- </Text>
143
- ),
144
- },
145
- queue: {
146
- label: "Queue",
147
- value: (item) => {
148
- const q = queueMap.get(item.name);
149
- if (
150
- !q ||
151
- q.pending + q.running + q.scheduled + q.retrying + q.dead === 0
152
- ) {
153
- return (
154
- <Text size="xs" c="dimmed">
155
-
156
- </Text>
157
- );
158
- }
159
- return (
160
- <Flex gap={4}>
161
- {q.running > 0 && (
162
- <Badge size="xs" variant="default">
163
- {q.running} run
164
- </Badge>
165
- )}
166
- {q.pending > 0 && (
167
- <Badge size="xs" variant="default">
168
- {q.pending} pen
169
- </Badge>
170
- )}
171
- {q.retrying > 0 && (
172
- <Badge size="xs" variant="default">
173
- {q.retrying} retry
174
- </Badge>
175
- )}
176
- {q.dead > 0 && (
177
- <Badge size="xs" variant="default">
178
- {q.dead} dead
179
- </Badge>
180
- )}
181
- </Flex>
182
- );
183
- },
184
- },
185
- }}
186
- rowActions={(item) => [
187
- {
188
- label: "Trigger",
189
- color: "blue",
190
- icon: IconPlayerPlay,
191
- onClick: () => handleTriggerJob(item.name),
192
- },
193
- ]}
194
- panel={(item) => {
195
- const cron = cronMap.get(item.name);
196
- const failure = failureMap.get(item.name);
197
-
198
- const detailItems: DetailListItem[] = [
199
- {
200
- label: "Cron",
201
- value: item.cron ? (
202
- <Text size="sm" ff="monospace">
203
- {item.cron}
204
- </Text>
205
- ) : undefined,
206
- hidden: !item.cron,
207
- },
208
- {
209
- label: "Timeout",
210
- value: item.timeout,
211
- hidden: !item.timeout,
212
- },
213
- {
214
- label: "Retry",
215
- value: item.retry
216
- ? `${item.retry.retries}x${item.retry.hasBackoff ? " (backoff)" : ""}`
217
- : undefined,
218
- hidden: !item.retry,
219
- },
220
- {
221
- label: "Batch",
222
- value: item.batch
223
- ? `${item.batch.size} / ${item.batch.window}`
224
- : undefined,
225
- hidden: !item.batch,
226
- },
227
- {
228
- label: "Schema",
229
- value: item.hasSchema ? "Yes" : "No",
230
- },
231
- ];
232
-
233
- return (
234
- <Flex direction="column" gap="sm" p="sm">
235
- <DetailList items={detailItems} columns={3} />
236
-
237
- {/* Last cron execution */}
238
- {cron?.lastExecution && (
239
- <Flex gap="lg" wrap="wrap" align="center">
240
- <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
241
- Last Run
242
- </Text>
243
- <Flex align="center" gap={4}>
244
- {cron.lastExecution.status === "completed" ? (
245
- <IconCircleCheck
246
- size={14}
247
- color="var(--mantine-color-dimmed)"
248
- />
249
- ) : (
250
- <IconCircleX
251
- size={14}
252
- color="var(--mantine-color-dimmed)"
253
- />
254
- )}
255
- <Text size="xs" tt="capitalize">
256
- {cron.lastExecution.status}
257
- </Text>
258
- </Flex>
259
- {cron.lastExecution.startedAt && (
260
- <Text size="xs" c="dimmed">
261
- {l(cron.lastExecution.startedAt, { date: "fromNow" })}
262
- </Text>
263
- )}
264
- {cron.lastExecution.error && (
265
- <Text size="xs" c="dimmed" lineClamp={1}>
266
- {cron.lastExecution.error}
267
- </Text>
268
- )}
269
- </Flex>
270
- )}
271
-
272
- {/* Failures */}
273
- {failure && (
274
- <Flex gap="lg" wrap="wrap" align="center">
275
- <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
276
- Failures (7d)
277
- </Text>
278
- <Text size="xs" fw={500}>
279
- {failure.failures}
280
- </Text>
281
- {failure.lastError && (
282
- <Text
283
- size="xs"
284
- c="dimmed"
285
- lineClamp={1}
286
- style={{ maxWidth: 400 }}
287
- >
288
- {failure.lastError}
289
- </Text>
290
- )}
291
- </Flex>
292
- )}
293
- </Flex>
294
- );
295
- }}
296
- />
297
- </Flex>
298
- );
299
- };
300
-
301
- export default AdminJobRegistry;
@@ -1,19 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import type { AlephaIntent } from "../interfaces/AlephaIntent.ts";
3
-
4
- export type HeadingProps = {
5
- title: string | ReactNode;
6
- description?: string | ReactNode;
7
- icon?: ReactNode;
8
- intent?: AlephaIntent;
9
- loading?: boolean;
10
- ellipsis?: boolean;
11
- fill?: boolean;
12
- tag?: string | ReactNode;
13
- };
14
-
15
- const Heading = (props: HeadingProps) => {
16
- return <h1>Heading</h1>;
17
- };
18
-
19
- export default Heading;