@alepha/ui 0.13.6 → 0.13.7
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/AdminAudits-CwvH8e8c.js +215 -0
- package/dist/admin/AdminAudits-CwvH8e8c.js.map +1 -0
- package/dist/admin/AdminAudits-Dv8Vk_6r.js +3 -0
- package/dist/admin/AdminFiles-5CPA3lQk.js +3 -0
- package/dist/admin/{AdminFiles-B_jfB_Py.js → AdminFiles-C_w1tb_x.js} +4 -3
- package/dist/admin/AdminFiles-C_w1tb_x.js.map +1 -0
- package/dist/admin/AdminLayout-BnSmtA4x.js +3 -0
- package/dist/admin/AdminLayout-XiSivwWH.js +39 -0
- package/dist/admin/AdminLayout-XiSivwWH.js.map +1 -0
- package/dist/admin/AdminNotifications-DLjmZWtf.js +3 -0
- package/dist/admin/{AdminNotifications-BFEjqpqx.js → AdminNotifications-DuYy74AN.js} +3 -3
- package/dist/admin/AdminNotifications-DuYy74AN.js.map +1 -0
- package/dist/admin/AdminParameters-DYg48Jwe.js +3 -0
- package/dist/admin/AdminParameters-YagqWTG3.js +575 -0
- package/dist/admin/AdminParameters-YagqWTG3.js.map +1 -0
- package/dist/admin/{AdminSessions-D7DESfWK.js → AdminSessions-BCjgJ-93.js} +4 -4
- package/dist/admin/AdminSessions-BCjgJ-93.js.map +1 -0
- package/dist/admin/AdminSessions-DEh2uN-4.js +3 -0
- package/dist/admin/AdminUserAudits-B_PUXCKC.js +177 -0
- package/dist/admin/AdminUserAudits-B_PUXCKC.js.map +1 -0
- package/dist/admin/AdminUserAudits-D7cTcElL.js +3 -0
- package/dist/admin/{AdminUserCreate-Bhxsn92l.js → AdminUserCreate-DzfRbGZ4.js} +4 -4
- package/dist/admin/AdminUserCreate-DzfRbGZ4.js.map +1 -0
- package/dist/admin/{AdminUserCreate-CYI_xW5T.js → AdminUserCreate-oUA1KDIl.js} +1 -1
- package/dist/admin/{AdminUserDetails-C2y1Ig4n.js → AdminUserDetails-DeTrJm-t.js} +5 -5
- package/dist/admin/AdminUserDetails-DeTrJm-t.js.map +1 -0
- package/dist/admin/{AdminUserDetails-Cmzx9HxH.js → AdminUserDetails-y1H5DW8Y.js} +1 -1
- package/dist/admin/{AdminUserLayout-sW6cjZL0.js → AdminUserLayout-CsfrrZkD.js} +4 -7
- package/dist/admin/AdminUserLayout-CsfrrZkD.js.map +1 -0
- package/dist/admin/{AdminUserLayout-DGSf612u.js → AdminUserLayout-Dejnz13m.js} +1 -1
- package/dist/admin/AdminUserSessions-Bbhcpz4k.js +3 -0
- package/dist/admin/{AdminUserSessions-CvN15wPe.js → AdminUserSessions-DO9H85O-.js} +4 -4
- package/dist/admin/AdminUserSessions-DO9H85O-.js.map +1 -0
- package/dist/admin/{AdminUserSettings-DvaaxgcV.js → AdminUserSettings-B3jA8g3p.js} +4 -4
- package/dist/admin/AdminUserSettings-B3jA8g3p.js.map +1 -0
- package/dist/admin/AdminUserSettings-CE0xpbQc.js +3 -0
- package/dist/admin/AdminUsers-CegGZDhW.js +3 -0
- package/dist/admin/{AdminUsers-BR3C-jrg.js → AdminUsers-ebbrJBT0.js} +13 -17
- package/dist/admin/AdminUsers-ebbrJBT0.js.map +1 -0
- package/dist/admin/index.d.ts +2044 -1044
- package/dist/admin/index.js +65 -62
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/AuthLayout-BAZJHzDG.js +23 -0
- package/dist/auth/AuthLayout-BAZJHzDG.js.map +1 -0
- package/dist/auth/{Login-7HlBjDeV.js → Login-CeNZZjrr.js} +80 -44
- package/dist/auth/Login-CeNZZjrr.js.map +1 -0
- package/dist/auth/Login-hQcu1nlu.js +4 -0
- package/dist/auth/Register-B6HBNVHS.js +4 -0
- package/dist/auth/{Register-CuQr3kgi.js → Register-s4ENeyiE.js} +131 -91
- package/dist/auth/Register-s4ENeyiE.js.map +1 -0
- package/dist/auth/ResetPassword-Cjd-W-Nu.js +3 -0
- package/dist/auth/ResetPassword-GLIFkJT7.js +278 -0
- package/dist/auth/ResetPassword-GLIFkJT7.js.map +1 -0
- package/dist/auth/index.d.ts +471 -426
- package/dist/auth/index.js +26 -18
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +400 -130
- package/dist/core/index.js +1751 -1369
- package/dist/core/index.js.map +1 -1
- package/package.json +15 -11
- package/src/admin/AdminRouter.ts +70 -16
- package/src/admin/components/AdminLayout.tsx +41 -61
- package/src/admin/components/audits/AdminAudits.tsx +240 -0
- package/src/admin/components/{AdminFiles.tsx → files/AdminFiles.tsx} +1 -1
- package/src/admin/components/{AdminJobs.tsx → jobs/AdminJobs.tsx} +1 -1
- package/src/admin/components/parameters/AdminParameters.tsx +137 -0
- package/src/admin/components/parameters/ParameterDetails.tsx +228 -0
- package/src/admin/components/parameters/ParameterHistory.tsx +146 -0
- package/src/admin/components/parameters/ParameterTree.tsx +146 -0
- package/src/admin/components/parameters/types.ts +35 -0
- package/src/admin/components/{AdminSessions.tsx → sessions/AdminSessions.tsx} +1 -1
- package/src/admin/components/users/AdminUserAudits.tsx +183 -0
- package/src/admin/components/{AdminUserCreate.tsx → users/AdminUserCreate.tsx} +1 -1
- package/src/admin/components/{AdminUserLayout.tsx → users/AdminUserLayout.tsx} +1 -4
- package/src/admin/components/{AdminUserSettings.tsx → users/AdminUserSettings.tsx} +1 -1
- package/src/admin/components/{AdminUsers.tsx → users/AdminUsers.tsx} +10 -12
- package/src/admin/index.ts +24 -16
- package/src/auth/AuthRouter.ts +23 -17
- package/src/auth/components/AuthLayout.tsx +6 -3
- package/src/auth/components/Login.tsx +109 -47
- package/src/auth/components/Register.tsx +158 -94
- package/src/auth/components/ResetPassword.tsx +51 -5
- package/src/auth/components/buttons/UserButton.tsx +2 -0
- package/src/core/atoms/alephaThemeAtom.ts +13 -0
- package/src/core/atoms/alephaThemeListAtom.ts +10 -0
- package/src/core/atoms/themes/default.ts +6 -0
- package/src/core/{themes → atoms/themes}/midnight.ts +3 -5
- package/src/core/components/buttons/ActionButton.tsx +33 -26
- package/src/core/components/buttons/DarkModeButton.tsx +0 -1
- package/src/core/components/buttons/ThemeButton.tsx +10 -7
- package/src/core/components/buttons/ToggleSidebarButton.tsx +19 -16
- package/src/core/components/data/ErrorViewer.tsx +171 -0
- package/src/core/components/data/JsonViewer.tsx +147 -138
- package/src/core/components/form/Control.tsx +95 -18
- package/src/core/components/form/ControlArray.tsx +377 -0
- package/src/core/components/form/ControlObject.tsx +127 -0
- package/src/core/components/form/TypeForm.tsx +99 -37
- package/src/core/components/layout/AdminShell.tsx +14 -1
- package/src/core/components/layout/AlephaMantineProvider.tsx +7 -3
- package/src/core/components/layout/Omnibar.tsx +1 -1
- package/src/core/components/layout/Sidebar.tsx +47 -14
- package/src/core/components/table/ColumnPicker.tsx +126 -0
- package/src/core/components/table/DataTable.tsx +354 -181
- package/src/core/components/table/DataTableFilters.tsx +64 -0
- package/src/core/components/table/DataTablePagination.tsx +59 -0
- package/src/core/components/table/DataTableToolbar.tsx +126 -0
- package/src/core/components/table/FilterPicker.tsx +138 -0
- package/src/core/components/table/types.ts +199 -0
- package/src/core/helpers/isComponentType.ts +9 -0
- package/src/core/helpers/renderIcon.tsx +13 -0
- package/src/core/hooks/useTheme.ts +24 -18
- package/src/core/index.ts +24 -3
- package/src/core/interfaces/AlephaTheme.ts +8 -0
- package/src/core/providers/ThemeProvider.ts +44 -62
- package/src/core/services/DialogService.tsx +24 -0
- package/src/core/utils/parseInput.ts +2 -2
- package/styles.css +1 -1
- package/dist/admin/AdminFiles-B-0UcHVV.js +0 -3
- package/dist/admin/AdminFiles-B_jfB_Py.js.map +0 -1
- package/dist/admin/AdminLayout-BMtiXAzS.js +0 -396
- package/dist/admin/AdminLayout-BMtiXAzS.js.map +0 -1
- package/dist/admin/AdminLayout-BNo3GoHR.js +0 -3
- package/dist/admin/AdminNotifications-BFEjqpqx.js.map +0 -1
- package/dist/admin/AdminNotifications-DJs2ZjNj.js +0 -3
- package/dist/admin/AdminSessions-D7DESfWK.js.map +0 -1
- package/dist/admin/AdminSessions-PS2M8iXi.js +0 -3
- package/dist/admin/AdminUserCreate-Bhxsn92l.js.map +0 -1
- package/dist/admin/AdminUserDetails-C2y1Ig4n.js.map +0 -1
- package/dist/admin/AdminUserLayout-sW6cjZL0.js.map +0 -1
- package/dist/admin/AdminUserSessions-CvN15wPe.js.map +0 -1
- package/dist/admin/AdminUserSessions-D-aOcZgV.js +0 -3
- package/dist/admin/AdminUserSettings-CEMhIYrI.js +0 -3
- package/dist/admin/AdminUserSettings-DvaaxgcV.js.map +0 -1
- package/dist/admin/AdminUsers-BR3C-jrg.js.map +0 -1
- package/dist/admin/AdminUsers-CMW9vN09.js +0 -3
- package/dist/auth/AuthLayout-CzwUKD9y.js +0 -19
- package/dist/auth/AuthLayout-CzwUKD9y.js.map +0 -1
- package/dist/auth/Login-7HlBjDeV.js.map +0 -1
- package/dist/auth/Login-C-e27DGb.js +0 -4
- package/dist/auth/Register-CuQr3kgi.js.map +0 -1
- package/dist/auth/Register-DbvXwgbG.js +0 -4
- package/dist/auth/ResetPassword-BzU-cdd4.js +0 -243
- package/dist/auth/ResetPassword-BzU-cdd4.js.map +0 -1
- package/dist/auth/ResetPassword-DSvrdpaA.js +0 -3
- package/src/admin/AdminSidebar.ts +0 -31
- package/src/admin/components/AdminParameters.tsx +0 -24
- package/src/core/themes/aurora.ts +0 -107
- package/src/core/themes/crystal.ts +0 -107
- package/src/core/themes/default.ts +0 -7
- package/src/core/themes/ember.ts +0 -107
- package/src/core/themes/index.ts +0 -7
- package/src/core/themes/remoraid.ts +0 -278
- package/src/core/themes/slate.ts +0 -81
- /package/src/admin/components/{AdminNotifications.tsx → notifications/AdminNotifications.tsx} +0 -0
- /package/src/admin/components/{AdminUserDetails.tsx → users/AdminUserDetails.tsx} +0 -0
- /package/src/admin/components/{AdminUserSessions.tsx → users/AdminUserSessions.tsx} +0 -0
- /package/src/admin/components/{AdminVerifications.tsx → verifications/AdminVerifications.tsx} +0 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { useClient } from "@alepha/react";
|
|
2
|
+
import { Flex, Text } from "@alepha/ui";
|
|
3
|
+
import { Loader, Stack } from "@mantine/core";
|
|
4
|
+
import { IconSettings } from "@tabler/icons-react";
|
|
5
|
+
import type {
|
|
6
|
+
ConfigController,
|
|
7
|
+
ConfigTreeNode,
|
|
8
|
+
Parameter,
|
|
9
|
+
} from "alepha/api/parameters";
|
|
10
|
+
import { useCallback, useEffect, useState } from "react";
|
|
11
|
+
import ParameterDetails from "./ParameterDetails.tsx";
|
|
12
|
+
import ParameterHistory from "./ParameterHistory.tsx";
|
|
13
|
+
import ParameterTree from "./ParameterTree.tsx";
|
|
14
|
+
import type { ConfigValue } from "./types.ts";
|
|
15
|
+
|
|
16
|
+
const AdminParameters = () => {
|
|
17
|
+
const client = useClient<ConfigController>();
|
|
18
|
+
|
|
19
|
+
// State
|
|
20
|
+
const [treeData, setTreeData] = useState<ConfigTreeNode[]>([]);
|
|
21
|
+
const [selectedConfig, setSelectedConfig] = useState<string | null>(null);
|
|
22
|
+
const [configValue, setConfigValue] = useState<ConfigValue | null>(null);
|
|
23
|
+
const [history, setHistory] = useState<Parameter[]>([]);
|
|
24
|
+
const [loading, setLoading] = useState(true);
|
|
25
|
+
const [loadingConfig, setLoadingConfig] = useState(false);
|
|
26
|
+
const [loadingHistory, setLoadingHistory] = useState(false);
|
|
27
|
+
|
|
28
|
+
// Load tree data
|
|
29
|
+
const loadTree = useCallback(async () => {
|
|
30
|
+
try {
|
|
31
|
+
const tree = await client.getConfigTree({});
|
|
32
|
+
setTreeData(tree as ConfigTreeNode[]);
|
|
33
|
+
} finally {
|
|
34
|
+
setLoading(false);
|
|
35
|
+
}
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
// Load config value and history when selection changes
|
|
39
|
+
const loadConfigDetails = useCallback(async (name: string) => {
|
|
40
|
+
setLoadingConfig(true);
|
|
41
|
+
setLoadingHistory(true);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const [current, historyData] = await Promise.all([
|
|
45
|
+
client.getCurrent({ params: { name } }),
|
|
46
|
+
client.getHistory({ params: { name } }),
|
|
47
|
+
]);
|
|
48
|
+
setConfigValue(current);
|
|
49
|
+
setHistory(historyData.versions);
|
|
50
|
+
} finally {
|
|
51
|
+
setLoadingConfig(false);
|
|
52
|
+
setLoadingHistory(false);
|
|
53
|
+
}
|
|
54
|
+
}, []);
|
|
55
|
+
|
|
56
|
+
// Initial load
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
loadTree();
|
|
59
|
+
}, [loadTree]);
|
|
60
|
+
|
|
61
|
+
// Load details when selection changes
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (selectedConfig) {
|
|
64
|
+
loadConfigDetails(selectedConfig);
|
|
65
|
+
} else {
|
|
66
|
+
setConfigValue(null);
|
|
67
|
+
setHistory([]);
|
|
68
|
+
}
|
|
69
|
+
}, [selectedConfig, loadConfigDetails]);
|
|
70
|
+
|
|
71
|
+
// Handle rollback
|
|
72
|
+
const handleRollback = async (version: number) => {
|
|
73
|
+
if (!selectedConfig) return;
|
|
74
|
+
|
|
75
|
+
await client.rollback({
|
|
76
|
+
params: { name: selectedConfig },
|
|
77
|
+
body: { targetVersion: version },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Reload details
|
|
81
|
+
await loadConfigDetails(selectedConfig);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (loading) {
|
|
85
|
+
return (
|
|
86
|
+
<Flex flex={1} justify="center" align="center">
|
|
87
|
+
<Loader />
|
|
88
|
+
</Flex>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Empty state when no configs exist
|
|
93
|
+
if (treeData.length === 0) {
|
|
94
|
+
return (
|
|
95
|
+
<Flex flex={1} justify="center" align="center">
|
|
96
|
+
<Stack align="center" gap="xs">
|
|
97
|
+
<IconSettings
|
|
98
|
+
size={48}
|
|
99
|
+
stroke={1.5}
|
|
100
|
+
color="var(--mantine-color-dimmed)"
|
|
101
|
+
/>
|
|
102
|
+
<Text c="dimmed">No Parameters Found</Text>
|
|
103
|
+
<Text size="xs" c="dimmed" ta="center" maw={400}>
|
|
104
|
+
Define parameters using the $config primitive to manage dynamic
|
|
105
|
+
application settings. Parameters will appear here once created.
|
|
106
|
+
</Text>
|
|
107
|
+
</Stack>
|
|
108
|
+
</Flex>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<Flex flex={1} gap={"xs"} h="100%">
|
|
114
|
+
<ParameterTree
|
|
115
|
+
treeData={treeData}
|
|
116
|
+
selectedConfig={selectedConfig}
|
|
117
|
+
onSelect={setSelectedConfig}
|
|
118
|
+
onRefresh={loadTree}
|
|
119
|
+
/>
|
|
120
|
+
|
|
121
|
+
<ParameterDetails
|
|
122
|
+
selectedConfig={selectedConfig}
|
|
123
|
+
configValue={configValue}
|
|
124
|
+
loading={loadingConfig}
|
|
125
|
+
/>
|
|
126
|
+
|
|
127
|
+
<ParameterHistory
|
|
128
|
+
selectedConfig={selectedConfig}
|
|
129
|
+
history={history}
|
|
130
|
+
loading={loadingHistory}
|
|
131
|
+
onRollback={handleRollback}
|
|
132
|
+
/>
|
|
133
|
+
</Flex>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export default AdminParameters;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { useForm } from "@alepha/react/form";
|
|
2
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { Flex, Text, TypeForm } from "@alepha/ui";
|
|
4
|
+
import {
|
|
5
|
+
Badge,
|
|
6
|
+
Box,
|
|
7
|
+
Card,
|
|
8
|
+
Code,
|
|
9
|
+
Group,
|
|
10
|
+
Loader,
|
|
11
|
+
ScrollArea,
|
|
12
|
+
Stack,
|
|
13
|
+
} from "@mantine/core";
|
|
14
|
+
import { IconClock, IconSettings } from "@tabler/icons-react";
|
|
15
|
+
import { jsonSchemaToTypeBox, type TObject } from "alepha";
|
|
16
|
+
import { useMemo } from "react";
|
|
17
|
+
import { type ConfigValue, formatJson } from "./types.ts";
|
|
18
|
+
|
|
19
|
+
export interface ParameterDetailsProps {
|
|
20
|
+
selectedConfig: string | null;
|
|
21
|
+
configValue: ConfigValue | null;
|
|
22
|
+
loading: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const ParameterDetails = ({
|
|
26
|
+
selectedConfig,
|
|
27
|
+
configValue,
|
|
28
|
+
loading,
|
|
29
|
+
}: ParameterDetailsProps) => {
|
|
30
|
+
const { l } = useI18n();
|
|
31
|
+
|
|
32
|
+
// Get the current value to display (from saved version or default)
|
|
33
|
+
const currentContent = useMemo(() => {
|
|
34
|
+
if (configValue?.current?.content) {
|
|
35
|
+
return configValue.current.content;
|
|
36
|
+
}
|
|
37
|
+
if (configValue?.currentValue !== undefined) {
|
|
38
|
+
return configValue.currentValue;
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}, [configValue]);
|
|
42
|
+
|
|
43
|
+
// Convert JSON Schema from API to TypeBox schema
|
|
44
|
+
const schemaForForm = useMemo(() => {
|
|
45
|
+
if (!configValue?.schema) {
|
|
46
|
+
return { type: "object", properties: {} } as unknown as TObject;
|
|
47
|
+
}
|
|
48
|
+
return jsonSchemaToTypeBox(configValue.schema) as TObject;
|
|
49
|
+
}, [configValue?.schema]);
|
|
50
|
+
|
|
51
|
+
const form = useForm(
|
|
52
|
+
{
|
|
53
|
+
schema: schemaForForm,
|
|
54
|
+
initialValues: (currentContent ?? {}) as Record<string, unknown>,
|
|
55
|
+
handler: async () => {
|
|
56
|
+
// Read-only for now
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
[selectedConfig, schemaForForm, currentContent],
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// Check if we have a valid schema with properties
|
|
63
|
+
const hasValidSchema = useMemo(() => {
|
|
64
|
+
const schema = configValue?.schema;
|
|
65
|
+
return (
|
|
66
|
+
schema &&
|
|
67
|
+
typeof schema === "object" &&
|
|
68
|
+
"properties" in schema &&
|
|
69
|
+
Object.keys(schema.properties ?? {}).length > 0
|
|
70
|
+
);
|
|
71
|
+
}, [configValue?.schema]);
|
|
72
|
+
|
|
73
|
+
if (!selectedConfig) {
|
|
74
|
+
return (
|
|
75
|
+
<Card withBorder flex={1} h="100%" style={{ overflow: "hidden" }}>
|
|
76
|
+
<Flex flex={1} justify="center" align="center" h="100%">
|
|
77
|
+
<Stack align="center" gap="xs">
|
|
78
|
+
<IconSettings
|
|
79
|
+
size={32}
|
|
80
|
+
stroke={1.5}
|
|
81
|
+
color="var(--mantine-color-dimmed)"
|
|
82
|
+
/>
|
|
83
|
+
<Text c="dimmed" size="sm">
|
|
84
|
+
Select a parameter to view its value
|
|
85
|
+
</Text>
|
|
86
|
+
</Stack>
|
|
87
|
+
</Flex>
|
|
88
|
+
</Card>
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (loading) {
|
|
93
|
+
return (
|
|
94
|
+
<Card withBorder flex={1} h="100%" style={{ overflow: "hidden" }}>
|
|
95
|
+
<Flex flex={1} justify="center" align="center" h="100%">
|
|
96
|
+
<Loader size="sm" />
|
|
97
|
+
</Flex>
|
|
98
|
+
</Card>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Card withBorder flex={1} h="100%" style={{ overflow: "hidden" }}>
|
|
104
|
+
<Stack gap="md" h="100%">
|
|
105
|
+
<Group justify="space-between">
|
|
106
|
+
<Stack gap={2}>
|
|
107
|
+
<Text size="sm" fw={500}>
|
|
108
|
+
{selectedConfig}
|
|
109
|
+
</Text>
|
|
110
|
+
{configValue?.current && (
|
|
111
|
+
<Group gap="xs">
|
|
112
|
+
<Badge size="xs" color="green" variant="light">
|
|
113
|
+
v{configValue.current.version}
|
|
114
|
+
</Badge>
|
|
115
|
+
{configValue.next && (
|
|
116
|
+
<Badge size="xs" color="blue" variant="light">
|
|
117
|
+
Next: v{configValue.next.version}
|
|
118
|
+
</Badge>
|
|
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>
|
|
132
|
+
{currentContent !== null ? (
|
|
133
|
+
<Stack gap="md">
|
|
134
|
+
<Box>
|
|
135
|
+
<Text size="xs" c="dimmed" mb={4}>
|
|
136
|
+
Current Value
|
|
137
|
+
</Text>
|
|
138
|
+
{hasValidSchema ? (
|
|
139
|
+
<TypeForm
|
|
140
|
+
form={form}
|
|
141
|
+
columns={1}
|
|
142
|
+
skipSubmitButton
|
|
143
|
+
skipFormElement
|
|
144
|
+
/>
|
|
145
|
+
) : (
|
|
146
|
+
<Code block style={{ whiteSpace: "pre-wrap" }}>
|
|
147
|
+
{formatJson(currentContent)}
|
|
148
|
+
</Code>
|
|
149
|
+
)}
|
|
150
|
+
</Box>
|
|
151
|
+
|
|
152
|
+
{configValue?.current?.changeDescription && (
|
|
153
|
+
<Box>
|
|
154
|
+
<Text size="xs" c="dimmed" mb={4}>
|
|
155
|
+
Change Description
|
|
156
|
+
</Text>
|
|
157
|
+
<Text size="sm">{configValue.current.changeDescription}</Text>
|
|
158
|
+
</Box>
|
|
159
|
+
)}
|
|
160
|
+
|
|
161
|
+
{configValue?.current && (
|
|
162
|
+
<Group gap="xl">
|
|
163
|
+
<Box>
|
|
164
|
+
<Text size="xs" c="dimmed" mb={2}>
|
|
165
|
+
Updated
|
|
166
|
+
</Text>
|
|
167
|
+
<Text size="sm">
|
|
168
|
+
{l(configValue.current.updatedAt, { date: "fromNow" })}
|
|
169
|
+
</Text>
|
|
170
|
+
</Box>
|
|
171
|
+
{configValue.current.creatorName && (
|
|
172
|
+
<Box>
|
|
173
|
+
<Text size="xs" c="dimmed" mb={2}>
|
|
174
|
+
Updated By
|
|
175
|
+
</Text>
|
|
176
|
+
<Text size="sm">{configValue.current.creatorName}</Text>
|
|
177
|
+
</Box>
|
|
178
|
+
)}
|
|
179
|
+
</Group>
|
|
180
|
+
)}
|
|
181
|
+
|
|
182
|
+
{!configValue?.current &&
|
|
183
|
+
configValue?.currentValue !== undefined && (
|
|
184
|
+
<Text size="xs" c="dimmed">
|
|
185
|
+
This configuration is using its default value. No versions
|
|
186
|
+
have been saved to the database yet.
|
|
187
|
+
</Text>
|
|
188
|
+
)}
|
|
189
|
+
|
|
190
|
+
{configValue?.next && (
|
|
191
|
+
<Card withBorder bg="blue.0" p="sm">
|
|
192
|
+
<Stack gap="xs">
|
|
193
|
+
<Group gap="xs">
|
|
194
|
+
<IconClock
|
|
195
|
+
size={14}
|
|
196
|
+
color="var(--mantine-color-blue-6)"
|
|
197
|
+
/>
|
|
198
|
+
<Text size="xs" fw={500} c="blue.7">
|
|
199
|
+
Scheduled Update (v{configValue.next.version})
|
|
200
|
+
</Text>
|
|
201
|
+
</Group>
|
|
202
|
+
<Text size="xs" c="dimmed">
|
|
203
|
+
Activates{" "}
|
|
204
|
+
{l(configValue.next.activationDate, {
|
|
205
|
+
date: "fromNow",
|
|
206
|
+
})}
|
|
207
|
+
</Text>
|
|
208
|
+
<Code block style={{ whiteSpace: "pre-wrap" }} fz="xs">
|
|
209
|
+
{formatJson(configValue.next.content)}
|
|
210
|
+
</Code>
|
|
211
|
+
</Stack>
|
|
212
|
+
</Card>
|
|
213
|
+
)}
|
|
214
|
+
</Stack>
|
|
215
|
+
) : (
|
|
216
|
+
<Flex justify="center" align="center" h={200}>
|
|
217
|
+
<Text c="dimmed" size="sm">
|
|
218
|
+
No current value
|
|
219
|
+
</Text>
|
|
220
|
+
</Flex>
|
|
221
|
+
)}
|
|
222
|
+
</ScrollArea>
|
|
223
|
+
</Stack>
|
|
224
|
+
</Card>
|
|
225
|
+
);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export default ParameterDetails;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
2
|
+
import { ActionButton, Flex, Text } from "@alepha/ui";
|
|
3
|
+
import {
|
|
4
|
+
Badge,
|
|
5
|
+
Card,
|
|
6
|
+
Group,
|
|
7
|
+
Loader,
|
|
8
|
+
ScrollArea,
|
|
9
|
+
Stack,
|
|
10
|
+
Timeline,
|
|
11
|
+
} from "@mantine/core";
|
|
12
|
+
import { IconHistory } from "@tabler/icons-react";
|
|
13
|
+
import type { Parameter } from "alepha/api/parameters";
|
|
14
|
+
import { getStatusColor } from "./types.ts";
|
|
15
|
+
|
|
16
|
+
export interface ParameterHistoryProps {
|
|
17
|
+
selectedConfig: string | null;
|
|
18
|
+
history: Parameter[];
|
|
19
|
+
loading: boolean;
|
|
20
|
+
onRollback: (version: number) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const ParameterHistory = ({
|
|
24
|
+
selectedConfig,
|
|
25
|
+
history,
|
|
26
|
+
loading,
|
|
27
|
+
onRollback,
|
|
28
|
+
}: ParameterHistoryProps) => {
|
|
29
|
+
const { l } = useI18n();
|
|
30
|
+
|
|
31
|
+
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
|
+
if (loading) {
|
|
43
|
+
return (
|
|
44
|
+
<Flex flex={1} justify="center" align="center">
|
|
45
|
+
<Loader size="sm" />
|
|
46
|
+
</Flex>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (history.length === 0) {
|
|
51
|
+
return (
|
|
52
|
+
<Flex flex={1} justify="center" align="center">
|
|
53
|
+
<Text c="dimmed" size="xs">
|
|
54
|
+
No history
|
|
55
|
+
</Text>
|
|
56
|
+
</Flex>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<ScrollArea flex={1} offsetScrollbars>
|
|
62
|
+
<Timeline
|
|
63
|
+
active={history.findIndex((h) => h.status === "current")}
|
|
64
|
+
bulletSize={24}
|
|
65
|
+
lineWidth={2}
|
|
66
|
+
>
|
|
67
|
+
{history.map((version) => (
|
|
68
|
+
<Timeline.Item
|
|
69
|
+
key={version.id}
|
|
70
|
+
bullet={
|
|
71
|
+
<Text size="xs" fw={500}>
|
|
72
|
+
{version.version}
|
|
73
|
+
</Text>
|
|
74
|
+
}
|
|
75
|
+
title={
|
|
76
|
+
<Group gap="xs">
|
|
77
|
+
<Text size="xs" fw={500}>
|
|
78
|
+
Version {version.version}
|
|
79
|
+
</Text>
|
|
80
|
+
<Badge
|
|
81
|
+
size="xs"
|
|
82
|
+
variant="light"
|
|
83
|
+
color={getStatusColor(version.status)}
|
|
84
|
+
>
|
|
85
|
+
{version.status}
|
|
86
|
+
</Badge>
|
|
87
|
+
</Group>
|
|
88
|
+
}
|
|
89
|
+
>
|
|
90
|
+
<Stack gap={4} mt={4}>
|
|
91
|
+
<Text size="xs" c="dimmed">
|
|
92
|
+
{l(version.createdAt, { date: "fromNow" })}
|
|
93
|
+
</Text>
|
|
94
|
+
{version.changeDescription && (
|
|
95
|
+
<Text size="xs" lineClamp={2}>
|
|
96
|
+
{version.changeDescription}
|
|
97
|
+
</Text>
|
|
98
|
+
)}
|
|
99
|
+
{version.creatorName && (
|
|
100
|
+
<Text size="xs" c="dimmed">
|
|
101
|
+
by {version.creatorName}
|
|
102
|
+
</Text>
|
|
103
|
+
)}
|
|
104
|
+
{version.migrationLog && (
|
|
105
|
+
<Badge size="xs" variant="outline" color="orange">
|
|
106
|
+
Schema Changed
|
|
107
|
+
</Badge>
|
|
108
|
+
)}
|
|
109
|
+
{version.status === "expired" && (
|
|
110
|
+
<ActionButton
|
|
111
|
+
size="compact-xs"
|
|
112
|
+
variant="subtle"
|
|
113
|
+
onClick={() => onRollback(version.version)}
|
|
114
|
+
>
|
|
115
|
+
Rollback to this version
|
|
116
|
+
</ActionButton>
|
|
117
|
+
)}
|
|
118
|
+
</Stack>
|
|
119
|
+
</Timeline.Item>
|
|
120
|
+
))}
|
|
121
|
+
</Timeline>
|
|
122
|
+
</ScrollArea>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
return (
|
|
127
|
+
<Card
|
|
128
|
+
withBorder
|
|
129
|
+
w={300}
|
|
130
|
+
h="100%"
|
|
131
|
+
style={{ flexShrink: 0, overflow: "hidden" }}
|
|
132
|
+
>
|
|
133
|
+
<Stack gap="xs" h="100%">
|
|
134
|
+
<Group gap="xs">
|
|
135
|
+
<IconHistory size={16} color="var(--mantine-color-dimmed)" />
|
|
136
|
+
<Text size="sm" fw={500}>
|
|
137
|
+
Version History
|
|
138
|
+
</Text>
|
|
139
|
+
</Group>
|
|
140
|
+
{renderContent()}
|
|
141
|
+
</Stack>
|
|
142
|
+
</Card>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export default ParameterHistory;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { ActionButton, Text } from "@alepha/ui";
|
|
2
|
+
import {
|
|
3
|
+
Box,
|
|
4
|
+
Card,
|
|
5
|
+
Group,
|
|
6
|
+
ScrollArea,
|
|
7
|
+
Stack,
|
|
8
|
+
Tooltip,
|
|
9
|
+
Tree,
|
|
10
|
+
type TreeNodeData,
|
|
11
|
+
useTree,
|
|
12
|
+
} from "@mantine/core";
|
|
13
|
+
import {
|
|
14
|
+
IconChevronDown,
|
|
15
|
+
IconChevronRight,
|
|
16
|
+
IconFolder,
|
|
17
|
+
IconFolderOpen,
|
|
18
|
+
IconRefresh,
|
|
19
|
+
IconSettings,
|
|
20
|
+
} from "@tabler/icons-react";
|
|
21
|
+
import type { ConfigTreeNode } from "alepha/api/parameters";
|
|
22
|
+
import { type HTMLAttributes, useMemo } from "react";
|
|
23
|
+
|
|
24
|
+
export interface ParameterTreeProps {
|
|
25
|
+
treeData: ConfigTreeNode[];
|
|
26
|
+
selectedConfig: string | null;
|
|
27
|
+
onSelect: (name: string) => void;
|
|
28
|
+
onRefresh: () => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const ParameterTree = ({
|
|
32
|
+
treeData,
|
|
33
|
+
selectedConfig,
|
|
34
|
+
onSelect,
|
|
35
|
+
onRefresh,
|
|
36
|
+
}: ParameterTreeProps) => {
|
|
37
|
+
const tree = useTree({
|
|
38
|
+
initialExpandedState: {},
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const mantineTreeData = useMemo((): TreeNodeData[] => {
|
|
42
|
+
const convert = (nodes: ConfigTreeNode[]): TreeNodeData[] => {
|
|
43
|
+
return nodes.map((node) => ({
|
|
44
|
+
value: node.path,
|
|
45
|
+
label: node.name,
|
|
46
|
+
children: node.children.length > 0 ? convert(node.children) : undefined,
|
|
47
|
+
}));
|
|
48
|
+
};
|
|
49
|
+
return convert(treeData);
|
|
50
|
+
}, [treeData]);
|
|
51
|
+
|
|
52
|
+
const renderNode = ({
|
|
53
|
+
node,
|
|
54
|
+
expanded,
|
|
55
|
+
hasChildren,
|
|
56
|
+
elementProps,
|
|
57
|
+
}: {
|
|
58
|
+
node: TreeNodeData;
|
|
59
|
+
expanded: boolean;
|
|
60
|
+
hasChildren: boolean;
|
|
61
|
+
elementProps: HTMLAttributes<HTMLDivElement>;
|
|
62
|
+
}) => {
|
|
63
|
+
const isLeaf = !hasChildren;
|
|
64
|
+
const isSelected = selectedConfig === node.value;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Group
|
|
68
|
+
gap="xs"
|
|
69
|
+
wrap="nowrap"
|
|
70
|
+
{...elementProps}
|
|
71
|
+
onClick={(e) => {
|
|
72
|
+
elementProps.onClick?.(e);
|
|
73
|
+
if (isLeaf) {
|
|
74
|
+
onSelect(node.value);
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
style={{
|
|
78
|
+
...elementProps.style,
|
|
79
|
+
cursor: isLeaf ? "pointer" : "default",
|
|
80
|
+
backgroundColor: isSelected
|
|
81
|
+
? "var(--mantine-color-blue-light)"
|
|
82
|
+
: undefined,
|
|
83
|
+
borderRadius: "var(--mantine-radius-sm)",
|
|
84
|
+
paddingTop: 4,
|
|
85
|
+
paddingBottom: 4,
|
|
86
|
+
paddingRight: 8,
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
{hasChildren ? (
|
|
90
|
+
<>
|
|
91
|
+
{expanded ? (
|
|
92
|
+
<IconChevronDown size={14} color="var(--mantine-color-dimmed)" />
|
|
93
|
+
) : (
|
|
94
|
+
<IconChevronRight size={14} color="var(--mantine-color-dimmed)" />
|
|
95
|
+
)}
|
|
96
|
+
{expanded ? (
|
|
97
|
+
<IconFolderOpen size={16} color="var(--mantine-color-blue-6)" />
|
|
98
|
+
) : (
|
|
99
|
+
<IconFolder size={16} color="var(--mantine-color-blue-6)" />
|
|
100
|
+
)}
|
|
101
|
+
</>
|
|
102
|
+
) : (
|
|
103
|
+
<>
|
|
104
|
+
<Box w={14} />
|
|
105
|
+
<IconSettings size={16} color="var(--mantine-color-gray-6)" />
|
|
106
|
+
</>
|
|
107
|
+
)}
|
|
108
|
+
<Text size="sm" fw={isSelected ? 500 : 400}>
|
|
109
|
+
{node.label}
|
|
110
|
+
</Text>
|
|
111
|
+
</Group>
|
|
112
|
+
);
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Card withBorder w={280} h="100%" style={{ flexShrink: 0 }}>
|
|
117
|
+
<Stack gap="xs" h="100%">
|
|
118
|
+
<Group justify="space-between">
|
|
119
|
+
<Text size="sm" fw={500}>
|
|
120
|
+
Parameters
|
|
121
|
+
</Text>
|
|
122
|
+
<Tooltip label="Refresh">
|
|
123
|
+
<ActionButton
|
|
124
|
+
variant="subtle"
|
|
125
|
+
size="compact-xs"
|
|
126
|
+
onClick={onRefresh}
|
|
127
|
+
>
|
|
128
|
+
<IconRefresh size={14} />
|
|
129
|
+
</ActionButton>
|
|
130
|
+
</Tooltip>
|
|
131
|
+
</Group>
|
|
132
|
+
<ScrollArea flex={1} offsetScrollbars>
|
|
133
|
+
<Tree
|
|
134
|
+
data={mantineTreeData}
|
|
135
|
+
tree={tree}
|
|
136
|
+
levelOffset={20}
|
|
137
|
+
expandOnClick
|
|
138
|
+
renderNode={renderNode}
|
|
139
|
+
/>
|
|
140
|
+
</ScrollArea>
|
|
141
|
+
</Stack>
|
|
142
|
+
</Card>
|
|
143
|
+
);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export default ParameterTree;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { Parameter } from "alepha/api/parameters";
|
|
2
|
+
|
|
3
|
+
export interface ConfigValue {
|
|
4
|
+
current?: Parameter;
|
|
5
|
+
next?: Parameter;
|
|
6
|
+
/** Default value from the registered $config primitive */
|
|
7
|
+
defaultValue?: unknown;
|
|
8
|
+
/** Current in-memory value (may be default if never saved) */
|
|
9
|
+
currentValue?: unknown;
|
|
10
|
+
/** TypeBox/JSON schema for the configuration (as JSON from API) */
|
|
11
|
+
schema?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const getStatusColor = (status: string) => {
|
|
15
|
+
switch (status) {
|
|
16
|
+
case "current":
|
|
17
|
+
return "green";
|
|
18
|
+
case "next":
|
|
19
|
+
return "blue";
|
|
20
|
+
case "future":
|
|
21
|
+
return "cyan";
|
|
22
|
+
case "expired":
|
|
23
|
+
return "gray";
|
|
24
|
+
default:
|
|
25
|
+
return "gray";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const formatJson = (obj: unknown): string => {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.stringify(obj, null, 2);
|
|
32
|
+
} catch {
|
|
33
|
+
return String(obj);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
sessions,
|
|
16
16
|
} from "alepha/api/users";
|
|
17
17
|
import { useState } from "react";
|
|
18
|
-
import type { AdminRouter } from "
|
|
18
|
+
import type { AdminRouter } from "../../AdminRouter.ts";
|
|
19
19
|
|
|
20
20
|
export interface AdminSessionsProps {
|
|
21
21
|
userRealmName?: string;
|