@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
|
@@ -1,61 +1,115 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Text, useToast } from "@alepha/ui";
|
|
2
|
+
import { Card, Flex } from "@mantine/core";
|
|
3
3
|
import { IconSettings } from "@tabler/icons-react";
|
|
4
4
|
import type {
|
|
5
|
-
|
|
6
|
-
ConfigTreeNode,
|
|
5
|
+
AdminParameterController,
|
|
7
6
|
Parameter,
|
|
7
|
+
ParameterTreeNode,
|
|
8
8
|
} from "alepha/api/parameters";
|
|
9
9
|
import { useClient } from "alepha/react";
|
|
10
10
|
import { useCallback, useEffect, useState } from "react";
|
|
11
11
|
import ParameterDetails from "./ParameterDetails.tsx";
|
|
12
|
+
import ParameterEmptyState from "./ParameterEmptyState.tsx";
|
|
12
13
|
import ParameterHistory from "./ParameterHistory.tsx";
|
|
13
14
|
import ParameterTree from "./ParameterTree.tsx";
|
|
14
|
-
import type {
|
|
15
|
+
import type { ParameterValue } from "./types.ts";
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
export interface AdminParametersProps {
|
|
18
|
+
treeData: ParameterTreeNode[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const AdminParameters = ({
|
|
22
|
+
treeData: initialTreeData,
|
|
23
|
+
}: AdminParametersProps) => {
|
|
24
|
+
const client = useClient<AdminParameterController>();
|
|
25
|
+
const toast = useToast();
|
|
18
26
|
|
|
19
27
|
// State
|
|
20
|
-
const [treeData, setTreeData] =
|
|
28
|
+
const [treeData, setTreeData] =
|
|
29
|
+
useState<ParameterTreeNode[]>(initialTreeData);
|
|
21
30
|
const [selectedConfig, setSelectedConfig] = useState<string | null>(null);
|
|
22
|
-
const [configValue, setConfigValue] = useState<
|
|
31
|
+
const [configValue, setConfigValue] = useState<ParameterValue | null>(null);
|
|
23
32
|
const [history, setHistory] = useState<Parameter[]>([]);
|
|
24
|
-
const [loading, setLoading] = useState(true);
|
|
25
33
|
const [loadingConfig, setLoadingConfig] = useState(false);
|
|
26
34
|
const [loadingHistory, setLoadingHistory] = useState(false);
|
|
35
|
+
const [saving, setSaving] = useState(false);
|
|
27
36
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
37
|
+
// Refresh tree data
|
|
38
|
+
const handleRefresh = useCallback(async () => {
|
|
30
39
|
try {
|
|
31
|
-
const tree = await client.
|
|
32
|
-
setTreeData(tree as
|
|
33
|
-
}
|
|
34
|
-
|
|
40
|
+
const tree = await client.getParameterTree({});
|
|
41
|
+
setTreeData(tree as ParameterTreeNode[]);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
toast.danger({
|
|
44
|
+
title: "Failed to refresh parameters",
|
|
45
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
46
|
+
});
|
|
35
47
|
}
|
|
36
|
-
}, []);
|
|
48
|
+
}, [client, toast]);
|
|
37
49
|
|
|
38
50
|
// Load config value and history when selection changes
|
|
39
|
-
const loadConfigDetails = useCallback(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
const loadConfigDetails = useCallback(
|
|
52
|
+
async (name: string) => {
|
|
53
|
+
setLoadingConfig(true);
|
|
54
|
+
setLoadingHistory(true);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const [currentResponse, historyResponse] = await Promise.all([
|
|
58
|
+
client.getCurrent({ params: { name } }),
|
|
59
|
+
client.getHistory({ params: { name } }),
|
|
60
|
+
]);
|
|
61
|
+
setConfigValue(currentResponse);
|
|
62
|
+
setHistory(historyResponse.versions);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
toast.danger({
|
|
65
|
+
title: "Failed to load configuration",
|
|
66
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
67
|
+
});
|
|
68
|
+
setConfigValue(null);
|
|
69
|
+
setHistory([]);
|
|
70
|
+
} finally {
|
|
71
|
+
setLoadingConfig(false);
|
|
72
|
+
setLoadingHistory(false);
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
[client, toast],
|
|
76
|
+
);
|
|
54
77
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
78
|
+
// Handle save
|
|
79
|
+
const handleSave = useCallback(
|
|
80
|
+
async (values: Record<string, unknown>) => {
|
|
81
|
+
if (!selectedConfig || !configValue) return;
|
|
82
|
+
|
|
83
|
+
setSaving(true);
|
|
84
|
+
try {
|
|
85
|
+
await client.createVersion({
|
|
86
|
+
params: { name: selectedConfig },
|
|
87
|
+
body: {
|
|
88
|
+
content: values,
|
|
89
|
+
schemaHash: "", // Schema hash is computed server-side when empty
|
|
90
|
+
changeDescription: "Updated via admin UI",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
toast.success({
|
|
95
|
+
title: "Configuration saved",
|
|
96
|
+
message: `${selectedConfig} has been updated`,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Reload details
|
|
100
|
+
await loadConfigDetails(selectedConfig);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
toast.danger({
|
|
103
|
+
title: "Failed to save configuration",
|
|
104
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
105
|
+
});
|
|
106
|
+
throw error;
|
|
107
|
+
} finally {
|
|
108
|
+
setSaving(false);
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
[client, selectedConfig, configValue, loadConfigDetails, toast],
|
|
112
|
+
);
|
|
59
113
|
|
|
60
114
|
// Load details when selection changes
|
|
61
115
|
useEffect(() => {
|
|
@@ -68,31 +122,38 @@ const AdminParameters = () => {
|
|
|
68
122
|
}, [selectedConfig, loadConfigDetails]);
|
|
69
123
|
|
|
70
124
|
// Handle rollback
|
|
71
|
-
const handleRollback =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
125
|
+
const handleRollback = useCallback(
|
|
126
|
+
async (version: number) => {
|
|
127
|
+
if (!selectedConfig) return;
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
await client.rollback({
|
|
131
|
+
params: { name: selectedConfig },
|
|
132
|
+
body: { targetVersion: version },
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
toast.success({
|
|
136
|
+
title: "Rollback successful",
|
|
137
|
+
message: `${selectedConfig} rolled back to version ${version}`,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Reload details
|
|
141
|
+
await loadConfigDetails(selectedConfig);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
toast.danger({
|
|
144
|
+
title: "Rollback failed",
|
|
145
|
+
message: error instanceof Error ? error.message : "Unknown error",
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
[client, selectedConfig, loadConfigDetails, toast],
|
|
150
|
+
);
|
|
90
151
|
|
|
91
152
|
// Empty state when no configs exist
|
|
92
153
|
if (treeData.length === 0) {
|
|
93
154
|
return (
|
|
94
155
|
<Flex flex={1} justify="center" align="center">
|
|
95
|
-
<
|
|
156
|
+
<Flex direction="column" align="center" gap="xs">
|
|
96
157
|
<IconSettings
|
|
97
158
|
size={48}
|
|
98
159
|
stroke={1.5}
|
|
@@ -100,35 +161,52 @@ const AdminParameters = () => {
|
|
|
100
161
|
/>
|
|
101
162
|
<Text c="dimmed">No Parameters Found</Text>
|
|
102
163
|
<Text size="xs" c="dimmed" ta="center" maw={400}>
|
|
103
|
-
Define parameters using the $
|
|
164
|
+
Define parameters using the $parameter primitive to manage dynamic
|
|
104
165
|
application settings. Parameters will appear here once created.
|
|
105
166
|
</Text>
|
|
106
|
-
</
|
|
167
|
+
</Flex>
|
|
107
168
|
</Flex>
|
|
108
169
|
);
|
|
109
170
|
}
|
|
110
171
|
|
|
111
172
|
return (
|
|
112
|
-
<Flex flex={1}
|
|
113
|
-
<
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
173
|
+
<Flex flex={1} p="md">
|
|
174
|
+
<Card
|
|
175
|
+
withBorder
|
|
176
|
+
p={0}
|
|
177
|
+
w={"100%"}
|
|
178
|
+
style={{
|
|
179
|
+
flexDirection: "row",
|
|
180
|
+
}}
|
|
181
|
+
>
|
|
182
|
+
<ParameterTree
|
|
183
|
+
treeData={treeData}
|
|
184
|
+
selectedConfig={selectedConfig}
|
|
185
|
+
onSelect={setSelectedConfig}
|
|
186
|
+
onRefresh={handleRefresh}
|
|
187
|
+
/>
|
|
188
|
+
|
|
189
|
+
{selectedConfig ? (
|
|
190
|
+
<>
|
|
191
|
+
<ParameterDetails
|
|
192
|
+
selectedConfig={selectedConfig}
|
|
193
|
+
configValue={configValue}
|
|
194
|
+
loading={loadingConfig}
|
|
195
|
+
saving={saving}
|
|
196
|
+
onSave={handleSave}
|
|
197
|
+
/>
|
|
198
|
+
|
|
199
|
+
<ParameterHistory
|
|
200
|
+
selectedConfig={selectedConfig}
|
|
201
|
+
history={history}
|
|
202
|
+
loading={loadingHistory}
|
|
203
|
+
onRollback={handleRollback}
|
|
204
|
+
/>
|
|
205
|
+
</>
|
|
206
|
+
) : (
|
|
207
|
+
<ParameterEmptyState />
|
|
208
|
+
)}
|
|
209
|
+
</Card>
|
|
132
210
|
</Flex>
|
|
133
211
|
);
|
|
134
212
|
};
|
|
@@ -1,32 +1,56 @@
|
|
|
1
|
-
import { Flex, Text, TypeForm } from "@alepha/ui";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Card,
|
|
6
|
-
Code,
|
|
7
|
-
Group,
|
|
8
|
-
Loader,
|
|
9
|
-
ScrollArea,
|
|
10
|
-
Stack,
|
|
11
|
-
} from "@mantine/core";
|
|
12
|
-
import { IconClock, IconSettings } from "@tabler/icons-react";
|
|
13
|
-
import { jsonSchemaToTypeBox, type TObject } from "alepha";
|
|
1
|
+
import { ActionButton, Flex, Text, TypeForm } from "@alepha/ui";
|
|
2
|
+
import { Card, Code, Loader } from "@mantine/core";
|
|
3
|
+
import { IconClock } from "@tabler/icons-react";
|
|
4
|
+
import { jsonSchemaToTypeBox, type TObject, t } from "alepha";
|
|
14
5
|
import { useForm } from "alepha/react/form";
|
|
15
6
|
import { useI18n } from "alepha/react/i18n";
|
|
16
7
|
import { useMemo } from "react";
|
|
17
|
-
import { type
|
|
8
|
+
import { formatJson, type ParameterValue } from "./types.ts";
|
|
18
9
|
|
|
19
10
|
export interface ParameterDetailsProps {
|
|
20
11
|
selectedConfig: string | null;
|
|
21
|
-
configValue:
|
|
12
|
+
configValue: ParameterValue | null;
|
|
22
13
|
loading: boolean;
|
|
14
|
+
saving: boolean;
|
|
15
|
+
onSave: (values: Record<string, unknown>) => Promise<void>;
|
|
23
16
|
}
|
|
24
17
|
|
|
25
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Loading state.
|
|
20
|
+
*/
|
|
21
|
+
const LoadingState = () => (
|
|
22
|
+
<Flex
|
|
23
|
+
flex={1}
|
|
24
|
+
h="100%"
|
|
25
|
+
p="md"
|
|
26
|
+
style={{
|
|
27
|
+
overflow: "hidden",
|
|
28
|
+
minWidth: 0,
|
|
29
|
+
display: "flex",
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<Flex flex={1} justify="center" align="center" h="100%">
|
|
33
|
+
<Loader size="sm" />
|
|
34
|
+
</Flex>
|
|
35
|
+
</Flex>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
interface ConfigFormProps {
|
|
39
|
+
selectedConfig: string;
|
|
40
|
+
configValue: ParameterValue | null;
|
|
41
|
+
saving: boolean;
|
|
42
|
+
onSave: (values: Record<string, unknown>) => Promise<void>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The actual form component - only rendered when a config is selected.
|
|
47
|
+
*/
|
|
48
|
+
const ConfigForm = ({
|
|
26
49
|
selectedConfig,
|
|
27
50
|
configValue,
|
|
28
|
-
|
|
29
|
-
|
|
51
|
+
saving,
|
|
52
|
+
onSave,
|
|
53
|
+
}: ConfigFormProps) => {
|
|
30
54
|
const { l } = useI18n();
|
|
31
55
|
|
|
32
56
|
// Get the current value to display (from saved version or default)
|
|
@@ -43,17 +67,21 @@ const ParameterDetails = ({
|
|
|
43
67
|
// Convert JSON Schema from API to TypeBox schema
|
|
44
68
|
const schemaForForm = useMemo(() => {
|
|
45
69
|
if (!configValue?.schema) {
|
|
46
|
-
return
|
|
70
|
+
return t.object({});
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
return jsonSchemaToTypeBox(configValue.schema) as TObject;
|
|
74
|
+
} catch {
|
|
75
|
+
return t.object({});
|
|
47
76
|
}
|
|
48
|
-
return jsonSchemaToTypeBox(configValue.schema) as TObject;
|
|
49
77
|
}, [configValue?.schema]);
|
|
50
78
|
|
|
51
79
|
const form = useForm(
|
|
52
80
|
{
|
|
53
81
|
schema: schemaForForm,
|
|
54
82
|
initialValues: (currentContent ?? {}) as Record<string, unknown>,
|
|
55
|
-
handler: async () => {
|
|
56
|
-
|
|
83
|
+
handler: async (values) => {
|
|
84
|
+
await onSave(values as Record<string, unknown>);
|
|
57
85
|
},
|
|
58
86
|
},
|
|
59
87
|
[selectedConfig, schemaForForm, currentContent],
|
|
@@ -66,117 +94,101 @@ const ParameterDetails = ({
|
|
|
66
94
|
schema &&
|
|
67
95
|
typeof schema === "object" &&
|
|
68
96
|
"properties" in schema &&
|
|
69
|
-
Object.keys(schema.properties
|
|
97
|
+
Object.keys(schema.properties as object).length > 0
|
|
70
98
|
);
|
|
71
99
|
}, [configValue?.schema]);
|
|
72
100
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
</Stack>
|
|
87
|
-
</Flex>
|
|
88
|
-
</Card>
|
|
89
|
-
);
|
|
90
|
-
}
|
|
101
|
+
// Count the number of fields to determine column layout
|
|
102
|
+
const fieldCount = useMemo(() => {
|
|
103
|
+
const schema = configValue?.schema;
|
|
104
|
+
if (
|
|
105
|
+
schema &&
|
|
106
|
+
typeof schema === "object" &&
|
|
107
|
+
"properties" in schema &&
|
|
108
|
+
schema.properties
|
|
109
|
+
) {
|
|
110
|
+
return Object.keys(schema.properties as object).length;
|
|
111
|
+
}
|
|
112
|
+
return 0;
|
|
113
|
+
}, [configValue?.schema]);
|
|
91
114
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
</Card>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
115
|
+
// Determine optimal column count based on field count
|
|
116
|
+
const columns = useMemo(() => {
|
|
117
|
+
if (fieldCount <= 2) return 1;
|
|
118
|
+
if (fieldCount <= 6) return 2;
|
|
119
|
+
return 3;
|
|
120
|
+
}, [fieldCount]);
|
|
101
121
|
|
|
102
122
|
return (
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
</Group>
|
|
121
|
-
)}
|
|
122
|
-
{!configValue?.current &&
|
|
123
|
-
configValue?.currentValue !== undefined && (
|
|
124
|
-
<Badge size="xs" color="yellow" variant="light">
|
|
125
|
-
Default
|
|
126
|
-
</Badge>
|
|
127
|
-
)}
|
|
128
|
-
</Stack>
|
|
129
|
-
</Group>
|
|
130
|
-
|
|
131
|
-
<ScrollArea flex={1} offsetScrollbars>
|
|
123
|
+
<Flex
|
|
124
|
+
flex={1}
|
|
125
|
+
h="100%"
|
|
126
|
+
style={{
|
|
127
|
+
overflow: "hidden",
|
|
128
|
+
minWidth: 0,
|
|
129
|
+
display: "flex",
|
|
130
|
+
}}
|
|
131
|
+
>
|
|
132
|
+
<Flex direction="column" h="100%" w="100%" style={{ minHeight: 0 }}>
|
|
133
|
+
{/* Content */}
|
|
134
|
+
<Flex
|
|
135
|
+
flex={1}
|
|
136
|
+
p="md"
|
|
137
|
+
className="overflow-auto"
|
|
138
|
+
style={{ minHeight: 0 }}
|
|
139
|
+
>
|
|
132
140
|
{currentContent !== null ? (
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
Current Value
|
|
137
|
-
</Text>
|
|
141
|
+
<Flex direction="column" gap="lg">
|
|
142
|
+
{/* Form or JSON view */}
|
|
143
|
+
<Flex>
|
|
138
144
|
{hasValidSchema ? (
|
|
139
145
|
<TypeForm
|
|
140
146
|
form={form}
|
|
141
|
-
columns={
|
|
147
|
+
columns={columns}
|
|
142
148
|
skipSubmitButton
|
|
143
|
-
|
|
149
|
+
fill={false}
|
|
144
150
|
/>
|
|
145
151
|
) : (
|
|
146
|
-
<
|
|
147
|
-
{
|
|
148
|
-
|
|
152
|
+
<Flex>
|
|
153
|
+
<Text size="xs" c="dimmed" mb={4}>
|
|
154
|
+
Current Value
|
|
155
|
+
</Text>
|
|
156
|
+
<Code block style={{ whiteSpace: "pre-wrap" }}>
|
|
157
|
+
{formatJson(currentContent)}
|
|
158
|
+
</Code>
|
|
159
|
+
</Flex>
|
|
149
160
|
)}
|
|
150
|
-
</
|
|
161
|
+
</Flex>
|
|
151
162
|
|
|
163
|
+
{/* Metadata */}
|
|
152
164
|
{configValue?.current?.changeDescription && (
|
|
153
|
-
<
|
|
165
|
+
<Flex>
|
|
154
166
|
<Text size="xs" c="dimmed" mb={4}>
|
|
155
167
|
Change Description
|
|
156
168
|
</Text>
|
|
157
169
|
<Text size="sm">{configValue.current.changeDescription}</Text>
|
|
158
|
-
</
|
|
170
|
+
</Flex>
|
|
159
171
|
)}
|
|
160
172
|
|
|
161
173
|
{configValue?.current && (
|
|
162
|
-
<
|
|
163
|
-
<
|
|
174
|
+
<Flex gap="xl">
|
|
175
|
+
<Flex>
|
|
164
176
|
<Text size="xs" c="dimmed" mb={2}>
|
|
165
177
|
Updated
|
|
166
178
|
</Text>
|
|
167
179
|
<Text size="sm">
|
|
168
180
|
{l(configValue.current.updatedAt, { date: "fromNow" })}
|
|
169
181
|
</Text>
|
|
170
|
-
</
|
|
182
|
+
</Flex>
|
|
171
183
|
{configValue.current.creatorName && (
|
|
172
|
-
<
|
|
184
|
+
<Flex>
|
|
173
185
|
<Text size="xs" c="dimmed" mb={2}>
|
|
174
186
|
Updated By
|
|
175
187
|
</Text>
|
|
176
188
|
<Text size="sm">{configValue.current.creatorName}</Text>
|
|
177
|
-
</
|
|
189
|
+
</Flex>
|
|
178
190
|
)}
|
|
179
|
-
</
|
|
191
|
+
</Flex>
|
|
180
192
|
)}
|
|
181
193
|
|
|
182
194
|
{!configValue?.current &&
|
|
@@ -187,18 +199,19 @@ const ParameterDetails = ({
|
|
|
187
199
|
</Text>
|
|
188
200
|
)}
|
|
189
201
|
|
|
202
|
+
{/* Scheduled update preview */}
|
|
190
203
|
{configValue?.next && (
|
|
191
|
-
<Card withBorder
|
|
192
|
-
<
|
|
193
|
-
<
|
|
204
|
+
<Card withBorder p="sm" bg="var(--mantine-color-blue-light)">
|
|
205
|
+
<Flex direction="column" gap="xs">
|
|
206
|
+
<Flex gap="xs">
|
|
194
207
|
<IconClock
|
|
195
208
|
size={14}
|
|
196
209
|
color="var(--mantine-color-blue-6)"
|
|
197
210
|
/>
|
|
198
|
-
<Text size="xs" fw={500} c="blue
|
|
211
|
+
<Text size="xs" fw={500} c="blue">
|
|
199
212
|
Scheduled Update (v{configValue.next.version})
|
|
200
213
|
</Text>
|
|
201
|
-
</
|
|
214
|
+
</Flex>
|
|
202
215
|
<Text size="xs" c="dimmed">
|
|
203
216
|
Activates{" "}
|
|
204
217
|
{l(configValue.next.activationDate, {
|
|
@@ -208,10 +221,10 @@ const ParameterDetails = ({
|
|
|
208
221
|
<Code block style={{ whiteSpace: "pre-wrap" }} fz="xs">
|
|
209
222
|
{formatJson(configValue.next.content)}
|
|
210
223
|
</Code>
|
|
211
|
-
</
|
|
224
|
+
</Flex>
|
|
212
225
|
</Card>
|
|
213
226
|
)}
|
|
214
|
-
</
|
|
227
|
+
</Flex>
|
|
215
228
|
) : (
|
|
216
229
|
<Flex justify="center" align="center" h={200}>
|
|
217
230
|
<Text c="dimmed" size="sm">
|
|
@@ -219,9 +232,61 @@ const ParameterDetails = ({
|
|
|
219
232
|
</Text>
|
|
220
233
|
</Flex>
|
|
221
234
|
)}
|
|
222
|
-
</
|
|
223
|
-
|
|
224
|
-
|
|
235
|
+
</Flex>
|
|
236
|
+
|
|
237
|
+
{/* Footer with actions */}
|
|
238
|
+
{hasValidSchema && currentContent !== null && (
|
|
239
|
+
<Flex
|
|
240
|
+
p="md"
|
|
241
|
+
style={{
|
|
242
|
+
flexShrink: 0,
|
|
243
|
+
borderTop: "1px solid var(--mantine-color-default-border)",
|
|
244
|
+
}}
|
|
245
|
+
>
|
|
246
|
+
<Flex justify="flex-end" gap="sm">
|
|
247
|
+
<ActionButton
|
|
248
|
+
variant="subtle"
|
|
249
|
+
onClick={() => form.reset({} as any)}
|
|
250
|
+
disabled={saving}
|
|
251
|
+
>
|
|
252
|
+
Reset
|
|
253
|
+
</ActionButton>
|
|
254
|
+
<ActionButton intent="primary" form={form} loading={saving}>
|
|
255
|
+
Save Changes
|
|
256
|
+
</ActionButton>
|
|
257
|
+
</Flex>
|
|
258
|
+
</Flex>
|
|
259
|
+
)}
|
|
260
|
+
</Flex>
|
|
261
|
+
</Flex>
|
|
262
|
+
);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Parameter details panel.
|
|
267
|
+
* Shows loading state or the config form.
|
|
268
|
+
* Note: Empty state is handled by parent (AdminParameters).
|
|
269
|
+
*/
|
|
270
|
+
const ParameterDetails = ({
|
|
271
|
+
selectedConfig,
|
|
272
|
+
configValue,
|
|
273
|
+
loading,
|
|
274
|
+
saving,
|
|
275
|
+
onSave,
|
|
276
|
+
}: ParameterDetailsProps) => {
|
|
277
|
+
// Loading state
|
|
278
|
+
if (loading) {
|
|
279
|
+
return <LoadingState />;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Config form (selectedConfig is guaranteed to be non-null by parent)
|
|
283
|
+
return (
|
|
284
|
+
<ConfigForm
|
|
285
|
+
selectedConfig={selectedConfig!}
|
|
286
|
+
configValue={configValue}
|
|
287
|
+
saving={saving}
|
|
288
|
+
onSave={onSave}
|
|
289
|
+
/>
|
|
225
290
|
);
|
|
226
291
|
};
|
|
227
292
|
|