@alepha/ui 0.17.2 → 0.18.1

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 (270) hide show
  1. package/dist/admin/{AdminApiKeys-CF_qOO3u.js → AdminApiKeys-C-6_Q-lH.js} +56 -192
  2. package/dist/admin/AdminApiKeys-C-6_Q-lH.js.map +1 -0
  3. package/dist/admin/{AdminAudits-BQno3hZG.js → AdminAudits-Bgbf04hO.js} +25 -61
  4. package/dist/admin/AdminAudits-Bgbf04hO.js.map +1 -0
  5. package/dist/admin/{AdminFiles-kvuUaASF.js → AdminFiles-B9a7G3cY.js} +6 -8
  6. package/dist/admin/AdminFiles-B9a7G3cY.js.map +1 -0
  7. package/dist/admin/{AdminJobDashboard-CrPxp0W1.js → AdminJobDashboard-DaTwf5OY.js} +55 -186
  8. package/dist/admin/AdminJobDashboard-DaTwf5OY.js.map +1 -0
  9. package/dist/admin/{AdminJobExecutions-D-b4Zt7W.js → AdminJobExecutions-B9cek5dl.js} +132 -168
  10. package/dist/admin/AdminJobExecutions-B9cek5dl.js.map +1 -0
  11. package/dist/admin/{AdminJobRegistry-CNX5cpDx.js → AdminJobRegistry-DFgV3oqx.js} +60 -83
  12. package/dist/admin/AdminJobRegistry-DFgV3oqx.js.map +1 -0
  13. package/dist/admin/AdminLayout-DHsvWxVB.js +70 -0
  14. package/dist/admin/AdminLayout-DHsvWxVB.js.map +1 -0
  15. package/dist/admin/{AdminParameters-DCGbpt2c.js → AdminParameters-DHw9ATgl.js} +53 -53
  16. package/dist/admin/AdminParameters-DHw9ATgl.js.map +1 -0
  17. package/dist/admin/{AdminSessions-DyhW6RZv.js → AdminSessions-BhGJPI3z.js} +11 -18
  18. package/dist/admin/AdminSessions-BhGJPI3z.js.map +1 -0
  19. package/dist/admin/{AdminUserLayout-CrBj4UuI.js → AdminUserLayout-BdC4Te8m.js} +112 -151
  20. package/dist/admin/AdminUserLayout-BdC4Te8m.js.map +1 -0
  21. package/dist/admin/AdminUserProfile-DAt23fqY.js +69 -0
  22. package/dist/admin/AdminUserProfile-DAt23fqY.js.map +1 -0
  23. package/dist/admin/AdminUserSessions-1uzcx02z.js +109 -0
  24. package/dist/admin/AdminUserSessions-1uzcx02z.js.map +1 -0
  25. package/dist/admin/AdminUsers-C85c3eiQ.js +121 -0
  26. package/dist/admin/AdminUsers-C85c3eiQ.js.map +1 -0
  27. package/dist/{auth/AuthLayout-CdJcrPs4.js → admin/AuthLayout-DFJvCvzw.js} +3 -3
  28. package/dist/{auth/AuthLayout-CdJcrPs4.js.map → admin/AuthLayout-DFJvCvzw.js.map} +1 -1
  29. package/dist/{auth/IconGoogle-Bm18QD2q.js → admin/IconGoogle-CSQLPYwX.js} +1 -1
  30. package/dist/{auth/IconGoogle-Bm18QD2q.js.map → admin/IconGoogle-CSQLPYwX.js.map} +1 -1
  31. package/dist/{demo/DemoLogin-DjJ9314c.js → admin/Login-BGheURrg.js} +15 -129
  32. package/dist/{auth/Login-BS_FYTy0.js.map → admin/Login-BGheURrg.js.map} +1 -1
  33. package/dist/{auth/Profile-CjDsW378.js → admin/Profile-B-c9pCPf.js} +5 -5
  34. package/dist/{auth/Profile-CjDsW378.js.map → admin/Profile-B-c9pCPf.js.map} +1 -1
  35. package/dist/{demo/DemoRegister-DzkJ5M83.js → admin/Register-Cs10l8vX.js} +20 -146
  36. package/dist/{auth/Register-C5eqzAaD.js.map → admin/Register-Cs10l8vX.js.map} +1 -1
  37. package/dist/{demo/DemoResetPassword-DWh4_BpQ.js → admin/ResetPassword-BwDdfkGH.js} +20 -82
  38. package/dist/{auth/ResetPassword-XifinVao.js.map → admin/ResetPassword-BwDdfkGH.js.map} +1 -1
  39. package/dist/{demo/DemoVerifyEmail-DbU_tCj8.js → admin/VerifyEmail-DfXHAiQl.js} +15 -32
  40. package/dist/{auth/VerifyEmail-DTgbeJOO.js.map → admin/VerifyEmail-DfXHAiQl.js.map} +1 -1
  41. package/dist/admin/auth-Dr0Cf8I7.js +319 -0
  42. package/dist/admin/auth-Dr0Cf8I7.js.map +1 -0
  43. package/dist/admin/core-2xoLiT0o.js +4031 -0
  44. package/dist/admin/core-2xoLiT0o.js.map +1 -0
  45. package/dist/admin/index.d.ts +739 -13
  46. package/dist/admin/index.d.ts.map +1 -1
  47. package/dist/admin/index.js +79 -111
  48. package/dist/admin/index.js.map +1 -1
  49. package/dist/admin/rolldown-runtime-CjeV3_4I.js +18 -0
  50. package/dist/auth/AuthLayout-CAE1pX9s.js +22 -0
  51. package/dist/auth/AuthLayout-CAE1pX9s.js.map +1 -0
  52. package/dist/auth/{Login-BS_FYTy0.js → Login-Denw_UGy.js} +8 -8
  53. package/dist/auth/Login-Denw_UGy.js.map +1 -0
  54. package/dist/auth/Profile-BMX_Ar_s.js +155 -0
  55. package/dist/auth/Profile-BMX_Ar_s.js.map +1 -0
  56. package/dist/auth/{Register-C5eqzAaD.js → Register-6hi_cpfF.js} +8 -8
  57. package/dist/auth/Register-6hi_cpfF.js.map +1 -0
  58. package/dist/auth/{ResetPassword-XifinVao.js → ResetPassword-CqfTk1FI.js} +6 -6
  59. package/dist/auth/ResetPassword-CqfTk1FI.js.map +1 -0
  60. package/dist/auth/{VerifyEmail-DTgbeJOO.js → VerifyEmail-nWiSTMjF.js} +5 -5
  61. package/dist/auth/VerifyEmail-nWiSTMjF.js.map +1 -0
  62. package/dist/auth/core-niW0sFLv.js +2264 -0
  63. package/dist/auth/core-niW0sFLv.js.map +1 -0
  64. package/dist/auth/index.d.ts +336 -8
  65. package/dist/auth/index.d.ts.map +1 -1
  66. package/dist/auth/index.js +18 -22
  67. package/dist/auth/index.js.map +1 -1
  68. package/dist/core/index.d.ts +1033 -843
  69. package/dist/core/index.d.ts.map +1 -1
  70. package/dist/core/index.js +1626 -1354
  71. package/dist/core/index.js.map +1 -1
  72. package/dist/demo/AuthLayout-jLa0aKsI.js +22 -0
  73. package/dist/demo/AuthLayout-jLa0aKsI.js.map +1 -0
  74. package/dist/demo/DemoButton-BmaWZVwf.js +178 -0
  75. package/dist/demo/DemoButton-BmaWZVwf.js.map +1 -0
  76. package/dist/demo/{DemoDataTable-lnBKWBf8.js → DemoDataTable-Z9xyV221.js} +18 -18
  77. package/dist/demo/DemoDataTable-Z9xyV221.js.map +1 -0
  78. package/dist/demo/DemoDialog-4ItHLf9t.js +101 -0
  79. package/dist/demo/DemoDialog-4ItHLf9t.js.map +1 -0
  80. package/dist/demo/DemoFlex-EtVq8QfX.js +105 -0
  81. package/dist/demo/DemoFlex-EtVq8QfX.js.map +1 -0
  82. package/dist/demo/DemoHeading-BS-vGfkI.js +18 -0
  83. package/dist/demo/DemoHeading-BS-vGfkI.js.map +1 -0
  84. package/dist/demo/{DemoHome-CUMZsYaH.js → DemoHome-Clbn8AmS.js} +9 -12
  85. package/dist/demo/DemoHome-Clbn8AmS.js.map +1 -0
  86. package/dist/demo/DemoJsonViewer-DkIX_ky2.js +109 -0
  87. package/dist/demo/DemoJsonViewer-DkIX_ky2.js.map +1 -0
  88. package/dist/demo/DemoLayout-C56xb5EE.js +73 -0
  89. package/dist/demo/DemoLayout-C56xb5EE.js.map +1 -0
  90. package/dist/demo/DemoLogin-BZwpicOS.js +128 -0
  91. package/dist/demo/DemoLogin-BZwpicOS.js.map +1 -0
  92. package/dist/demo/DemoRegister-C7_qc4MJ.js +140 -0
  93. package/dist/demo/DemoRegister-C7_qc4MJ.js.map +1 -0
  94. package/dist/demo/DemoResetPassword-BI1Ct4Dw.js +76 -0
  95. package/dist/demo/DemoResetPassword-BI1Ct4Dw.js.map +1 -0
  96. package/dist/demo/{DemoSidebar-C1csnGhX.js → DemoSidebar-CcBo4ltC.js} +6 -9
  97. package/dist/demo/DemoSidebar-CcBo4ltC.js.map +1 -0
  98. package/dist/demo/DemoText-CzXuUn3g.js +124 -0
  99. package/dist/demo/DemoText-CzXuUn3g.js.map +1 -0
  100. package/dist/demo/DemoToast-BgHDhWrX.js +95 -0
  101. package/dist/demo/DemoToast-BgHDhWrX.js.map +1 -0
  102. package/dist/demo/{DemoTypeForm-CWz6fJrJ.js → DemoTypeForm-DDzWoMSV.js} +4 -4
  103. package/dist/demo/{DemoTypeForm-CWz6fJrJ.js.map → DemoTypeForm-DDzWoMSV.js.map} +1 -1
  104. package/dist/demo/DemoVerifyEmail-C_Irdnov.js +30 -0
  105. package/dist/demo/DemoVerifyEmail-C_Irdnov.js.map +1 -0
  106. package/dist/demo/IconGoogle-CSQLPYwX.js +56 -0
  107. package/dist/demo/IconGoogle-CSQLPYwX.js.map +1 -0
  108. package/dist/demo/Login-hSOU3jZc.js +219 -0
  109. package/dist/demo/Login-hSOU3jZc.js.map +1 -0
  110. package/dist/demo/Profile-CWqti7FB.js +155 -0
  111. package/dist/demo/Profile-CWqti7FB.js.map +1 -0
  112. package/dist/demo/Register-a70LPgs2.js +375 -0
  113. package/dist/demo/Register-a70LPgs2.js.map +1 -0
  114. package/dist/demo/ResetPassword-DWN0lzr5.js +286 -0
  115. package/dist/demo/ResetPassword-DWN0lzr5.js.map +1 -0
  116. package/dist/demo/Showcase-Dq3MISpd.js +232 -0
  117. package/dist/demo/Showcase-Dq3MISpd.js.map +1 -0
  118. package/dist/demo/VerifyEmail-DZWL72K4.js +135 -0
  119. package/dist/demo/VerifyEmail-DZWL72K4.js.map +1 -0
  120. package/dist/demo/auth-d6n3xbug.js +257 -0
  121. package/dist/demo/auth-d6n3xbug.js.map +1 -0
  122. package/dist/demo/core-RCUw1Q-a.js +4217 -0
  123. package/dist/demo/core-RCUw1Q-a.js.map +1 -0
  124. package/dist/demo/index.d.ts +17 -6
  125. package/dist/demo/index.d.ts.map +1 -1
  126. package/dist/demo/index.js +92 -24
  127. package/dist/demo/index.js.map +1 -1
  128. package/dist/demo/rolldown-runtime-CjeV3_4I.js +18 -0
  129. package/package.json +16 -20
  130. package/src/admin/AdminRouter.ts +10 -39
  131. package/src/admin/components/AdminLayout.tsx +42 -10
  132. package/src/admin/components/audits/AdminAudits.tsx +10 -64
  133. package/src/admin/components/files/AdminFiles.tsx +2 -3
  134. package/src/admin/components/jobs/AdminJobDashboard.tsx +36 -142
  135. package/src/admin/components/jobs/AdminJobExecutions.tsx +117 -175
  136. package/src/admin/components/jobs/AdminJobRegistry.tsx +58 -73
  137. package/src/admin/components/keys/AdminApiKeys.tsx +21 -169
  138. package/src/admin/components/parameters/AdminParameters.tsx +4 -4
  139. package/src/admin/components/parameters/ParameterEmptyState.tsx +1 -2
  140. package/src/admin/components/parameters/ParameterHistory.tsx +3 -3
  141. package/src/admin/components/parameters/ParameterTree.tsx +2 -8
  142. package/src/admin/components/parameters/types.ts +3 -3
  143. package/src/admin/components/sessions/AdminSessions.tsx +8 -16
  144. package/src/admin/components/users/AdminUserLayout.tsx +113 -150
  145. package/src/admin/components/users/AdminUserProfile.tsx +50 -0
  146. package/src/admin/components/users/AdminUserSessions.tsx +106 -126
  147. package/src/admin/components/users/AdminUsers.tsx +46 -62
  148. package/src/admin/index.ts +0 -4
  149. package/src/auth/components/buttons/UserButton.tsx +1 -1
  150. package/src/auth/index.ts +0 -4
  151. package/src/core/UiRouter.ts +1 -1
  152. package/src/core/atoms/alephaSidebarAtom.ts +7 -31
  153. package/src/core/components/{layout/AlephaMantineProvider.tsx → AlephaMantineProvider.tsx} +3 -4
  154. package/src/core/components/Flex.tsx +63 -0
  155. package/src/core/components/Heading.tsx +19 -0
  156. package/src/core/components/Text.tsx +140 -0
  157. package/src/core/components/buttons/ActionButton.tsx +12 -1
  158. package/src/core/components/buttons/BurgerButton.tsx +3 -3
  159. package/src/core/components/buttons/LanguageButton.tsx +1 -1
  160. package/src/core/components/buttons/ToggleSidebarButton.tsx +1 -4
  161. package/src/core/components/data/DetailDrawer.tsx +144 -0
  162. package/src/core/components/data/DetailList.tsx +64 -0
  163. package/src/core/components/data/StatCards.tsx +50 -0
  164. package/src/core/components/layout/AppBar.tsx +11 -10
  165. package/src/core/components/layout/Breadcrumb.tsx +8 -8
  166. package/src/core/components/layout/Container.tsx +15 -0
  167. package/src/core/components/layout/DashboardShell.tsx +23 -238
  168. package/src/core/components/layout/Omnibar.tsx +1 -2
  169. package/src/core/components/layout/Sidebar.tsx +103 -71
  170. package/src/core/components/layout/index.ts +65 -0
  171. package/src/core/{components/form → form/components}/Control.tsx +32 -14
  172. package/src/core/{components/form → form/components}/ControlArray.tsx +2 -5
  173. package/src/core/{components/form → form/components}/ControlDate.tsx +1 -4
  174. package/src/core/{components/form → form/components}/ControlNumber.tsx +1 -4
  175. package/src/core/{components/form → form/components}/ControlObject.tsx +1 -4
  176. package/src/core/{components/form → form/components}/ControlQueryBuilder.tsx +7 -7
  177. package/src/core/{components/form → form/components}/ControlSelect.tsx +2 -4
  178. package/src/core/{components/form → form/components}/TypeForm.browser.spec.tsx +22 -64
  179. package/src/core/{components/form → form/components}/TypeForm.tsx +1 -3
  180. package/src/core/form/factories/dialogForm.tsx +31 -0
  181. package/src/core/form/index.ts +23 -0
  182. package/src/core/{utils → form/utils}/parseInput.ts +2 -4
  183. package/src/core/index.ts +43 -51
  184. package/src/core/interfaces/AlephaIntent.ts +6 -0
  185. package/src/core/interfaces/AlephaTheme.ts +0 -1
  186. package/src/core/json/factories/dialogJson.tsx +24 -0
  187. package/src/core/json/index.ts +2 -0
  188. package/src/core/primitives/$ui.ts +17 -0
  189. package/src/core/services/DialogService.tsx +1 -48
  190. package/src/core/styles.css +1 -8
  191. package/src/core/{components/table → table/components}/ColumnPicker.tsx +2 -3
  192. package/src/core/{components/table → table/components}/DataTable.tsx +8 -9
  193. package/src/core/{components/table → table/components}/DataTableFilters.tsx +6 -3
  194. package/src/core/{components/table → table/components}/DataTableToolbar.tsx +4 -5
  195. package/src/core/{components/table → table/components}/FilterPicker.tsx +2 -3
  196. package/src/core/table/index.ts +12 -0
  197. package/src/core/{components/table → table/interfaces}/types.ts +2 -2
  198. package/src/demo/DemoRouter.ts +87 -6
  199. package/src/demo/components/DemoHome.tsx +6 -10
  200. package/src/demo/components/DemoLayout.tsx +38 -8
  201. package/src/demo/components/auth/DemoLogin.tsx +1 -1
  202. package/src/demo/components/auth/DemoRegister.tsx +1 -1
  203. package/src/demo/components/auth/DemoResetPassword.tsx +1 -1
  204. package/src/demo/components/auth/DemoVerifyEmail.tsx +1 -1
  205. package/src/demo/components/core/DemoButton.tsx +160 -0
  206. package/src/demo/components/core/DemoFlex.tsx +101 -0
  207. package/src/demo/components/core/DemoHeading.tsx +13 -0
  208. package/src/demo/components/core/DemoText.tsx +110 -0
  209. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  210. package/src/demo/components/layout/DemoDialog.tsx +103 -0
  211. package/src/demo/components/{core → layout}/DemoSidebar.tsx +0 -1
  212. package/src/demo/components/layout/DemoToast.tsx +96 -0
  213. package/src/demo/components/shared/MacWindow.tsx +149 -74
  214. package/src/demo/components/shared/Showcase.tsx +4 -8
  215. package/src/demo/index.ts +1 -4
  216. package/src/demo/primitives/$uiDemo.ts +10 -0
  217. package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +0 -1
  218. package/dist/admin/AdminAudits-BQno3hZG.js.map +0 -1
  219. package/dist/admin/AdminFiles-kvuUaASF.js.map +0 -1
  220. package/dist/admin/AdminJobDashboard-CrPxp0W1.js.map +0 -1
  221. package/dist/admin/AdminJobExecutions-D-b4Zt7W.js.map +0 -1
  222. package/dist/admin/AdminJobRegistry-CNX5cpDx.js.map +0 -1
  223. package/dist/admin/AdminLayout-e-ZP5nWw.js +0 -37
  224. package/dist/admin/AdminLayout-e-ZP5nWw.js.map +0 -1
  225. package/dist/admin/AdminParameters-DCGbpt2c.js.map +0 -1
  226. package/dist/admin/AdminSessions-DyhW6RZv.js.map +0 -1
  227. package/dist/admin/AdminUserAudits-D1GcREEE.js +0 -177
  228. package/dist/admin/AdminUserAudits-D1GcREEE.js.map +0 -1
  229. package/dist/admin/AdminUserCreate-DR8LA0tv.js +0 -104
  230. package/dist/admin/AdminUserCreate-DR8LA0tv.js.map +0 -1
  231. package/dist/admin/AdminUserDetails-CDkZNHQD.js +0 -477
  232. package/dist/admin/AdminUserDetails-CDkZNHQD.js.map +0 -1
  233. package/dist/admin/AdminUserLayout-CrBj4UuI.js.map +0 -1
  234. package/dist/admin/AdminUserSessions-srgFHrqy.js +0 -129
  235. package/dist/admin/AdminUserSessions-srgFHrqy.js.map +0 -1
  236. package/dist/admin/AdminUserSettings-BFuxl-xT.js +0 -167
  237. package/dist/admin/AdminUserSettings-BFuxl-xT.js.map +0 -1
  238. package/dist/admin/AdminUsers-D1pDpiwK.js +0 -118
  239. package/dist/admin/AdminUsers-D1pDpiwK.js.map +0 -1
  240. package/dist/demo/DemoDataTable-lnBKWBf8.js.map +0 -1
  241. package/dist/demo/DemoHome-CUMZsYaH.js.map +0 -1
  242. package/dist/demo/DemoJsonViewer-_uokbGaW.js +0 -429
  243. package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +0 -1
  244. package/dist/demo/DemoLayout-DHVoacE6.js +0 -46
  245. package/dist/demo/DemoLayout-DHVoacE6.js.map +0 -1
  246. package/dist/demo/DemoLogin-DjJ9314c.js.map +0 -1
  247. package/dist/demo/DemoRegister-DzkJ5M83.js.map +0 -1
  248. package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +0 -1
  249. package/dist/demo/DemoSidebar-C1csnGhX.js.map +0 -1
  250. package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +0 -1
  251. package/dist/demo/Showcase-BzoXNlCn.js +0 -185
  252. package/dist/demo/Showcase-BzoXNlCn.js.map +0 -1
  253. package/dist/json/index.d.ts +0 -57
  254. package/dist/json/index.d.ts.map +0 -1
  255. package/dist/json/index.js +0 -325
  256. package/dist/json/index.js.map +0 -1
  257. package/src/admin/components/users/AdminUserAudits.tsx +0 -184
  258. package/src/admin/components/users/AdminUserCreate.tsx +0 -85
  259. package/src/admin/components/users/AdminUserDetails.tsx +0 -431
  260. package/src/admin/components/users/AdminUserSettings.tsx +0 -171
  261. package/src/core/components/data/ErrorViewer.tsx +0 -171
  262. package/src/json/extensions/DialogService.tsx +0 -31
  263. package/src/json/index.ts +0 -18
  264. package/src/json/styles.css +0 -1
  265. /package/dist/{demo → auth}/IconGoogle-Ch1m3Uzl.js +0 -0
  266. /package/dist/{demo → auth}/IconGoogle-Ch1m3Uzl.js.map +0 -0
  267. /package/src/{json → core/json}/components/JsonViewer.css +0 -0
  268. /package/src/{json → core/json}/components/JsonViewer.tsx +0 -0
  269. /package/src/core/{components/table → table/components}/DataTablePagination.tsx +0 -0
  270. /package/src/core/{components/table → table/components}/useTableSelection.ts +0 -0
@@ -1,20 +1,17 @@
1
- import { Center, Flex, Loader, Text } from "@mantine/core";
2
- import {
3
- IconBan,
4
- IconDevices,
5
- IconHistory,
6
- IconLock,
7
- IconMail,
8
- IconPencil,
9
- IconSettings,
10
- IconShieldCheck,
11
- IconTrash,
12
- IconUser,
13
- } from "@tabler/icons-react";
1
+ import { Flex, Text, useDialog, useToast } from "@alepha/ui";
2
+ import { Loader } from "@mantine/core";
3
+ import { IconBan, IconShieldCheck, IconTrash } from "@tabler/icons-react";
4
+ import { t } from "alepha";
14
5
  import type { AdminUserController, UserEntity } from "alepha/api/users";
15
6
  import { useClient } from "alepha/react";
16
7
  import { NestedView, useRouter, useRouterState } from "alepha/react/router";
17
- import { useEffect, useState } from "react";
8
+ import {
9
+ createContext,
10
+ useCallback,
11
+ useContext,
12
+ useEffect,
13
+ useState,
14
+ } from "react";
18
15
  import type { AdminRouter } from "../../AdminRouter.ts";
19
16
  import AdminResourceHeader from "../shared/AdminResourceHeader.tsx";
20
17
  import AdminResourceTabs from "../shared/AdminResourceTabs.tsx";
@@ -23,198 +20,164 @@ export interface AdminUserLayoutProps {
23
20
  userRealmName?: string;
24
21
  }
25
22
 
23
+ interface UserContextValue {
24
+ user: UserEntity;
25
+ reload: () => void;
26
+ }
27
+
28
+ const UserContext = createContext<UserContextValue | null>(null);
29
+
30
+ export const useUser = () => {
31
+ const ctx = useContext(UserContext);
32
+ if (!ctx) throw new Error("useUser must be used within AdminUserLayout");
33
+ return ctx;
34
+ };
35
+
36
+ const updateUserSchema = t.object({
37
+ email: t.optional(t.email()),
38
+ phoneNumber: t.optional(t.e164()),
39
+ firstName: t.optional(t.string()),
40
+ lastName: t.optional(t.string()),
41
+ roles: t.optional(t.array(t.string())),
42
+ enabled: t.optional(t.boolean()),
43
+ });
44
+
45
+ const displayName = (u: UserEntity) =>
46
+ u.firstName || u.lastName
47
+ ? `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim()
48
+ : u.username || u.email || "User";
49
+
26
50
  const AdminUserLayout = (props: AdminUserLayoutProps) => {
27
51
  const router = useRouter<AdminRouter>();
28
52
  const state = useRouterState();
29
53
  const client = useClient<AdminUserController>();
54
+ const dialog = useDialog();
55
+ const toast = useToast();
30
56
  const userId = state.params.userId as string;
31
57
 
32
58
  const [user, setUser] = useState<UserEntity | null>(null);
33
59
  const [loading, setLoading] = useState(true);
34
- const [actionLoading, setActionLoading] = useState<string | null>(null);
35
-
36
- useEffect(() => {
37
- const loadUser = async () => {
38
- try {
39
- const data = await client.getUser({
40
- params: { id: userId },
41
- query: { userRealmName: props.userRealmName },
42
- });
43
- setUser(data);
44
- } finally {
45
- setLoading(false);
46
- }
47
- };
48
-
49
- loadUser();
50
- }, [userId]);
51
-
52
- if (loading) {
53
- return (
54
- <Center flex={1}>
55
- <Loader />
56
- </Center>
57
- );
58
- }
59
-
60
- if (!user) {
61
- return (
62
- <Center flex={1}>
63
- <Flex direction="column" align="center" gap="xs">
64
- <IconUser size={48} opacity={0.3} />
65
- <Text c="dimmed">User not found</Text>
66
- </Flex>
67
- </Center>
68
- );
69
- }
70
60
 
71
- const displayName =
72
- user.firstName || user.lastName
73
- ? `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim()
74
- : user.username || user.email || "User";
75
-
76
- const currentPath = state.url.pathname;
77
- const getActiveTab = () => {
78
- if (currentPath.endsWith("/sessions")) return "sessions";
79
- if (currentPath.endsWith("/settings")) return "settings";
80
- if (currentPath.endsWith("/audits")) return "audits";
81
- return "profile";
82
- };
61
+ const realmQuery = { userRealmName: props.userRealmName };
83
62
 
84
- const handleBlockUser = async () => {
85
- setActionLoading("block");
63
+ const loadUser = useCallback(async () => {
64
+ setLoading(true);
86
65
  try {
87
- const updated = await client.updateUser({
66
+ const data = await client.getUser({
88
67
  params: { id: userId },
89
- query: { userRealmName: props.userRealmName },
90
- body: { enabled: !user.enabled },
68
+ query: realmQuery,
91
69
  });
92
- setUser(updated);
70
+ setUser(data);
93
71
  } finally {
94
- setActionLoading(null);
72
+ setLoading(false);
95
73
  }
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
- };
74
+ }, [userId, client]);
104
75
 
105
- const handleResetPassword = async () => {
106
- setActionLoading("reset");
107
- // TODO: Implement reset password
108
- await new Promise((resolve) => setTimeout(resolve, 1000));
109
- setActionLoading(null);
76
+ useEffect(() => {
77
+ loadUser();
78
+ }, [loadUser]);
79
+
80
+ const handleToggleEnabled = async () => {
81
+ if (!user) return;
82
+ const action = user.enabled ? "disable" : "enable";
83
+ const confirmed = await dialog.confirm({
84
+ title: `${user.enabled ? "Disable" : "Enable"} User`,
85
+ message: `Are you sure you want to ${action} ${displayName(user)}?`,
86
+ });
87
+ if (confirmed) {
88
+ const updated = await client.updateUser({
89
+ params: { id: user.id },
90
+ query: realmQuery,
91
+ body: { enabled: !user.enabled },
92
+ });
93
+ setUser(updated);
94
+ toast.success({ title: `User ${action}d` });
95
+ }
110
96
  };
111
97
 
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 {
98
+ const handleDelete = async () => {
99
+ if (!user) return;
100
+ const confirmed = await dialog.confirm({
101
+ title: "Delete User",
102
+ message: `Are you sure you want to delete ${displayName(user)}? This cannot be undone.`,
103
+ confirmColor: "red",
104
+ confirmLabel: "Delete",
105
+ });
106
+ if (confirmed) {
122
107
  await client.deleteUser({
123
- params: { id: userId },
124
- query: { userRealmName: props.userRealmName },
108
+ params: { id: user.id },
109
+ query: realmQuery,
125
110
  });
126
- await router.push("adminUsers");
127
- } finally {
128
- setActionLoading(null);
111
+ toast.success({ title: "User deleted" });
112
+ router.push("adminUsers");
129
113
  }
130
114
  };
131
115
 
116
+ if (loading) {
117
+ return (
118
+ <Flex flex={1} justify="center" align="center">
119
+ <Loader />
120
+ </Flex>
121
+ );
122
+ }
123
+
124
+ if (!user) {
125
+ return (
126
+ <Flex flex={1} justify="center" align="center">
127
+ <Text c="dimmed">User not found</Text>
128
+ </Flex>
129
+ );
130
+ }
131
+
132
132
  return (
133
- <Flex py="xl" px="xl" flex={1}>
134
- <Flex direction="column" gap="lg">
133
+ <UserContext.Provider value={{ user, reload: loadUser }}>
134
+ <Flex flex={1} direction="column" gap="lg" p="md">
135
135
  <AdminResourceHeader
136
136
  backHref={router.path("adminUsers")}
137
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}
138
+ title={displayName(user)}
139
+ subtitle={user.email || user.username}
142
140
  status={{
143
141
  label: user.enabled ? "Active" : "Disabled",
144
- color: user.enabled ? "green" : "red",
142
+ color: user.enabled ? "green" : "gray",
145
143
  }}
146
144
  menuActions={[
147
145
  {
148
- label: "Edit Profile",
149
- icon: IconPencil,
150
- href: router.path("adminUserDetails", { params: { userId } }),
151
- },
152
- {
153
- label: user.enabled ? "Disable User" : "Enable User",
146
+ label: user.enabled ? "Disable" : "Enable",
154
147
  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",
148
+ onClick: handleToggleEnabled,
174
149
  },
175
150
  {
176
- label: "Delete User",
151
+ label: "Delete",
177
152
  icon: IconTrash,
178
153
  color: "red",
179
- onClick: handleDeleteUser,
180
- loading: actionLoading === "delete",
154
+ onClick: handleDelete,
181
155
  },
182
156
  ]}
183
157
  />
184
158
 
185
159
  <AdminResourceTabs
186
- activeTab={getActiveTab()}
187
160
  tabs={[
188
161
  {
189
162
  value: "profile",
190
163
  label: "Profile",
191
- icon: IconUser,
192
- href: router.path("adminUserDetails", { params: { userId } }),
164
+ href: router.path("adminUserProfile", {
165
+ params: { userId },
166
+ }),
193
167
  },
194
168
  {
195
169
  value: "sessions",
196
170
  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 } }),
171
+ href: router.path("adminUserSessions", {
172
+ params: { userId },
173
+ }),
211
174
  },
212
175
  ]}
213
176
  />
214
177
 
215
178
  <NestedView />
216
179
  </Flex>
217
- </Flex>
180
+ </UserContext.Provider>
218
181
  );
219
182
  };
220
183
 
@@ -0,0 +1,50 @@
1
+ import { DetailList, Flex } from "@alepha/ui";
2
+ import { Badge } from "@mantine/core";
3
+ import { useI18n } from "alepha/react/i18n";
4
+ import { useUser } from "./AdminUserLayout.tsx";
5
+
6
+ const AdminUserProfile = () => {
7
+ const { user } = useUser();
8
+ const { l } = useI18n();
9
+
10
+ return (
11
+ <DetailList
12
+ items={[
13
+ { label: "ID", value: user.id, copyable: user.id },
14
+ { label: "Username", value: user.username },
15
+ { label: "Email", value: user.email },
16
+ {
17
+ label: "Email Verified",
18
+ value: user.emailVerified ? "Yes" : "No",
19
+ },
20
+ { label: "Phone", value: user.phoneNumber },
21
+ { label: "First Name", value: user.firstName },
22
+ { label: "Last Name", value: user.lastName },
23
+ { label: "Realm", value: user.realm },
24
+ {
25
+ label: "Roles",
26
+ value:
27
+ user.roles.length > 0 ? (
28
+ <Flex gap={4}>
29
+ {user.roles.map((role) => (
30
+ <Badge key={role} size="xs" variant="default">
31
+ {role}
32
+ </Badge>
33
+ ))}
34
+ </Flex>
35
+ ) : null,
36
+ },
37
+ {
38
+ label: "Created",
39
+ value: String(l(user.createdAt, { date: "lll" })),
40
+ },
41
+ {
42
+ label: "Updated",
43
+ value: String(l(user.updatedAt, { date: "lll" })),
44
+ },
45
+ ]}
46
+ />
47
+ );
48
+ };
49
+
50
+ export default AdminUserProfile;
@@ -1,5 +1,4 @@
1
- import { ActionButton, DataTable, Flex, Text } from "@alepha/ui";
2
- import { Badge } from "@mantine/core";
1
+ import { DataTable, Flex, Text, useDialog } from "@alepha/ui";
3
2
  import {
4
3
  IconDeviceDesktop,
5
4
  IconDeviceMobile,
@@ -17,142 +16,123 @@ export interface AdminUserSessionsProps {
17
16
  userRealmName?: string;
18
17
  }
19
18
 
19
+ const emptyFilters = t.object({});
20
+
21
+ const getDeviceIcon = (device?: string) => {
22
+ switch (device) {
23
+ case "MOBILE":
24
+ return <IconDeviceMobile size={14} />;
25
+ case "TABLET":
26
+ return <IconDeviceTablet size={14} />;
27
+ default:
28
+ return <IconDeviceDesktop size={14} />;
29
+ }
30
+ };
31
+
32
+ const isExpired = (expiresAt: Date | string) =>
33
+ new Date(expiresAt) < new Date();
34
+
20
35
  const AdminUserSessions = (props: AdminUserSessionsProps) => {
21
36
  const state = useRouterState();
37
+ const userId = state.params.userId as string;
22
38
  const client = useClient<AdminSessionController>();
23
39
  const { l } = useI18n();
24
- const userId = state.params.userId as string;
40
+ const dialog = useDialog();
25
41
  const [refreshKey, setRefreshKey] = useState(0);
26
42
 
27
- const getDeviceIcon = (device?: string) => {
28
- switch (device) {
29
- case "MOBILE":
30
- return <IconDeviceMobile size={14} />;
31
- case "TABLET":
32
- return <IconDeviceTablet size={14} />;
33
- default:
34
- return <IconDeviceDesktop size={14} />;
35
- }
36
- };
37
-
38
- const isExpired = (expiresAt: Date | string) => {
39
- return new Date(expiresAt) < new Date();
40
- };
41
-
42
- const handleDelete = async (sessionId: string) => {
43
- await client.deleteSession({
44
- params: { id: sessionId },
45
- query: { userRealmName: props.userRealmName },
43
+ const handleDeleteSession = async (sessionId: string) => {
44
+ const confirmed = await dialog.confirm({
45
+ title: "Revoke Session",
46
+ message: "Are you sure you want to revoke this session?",
46
47
  });
47
- setRefreshKey((k) => k + 1);
48
+ if (confirmed) {
49
+ await client.deleteSession({
50
+ params: { id: sessionId },
51
+ query: { userRealmName: props.userRealmName },
52
+ });
53
+ setRefreshKey((k) => k + 1);
54
+ }
48
55
  };
49
56
 
50
- const filters = t.object({});
51
-
52
57
  return (
53
- <Flex flex={1} direction="column">
54
- <DataTable<SessionEntity, typeof filters>
55
- key={refreshKey}
56
- submitOnInit
57
- defaultSize={10}
58
- filters={filters}
59
- tableProps={{
60
- horizontalSpacing: "xs",
61
- verticalSpacing: "xs",
62
- }}
63
- tableTrProps={(item) => {
64
- if (isExpired(item.expiresAt)) {
65
- return {
66
- opacity: 0.5,
67
- };
68
- }
69
- return {};
70
- }}
71
- items={async (filters) => {
72
- const response = await client.findSessions({
73
- query: {
74
- ...filters,
75
- userId,
76
- userRealmName: props.userRealmName,
77
- },
78
- });
79
-
80
- return response as Page<SessionEntity>;
81
- }}
82
- columns={{
83
- userAgent: {
84
- label: "Device",
85
- value: (item) => (
86
- <Flex gap={4}>
87
- {item.userAgent ? (
88
- <>
89
- <Badge
90
- size="xs"
91
- variant="light"
92
- leftSection={getDeviceIcon(item.userAgent.device)}
93
- >
94
- {item.userAgent.device}
95
- </Badge>
96
- <Text size="xs" c="dimmed">
97
- {item.userAgent.browser} / {item.userAgent.os}
98
- </Text>
99
- </>
100
- ) : (
101
- <Text size="xs" c="dimmed">
102
- -
103
- </Text>
104
- )}
105
- </Flex>
106
- ),
107
- },
108
- ip: {
109
- label: "IP Address",
110
- fit: true,
111
- value: (item) => (
112
- <Text size="xs" ff="monospace" c="dimmed">
113
- {item.ip || "-"}
114
- </Text>
115
- ),
116
- },
117
- expiresAt: {
118
- label: "Status",
119
- fit: true,
120
- value: (item) => (
121
- <Badge
122
- size="sm"
123
- variant="light"
124
- color={isExpired(item.expiresAt) ? "red" : "green"}
125
- >
126
- {isExpired(item.expiresAt) ? "Expired" : "Active"}
127
- </Badge>
128
- ),
58
+ <DataTable<SessionEntity, typeof emptyFilters>
59
+ key={refreshKey}
60
+ submitOnInit
61
+ defaultSize={10}
62
+ filters={emptyFilters}
63
+ tableProps={{
64
+ horizontalSpacing: "xs",
65
+ verticalSpacing: "xs",
66
+ }}
67
+ tableTrProps={(item) => ({
68
+ style: {
69
+ opacity: isExpired(item.expiresAt) ? 0.5 : 1,
70
+ },
71
+ })}
72
+ items={async (filters) => {
73
+ const response = await client.findSessions({
74
+ query: {
75
+ ...filters,
76
+ userId,
77
+ userRealmName: props.userRealmName,
129
78
  },
130
- createdAt: {
131
- label: "Created",
132
- fit: true,
133
- value: (item) => (
134
- <Text size="xs" c="dimmed">
135
- {l(item.createdAt, { date: "fromNow" })}
79
+ });
80
+ return response as Page<SessionEntity>;
81
+ }}
82
+ columns={{
83
+ device: {
84
+ label: "Device",
85
+ value: (item) => (
86
+ <Flex gap={4} align="center">
87
+ {getDeviceIcon(item.userAgent?.device)}
88
+ <Text size="xs">
89
+ {item.userAgent
90
+ ? `${item.userAgent.browser} / ${item.userAgent.os}`
91
+ : "\u2014"}
136
92
  </Text>
137
- ),
138
- },
139
- actions: {
140
- label: "",
141
- fit: true,
142
- value: (item) => (
143
- <ActionButton
144
- size="xs"
145
- variant="subtle"
146
- color="red"
147
- onClick={() => handleDelete(item.id)}
148
- >
149
- <IconTrash size={14} />
150
- </ActionButton>
151
- ),
152
- },
153
- }}
154
- />
155
- </Flex>
93
+ </Flex>
94
+ ),
95
+ },
96
+ ip: {
97
+ label: "IP",
98
+ fit: true,
99
+ value: (item) => (
100
+ <Text size="xs" ff="monospace" c="dimmed">
101
+ {item.ip || "\u2014"}
102
+ </Text>
103
+ ),
104
+ },
105
+ status: {
106
+ label: "Status",
107
+ fit: true,
108
+ value: (item) => (
109
+ <Text size="xs" c="dimmed">
110
+ {isExpired(item.expiresAt) ? "Expired" : "Active"}
111
+ </Text>
112
+ ),
113
+ },
114
+ createdAt: {
115
+ label: "Created",
116
+ fit: true,
117
+ value: (item) => (
118
+ <Text size="xs" c="dimmed">
119
+ {l(item.createdAt, { date: "fromNow" })}
120
+ </Text>
121
+ ),
122
+ },
123
+ actions: {
124
+ label: "",
125
+ fit: true,
126
+ actions: (item) => [
127
+ {
128
+ icon: <IconTrash size={14} />,
129
+ onClick: () => handleDeleteSession(item.id),
130
+ tooltip: "Revoke session",
131
+ },
132
+ ],
133
+ },
134
+ }}
135
+ />
156
136
  );
157
137
  };
158
138