@alepha/ui 0.12.0 → 0.13.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/README.md +2 -30
- package/dist/admin/AdminFiles-BM6_7_5A.cjs +4 -0
- package/dist/admin/AdminFiles-BaCIMeNt.js +4 -0
- package/dist/admin/AdminFiles-CllAxb1B.js +117 -0
- package/dist/admin/AdminFiles-CllAxb1B.js.map +1 -0
- package/dist/admin/AdminFiles-DC3T8uWZ.cjs +122 -0
- package/dist/admin/AdminFiles-DC3T8uWZ.cjs.map +1 -0
- package/dist/admin/AdminJobs-BXkFtlVo.js +125 -0
- package/dist/admin/AdminJobs-BXkFtlVo.js.map +1 -0
- package/dist/admin/AdminJobs-C428qrNQ.cjs +130 -0
- package/dist/admin/AdminJobs-C428qrNQ.cjs.map +1 -0
- package/dist/admin/AdminJobs-DCPPaJ4i.cjs +4 -0
- package/dist/admin/AdminJobs-yC6DarGO.js +4 -0
- package/dist/admin/AdminLayout-Bqo4cd33.cjs +4 -0
- package/dist/admin/AdminLayout-CQpxfko6.js +4 -0
- package/dist/admin/AdminLayout-CiLlywAQ.cjs +93 -0
- package/dist/admin/AdminLayout-CiLlywAQ.cjs.map +1 -0
- package/dist/admin/AdminLayout-CtkVYk-u.js +88 -0
- package/dist/admin/AdminLayout-CtkVYk-u.js.map +1 -0
- package/dist/admin/AdminNotifications-DNUeJ-PW.cjs +44 -0
- package/dist/admin/AdminNotifications-DNUeJ-PW.cjs.map +1 -0
- package/dist/admin/AdminNotifications-DaMu1AQ4.js +4 -0
- package/dist/admin/AdminNotifications-DnnulNNV.js +40 -0
- package/dist/admin/AdminNotifications-DnnulNNV.js.map +1 -0
- package/dist/admin/AdminNotifications-ihgbKVCx.cjs +4 -0
- package/dist/admin/AdminParameters-B3hvpLpu.js +40 -0
- package/dist/admin/AdminParameters-B3hvpLpu.js.map +1 -0
- package/dist/admin/AdminParameters-U4lU1rUF.cjs +4 -0
- package/dist/admin/AdminParameters-gdf7036N.cjs +44 -0
- package/dist/admin/AdminParameters-gdf7036N.cjs.map +1 -0
- package/dist/admin/AdminParameters-prMcCgxf.js +4 -0
- package/dist/admin/AdminSessions-BF_P4lHs.cjs +128 -0
- package/dist/admin/AdminSessions-BF_P4lHs.cjs.map +1 -0
- package/dist/admin/AdminSessions-CATIU61I.cjs +4 -0
- package/dist/admin/AdminSessions-DqOXOpYR.js +4 -0
- package/dist/admin/AdminSessions-Pjdz-iZx.js +123 -0
- package/dist/admin/AdminSessions-Pjdz-iZx.js.map +1 -0
- package/dist/admin/AdminUsers-BgTL-zSY.js +4 -0
- package/dist/admin/AdminUsers-C1HsrRxn.js +104 -0
- package/dist/admin/AdminUsers-C1HsrRxn.js.map +1 -0
- package/dist/admin/AdminUsers-HqvxwNGZ.cjs +4 -0
- package/dist/admin/AdminUsers-M2uEQbp5.cjs +109 -0
- package/dist/admin/AdminUsers-M2uEQbp5.cjs.map +1 -0
- package/dist/admin/AdminVerifications-BVssbtfU.cjs +44 -0
- package/dist/admin/AdminVerifications-BVssbtfU.cjs.map +1 -0
- package/dist/admin/AdminVerifications-Df6DRgNo.js +4 -0
- package/dist/admin/AdminVerifications-DxAtcYUR.cjs +4 -0
- package/dist/admin/AdminVerifications-VMpm30mS.js +40 -0
- package/dist/admin/AdminVerifications-VMpm30mS.js.map +1 -0
- package/dist/admin/core-CzO6aavT.js +2507 -0
- package/dist/admin/core-CzO6aavT.js.map +1 -0
- package/dist/{index.cjs → admin/core-aFtK4l9I.cjs} +287 -204
- package/dist/admin/core-aFtK4l9I.cjs.map +1 -0
- package/dist/admin/index.cjs +87 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +1739 -0
- package/dist/admin/index.d.ts +1745 -0
- package/dist/admin/index.js +78 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/auth/IconGoogle-B17BTQyD.cjs +69 -0
- package/dist/auth/IconGoogle-B17BTQyD.cjs.map +1 -0
- package/dist/auth/IconGoogle-Bfmuv9Rv.js +58 -0
- package/dist/auth/IconGoogle-Bfmuv9Rv.js.map +1 -0
- package/dist/auth/Login-BTBmbnWl.cjs +181 -0
- package/dist/auth/Login-BTBmbnWl.cjs.map +1 -0
- package/dist/auth/Login-BcQOtG3v.js +5 -0
- package/dist/auth/Login-Btmd70Um.cjs +5 -0
- package/dist/auth/Login-JeXFsUf5.js +176 -0
- package/dist/auth/Login-JeXFsUf5.js.map +1 -0
- package/dist/auth/Register-CPQnvXCZ.js +318 -0
- package/dist/auth/Register-CPQnvXCZ.js.map +1 -0
- package/dist/auth/Register-CbesZal3.cjs +5 -0
- package/dist/auth/Register-DpI_JdyO.js +5 -0
- package/dist/auth/Register-HP3rP71B.cjs +323 -0
- package/dist/auth/Register-HP3rP71B.cjs.map +1 -0
- package/dist/auth/ResetPassword-B-tkzV7g.cjs +248 -0
- package/dist/auth/ResetPassword-B-tkzV7g.cjs.map +1 -0
- package/dist/auth/ResetPassword-BlK3xEpU.js +4 -0
- package/dist/auth/ResetPassword-BzUjGG_-.js +243 -0
- package/dist/auth/ResetPassword-BzUjGG_-.js.map +1 -0
- package/dist/auth/ResetPassword-W3xjOnWy.cjs +4 -0
- package/dist/auth/chunk-DhGyd7sr.js +28 -0
- package/dist/auth/core-D1MHij1j.js +1795 -0
- package/dist/auth/core-D1MHij1j.js.map +1 -0
- package/dist/auth/core-rDZ9d92K.cjs +1824 -0
- package/dist/auth/core-rDZ9d92K.cjs.map +1 -0
- package/dist/auth/index.cjs +211 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +6265 -0
- package/dist/auth/index.d.ts +6274 -0
- package/dist/auth/index.js +206 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/core/index.cjs +2620 -0
- package/dist/core/index.cjs.map +1 -0
- package/dist/core/index.d.cts +2737 -0
- package/dist/core/index.d.ts +2743 -0
- package/dist/{index.js → core/index.js} +298 -126
- package/dist/core/index.js.map +1 -0
- package/package.json +32 -14
- package/src/admin/AdminRouter.ts +58 -0
- package/src/admin/components/AdminFiles.tsx +117 -0
- package/src/admin/components/AdminJobs.tsx +158 -0
- package/src/admin/components/AdminLayout.tsx +114 -0
- package/src/admin/components/AdminNotifications.tsx +20 -0
- package/src/admin/components/AdminParameters.tsx +24 -0
- package/src/admin/components/AdminSessions.tsx +159 -0
- package/src/admin/components/AdminUsers.tsx +137 -0
- package/src/admin/components/AdminVerifications.tsx +25 -0
- package/src/admin/index.ts +29 -0
- package/src/auth/AuthI18n.ts +118 -0
- package/src/auth/AuthRouter.ts +53 -0
- package/src/auth/components/Login.tsx +193 -0
- package/src/auth/components/Register.tsx +421 -0
- package/src/auth/components/ResetPassword.tsx +259 -0
- package/src/auth/components/buttons/UserButton.tsx +118 -0
- package/src/auth/components/icons/IconGithub.tsx +21 -0
- package/src/auth/components/icons/IconGoogle.tsx +30 -0
- package/src/auth/index.ts +27 -0
- package/src/{RootRouter.ts → core/RootRouter.ts} +2 -1
- package/src/{components → core/components}/buttons/ActionButton.tsx +49 -6
- package/src/core/components/buttons/ClipboardButton.tsx +56 -0
- package/src/{components → core/components}/buttons/DarkModeButton.tsx +7 -8
- package/src/{components → core/components}/buttons/LanguageButton.tsx +2 -2
- package/src/{components → core/components}/buttons/OmnibarButton.tsx +1 -1
- package/src/{components → core/components}/dialogs/AlertDialog.tsx +1 -1
- package/src/{components → core/components}/dialogs/ConfirmDialog.tsx +1 -1
- package/src/{components → core/components}/dialogs/PromptDialog.tsx +1 -1
- package/src/{components → core/components}/form/Control.tsx +1 -0
- package/src/{components → core/components}/layout/AdminShell.tsx +38 -7
- package/src/{components → core/components}/layout/AlephaMantineProvider.tsx +12 -8
- package/src/{components → core/components}/layout/AppBar.tsx +1 -1
- package/src/{components → core/components}/layout/Omnibar.tsx +1 -1
- package/src/{components → core/components}/layout/Sidebar.tsx +29 -26
- package/src/{components → core/components}/table/DataTable.tsx +1 -1
- package/src/{constants → core/constants}/ui.ts +9 -0
- package/src/{index.ts → core/index.ts} +3 -0
- package/src/{services → core/services}/DialogService.tsx +3 -3
- package/src/{services → core/services}/ToastService.tsx +3 -1
- package/src/{utils → core/utils}/extractSchemaFields.ts +2 -8
- package/src/{utils → core/utils}/icons.tsx +5 -15
- package/src/{utils → core/utils}/parseInput.ts +34 -26
- package/dist/AlephaMantineProvider-CGpgWDt8.cjs +0 -3
- package/dist/AlephaMantineProvider-D8cHYAge.js +0 -152
- package/dist/AlephaMantineProvider-D8cHYAge.js.map +0 -1
- package/dist/AlephaMantineProvider-DuvZFAuk.cjs +0 -175
- package/dist/AlephaMantineProvider-DuvZFAuk.cjs.map +0 -1
- package/dist/AlephaMantineProvider-twBqV4IO.js +0 -3
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -821
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts +0 -821
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- /package/src/{components → core/components}/buttons/BurgerButton.tsx +0 -0
- /package/src/{components → core/components}/buttons/ToggleSidebarButton.tsx +0 -0
- /package/src/{components → core/components}/data/JsonViewer.tsx +0 -0
- /package/src/{components → core/components}/form/ControlDate.tsx +0 -0
- /package/src/{components → core/components}/form/ControlNumber.tsx +0 -0
- /package/src/{components → core/components}/form/ControlQueryBuilder.tsx +0 -0
- /package/src/{components → core/components}/form/ControlSelect.tsx +0 -0
- /package/src/{components → core/components}/form/TypeForm.tsx +0 -0
- /package/src/{hooks → core/hooks}/useDialog.ts +0 -0
- /package/src/{hooks → core/hooks}/useToast.ts +0 -0
- /package/src/{utils → core/utils}/string.ts +0 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { useClient } from "@alepha/react";
|
|
2
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { DataTable, Flex, Text } from "@alepha/ui";
|
|
4
|
+
import { Badge } from "@mantine/core";
|
|
5
|
+
import { type Page, t } from "alepha";
|
|
6
|
+
import { type FileController, type FileEntity, files } from "alepha/api/files";
|
|
7
|
+
|
|
8
|
+
const AdminFiles = () => {
|
|
9
|
+
const client = useClient<FileController>();
|
|
10
|
+
const { l } = useI18n();
|
|
11
|
+
|
|
12
|
+
const filters = t.object({
|
|
13
|
+
bucket: t.optional(t.string()),
|
|
14
|
+
name: t.optional(
|
|
15
|
+
t.string({
|
|
16
|
+
$control: {
|
|
17
|
+
query: t.pick(files.schema, ["name", "bucket", "mimeType"]),
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const formatFileSize = (bytes: number) => {
|
|
24
|
+
if (bytes === 0) return "0 B";
|
|
25
|
+
const k = 1024;
|
|
26
|
+
const sizes = ["B", "KB", "MB", "GB"];
|
|
27
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
28
|
+
return `${Number.parseFloat((bytes / k ** i).toFixed(1))} ${sizes[i]}`;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<Flex flex={1}>
|
|
33
|
+
<DataTable<FileEntity, typeof filters>
|
|
34
|
+
submitOnInit
|
|
35
|
+
defaultSize={10}
|
|
36
|
+
typeFormProps={{
|
|
37
|
+
skipSubmitButton: true,
|
|
38
|
+
columns: 3,
|
|
39
|
+
}}
|
|
40
|
+
tableProps={{
|
|
41
|
+
horizontalSpacing: "xs",
|
|
42
|
+
verticalSpacing: "xs",
|
|
43
|
+
}}
|
|
44
|
+
onFilterChange={(key, _value, form) => {
|
|
45
|
+
if (key === "name" || key === "bucket") {
|
|
46
|
+
return form.submit();
|
|
47
|
+
}
|
|
48
|
+
}}
|
|
49
|
+
filters={filters}
|
|
50
|
+
items={async (filters) => {
|
|
51
|
+
const response = await client.findFiles({
|
|
52
|
+
query: filters,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return response as Page<FileEntity>;
|
|
56
|
+
}}
|
|
57
|
+
columns={{
|
|
58
|
+
name: {
|
|
59
|
+
label: "Name",
|
|
60
|
+
value: (item) => (
|
|
61
|
+
<Text size="sm" fw={500} lineClamp={1}>
|
|
62
|
+
{item.name}
|
|
63
|
+
</Text>
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
bucket: {
|
|
67
|
+
label: "Bucket",
|
|
68
|
+
fit: true,
|
|
69
|
+
value: (item) => (
|
|
70
|
+
<Badge size="sm" variant="light" color="blue">
|
|
71
|
+
{item.bucket}
|
|
72
|
+
</Badge>
|
|
73
|
+
),
|
|
74
|
+
},
|
|
75
|
+
mimeType: {
|
|
76
|
+
label: "Type",
|
|
77
|
+
fit: true,
|
|
78
|
+
value: (item) => (
|
|
79
|
+
<Text size="xs" c="dimmed">
|
|
80
|
+
{item.mimeType}
|
|
81
|
+
</Text>
|
|
82
|
+
),
|
|
83
|
+
},
|
|
84
|
+
size: {
|
|
85
|
+
label: "Size",
|
|
86
|
+
fit: true,
|
|
87
|
+
value: (item) => (
|
|
88
|
+
<Text size="xs" c="dimmed">
|
|
89
|
+
{formatFileSize(item.size)}
|
|
90
|
+
</Text>
|
|
91
|
+
),
|
|
92
|
+
},
|
|
93
|
+
creatorName: {
|
|
94
|
+
label: "Creator",
|
|
95
|
+
fit: true,
|
|
96
|
+
value: (item) => (
|
|
97
|
+
<Text size="xs" c="dimmed">
|
|
98
|
+
{item.creatorName || "-"}
|
|
99
|
+
</Text>
|
|
100
|
+
),
|
|
101
|
+
},
|
|
102
|
+
createdAt: {
|
|
103
|
+
label: "Created",
|
|
104
|
+
fit: true,
|
|
105
|
+
value: (item) => (
|
|
106
|
+
<Text size="xs" c="dimmed">
|
|
107
|
+
{l(item.createdAt, { date: "fromNow" })}
|
|
108
|
+
</Text>
|
|
109
|
+
),
|
|
110
|
+
},
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
</Flex>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default AdminFiles;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { useClient } from "@alepha/react";
|
|
2
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { DataTable, Flex, Text } from "@alepha/ui";
|
|
4
|
+
import { Badge } from "@mantine/core";
|
|
5
|
+
import {
|
|
6
|
+
IconCheck,
|
|
7
|
+
IconClock,
|
|
8
|
+
IconPlayerPlay,
|
|
9
|
+
IconX,
|
|
10
|
+
} from "@tabler/icons-react";
|
|
11
|
+
import { type Page, t } from "alepha";
|
|
12
|
+
import {
|
|
13
|
+
type JobController,
|
|
14
|
+
type JobExecutionEntity,
|
|
15
|
+
jobExecutions,
|
|
16
|
+
} from "alepha/api/jobs";
|
|
17
|
+
|
|
18
|
+
const AdminJobs = () => {
|
|
19
|
+
const client = useClient<JobController>();
|
|
20
|
+
const { l } = useI18n();
|
|
21
|
+
|
|
22
|
+
const filters = t.object({
|
|
23
|
+
job: t.optional(
|
|
24
|
+
t.string({
|
|
25
|
+
$control: {
|
|
26
|
+
query: t.pick(jobExecutions.schema, ["job"]),
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
),
|
|
30
|
+
status: t.optional(t.enum(["STARTED", "FAILED", "COMPLETED"])),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const getStatusColor = (status: string) => {
|
|
34
|
+
switch (status) {
|
|
35
|
+
case "COMPLETED":
|
|
36
|
+
return "green";
|
|
37
|
+
case "FAILED":
|
|
38
|
+
return "red";
|
|
39
|
+
case "STARTED":
|
|
40
|
+
return "blue";
|
|
41
|
+
default:
|
|
42
|
+
return "gray";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const getStatusIcon = (status: string) => {
|
|
47
|
+
switch (status) {
|
|
48
|
+
case "COMPLETED":
|
|
49
|
+
return <IconCheck size={12} />;
|
|
50
|
+
case "FAILED":
|
|
51
|
+
return <IconX size={12} />;
|
|
52
|
+
case "STARTED":
|
|
53
|
+
return <IconPlayerPlay size={12} />;
|
|
54
|
+
default:
|
|
55
|
+
return <IconClock size={12} />;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const formatDuration = (
|
|
60
|
+
start: Date | string,
|
|
61
|
+
end?: Date | string | null,
|
|
62
|
+
): string => {
|
|
63
|
+
const startTime = new Date(start).getTime();
|
|
64
|
+
const endTime = end ? new Date(end).getTime() : Date.now();
|
|
65
|
+
const duration = endTime - startTime;
|
|
66
|
+
|
|
67
|
+
if (duration < 1000) return `${duration}ms`;
|
|
68
|
+
if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`;
|
|
69
|
+
return `${Math.floor(duration / 60000)}m ${Math.floor((duration % 60000) / 1000)}s`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<Flex flex={1}>
|
|
74
|
+
<DataTable<JobExecutionEntity, typeof filters>
|
|
75
|
+
submitOnInit
|
|
76
|
+
defaultSize={10}
|
|
77
|
+
typeFormProps={{
|
|
78
|
+
skipSubmitButton: true,
|
|
79
|
+
columns: 3,
|
|
80
|
+
}}
|
|
81
|
+
tableProps={{
|
|
82
|
+
horizontalSpacing: "xs",
|
|
83
|
+
verticalSpacing: "xs",
|
|
84
|
+
}}
|
|
85
|
+
onFilterChange={(key, _value, form) => {
|
|
86
|
+
if (key === "job" || key === "status") {
|
|
87
|
+
return form.submit();
|
|
88
|
+
}
|
|
89
|
+
}}
|
|
90
|
+
filters={filters}
|
|
91
|
+
items={async (filters) => {
|
|
92
|
+
const response = await client.getJobExecutions({
|
|
93
|
+
query: filters,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
return response as Page<JobExecutionEntity>;
|
|
97
|
+
}}
|
|
98
|
+
columns={{
|
|
99
|
+
job: {
|
|
100
|
+
label: "Job",
|
|
101
|
+
value: (item) => (
|
|
102
|
+
<Text size="sm" fw={500}>
|
|
103
|
+
{item.job}
|
|
104
|
+
</Text>
|
|
105
|
+
),
|
|
106
|
+
},
|
|
107
|
+
status: {
|
|
108
|
+
label: "Status",
|
|
109
|
+
fit: true,
|
|
110
|
+
value: (item) => (
|
|
111
|
+
<Badge
|
|
112
|
+
size="sm"
|
|
113
|
+
variant="light"
|
|
114
|
+
color={getStatusColor(item.status)}
|
|
115
|
+
leftSection={getStatusIcon(item.status)}
|
|
116
|
+
>
|
|
117
|
+
{item.status}
|
|
118
|
+
</Badge>
|
|
119
|
+
),
|
|
120
|
+
},
|
|
121
|
+
duration: {
|
|
122
|
+
label: "Duration",
|
|
123
|
+
fit: true,
|
|
124
|
+
value: (item) => (
|
|
125
|
+
<Text size="xs" c="dimmed" ff="monospace">
|
|
126
|
+
{formatDuration(item.createdAt, item.finishedAt)}
|
|
127
|
+
</Text>
|
|
128
|
+
),
|
|
129
|
+
},
|
|
130
|
+
error: {
|
|
131
|
+
label: "Error",
|
|
132
|
+
value: (item) =>
|
|
133
|
+
item.error ? (
|
|
134
|
+
<Text size="xs" c="red" lineClamp={1}>
|
|
135
|
+
{item.error}
|
|
136
|
+
</Text>
|
|
137
|
+
) : (
|
|
138
|
+
<Text size="xs" c="dimmed">
|
|
139
|
+
-
|
|
140
|
+
</Text>
|
|
141
|
+
),
|
|
142
|
+
},
|
|
143
|
+
createdAt: {
|
|
144
|
+
label: "Started",
|
|
145
|
+
fit: true,
|
|
146
|
+
value: (item) => (
|
|
147
|
+
<Text size="xs" c="dimmed">
|
|
148
|
+
{l(item.createdAt, { date: "fromNow" })}
|
|
149
|
+
</Text>
|
|
150
|
+
),
|
|
151
|
+
},
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
</Flex>
|
|
155
|
+
);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export default AdminJobs;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { NestedView, useRouter } from "@alepha/react";
|
|
2
|
+
import { AdminShell, AlephaMantineProvider } from "@alepha/ui";
|
|
3
|
+
import { Flex } from "@mantine/core";
|
|
4
|
+
import {
|
|
5
|
+
IconChecklist,
|
|
6
|
+
IconFileDatabase,
|
|
7
|
+
IconJumpRope,
|
|
8
|
+
IconMail,
|
|
9
|
+
IconSettings,
|
|
10
|
+
IconShield,
|
|
11
|
+
IconTruckDelivery,
|
|
12
|
+
IconUser,
|
|
13
|
+
} from "@tabler/icons-react";
|
|
14
|
+
import type { AdminRouter } from "../AdminRouter.ts";
|
|
15
|
+
|
|
16
|
+
const AdminLayout = () => {
|
|
17
|
+
const router = useRouter<AdminRouter>();
|
|
18
|
+
return (
|
|
19
|
+
<AlephaMantineProvider>
|
|
20
|
+
<AdminShell
|
|
21
|
+
appShellMainProps={{
|
|
22
|
+
bg: "var(--alepha-background)",
|
|
23
|
+
}}
|
|
24
|
+
appShellHeaderProps={{
|
|
25
|
+
bg: "var(--alepha-background)",
|
|
26
|
+
}}
|
|
27
|
+
appShellNavbarProps={{
|
|
28
|
+
bg: "var(--alepha-background)",
|
|
29
|
+
}}
|
|
30
|
+
appShellProps={{
|
|
31
|
+
withBorder: false,
|
|
32
|
+
}}
|
|
33
|
+
appBarProps={{
|
|
34
|
+
items: [
|
|
35
|
+
{
|
|
36
|
+
type: "search",
|
|
37
|
+
position: "center",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: "dark",
|
|
41
|
+
position: "right",
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
}}
|
|
45
|
+
sidebarProps={{
|
|
46
|
+
menu: [
|
|
47
|
+
{
|
|
48
|
+
type: "section",
|
|
49
|
+
label: "Management",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
icon: IconUser,
|
|
53
|
+
label: "Users",
|
|
54
|
+
href: router.path("adminUsers"),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
icon: IconMail,
|
|
58
|
+
label: "Notifications",
|
|
59
|
+
href: router.path("adminNotifications"),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
icon: IconShield,
|
|
63
|
+
label: "Sessions",
|
|
64
|
+
href: router.path("adminSessions"),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
icon: IconChecklist,
|
|
68
|
+
label: "Verifications",
|
|
69
|
+
href: router.path("adminVerifications"),
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
type: "section",
|
|
73
|
+
label: "System",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
label: "Jobs",
|
|
77
|
+
href: router.path("adminJobs"),
|
|
78
|
+
icon: IconTruckDelivery,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
label: "Workflows",
|
|
82
|
+
href: router.path("adminWorkflows"),
|
|
83
|
+
icon: IconJumpRope,
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
label: "Parameters",
|
|
87
|
+
href: router.path("adminParameters"),
|
|
88
|
+
icon: IconSettings,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: "Files",
|
|
92
|
+
href: router.path("adminFiles"),
|
|
93
|
+
icon: IconFileDatabase,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
}}
|
|
97
|
+
>
|
|
98
|
+
<Flex
|
|
99
|
+
flex={1}
|
|
100
|
+
p={"lg"}
|
|
101
|
+
mt={-16}
|
|
102
|
+
ml={-16}
|
|
103
|
+
bg={"var(--alepha-surface)"}
|
|
104
|
+
bdrs={"lg"}
|
|
105
|
+
bd={"1px solid var(--alepha-border)"}
|
|
106
|
+
>
|
|
107
|
+
<NestedView />
|
|
108
|
+
</Flex>
|
|
109
|
+
</AdminShell>
|
|
110
|
+
</AlephaMantineProvider>
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export default AdminLayout;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Flex, Text } from "@alepha/ui";
|
|
2
|
+
import { Stack } from "@mantine/core";
|
|
3
|
+
import { IconBell } from "@tabler/icons-react";
|
|
4
|
+
|
|
5
|
+
const AdminNotifications = () => {
|
|
6
|
+
return (
|
|
7
|
+
<Flex flex={1} justify="center" align="center">
|
|
8
|
+
<Stack align="center" gap="xs">
|
|
9
|
+
<IconBell size={48} stroke={1.5} color="var(--mantine-color-dimmed)" />
|
|
10
|
+
<Text c="dimmed">Notification Center</Text>
|
|
11
|
+
<Text size="xs" c="dimmed" ta="center" maw={400}>
|
|
12
|
+
Notifications are processed through the queue system. Email and SMS
|
|
13
|
+
notifications are sent asynchronously using the configured providers.
|
|
14
|
+
</Text>
|
|
15
|
+
</Stack>
|
|
16
|
+
</Flex>
|
|
17
|
+
);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default AdminNotifications;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Flex, Text } from "@alepha/ui";
|
|
2
|
+
import { Stack } from "@mantine/core";
|
|
3
|
+
import { IconSettings } from "@tabler/icons-react";
|
|
4
|
+
|
|
5
|
+
const AdminParameters = () => {
|
|
6
|
+
return (
|
|
7
|
+
<Flex flex={1} justify="center" align="center">
|
|
8
|
+
<Stack align="center" gap="xs">
|
|
9
|
+
<IconSettings
|
|
10
|
+
size={48}
|
|
11
|
+
stroke={1.5}
|
|
12
|
+
color="var(--mantine-color-dimmed)"
|
|
13
|
+
/>
|
|
14
|
+
<Text c="dimmed">Parameter Management</Text>
|
|
15
|
+
<Text size="xs" c="dimmed" ta="center" maw={400}>
|
|
16
|
+
Application parameters and configuration settings. Define parameters
|
|
17
|
+
using the $config descriptor to manage dynamic application settings.
|
|
18
|
+
</Text>
|
|
19
|
+
</Stack>
|
|
20
|
+
</Flex>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default AdminParameters;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { useClient } from "@alepha/react";
|
|
2
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { DataTable, Flex, Text } from "@alepha/ui";
|
|
4
|
+
import { Badge, Group } from "@mantine/core";
|
|
5
|
+
import {
|
|
6
|
+
IconDeviceDesktop,
|
|
7
|
+
IconDeviceMobile,
|
|
8
|
+
IconDeviceTablet,
|
|
9
|
+
} from "@tabler/icons-react";
|
|
10
|
+
import { type Page, t } from "alepha";
|
|
11
|
+
import {
|
|
12
|
+
type SessionController,
|
|
13
|
+
type SessionEntity,
|
|
14
|
+
sessions,
|
|
15
|
+
} from "alepha/api/users";
|
|
16
|
+
|
|
17
|
+
export interface AdminSessionsProps {
|
|
18
|
+
userRealmName?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const AdminSessions = (props: AdminSessionsProps) => {
|
|
22
|
+
const client = useClient<SessionController>();
|
|
23
|
+
const { l } = useI18n();
|
|
24
|
+
|
|
25
|
+
const filters = t.object({
|
|
26
|
+
userId: t.optional(
|
|
27
|
+
t.uuid({
|
|
28
|
+
$control: {
|
|
29
|
+
query: t.pick(sessions.schema, ["userId"]),
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const getDeviceIcon = (device?: string) => {
|
|
36
|
+
switch (device) {
|
|
37
|
+
case "MOBILE":
|
|
38
|
+
return <IconDeviceMobile size={14} />;
|
|
39
|
+
case "TABLET":
|
|
40
|
+
return <IconDeviceTablet size={14} />;
|
|
41
|
+
default:
|
|
42
|
+
return <IconDeviceDesktop size={14} />;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const isExpired = (expiresAt: Date | string) => {
|
|
47
|
+
return new Date(expiresAt) < new Date();
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<Flex flex={1}>
|
|
52
|
+
<DataTable<SessionEntity, typeof filters>
|
|
53
|
+
submitOnInit
|
|
54
|
+
defaultSize={10}
|
|
55
|
+
typeFormProps={{
|
|
56
|
+
skipSubmitButton: true,
|
|
57
|
+
columns: 3,
|
|
58
|
+
}}
|
|
59
|
+
tableProps={{
|
|
60
|
+
horizontalSpacing: "xs",
|
|
61
|
+
verticalSpacing: "xs",
|
|
62
|
+
}}
|
|
63
|
+
onFilterChange={(key, _value, form) => {
|
|
64
|
+
if (key === "userId") {
|
|
65
|
+
return form.submit();
|
|
66
|
+
}
|
|
67
|
+
}}
|
|
68
|
+
filters={filters}
|
|
69
|
+
tableTrProps={(item) => {
|
|
70
|
+
if (isExpired(item.expiresAt)) {
|
|
71
|
+
return {
|
|
72
|
+
opacity: 0.5,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
return {};
|
|
76
|
+
}}
|
|
77
|
+
items={async (filters) => {
|
|
78
|
+
const response = await client.findSessions({
|
|
79
|
+
query: {
|
|
80
|
+
...filters,
|
|
81
|
+
userRealmName: props.userRealmName,
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
return response as Page<SessionEntity>;
|
|
86
|
+
}}
|
|
87
|
+
columns={{
|
|
88
|
+
userId: {
|
|
89
|
+
label: "User ID",
|
|
90
|
+
value: (item) => (
|
|
91
|
+
<Text size="xs" ff="monospace">
|
|
92
|
+
{item.userId.slice(0, 8)}...
|
|
93
|
+
</Text>
|
|
94
|
+
),
|
|
95
|
+
},
|
|
96
|
+
userAgent: {
|
|
97
|
+
label: "Device",
|
|
98
|
+
fit: true,
|
|
99
|
+
value: (item) => (
|
|
100
|
+
<Group gap={4}>
|
|
101
|
+
{item.userAgent ? (
|
|
102
|
+
<>
|
|
103
|
+
<Badge
|
|
104
|
+
size="xs"
|
|
105
|
+
variant="light"
|
|
106
|
+
leftSection={getDeviceIcon(item.userAgent.device)}
|
|
107
|
+
>
|
|
108
|
+
{item.userAgent.device}
|
|
109
|
+
</Badge>
|
|
110
|
+
<Text size="xs" c="dimmed">
|
|
111
|
+
{item.userAgent.browser} / {item.userAgent.os}
|
|
112
|
+
</Text>
|
|
113
|
+
</>
|
|
114
|
+
) : (
|
|
115
|
+
<Text size="xs" c="dimmed">
|
|
116
|
+
-
|
|
117
|
+
</Text>
|
|
118
|
+
)}
|
|
119
|
+
</Group>
|
|
120
|
+
),
|
|
121
|
+
},
|
|
122
|
+
ip: {
|
|
123
|
+
label: "IP",
|
|
124
|
+
fit: true,
|
|
125
|
+
value: (item) => (
|
|
126
|
+
<Text size="xs" ff="monospace" c="dimmed">
|
|
127
|
+
{item.ip || "-"}
|
|
128
|
+
</Text>
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
expiresAt: {
|
|
132
|
+
label: "Status",
|
|
133
|
+
fit: true,
|
|
134
|
+
value: (item) => (
|
|
135
|
+
<Badge
|
|
136
|
+
size="sm"
|
|
137
|
+
variant="light"
|
|
138
|
+
color={isExpired(item.expiresAt) ? "red" : "green"}
|
|
139
|
+
>
|
|
140
|
+
{isExpired(item.expiresAt) ? "Expired" : "Active"}
|
|
141
|
+
</Badge>
|
|
142
|
+
),
|
|
143
|
+
},
|
|
144
|
+
createdAt: {
|
|
145
|
+
label: "Created",
|
|
146
|
+
fit: true,
|
|
147
|
+
value: (item) => (
|
|
148
|
+
<Text size="xs" c="dimmed">
|
|
149
|
+
{l(item.createdAt, { date: "fromNow" })}
|
|
150
|
+
</Text>
|
|
151
|
+
),
|
|
152
|
+
},
|
|
153
|
+
}}
|
|
154
|
+
/>
|
|
155
|
+
</Flex>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export default AdminSessions;
|