@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.
- package/dist/admin/{AdminApiKeys-GMORg-1l.js → AdminApiKeys-CF_qOO3u.js} +20 -19
- package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +1 -0
- package/dist/admin/{AdminAudits-pkWrjq1Z.js → AdminAudits-BQno3hZG.js} +7 -7
- package/dist/admin/AdminAudits-BQno3hZG.js.map +1 -0
- package/dist/admin/{AdminFiles-WeQbsCsl.js → AdminFiles-kvuUaASF.js} +3 -4
- package/dist/admin/{AdminFiles-WeQbsCsl.js.map → AdminFiles-kvuUaASF.js.map} +1 -1
- package/dist/admin/AdminJobDashboard-CrPxp0W1.js +485 -0
- package/dist/admin/AdminJobDashboard-CrPxp0W1.js.map +1 -0
- package/dist/admin/AdminJobExecutions-D-b4Zt7W.js +678 -0
- package/dist/admin/AdminJobExecutions-D-b4Zt7W.js.map +1 -0
- package/dist/admin/AdminJobRegistry-CNX5cpDx.js +301 -0
- package/dist/admin/AdminJobRegistry-CNX5cpDx.js.map +1 -0
- package/dist/admin/{AdminLayout-BqZiXx4H.js → AdminLayout-e-ZP5nWw.js} +6 -9
- package/dist/admin/AdminLayout-e-ZP5nWw.js.map +1 -0
- package/dist/admin/{AdminNotifications-Ds5Un0NJ.js → AdminNotifications-DeHJFf6W.js} +3 -4
- package/dist/admin/{AdminNotifications-Ds5Un0NJ.js.map → AdminNotifications-DeHJFf6W.js.map} +1 -1
- package/dist/admin/AdminParameters-iQE8o7a7.js +774 -0
- package/dist/admin/AdminParameters-iQE8o7a7.js.map +1 -0
- package/dist/admin/{AdminSessions-DzIOxM3b.js → AdminSessions-oKJCbd7w.js} +5 -6
- package/dist/admin/AdminSessions-oKJCbd7w.js.map +1 -0
- package/dist/admin/{AdminUserAudits-CiUPN2BC.js → AdminUserAudits-BNCEle_E.js} +6 -7
- package/dist/admin/AdminUserAudits-BNCEle_E.js.map +1 -0
- package/dist/admin/{AdminUserCreate-BwQKr4xE.js → AdminUserCreate-CgqeFwCt.js} +6 -6
- package/dist/admin/AdminUserCreate-CgqeFwCt.js.map +1 -0
- package/dist/admin/{AdminUserDetails-uqtC5aJ1.js → AdminUserDetails-DDe1A1GP.js} +30 -28
- package/dist/admin/AdminUserDetails-DDe1A1GP.js.map +1 -0
- package/dist/admin/{AdminUserLayout-CiPay35T.js → AdminUserLayout-HAlobhWf.js} +20 -19
- package/dist/admin/AdminUserLayout-HAlobhWf.js.map +1 -0
- package/dist/admin/{AdminUserSessions-DAE8Nf1F.js → AdminUserSessions-Bq1LnVLf.js} +5 -6
- package/dist/admin/AdminUserSessions-Bq1LnVLf.js.map +1 -0
- package/dist/admin/{AdminUserSettings-EbahaV2a.js → AdminUserSettings-BRsBZoxV.js} +10 -9
- package/dist/admin/AdminUserSettings-BRsBZoxV.js.map +1 -0
- package/dist/admin/{AdminUsers-Dcjh0KNW.js → AdminUsers-D71kIOSn.js} +6 -7
- package/dist/admin/AdminUsers-D71kIOSn.js.map +1 -0
- package/dist/admin/index.d.ts +21 -85
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +66 -88
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/{AuthLayout-Dj5K4SIN.js → AuthLayout-CdJcrPs4.js} +2 -3
- package/dist/auth/{AuthLayout-Dj5K4SIN.js.map → AuthLayout-CdJcrPs4.js.map} +1 -1
- package/dist/{demo/IconGoogle-CbBF8Hqq.js → auth/IconGoogle-Bm18QD2q.js} +2 -4
- package/dist/auth/{IconGoogle-DpSlPZ1u.js.map → IconGoogle-Bm18QD2q.js.map} +1 -1
- package/dist/auth/{Login-BBqTosqZ.js → Login-BS_FYTy0.js} +19 -13
- package/dist/auth/Login-BS_FYTy0.js.map +1 -0
- package/dist/auth/{Profile-Bxj8Nwom.js → Profile-CjDsW378.js} +17 -12
- package/dist/auth/Profile-CjDsW378.js.map +1 -0
- package/dist/auth/{Register-Ce675Crg.js → Register-C5eqzAaD.js} +27 -17
- package/dist/auth/Register-C5eqzAaD.js.map +1 -0
- package/dist/auth/{ResetPassword-DWdt7c40.js → ResetPassword-XifinVao.js} +17 -10
- package/dist/auth/ResetPassword-XifinVao.js.map +1 -0
- package/dist/auth/{VerifyEmail-CI4JwByV.js → VerifyEmail-DTgbeJOO.js} +9 -6
- package/dist/auth/VerifyEmail-DTgbeJOO.js.map +1 -0
- package/dist/auth/index.d.ts +18 -14
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +19 -18
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/rolldown-runtime-CjeV3_4I.js +18 -0
- package/dist/core/index.d.ts +182 -92
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +789 -476
- package/dist/core/index.js.map +1 -1
- package/dist/demo/DemoDataTable-lnBKWBf8.js +362 -0
- package/dist/demo/DemoDataTable-lnBKWBf8.js.map +1 -0
- package/dist/demo/{DemoHome-Cce2bWmg.js → DemoHome-CUMZsYaH.js} +6 -6
- package/dist/demo/DemoHome-CUMZsYaH.js.map +1 -0
- package/dist/demo/{DemoJsonViewer-Dgdk3Txb.js → DemoJsonViewer-_uokbGaW.js} +18 -19
- package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +1 -0
- package/dist/demo/{DemoLayout-B20TEuhV.js → DemoLayout-DHVoacE6.js} +4 -5
- package/dist/demo/DemoLayout-DHVoacE6.js.map +1 -0
- package/dist/demo/{DemoLogin-CvCG2WVh.js → DemoLogin-DjJ9314c.js} +27 -24
- package/dist/demo/DemoLogin-DjJ9314c.js.map +1 -0
- package/dist/demo/{DemoRegister-CmeHbOAs.js → DemoRegister-DzkJ5M83.js} +39 -32
- package/dist/demo/DemoRegister-DzkJ5M83.js.map +1 -0
- package/dist/demo/{DemoResetPassword-CKO5iA_6.js → DemoResetPassword-DWh4_BpQ.js} +30 -26
- package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +1 -0
- package/dist/demo/{DemoSidebar-MVmQKfMt.js → DemoSidebar-C1csnGhX.js} +4 -5
- package/dist/demo/DemoSidebar-C1csnGhX.js.map +1 -0
- package/dist/demo/{DemoTypeForm-w-qtfRlC.js → DemoTypeForm-CWz6fJrJ.js} +4 -5
- package/dist/demo/DemoTypeForm-CWz6fJrJ.js.map +1 -0
- package/dist/demo/{DemoVerifyEmail-C8FFJT5A.js → DemoVerifyEmail-DbU_tCj8.js} +16 -16
- package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +1 -0
- package/dist/{auth/IconGoogle-DpSlPZ1u.js → demo/IconGoogle-Ch1m3Uzl.js} +2 -4
- package/dist/demo/{IconGoogle-CbBF8Hqq.js.map → IconGoogle-Ch1m3Uzl.js.map} +1 -1
- package/dist/demo/{Showcase-CQrMWars.js → Showcase-BzoXNlCn.js} +11 -13
- package/dist/demo/Showcase-BzoXNlCn.js.map +1 -0
- package/dist/demo/index.d.ts +3 -70
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +11 -15
- package/dist/demo/index.js.map +1 -1
- package/dist/json/index.js +2 -2
- package/dist/json/index.js.map +1 -1
- package/package.json +11 -5
- package/src/admin/AdminRouter.ts +51 -29
- package/src/admin/components/AdminLayout.tsx +6 -9
- package/src/admin/components/audits/AdminAudits.tsx +5 -5
- package/src/admin/components/jobs/AdminJobDashboard.tsx +455 -0
- package/src/admin/components/jobs/AdminJobExecutions.tsx +693 -0
- package/src/admin/components/jobs/AdminJobRegistry.tsx +325 -0
- package/src/admin/components/keys/AdminApiKeys.tsx +28 -31
- package/src/admin/components/parameters/AdminParameters.tsx +156 -78
- package/src/admin/components/parameters/ParameterDetails.tsx +173 -108
- package/src/admin/components/parameters/ParameterEmptyState.tsx +27 -0
- package/src/admin/components/parameters/ParameterHistory.tsx +22 -35
- package/src/admin/components/parameters/ParameterTree.tsx +283 -109
- package/src/admin/components/parameters/types.ts +3 -3
- package/src/admin/components/sessions/AdminSessions.tsx +3 -3
- package/src/admin/components/shared/AdminResourceHeader.tsx +20 -16
- package/src/admin/components/users/AdminUserAudits.tsx +5 -5
- package/src/admin/components/users/AdminUserCreate.tsx +3 -3
- package/src/admin/components/users/AdminUserDetails.tsx +51 -53
- package/src/admin/components/users/AdminUserLayout.tsx +7 -7
- package/src/admin/components/users/AdminUserSessions.tsx +3 -3
- package/src/admin/components/users/AdminUserSettings.tsx +9 -9
- package/src/admin/components/users/AdminUsers.tsx +5 -5
- package/src/admin/components/verifications/AdminVerifications.tsx +3 -3
- package/src/admin/index.ts +0 -24
- package/src/admin/primitives/$uiAdmin.ts +2 -2
- package/src/auth/AuthRouter.ts +1 -0
- package/src/auth/components/Login.tsx +13 -13
- package/src/auth/components/Profile.tsx +17 -26
- package/src/auth/components/Register.tsx +21 -31
- package/src/auth/components/ResetPassword.tsx +13 -22
- package/src/auth/components/VerifyEmail.tsx +5 -5
- package/src/auth/components/buttons/UserButton.tsx +14 -4
- package/src/core/components/buttons/ActionButton.tsx +13 -17
- package/src/core/components/buttons/DarkModeButton.tsx +8 -4
- package/src/core/components/buttons/ToggleSidebarButton.tsx +3 -5
- package/src/core/components/data/ErrorViewer.tsx +15 -15
- package/src/core/components/dialogs/AlertDialog.tsx +3 -3
- package/src/core/components/dialogs/ConfirmDialog.tsx +3 -3
- package/src/core/components/dialogs/PromptDialog.tsx +3 -3
- package/src/core/components/form/Control.tsx +19 -32
- package/src/core/components/form/ControlArray.tsx +206 -96
- package/src/core/components/form/ControlObject.tsx +3 -3
- package/src/core/components/form/ControlQueryBuilder.tsx +20 -22
- package/src/core/components/form/ControlSelect.tsx +4 -0
- package/src/core/components/form/TypeForm.browser.spec.tsx +727 -0
- package/src/core/components/form/TypeForm.tsx +7 -0
- package/src/core/components/layout/AlephaMantineProvider.tsx +1 -0
- package/src/core/components/layout/Breadcrumb.tsx +91 -0
- package/src/core/components/layout/{AdminShell.tsx → DashboardShell.tsx} +77 -32
- package/src/core/components/layout/Omnibar.tsx +2 -1
- package/src/core/components/layout/Sidebar.tsx +63 -19
- package/src/core/components/table/ColumnPicker.tsx +47 -31
- package/src/core/components/table/DataTable.tsx +277 -201
- package/src/core/components/table/DataTableFilters.tsx +8 -0
- package/src/core/components/table/DataTableToolbar.tsx +98 -5
- package/src/core/components/table/FilterPicker.tsx +28 -26
- package/src/core/components/table/types.ts +52 -37
- package/src/core/components/table/useTableSelection.ts +83 -0
- package/src/core/constants/ui.ts +1 -1
- package/src/core/helpers/renderIcon.tsx +5 -2
- package/src/core/index.ts +9 -5
- package/src/core/styles.css +8 -7
- package/src/core/utils/parseInput.ts +1 -0
- package/src/core/utils/string.ts +28 -4
- package/src/demo/components/DemoHome.tsx +5 -5
- package/src/demo/components/DemoLayout.tsx +6 -2
- package/src/demo/components/core/DemoDataTable.tsx +209 -5
- package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
- package/src/demo/components/shared/MacWindow.tsx +7 -7
- package/src/demo/components/shared/Showcase.tsx +3 -3
- package/src/demo/index.ts +0 -11
- package/src/json/components/JsonViewer.tsx +3 -3
- package/dist/admin/AdminApiKeys-DsmGnHNh.js +0 -3
- package/dist/admin/AdminApiKeys-GMORg-1l.js.map +0 -1
- package/dist/admin/AdminAudits-8SM96viT.js +0 -3
- package/dist/admin/AdminAudits-pkWrjq1Z.js.map +0 -1
- package/dist/admin/AdminFiles-B56ocq4H.js +0 -3
- package/dist/admin/AdminJobs-B-q9iGO3.js +0 -697
- package/dist/admin/AdminJobs-B-q9iGO3.js.map +0 -1
- package/dist/admin/AdminJobs-CED1syCn.js +0 -3
- package/dist/admin/AdminLayout-BqZiXx4H.js.map +0 -1
- package/dist/admin/AdminNotifications-B0B1rdc4.js +0 -3
- package/dist/admin/AdminParameters-BU3lATdJ.js +0 -3
- package/dist/admin/AdminParameters-CfDUpc78.js +0 -575
- package/dist/admin/AdminParameters-CfDUpc78.js.map +0 -1
- package/dist/admin/AdminSessions-BDGK2MS6.js +0 -3
- package/dist/admin/AdminSessions-DzIOxM3b.js.map +0 -1
- package/dist/admin/AdminUserAudits-CiUPN2BC.js.map +0 -1
- package/dist/admin/AdminUserAudits-Cj79gENT.js +0 -3
- package/dist/admin/AdminUserCreate-BwQKr4xE.js.map +0 -1
- package/dist/admin/AdminUserCreate-Cq-mUmBs.js +0 -3
- package/dist/admin/AdminUserDetails-DRjVAPFd.js +0 -3
- package/dist/admin/AdminUserDetails-uqtC5aJ1.js.map +0 -1
- package/dist/admin/AdminUserLayout-CGzmHHby.js +0 -3
- package/dist/admin/AdminUserLayout-CiPay35T.js.map +0 -1
- package/dist/admin/AdminUserSessions-DAE8Nf1F.js.map +0 -1
- package/dist/admin/AdminUserSessions-DcdzuNZ9.js +0 -3
- package/dist/admin/AdminUserSettings-D7V6-ceX.js +0 -3
- package/dist/admin/AdminUserSettings-EbahaV2a.js.map +0 -1
- package/dist/admin/AdminUsers-D9nyzGqQ.js +0 -3
- package/dist/admin/AdminUsers-Dcjh0KNW.js.map +0 -1
- package/dist/auth/Login-BBqTosqZ.js.map +0 -1
- package/dist/auth/Login-CoU63mMR.js +0 -4
- package/dist/auth/Profile-Bxj8Nwom.js.map +0 -1
- package/dist/auth/Register-BV_oa_AK.js +0 -4
- package/dist/auth/Register-Ce675Crg.js.map +0 -1
- package/dist/auth/ResetPassword-D5wC8GAA.js +0 -3
- package/dist/auth/ResetPassword-DWdt7c40.js.map +0 -1
- package/dist/auth/VerifyEmail-CI4JwByV.js.map +0 -1
- package/dist/auth/VerifyEmail-DAfqVm5s.js +0 -3
- package/dist/demo/DemoDataTable-CguplbR7.js +0 -150
- package/dist/demo/DemoDataTable-CguplbR7.js.map +0 -1
- package/dist/demo/DemoHome-Cce2bWmg.js.map +0 -1
- package/dist/demo/DemoHome-DC9qkMNe.js +0 -3
- package/dist/demo/DemoJsonViewer-DIssGVlJ.js +0 -4
- package/dist/demo/DemoJsonViewer-Dgdk3Txb.js.map +0 -1
- package/dist/demo/DemoLayout-B20TEuhV.js.map +0 -1
- package/dist/demo/DemoLayout-DSRyf4qJ.js +0 -3
- package/dist/demo/DemoLogin-CvCG2WVh.js.map +0 -1
- package/dist/demo/DemoRegister-CmeHbOAs.js.map +0 -1
- package/dist/demo/DemoResetPassword-CKO5iA_6.js.map +0 -1
- package/dist/demo/DemoSidebar-MVmQKfMt.js.map +0 -1
- package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +0 -1
- package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +0 -1
- package/dist/demo/Showcase-CQrMWars.js.map +0 -1
- package/src/admin/components/jobs/AdminJobs.tsx +0 -772
package/dist/json/index.js.map
CHANGED
|
@@ -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":"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"}
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"mantine"
|
|
8
8
|
],
|
|
9
9
|
"author": "Nicolas Foures",
|
|
10
|
-
"version": "0.
|
|
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.
|
|
37
|
-
"
|
|
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.
|
|
51
|
+
"alepha": "0.17.0",
|
|
46
52
|
"react": "*",
|
|
47
53
|
"react-dom": "*"
|
|
48
54
|
},
|
package/src/admin/AdminRouter.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type {
|
|
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 {
|
|
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
|
|
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) =>
|
|
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
|
-
|
|
77
|
-
|
|
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.
|
|
97
|
-
|
|
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.
|
|
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():
|
|
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
|
|
277
|
+
description: "View and manage application parameters.",
|
|
280
278
|
lazy: () => import("./components/parameters/AdminParameters.tsx"),
|
|
281
|
-
can: () => this.
|
|
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
|
|
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/
|
|
295
|
-
can: () => this.jobCtrl.
|
|
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
|
-
|
|
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?:
|
|
12
|
+
adminShellProps?: DashboardShellProps;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const AdminLayout = (props: AdminLayoutProps) => {
|
|
16
16
|
return (
|
|
17
17
|
<AlephaMantineProvider>
|
|
18
|
-
<
|
|
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,
|
|
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
|
-
<
|
|
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
|
-
</
|
|
163
|
+
</Flex>
|
|
164
164
|
}
|
|
165
165
|
>
|
|
166
|
-
<
|
|
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
|
-
</
|
|
171
|
+
</Flex>
|
|
172
172
|
</Tooltip>
|
|
173
173
|
) : (
|
|
174
174
|
<Text size="xs" c="dimmed">
|