@alepha/ui 0.16.1 → 0.17.0

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 (218) hide show
  1. package/dist/admin/{AdminApiKeys-GMORg-1l.js → AdminApiKeys-CF_qOO3u.js} +20 -19
  2. package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +1 -0
  3. package/dist/admin/{AdminAudits-pkWrjq1Z.js → AdminAudits-BQno3hZG.js} +7 -7
  4. package/dist/admin/AdminAudits-BQno3hZG.js.map +1 -0
  5. package/dist/admin/{AdminFiles-WeQbsCsl.js → AdminFiles-kvuUaASF.js} +3 -4
  6. package/dist/admin/{AdminFiles-WeQbsCsl.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-BqZiXx4H.js → AdminLayout-e-ZP5nWw.js} +6 -9
  14. package/dist/admin/AdminLayout-e-ZP5nWw.js.map +1 -0
  15. package/dist/admin/{AdminNotifications-Ds5Un0NJ.js → AdminNotifications-DeHJFf6W.js} +3 -4
  16. package/dist/admin/{AdminNotifications-Ds5Un0NJ.js.map → AdminNotifications-DeHJFf6W.js.map} +1 -1
  17. package/dist/admin/AdminParameters-iQE8o7a7.js +774 -0
  18. package/dist/admin/AdminParameters-iQE8o7a7.js.map +1 -0
  19. package/dist/admin/{AdminSessions-DzIOxM3b.js → AdminSessions-oKJCbd7w.js} +5 -6
  20. package/dist/admin/AdminSessions-oKJCbd7w.js.map +1 -0
  21. package/dist/admin/{AdminUserAudits-CiUPN2BC.js → AdminUserAudits-BNCEle_E.js} +6 -7
  22. package/dist/admin/AdminUserAudits-BNCEle_E.js.map +1 -0
  23. package/dist/admin/{AdminUserCreate-BwQKr4xE.js → AdminUserCreate-CgqeFwCt.js} +6 -6
  24. package/dist/admin/AdminUserCreate-CgqeFwCt.js.map +1 -0
  25. package/dist/admin/{AdminUserDetails-uqtC5aJ1.js → AdminUserDetails-DDe1A1GP.js} +30 -28
  26. package/dist/admin/AdminUserDetails-DDe1A1GP.js.map +1 -0
  27. package/dist/admin/{AdminUserLayout-CiPay35T.js → AdminUserLayout-HAlobhWf.js} +20 -19
  28. package/dist/admin/AdminUserLayout-HAlobhWf.js.map +1 -0
  29. package/dist/admin/{AdminUserSessions-DAE8Nf1F.js → AdminUserSessions-Bq1LnVLf.js} +5 -6
  30. package/dist/admin/AdminUserSessions-Bq1LnVLf.js.map +1 -0
  31. package/dist/admin/{AdminUserSettings-EbahaV2a.js → AdminUserSettings-BRsBZoxV.js} +10 -9
  32. package/dist/admin/AdminUserSettings-BRsBZoxV.js.map +1 -0
  33. package/dist/admin/{AdminUsers-Dcjh0KNW.js → AdminUsers-D71kIOSn.js} +6 -7
  34. package/dist/admin/AdminUsers-D71kIOSn.js.map +1 -0
  35. package/dist/admin/index.d.ts +21 -85
  36. package/dist/admin/index.d.ts.map +1 -1
  37. package/dist/admin/index.js +66 -88
  38. package/dist/admin/index.js.map +1 -1
  39. package/dist/auth/{AuthLayout-Dj5K4SIN.js → AuthLayout-CdJcrPs4.js} +2 -3
  40. package/dist/auth/{AuthLayout-Dj5K4SIN.js.map → AuthLayout-CdJcrPs4.js.map} +1 -1
  41. package/dist/{demo/IconGoogle-CbBF8Hqq.js → auth/IconGoogle-Bm18QD2q.js} +2 -4
  42. package/dist/auth/{IconGoogle-DpSlPZ1u.js.map → IconGoogle-Bm18QD2q.js.map} +1 -1
  43. package/dist/auth/{Login-BBqTosqZ.js → Login-BS_FYTy0.js} +19 -13
  44. package/dist/auth/Login-BS_FYTy0.js.map +1 -0
  45. package/dist/auth/{Profile-Bxj8Nwom.js → Profile-CjDsW378.js} +17 -12
  46. package/dist/auth/Profile-CjDsW378.js.map +1 -0
  47. package/dist/auth/{Register-Ce675Crg.js → Register-C5eqzAaD.js} +27 -17
  48. package/dist/auth/Register-C5eqzAaD.js.map +1 -0
  49. package/dist/auth/{ResetPassword-DWdt7c40.js → ResetPassword-XifinVao.js} +17 -10
  50. package/dist/auth/ResetPassword-XifinVao.js.map +1 -0
  51. package/dist/auth/{VerifyEmail-CI4JwByV.js → VerifyEmail-DTgbeJOO.js} +9 -6
  52. package/dist/auth/VerifyEmail-DTgbeJOO.js.map +1 -0
  53. package/dist/auth/index.d.ts +18 -14
  54. package/dist/auth/index.d.ts.map +1 -1
  55. package/dist/auth/index.js +19 -18
  56. package/dist/auth/index.js.map +1 -1
  57. package/dist/auth/rolldown-runtime-CjeV3_4I.js +18 -0
  58. package/dist/core/index.d.ts +182 -92
  59. package/dist/core/index.d.ts.map +1 -1
  60. package/dist/core/index.js +789 -476
  61. package/dist/core/index.js.map +1 -1
  62. package/dist/demo/DemoDataTable-lnBKWBf8.js +362 -0
  63. package/dist/demo/DemoDataTable-lnBKWBf8.js.map +1 -0
  64. package/dist/demo/{DemoHome-Cce2bWmg.js → DemoHome-CUMZsYaH.js} +6 -6
  65. package/dist/demo/DemoHome-CUMZsYaH.js.map +1 -0
  66. package/dist/demo/{DemoJsonViewer-Dgdk3Txb.js → DemoJsonViewer-_uokbGaW.js} +18 -19
  67. package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +1 -0
  68. package/dist/demo/{DemoLayout-B20TEuhV.js → DemoLayout-DHVoacE6.js} +4 -5
  69. package/dist/demo/DemoLayout-DHVoacE6.js.map +1 -0
  70. package/dist/demo/{DemoLogin-CvCG2WVh.js → DemoLogin-DjJ9314c.js} +27 -24
  71. package/dist/demo/DemoLogin-DjJ9314c.js.map +1 -0
  72. package/dist/demo/{DemoRegister-CmeHbOAs.js → DemoRegister-DzkJ5M83.js} +39 -32
  73. package/dist/demo/DemoRegister-DzkJ5M83.js.map +1 -0
  74. package/dist/demo/{DemoResetPassword-CKO5iA_6.js → DemoResetPassword-DWh4_BpQ.js} +30 -26
  75. package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +1 -0
  76. package/dist/demo/{DemoSidebar-MVmQKfMt.js → DemoSidebar-C1csnGhX.js} +4 -5
  77. package/dist/demo/DemoSidebar-C1csnGhX.js.map +1 -0
  78. package/dist/demo/{DemoTypeForm-w-qtfRlC.js → DemoTypeForm-CWz6fJrJ.js} +4 -5
  79. package/dist/demo/DemoTypeForm-CWz6fJrJ.js.map +1 -0
  80. package/dist/demo/{DemoVerifyEmail-C8FFJT5A.js → DemoVerifyEmail-DbU_tCj8.js} +16 -16
  81. package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +1 -0
  82. package/dist/{auth/IconGoogle-DpSlPZ1u.js → demo/IconGoogle-Ch1m3Uzl.js} +2 -4
  83. package/dist/demo/{IconGoogle-CbBF8Hqq.js.map → IconGoogle-Ch1m3Uzl.js.map} +1 -1
  84. package/dist/demo/{Showcase-CQrMWars.js → Showcase-BzoXNlCn.js} +11 -13
  85. package/dist/demo/Showcase-BzoXNlCn.js.map +1 -0
  86. package/dist/demo/index.d.ts +3 -70
  87. package/dist/demo/index.d.ts.map +1 -1
  88. package/dist/demo/index.js +11 -15
  89. package/dist/demo/index.js.map +1 -1
  90. package/dist/json/index.js +2 -2
  91. package/dist/json/index.js.map +1 -1
  92. package/package.json +11 -5
  93. package/src/admin/AdminRouter.ts +51 -29
  94. package/src/admin/components/AdminLayout.tsx +6 -9
  95. package/src/admin/components/audits/AdminAudits.tsx +5 -5
  96. package/src/admin/components/jobs/AdminJobDashboard.tsx +455 -0
  97. package/src/admin/components/jobs/AdminJobExecutions.tsx +693 -0
  98. package/src/admin/components/jobs/AdminJobRegistry.tsx +325 -0
  99. package/src/admin/components/keys/AdminApiKeys.tsx +28 -31
  100. package/src/admin/components/parameters/AdminParameters.tsx +156 -78
  101. package/src/admin/components/parameters/ParameterDetails.tsx +173 -108
  102. package/src/admin/components/parameters/ParameterEmptyState.tsx +27 -0
  103. package/src/admin/components/parameters/ParameterHistory.tsx +22 -35
  104. package/src/admin/components/parameters/ParameterTree.tsx +283 -109
  105. package/src/admin/components/parameters/types.ts +3 -3
  106. package/src/admin/components/sessions/AdminSessions.tsx +3 -3
  107. package/src/admin/components/shared/AdminResourceHeader.tsx +20 -16
  108. package/src/admin/components/users/AdminUserAudits.tsx +5 -5
  109. package/src/admin/components/users/AdminUserCreate.tsx +3 -3
  110. package/src/admin/components/users/AdminUserDetails.tsx +51 -53
  111. package/src/admin/components/users/AdminUserLayout.tsx +7 -7
  112. package/src/admin/components/users/AdminUserSessions.tsx +3 -3
  113. package/src/admin/components/users/AdminUserSettings.tsx +9 -9
  114. package/src/admin/components/users/AdminUsers.tsx +5 -5
  115. package/src/admin/components/verifications/AdminVerifications.tsx +3 -3
  116. package/src/admin/index.ts +0 -24
  117. package/src/admin/primitives/$uiAdmin.ts +2 -2
  118. package/src/auth/AuthRouter.ts +1 -0
  119. package/src/auth/components/Login.tsx +13 -13
  120. package/src/auth/components/Profile.tsx +17 -26
  121. package/src/auth/components/Register.tsx +21 -31
  122. package/src/auth/components/ResetPassword.tsx +13 -22
  123. package/src/auth/components/VerifyEmail.tsx +5 -5
  124. package/src/auth/components/buttons/UserButton.tsx +14 -4
  125. package/src/core/components/buttons/ActionButton.tsx +13 -17
  126. package/src/core/components/buttons/DarkModeButton.tsx +8 -4
  127. package/src/core/components/buttons/ToggleSidebarButton.tsx +3 -5
  128. package/src/core/components/data/ErrorViewer.tsx +15 -15
  129. package/src/core/components/dialogs/AlertDialog.tsx +3 -3
  130. package/src/core/components/dialogs/ConfirmDialog.tsx +3 -3
  131. package/src/core/components/dialogs/PromptDialog.tsx +3 -3
  132. package/src/core/components/form/Control.tsx +19 -32
  133. package/src/core/components/form/ControlArray.tsx +206 -96
  134. package/src/core/components/form/ControlObject.tsx +3 -3
  135. package/src/core/components/form/ControlQueryBuilder.tsx +20 -22
  136. package/src/core/components/form/ControlSelect.tsx +4 -0
  137. package/src/core/components/form/TypeForm.browser.spec.tsx +727 -0
  138. package/src/core/components/form/TypeForm.tsx +7 -0
  139. package/src/core/components/layout/AlephaMantineProvider.tsx +1 -0
  140. package/src/core/components/layout/Breadcrumb.tsx +91 -0
  141. package/src/core/components/layout/{AdminShell.tsx → DashboardShell.tsx} +77 -32
  142. package/src/core/components/layout/Omnibar.tsx +2 -1
  143. package/src/core/components/layout/Sidebar.tsx +63 -19
  144. package/src/core/components/table/ColumnPicker.tsx +47 -31
  145. package/src/core/components/table/DataTable.tsx +277 -201
  146. package/src/core/components/table/DataTableFilters.tsx +8 -0
  147. package/src/core/components/table/DataTableToolbar.tsx +98 -5
  148. package/src/core/components/table/FilterPicker.tsx +28 -26
  149. package/src/core/components/table/types.ts +52 -37
  150. package/src/core/components/table/useTableSelection.ts +83 -0
  151. package/src/core/constants/ui.ts +1 -1
  152. package/src/core/helpers/renderIcon.tsx +5 -2
  153. package/src/core/index.ts +9 -5
  154. package/src/core/styles.css +8 -7
  155. package/src/core/utils/parseInput.ts +1 -0
  156. package/src/core/utils/string.ts +28 -4
  157. package/src/demo/components/DemoHome.tsx +5 -5
  158. package/src/demo/components/DemoLayout.tsx +6 -2
  159. package/src/demo/components/core/DemoDataTable.tsx +209 -5
  160. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  161. package/src/demo/components/shared/MacWindow.tsx +7 -7
  162. package/src/demo/components/shared/Showcase.tsx +3 -3
  163. package/src/demo/index.ts +0 -11
  164. package/src/json/components/JsonViewer.tsx +3 -3
  165. package/dist/admin/AdminApiKeys-DsmGnHNh.js +0 -3
  166. package/dist/admin/AdminApiKeys-GMORg-1l.js.map +0 -1
  167. package/dist/admin/AdminAudits-8SM96viT.js +0 -3
  168. package/dist/admin/AdminAudits-pkWrjq1Z.js.map +0 -1
  169. package/dist/admin/AdminFiles-B56ocq4H.js +0 -3
  170. package/dist/admin/AdminJobs-B-q9iGO3.js +0 -697
  171. package/dist/admin/AdminJobs-B-q9iGO3.js.map +0 -1
  172. package/dist/admin/AdminJobs-CED1syCn.js +0 -3
  173. package/dist/admin/AdminLayout-BqZiXx4H.js.map +0 -1
  174. package/dist/admin/AdminNotifications-B0B1rdc4.js +0 -3
  175. package/dist/admin/AdminParameters-BU3lATdJ.js +0 -3
  176. package/dist/admin/AdminParameters-CfDUpc78.js +0 -575
  177. package/dist/admin/AdminParameters-CfDUpc78.js.map +0 -1
  178. package/dist/admin/AdminSessions-BDGK2MS6.js +0 -3
  179. package/dist/admin/AdminSessions-DzIOxM3b.js.map +0 -1
  180. package/dist/admin/AdminUserAudits-CiUPN2BC.js.map +0 -1
  181. package/dist/admin/AdminUserAudits-Cj79gENT.js +0 -3
  182. package/dist/admin/AdminUserCreate-BwQKr4xE.js.map +0 -1
  183. package/dist/admin/AdminUserCreate-Cq-mUmBs.js +0 -3
  184. package/dist/admin/AdminUserDetails-DRjVAPFd.js +0 -3
  185. package/dist/admin/AdminUserDetails-uqtC5aJ1.js.map +0 -1
  186. package/dist/admin/AdminUserLayout-CGzmHHby.js +0 -3
  187. package/dist/admin/AdminUserLayout-CiPay35T.js.map +0 -1
  188. package/dist/admin/AdminUserSessions-DAE8Nf1F.js.map +0 -1
  189. package/dist/admin/AdminUserSessions-DcdzuNZ9.js +0 -3
  190. package/dist/admin/AdminUserSettings-D7V6-ceX.js +0 -3
  191. package/dist/admin/AdminUserSettings-EbahaV2a.js.map +0 -1
  192. package/dist/admin/AdminUsers-D9nyzGqQ.js +0 -3
  193. package/dist/admin/AdminUsers-Dcjh0KNW.js.map +0 -1
  194. package/dist/auth/Login-BBqTosqZ.js.map +0 -1
  195. package/dist/auth/Login-CoU63mMR.js +0 -4
  196. package/dist/auth/Profile-Bxj8Nwom.js.map +0 -1
  197. package/dist/auth/Register-BV_oa_AK.js +0 -4
  198. package/dist/auth/Register-Ce675Crg.js.map +0 -1
  199. package/dist/auth/ResetPassword-D5wC8GAA.js +0 -3
  200. package/dist/auth/ResetPassword-DWdt7c40.js.map +0 -1
  201. package/dist/auth/VerifyEmail-CI4JwByV.js.map +0 -1
  202. package/dist/auth/VerifyEmail-DAfqVm5s.js +0 -3
  203. package/dist/demo/DemoDataTable-CguplbR7.js +0 -150
  204. package/dist/demo/DemoDataTable-CguplbR7.js.map +0 -1
  205. package/dist/demo/DemoHome-Cce2bWmg.js.map +0 -1
  206. package/dist/demo/DemoHome-DC9qkMNe.js +0 -3
  207. package/dist/demo/DemoJsonViewer-DIssGVlJ.js +0 -4
  208. package/dist/demo/DemoJsonViewer-Dgdk3Txb.js.map +0 -1
  209. package/dist/demo/DemoLayout-B20TEuhV.js.map +0 -1
  210. package/dist/demo/DemoLayout-DSRyf4qJ.js +0 -3
  211. package/dist/demo/DemoLogin-CvCG2WVh.js.map +0 -1
  212. package/dist/demo/DemoRegister-CmeHbOAs.js.map +0 -1
  213. package/dist/demo/DemoResetPassword-CKO5iA_6.js.map +0 -1
  214. package/dist/demo/DemoSidebar-MVmQKfMt.js.map +0 -1
  215. package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +0 -1
  216. package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +0 -1
  217. package/dist/demo/Showcase-CQrMWars.js.map +0 -1
  218. package/src/admin/components/jobs/AdminJobs.tsx +0 -772
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/json/components/JsonViewer.tsx","../../src/json/extensions/DialogService.tsx"],"sourcesContent":["import {\n ActionIcon,\n Group,\n getTreeExpandedState,\n type MantineSize,\n Text,\n Tree,\n useTree,\n} from \"@mantine/core\";\nimport {\n IconCheck,\n IconChevronDown,\n IconChevronRight,\n IconCopy,\n} from \"@tabler/icons-react\";\nimport {\n type CSSProperties,\n type ReactNode,\n useCallback,\n useMemo,\n useState,\n} from \"react\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface JsonViewerProps {\n data: any;\n /**\n * Depth level to expand by default (0 = collapsed, Infinity = all expanded)\n */\n defaultExpandedDepth?: number;\n /**\n * Maximum nesting depth to render\n */\n maxDepth?: number;\n /**\n * Size variant\n */\n size?: MantineSize;\n /**\n * Whether to show quotes around keys and strings\n */\n showQuotes?: boolean;\n /**\n * Show copy button on row hover\n */\n showCopyButton?: boolean;\n /**\n * Custom value formatter. Return formatted string or undefined to use default.\n */\n formatValue?: (\n key: string | undefined,\n value: any,\n path: string[],\n ) => string | number | undefined;\n}\n\ninterface JsonTreeNode {\n value: string;\n label: string;\n children?: JsonTreeNode[];\n // Custom properties\n nodeValue: any;\n nodeKey: string | undefined;\n path: string[];\n isArrayItem: boolean;\n isRoot?: boolean;\n}\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst SIZE_CONFIG: Record<MantineSize, { icon: number; levelOffset: number }> =\n {\n xs: { icon: 14, levelOffset: 16 },\n sm: { icon: 16, levelOffset: 20 },\n md: { icon: 18, levelOffset: 24 },\n lg: { icon: 20, levelOffset: 28 },\n xl: { icon: 22, levelOffset: 32 },\n };\n\nconst STYLES = {\n root: {\n fontFamily: \"var(--mantine-font-family-monospace)\",\n } satisfies CSSProperties,\n chevron: {\n flexShrink: 0,\n color: \"var(--mantine-color-dimmed)\",\n } satisfies CSSProperties,\n key: {\n color: \"var(--mantine-color-cyan-text)\",\n fontWeight: 500,\n } satisfies CSSProperties,\n colon: {\n color: \"var(--mantine-color-dimmed)\",\n } satisfies CSSProperties,\n string: {\n color: \"var(--mantine-color-teal-text)\",\n } satisfies CSSProperties,\n number: {\n color: \"var(--mantine-color-blue-text)\",\n } satisfies CSSProperties,\n boolean: {\n color: \"var(--mantine-color-violet-text)\",\n } satisfies CSSProperties,\n null: {\n color: \"var(--mantine-color-dimmed)\",\n fontStyle: \"italic\",\n } satisfies CSSProperties,\n preview: {\n color: \"var(--mantine-color-dimmed)\",\n } satisfies CSSProperties,\n};\n\n// =============================================================================\n// HELPERS\n// =============================================================================\n\nconst getValueType = (val: any): string => {\n if (val === null) return \"null\";\n if (val === undefined) return \"undefined\";\n if (Array.isArray(val)) return \"array\";\n return typeof val;\n};\n\n// Convert JSON to tree data structure\nfunction buildTreeNodes(\n data: any,\n path: string[] = [],\n key?: string,\n isArrayItem = false,\n maxDepth = 10,\n): JsonTreeNode | null {\n const currentPath = key !== undefined ? [...path, key] : path;\n const nodeId = currentPath.length > 0 ? currentPath.join(\".\") : \"root\";\n\n if (currentPath.length > maxDepth) {\n return {\n value: nodeId,\n label: key ?? \"\",\n nodeValue: data,\n nodeKey: key,\n path: currentPath,\n isArrayItem,\n };\n }\n\n const type = getValueType(data);\n\n if (type === \"object\" || type === \"array\") {\n const entries =\n type === \"array\"\n ? (data as any[]).map((v, i) => [String(i), v] as const)\n : Object.entries(data);\n\n const children = entries\n .map(([k, v]) =>\n buildTreeNodes(v, currentPath, k, type === \"array\", maxDepth),\n )\n .filter((n): n is JsonTreeNode => n !== null);\n\n return {\n value: nodeId,\n label: key ?? \"\",\n nodeValue: data,\n nodeKey: key,\n path: currentPath,\n isArrayItem,\n children: children.length > 0 ? children : undefined,\n };\n }\n\n return {\n value: nodeId,\n label: key ?? \"\",\n nodeValue: data,\n nodeKey: key,\n path: currentPath,\n isArrayItem,\n };\n}\n\n// Get all expandable node IDs up to a certain depth\nfunction getExpandedIds(\n nodes: JsonTreeNode[],\n targetDepth: number,\n currentDepth = 0,\n): string[] {\n if (currentDepth >= targetDepth) return [];\n const ids: string[] = [];\n for (const node of nodes) {\n if (node.children) {\n ids.push(node.value);\n ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));\n }\n }\n return ids;\n}\n\n// =============================================================================\n// COPY BUTTON COMPONENT\n// =============================================================================\n\nconst CopyButton = ({\n value,\n iconSize,\n}: {\n value: string;\n iconSize: number;\n}) => {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n navigator.clipboard.writeText(value);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n },\n [value],\n );\n\n return (\n <ActionIcon\n size={iconSize + 4}\n variant=\"transparent\"\n c={copied ? \"green\" : \"dimmed\"}\n onClick={handleCopy}\n className=\"alepha-json-viewer-copy\"\n >\n {copied ? <IconCheck size={iconSize} /> : <IconCopy size={iconSize} />}\n </ActionIcon>\n );\n};\n\n// =============================================================================\n// ROW NODE COMPONENT\n// =============================================================================\n\ninterface RowNodeProps {\n node: JsonTreeNode;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: any;\n size: MantineSize;\n config: { icon: number; levelOffset: number };\n showQuotes: boolean;\n showCopyButton: boolean;\n renderValue: (val: any, key: string | undefined, path: string[]) => ReactNode;\n}\n\nconst RowNode = ({\n node,\n expanded,\n hasChildren,\n elementProps,\n size,\n config,\n showQuotes,\n showCopyButton,\n renderValue,\n}: RowNodeProps) => {\n const { nodeValue, nodeKey, path, isArrayItem, isRoot } = node;\n const type = getValueType(nodeValue);\n const isExpandable = type === \"object\" || type === \"array\";\n\n const getPreview = () => {\n if (!isExpandable) return null;\n const entries = type === \"array\" ? nodeValue : Object.keys(nodeValue);\n const count = entries.length;\n const label = type === \"array\" ? \"item\" : \"key\";\n\n // For root node or collapsed nodes, show the count\n if (!expanded) {\n return (\n <Text fs={\"italic\"} component=\"span\" size={size} style={STYLES.preview}>\n {count === 0\n ? type === \"array\"\n ? \"[]\"\n : \"{}\"\n : type === \"array\"\n ? `[ ${count} ${count === 1 ? label : `${label}s`} ]`\n : `{ ${count} ${count === 1 ? label : `${label}s`} }`}\n </Text>\n );\n }\n\n return null;\n };\n\n const getCopyValue = () =>\n isExpandable ? JSON.stringify(nodeValue, null, 2) : String(nodeValue ?? \"\");\n\n return (\n <Group\n gap={6}\n wrap=\"nowrap\"\n {...elementProps}\n className={`alepha-json-viewer-row ${elementProps.className || \"\"}`}\n >\n {hasChildren ? (\n expanded ? (\n <IconChevronDown size={config.icon} style={STYLES.chevron} />\n ) : (\n <IconChevronRight size={config.icon} style={STYLES.chevron} />\n )\n ) : (\n <span style={{ width: config.icon, flexShrink: 0 }} />\n )}\n\n {nodeKey !== undefined && !isArrayItem && (\n <Text component=\"span\" size={size}>\n <span style={STYLES.key}>\n {showQuotes ? `\"${nodeKey}\"` : nodeKey}\n </span>\n <span style={STYLES.colon}>:</span>\n </Text>\n )}\n\n {nodeKey !== undefined && isArrayItem && (\n <Text component=\"span\" size={size}>\n <span style={STYLES.key}>{nodeKey}</span>\n <span style={STYLES.colon}>:</span>\n </Text>\n )}\n\n {hasChildren ? (\n getPreview()\n ) : isExpandable ? (\n type === \"array\" ? (\n <Text component=\"span\" size={size} style={STYLES.preview}>\n []\n </Text>\n ) : (\n <Text component=\"span\" size={size} style={STYLES.preview}>\n {\"{}\"}\n </Text>\n )\n ) : (\n renderValue(nodeValue, nodeKey, path)\n )}\n\n {showCopyButton && (\n <CopyButton value={getCopyValue()} iconSize={config.icon} />\n )}\n </Group>\n );\n};\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\nexport const JsonViewer = ({\n data,\n defaultExpandedDepth = 2,\n maxDepth = 10,\n size = \"sm\",\n showQuotes = false,\n showCopyButton = true,\n formatValue,\n}: JsonViewerProps) => {\n const config = SIZE_CONFIG[size] || SIZE_CONFIG.sm;\n\n // Build tree data from JSON with root wrapper\n const treeData = useMemo(() => {\n const type = getValueType(data);\n\n // For objects and arrays, create a root node wrapper\n if (type === \"object\" || type === \"array\") {\n const entries =\n type === \"array\"\n ? (data as any[]).map((v, i) => [String(i), v] as const)\n : Object.entries(data);\n const children = entries\n .map(([k, v]) => buildTreeNodes(v, [], k, type === \"array\", maxDepth))\n .filter((n): n is JsonTreeNode => n !== null);\n\n const rootNode: JsonTreeNode = {\n value: \"root\",\n label: \"\",\n nodeValue: data,\n nodeKey: undefined,\n path: [],\n isArrayItem: false,\n isRoot: true,\n children: children.length > 0 ? children : undefined,\n };\n return [rootNode];\n }\n\n // For primitives, just show the value directly\n const node = buildTreeNodes(data, [], undefined, false, maxDepth);\n return node ? [node] : [];\n }, [data, maxDepth]);\n\n // Compute initial expanded state (root is always expanded by default unless depth is 0)\n const initialExpandedState = useMemo(() => {\n if (defaultExpandedDepth === 0) return {};\n if (defaultExpandedDepth === Infinity) {\n return getTreeExpandedState(treeData, \"*\");\n }\n // Add 1 to depth to account for root node\n const ids = getExpandedIds(treeData, defaultExpandedDepth + 1);\n return getTreeExpandedState(treeData, ids);\n }, [treeData, defaultExpandedDepth]);\n\n const tree = useTree({ initialExpandedState });\n\n // Render value based on type\n const renderValue = useCallback(\n (val: any, key: string | undefined, path: string[]): ReactNode => {\n const custom = formatValue?.(key, val, path);\n if (custom !== undefined) {\n return (\n <Text\n component=\"span\"\n size={size}\n style={STYLES.string}\n className=\"alepha-json-viewer-value\"\n title={String(val)}\n >\n {custom}\n </Text>\n );\n }\n\n const type = getValueType(val);\n switch (type) {\n case \"string\": {\n return (\n <Text\n style={STYLES.string}\n component=\"span\"\n size={size}\n className=\"alepha-json-viewer-value\"\n title={val}\n >\n \"{val}\"\n </Text>\n );\n }\n case \"number\":\n return (\n <Text component=\"span\" size={size} style={STYLES.number}>\n {val}\n </Text>\n );\n case \"boolean\":\n return (\n <Text component=\"span\" size={size} style={STYLES.boolean}>\n {String(val)}\n </Text>\n );\n case \"null\":\n case \"undefined\":\n return (\n <Text component=\"span\" size={size} style={STYLES.null}>\n {type}\n </Text>\n );\n default:\n return (\n <Text component=\"span\" size={size}>\n {String(val)}\n </Text>\n );\n }\n },\n [formatValue, showQuotes, size],\n );\n\n // Render tree node\n const renderNode = useCallback(\n ({\n node,\n expanded,\n hasChildren,\n elementProps,\n }: {\n node: JsonTreeNode;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: any;\n }): ReactNode => {\n return (\n <RowNode\n node={node}\n expanded={expanded}\n hasChildren={hasChildren}\n elementProps={elementProps}\n size={size}\n config={config}\n showQuotes={showQuotes}\n showCopyButton={showCopyButton}\n renderValue={renderValue}\n />\n );\n },\n [config, renderValue, showCopyButton, showQuotes, size],\n );\n\n if (treeData.length === 0) {\n return (\n <Text size={size} style={STYLES.null}>\n {data === null ? \"null\" : data === undefined ? \"undefined\" : \"{}\"}\n </Text>\n );\n }\n\n return (\n <Tree\n data={treeData}\n tree={tree}\n levelOffset={config.levelOffset}\n expandOnClick\n renderNode={renderNode as any}\n styles={{ root: STYLES.root }}\n />\n );\n};\n\nexport default JsonViewer;\n","import { type BaseDialogOptions, DialogService, ui } from \"@alepha/ui\";\nimport { Flex } from \"@mantine/core\";\nimport { JsonViewer } from \"../components/JsonViewer.tsx\";\n\ndeclare module \"@alepha/ui\" {\n interface DialogService {\n /**\n * Opens a JSON viewer dialog.\n *\n * @param data - The JSON data to display.\n * @param options - Additional dialog options.\n */\n json(data?: any, options?: BaseDialogOptions): void;\n }\n}\n\nDialogService.prototype.json = function (\n data?: any,\n options?: BaseDialogOptions,\n) {\n this.open({\n size: \"lg\",\n title: options?.title || \"Json Viewer\",\n ...options,\n content: (\n <Flex bdrs={\"md\"} w={\"100%\"} flex={1} p={\"sm\"} bg={ui.colors.surface}>\n <JsonViewer size={\"xs\"} data={data} />\n </Flex>\n ),\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/json/components/JsonViewer.tsx","../../src/json/extensions/DialogService.tsx"],"sourcesContent":["import {\n ActionIcon,\n Flex,\n getTreeExpandedState,\n type MantineSize,\n Text,\n Tree,\n useTree,\n} from \"@mantine/core\";\nimport {\n IconCheck,\n IconChevronDown,\n IconChevronRight,\n IconCopy,\n} from \"@tabler/icons-react\";\nimport {\n type CSSProperties,\n type ReactNode,\n useCallback,\n useMemo,\n useState,\n} from \"react\";\n\n// =============================================================================\n// TYPES\n// =============================================================================\n\ninterface JsonViewerProps {\n data: any;\n /**\n * Depth level to expand by default (0 = collapsed, Infinity = all expanded)\n */\n defaultExpandedDepth?: number;\n /**\n * Maximum nesting depth to render\n */\n maxDepth?: number;\n /**\n * Size variant\n */\n size?: MantineSize;\n /**\n * Whether to show quotes around keys and strings\n */\n showQuotes?: boolean;\n /**\n * Show copy button on row hover\n */\n showCopyButton?: boolean;\n /**\n * Custom value formatter. Return formatted string or undefined to use default.\n */\n formatValue?: (\n key: string | undefined,\n value: any,\n path: string[],\n ) => string | number | undefined;\n}\n\ninterface JsonTreeNode {\n value: string;\n label: string;\n children?: JsonTreeNode[];\n // Custom properties\n nodeValue: any;\n nodeKey: string | undefined;\n path: string[];\n isArrayItem: boolean;\n isRoot?: boolean;\n}\n\n// =============================================================================\n// CONSTANTS\n// =============================================================================\n\nconst SIZE_CONFIG: Record<MantineSize, { icon: number; levelOffset: number }> =\n {\n xs: { icon: 14, levelOffset: 16 },\n sm: { icon: 16, levelOffset: 20 },\n md: { icon: 18, levelOffset: 24 },\n lg: { icon: 20, levelOffset: 28 },\n xl: { icon: 22, levelOffset: 32 },\n };\n\nconst STYLES = {\n root: {\n fontFamily: \"var(--mantine-font-family-monospace)\",\n } satisfies CSSProperties,\n chevron: {\n flexShrink: 0,\n color: \"var(--mantine-color-dimmed)\",\n } satisfies CSSProperties,\n key: {\n color: \"var(--mantine-color-cyan-text)\",\n fontWeight: 500,\n } satisfies CSSProperties,\n colon: {\n color: \"var(--mantine-color-dimmed)\",\n } satisfies CSSProperties,\n string: {\n color: \"var(--mantine-color-teal-text)\",\n } satisfies CSSProperties,\n number: {\n color: \"var(--mantine-color-blue-text)\",\n } satisfies CSSProperties,\n boolean: {\n color: \"var(--mantine-color-violet-text)\",\n } satisfies CSSProperties,\n null: {\n color: \"var(--mantine-color-dimmed)\",\n fontStyle: \"italic\",\n } satisfies CSSProperties,\n preview: {\n color: \"var(--mantine-color-dimmed)\",\n } satisfies CSSProperties,\n};\n\n// =============================================================================\n// HELPERS\n// =============================================================================\n\nconst getValueType = (val: any): string => {\n if (val === null) return \"null\";\n if (val === undefined) return \"undefined\";\n if (Array.isArray(val)) return \"array\";\n return typeof val;\n};\n\n// Convert JSON to tree data structure\nfunction buildTreeNodes(\n data: any,\n path: string[] = [],\n key?: string,\n isArrayItem = false,\n maxDepth = 10,\n): JsonTreeNode | null {\n const currentPath = key !== undefined ? [...path, key] : path;\n const nodeId = currentPath.length > 0 ? currentPath.join(\".\") : \"root\";\n\n if (currentPath.length > maxDepth) {\n return {\n value: nodeId,\n label: key ?? \"\",\n nodeValue: data,\n nodeKey: key,\n path: currentPath,\n isArrayItem,\n };\n }\n\n const type = getValueType(data);\n\n if (type === \"object\" || type === \"array\") {\n const entries =\n type === \"array\"\n ? (data as any[]).map((v, i) => [String(i), v] as const)\n : Object.entries(data);\n\n const children = entries\n .map(([k, v]) =>\n buildTreeNodes(v, currentPath, k, type === \"array\", maxDepth),\n )\n .filter((n): n is JsonTreeNode => n !== null);\n\n return {\n value: nodeId,\n label: key ?? \"\",\n nodeValue: data,\n nodeKey: key,\n path: currentPath,\n isArrayItem,\n children: children.length > 0 ? children : undefined,\n };\n }\n\n return {\n value: nodeId,\n label: key ?? \"\",\n nodeValue: data,\n nodeKey: key,\n path: currentPath,\n isArrayItem,\n };\n}\n\n// Get all expandable node IDs up to a certain depth\nfunction getExpandedIds(\n nodes: JsonTreeNode[],\n targetDepth: number,\n currentDepth = 0,\n): string[] {\n if (currentDepth >= targetDepth) return [];\n const ids: string[] = [];\n for (const node of nodes) {\n if (node.children) {\n ids.push(node.value);\n ids.push(...getExpandedIds(node.children, targetDepth, currentDepth + 1));\n }\n }\n return ids;\n}\n\n// =============================================================================\n// COPY BUTTON COMPONENT\n// =============================================================================\n\nconst CopyButton = ({\n value,\n iconSize,\n}: {\n value: string;\n iconSize: number;\n}) => {\n const [copied, setCopied] = useState(false);\n\n const handleCopy = useCallback(\n (e: React.MouseEvent) => {\n e.stopPropagation();\n navigator.clipboard.writeText(value);\n setCopied(true);\n setTimeout(() => setCopied(false), 1500);\n },\n [value],\n );\n\n return (\n <ActionIcon\n size={iconSize + 4}\n variant=\"transparent\"\n c={copied ? \"green\" : \"dimmed\"}\n onClick={handleCopy}\n className=\"alepha-json-viewer-copy\"\n >\n {copied ? <IconCheck size={iconSize} /> : <IconCopy size={iconSize} />}\n </ActionIcon>\n );\n};\n\n// =============================================================================\n// ROW NODE COMPONENT\n// =============================================================================\n\ninterface RowNodeProps {\n node: JsonTreeNode;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: any;\n size: MantineSize;\n config: { icon: number; levelOffset: number };\n showQuotes: boolean;\n showCopyButton: boolean;\n renderValue: (val: any, key: string | undefined, path: string[]) => ReactNode;\n}\n\nconst RowNode = ({\n node,\n expanded,\n hasChildren,\n elementProps,\n size,\n config,\n showQuotes,\n showCopyButton,\n renderValue,\n}: RowNodeProps) => {\n const { nodeValue, nodeKey, path, isArrayItem, isRoot } = node;\n const type = getValueType(nodeValue);\n const isExpandable = type === \"object\" || type === \"array\";\n\n const getPreview = () => {\n if (!isExpandable) return null;\n const entries = type === \"array\" ? nodeValue : Object.keys(nodeValue);\n const count = entries.length;\n const label = type === \"array\" ? \"item\" : \"key\";\n\n // For root node or collapsed nodes, show the count\n if (!expanded) {\n return (\n <Text fs={\"italic\"} component=\"span\" size={size} style={STYLES.preview}>\n {count === 0\n ? type === \"array\"\n ? \"[]\"\n : \"{}\"\n : type === \"array\"\n ? `[ ${count} ${count === 1 ? label : `${label}s`} ]`\n : `{ ${count} ${count === 1 ? label : `${label}s`} }`}\n </Text>\n );\n }\n\n return null;\n };\n\n const getCopyValue = () =>\n isExpandable ? JSON.stringify(nodeValue, null, 2) : String(nodeValue ?? \"\");\n\n return (\n <Flex\n gap={6}\n wrap=\"nowrap\"\n {...elementProps}\n className={`alepha-json-viewer-row ${elementProps.className || \"\"}`}\n >\n {hasChildren ? (\n expanded ? (\n <IconChevronDown size={config.icon} style={STYLES.chevron} />\n ) : (\n <IconChevronRight size={config.icon} style={STYLES.chevron} />\n )\n ) : (\n <span style={{ width: config.icon, flexShrink: 0 }} />\n )}\n\n {nodeKey !== undefined && !isArrayItem && (\n <Text component=\"span\" size={size}>\n <span style={STYLES.key}>\n {showQuotes ? `\"${nodeKey}\"` : nodeKey}\n </span>\n <span style={STYLES.colon}>:</span>\n </Text>\n )}\n\n {nodeKey !== undefined && isArrayItem && (\n <Text component=\"span\" size={size}>\n <span style={STYLES.key}>{nodeKey}</span>\n <span style={STYLES.colon}>:</span>\n </Text>\n )}\n\n {hasChildren ? (\n getPreview()\n ) : isExpandable ? (\n type === \"array\" ? (\n <Text component=\"span\" size={size} style={STYLES.preview}>\n []\n </Text>\n ) : (\n <Text component=\"span\" size={size} style={STYLES.preview}>\n {\"{}\"}\n </Text>\n )\n ) : (\n renderValue(nodeValue, nodeKey, path)\n )}\n\n {showCopyButton && (\n <CopyButton value={getCopyValue()} iconSize={config.icon} />\n )}\n </Flex>\n );\n};\n\n// =============================================================================\n// MAIN COMPONENT\n// =============================================================================\n\nexport const JsonViewer = ({\n data,\n defaultExpandedDepth = 2,\n maxDepth = 10,\n size = \"sm\",\n showQuotes = false,\n showCopyButton = true,\n formatValue,\n}: JsonViewerProps) => {\n const config = SIZE_CONFIG[size] || SIZE_CONFIG.sm;\n\n // Build tree data from JSON with root wrapper\n const treeData = useMemo(() => {\n const type = getValueType(data);\n\n // For objects and arrays, create a root node wrapper\n if (type === \"object\" || type === \"array\") {\n const entries =\n type === \"array\"\n ? (data as any[]).map((v, i) => [String(i), v] as const)\n : Object.entries(data);\n const children = entries\n .map(([k, v]) => buildTreeNodes(v, [], k, type === \"array\", maxDepth))\n .filter((n): n is JsonTreeNode => n !== null);\n\n const rootNode: JsonTreeNode = {\n value: \"root\",\n label: \"\",\n nodeValue: data,\n nodeKey: undefined,\n path: [],\n isArrayItem: false,\n isRoot: true,\n children: children.length > 0 ? children : undefined,\n };\n return [rootNode];\n }\n\n // For primitives, just show the value directly\n const node = buildTreeNodes(data, [], undefined, false, maxDepth);\n return node ? [node] : [];\n }, [data, maxDepth]);\n\n // Compute initial expanded state (root is always expanded by default unless depth is 0)\n const initialExpandedState = useMemo(() => {\n if (defaultExpandedDepth === 0) return {};\n if (defaultExpandedDepth === Infinity) {\n return getTreeExpandedState(treeData, \"*\");\n }\n // Add 1 to depth to account for root node\n const ids = getExpandedIds(treeData, defaultExpandedDepth + 1);\n return getTreeExpandedState(treeData, ids);\n }, [treeData, defaultExpandedDepth]);\n\n const tree = useTree({ initialExpandedState });\n\n // Render value based on type\n const renderValue = useCallback(\n (val: any, key: string | undefined, path: string[]): ReactNode => {\n const custom = formatValue?.(key, val, path);\n if (custom !== undefined) {\n return (\n <Text\n component=\"span\"\n size={size}\n style={STYLES.string}\n className=\"alepha-json-viewer-value\"\n title={String(val)}\n >\n {custom}\n </Text>\n );\n }\n\n const type = getValueType(val);\n switch (type) {\n case \"string\": {\n return (\n <Text\n style={STYLES.string}\n component=\"span\"\n size={size}\n className=\"alepha-json-viewer-value\"\n title={val}\n >\n \"{val}\"\n </Text>\n );\n }\n case \"number\":\n return (\n <Text component=\"span\" size={size} style={STYLES.number}>\n {val}\n </Text>\n );\n case \"boolean\":\n return (\n <Text component=\"span\" size={size} style={STYLES.boolean}>\n {String(val)}\n </Text>\n );\n case \"null\":\n case \"undefined\":\n return (\n <Text component=\"span\" size={size} style={STYLES.null}>\n {type}\n </Text>\n );\n default:\n return (\n <Text component=\"span\" size={size}>\n {String(val)}\n </Text>\n );\n }\n },\n [formatValue, showQuotes, size],\n );\n\n // Render tree node\n const renderNode = useCallback(\n ({\n node,\n expanded,\n hasChildren,\n elementProps,\n }: {\n node: JsonTreeNode;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: any;\n }): ReactNode => {\n return (\n <RowNode\n node={node}\n expanded={expanded}\n hasChildren={hasChildren}\n elementProps={elementProps}\n size={size}\n config={config}\n showQuotes={showQuotes}\n showCopyButton={showCopyButton}\n renderValue={renderValue}\n />\n );\n },\n [config, renderValue, showCopyButton, showQuotes, size],\n );\n\n if (treeData.length === 0) {\n return (\n <Text size={size} style={STYLES.null}>\n {data === null ? \"null\" : data === undefined ? \"undefined\" : \"{}\"}\n </Text>\n );\n }\n\n return (\n <Tree\n data={treeData}\n tree={tree}\n levelOffset={config.levelOffset}\n expandOnClick\n renderNode={renderNode as any}\n styles={{ root: STYLES.root }}\n />\n );\n};\n\nexport default JsonViewer;\n","import { type BaseDialogOptions, DialogService, ui } from \"@alepha/ui\";\nimport { Flex } from \"@mantine/core\";\nimport { JsonViewer } from \"../components/JsonViewer.tsx\";\n\ndeclare module \"@alepha/ui\" {\n interface DialogService {\n /**\n * Opens a JSON viewer dialog.\n *\n * @param data - The JSON data to display.\n * @param options - Additional dialog options.\n */\n json(data?: any, options?: BaseDialogOptions): void;\n }\n}\n\nDialogService.prototype.json = function (\n data?: any,\n options?: BaseDialogOptions,\n) {\n this.open({\n size: \"lg\",\n title: options?.title || \"Json Viewer\",\n ...options,\n content: (\n <Flex bdrs={\"md\"} w={\"100%\"} flex={1} p={\"sm\"} bg={ui.colors.surface}>\n <JsonViewer size={\"xs\"} data={data} />\n </Flex>\n ),\n });\n};\n"],"mappings":";;;;;;;AA2EA,MAAM,cACJ;CACE,IAAI;EAAE,MAAM;EAAI,aAAa;EAAI;CACjC,IAAI;EAAE,MAAM;EAAI,aAAa;EAAI;CACjC,IAAI;EAAE,MAAM;EAAI,aAAa;EAAI;CACjC,IAAI;EAAE,MAAM;EAAI,aAAa;EAAI;CACjC,IAAI;EAAE,MAAM;EAAI,aAAa;EAAI;CAClC;AAEH,MAAM,SAAS;CACb,MAAM,EACJ,YAAY,wCACb;CACD,SAAS;EACP,YAAY;EACZ,OAAO;EACR;CACD,KAAK;EACH,OAAO;EACP,YAAY;EACb;CACD,OAAO,EACL,OAAO,+BACR;CACD,QAAQ,EACN,OAAO,kCACR;CACD,QAAQ,EACN,OAAO,kCACR;CACD,SAAS,EACP,OAAO,oCACR;CACD,MAAM;EACJ,OAAO;EACP,WAAW;EACZ;CACD,SAAS,EACP,OAAO,+BACR;CACF;AAMD,MAAM,gBAAgB,QAAqB;AACzC,KAAI,QAAQ,KAAM,QAAO;AACzB,KAAI,QAAQ,OAAW,QAAO;AAC9B,KAAI,MAAM,QAAQ,IAAI,CAAE,QAAO;AAC/B,QAAO,OAAO;;AAIhB,SAAS,eACP,MACA,OAAiB,EAAE,EACnB,KACA,cAAc,OACd,WAAW,IACU;CACrB,MAAM,cAAc,QAAQ,SAAY,CAAC,GAAG,MAAM,IAAI,GAAG;CACzD,MAAM,SAAS,YAAY,SAAS,IAAI,YAAY,KAAK,IAAI,GAAG;AAEhE,KAAI,YAAY,SAAS,SACvB,QAAO;EACL,OAAO;EACP,OAAO,OAAO;EACd,WAAW;EACX,SAAS;EACT,MAAM;EACN;EACD;CAGH,MAAM,OAAO,aAAa,KAAK;AAE/B,KAAI,SAAS,YAAY,SAAS,SAAS;EAMzC,MAAM,YAJJ,SAAS,UACJ,KAAe,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,EAAE,EAAE,CAAU,GACtD,OAAO,QAAQ,KAAK,EAGvB,KAAK,CAAC,GAAG,OACR,eAAe,GAAG,aAAa,GAAG,SAAS,SAAS,SAAS,CAC9D,CACA,QAAQ,MAAyB,MAAM,KAAK;AAE/C,SAAO;GACL,OAAO;GACP,OAAO,OAAO;GACd,WAAW;GACX,SAAS;GACT,MAAM;GACN;GACA,UAAU,SAAS,SAAS,IAAI,WAAW;GAC5C;;AAGH,QAAO;EACL,OAAO;EACP,OAAO,OAAO;EACd,WAAW;EACX,SAAS;EACT,MAAM;EACN;EACD;;AAIH,SAAS,eACP,OACA,aACA,eAAe,GACL;AACV,KAAI,gBAAgB,YAAa,QAAO,EAAE;CAC1C,MAAM,MAAgB,EAAE;AACxB,MAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,UAAU;AACjB,MAAI,KAAK,KAAK,MAAM;AACpB,MAAI,KAAK,GAAG,eAAe,KAAK,UAAU,aAAa,eAAe,EAAE,CAAC;;AAG7E,QAAO;;AAOT,MAAM,cAAc,EAClB,OACA,eAII;CACJ,MAAM,CAAC,QAAQ,aAAa,SAAS,MAAM;CAE3C,MAAM,aAAa,aAChB,MAAwB;AACvB,IAAE,iBAAiB;AACnB,YAAU,UAAU,UAAU,MAAM;AACpC,YAAU,KAAK;AACf,mBAAiB,UAAU,MAAM,EAAE,KAAK;IAE1C,CAAC,MAAM,CACR;AAED,QACE,oBAAC;EACC,MAAM,WAAW;EACjB,SAAQ;EACR,GAAG,SAAS,UAAU;EACtB,SAAS;EACT,WAAU;YAET,SAAS,oBAAC,aAAU,MAAM,WAAY,GAAG,oBAAC,YAAS,MAAM,WAAY;GAC3D;;AAoBjB,MAAM,WAAW,EACf,MACA,UACA,aACA,cACA,MACA,QACA,YACA,gBACA,kBACkB;CAClB,MAAM,EAAE,WAAW,SAAS,MAAM,aAAa,WAAW;CAC1D,MAAM,OAAO,aAAa,UAAU;CACpC,MAAM,eAAe,SAAS,YAAY,SAAS;CAEnD,MAAM,mBAAmB;AACvB,MAAI,CAAC,aAAc,QAAO;EAE1B,MAAM,SADU,SAAS,UAAU,YAAY,OAAO,KAAK,UAAU,EAC/C;EACtB,MAAM,QAAQ,SAAS,UAAU,SAAS;AAG1C,MAAI,CAAC,SACH,QACE,oBAAC;GAAK,IAAI;GAAU,WAAU;GAAa;GAAM,OAAO,OAAO;aAC5D,UAAU,IACP,SAAS,UACP,OACA,OACF,SAAS,UACP,KAAK,MAAM,GAAG,UAAU,IAAI,QAAQ,GAAG,MAAM,GAAG,MAChD,KAAK,MAAM,GAAG,UAAU,IAAI,QAAQ,GAAG,MAAM,GAAG;IACjD;AAIX,SAAO;;CAGT,MAAM,qBACJ,eAAe,KAAK,UAAU,WAAW,MAAM,EAAE,GAAG,OAAO,aAAa,GAAG;AAE7E,QACE,qBAAC;EACC,KAAK;EACL,MAAK;EACL,GAAI;EACJ,WAAW,0BAA0B,aAAa,aAAa;;GAE9D,cACC,WACE,oBAAC;IAAgB,MAAM,OAAO;IAAM,OAAO,OAAO;KAAW,GAE7D,oBAAC;IAAiB,MAAM,OAAO;IAAM,OAAO,OAAO;KAAW,GAGhE,oBAAC,UAAK,OAAO;IAAE,OAAO,OAAO;IAAM,YAAY;IAAG,GAAI;GAGvD,YAAY,UAAa,CAAC,eACzB,qBAAC;IAAK,WAAU;IAAa;eAC3B,oBAAC;KAAK,OAAO,OAAO;eACjB,aAAa,IAAI,QAAQ,KAAK;MAC1B,EACP,oBAAC;KAAK,OAAO,OAAO;eAAO;MAAQ;KAC9B;GAGR,YAAY,UAAa,eACxB,qBAAC;IAAK,WAAU;IAAa;eAC3B,oBAAC;KAAK,OAAO,OAAO;eAAM;MAAe,EACzC,oBAAC;KAAK,OAAO,OAAO;eAAO;MAAQ;KAC9B;GAGR,cACC,YAAY,GACV,eACF,SAAS,UACP,oBAAC;IAAK,WAAU;IAAa;IAAM,OAAO,OAAO;cAAS;KAEnD,GAEP,oBAAC;IAAK,WAAU;IAAa;IAAM,OAAO,OAAO;cAC9C;KACI,GAGT,YAAY,WAAW,SAAS,KAAK;GAGtC,kBACC,oBAAC;IAAW,OAAO,cAAc;IAAE,UAAU,OAAO;KAAQ;;GAEzD;;AAQX,MAAa,cAAc,EACzB,MACA,uBAAuB,GACvB,WAAW,IACX,OAAO,MACP,aAAa,OACb,iBAAiB,MACjB,kBACqB;CACrB,MAAM,SAAS,YAAY,SAAS,YAAY;CAGhD,MAAM,WAAW,cAAc;EAC7B,MAAM,OAAO,aAAa,KAAK;AAG/B,MAAI,SAAS,YAAY,SAAS,SAAS;GAKzC,MAAM,YAHJ,SAAS,UACJ,KAAe,KAAK,GAAG,MAAM,CAAC,OAAO,EAAE,EAAE,EAAE,CAAU,GACtD,OAAO,QAAQ,KAAK,EAEvB,KAAK,CAAC,GAAG,OAAO,eAAe,GAAG,EAAE,EAAE,GAAG,SAAS,SAAS,SAAS,CAAC,CACrE,QAAQ,MAAyB,MAAM,KAAK;AAY/C,UAAO,CAVwB;IAC7B,OAAO;IACP,OAAO;IACP,WAAW;IACX,SAAS;IACT,MAAM,EAAE;IACR,aAAa;IACb,QAAQ;IACR,UAAU,SAAS,SAAS,IAAI,WAAW;IAC5C,CACgB;;EAInB,MAAM,OAAO,eAAe,MAAM,EAAE,EAAE,QAAW,OAAO,SAAS;AACjE,SAAO,OAAO,CAAC,KAAK,GAAG,EAAE;IACxB,CAAC,MAAM,SAAS,CAAC;CAapB,MAAM,OAAO,QAAQ,EAAE,sBAVM,cAAc;AACzC,MAAI,yBAAyB,EAAG,QAAO,EAAE;AACzC,MAAI,yBAAyB,SAC3B,QAAO,qBAAqB,UAAU,IAAI;AAI5C,SAAO,qBAAqB,UADhB,eAAe,UAAU,uBAAuB,EAAE,CACpB;IACzC,CAAC,UAAU,qBAAqB,CAAC,EAES,CAAC;CAG9C,MAAM,cAAc,aACjB,KAAU,KAAyB,SAA8B;EAChE,MAAM,SAAS,cAAc,KAAK,KAAK,KAAK;AAC5C,MAAI,WAAW,OACb,QACE,oBAAC;GACC,WAAU;GACJ;GACN,OAAO,OAAO;GACd,WAAU;GACV,OAAO,OAAO,IAAI;aAEjB;IACI;EAIX,MAAM,OAAO,aAAa,IAAI;AAC9B,UAAQ,MAAR;GACE,KAAK,SACH,QACE,qBAAC;IACC,OAAO,OAAO;IACd,WAAU;IACJ;IACN,WAAU;IACV,OAAO;;KACR;KACG;KAAI;;KACD;GAGX,KAAK,SACH,QACE,oBAAC;IAAK,WAAU;IAAa;IAAM,OAAO,OAAO;cAC9C;KACI;GAEX,KAAK,UACH,QACE,oBAAC;IAAK,WAAU;IAAa;IAAM,OAAO,OAAO;cAC9C,OAAO,IAAI;KACP;GAEX,KAAK;GACL,KAAK,YACH,QACE,oBAAC;IAAK,WAAU;IAAa;IAAM,OAAO,OAAO;cAC9C;KACI;GAEX,QACE,QACE,oBAAC;IAAK,WAAU;IAAa;cAC1B,OAAO,IAAI;KACP;;IAIf;EAAC;EAAa;EAAY;EAAK,CAChC;CAGD,MAAM,aAAa,aAChB,EACC,MACA,UACA,aACA,mBAMe;AACf,SACE,oBAAC;GACO;GACI;GACG;GACC;GACR;GACE;GACI;GACI;GACH;IACb;IAGN;EAAC;EAAQ;EAAa;EAAgB;EAAY;EAAK,CACxD;AAED,KAAI,SAAS,WAAW,EACtB,QACE,oBAAC;EAAW;EAAM,OAAO,OAAO;YAC7B,SAAS,OAAO,SAAS,SAAS,SAAY,cAAc;GACxD;AAIX,QACE,oBAAC;EACC,MAAM;EACA;EACN,aAAa,OAAO;EACpB;EACY;EACZ,QAAQ,EAAE,MAAM,OAAO,MAAM;GAC7B;;;;;ACzfN,cAAc,UAAU,OAAO,SAC7B,MACA,SACA;AACA,MAAK,KAAK;EACR,MAAM;EACN,OAAO,SAAS,SAAS;EACzB,GAAG;EACH,SACE,oBAAC;GAAK,MAAM;GAAM,GAAG;GAAQ,MAAM;GAAG,GAAG;GAAM,IAAI,GAAG,OAAO;aAC3D,oBAAC;IAAW,MAAM;IAAY;KAAQ;IACjC;EAEV,CAAC"}
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "mantine"
8
8
  ],
9
9
  "author": "Nicolas Foures",
10
- "version": "0.16.1",
10
+ "version": "0.17.0",
11
11
  "type": "module",
12
12
  "engines": {
13
13
  "node": ">=22.0.0"
@@ -22,6 +22,7 @@
22
22
  "styles.css"
23
23
  ],
24
24
  "dependencies": {
25
+ "@mantine/charts": "^8.3.14",
25
26
  "@mantine/core": "^8.3.14",
26
27
  "@mantine/dates": "^8.3.14",
27
28
  "@mantine/hooks": "^8.3.14",
@@ -30,11 +31,16 @@
30
31
  "@mantine/nprogress": "^8.3.14",
31
32
  "@mantine/spotlight": "^8.3.14",
32
33
  "@tabler/icons-react": "^3.36.1",
33
- "dayjs": "^1.11.19"
34
+ "dayjs": "^1.11.19",
35
+ "react-is": "^19.2.4",
36
+ "recharts": "^3.7.0"
34
37
  },
35
38
  "devDependencies": {
36
- "@biomejs/biome": "^2.3.14",
37
- "alepha": "0.16.1",
39
+ "@biomejs/biome": "^2.3.15",
40
+ "@testing-library/dom": "^10.4.1",
41
+ "@testing-library/react": "^16.3.2",
42
+ "@types/react-is": "^19.2.0",
43
+ "alepha": "0.17.0",
38
44
  "react": "^19.2.4",
39
45
  "react-dom": "^19.2.4",
40
46
  "typescript": "^5.9.3",
@@ -42,7 +48,7 @@
42
48
  "vitest": "^4.0.18"
43
49
  },
44
50
  "peerDependencies": {
45
- "alepha": "0.16.1",
51
+ "alepha": "0.17.0",
46
52
  "react": "*",
47
53
  "react-dom": "*"
48
54
  },
@@ -1,11 +1,13 @@
1
- import type { AdminShellProps, SidebarNode } from "@alepha/ui";
1
+ import type { DashboardShellProps, SidebarNode } from "@alepha/ui";
2
2
  import { AuthRouter } from "@alepha/ui/auth";
3
3
  import {
4
4
  IconBell,
5
+ IconClock,
5
6
  IconDevices,
6
7
  IconFile,
7
8
  IconHistory,
8
9
  IconKey,
10
+ IconListDetails,
9
11
  IconPlus,
10
12
  IconSettings,
11
13
  IconTerminal2,
@@ -18,7 +20,7 @@ import type { FileController } from "alepha/api/files";
18
20
  import type { AdminJobController } from "alepha/api/jobs";
19
21
  import type { AdminApiKeyController } from "alepha/api/keys";
20
22
  import type { AdminNotificationController } from "alepha/api/notifications";
21
- import type { AdminConfigController } from "alepha/api/parameters";
23
+ import type { AdminParameterController } from "alepha/api/parameters";
22
24
  import type {
23
25
  AdminSessionController,
24
26
  AdminUserController,
@@ -35,12 +37,12 @@ export class AdminRouter {
35
37
  protected readonly sessionCtrl = $client<AdminSessionController>();
36
38
  protected readonly notificationCtrl = $client<AdminNotificationController>();
37
39
  protected readonly fileCtrl = $client<FileController>();
38
- protected readonly configCtrl = $client<AdminConfigController>();
40
+ protected readonly paramCtrl = $client<AdminParameterController>();
39
41
  protected readonly auditCtrl = $client<AdminAuditController>();
40
42
  protected readonly jobCtrl = $client<AdminJobController>();
41
43
  protected readonly apiKeyCtrl = $client<AdminApiKeyController>();
42
44
 
43
- public configFn?: (adminRouter: AdminRouter) => AdminShellProps = () => {
45
+ public configFn?: (adminRouter: AdminRouter) => DashboardShellProps = () => {
44
46
  return {
45
47
  sidebarResizable: true,
46
48
  sidebarProps: {
@@ -51,10 +53,9 @@ export class AdminRouter {
51
53
 
52
54
  public getDefaultSidebarItems(): SidebarNode[] {
53
55
  return [
54
- // Identity & Access
55
56
  {
57
+ type: "section",
56
58
  label: "Identity",
57
- icon: IconUsers,
58
59
  children: [
59
60
  {
60
61
  ...this.router.node(this.adminUsers.name),
@@ -70,11 +71,9 @@ export class AdminRouter {
70
71
  },
71
72
  ],
72
73
  },
73
-
74
- // Content & Storage
75
74
  {
76
- label: "Content",
77
- icon: IconFile,
75
+ type: "section",
76
+ label: "System",
78
77
  children: [
79
78
  {
80
79
  ...this.router.node(this.adminFiles.name),
@@ -84,17 +83,18 @@ export class AdminRouter {
84
83
  ...this.router.node(this.adminNotifications.name),
85
84
  can: () => this.notificationCtrl.findNotifications.can(),
86
85
  },
87
- ],
88
- },
89
-
90
- // System
91
- {
92
- label: "System",
93
- icon: IconSettings,
94
- children: [
95
86
  {
96
- ...this.router.node(this.adminJobs.name),
97
- can: () => this.jobCtrl.getJobs.can(),
87
+ ...this.router.node(this.adminJobDashboard.name),
88
+ href: undefined,
89
+ can: () => this.jobCtrl.getRegistry.can(),
90
+ children: [
91
+ {
92
+ ...this.router.node(this.adminJobDashboard.name),
93
+ label: "Dashboard",
94
+ },
95
+ { ...this.router.node(this.adminJobRegistry.name) },
96
+ { ...this.router.node(this.adminJobExecutions.name) },
97
+ ],
98
98
  },
99
99
  {
100
100
  ...this.router.node(this.adminAudits.name),
@@ -102,17 +102,15 @@ export class AdminRouter {
102
102
  },
103
103
  {
104
104
  ...this.router.node(this.adminParameters.name),
105
- can: () => this.configCtrl.getConfigTree.can(),
105
+ can: () => this.paramCtrl.getParameterTree.can(),
106
106
  },
107
107
  ],
108
108
  },
109
-
110
- // Bottom
111
109
  { type: "toggle", position: "bottom" },
112
110
  ];
113
111
  }
114
112
 
115
- protected adminShellProps(): AdminShellProps {
113
+ protected adminShellProps(): DashboardShellProps {
116
114
  if (this.configFn) {
117
115
  return this.configFn(this);
118
116
  }
@@ -276,23 +274,47 @@ export class AdminRouter {
276
274
  parent: this.adminLayout,
277
275
  path: "/parameters",
278
276
  label: "Parameters",
279
- description: "View and manage application configuration parameters.",
277
+ description: "View and manage application parameters.",
280
278
  lazy: () => import("./components/parameters/AdminParameters.tsx"),
281
- can: () => this.configCtrl.getConfigTree.can(),
279
+ can: () => this.paramCtrl.getParameterTree.can(),
280
+ loader: async () => {
281
+ const treeData = await this.paramCtrl.getParameterTree({});
282
+ return { treeData };
283
+ },
282
284
  });
283
285
 
284
286
  // ─────────────────────────────────────────────────────────────────────────────
285
287
  // Jobs
286
288
  // ─────────────────────────────────────────────────────────────────────────────
287
289
 
288
- public readonly adminJobs = $page({
290
+ public readonly adminJobDashboard = $page({
289
291
  icon: IconTerminal2,
290
292
  parent: this.adminLayout,
291
293
  path: "/jobs",
292
294
  label: "Jobs",
293
295
  description: "Monitor and manage background jobs and scheduled tasks.",
294
- lazy: () => import("./components/jobs/AdminJobs.tsx"),
295
- can: () => this.jobCtrl.getJobs.can(),
296
+ lazy: () => import("./components/jobs/AdminJobDashboard.tsx"),
297
+ can: () => this.jobCtrl.getRegistry.can(),
298
+ });
299
+
300
+ public readonly adminJobRegistry = $page({
301
+ icon: IconListDetails,
302
+ parent: this.adminLayout,
303
+ path: "/jobs/registry",
304
+ label: "Registry",
305
+ description: "View all registered job definitions.",
306
+ lazy: () => import("./components/jobs/AdminJobRegistry.tsx"),
307
+ can: () => this.jobCtrl.getRegistry.can(),
308
+ });
309
+
310
+ public readonly adminJobExecutions = $page({
311
+ icon: IconClock,
312
+ parent: this.adminLayout,
313
+ path: "/jobs/executions",
314
+ label: "Executions",
315
+ description: "Browse and filter job execution history.",
316
+ lazy: () => import("./components/jobs/AdminJobExecutions.tsx"),
317
+ can: () => this.jobCtrl.findExecutions.can(),
296
318
  });
297
319
 
298
320
  // ─────────────────────────────────────────────────────────────────────────────
@@ -1,21 +1,22 @@
1
1
  import {
2
2
  ActionButton,
3
- AdminShell,
4
- type AdminShellProps,
5
3
  AlephaMantineProvider,
6
- OmnibarButton,
4
+ DashboardShell,
5
+ type DashboardShellProps,
7
6
  } from "@alepha/ui";
8
7
  import { UserButton } from "@alepha/ui/auth";
8
+ import { Flex } from "@mantine/core";
9
9
  import { IconArrowLeft } from "@tabler/icons-react";
10
10
 
11
11
  export interface AdminLayoutProps {
12
- adminShellProps?: AdminShellProps;
12
+ adminShellProps?: DashboardShellProps;
13
13
  }
14
14
 
15
15
  const AdminLayout = (props: AdminLayoutProps) => {
16
16
  return (
17
17
  <AlephaMantineProvider>
18
- <AdminShell
18
+ <DashboardShell
19
+ footer={<Flex h={24} />}
19
20
  appBarProps={{
20
21
  items: [
21
22
  {
@@ -28,10 +29,6 @@ const AdminLayout = (props: AdminLayoutProps) => {
28
29
  ),
29
30
  position: "left",
30
31
  },
31
- {
32
- element: <OmnibarButton />,
33
- position: "center",
34
- },
35
32
  {
36
33
  element: <UserButton />,
37
34
  position: "right",
@@ -1,5 +1,5 @@
1
1
  import { DataTable, Flex, Text } from "@alepha/ui";
2
- import { Badge, Group, Stack, Tooltip } from "@mantine/core";
2
+ import { Badge, Tooltip } from "@mantine/core";
3
3
  import {
4
4
  IconAlertTriangle,
5
5
  IconCheck,
@@ -155,20 +155,20 @@ const AdminAudits = (props: AdminAuditsProps) => {
155
155
  item.userId ? (
156
156
  <Tooltip
157
157
  label={
158
- <Stack gap={2}>
158
+ <Flex direction="column" gap={2}>
159
159
  <Text size="xs">{item.userEmail || "No email"}</Text>
160
160
  <Text size="xs" c="dimmed">
161
161
  {item.userRealm || "default"}
162
162
  </Text>
163
- </Stack>
163
+ </Flex>
164
164
  }
165
165
  >
166
- <Group gap={4}>
166
+ <Flex gap={4}>
167
167
  <IconUser size={12} />
168
168
  <Text size="xs" lineClamp={1} maw={100}>
169
169
  {item.userEmail?.split("@")[0] || item.userId.slice(0, 8)}
170
170
  </Text>
171
- </Group>
171
+ </Flex>
172
172
  </Tooltip>
173
173
  ) : (
174
174
  <Text size="xs" c="dimmed">