@alepha/ui 0.13.6 → 0.13.8
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 +2700 -1178
- 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 +605 -532
- package/dist/auth/index.js +26 -18
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +425 -155
- package/dist/core/index.js +1751 -1369
- package/dist/core/index.js.map +1 -1
- package/package.json +23 -20
- 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,183 @@
|
|
|
1
|
+
import { useClient, useRouterState } from "@alepha/react";
|
|
2
|
+
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
+
import { DataTable, Flex, Text } from "@alepha/ui";
|
|
4
|
+
import { Badge, Group, Tooltip } from "@mantine/core";
|
|
5
|
+
import {
|
|
6
|
+
IconAlertTriangle,
|
|
7
|
+
IconCheck,
|
|
8
|
+
IconInfoCircle,
|
|
9
|
+
IconX,
|
|
10
|
+
} from "@tabler/icons-react";
|
|
11
|
+
import { type Page, t } from "alepha";
|
|
12
|
+
import type { AuditController, AuditEntity } from "alepha/api/audits";
|
|
13
|
+
|
|
14
|
+
export interface AdminUserAuditsProps {
|
|
15
|
+
userRealmName?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const getSeverityColor = (severity: string) => {
|
|
19
|
+
switch (severity) {
|
|
20
|
+
case "critical":
|
|
21
|
+
return "red";
|
|
22
|
+
case "warning":
|
|
23
|
+
return "yellow";
|
|
24
|
+
default:
|
|
25
|
+
return "blue";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const getSeverityIcon = (severity: string) => {
|
|
30
|
+
switch (severity) {
|
|
31
|
+
case "critical":
|
|
32
|
+
return <IconAlertTriangle size={12} />;
|
|
33
|
+
case "warning":
|
|
34
|
+
return <IconAlertTriangle size={12} />;
|
|
35
|
+
default:
|
|
36
|
+
return <IconInfoCircle size={12} />;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const AdminUserAudits = (_props: AdminUserAuditsProps) => {
|
|
41
|
+
const state = useRouterState();
|
|
42
|
+
const client = useClient<AuditController>();
|
|
43
|
+
const { l } = useI18n();
|
|
44
|
+
const userId = state.params.userId as string;
|
|
45
|
+
|
|
46
|
+
const filters = t.object({
|
|
47
|
+
type: t.optional(t.text()),
|
|
48
|
+
action: t.optional(t.text()),
|
|
49
|
+
severity: t.optional(t.enum(["info", "warning", "critical"])),
|
|
50
|
+
success: t.optional(t.boolean()),
|
|
51
|
+
from: t.optional(t.datetime()),
|
|
52
|
+
to: t.optional(t.datetime()),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<Flex flex={1} direction="column">
|
|
57
|
+
<DataTable<AuditEntity, typeof filters>
|
|
58
|
+
submitOnInit
|
|
59
|
+
defaultSize={15}
|
|
60
|
+
typeFormProps={{
|
|
61
|
+
skipSubmitButton: true,
|
|
62
|
+
columns: 4,
|
|
63
|
+
}}
|
|
64
|
+
tableProps={{
|
|
65
|
+
horizontalSpacing: "xs",
|
|
66
|
+
verticalSpacing: "xs",
|
|
67
|
+
striped: false,
|
|
68
|
+
highlightOnHover: true,
|
|
69
|
+
}}
|
|
70
|
+
filters={filters}
|
|
71
|
+
items={async (query) => {
|
|
72
|
+
const response = await client.findByUser({
|
|
73
|
+
params: { userId },
|
|
74
|
+
query,
|
|
75
|
+
});
|
|
76
|
+
return response as Page<AuditEntity>;
|
|
77
|
+
}}
|
|
78
|
+
columns={{
|
|
79
|
+
type: {
|
|
80
|
+
label: "Type",
|
|
81
|
+
fit: true,
|
|
82
|
+
value: (item) => (
|
|
83
|
+
<Badge size="sm" variant="light" color="grape">
|
|
84
|
+
{item.type}
|
|
85
|
+
</Badge>
|
|
86
|
+
),
|
|
87
|
+
},
|
|
88
|
+
action: {
|
|
89
|
+
label: "Action",
|
|
90
|
+
fit: true,
|
|
91
|
+
value: (item) => (
|
|
92
|
+
<Badge size="sm" variant="outline">
|
|
93
|
+
{item.action}
|
|
94
|
+
</Badge>
|
|
95
|
+
),
|
|
96
|
+
},
|
|
97
|
+
severity: {
|
|
98
|
+
label: "Severity",
|
|
99
|
+
fit: true,
|
|
100
|
+
value: (item) => (
|
|
101
|
+
<Badge
|
|
102
|
+
size="sm"
|
|
103
|
+
variant="light"
|
|
104
|
+
color={getSeverityColor(item.severity)}
|
|
105
|
+
leftSection={getSeverityIcon(item.severity)}
|
|
106
|
+
>
|
|
107
|
+
{item.severity}
|
|
108
|
+
</Badge>
|
|
109
|
+
),
|
|
110
|
+
},
|
|
111
|
+
description: {
|
|
112
|
+
label: "Description",
|
|
113
|
+
value: (item) => (
|
|
114
|
+
<Text size="sm" lineClamp={1}>
|
|
115
|
+
{item.description || "-"}
|
|
116
|
+
</Text>
|
|
117
|
+
),
|
|
118
|
+
},
|
|
119
|
+
resource: {
|
|
120
|
+
label: "Resource",
|
|
121
|
+
fit: true,
|
|
122
|
+
value: (item) =>
|
|
123
|
+
item.resourceType ? (
|
|
124
|
+
<Tooltip label={item.resourceId || "N/A"}>
|
|
125
|
+
<Badge size="xs" variant="dot" color="gray">
|
|
126
|
+
{item.resourceType}
|
|
127
|
+
</Badge>
|
|
128
|
+
</Tooltip>
|
|
129
|
+
) : (
|
|
130
|
+
<Text size="xs" c="dimmed">
|
|
131
|
+
-
|
|
132
|
+
</Text>
|
|
133
|
+
),
|
|
134
|
+
},
|
|
135
|
+
success: {
|
|
136
|
+
label: "Status",
|
|
137
|
+
fit: true,
|
|
138
|
+
value: (item) =>
|
|
139
|
+
item.success ? (
|
|
140
|
+
<Group gap={4}>
|
|
141
|
+
<IconCheck size={14} color="var(--mantine-color-green-6)" />
|
|
142
|
+
<Text size="xs" c="green">
|
|
143
|
+
Success
|
|
144
|
+
</Text>
|
|
145
|
+
</Group>
|
|
146
|
+
) : (
|
|
147
|
+
<Tooltip label={item.errorMessage || "Failed"}>
|
|
148
|
+
<Group gap={4}>
|
|
149
|
+
<IconX size={14} color="var(--mantine-color-red-6)" />
|
|
150
|
+
<Text size="xs" c="red">
|
|
151
|
+
Failed
|
|
152
|
+
</Text>
|
|
153
|
+
</Group>
|
|
154
|
+
</Tooltip>
|
|
155
|
+
),
|
|
156
|
+
},
|
|
157
|
+
ipAddress: {
|
|
158
|
+
label: "IP",
|
|
159
|
+
fit: true,
|
|
160
|
+
value: (item) => (
|
|
161
|
+
<Text size="xs" c="dimmed" ff="monospace">
|
|
162
|
+
{item.ipAddress || "-"}
|
|
163
|
+
</Text>
|
|
164
|
+
),
|
|
165
|
+
},
|
|
166
|
+
createdAt: {
|
|
167
|
+
label: "Time",
|
|
168
|
+
fit: true,
|
|
169
|
+
value: (item) => (
|
|
170
|
+
<Tooltip label={l(item.createdAt, { date: "medium" })}>
|
|
171
|
+
<Text size="xs" c="dimmed">
|
|
172
|
+
{l(item.createdAt, { date: "fromNow" })}
|
|
173
|
+
</Text>
|
|
174
|
+
</Tooltip>
|
|
175
|
+
),
|
|
176
|
+
},
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
</Flex>
|
|
180
|
+
);
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export default AdminUserAudits;
|
|
@@ -4,7 +4,7 @@ import { ActionButton, Control, Flex } from "@alepha/ui";
|
|
|
4
4
|
import { Card, Stack, Text } from "@mantine/core";
|
|
5
5
|
import { t } from "alepha";
|
|
6
6
|
import type { UserController } from "alepha/api/users";
|
|
7
|
-
import type { AdminRouter } from "
|
|
7
|
+
import type { AdminRouter } from "../../AdminRouter.ts";
|
|
8
8
|
|
|
9
9
|
export interface AdminUserCreateProps {
|
|
10
10
|
userRealmName?: string;
|
|
@@ -9,7 +9,7 @@ import { Avatar, Badge, Card, Group, Loader, Stack, Tabs } from "@mantine/core";
|
|
|
9
9
|
import { IconDevices, IconSettings, IconUser } from "@tabler/icons-react";
|
|
10
10
|
import type { UserController, UserEntity } from "alepha/api/users";
|
|
11
11
|
import { useEffect, useState } from "react";
|
|
12
|
-
import type { AdminRouter } from "
|
|
12
|
+
import type { AdminRouter } from "../../AdminRouter.ts";
|
|
13
13
|
|
|
14
14
|
export interface AdminUserLayoutProps {
|
|
15
15
|
userRealmName?: string;
|
|
@@ -112,7 +112,6 @@ const AdminUserLayout = (props: AdminUserLayoutProps) => {
|
|
|
112
112
|
<Tabs value={activeTab}>
|
|
113
113
|
<Tabs.List>
|
|
114
114
|
<ActionButton
|
|
115
|
-
variant="subtle"
|
|
116
115
|
href={detailsPath}
|
|
117
116
|
leftSection={<IconUser size={16} />}
|
|
118
117
|
c={activeTab === "details" ? undefined : "dimmed"}
|
|
@@ -128,7 +127,6 @@ const AdminUserLayout = (props: AdminUserLayoutProps) => {
|
|
|
128
127
|
Details
|
|
129
128
|
</ActionButton>
|
|
130
129
|
<ActionButton
|
|
131
|
-
variant="subtle"
|
|
132
130
|
href={sessionsPath}
|
|
133
131
|
leftSection={<IconDevices size={16} />}
|
|
134
132
|
c={activeTab === "sessions" ? undefined : "dimmed"}
|
|
@@ -144,7 +142,6 @@ const AdminUserLayout = (props: AdminUserLayoutProps) => {
|
|
|
144
142
|
Sessions
|
|
145
143
|
</ActionButton>
|
|
146
144
|
<ActionButton
|
|
147
|
-
variant="subtle"
|
|
148
145
|
href={settingsPath}
|
|
149
146
|
leftSection={<IconSettings size={16} />}
|
|
150
147
|
c={activeTab === "settings" ? undefined : "dimmed"}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from "@tabler/icons-react";
|
|
10
10
|
import type { UserController, UserEntity } from "alepha/api/users";
|
|
11
11
|
import { useEffect, useState } from "react";
|
|
12
|
-
import type { AdminRouter } from "
|
|
12
|
+
import type { AdminRouter } from "../../AdminRouter.ts";
|
|
13
13
|
|
|
14
14
|
export interface AdminUserSettingsProps {
|
|
15
15
|
userRealmName?: string;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useClient, useRouter } from "@alepha/react";
|
|
2
2
|
import { useI18n } from "@alepha/react/i18n";
|
|
3
|
-
import {
|
|
3
|
+
import { DataTable, Text } from "@alepha/ui";
|
|
4
4
|
import { Badge, Flex, Group } from "@mantine/core";
|
|
5
|
-
import { IconCheck,
|
|
5
|
+
import { IconCheck, IconUsersPlus, IconX } from "@tabler/icons-react";
|
|
6
6
|
import { type Page, t } from "alepha";
|
|
7
7
|
import { type UserController, type UserEntity, users } from "alepha/api/users";
|
|
8
|
-
import type { AdminRouter } from "
|
|
8
|
+
import type { AdminRouter } from "../../AdminRouter.ts";
|
|
9
9
|
|
|
10
10
|
export interface AdminUsersProps {
|
|
11
11
|
userRealmName?: string;
|
|
@@ -28,17 +28,15 @@ const AdminUsers = (props: AdminUsersProps) => {
|
|
|
28
28
|
|
|
29
29
|
return (
|
|
30
30
|
<Flex flex={1} direction="column">
|
|
31
|
-
<Flex justify="flex-end" p="md" pb={0}>
|
|
32
|
-
<ActionButton
|
|
33
|
-
leftSection={<IconPlus size={16} />}
|
|
34
|
-
href={router.path("adminUserCreate")}
|
|
35
|
-
>
|
|
36
|
-
Create User
|
|
37
|
-
</ActionButton>
|
|
38
|
-
</Flex>
|
|
39
|
-
|
|
40
31
|
<DataTable<UserEntity, typeof filters>
|
|
41
32
|
submitOnInit
|
|
33
|
+
actions={[
|
|
34
|
+
{
|
|
35
|
+
icon: IconUsersPlus,
|
|
36
|
+
href: router.path("adminUserCreate"),
|
|
37
|
+
label: "Create User",
|
|
38
|
+
},
|
|
39
|
+
]}
|
|
42
40
|
defaultSize={10}
|
|
43
41
|
typeFormProps={{
|
|
44
42
|
skipSubmitButton: true,
|
package/src/admin/index.ts
CHANGED
|
@@ -2,26 +2,35 @@ import { AlephaUI } from "@alepha/ui";
|
|
|
2
2
|
import { AlephaUIAuth } from "@alepha/ui/auth";
|
|
3
3
|
import { $module } from "alepha";
|
|
4
4
|
import { AdminRouter } from "./AdminRouter.ts";
|
|
5
|
-
import { AdminSidebar } from "./AdminSidebar.ts";
|
|
6
5
|
import { MainRouter } from "./MainRouter.ts";
|
|
7
6
|
|
|
8
7
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
9
8
|
|
|
10
9
|
export { AdminRouter } from "./AdminRouter.ts";
|
|
11
|
-
|
|
12
|
-
export { default as AdminFiles } from "./components/AdminFiles.tsx";
|
|
13
|
-
export { default as AdminJobs } from "./components/AdminJobs.tsx";
|
|
10
|
+
// Layout
|
|
14
11
|
export { default as AdminLayout } from "./components/AdminLayout.tsx";
|
|
15
|
-
|
|
16
|
-
export { default as
|
|
17
|
-
|
|
18
|
-
export { default as
|
|
19
|
-
|
|
20
|
-
export { default as
|
|
21
|
-
|
|
22
|
-
export { default as
|
|
23
|
-
|
|
24
|
-
export { default as
|
|
12
|
+
// Audits
|
|
13
|
+
export { default as AdminAudits } from "./components/audits/AdminAudits.tsx";
|
|
14
|
+
// Files
|
|
15
|
+
export { default as AdminFiles } from "./components/files/AdminFiles.tsx";
|
|
16
|
+
// Jobs
|
|
17
|
+
export { default as AdminJobs } from "./components/jobs/AdminJobs.tsx";
|
|
18
|
+
// Notifications
|
|
19
|
+
export { default as AdminNotifications } from "./components/notifications/AdminNotifications.tsx";
|
|
20
|
+
// Parameters
|
|
21
|
+
export { default as AdminParameters } from "./components/parameters/AdminParameters.tsx";
|
|
22
|
+
// Sessions
|
|
23
|
+
export { default as AdminSessions } from "./components/sessions/AdminSessions.tsx";
|
|
24
|
+
// Users
|
|
25
|
+
export { default as AdminUserAudits } from "./components/users/AdminUserAudits.tsx";
|
|
26
|
+
export { default as AdminUserCreate } from "./components/users/AdminUserCreate.tsx";
|
|
27
|
+
export { default as AdminUserDetails } from "./components/users/AdminUserDetails.tsx";
|
|
28
|
+
export { default as AdminUserLayout } from "./components/users/AdminUserLayout.tsx";
|
|
29
|
+
export { default as AdminUserSessions } from "./components/users/AdminUserSessions.tsx";
|
|
30
|
+
export { default as AdminUserSettings } from "./components/users/AdminUserSettings.tsx";
|
|
31
|
+
export { default as AdminUsers } from "./components/users/AdminUsers.tsx";
|
|
32
|
+
// Verifications
|
|
33
|
+
export { default as AdminVerifications } from "./components/verifications/AdminVerifications.tsx";
|
|
25
34
|
export { MainRouter } from "./MainRouter.ts";
|
|
26
35
|
|
|
27
36
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
@@ -33,9 +42,8 @@ export { MainRouter } from "./MainRouter.ts";
|
|
|
33
42
|
*/
|
|
34
43
|
export const AlephaUIAdmin = $module({
|
|
35
44
|
name: "alepha.ui.admin",
|
|
36
|
-
services: [AlephaUI, AlephaUIAuth, AdminRouter, MainRouter
|
|
45
|
+
services: [AlephaUI, AlephaUIAuth, AdminRouter, MainRouter],
|
|
37
46
|
register: (alepha) => {
|
|
38
47
|
alepha.with(AdminRouter);
|
|
39
|
-
alepha.with(AdminSidebar);
|
|
40
48
|
},
|
|
41
49
|
});
|
package/src/auth/AuthRouter.ts
CHANGED
|
@@ -11,6 +11,16 @@ import { $inject, AlephaError, t } from "alepha";
|
|
|
11
11
|
import type { UserRealmController } from "alepha/api/users";
|
|
12
12
|
import { $client } from "alepha/server/links";
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Schema for realm query parameter used across auth pages.
|
|
16
|
+
*/
|
|
17
|
+
const realmQuerySchema = t.object({
|
|
18
|
+
r: t.optional(t.string({ description: "Redirect URL after authentication" })),
|
|
19
|
+
realm: t.optional(
|
|
20
|
+
t.string({ description: "User realm name for multi-tenant auth" }),
|
|
21
|
+
),
|
|
22
|
+
});
|
|
23
|
+
|
|
14
24
|
export class AuthRouter {
|
|
15
25
|
protected readonly userRealmClient = $client<UserRealmController>();
|
|
16
26
|
protected readonly auth = $inject(ReactAuth);
|
|
@@ -33,15 +43,13 @@ export class AuthRouter {
|
|
|
33
43
|
description: "Sign in to your account",
|
|
34
44
|
path: "/login",
|
|
35
45
|
schema: {
|
|
36
|
-
query:
|
|
37
|
-
r: t.optional(t.string()),
|
|
38
|
-
}),
|
|
46
|
+
query: realmQuerySchema,
|
|
39
47
|
},
|
|
40
48
|
can: () => !this.auth.user,
|
|
41
49
|
lazy: () => import("./components/Login.tsx"),
|
|
42
|
-
resolve: async () => {
|
|
50
|
+
resolve: async ({ query }) => {
|
|
43
51
|
return {
|
|
44
|
-
realmConfig: await this.loadRealmConfig(),
|
|
52
|
+
realmConfig: await this.loadRealmConfig(query.realm),
|
|
45
53
|
};
|
|
46
54
|
},
|
|
47
55
|
});
|
|
@@ -52,15 +60,13 @@ export class AuthRouter {
|
|
|
52
60
|
description: "Create a new account",
|
|
53
61
|
path: "/register",
|
|
54
62
|
schema: {
|
|
55
|
-
query:
|
|
56
|
-
r: t.optional(t.string()),
|
|
57
|
-
}),
|
|
63
|
+
query: realmQuerySchema,
|
|
58
64
|
},
|
|
59
65
|
can: () => !this.auth.user,
|
|
60
66
|
lazy: () => import("./components/Register.tsx"),
|
|
61
|
-
resolve: async () => {
|
|
67
|
+
resolve: async ({ query }) => {
|
|
62
68
|
return {
|
|
63
|
-
realmConfig: await this.loadRealmConfig(),
|
|
69
|
+
realmConfig: await this.loadRealmConfig(query.realm),
|
|
64
70
|
};
|
|
65
71
|
},
|
|
66
72
|
});
|
|
@@ -71,15 +77,13 @@ export class AuthRouter {
|
|
|
71
77
|
description: "Reset your account password",
|
|
72
78
|
path: "/reset-password",
|
|
73
79
|
schema: {
|
|
74
|
-
query:
|
|
75
|
-
r: t.optional(t.string()),
|
|
76
|
-
}),
|
|
80
|
+
query: realmQuerySchema,
|
|
77
81
|
},
|
|
78
82
|
can: () => !this.auth.user,
|
|
79
83
|
lazy: () => import("./components/ResetPassword.tsx"),
|
|
80
|
-
resolve: async () => {
|
|
84
|
+
resolve: async ({ query }) => {
|
|
81
85
|
return {
|
|
82
|
-
realmConfig: await this.loadRealmConfig(),
|
|
86
|
+
realmConfig: await this.loadRealmConfig(query.realm),
|
|
83
87
|
};
|
|
84
88
|
},
|
|
85
89
|
});
|
|
@@ -111,9 +115,11 @@ export class AuthRouter {
|
|
|
111
115
|
},
|
|
112
116
|
});
|
|
113
117
|
|
|
114
|
-
protected async loadRealmConfig() {
|
|
118
|
+
protected async loadRealmConfig(userRealmName?: string) {
|
|
115
119
|
try {
|
|
116
|
-
return await this.userRealmClient.getRealmConfig(
|
|
120
|
+
return await this.userRealmClient.getRealmConfig({
|
|
121
|
+
query: { userRealmName },
|
|
122
|
+
});
|
|
117
123
|
} catch (e) {
|
|
118
124
|
if (e instanceof AlephaError) {
|
|
119
125
|
throw new AlephaError(
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { NestedView } from "@alepha/react";
|
|
2
|
+
import { AlephaMantineProvider } from "@alepha/ui";
|
|
2
3
|
import { Flex } from "@mantine/core";
|
|
3
4
|
|
|
4
5
|
const AuthLayout = () => {
|
|
5
6
|
return (
|
|
6
|
-
<
|
|
7
|
-
<
|
|
8
|
-
|
|
7
|
+
<AlephaMantineProvider omnibar={false}>
|
|
8
|
+
<Flex flex={1} align={"center"} h={"100vh"} justify={"center"}>
|
|
9
|
+
<NestedView />
|
|
10
|
+
</Flex>
|
|
11
|
+
</AlephaMantineProvider>
|
|
9
12
|
);
|
|
10
13
|
};
|
|
11
14
|
|
|
@@ -3,9 +3,9 @@ import { useAuth } from "@alepha/react/auth";
|
|
|
3
3
|
import { FormValidationError, useForm } from "@alepha/react/form";
|
|
4
4
|
import { useI18n } from "@alepha/react/i18n";
|
|
5
5
|
import { ActionButton, Control, capitalize } from "@alepha/ui";
|
|
6
|
-
import { Card, Flex, Group, Stack, Text } from "@mantine/core";
|
|
6
|
+
import { Card, Flex, Group, Image, Stack, Text, Title } from "@mantine/core";
|
|
7
7
|
import { IconLock, IconUser } from "@tabler/icons-react";
|
|
8
|
-
import { t } from "alepha";
|
|
8
|
+
import { AlephaError, t } from "alepha";
|
|
9
9
|
import type { UserRealmConfig } from "alepha/api/users";
|
|
10
10
|
import { HttpError } from "alepha/server";
|
|
11
11
|
import { useMemo } from "react";
|
|
@@ -24,7 +24,7 @@ const Login = (props: LoginProps) => {
|
|
|
24
24
|
const { tr } = useI18n<AuthI18n, "en">();
|
|
25
25
|
const redirect = router.query.r || "/";
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const credentialsProvider = props.realmConfig.authenticationMethods.find(
|
|
28
28
|
(it) => it.type === "CREDENTIALS",
|
|
29
29
|
);
|
|
30
30
|
|
|
@@ -68,10 +68,15 @@ const Login = (props: LoginProps) => {
|
|
|
68
68
|
}),
|
|
69
69
|
}),
|
|
70
70
|
handler: async (data) => {
|
|
71
|
+
if (!credentialsProvider) {
|
|
72
|
+
throw new AlephaError("Credentials provider not configured");
|
|
73
|
+
}
|
|
74
|
+
|
|
71
75
|
try {
|
|
72
|
-
await auth.login(
|
|
76
|
+
await auth.login(credentialsProvider.name, {
|
|
73
77
|
username: data.identifier,
|
|
74
78
|
password: data.password,
|
|
79
|
+
realm: props.realmConfig.realmName,
|
|
75
80
|
});
|
|
76
81
|
await router.go(router.query.r || "/");
|
|
77
82
|
} catch (error) {
|
|
@@ -89,12 +94,59 @@ const Login = (props: LoginProps) => {
|
|
|
89
94
|
},
|
|
90
95
|
});
|
|
91
96
|
|
|
97
|
+
const getAutoCompleteType = () => {
|
|
98
|
+
if (loginMethods.includes("email")) {
|
|
99
|
+
return "email";
|
|
100
|
+
}
|
|
101
|
+
if (loginMethods.includes("username")) {
|
|
102
|
+
return "username";
|
|
103
|
+
}
|
|
104
|
+
if (loginMethods.includes("phone")) {
|
|
105
|
+
return "tel";
|
|
106
|
+
}
|
|
107
|
+
return "username";
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const externalLoginMethods = props.realmConfig.authenticationMethods.filter(
|
|
111
|
+
(method) => method.type !== "CREDENTIALS",
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const showOrDivider = credentialsProvider && externalLoginMethods.length > 0;
|
|
115
|
+
|
|
92
116
|
return (
|
|
93
117
|
<Flex flex={1} justify={"center"} align={"center"}>
|
|
94
118
|
<Stack gap={"sm"} w={360}>
|
|
95
119
|
<Card withBorder p={"lg"} bg={"var(--alepha-elevated)"}>
|
|
96
120
|
<Stack gap={"md"}>
|
|
97
|
-
{
|
|
121
|
+
{/* Realm branding */}
|
|
122
|
+
{(settings.logoUrl ||
|
|
123
|
+
settings.displayName ||
|
|
124
|
+
settings.description) && (
|
|
125
|
+
<Stack gap={"xs"} align="center" mb="xs">
|
|
126
|
+
{settings.logoUrl && (
|
|
127
|
+
<Image
|
|
128
|
+
src={settings.logoUrl}
|
|
129
|
+
alt={settings.displayName || props.realmConfig.realmName}
|
|
130
|
+
h={48}
|
|
131
|
+
w="auto"
|
|
132
|
+
fit="contain"
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
135
|
+
{settings.displayName && (
|
|
136
|
+
<Title order={4} ta="center">
|
|
137
|
+
{settings.displayName}
|
|
138
|
+
</Title>
|
|
139
|
+
)}
|
|
140
|
+
{settings.description && (
|
|
141
|
+
<Text size="sm" c="dimmed" ta="center">
|
|
142
|
+
{settings.description}
|
|
143
|
+
</Text>
|
|
144
|
+
)}
|
|
145
|
+
</Stack>
|
|
146
|
+
)}
|
|
147
|
+
|
|
148
|
+
{/* Credentials login form */}
|
|
149
|
+
{credentialsProvider && (
|
|
98
150
|
<>
|
|
99
151
|
<form {...form.props}>
|
|
100
152
|
<Stack flex={1} gap={"md"}>
|
|
@@ -103,9 +155,7 @@ const Login = (props: LoginProps) => {
|
|
|
103
155
|
input={form.input.identifier}
|
|
104
156
|
icon={IconUser}
|
|
105
157
|
text={{
|
|
106
|
-
autoComplete:
|
|
107
|
-
? "email"
|
|
108
|
-
: "username",
|
|
158
|
+
autoComplete: getAutoCompleteType(),
|
|
109
159
|
}}
|
|
110
160
|
/>
|
|
111
161
|
<Control
|
|
@@ -116,56 +166,68 @@ const Login = (props: LoginProps) => {
|
|
|
116
166
|
autoComplete: "current-password",
|
|
117
167
|
}}
|
|
118
168
|
/>
|
|
119
|
-
<ActionButton
|
|
169
|
+
<ActionButton variant={"filled"} form={form}>
|
|
120
170
|
{tr("loginSignIn")}
|
|
121
171
|
</ActionButton>
|
|
122
172
|
</Stack>
|
|
123
173
|
</form>
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
<Text size="sm" ta="center">
|
|
127
|
-
<ActionButton
|
|
128
|
-
href={router.path("resetPassword")}
|
|
129
|
-
anchorProps={{ inherit: true }}
|
|
130
|
-
>
|
|
131
|
-
{tr("loginForgotPassword")}
|
|
132
|
-
</ActionButton>
|
|
133
|
-
</Text>
|
|
134
|
-
)}
|
|
135
|
-
<Group align="center" justify="center" gap={"md"}>
|
|
136
|
-
<Flex flex={1} h={"1px"} bg={"var(--alepha-text-muted)"} />
|
|
137
|
-
<Text size="xs">{tr("loginOr")}</Text>
|
|
138
|
-
<Flex flex={1} h={"1px"} bg={"var(--alepha-text-muted)"} />
|
|
139
|
-
</Group>
|
|
140
|
-
</Stack>
|
|
141
|
-
</>
|
|
142
|
-
)}
|
|
143
|
-
<Stack gap={"sm"}>
|
|
144
|
-
{props.realmConfig.authenticationMethods.map(
|
|
145
|
-
(method) =>
|
|
146
|
-
method.type !== "CREDENTIALS" && (
|
|
174
|
+
{settings.resetPasswordAllowed && (
|
|
175
|
+
<Text size="sm" ta="center">
|
|
147
176
|
<ActionButton
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
leftSection={leftSection(method.name.toLowerCase())}
|
|
151
|
-
onClick={() =>
|
|
152
|
-
auth.login(method.name, {
|
|
153
|
-
redirect,
|
|
154
|
-
})
|
|
155
|
-
}
|
|
156
|
-
>
|
|
157
|
-
{tr("loginContinueWith", {
|
|
158
|
-
args: [capitalize(method.name)],
|
|
177
|
+
href={router.path("resetPassword", {
|
|
178
|
+
query: { realm: props.realmConfig.realmName },
|
|
159
179
|
})}
|
|
180
|
+
anchorProps={{ inherit: true }}
|
|
181
|
+
>
|
|
182
|
+
{tr("loginForgotPassword")}
|
|
160
183
|
</ActionButton>
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
184
|
+
</Text>
|
|
185
|
+
)}
|
|
186
|
+
</>
|
|
187
|
+
)}
|
|
188
|
+
|
|
189
|
+
{/* OR divider - only when both credentials AND external methods exist */}
|
|
190
|
+
{showOrDivider && (
|
|
191
|
+
<Group align="center" justify="center" gap={"md"}>
|
|
192
|
+
<Flex flex={1} h={"1px"} bg={"var(--alepha-border)"} />
|
|
193
|
+
<Text size="xs" c={"dimmed"}>
|
|
194
|
+
{tr("loginOr")}
|
|
195
|
+
</Text>
|
|
196
|
+
<Flex flex={1} h={"1px"} bg={"var(--alepha-border)"} />
|
|
197
|
+
</Group>
|
|
198
|
+
)}
|
|
199
|
+
|
|
200
|
+
{/* External login methods */}
|
|
201
|
+
{externalLoginMethods.length > 0 && (
|
|
202
|
+
<Stack gap={"sm"}>
|
|
203
|
+
{externalLoginMethods.map((method) => (
|
|
204
|
+
<ActionButton
|
|
205
|
+
variant={"default"}
|
|
206
|
+
key={method.type}
|
|
207
|
+
leftSection={leftSection(method.name.toLowerCase())}
|
|
208
|
+
onClick={() =>
|
|
209
|
+
auth.login(method.name, {
|
|
210
|
+
redirect,
|
|
211
|
+
realm: props.realmConfig.realmName,
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
>
|
|
215
|
+
{tr("loginContinueWith", {
|
|
216
|
+
args: [capitalize(method.name)],
|
|
217
|
+
})}
|
|
218
|
+
</ActionButton>
|
|
219
|
+
))}
|
|
220
|
+
</Stack>
|
|
221
|
+
)}
|
|
222
|
+
|
|
223
|
+
{/* Registration link */}
|
|
164
224
|
{settings.registrationAllowed && (
|
|
165
225
|
<Text size="sm" ta="center">
|
|
166
226
|
{tr("loginNoAccount")}{" "}
|
|
167
227
|
<ActionButton
|
|
168
|
-
href={router.path("register"
|
|
228
|
+
href={router.path("register", {
|
|
229
|
+
query: { realm: props.realmConfig.realmName },
|
|
230
|
+
})}
|
|
169
231
|
anchorProps={{ inherit: true }}
|
|
170
232
|
>
|
|
171
233
|
{tr("loginSignUp")}
|