@alepha/ui 0.14.1 → 0.14.3

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.
Files changed (183) hide show
  1. package/dist/admin/AdminAudits-B3EhKhN7.js +3 -0
  2. package/dist/admin/{AdminAudits-CwvH8e8c.js → AdminAudits-DIrCCPk3.js} +3 -2
  3. package/dist/admin/AdminAudits-DIrCCPk3.js.map +1 -0
  4. package/dist/admin/AdminFiles-C8OG4dtD.js +3 -0
  5. package/dist/admin/{AdminFiles-C_w1tb_x.js → AdminFiles-RsL178Ta.js} +2 -2
  6. package/dist/admin/{AdminFiles-C_w1tb_x.js.map → AdminFiles-RsL178Ta.js.map} +1 -1
  7. package/dist/admin/AdminNotifications-BSL4B2fQ.js +3 -0
  8. package/dist/admin/{AdminNotifications-DuYy74AN.js → AdminNotifications-cIbywWKi.js} +2 -2
  9. package/dist/admin/AdminNotifications-cIbywWKi.js.map +1 -0
  10. package/dist/admin/{AdminParameters-DYg48Jwe.js → AdminParameters-BKObzzpN.js} +1 -1
  11. package/dist/admin/{AdminParameters-YagqWTG3.js → AdminParameters-D-q3Qmhv.js} +2 -2
  12. package/dist/admin/{AdminParameters-YagqWTG3.js.map → AdminParameters-D-q3Qmhv.js.map} +1 -1
  13. package/dist/admin/AdminSessions-DHG9zPfr.js +3 -0
  14. package/dist/admin/{AdminSessions-BCjgJ-93.js → AdminSessions-vOgkrQ2U.js} +3 -2
  15. package/dist/admin/AdminSessions-vOgkrQ2U.js.map +1 -0
  16. package/dist/admin/{AdminUserAudits-B_PUXCKC.js → AdminUserAudits-CSsN1fIC.js} +3 -2
  17. package/dist/admin/AdminUserAudits-CSsN1fIC.js.map +1 -0
  18. package/dist/admin/{AdminUserAudits-D7cTcElL.js → AdminUserAudits-DmAnivo3.js} +1 -1
  19. package/dist/admin/{AdminUserCreate-DzfRbGZ4.js → AdminUserCreate-B72nu-3W.js} +3 -2
  20. package/dist/admin/AdminUserCreate-B72nu-3W.js.map +1 -0
  21. package/dist/admin/{AdminUserCreate-oUA1KDIl.js → AdminUserCreate-DpA13zwj.js} +1 -1
  22. package/dist/admin/AdminUserDetails-CKM2IEMr.js +475 -0
  23. package/dist/admin/AdminUserDetails-CKM2IEMr.js.map +1 -0
  24. package/dist/admin/{AdminUserDetails-y1H5DW8Y.js → AdminUserDetails-Zib_B6Al.js} +1 -1
  25. package/dist/admin/{AdminUserLayout-Dejnz13m.js → AdminUserLayout-BNBOEiAO.js} +1 -1
  26. package/dist/admin/AdminUserLayout-D7En9UBq.js +334 -0
  27. package/dist/admin/AdminUserLayout-D7En9UBq.js.map +1 -0
  28. package/dist/admin/AdminUserSessions-D9X2_HMA.js +3 -0
  29. package/dist/admin/{AdminUserSessions-DO9H85O-.js → AdminUserSessions-DEaGu6n6.js} +3 -2
  30. package/dist/admin/AdminUserSessions-DEaGu6n6.js.map +1 -0
  31. package/dist/admin/{AdminUserSettings-B3jA8g3p.js → AdminUserSettings-Di73D7g2.js} +8 -6
  32. package/dist/admin/AdminUserSettings-Di73D7g2.js.map +1 -0
  33. package/dist/admin/AdminUserSettings-yI-JECf5.js +3 -0
  34. package/dist/admin/{AdminUsers-ebbrJBT0.js → AdminUsers-BnGIRvmV.js} +3 -2
  35. package/dist/admin/AdminUsers-BnGIRvmV.js.map +1 -0
  36. package/dist/admin/AdminUsers-CG9-2Z8W.js +3 -0
  37. package/dist/admin/index.d.ts +25 -25
  38. package/dist/admin/index.d.ts.map +1 -1
  39. package/dist/admin/index.js +37 -36
  40. package/dist/admin/index.js.map +1 -1
  41. package/dist/auth/{AuthLayout-BAZJHzDG.js → AuthLayout-B1sUB8fB.js} +2 -2
  42. package/dist/auth/AuthLayout-B1sUB8fB.js.map +1 -0
  43. package/dist/auth/Login-BWi-pPbO.js +4 -0
  44. package/dist/auth/{Login-CeNZZjrr.js → Login-Cjxv3EDi.js} +2 -2
  45. package/dist/auth/Login-Cjxv3EDi.js.map +1 -0
  46. package/dist/auth/{Register-s4ENeyiE.js → Register-BKBIpHhW.js} +3 -2
  47. package/dist/auth/Register-BKBIpHhW.js.map +1 -0
  48. package/dist/auth/Register-CtdvihIM.js +4 -0
  49. package/dist/auth/ResetPassword-BUdM7T_R.js +3 -0
  50. package/dist/auth/{ResetPassword-GLIFkJT7.js → ResetPassword-DvqD_1SJ.js} +3 -2
  51. package/dist/auth/ResetPassword-DvqD_1SJ.js.map +1 -0
  52. package/dist/auth/VerifyEmail-BYmtnkEl.js +3 -0
  53. package/dist/auth/{VerifyEmail-R79sSej_.js → VerifyEmail-VaBruOnO.js} +3 -2
  54. package/dist/auth/VerifyEmail-VaBruOnO.js.map +1 -0
  55. package/dist/auth/index.d.ts +11 -11
  56. package/dist/auth/index.d.ts.map +1 -1
  57. package/dist/auth/index.js +10 -10
  58. package/dist/auth/index.js.map +1 -1
  59. package/dist/core/index.d.ts +36 -55
  60. package/dist/core/index.d.ts.map +1 -1
  61. package/dist/core/index.js +50 -350
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/demo/DemoDataTable-2mzzf__a.js +150 -0
  64. package/dist/demo/DemoDataTable-2mzzf__a.js.map +1 -0
  65. package/dist/demo/DemoHome-CnuL5WV9.js +25 -0
  66. package/dist/demo/DemoHome-CnuL5WV9.js.map +1 -0
  67. package/dist/demo/DemoHome-D6Z7EE4V.js +3 -0
  68. package/dist/demo/DemoJsonViewer-CYUggLop.js +4 -0
  69. package/dist/demo/DemoJsonViewer-NUGst5wW.js +430 -0
  70. package/dist/demo/DemoJsonViewer-NUGst5wW.js.map +1 -0
  71. package/dist/demo/DemoLayout-ZFDzyvY3.js +3 -0
  72. package/dist/demo/DemoLayout-dvbeuBBf.js +47 -0
  73. package/dist/demo/DemoLayout-dvbeuBBf.js.map +1 -0
  74. package/dist/demo/DemoLogin--wE44i23.js +327 -0
  75. package/dist/demo/DemoLogin--wE44i23.js.map +1 -0
  76. package/dist/demo/DemoRegister-BtrMksx6.js +488 -0
  77. package/dist/demo/DemoRegister-BtrMksx6.js.map +1 -0
  78. package/dist/demo/DemoResetPassword-DVXiiiX7.js +341 -0
  79. package/dist/demo/DemoResetPassword-DVXiiiX7.js.map +1 -0
  80. package/dist/demo/DemoSidebar-DWnjYHoP.js +82 -0
  81. package/dist/demo/DemoSidebar-DWnjYHoP.js.map +1 -0
  82. package/dist/demo/DemoTypeForm-P5_VInW2.js +83 -0
  83. package/dist/demo/DemoTypeForm-P5_VInW2.js.map +1 -0
  84. package/dist/demo/DemoVerifyEmail-C_ooC5u8.js +152 -0
  85. package/dist/demo/DemoVerifyEmail-C_ooC5u8.js.map +1 -0
  86. package/dist/demo/IconGoogle-DvmFiEDB.js +58 -0
  87. package/dist/demo/IconGoogle-DvmFiEDB.js.map +1 -0
  88. package/dist/demo/Showcase-vemLuO2t.js +187 -0
  89. package/dist/demo/Showcase-vemLuO2t.js.map +1 -0
  90. package/dist/demo/index.d.ts +97 -0
  91. package/dist/demo/index.d.ts.map +1 -0
  92. package/dist/demo/index.js +121 -0
  93. package/dist/demo/index.js.map +1 -0
  94. package/dist/json/index.d.ts +58 -0
  95. package/dist/json/index.d.ts.map +1 -0
  96. package/dist/json/index.js +325 -0
  97. package/dist/json/index.js.map +1 -0
  98. package/package.json +25 -14
  99. package/src/admin/AdminRouter.ts +23 -20
  100. package/src/admin/MainRouter.ts +2 -2
  101. package/src/admin/components/audits/AdminAudits.tsx +4 -3
  102. package/src/admin/components/jobs/AdminJobs.tsx +2 -2
  103. package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
  104. package/src/admin/components/parameters/AdminParameters.tsx +2 -2
  105. package/src/admin/components/sessions/AdminSessions.tsx +4 -3
  106. package/src/admin/components/shared/AdminResourceHeader.tsx +281 -0
  107. package/src/admin/components/shared/AdminResourceTabs.tsx +94 -0
  108. package/src/admin/components/shared/index.ts +10 -0
  109. package/src/admin/components/users/AdminUserAudits.tsx +4 -3
  110. package/src/admin/components/users/AdminUserCreate.tsx +4 -3
  111. package/src/admin/components/users/AdminUserDetails.tsx +339 -86
  112. package/src/admin/components/users/AdminUserLayout.tsx +165 -113
  113. package/src/admin/components/users/AdminUserSessions.tsx +4 -3
  114. package/src/admin/components/users/AdminUserSettings.tsx +12 -6
  115. package/src/admin/components/users/AdminUsers.tsx +8 -3
  116. package/src/auth/AuthRouter.ts +1 -1
  117. package/src/auth/components/AuthLayout.tsx +1 -1
  118. package/src/auth/components/Login.tsx +1 -1
  119. package/src/auth/components/Register.tsx +2 -1
  120. package/src/auth/components/ResetPassword.tsx +2 -1
  121. package/src/auth/components/VerifyEmail.tsx +2 -1
  122. package/src/auth/components/buttons/UserButton.tsx +1 -1
  123. package/src/core/RootRouter.ts +1 -1
  124. package/src/core/components/buttons/ActionButton.tsx +3 -4
  125. package/src/core/components/form/Control.tsx +12 -1
  126. package/src/core/components/form/ControlNumber.tsx +5 -0
  127. package/src/core/components/form/TypeForm.tsx +3 -2
  128. package/src/core/components/layout/AdminShell.tsx +2 -1
  129. package/src/core/components/layout/AlephaMantineProvider.tsx +7 -2
  130. package/src/core/components/layout/Omnibar.tsx +2 -1
  131. package/src/core/components/layout/Sidebar.tsx +18 -18
  132. package/src/core/index.ts +1 -2
  133. package/src/core/services/DialogService.tsx +0 -17
  134. package/{styles.css → src/core/styles.css} +1 -5
  135. package/src/demo/DemoRouter.ts +123 -0
  136. package/src/demo/components/DemoHome.tsx +29 -0
  137. package/src/demo/components/DemoLayout.tsx +52 -0
  138. package/src/demo/components/auth/DemoLogin.tsx +130 -0
  139. package/src/demo/components/auth/DemoRegister.tsx +144 -0
  140. package/src/demo/components/auth/DemoResetPassword.tsx +69 -0
  141. package/src/demo/components/auth/DemoVerifyEmail.tsx +28 -0
  142. package/src/demo/components/core/DemoDataTable.tsx +174 -0
  143. package/src/demo/components/core/DemoSidebar.tsx +85 -0
  144. package/src/demo/components/core/DemoTypeForm.tsx +69 -0
  145. package/src/demo/components/json/DemoJsonViewer.tsx +128 -0
  146. package/src/demo/components/shared/MacWindow.tsx +105 -0
  147. package/src/demo/components/shared/Showcase.tsx +112 -0
  148. package/src/demo/index.ts +30 -0
  149. package/src/demo/styles.css +0 -0
  150. package/src/json/components/JsonViewer.css +25 -0
  151. package/src/json/components/JsonViewer.tsx +526 -0
  152. package/src/json/extensions/DialogService.tsx +31 -0
  153. package/src/json/index.ts +5 -0
  154. package/src/json/styles.css +1 -0
  155. package/dist/admin/AdminAudits-CwvH8e8c.js.map +0 -1
  156. package/dist/admin/AdminAudits-Dv8Vk_6r.js +0 -3
  157. package/dist/admin/AdminFiles-5CPA3lQk.js +0 -3
  158. package/dist/admin/AdminNotifications-DLjmZWtf.js +0 -3
  159. package/dist/admin/AdminNotifications-DuYy74AN.js.map +0 -1
  160. package/dist/admin/AdminSessions-BCjgJ-93.js.map +0 -1
  161. package/dist/admin/AdminSessions-DEh2uN-4.js +0 -3
  162. package/dist/admin/AdminUserAudits-B_PUXCKC.js.map +0 -1
  163. package/dist/admin/AdminUserCreate-DzfRbGZ4.js.map +0 -1
  164. package/dist/admin/AdminUserDetails-DeTrJm-t.js +0 -221
  165. package/dist/admin/AdminUserDetails-DeTrJm-t.js.map +0 -1
  166. package/dist/admin/AdminUserLayout-CsfrrZkD.js +0 -150
  167. package/dist/admin/AdminUserLayout-CsfrrZkD.js.map +0 -1
  168. package/dist/admin/AdminUserSessions-Bbhcpz4k.js +0 -3
  169. package/dist/admin/AdminUserSessions-DO9H85O-.js.map +0 -1
  170. package/dist/admin/AdminUserSettings-B3jA8g3p.js.map +0 -1
  171. package/dist/admin/AdminUserSettings-CE0xpbQc.js +0 -3
  172. package/dist/admin/AdminUsers-CegGZDhW.js +0 -3
  173. package/dist/admin/AdminUsers-ebbrJBT0.js.map +0 -1
  174. package/dist/auth/AuthLayout-BAZJHzDG.js.map +0 -1
  175. package/dist/auth/Login-CeNZZjrr.js.map +0 -1
  176. package/dist/auth/Login-hQcu1nlu.js +0 -4
  177. package/dist/auth/Register-B6HBNVHS.js +0 -4
  178. package/dist/auth/Register-s4ENeyiE.js.map +0 -1
  179. package/dist/auth/ResetPassword-Cjd-W-Nu.js +0 -3
  180. package/dist/auth/ResetPassword-GLIFkJT7.js.map +0 -1
  181. package/dist/auth/VerifyEmail-Dc9ABKUw.js +0 -3
  182. package/dist/auth/VerifyEmail-R79sSej_.js.map +0 -1
  183. package/src/core/components/data/JsonViewer.tsx +0 -361
@@ -1,15 +1,23 @@
1
+ import { useClient } from "@alepha/react";
2
+ import { NestedView, useRouter, useRouterState } from "@alepha/react/router";
3
+ import { Box, Center, Loader, Stack, Text } from "@mantine/core";
1
4
  import {
2
- NestedView,
3
- useClient,
4
- useRouter,
5
- useRouterState,
6
- } from "@alepha/react";
7
- import { ActionButton, Flex, Text } from "@alepha/ui";
8
- import { Avatar, Badge, Card, Group, Loader, Stack, Tabs } from "@mantine/core";
9
- import { IconDevices, IconSettings, IconUser } from "@tabler/icons-react";
10
- import type { UserController, UserEntity } from "alepha/api/users";
5
+ IconBan,
6
+ IconDevices,
7
+ IconHistory,
8
+ IconLock,
9
+ IconMail,
10
+ IconPencil,
11
+ IconSettings,
12
+ IconShieldCheck,
13
+ IconTrash,
14
+ IconUser,
15
+ } from "@tabler/icons-react";
16
+ import type { AdminUserController, UserEntity } from "alepha/api/users";
11
17
  import { useEffect, useState } from "react";
12
18
  import type { AdminRouter } from "../../AdminRouter.ts";
19
+ import AdminResourceHeader from "../shared/AdminResourceHeader.tsx";
20
+ import AdminResourceTabs from "../shared/AdminResourceTabs.tsx";
13
21
 
14
22
  export interface AdminUserLayoutProps {
15
23
  userRealmName?: string;
@@ -18,11 +26,12 @@ export interface AdminUserLayoutProps {
18
26
  const AdminUserLayout = (props: AdminUserLayoutProps) => {
19
27
  const router = useRouter<AdminRouter>();
20
28
  const state = useRouterState();
21
- const client = useClient<UserController>();
29
+ const client = useClient<AdminUserController>();
22
30
  const userId = state.params.userId as string;
23
31
 
24
32
  const [user, setUser] = useState<UserEntity | null>(null);
25
33
  const [loading, setLoading] = useState(true);
34
+ const [actionLoading, setActionLoading] = useState<string | null>(null);
26
35
 
27
36
  useEffect(() => {
28
37
  const loadUser = async () => {
@@ -42,127 +51,170 @@ const AdminUserLayout = (props: AdminUserLayoutProps) => {
42
51
 
43
52
  if (loading) {
44
53
  return (
45
- <Flex flex={1} justify="center" align="center">
54
+ <Center flex={1}>
46
55
  <Loader />
47
- </Flex>
56
+ </Center>
48
57
  );
49
58
  }
50
59
 
51
60
  if (!user) {
52
61
  return (
53
- <Flex flex={1} justify="center" align="center">
54
- <Text c="dimmed">User not found</Text>
55
- </Flex>
62
+ <Center flex={1}>
63
+ <Stack align="center" gap="xs">
64
+ <IconUser size={48} opacity={0.3} />
65
+ <Text c="dimmed">User not found</Text>
66
+ </Stack>
67
+ </Center>
56
68
  );
57
69
  }
58
70
 
59
- const currentPath = state.url.pathname;
60
- const detailsPath = router.path("adminUserDetails", { params: { userId } });
61
- const sessionsPath = router.path("adminUserSessions", { params: { userId } });
62
- const settingsPath = router.path("adminUserSettings", { params: { userId } });
71
+ const displayName =
72
+ user.firstName || user.lastName
73
+ ? `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim()
74
+ : user.username || user.email || "User";
63
75
 
76
+ const currentPath = state.url.pathname;
64
77
  const getActiveTab = () => {
65
78
  if (currentPath.endsWith("/sessions")) return "sessions";
66
79
  if (currentPath.endsWith("/settings")) return "settings";
67
- return "details";
80
+ if (currentPath.endsWith("/audits")) return "audits";
81
+ return "profile";
68
82
  };
69
- const activeTab = getActiveTab();
70
83
 
71
- const displayName =
72
- user.firstName || user.lastName
73
- ? `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim()
74
- : user.username || user.email || "User";
84
+ const handleBlockUser = async () => {
85
+ setActionLoading("block");
86
+ try {
87
+ const updated = await client.updateUser({
88
+ params: { id: userId },
89
+ query: { userRealmName: props.userRealmName },
90
+ body: { enabled: !user.enabled },
91
+ });
92
+ setUser(updated);
93
+ } finally {
94
+ setActionLoading(null);
95
+ }
96
+ };
97
+
98
+ const handleSendVerification = async () => {
99
+ setActionLoading("verify");
100
+ // TODO: Implement send verification
101
+ await new Promise((resolve) => setTimeout(resolve, 1000));
102
+ setActionLoading(null);
103
+ };
104
+
105
+ const handleResetPassword = async () => {
106
+ setActionLoading("reset");
107
+ // TODO: Implement reset password
108
+ await new Promise((resolve) => setTimeout(resolve, 1000));
109
+ setActionLoading(null);
110
+ };
111
+
112
+ const handleDeleteUser = async () => {
113
+ if (
114
+ !confirm(
115
+ "Are you sure you want to delete this user? This action cannot be undone.",
116
+ )
117
+ ) {
118
+ return;
119
+ }
120
+ setActionLoading("delete");
121
+ try {
122
+ await client.deleteUser({
123
+ params: { id: userId },
124
+ query: { userRealmName: props.userRealmName },
125
+ });
126
+ await router.go("adminUsers");
127
+ } finally {
128
+ setActionLoading(null);
129
+ }
130
+ };
75
131
 
76
132
  return (
77
- <Flex flex={1} direction="column" gap="md" p="md">
78
- <Card withBorder p="md">
79
- <Group>
80
- <Avatar size="lg" radius="xl" color="blue">
81
- {displayName.charAt(0).toUpperCase()}
82
- </Avatar>
83
- <Stack gap={4}>
84
- <Group gap="xs">
85
- <Text size="lg" fw={500}>
86
- {displayName}
87
- </Text>
88
- <Badge
89
- size="sm"
90
- variant="light"
91
- color={user.enabled ? "green" : "red"}
92
- >
93
- {user.enabled ? "Active" : "Disabled"}
94
- </Badge>
95
- </Group>
96
- <Text size="sm" c="dimmed">
97
- {user.email || user.username || user.id}
98
- </Text>
99
- {user.roles.length > 0 && (
100
- <Group gap={4}>
101
- {user.roles.map((role: string) => (
102
- <Badge key={role} size="xs" variant="outline">
103
- {role}
104
- </Badge>
105
- ))}
106
- </Group>
107
- )}
108
- </Stack>
109
- </Group>
110
- </Card>
111
-
112
- <Tabs value={activeTab}>
113
- <Tabs.List>
114
- <ActionButton
115
- href={detailsPath}
116
- leftSection={<IconUser size={16} />}
117
- c={activeTab === "details" ? undefined : "dimmed"}
118
- fw={activeTab === "details" ? 500 : 400}
119
- style={{
120
- borderBottom:
121
- activeTab === "details"
122
- ? "2px solid var(--mantine-primary-color-filled)"
123
- : "2px solid transparent",
124
- borderRadius: 0,
125
- }}
126
- >
127
- Details
128
- </ActionButton>
129
- <ActionButton
130
- href={sessionsPath}
131
- leftSection={<IconDevices size={16} />}
132
- c={activeTab === "sessions" ? undefined : "dimmed"}
133
- fw={activeTab === "sessions" ? 500 : 400}
134
- style={{
135
- borderBottom:
136
- activeTab === "sessions"
137
- ? "2px solid var(--mantine-primary-color-filled)"
138
- : "2px solid transparent",
139
- borderRadius: 0,
140
- }}
141
- >
142
- Sessions
143
- </ActionButton>
144
- <ActionButton
145
- href={settingsPath}
146
- leftSection={<IconSettings size={16} />}
147
- c={activeTab === "settings" ? undefined : "dimmed"}
148
- fw={activeTab === "settings" ? 500 : 400}
149
- style={{
150
- borderBottom:
151
- activeTab === "settings"
152
- ? "2px solid var(--mantine-primary-color-filled)"
153
- : "2px solid transparent",
154
- borderRadius: 0,
155
- }}
156
- >
157
- Settings
158
- </ActionButton>
159
- </Tabs.List>
160
- </Tabs>
161
-
162
- <Flex flex={1}>
133
+ <Box py="xl" px="xl" flex={1}>
134
+ <Stack gap="lg">
135
+ <AdminResourceHeader
136
+ backHref={router.path("adminUsers")}
137
+ backLabel="Users"
138
+ avatar={user.picture || displayName.charAt(0).toUpperCase()}
139
+ avatarColor={user.enabled ? "blue" : "gray"}
140
+ title={displayName}
141
+ subtitle={user.email || user.username || undefined}
142
+ status={{
143
+ label: user.enabled ? "Active" : "Disabled",
144
+ color: user.enabled ? "green" : "red",
145
+ }}
146
+ menuActions={[
147
+ {
148
+ label: "Edit Profile",
149
+ icon: IconPencil,
150
+ href: router.path("adminUserDetails", { params: { userId } }),
151
+ },
152
+ {
153
+ label: user.enabled ? "Disable User" : "Enable User",
154
+ icon: user.enabled ? IconBan : IconShieldCheck,
155
+ color: user.enabled ? "orange" : "green",
156
+ onClick: handleBlockUser,
157
+ loading: actionLoading === "block",
158
+ },
159
+ ...(user.email && !user.emailVerified
160
+ ? [
161
+ {
162
+ label: "Send Verification Email",
163
+ icon: IconMail,
164
+ onClick: handleSendVerification,
165
+ loading: actionLoading === "verify",
166
+ },
167
+ ]
168
+ : []),
169
+ {
170
+ label: "Reset Password",
171
+ icon: IconLock,
172
+ onClick: handleResetPassword,
173
+ loading: actionLoading === "reset",
174
+ },
175
+ {
176
+ label: "Delete User",
177
+ icon: IconTrash,
178
+ color: "red",
179
+ onClick: handleDeleteUser,
180
+ loading: actionLoading === "delete",
181
+ },
182
+ ]}
183
+ />
184
+
185
+ <AdminResourceTabs
186
+ activeTab={getActiveTab()}
187
+ tabs={[
188
+ {
189
+ value: "profile",
190
+ label: "Profile",
191
+ icon: IconUser,
192
+ href: router.path("adminUserDetails", { params: { userId } }),
193
+ },
194
+ {
195
+ value: "sessions",
196
+ label: "Sessions",
197
+ icon: IconDevices,
198
+ href: router.path("adminUserSessions", { params: { userId } }),
199
+ },
200
+ {
201
+ value: "audits",
202
+ label: "Activity",
203
+ icon: IconHistory,
204
+ href: router.path("adminUserAudits", { params: { userId } }),
205
+ },
206
+ {
207
+ value: "settings",
208
+ label: "Settings",
209
+ icon: IconSettings,
210
+ href: router.path("adminUserSettings", { params: { userId } }),
211
+ },
212
+ ]}
213
+ />
214
+
163
215
  <NestedView />
164
- </Flex>
165
- </Flex>
216
+ </Stack>
217
+ </Box>
166
218
  );
167
219
  };
168
220
 
@@ -1,5 +1,6 @@
1
- import { useClient, useRouterState } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useI18n } from "@alepha/react/i18n";
3
+ import { useRouterState } from "@alepha/react/router";
3
4
  import { ActionButton, DataTable, Flex, Text } from "@alepha/ui";
4
5
  import { Badge, Group } from "@mantine/core";
5
6
  import {
@@ -9,7 +10,7 @@ import {
9
10
  IconTrash,
10
11
  } from "@tabler/icons-react";
11
12
  import { type Page, t } from "alepha";
12
- import type { SessionController, SessionEntity } from "alepha/api/users";
13
+ import type { AdminSessionController, SessionEntity } from "alepha/api/users";
13
14
  import { useState } from "react";
14
15
 
15
16
  export interface AdminUserSessionsProps {
@@ -18,7 +19,7 @@ export interface AdminUserSessionsProps {
18
19
 
19
20
  const AdminUserSessions = (props: AdminUserSessionsProps) => {
20
21
  const state = useRouterState();
21
- const client = useClient<SessionController>();
22
+ const client = useClient<AdminSessionController>();
22
23
  const { l } = useI18n();
23
24
  const userId = state.params.userId as string;
24
25
  const [refreshKey, setRefreshKey] = useState(0);
@@ -1,4 +1,5 @@
1
- import { useClient, useRouter, useRouterState } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
+ import { useRouter, useRouterState } from "@alepha/react/router";
2
3
  import { ActionButton, Flex, Text } from "@alepha/ui";
3
4
  import { Alert, Card, Group, Loader, Stack } from "@mantine/core";
4
5
  import {
@@ -7,7 +8,11 @@ import {
7
8
  IconMail,
8
9
  IconTrash,
9
10
  } from "@tabler/icons-react";
10
- import type { UserController, UserEntity } from "alepha/api/users";
11
+ import type {
12
+ AdminUserController,
13
+ UserController,
14
+ UserEntity,
15
+ } from "alepha/api/users";
11
16
  import { useEffect, useState } from "react";
12
17
  import type { AdminRouter } from "../../AdminRouter.ts";
13
18
 
@@ -18,7 +23,8 @@ export interface AdminUserSettingsProps {
18
23
  const AdminUserSettings = (props: AdminUserSettingsProps) => {
19
24
  const router = useRouter<AdminRouter>();
20
25
  const state = useRouterState();
21
- const client = useClient<UserController>();
26
+ const adminClient = useClient<AdminUserController>();
27
+ const userClient = useClient<UserController>();
22
28
  const userId = state.params.userId as string;
23
29
 
24
30
  const [user, setUser] = useState<UserEntity | null>(null);
@@ -30,7 +36,7 @@ const AdminUserSettings = (props: AdminUserSettingsProps) => {
30
36
  useEffect(() => {
31
37
  const loadUser = async () => {
32
38
  try {
33
- const data = await client.getUser({
39
+ const data = await adminClient.getUser({
34
40
  params: { id: userId },
35
41
  query: { userRealmName: props.userRealmName },
36
42
  });
@@ -50,7 +56,7 @@ const AdminUserSettings = (props: AdminUserSettingsProps) => {
50
56
 
51
57
  setDeleteLoading(true);
52
58
  try {
53
- await client.deleteUser({
59
+ await adminClient.deleteUser({
54
60
  params: { id: userId },
55
61
  query: { userRealmName: props.userRealmName },
56
62
  });
@@ -66,7 +72,7 @@ const AdminUserSettings = (props: AdminUserSettingsProps) => {
66
72
  setVerifyLoading(true);
67
73
  setVerifySuccess(false);
68
74
  try {
69
- await client.requestEmailVerification({
75
+ await userClient.requestEmailVerification({
70
76
  query: {
71
77
  userRealmName: props.userRealmName,
72
78
  method: "link",
@@ -1,10 +1,15 @@
1
- import { useClient, useRouter } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useI18n } from "@alepha/react/i18n";
3
+ import { useRouter } from "@alepha/react/router";
3
4
  import { DataTable, Text } from "@alepha/ui";
4
5
  import { Badge, Flex, Group } from "@mantine/core";
5
6
  import { IconCheck, IconUsersPlus, IconX } from "@tabler/icons-react";
6
7
  import { type Page, t } from "alepha";
7
- import { type UserController, type UserEntity, users } from "alepha/api/users";
8
+ import {
9
+ type AdminUserController,
10
+ type UserEntity,
11
+ users,
12
+ } from "alepha/api/users";
8
13
  import type { AdminRouter } from "../../AdminRouter.ts";
9
14
 
10
15
  export interface AdminUsersProps {
@@ -12,7 +17,7 @@ export interface AdminUsersProps {
12
17
  }
13
18
 
14
19
  const AdminUsers = (props: AdminUsersProps) => {
15
- const client = useClient<UserController>();
20
+ const client = useClient<AdminUserController>();
16
21
  const router = useRouter<AdminRouter>();
17
22
  const { l } = useI18n();
18
23
 
@@ -1,5 +1,5 @@
1
- import { $page } from "@alepha/react";
2
1
  import { ReactAuth } from "@alepha/react/auth";
2
+ import { $page } from "@alepha/react/router";
3
3
  import {
4
4
  IconLogin2,
5
5
  IconLogout2,
@@ -1,4 +1,4 @@
1
- import { NestedView } from "@alepha/react";
1
+ import { NestedView } from "@alepha/react/router";
2
2
  import { AlephaMantineProvider } from "@alepha/ui";
3
3
  import { Flex } from "@mantine/core";
4
4
 
@@ -1,7 +1,7 @@
1
- import { useRouter } from "@alepha/react";
2
1
  import { useAuth } from "@alepha/react/auth";
3
2
  import { FormValidationError, useForm } from "@alepha/react/form";
4
3
  import { useI18n } from "@alepha/react/i18n";
4
+ import { useRouter } from "@alepha/react/router";
5
5
  import { ActionButton, Control, capitalize } from "@alepha/ui";
6
6
  import { Card, Flex, Group, Image, Stack, Text, Title } from "@mantine/core";
7
7
  import { IconLock, IconUser } from "@tabler/icons-react";
@@ -1,7 +1,8 @@
1
- import { useClient, useRouter } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useAuth } from "@alepha/react/auth";
3
3
  import { useForm } from "@alepha/react/form";
4
4
  import { useI18n } from "@alepha/react/i18n";
5
+ import { useRouter } from "@alepha/react/router";
5
6
  import { ActionButton, Control, capitalize } from "@alepha/ui";
6
7
  import {
7
8
  Alert,
@@ -1,6 +1,7 @@
1
- import { useClient, useRouter } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useForm } from "@alepha/react/form";
3
3
  import { useI18n } from "@alepha/react/i18n";
4
+ import { useRouter } from "@alepha/react/router";
4
5
  import { ActionButton, Control } from "@alepha/ui";
5
6
  import {
6
7
  Alert,
@@ -1,5 +1,6 @@
1
- import { useClient, useRouter, useRouterState } from "@alepha/react";
1
+ import { useClient } from "@alepha/react";
2
2
  import { useI18n } from "@alepha/react/i18n";
3
+ import { useRouter, useRouterState } from "@alepha/react/router";
3
4
  import { ActionButton } from "@alepha/ui";
4
5
  import { Alert, Card, Flex, Loader, Stack, Text } from "@mantine/core";
5
6
  import { IconAlertCircle, IconCheck, IconMailCheck } from "@tabler/icons-react";
@@ -1,5 +1,5 @@
1
- import { useRouter } from "@alepha/react";
2
1
  import { useAuth } from "@alepha/react/auth";
2
+ import { useRouter } from "@alepha/react/router";
3
3
  import {
4
4
  ActionButton,
5
5
  type ActionMenuConfig,
@@ -1,4 +1,4 @@
1
- import { $page } from "@alepha/react";
1
+ import { $page } from "@alepha/react/router";
2
2
  import AlephaMantineProvider from "./components/layout/AlephaMantineProvider.tsx";
3
3
 
4
4
  export class RootRouter {
@@ -1,12 +1,11 @@
1
+ import { type UseActionReturn, useAction } from "@alepha/react";
2
+ import { type FormModel, useFormState } from "@alepha/react/form";
1
3
  import {
2
4
  type RouterGoOptions,
3
- type UseActionReturn,
4
5
  type UseActiveOptions,
5
- useAction,
6
6
  useActive,
7
7
  useRouter,
8
- } from "@alepha/react";
9
- import { type FormModel, useFormState } from "@alepha/react/form";
8
+ } from "@alepha/react/router";
10
9
  import {
11
10
  Anchor,
12
11
  type AnchorProps,
@@ -48,6 +48,9 @@ export interface ControlProps extends GenericControlProps {
48
48
  object?: boolean | Partial<Omit<ControlObjectProps, "input">>; // Nested object editing
49
49
  array?: boolean | Partial<Omit<ControlArrayProps, "input">>; // Array of items editing
50
50
  custom?: ComponentType<CustomControlProps>;
51
+
52
+ slider?: boolean;
53
+ segmented?: boolean;
51
54
  }
52
55
 
53
56
  /**
@@ -183,6 +186,9 @@ const Control = (_props: ControlProps) => {
183
186
  ) {
184
187
  const controlNumberProps =
185
188
  typeof props.number === "object" ? props.number : {};
189
+ if (props.slider) {
190
+ controlNumberProps.sliderProps ??= {};
191
+ }
186
192
  return (
187
193
  <ControlNumber
188
194
  input={props.input}
@@ -237,6 +243,9 @@ const Control = (_props: ControlProps) => {
237
243
 
238
244
  if (isEnum || (isArray && !isArrayOfObjects) || props.select) {
239
245
  const opts = typeof props.select === "object" ? props.select : {};
246
+ if (props.segmented) {
247
+ opts.segmented ??= {};
248
+ }
240
249
  return (
241
250
  <ControlSelect
242
251
  input={props.input}
@@ -264,7 +273,9 @@ const Control = (_props: ControlProps) => {
264
273
  id={id}
265
274
  color={"blue"}
266
275
  defaultChecked={props.input.props.defaultValue}
267
- {...props.input.props}
276
+ onChange={(event) => {
277
+ props.input.set(event.currentTarget.checked);
278
+ }}
268
279
  {...switchProps}
269
280
  />
270
281
  );
@@ -50,6 +50,8 @@ const ControlNumber = (props: ControlNumberProps) => {
50
50
  const { type, ...inputPropsWithoutType } = props.input.props;
51
51
 
52
52
  if (props.sliderProps) {
53
+ const min = props.sliderProps.min ?? inputProps.minimum ?? 0;
54
+ const max = props.sliderProps.max ?? inputProps.maximum ?? 100;
53
55
  return (
54
56
  <Input.Wrapper {...inputProps}>
55
57
  <div
@@ -65,6 +67,9 @@ const ControlNumber = (props: ControlNumberProps) => {
65
67
  {...inputPropsWithoutType}
66
68
  {...props.sliderProps}
67
69
  value={value}
70
+ min={min}
71
+ max={max}
72
+ label={() => value}
68
73
  onChange={(val) => {
69
74
  setValue(val);
70
75
  props.input.set(val);
@@ -92,6 +92,7 @@ const TypeForm = <T extends TObject>(props: TypeFormProps<T>) => {
92
92
  skipFormElement = false,
93
93
  skipSubmitButton = false,
94
94
  submitButtonProps,
95
+ fill = true,
95
96
  } = props;
96
97
 
97
98
  const schema = props.schema || form.options.schema;
@@ -176,7 +177,7 @@ const TypeForm = <T extends TObject>(props: TypeFormProps<T>) => {
176
177
  <Flex
177
178
  direction={"column"}
178
179
  gap={"sm"}
179
- flex={props.fill ? 1 : undefined}
180
+ flex={fill ? 1 : undefined}
180
181
  {...props.flexProps}
181
182
  >
182
183
  <Flex direction={"column"} gap={"sm"} flex={1}>
@@ -212,7 +213,7 @@ const TypeForm = <T extends TObject>(props: TypeFormProps<T>) => {
212
213
  return (
213
214
  <Flex
214
215
  component={"form"}
215
- flex={props.fill ? 1 : undefined}
216
+ flex={fill ? 1 : undefined}
216
217
  {...form.props}
217
218
  {...props.flexProps}
218
219
  >
@@ -1,4 +1,5 @@
1
- import { NestedView, useEvents, useRouter, useStore } from "@alepha/react";
1
+ import { useEvents, useStore } from "@alepha/react";
2
+ import { NestedView, useRouter } from "@alepha/react/router";
2
3
  import {
3
4
  AppShell,
4
5
  type AppShellFooterProps,
@@ -1,5 +1,6 @@
1
- import { NestedView, useEvents } from "@alepha/react";
1
+ import { useEvents } from "@alepha/react";
2
2
  import { FormValidationError } from "@alepha/react/form";
3
+ import { NestedView } from "@alepha/react/router";
3
4
  import {
4
5
  ColorSchemeScript,
5
6
  type ColorSchemeScriptProps,
@@ -10,6 +11,7 @@ import { ModalsProvider, type ModalsProviderProps } from "@mantine/modals";
10
11
  import { Notifications, type NotificationsProps } from "@mantine/notifications";
11
12
  import type { NavigationProgressProps } from "@mantine/nprogress";
12
13
  import { NavigationProgress, nprogress } from "@mantine/nprogress";
14
+ import { TypeBoxError } from "alepha";
13
15
  import type { ReactNode } from "react";
14
16
  import { useTheme } from "../../hooks/useTheme.ts";
15
17
  import { useToast } from "../../hooks/useToast.ts";
@@ -38,7 +40,10 @@ const AlephaMantineProvider = (props: AlephaMantineProviderProps) => {
38
40
  nprogress.complete();
39
41
  },
40
42
  "react:action:error": ({ error }) => {
41
- if (error instanceof FormValidationError) {
43
+ if (
44
+ error instanceof FormValidationError ||
45
+ error instanceof TypeBoxError
46
+ ) {
42
47
  // Validation errors are handled by the form component
43
48
  return;
44
49
  }
@@ -1,4 +1,5 @@
1
- import { useRouter, useStore } from "@alepha/react";
1
+ import { useStore } from "@alepha/react";
2
+ import { useRouter } from "@alepha/react/router";
2
3
  import { Spotlight, type SpotlightActionData } from "@mantine/spotlight";
3
4
  import { IconSearch } from "@tabler/icons-react";
4
5
  import { type ReactNode, useMemo } from "react";