@alepha/ui 0.16.0 → 0.16.2
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-CoTOTfgU.js} +4 -3
- package/dist/admin/{AdminApiKeys-GMORg-1l.js.map → AdminApiKeys-CoTOTfgU.js.map} +1 -1
- package/dist/admin/{AdminAudits-pkWrjq1Z.js → AdminAudits-BmsxFbDa.js} +4 -3
- package/dist/admin/{AdminAudits-pkWrjq1Z.js.map → AdminAudits-BmsxFbDa.js.map} +1 -1
- package/dist/admin/{AdminFiles-WeQbsCsl.js → AdminFiles-BBB8knca.js} +4 -3
- package/dist/admin/{AdminFiles-WeQbsCsl.js.map → AdminFiles-BBB8knca.js.map} +1 -1
- package/dist/admin/{AdminJobs-B-q9iGO3.js → AdminJobs-C604joTz.js} +4 -3
- package/dist/admin/{AdminJobs-B-q9iGO3.js.map → AdminJobs-C604joTz.js.map} +1 -1
- package/dist/admin/{AdminLayout-D8yZ-8lG.js → AdminLayout-CsjvpeD1.js} +6 -10
- package/dist/admin/AdminLayout-CsjvpeD1.js.map +1 -0
- package/dist/admin/{AdminNotifications-Ds5Un0NJ.js → AdminNotifications-LwR6RKrx.js} +4 -3
- package/dist/admin/{AdminNotifications-Ds5Un0NJ.js.map → AdminNotifications-LwR6RKrx.js.map} +1 -1
- package/dist/admin/AdminParameters-B_83Vie9.js +767 -0
- package/dist/admin/AdminParameters-B_83Vie9.js.map +1 -0
- package/dist/admin/{AdminSessions-DzIOxM3b.js → AdminSessions-CWnPosdd.js} +4 -3
- package/dist/admin/{AdminSessions-DzIOxM3b.js.map → AdminSessions-CWnPosdd.js.map} +1 -1
- package/dist/admin/{AdminUserAudits-CiUPN2BC.js → AdminUserAudits-nHv636E_.js} +4 -3
- package/dist/admin/{AdminUserAudits-CiUPN2BC.js.map → AdminUserAudits-nHv636E_.js.map} +1 -1
- package/dist/admin/{AdminUserCreate-BwQKr4xE.js → AdminUserCreate-CjYD3Kjc.js} +4 -3
- package/dist/admin/{AdminUserCreate-BwQKr4xE.js.map → AdminUserCreate-CjYD3Kjc.js.map} +1 -1
- package/dist/admin/{AdminUserDetails-uqtC5aJ1.js → AdminUserDetails-Ccq-LsZ0.js} +4 -3
- package/dist/admin/{AdminUserDetails-uqtC5aJ1.js.map → AdminUserDetails-Ccq-LsZ0.js.map} +1 -1
- package/dist/admin/{AdminUserLayout-CiPay35T.js → AdminUserLayout-7s41DiF_.js} +6 -7
- package/dist/admin/AdminUserLayout-7s41DiF_.js.map +1 -0
- package/dist/admin/{AdminUserSessions-DAE8Nf1F.js → AdminUserSessions-Ds3ODq_d.js} +4 -3
- package/dist/admin/{AdminUserSessions-DAE8Nf1F.js.map → AdminUserSessions-Ds3ODq_d.js.map} +1 -1
- package/dist/admin/{AdminUserSettings-EbahaV2a.js → AdminUserSettings-CGh4gROo.js} +4 -3
- package/dist/admin/{AdminUserSettings-EbahaV2a.js.map → AdminUserSettings-CGh4gROo.js.map} +1 -1
- package/dist/admin/{AdminUsers-Dcjh0KNW.js → AdminUsers-CvPiBzQK.js} +4 -3
- package/dist/admin/{AdminUsers-Dcjh0KNW.js.map → AdminUsers-CvPiBzQK.js.map} +1 -1
- package/dist/admin/index.d.ts +22 -10
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +47 -48
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/rolldown-runtime-CjeV3_4I.js +18 -0
- package/dist/auth/{AuthLayout-mFOWbiSP.js → AuthLayout-CdJcrPs4.js} +2 -4
- package/dist/auth/AuthLayout-CdJcrPs4.js.map +1 -0
- 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-DS_OqA0G.js} +7 -6
- package/dist/auth/Login-DS_OqA0G.js.map +1 -0
- package/dist/auth/{Profile-Bxj8Nwom.js → Profile-Di7N7HZL.js} +2 -3
- package/dist/auth/{Profile-Bxj8Nwom.js.map → Profile-Di7N7HZL.js.map} +1 -1
- package/dist/auth/{Register-Ce675Crg.js → Register-BRR2_gux.js} +7 -6
- package/dist/auth/Register-BRR2_gux.js.map +1 -0
- package/dist/auth/{ResetPassword-DWdt7c40.js → ResetPassword-oQu72lod.js} +4 -3
- package/dist/auth/{ResetPassword-DWdt7c40.js.map → ResetPassword-oQu72lod.js.map} +1 -1
- package/dist/auth/{VerifyEmail-CI4JwByV.js → VerifyEmail-DC6HPZjd.js} +4 -3
- package/dist/auth/{VerifyEmail-CI4JwByV.js.map → VerifyEmail-DC6HPZjd.js.map} +1 -1
- package/dist/auth/index.d.ts +14 -14
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +13 -17
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/rolldown-runtime-CjeV3_4I.js +18 -0
- package/dist/core/index.d.ts +147 -68
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +349 -287
- package/dist/core/index.js.map +1 -1
- package/dist/demo/{DemoDataTable-CguplbR7.js → DemoDataTable-DCsJq8v5.js} +4 -5
- package/dist/demo/DemoDataTable-DCsJq8v5.js.map +1 -0
- package/dist/demo/{DemoHome-Cce2bWmg.js → DemoHome-DpRrPlBC.js} +4 -3
- package/dist/demo/{DemoHome-Cce2bWmg.js.map → DemoHome-DpRrPlBC.js.map} +1 -1
- package/dist/demo/{DemoJsonViewer-Dgdk3Txb.js → DemoJsonViewer-zeucGKHV.js} +6 -5
- package/dist/demo/DemoJsonViewer-zeucGKHV.js.map +1 -0
- package/dist/demo/{DemoLayout-B20TEuhV.js → DemoLayout-PhgbAAiQ.js} +6 -5
- package/dist/demo/DemoLayout-PhgbAAiQ.js.map +1 -0
- package/dist/demo/{DemoLogin-CvCG2WVh.js → DemoLogin-DSzP0Lkv.js} +8 -10
- package/dist/demo/DemoLogin-DSzP0Lkv.js.map +1 -0
- package/dist/demo/{DemoRegister-CmeHbOAs.js → DemoRegister-DavFBsCz.js} +8 -10
- package/dist/demo/DemoRegister-DavFBsCz.js.map +1 -0
- package/dist/demo/{DemoResetPassword-CKO5iA_6.js → DemoResetPassword-BS2rIAQK.js} +5 -7
- package/dist/demo/DemoResetPassword-BS2rIAQK.js.map +1 -0
- package/dist/demo/{DemoSidebar-MVmQKfMt.js → DemoSidebar-zNkUmHRl.js} +4 -5
- package/dist/demo/DemoSidebar-zNkUmHRl.js.map +1 -0
- package/dist/demo/{DemoTypeForm-w-qtfRlC.js → DemoTypeForm-B9q7oT0b.js} +4 -5
- package/dist/demo/DemoTypeForm-B9q7oT0b.js.map +1 -0
- package/dist/demo/{DemoVerifyEmail-C8FFJT5A.js → DemoVerifyEmail-Bi4SdWz0.js} +5 -7
- package/dist/demo/DemoVerifyEmail-Bi4SdWz0.js.map +1 -0
- package/dist/{auth/IconGoogle-DpSlPZ1u.js → demo/IconGoogle-CTeZyrek.js} +2 -4
- package/dist/demo/{IconGoogle-CbBF8Hqq.js.map → IconGoogle-CTeZyrek.js.map} +1 -1
- package/dist/demo/{Showcase-CQrMWars.js → Showcase-C9btr_SJ.js} +3 -5
- package/dist/demo/Showcase-C9btr_SJ.js.map +1 -0
- package/dist/demo/index.d.ts +2 -2
- package/dist/demo/index.d.ts.map +1 -1
- package/dist/demo/index.js +15 -15
- package/dist/demo/rolldown-runtime-CjeV3_4I.js +18 -0
- package/package.json +5 -3
- package/src/admin/AdminRouter.ts +15 -24
- package/src/admin/components/AdminLayout.tsx +6 -10
- package/src/admin/components/parameters/AdminParameters.tsx +154 -76
- package/src/admin/components/parameters/ParameterDetails.tsx +153 -93
- package/src/admin/components/parameters/ParameterEmptyState.tsx +27 -0
- package/src/admin/components/parameters/ParameterHistory.tsx +15 -20
- package/src/admin/components/parameters/ParameterTree.tsx +280 -104
- package/src/admin/components/parameters/types.ts +3 -3
- package/src/admin/primitives/$uiAdmin.ts +2 -2
- package/src/auth/AuthRouter.ts +1 -4
- package/src/auth/components/AuthLayout.tsx +0 -1
- package/src/core/components/buttons/ActionButton.tsx +4 -15
- package/src/core/components/buttons/DarkModeButton.tsx +8 -4
- package/src/core/components/buttons/ToggleSidebarButton.tsx +3 -5
- package/src/core/components/form/Control.tsx +10 -32
- package/src/core/components/form/ControlArray.tsx +200 -89
- package/src/core/components/form/TypeForm.browser.spec.tsx +727 -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/Sidebar.tsx +58 -18
- 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 +7 -6
- package/src/core/utils/string.ts +28 -4
- package/src/demo/components/DemoLayout.tsx +6 -2
- package/dist/admin/AdminApiKeys-DsmGnHNh.js +0 -3
- package/dist/admin/AdminAudits-8SM96viT.js +0 -3
- package/dist/admin/AdminFiles-B56ocq4H.js +0 -3
- package/dist/admin/AdminJobs-CED1syCn.js +0 -3
- package/dist/admin/AdminLayout-D8yZ-8lG.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/AdminUserAudits-Cj79gENT.js +0 -3
- package/dist/admin/AdminUserCreate-Cq-mUmBs.js +0 -3
- package/dist/admin/AdminUserDetails-DRjVAPFd.js +0 -3
- package/dist/admin/AdminUserLayout-CGzmHHby.js +0 -3
- package/dist/admin/AdminUserLayout-CiPay35T.js.map +0 -1
- package/dist/admin/AdminUserSessions-DcdzuNZ9.js +0 -3
- package/dist/admin/AdminUserSettings-D7V6-ceX.js +0 -3
- package/dist/admin/AdminUsers-D9nyzGqQ.js +0 -3
- package/dist/auth/AuthLayout-mFOWbiSP.js.map +0 -1
- package/dist/auth/Login-BBqTosqZ.js.map +0 -1
- package/dist/auth/Login-CoU63mMR.js +0 -4
- 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/VerifyEmail-DAfqVm5s.js +0 -3
- package/dist/demo/DemoDataTable-CguplbR7.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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ActionButton, Flex, Text } from "@alepha/ui";
|
|
2
2
|
import {
|
|
3
3
|
Badge,
|
|
4
|
-
|
|
4
|
+
Box,
|
|
5
5
|
Group,
|
|
6
6
|
Loader,
|
|
7
7
|
ScrollArea,
|
|
@@ -21,7 +21,6 @@ export interface ParameterHistoryProps {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const ParameterHistory = ({
|
|
24
|
-
selectedConfig,
|
|
25
24
|
history,
|
|
26
25
|
loading,
|
|
27
26
|
onRollback,
|
|
@@ -29,16 +28,6 @@ const ParameterHistory = ({
|
|
|
29
28
|
const { l } = useI18n();
|
|
30
29
|
|
|
31
30
|
const renderContent = () => {
|
|
32
|
-
if (!selectedConfig) {
|
|
33
|
-
return (
|
|
34
|
-
<Flex flex={1} justify="center" align="center">
|
|
35
|
-
<Text c="dimmed" size="xs">
|
|
36
|
-
Select a parameter
|
|
37
|
-
</Text>
|
|
38
|
-
</Flex>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
31
|
if (loading) {
|
|
43
32
|
return (
|
|
44
33
|
<Flex flex={1} justify="center" align="center">
|
|
@@ -51,7 +40,7 @@ const ParameterHistory = ({
|
|
|
51
40
|
return (
|
|
52
41
|
<Flex flex={1} justify="center" align="center">
|
|
53
42
|
<Text c="dimmed" size="xs">
|
|
54
|
-
|
|
43
|
+
Empty
|
|
55
44
|
</Text>
|
|
56
45
|
</Flex>
|
|
57
46
|
);
|
|
@@ -124,22 +113,28 @@ const ParameterHistory = ({
|
|
|
124
113
|
};
|
|
125
114
|
|
|
126
115
|
return (
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
w={300}
|
|
116
|
+
<Box
|
|
117
|
+
w={160}
|
|
130
118
|
h="100%"
|
|
131
|
-
|
|
119
|
+
p="xs"
|
|
120
|
+
style={{
|
|
121
|
+
flexShrink: 0,
|
|
122
|
+
overflow: "hidden",
|
|
123
|
+
display: "flex",
|
|
124
|
+
flexDirection: "column",
|
|
125
|
+
borderLeft: "1px solid var(--mantine-color-default-border)",
|
|
126
|
+
}}
|
|
132
127
|
>
|
|
133
|
-
<Stack gap="xs" h="100%">
|
|
128
|
+
<Stack gap="xs" h="100%" style={{ minHeight: 0 }}>
|
|
134
129
|
<Group gap="xs">
|
|
135
130
|
<IconHistory size={16} color="var(--mantine-color-dimmed)" />
|
|
136
131
|
<Text size="sm" fw={500}>
|
|
137
|
-
|
|
132
|
+
History
|
|
138
133
|
</Text>
|
|
139
134
|
</Group>
|
|
140
135
|
{renderContent()}
|
|
141
136
|
</Stack>
|
|
142
|
-
</
|
|
137
|
+
</Box>
|
|
143
138
|
);
|
|
144
139
|
};
|
|
145
140
|
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { ActionButton, Text } from "@alepha/ui";
|
|
2
2
|
import {
|
|
3
3
|
Box,
|
|
4
|
-
|
|
4
|
+
Collapse,
|
|
5
5
|
Group,
|
|
6
6
|
ScrollArea,
|
|
7
7
|
Stack,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
type TreeNodeData,
|
|
11
|
-
useTree,
|
|
8
|
+
TextInput,
|
|
9
|
+
UnstyledButton,
|
|
12
10
|
} from "@mantine/core";
|
|
13
11
|
import {
|
|
14
12
|
IconChevronDown,
|
|
@@ -16,130 +14,308 @@ import {
|
|
|
16
14
|
IconFolder,
|
|
17
15
|
IconFolderOpen,
|
|
18
16
|
IconRefresh,
|
|
17
|
+
IconSearch,
|
|
19
18
|
IconSettings,
|
|
20
19
|
} from "@tabler/icons-react";
|
|
21
|
-
import type {
|
|
22
|
-
import {
|
|
20
|
+
import type { ParameterTreeNode } from "alepha/api/parameters";
|
|
21
|
+
import { memo, useCallback, useMemo, useState } from "react";
|
|
23
22
|
|
|
24
23
|
export interface ParameterTreeProps {
|
|
25
|
-
treeData:
|
|
24
|
+
treeData: ParameterTreeNode[];
|
|
26
25
|
selectedConfig: string | null;
|
|
27
26
|
onSelect: (name: string) => void;
|
|
28
27
|
onRefresh: () => void;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Filters tree nodes by search query.
|
|
32
|
+
*/
|
|
33
|
+
const filterTree = (
|
|
34
|
+
nodes: ParameterTreeNode[],
|
|
35
|
+
query: string,
|
|
36
|
+
): ParameterTreeNode[] => {
|
|
37
|
+
if (!query.trim()) return nodes;
|
|
38
|
+
|
|
39
|
+
const lowerQuery = query.toLowerCase();
|
|
40
|
+
|
|
41
|
+
return nodes
|
|
42
|
+
.map((node) => {
|
|
43
|
+
const filteredChildren = filterTree(node.children, query);
|
|
44
|
+
const nameMatches = node.name.toLowerCase().includes(lowerQuery);
|
|
45
|
+
const pathMatches = node.path.toLowerCase().includes(lowerQuery);
|
|
46
|
+
|
|
47
|
+
if (nameMatches || pathMatches || filteredChildren.length > 0) {
|
|
48
|
+
return {
|
|
49
|
+
...node,
|
|
50
|
+
children: filteredChildren,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return null;
|
|
55
|
+
})
|
|
56
|
+
.filter((node): node is ParameterTreeNode => node !== null);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
interface TreeNodeProps {
|
|
60
|
+
node: ParameterTreeNode;
|
|
61
|
+
level: number;
|
|
62
|
+
selectedConfig: string | null;
|
|
63
|
+
onSelect: (name: string) => void;
|
|
64
|
+
expandedNodes: Set<string>;
|
|
65
|
+
onToggle: (path: string) => void;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Memoized tree node to prevent unnecessary re-renders.
|
|
70
|
+
*/
|
|
71
|
+
const TreeNode = memo(
|
|
72
|
+
({
|
|
53
73
|
node,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
level,
|
|
75
|
+
selectedConfig,
|
|
76
|
+
onSelect,
|
|
77
|
+
expandedNodes,
|
|
78
|
+
onToggle,
|
|
79
|
+
}: TreeNodeProps) => {
|
|
80
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
81
|
+
const hasChildren = node.children.length > 0;
|
|
82
|
+
const isExpanded = expandedNodes.has(node.path);
|
|
83
|
+
const isSelected = selectedConfig === node.path;
|
|
63
84
|
const isLeaf = !hasChildren;
|
|
64
|
-
|
|
85
|
+
|
|
86
|
+
const handleClick = useCallback(() => {
|
|
87
|
+
if (hasChildren) {
|
|
88
|
+
onToggle(node.path);
|
|
89
|
+
} else {
|
|
90
|
+
onSelect(node.path);
|
|
91
|
+
}
|
|
92
|
+
}, [hasChildren, node.path, onToggle, onSelect]);
|
|
93
|
+
|
|
94
|
+
const handleMouseEnter = useCallback(() => setIsHovered(true), []);
|
|
95
|
+
const handleMouseLeave = useCallback(() => setIsHovered(false), []);
|
|
65
96
|
|
|
66
97
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
+
<Box>
|
|
99
|
+
<UnstyledButton
|
|
100
|
+
onClick={handleClick}
|
|
101
|
+
onMouseEnter={handleMouseEnter}
|
|
102
|
+
onMouseLeave={handleMouseLeave}
|
|
103
|
+
w="100%"
|
|
104
|
+
style={{ display: "block" }}
|
|
105
|
+
>
|
|
106
|
+
<Group
|
|
107
|
+
gap={6}
|
|
108
|
+
wrap="nowrap"
|
|
109
|
+
p="4px 8px"
|
|
110
|
+
pl={8 + level * 16}
|
|
111
|
+
style={{
|
|
112
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
113
|
+
backgroundColor:
|
|
114
|
+
isSelected || isHovered
|
|
115
|
+
? "var(--mantine-color-default-hover)"
|
|
116
|
+
: undefined,
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
{hasChildren ? (
|
|
120
|
+
<>
|
|
121
|
+
<Box
|
|
122
|
+
style={{
|
|
123
|
+
display: "flex",
|
|
124
|
+
alignItems: "center",
|
|
125
|
+
justifyContent: "center",
|
|
126
|
+
width: 16,
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
{isExpanded ? (
|
|
130
|
+
<IconChevronDown
|
|
131
|
+
size={14}
|
|
132
|
+
color="var(--mantine-color-dimmed)"
|
|
133
|
+
/>
|
|
134
|
+
) : (
|
|
135
|
+
<IconChevronRight
|
|
136
|
+
size={14}
|
|
137
|
+
color="var(--mantine-color-dimmed)"
|
|
138
|
+
/>
|
|
139
|
+
)}
|
|
140
|
+
</Box>
|
|
141
|
+
{isExpanded ? (
|
|
142
|
+
<IconFolderOpen
|
|
143
|
+
size={16}
|
|
144
|
+
color="var(--mantine-color-dimmed)"
|
|
145
|
+
style={{ flexShrink: 0 }}
|
|
146
|
+
/>
|
|
147
|
+
) : (
|
|
148
|
+
<IconFolder
|
|
149
|
+
size={16}
|
|
150
|
+
color="var(--mantine-color-dimmed)"
|
|
151
|
+
style={{ flexShrink: 0 }}
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
</>
|
|
98
155
|
) : (
|
|
99
|
-
|
|
156
|
+
<>
|
|
157
|
+
<Box w={16} />
|
|
158
|
+
<IconSettings
|
|
159
|
+
size={16}
|
|
160
|
+
color={
|
|
161
|
+
isSelected
|
|
162
|
+
? "var(--mantine-color-blue-6)"
|
|
163
|
+
: "var(--mantine-color-dimmed)"
|
|
164
|
+
}
|
|
165
|
+
style={{ flexShrink: 0 }}
|
|
166
|
+
/>
|
|
167
|
+
</>
|
|
100
168
|
)}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
169
|
+
<Text
|
|
170
|
+
size="sm"
|
|
171
|
+
fw={isSelected ? 600 : 400}
|
|
172
|
+
c={isSelected ? undefined : isLeaf ? undefined : "dimmed"}
|
|
173
|
+
style={{
|
|
174
|
+
whiteSpace: "nowrap",
|
|
175
|
+
overflow: "hidden",
|
|
176
|
+
textOverflow: "ellipsis",
|
|
177
|
+
}}
|
|
178
|
+
>
|
|
179
|
+
{node.name}
|
|
180
|
+
</Text>
|
|
181
|
+
</Group>
|
|
182
|
+
</UnstyledButton>
|
|
183
|
+
|
|
184
|
+
{hasChildren && (
|
|
185
|
+
<Collapse in={isExpanded}>
|
|
186
|
+
{node.children.map((child: ParameterTreeNode) => (
|
|
187
|
+
<TreeNode
|
|
188
|
+
key={child.path}
|
|
189
|
+
node={child}
|
|
190
|
+
level={level + 1}
|
|
191
|
+
selectedConfig={selectedConfig}
|
|
192
|
+
onSelect={onSelect}
|
|
193
|
+
expandedNodes={expandedNodes}
|
|
194
|
+
onToggle={onToggle}
|
|
195
|
+
/>
|
|
196
|
+
))}
|
|
197
|
+
</Collapse>
|
|
107
198
|
)}
|
|
108
|
-
|
|
109
|
-
{node.label}
|
|
110
|
-
</Text>
|
|
111
|
-
</Group>
|
|
199
|
+
</Box>
|
|
112
200
|
);
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
TreeNode.displayName = "TreeNode";
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Collects all folder paths to expand by default.
|
|
208
|
+
*/
|
|
209
|
+
const collectAllFolderPaths = (nodes: ParameterTreeNode[]): Set<string> => {
|
|
210
|
+
const paths = new Set<string>();
|
|
211
|
+
|
|
212
|
+
const traverse = (nodeList: ParameterTreeNode[]) => {
|
|
213
|
+
for (const node of nodeList) {
|
|
214
|
+
if (node.children.length > 0) {
|
|
215
|
+
paths.add(node.path);
|
|
216
|
+
traverse(node.children);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
113
219
|
};
|
|
114
220
|
|
|
221
|
+
traverse(nodes);
|
|
222
|
+
return paths;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const ParameterTree = ({
|
|
226
|
+
treeData,
|
|
227
|
+
selectedConfig,
|
|
228
|
+
onSelect,
|
|
229
|
+
onRefresh,
|
|
230
|
+
}: ParameterTreeProps) => {
|
|
231
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
232
|
+
const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() =>
|
|
233
|
+
collectAllFolderPaths(treeData),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
// Filter tree by search query
|
|
237
|
+
const filteredTreeData = useMemo(
|
|
238
|
+
() => filterTree(treeData, searchQuery),
|
|
239
|
+
[treeData, searchQuery],
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
const handleToggle = useCallback((path: string) => {
|
|
243
|
+
setExpandedNodes((prev) => {
|
|
244
|
+
const next = new Set(prev);
|
|
245
|
+
if (next.has(path)) {
|
|
246
|
+
next.delete(path);
|
|
247
|
+
} else {
|
|
248
|
+
next.add(path);
|
|
249
|
+
}
|
|
250
|
+
return next;
|
|
251
|
+
});
|
|
252
|
+
}, []);
|
|
253
|
+
|
|
254
|
+
const handleSearchChange = useCallback(
|
|
255
|
+
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
256
|
+
setSearchQuery(e.currentTarget.value);
|
|
257
|
+
},
|
|
258
|
+
[],
|
|
259
|
+
);
|
|
260
|
+
|
|
115
261
|
return (
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
262
|
+
<Box
|
|
263
|
+
w={280}
|
|
264
|
+
h="100%"
|
|
265
|
+
p="sm"
|
|
266
|
+
style={{
|
|
267
|
+
flexShrink: 0,
|
|
268
|
+
display: "flex",
|
|
269
|
+
flexDirection: "column",
|
|
270
|
+
borderRight: "1px solid var(--mantine-color-default-border)",
|
|
271
|
+
}}
|
|
272
|
+
>
|
|
273
|
+
<Stack gap="sm" h="100%" style={{ minHeight: 0 }}>
|
|
274
|
+
<Group justify="space-between" gap="xs">
|
|
275
|
+
<Text size="sm" fw={600}>
|
|
120
276
|
Parameters
|
|
121
277
|
</Text>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
</Tooltip>
|
|
278
|
+
<ActionButton
|
|
279
|
+
variant="subtle"
|
|
280
|
+
size="compact-xs"
|
|
281
|
+
onClick={onRefresh}
|
|
282
|
+
tooltip="Refresh"
|
|
283
|
+
>
|
|
284
|
+
<IconRefresh size={14} />
|
|
285
|
+
</ActionButton>
|
|
131
286
|
</Group>
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
287
|
+
|
|
288
|
+
<TextInput
|
|
289
|
+
placeholder="Search..."
|
|
290
|
+
size="xs"
|
|
291
|
+
leftSection={<IconSearch size={14} />}
|
|
292
|
+
value={searchQuery}
|
|
293
|
+
onChange={handleSearchChange}
|
|
294
|
+
/>
|
|
295
|
+
|
|
296
|
+
<ScrollArea flex={1} offsetScrollbars style={{ minHeight: 0 }}>
|
|
297
|
+
{filteredTreeData.length === 0 ? (
|
|
298
|
+
<Text size="xs" c="dimmed" ta="center" py="md">
|
|
299
|
+
{searchQuery ? "No matching parameters" : "No parameters"}
|
|
300
|
+
</Text>
|
|
301
|
+
) : (
|
|
302
|
+
<Stack gap={0} style={{ gap: 1 }}>
|
|
303
|
+
{filteredTreeData.map((node) => (
|
|
304
|
+
<TreeNode
|
|
305
|
+
key={node.path}
|
|
306
|
+
node={node}
|
|
307
|
+
level={0}
|
|
308
|
+
selectedConfig={selectedConfig}
|
|
309
|
+
onSelect={onSelect}
|
|
310
|
+
expandedNodes={expandedNodes}
|
|
311
|
+
onToggle={handleToggle}
|
|
312
|
+
/>
|
|
313
|
+
))}
|
|
314
|
+
</Stack>
|
|
315
|
+
)}
|
|
140
316
|
</ScrollArea>
|
|
141
317
|
</Stack>
|
|
142
|
-
</
|
|
318
|
+
</Box>
|
|
143
319
|
);
|
|
144
320
|
};
|
|
145
321
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { Parameter } from "alepha/api/parameters";
|
|
2
2
|
|
|
3
|
-
export interface
|
|
3
|
+
export interface ParameterValue {
|
|
4
4
|
current?: Parameter;
|
|
5
5
|
next?: Parameter;
|
|
6
6
|
/**
|
|
7
|
-
* Default value from the registered $
|
|
7
|
+
* Default value from the registered $parameter primitive.
|
|
8
8
|
*/
|
|
9
9
|
defaultValue?: unknown;
|
|
10
10
|
/**
|
|
@@ -12,7 +12,7 @@ export interface ConfigValue {
|
|
|
12
12
|
*/
|
|
13
13
|
currentValue?: unknown;
|
|
14
14
|
/**
|
|
15
|
-
* TypeBox/JSON schema for the
|
|
15
|
+
* TypeBox/JSON schema for the parameter (as JSON from API).
|
|
16
16
|
*/
|
|
17
17
|
schema?: Record<string, unknown>;
|
|
18
18
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DashboardShellProps } from "@alepha/ui";
|
|
2
2
|
import { $context } from "alepha";
|
|
3
3
|
import { AdminRouter } from "../AdminRouter.ts";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Register Admin UI components and get the AdminRouter instance.
|
|
7
7
|
*/
|
|
8
|
-
export const $uiAdmin = (optsFn?: (a: AdminRouter) =>
|
|
8
|
+
export const $uiAdmin = (optsFn?: (a: AdminRouter) => DashboardShellProps) => {
|
|
9
9
|
const { alepha } = $context();
|
|
10
10
|
const adminRouter = alepha.inject(AdminRouter);
|
|
11
11
|
|
package/src/auth/AuthRouter.ts
CHANGED
|
@@ -27,6 +27,7 @@ export class AuthRouter {
|
|
|
27
27
|
protected readonly auth = $inject(ReactAuth);
|
|
28
28
|
|
|
29
29
|
authLayout = $page({
|
|
30
|
+
label: "Auth",
|
|
30
31
|
path: "/auth",
|
|
31
32
|
lazy: () => import("./components/AuthLayout.tsx"),
|
|
32
33
|
children: () => [
|
|
@@ -46,7 +47,6 @@ export class AuthRouter {
|
|
|
46
47
|
schema: {
|
|
47
48
|
query: realmQuerySchema,
|
|
48
49
|
},
|
|
49
|
-
can: () => !this.auth.user,
|
|
50
50
|
lazy: () => import("./components/Login.tsx"),
|
|
51
51
|
loader: async ({ query, user }) => {
|
|
52
52
|
if (user) {
|
|
@@ -66,7 +66,6 @@ export class AuthRouter {
|
|
|
66
66
|
schema: {
|
|
67
67
|
query: realmQuerySchema,
|
|
68
68
|
},
|
|
69
|
-
can: () => !this.auth.user,
|
|
70
69
|
lazy: () => import("./components/Register.tsx"),
|
|
71
70
|
loader: async ({ query, user }) => {
|
|
72
71
|
if (user) {
|
|
@@ -86,7 +85,6 @@ export class AuthRouter {
|
|
|
86
85
|
schema: {
|
|
87
86
|
query: realmQuerySchema,
|
|
88
87
|
},
|
|
89
|
-
can: () => !this.auth.user,
|
|
90
88
|
lazy: () => import("./components/ResetPassword.tsx"),
|
|
91
89
|
loader: async ({ query, user }) => {
|
|
92
90
|
if (user) {
|
|
@@ -116,7 +114,6 @@ export class AuthRouter {
|
|
|
116
114
|
icon: IconLogout2,
|
|
117
115
|
label: "Sign Out",
|
|
118
116
|
description: "Sign out of your account",
|
|
119
|
-
can: () => !!this.auth.user,
|
|
120
117
|
path: "/logout",
|
|
121
118
|
component: () => null,
|
|
122
119
|
loader: () => {
|
|
@@ -243,17 +243,14 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
243
243
|
const props = { ..._props };
|
|
244
244
|
const { tooltip, menu, icon, ...restProps } = props;
|
|
245
245
|
|
|
246
|
-
if (props.variant === "subtle") {
|
|
247
|
-
restProps.c ??= "var(--mantine-color-text)";
|
|
246
|
+
if (props.variant === "subtle" || props.variant === "outline") {
|
|
248
247
|
restProps.color ??= "gray";
|
|
249
248
|
}
|
|
250
249
|
|
|
251
250
|
if (props.intent) {
|
|
252
251
|
if (props.intent === "none") {
|
|
253
|
-
restProps.c ??= "var(--mantine-color-text)";
|
|
254
252
|
restProps.color ??= "gray";
|
|
255
253
|
} else if (props.intent === "primary") {
|
|
256
|
-
restProps.c ??= "white";
|
|
257
254
|
restProps.color ??= theme.primaryColor;
|
|
258
255
|
} else if (props.intent === "success") {
|
|
259
256
|
restProps.c ??= "white";
|
|
@@ -262,7 +259,6 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
262
259
|
restProps.c ??= "white";
|
|
263
260
|
restProps.color ??= "red";
|
|
264
261
|
} else if (props.intent === "warning") {
|
|
265
|
-
restProps.c ??= "var(--mantine-color-text)";
|
|
266
262
|
restProps.color ??= "yellow";
|
|
267
263
|
} else if (props.intent === "info") {
|
|
268
264
|
restProps.c ??= "white";
|
|
@@ -271,18 +267,11 @@ const ActionButton = (_props: ActionProps) => {
|
|
|
271
267
|
}
|
|
272
268
|
|
|
273
269
|
if (props.icon) {
|
|
270
|
+
const sizes = ui.sizes.icon as Record<string, number>;
|
|
274
271
|
const icon = isComponentType(props.icon) ? (
|
|
275
|
-
<props.icon size={
|
|
272
|
+
<props.icon size={sizes[props.size || "md"]} />
|
|
276
273
|
) : (
|
|
277
|
-
<
|
|
278
|
-
w={24} // TODO: make size configurable
|
|
279
|
-
variant={"transparent"}
|
|
280
|
-
size={"sm"}
|
|
281
|
-
c={"var(--mantine-color-text)"}
|
|
282
|
-
{...props.themeIconProps}
|
|
283
|
-
>
|
|
284
|
-
{props.icon as ReactNode}
|
|
285
|
-
</ThemeIcon>
|
|
274
|
+
<span>{props.icon as ReactNode}</span>
|
|
286
275
|
);
|
|
287
276
|
|
|
288
277
|
if (!props.children) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMantineColorScheme } from "@mantine/core";
|
|
2
2
|
import { IconMoon, IconSun } from "@tabler/icons-react";
|
|
3
|
+
import { ui } from "../../constants/ui.ts";
|
|
3
4
|
import ActionButton, { type ActionProps } from "./ActionButton.tsx";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -19,17 +20,20 @@ const DarkModeButton = (props: Partial<ActionProps>) => {
|
|
|
19
20
|
setColorScheme(current === "dark" ? "light" : "dark");
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
const size = props.size ?? "md";
|
|
24
|
+
const iconSize =
|
|
25
|
+
(ui.sizes.icon as Record<string, number>)[size] ?? ui.sizes.icon.md;
|
|
26
|
+
|
|
22
27
|
return (
|
|
23
28
|
<ActionButton
|
|
24
29
|
onClick={toggleColorScheme}
|
|
25
30
|
variant={props.variant ?? "subtle"}
|
|
26
|
-
size={
|
|
31
|
+
size={size}
|
|
27
32
|
aria-label="Toggle color scheme"
|
|
28
|
-
px={"xs"}
|
|
29
33
|
icon={
|
|
30
34
|
<>
|
|
31
|
-
<IconSun className="alepha-light-hidden" />
|
|
32
|
-
<IconMoon className="alepha-dark-hidden" />
|
|
35
|
+
<IconSun size={iconSize} className="alepha-light-hidden" />
|
|
36
|
+
<IconMoon size={iconSize} className="alepha-dark-hidden" />
|
|
33
37
|
</>
|
|
34
38
|
}
|
|
35
39
|
{...props}
|
|
@@ -14,11 +14,9 @@ const ToggleSidebarButton = (props: Props) => {
|
|
|
14
14
|
return (
|
|
15
15
|
<ActionButton
|
|
16
16
|
icon={
|
|
17
|
-
sidebar.collapsed
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<IconLayoutSidebarLeftCollapse />
|
|
21
|
-
)
|
|
17
|
+
sidebar.collapsed
|
|
18
|
+
? IconLayoutSidebarRightCollapse
|
|
19
|
+
: IconLayoutSidebarLeftCollapse
|
|
22
20
|
}
|
|
23
21
|
visibleFrom={"sm"}
|
|
24
22
|
variant={"subtle"}
|