@alepha/ui 0.18.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/dist/admin/{AdminApiKeys-BJhIwfD6.js → AdminApiKeys-Bt1PjO6o.js} +3 -4
  2. package/dist/admin/{AdminApiKeys-BJhIwfD6.js.map → AdminApiKeys-Bt1PjO6o.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-DzD_4cDt.js → AdminAudits-C7c1CN4c.js} +3 -4
  4. package/dist/admin/{AdminAudits-DzD_4cDt.js.map → AdminAudits-C7c1CN4c.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-C92tIc6x.js → AdminDashboard-C3RXpTp6.js} +3 -4
  6. package/dist/admin/{AdminDashboard-C92tIc6x.js.map → AdminDashboard-C3RXpTp6.js.map} +1 -1
  7. package/dist/admin/{AdminFiles-DLpfhBkf.js → AdminFiles-31ivR6Wq.js} +3 -4
  8. package/dist/admin/{AdminFiles-DLpfhBkf.js.map → AdminFiles-31ivR6Wq.js.map} +1 -1
  9. package/dist/admin/{AdminJobDashboard-KIOkeMgE.js → AdminJobDashboard-BABLe7hL.js} +73 -25
  10. package/dist/admin/AdminJobDashboard-BABLe7hL.js.map +1 -0
  11. package/dist/admin/{AdminJobExecutions-D0Yo_PU0.js → AdminJobExecutions-D-G8RIlr.js} +3 -4
  12. package/dist/admin/{AdminJobExecutions-D0Yo_PU0.js.map → AdminJobExecutions-D-G8RIlr.js.map} +1 -1
  13. package/dist/admin/{AdminJobRegistry-PFajqaGK.js → AdminJobRegistry-oIS3K9NX.js} +3 -4
  14. package/dist/admin/{AdminJobRegistry-PFajqaGK.js.map → AdminJobRegistry-oIS3K9NX.js.map} +1 -1
  15. package/dist/admin/{AdminLayout-B1DXZHDn.js → AdminLayout-BmZ9mtXh.js} +8 -25
  16. package/dist/admin/AdminLayout-BmZ9mtXh.js.map +1 -0
  17. package/dist/admin/AdminNotifications-DHdzksww.js +541 -0
  18. package/dist/admin/AdminNotifications-DHdzksww.js.map +1 -0
  19. package/dist/admin/{AdminParameters-BspPeqp_.js → AdminParameters-CyZQSXnN.js} +118 -112
  20. package/dist/admin/AdminParameters-CyZQSXnN.js.map +1 -0
  21. package/dist/admin/{AdminSessions-BnH5CZQl.js → AdminSessions--xwELDSO.js} +3 -4
  22. package/dist/admin/{AdminSessions-BnH5CZQl.js.map → AdminSessions--xwELDSO.js.map} +1 -1
  23. package/dist/admin/{AdminUserLayout-DUbC6-BI.js → AdminUserLayout-DvBTG5gd.js} +82 -115
  24. package/dist/admin/AdminUserLayout-DvBTG5gd.js.map +1 -0
  25. package/dist/admin/{AdminUserProfile-DuTUnjdG.js → AdminUserProfile-CzsPBl6Z.js} +7 -6
  26. package/dist/admin/AdminUserProfile-CzsPBl6Z.js.map +1 -0
  27. package/dist/admin/{AdminUserSessions-DvZdAGpL.js → AdminUserSessions-C-aUnhVN.js} +3 -4
  28. package/dist/admin/{AdminUserSessions-DvZdAGpL.js.map → AdminUserSessions-C-aUnhVN.js.map} +1 -1
  29. package/dist/admin/{AdminUsers-CR9z0g_5.js → AdminUsers-BYwei5sj.js} +4 -4
  30. package/dist/admin/AdminUsers-BYwei5sj.js.map +1 -0
  31. package/dist/admin/{AuthLayout-DsUfp9RG.js → AuthLayout-CkPGLJku.js} +3 -4
  32. package/dist/admin/{AuthLayout-DsUfp9RG.js.map → AuthLayout-CkPGLJku.js.map} +1 -1
  33. package/dist/{demo/IconGoogle-CSQLPYwX.js → admin/IconGoogle-8Nkx6yax.js} +2 -4
  34. package/dist/admin/{IconGoogle-Ch1m3Uzl.js.map → IconGoogle-8Nkx6yax.js.map} +1 -1
  35. package/dist/admin/Login-DSBqNsZc.js +274 -0
  36. package/dist/admin/Login-DSBqNsZc.js.map +1 -0
  37. package/dist/admin/{Profile-B2EcIDB9.js → Profile-CDRjJo0P.js} +31 -29
  38. package/dist/admin/Profile-CDRjJo0P.js.map +1 -0
  39. package/dist/admin/{Register-Z3fxRbUF.js → Register-4QGFOnfh.js} +201 -146
  40. package/dist/admin/Register-4QGFOnfh.js.map +1 -0
  41. package/dist/admin/{ResetPassword-_Y1qTTKh.js → ResetPassword-Gxc9L_mY.js} +9 -10
  42. package/dist/admin/ResetPassword-Gxc9L_mY.js.map +1 -0
  43. package/dist/admin/{VerifyEmail-Bg22bwcC.js → VerifyEmail-D7G5NnaN.js} +25 -11
  44. package/dist/admin/VerifyEmail-D7G5NnaN.js.map +1 -0
  45. package/dist/admin/adminUserAtom-DCi4wf-v.js +11 -0
  46. package/dist/admin/adminUserAtom-DCi4wf-v.js.map +1 -0
  47. package/dist/admin/{core-BVO_TQxb.js → core-D1AbU50V.js} +662 -570
  48. package/dist/admin/core-D1AbU50V.js.map +1 -0
  49. package/dist/admin/index.d.ts +141 -53
  50. package/dist/admin/index.d.ts.map +1 -1
  51. package/dist/admin/index.js +67 -49
  52. package/dist/admin/index.js.map +1 -1
  53. package/dist/admin/rolldown-runtime-CiIaOW0V.js +13 -0
  54. package/dist/{demo/AuthLayout-DN-ClJQk.js → auth/AuthLayout-CfRKcTqP.js} +3 -4
  55. package/dist/auth/{AuthLayout-C161NeF6.js.map → AuthLayout-CfRKcTqP.js.map} +1 -1
  56. package/dist/{admin/IconGoogle-Ch1m3Uzl.js → auth/IconGoogle-8Nkx6yax.js} +2 -4
  57. package/dist/auth/{IconGoogle-Ch1m3Uzl.js.map → IconGoogle-8Nkx6yax.js.map} +1 -1
  58. package/dist/auth/Login-DJyweoPS.js +274 -0
  59. package/dist/auth/Login-DJyweoPS.js.map +1 -0
  60. package/dist/auth/{Profile-BMpXJ0oi.js → Profile-Cy93pNTw.js} +31 -29
  61. package/dist/auth/Profile-Cy93pNTw.js.map +1 -0
  62. package/dist/auth/{Register-2gx8qll-.js → Register-CSqzzitW.js} +201 -146
  63. package/dist/auth/Register-CSqzzitW.js.map +1 -0
  64. package/dist/{demo/ResetPassword-CAPj8MO3.js → auth/ResetPassword-B61QPlQi.js} +9 -10
  65. package/dist/auth/ResetPassword-B61QPlQi.js.map +1 -0
  66. package/dist/{demo/VerifyEmail-DFmdCdYs.js → auth/VerifyEmail-CqBJ11id.js} +25 -11
  67. package/dist/auth/VerifyEmail-CqBJ11id.js.map +1 -0
  68. package/dist/auth/{core-DyfeVr5c.js → core-C6D3pazL.js} +403 -343
  69. package/dist/auth/core-C6D3pazL.js.map +1 -0
  70. package/dist/auth/index.d.ts +93 -54
  71. package/dist/auth/index.d.ts.map +1 -1
  72. package/dist/auth/index.js +30 -31
  73. package/dist/auth/index.js.map +1 -1
  74. package/dist/auth/rolldown-runtime-CiIaOW0V.js +13 -0
  75. package/dist/core/index.d.ts +123 -62
  76. package/dist/core/index.d.ts.map +1 -1
  77. package/dist/core/index.js +878 -776
  78. package/dist/core/index.js.map +1 -1
  79. package/dist/{auth/AuthLayout-C161NeF6.js → demo/AuthLayout-Dq5tSLSc.js} +3 -4
  80. package/dist/demo/{AuthLayout-DN-ClJQk.js.map → AuthLayout-Dq5tSLSc.js.map} +1 -1
  81. package/dist/demo/DemoButton-_Ws2w-J0.js +181 -0
  82. package/dist/demo/DemoButton-_Ws2w-J0.js.map +1 -0
  83. package/dist/demo/DemoControlSelect-ChP4ZOpQ.js +304 -0
  84. package/dist/demo/DemoControlSelect-ChP4ZOpQ.js.map +1 -0
  85. package/dist/demo/DemoDataTable-Hwf_UUni.js +361 -0
  86. package/dist/demo/DemoDataTable-Hwf_UUni.js.map +1 -0
  87. package/dist/demo/{DemoDialog-DW8QEvD1.js → DemoDialog-B01OMVRd.js} +3 -4
  88. package/dist/demo/{DemoDialog-DW8QEvD1.js.map → DemoDialog-B01OMVRd.js.map} +1 -1
  89. package/dist/demo/{DemoFlex-CAhLUanT.js → DemoFlex-870PEl0V.js} +4 -5
  90. package/dist/demo/{DemoFlex-CAhLUanT.js.map → DemoFlex-870PEl0V.js.map} +1 -1
  91. package/dist/demo/{DemoHeading-yIFmNjHB.js → DemoHeading-C1YR27fz.js} +4 -5
  92. package/dist/demo/{DemoHeading-yIFmNjHB.js.map → DemoHeading-C1YR27fz.js.map} +1 -1
  93. package/dist/demo/{DemoHome-BSGuBHus.js → DemoHome-DRbL2eGf.js} +4 -5
  94. package/dist/demo/{DemoHome-BSGuBHus.js.map → DemoHome-DRbL2eGf.js.map} +1 -1
  95. package/dist/demo/{DemoJsonViewer-DsA2IpgV.js → DemoJsonViewer-DoABiqBW.js} +4 -5
  96. package/dist/demo/{DemoJsonViewer-DsA2IpgV.js.map → DemoJsonViewer-DoABiqBW.js.map} +1 -1
  97. package/dist/demo/{DemoLayout-Cy6xjn6P.js → DemoLayout-CN_PDCX2.js} +16 -8
  98. package/dist/demo/DemoLayout-CN_PDCX2.js.map +1 -0
  99. package/dist/demo/{DemoLogin-vqxgTu4P.js → DemoLogin-B5x-ug3Q.js} +51 -35
  100. package/dist/demo/DemoLogin-B5x-ug3Q.js.map +1 -0
  101. package/dist/demo/{DemoRegister-YHPvPg77.js → DemoRegister-Q6sg2xuV.js} +51 -53
  102. package/dist/demo/DemoRegister-Q6sg2xuV.js.map +1 -0
  103. package/dist/demo/{DemoResetPassword-mOW18Zlm.js → DemoResetPassword-DrqZfmEw.js} +14 -19
  104. package/dist/demo/DemoResetPassword-DrqZfmEw.js.map +1 -0
  105. package/dist/demo/{DemoSidebar-od7aLjP_.js → DemoSidebar-CfKS6w1o.js} +4 -5
  106. package/dist/demo/{DemoSidebar-od7aLjP_.js.map → DemoSidebar-CfKS6w1o.js.map} +1 -1
  107. package/dist/demo/{DemoText-DU3JeRS0.js → DemoText-pT6Gi5b5.js} +4 -5
  108. package/dist/demo/{DemoText-DU3JeRS0.js.map → DemoText-pT6Gi5b5.js.map} +1 -1
  109. package/dist/demo/{DemoToast-CUJEiPRa.js → DemoToast-I13NBzQQ.js} +3 -4
  110. package/dist/demo/{DemoToast-CUJEiPRa.js.map → DemoToast-I13NBzQQ.js.map} +1 -1
  111. package/dist/demo/{DemoTypeForm-C1dNkahD.js → DemoTypeForm-BqzcrtvN.js} +9 -6
  112. package/dist/demo/DemoTypeForm-BqzcrtvN.js.map +1 -0
  113. package/dist/demo/DemoVerifyEmail-HwD8xfQw.js +33 -0
  114. package/dist/demo/DemoVerifyEmail-HwD8xfQw.js.map +1 -0
  115. package/dist/{auth/IconGoogle-Ch1m3Uzl.js → demo/IconGoogle-CwQy4G9y.js} +2 -4
  116. package/dist/demo/{IconGoogle-CSQLPYwX.js.map → IconGoogle-CwQy4G9y.js.map} +1 -1
  117. package/dist/demo/Login-CqG1iJbn.js +274 -0
  118. package/dist/demo/Login-CqG1iJbn.js.map +1 -0
  119. package/dist/demo/{Profile-BE_Y3co2.js → Profile-C0ojJCaG.js} +31 -29
  120. package/dist/demo/Profile-C0ojJCaG.js.map +1 -0
  121. package/dist/demo/{Register-fXHmBpr3.js → Register-KKZwr_lL.js} +201 -146
  122. package/dist/demo/Register-KKZwr_lL.js.map +1 -0
  123. package/dist/{auth/ResetPassword-DBxt9hKk.js → demo/ResetPassword-DMrLFEtr.js} +9 -10
  124. package/dist/demo/ResetPassword-DMrLFEtr.js.map +1 -0
  125. package/dist/demo/{Showcase-BtEU0pY9.js → Showcase-D49Wud2v.js} +65 -68
  126. package/dist/demo/Showcase-D49Wud2v.js.map +1 -0
  127. package/dist/{auth/VerifyEmail-Z80Ubajk.js → demo/VerifyEmail-BFCAFz6T.js} +25 -11
  128. package/dist/demo/VerifyEmail-BFCAFz6T.js.map +1 -0
  129. package/dist/demo/{auth-Djd7SKiw.js → auth-D9qTZzCa.js} +18 -35
  130. package/dist/demo/{auth-Djd7SKiw.js.map → auth-D9qTZzCa.js.map} +1 -1
  131. package/dist/demo/{core-B7LNjM78.js → core-DRtQklr3.js} +752 -647
  132. package/dist/demo/core-DRtQklr3.js.map +1 -0
  133. package/dist/demo/index.d.ts +1 -0
  134. package/dist/demo/index.d.ts.map +1 -1
  135. package/dist/demo/index.js +25 -22
  136. package/dist/demo/index.js.map +1 -1
  137. package/dist/demo/rolldown-runtime-CiIaOW0V.js +13 -0
  138. package/package.json +19 -19
  139. package/src/admin/AdminRouter.tsx +42 -2
  140. package/src/admin/atoms/adminUserAtom.ts +7 -0
  141. package/src/admin/components/AdminLayout.tsx +2 -14
  142. package/src/admin/components/jobs/AdminJobDashboard.tsx +51 -20
  143. package/src/admin/components/notifications/AdminNotifications.tsx +519 -0
  144. package/src/admin/components/parameters/ParameterDetails.tsx +12 -270
  145. package/src/admin/components/parameters/ParameterDetailsConfigForm.tsx +238 -0
  146. package/src/admin/components/parameters/ParameterDetailsLoading.tsx +24 -0
  147. package/src/admin/components/parameters/ParameterHistory.tsx +10 -11
  148. package/src/admin/components/parameters/ParameterTree.tsx +28 -184
  149. package/src/admin/components/parameters/ParameterTreeNode.tsx +151 -0
  150. package/src/admin/components/shared/AdminResourceHeader.tsx +2 -25
  151. package/src/admin/components/shared/AdminResourceHeaderMenuItem.tsx +37 -0
  152. package/src/admin/components/shared/AdminResourceTabs.tsx +2 -26
  153. package/src/admin/components/shared/AdminResourceTabsItem.tsx +36 -0
  154. package/src/admin/components/users/AdminUserLayout.tsx +84 -127
  155. package/src/admin/components/users/AdminUserProfile.tsx +5 -2
  156. package/src/admin/components/users/AdminUsers.tsx +1 -1
  157. package/src/auth/components/Login.tsx +188 -121
  158. package/src/auth/components/Profile.tsx +1 -22
  159. package/src/auth/components/ProfileField.tsx +39 -0
  160. package/src/auth/components/Register.tsx +215 -158
  161. package/src/auth/components/ResetPassword.tsx +7 -11
  162. package/src/auth/components/VerifyEmail.tsx +35 -10
  163. package/src/auth/components/buttons/UserButton.tsx +19 -21
  164. package/src/auth/index.ts +1 -0
  165. package/src/core/components/Flex.tsx +34 -0
  166. package/src/core/components/buttons/ActionButton.tsx +105 -78
  167. package/src/core/components/data/DetailDrawer.tsx +102 -96
  168. package/src/core/components/data/DetailList.tsx +2 -1
  169. package/src/core/components/dialogs/PromptDialog.tsx +1 -1
  170. package/src/core/components/layout/Breadcrumb.tsx +4 -7
  171. package/src/core/components/layout/DashboardShell.tsx +18 -4
  172. package/src/core/components/layout/Sidebar.tsx +16 -241
  173. package/src/core/components/layout/SidebarCollapsedItem.tsx +91 -0
  174. package/src/core/components/layout/SidebarItem.tsx +146 -0
  175. package/src/core/components/layout/index.ts +3 -1
  176. package/src/core/form/components/Control.tsx +31 -29
  177. package/src/core/form/components/ControlArray.tsx +13 -39
  178. package/src/core/form/components/ControlDate.tsx +10 -21
  179. package/src/core/form/components/ControlNumber.tsx +4 -33
  180. package/src/core/form/components/ControlQueryBuilder.tsx +12 -175
  181. package/src/core/form/components/ControlQueryBuilderHelp.tsx +165 -0
  182. package/src/core/form/components/ControlSelect.browser.spec.tsx +343 -0
  183. package/src/core/form/components/ControlSelect.tsx +294 -92
  184. package/src/core/form/components/TypeForm.browser.spec.tsx +3 -3
  185. package/src/core/form/components/TypeForm.tsx +5 -2
  186. package/src/core/form/index.ts +8 -1
  187. package/src/core/form/utils/parseInput.ts +7 -3
  188. package/src/core/index.ts +3 -1
  189. package/src/core/json/components/JsonViewer.tsx +103 -319
  190. package/src/core/json/components/JsonViewerCopyButton.tsx +46 -0
  191. package/src/core/json/components/JsonViewerRowNode.tsx +120 -0
  192. package/src/core/json/components/JsonViewerShared.ts +76 -0
  193. package/src/core/services/DialogService.tsx +2 -2
  194. package/src/core/styles.css +13 -2
  195. package/src/core/table/components/ColumnPicker.tsx +3 -3
  196. package/src/core/table/components/DataTable.tsx +88 -29
  197. package/src/core/table/components/DataTableFilters.tsx +6 -11
  198. package/src/core/table/components/DataTablePagination.tsx +9 -3
  199. package/src/core/table/components/DataTableToolbar.tsx +7 -3
  200. package/src/core/table/components/FilterPicker.tsx +3 -3
  201. package/src/core/table/interfaces/types.ts +29 -0
  202. package/src/core/utils/icons.tsx +2 -2
  203. package/src/demo/DemoRouter.ts +8 -1
  204. package/src/demo/components/DemoLayout.tsx +12 -2
  205. package/src/demo/components/auth/DemoLogin.tsx +35 -28
  206. package/src/demo/components/auth/DemoRegister.tsx +35 -49
  207. package/src/demo/components/auth/DemoResetPassword.tsx +5 -9
  208. package/src/demo/components/auth/DemoVerifyEmail.tsx +7 -6
  209. package/src/demo/components/core/DemoButton.tsx +123 -103
  210. package/src/demo/components/core/DemoControlSelect.tsx +325 -0
  211. package/src/demo/components/core/DemoDataTable.tsx +255 -237
  212. package/src/demo/components/core/DemoTypeForm.tsx +7 -2
  213. package/src/demo/components/shared/MacWindow.tsx +5 -11
  214. package/src/demo/components/shared/Showcase.tsx +28 -42
  215. package/dist/admin/AdminJobDashboard-KIOkeMgE.js.map +0 -1
  216. package/dist/admin/AdminLayout-B1DXZHDn.js.map +0 -1
  217. package/dist/admin/AdminParameters-BspPeqp_.js.map +0 -1
  218. package/dist/admin/AdminUserLayout-DUbC6-BI.js.map +0 -1
  219. package/dist/admin/AdminUserProfile-DuTUnjdG.js.map +0 -1
  220. package/dist/admin/AdminUsers-CR9z0g_5.js.map +0 -1
  221. package/dist/admin/Login-DHbYJKwg.js +0 -219
  222. package/dist/admin/Login-DHbYJKwg.js.map +0 -1
  223. package/dist/admin/Profile-B2EcIDB9.js.map +0 -1
  224. package/dist/admin/Register-Z3fxRbUF.js.map +0 -1
  225. package/dist/admin/ResetPassword-_Y1qTTKh.js.map +0 -1
  226. package/dist/admin/VerifyEmail-Bg22bwcC.js.map +0 -1
  227. package/dist/admin/core-BVO_TQxb.js.map +0 -1
  228. package/dist/admin/rolldown-runtime-CjeV3_4I.js +0 -18
  229. package/dist/auth/Login-C7jIqf00.js +0 -219
  230. package/dist/auth/Login-C7jIqf00.js.map +0 -1
  231. package/dist/auth/Profile-BMpXJ0oi.js.map +0 -1
  232. package/dist/auth/Register-2gx8qll-.js.map +0 -1
  233. package/dist/auth/ResetPassword-DBxt9hKk.js.map +0 -1
  234. package/dist/auth/VerifyEmail-Z80Ubajk.js.map +0 -1
  235. package/dist/auth/core-DyfeVr5c.js.map +0 -1
  236. package/dist/auth/rolldown-runtime-CjeV3_4I.js +0 -18
  237. package/dist/demo/DemoButton-CGUyR9eM.js +0 -178
  238. package/dist/demo/DemoButton-CGUyR9eM.js.map +0 -1
  239. package/dist/demo/DemoDataTable-QFG-xXSx.js +0 -358
  240. package/dist/demo/DemoDataTable-QFG-xXSx.js.map +0 -1
  241. package/dist/demo/DemoLayout-Cy6xjn6P.js.map +0 -1
  242. package/dist/demo/DemoLogin-vqxgTu4P.js.map +0 -1
  243. package/dist/demo/DemoRegister-YHPvPg77.js.map +0 -1
  244. package/dist/demo/DemoResetPassword-mOW18Zlm.js.map +0 -1
  245. package/dist/demo/DemoTypeForm-C1dNkahD.js.map +0 -1
  246. package/dist/demo/DemoVerifyEmail-D9EcXZ38.js +0 -30
  247. package/dist/demo/DemoVerifyEmail-D9EcXZ38.js.map +0 -1
  248. package/dist/demo/Login-CoYf_P_F.js +0 -219
  249. package/dist/demo/Login-CoYf_P_F.js.map +0 -1
  250. package/dist/demo/Profile-BE_Y3co2.js.map +0 -1
  251. package/dist/demo/Register-fXHmBpr3.js.map +0 -1
  252. package/dist/demo/ResetPassword-CAPj8MO3.js.map +0 -1
  253. package/dist/demo/Showcase-BtEU0pY9.js.map +0 -1
  254. package/dist/demo/VerifyEmail-DFmdCdYs.js.map +0 -1
  255. package/dist/demo/core-B7LNjM78.js.map +0 -1
  256. package/dist/demo/rolldown-runtime-CjeV3_4I.js +0 -18
  257. package/src/demo/styles.css +0 -0
@@ -1,47 +1,19 @@
1
- import { Flex, Text, useDialog, useToast } from "@alepha/ui";
2
- import { Loader } from "@mantine/core";
1
+ import { Flex, useDialog, useToast } from "@alepha/ui";
3
2
  import { IconBan, IconShieldCheck, IconTrash } from "@tabler/icons-react";
4
- import { t } from "alepha";
5
3
  import type { AdminUserController, UserEntity } from "alepha/api/users";
6
- import { useClient } from "alepha/react";
7
- import { NestedView, useRouter, useRouterState } from "alepha/react/router";
8
- import {
9
- createContext,
10
- useCallback,
11
- useContext,
12
- useEffect,
13
- useState,
14
- } from "react";
4
+ import { useAlepha, useClient, useStore } from "alepha/react";
5
+ import { NestedView, useRouter } from "alepha/react/router";
6
+ import { useCallback, useEffect } from "react";
15
7
  import type { AdminRouter } from "../../AdminRouter.ts";
8
+ import { adminUserAtom } from "../../atoms/adminUserAtom.ts";
16
9
  import AdminResourceHeader from "../shared/AdminResourceHeader.tsx";
17
10
  import AdminResourceTabs from "../shared/AdminResourceTabs.tsx";
18
11
 
19
12
  export interface AdminUserLayoutProps {
20
- userRealmName?: string;
21
- }
22
-
23
- interface UserContextValue {
24
13
  user: UserEntity;
25
- reload: () => void;
14
+ userRealmName?: string;
26
15
  }
27
16
 
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
17
  const displayName = (u: UserEntity) =>
46
18
  u.firstName || u.lastName
47
19
  ? `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim()
@@ -49,63 +21,55 @@ const displayName = (u: UserEntity) =>
49
21
 
50
22
  const AdminUserLayout = (props: AdminUserLayoutProps) => {
51
23
  const router = useRouter<AdminRouter>();
52
- const state = useRouterState();
53
24
  const client = useClient<AdminUserController>();
25
+ const alepha = useAlepha();
54
26
  const dialog = useDialog();
55
27
  const toast = useToast();
56
- const userId = state.params.userId as string;
28
+ const [user] = useStore(adminUserAtom);
57
29
 
58
- const [user, setUser] = useState<UserEntity | null>(null);
59
- const [loading, setLoading] = useState(true);
30
+ useEffect(() => {
31
+ alepha.store.set(adminUserAtom, props.user);
32
+ }, [props.user]);
60
33
 
34
+ const currentUser = user ?? props.user;
35
+ const userId = currentUser.id;
61
36
  const realmQuery = { userRealmName: props.userRealmName };
62
37
 
63
- const loadUser = useCallback(async () => {
64
- setLoading(true);
65
- try {
66
- const data = await client.getUser({
67
- params: { id: userId },
68
- query: realmQuery,
69
- });
70
- setUser(data);
71
- } finally {
72
- setLoading(false);
73
- }
38
+ const reload = useCallback(async () => {
39
+ const data = await client.getUser({
40
+ params: { id: userId },
41
+ query: realmQuery,
42
+ });
43
+ alepha.store.set(adminUserAtom, data);
74
44
  }, [userId, client]);
75
45
 
76
- useEffect(() => {
77
- loadUser();
78
- }, [loadUser]);
79
-
80
46
  const handleToggleEnabled = async () => {
81
- if (!user) return;
82
- const action = user.enabled ? "disable" : "enable";
47
+ const action = currentUser.enabled ? "disable" : "enable";
83
48
  const confirmed = await dialog.confirm({
84
- title: `${user.enabled ? "Disable" : "Enable"} User`,
85
- message: `Are you sure you want to ${action} ${displayName(user)}?`,
49
+ title: `${currentUser.enabled ? "Disable" : "Enable"} User`,
50
+ message: `Are you sure you want to ${action} ${displayName(currentUser)}?`,
86
51
  });
87
52
  if (confirmed) {
88
53
  const updated = await client.updateUser({
89
- params: { id: user.id },
54
+ params: { id: currentUser.id },
90
55
  query: realmQuery,
91
- body: { enabled: !user.enabled },
56
+ body: { enabled: !currentUser.enabled },
92
57
  });
93
- setUser(updated);
58
+ alepha.store.set(adminUserAtom, updated);
94
59
  toast.success({ title: `User ${action}d` });
95
60
  }
96
61
  };
97
62
 
98
63
  const handleDelete = async () => {
99
- if (!user) return;
100
64
  const confirmed = await dialog.confirm({
101
65
  title: "Delete User",
102
- message: `Are you sure you want to delete ${displayName(user)}? This cannot be undone.`,
66
+ message: `Are you sure you want to delete ${displayName(currentUser)}? This cannot be undone.`,
103
67
  confirmColor: "red",
104
68
  confirmLabel: "Delete",
105
69
  });
106
70
  if (confirmed) {
107
71
  await client.deleteUser({
108
- params: { id: user.id },
72
+ params: { id: currentUser.id },
109
73
  query: realmQuery,
110
74
  });
111
75
  toast.success({ title: "User deleted" });
@@ -113,71 +77,64 @@ const AdminUserLayout = (props: AdminUserLayoutProps) => {
113
77
  }
114
78
  };
115
79
 
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
80
  return (
133
- <UserContext.Provider value={{ user, reload: loadUser }}>
134
- <Flex flex={1} direction="column" gap="lg" p="md">
135
- <AdminResourceHeader
136
- backHref={router.path("adminUsers")}
137
- backLabel="Users"
138
- title={displayName(user)}
139
- subtitle={user.email || user.username}
140
- status={{
141
- label: user.enabled ? "Active" : "Disabled",
142
- color: user.enabled ? "green" : "gray",
143
- }}
144
- menuActions={[
145
- {
146
- label: user.enabled ? "Disable" : "Enable",
147
- icon: user.enabled ? IconBan : IconShieldCheck,
148
- onClick: handleToggleEnabled,
149
- },
150
- {
151
- label: "Delete",
152
- icon: IconTrash,
153
- color: "red",
154
- onClick: handleDelete,
155
- },
156
- ]}
157
- />
158
-
159
- <AdminResourceTabs
160
- tabs={[
161
- {
162
- value: "profile",
163
- label: "Profile",
164
- href: router.path("adminUserProfile", {
165
- params: { userId },
166
- }),
167
- },
168
- {
169
- value: "sessions",
170
- label: "Sessions",
171
- href: router.path("adminUserSessions", {
172
- params: { userId },
173
- }),
174
- },
175
- ]}
176
- />
177
-
178
- <NestedView />
179
- </Flex>
180
- </UserContext.Provider>
81
+ <Flex
82
+ flex={1}
83
+ direction="column"
84
+ gap="lg"
85
+ p="md"
86
+ m="md"
87
+ style={{
88
+ backgroundColor: "var(--mantine-color-body)",
89
+ borderRadius: "var(--mantine-radius-md)",
90
+ border: "1px solid var(--mantine-color-default-border)",
91
+ }}
92
+ >
93
+ <AdminResourceHeader
94
+ backHref={router.path("adminUsers")}
95
+ backLabel="Users"
96
+ title={displayName(currentUser)}
97
+ subtitle={currentUser.email || currentUser.username}
98
+ status={{
99
+ label: currentUser.enabled ? "Active" : "Disabled",
100
+ color: currentUser.enabled ? "green" : "gray",
101
+ }}
102
+ menuActions={[
103
+ {
104
+ label: currentUser.enabled ? "Disable" : "Enable",
105
+ icon: currentUser.enabled ? IconBan : IconShieldCheck,
106
+ onClick: handleToggleEnabled,
107
+ },
108
+ {
109
+ label: "Delete",
110
+ icon: IconTrash,
111
+ color: "red",
112
+ onClick: handleDelete,
113
+ },
114
+ ]}
115
+ />
116
+
117
+ <AdminResourceTabs
118
+ tabs={[
119
+ {
120
+ value: "profile",
121
+ label: "Profile",
122
+ href: router.path("adminUserProfile", {
123
+ params: { userId },
124
+ }),
125
+ },
126
+ {
127
+ value: "sessions",
128
+ label: "Sessions",
129
+ href: router.path("adminUserSessions", {
130
+ params: { userId },
131
+ }),
132
+ },
133
+ ]}
134
+ />
135
+
136
+ <NestedView />
137
+ </Flex>
181
138
  );
182
139
  };
183
140
 
@@ -1,12 +1,15 @@
1
1
  import { DetailList, Flex } from "@alepha/ui";
2
2
  import { Badge } from "@mantine/core";
3
+ import { useStore } from "alepha/react";
3
4
  import { useI18n } from "alepha/react/i18n";
4
- import { useUser } from "./AdminUserLayout.tsx";
5
+ import { adminUserAtom } from "../../atoms/adminUserAtom.ts";
5
6
 
6
7
  const AdminUserProfile = () => {
7
- const { user } = useUser();
8
+ const [user] = useStore(adminUserAtom);
8
9
  const { l } = useI18n();
9
10
 
11
+ if (!user) return null;
12
+
10
13
  return (
11
14
  <DetailList
12
15
  items={[
@@ -152,7 +152,7 @@ const AdminUsers = (props: AdminUsersProps) => {
152
152
  >
153
153
  <Flex gap={"xs"} centerY>
154
154
  <Avatar size={"sm"} name={name} />
155
- <Flex col>
155
+ <Flex col align={"start"}>
156
156
  <Text size={"sm"} bold>
157
157
  {name}
158
158
  </Text>
@@ -1,6 +1,6 @@
1
1
  import { ActionButton, Control, capitalize } from "@alepha/ui";
2
2
  import { Card, Flex, Image, Text, Title } from "@mantine/core";
3
- import { IconLock, IconUser } from "@tabler/icons-react";
3
+ import { IconLock, IconPhoto, IconUser } from "@tabler/icons-react";
4
4
  import { AlephaError, t } from "alepha";
5
5
  import type { RealmConfig } from "alepha/api/users";
6
6
  import { useAuth } from "alepha/react/auth";
@@ -10,17 +10,20 @@ import { useRouter } from "alepha/react/router";
10
10
  import { HttpError } from "alepha/server";
11
11
  import { useMemo } from "react";
12
12
  import type { AuthI18n } from "../AuthI18n.ts";
13
- import type { AuthRouter } from "../AuthRouter.ts";
14
13
  import IconGithub from "./icons/IconGithub.tsx";
15
14
  import IconGoogle from "./icons/IconGoogle.tsx";
16
15
 
17
16
  export interface LoginProps {
18
17
  realmConfig: RealmConfig;
18
+ registerPath?: string;
19
+ resetPasswordPath?: string;
20
+ variant?: "card" | "split";
21
+ image?: string;
19
22
  }
20
23
 
21
24
  const Login = (props: LoginProps) => {
22
25
  const auth = useAuth();
23
- const router = useRouter<AuthRouter>();
26
+ const router = useRouter();
24
27
  const { tr } = useI18n<AuthI18n, "en">();
25
28
  const redirect = router.query.r || "/";
26
29
 
@@ -33,9 +36,9 @@ const Login = (props: LoginProps) => {
33
36
  // Determine what login methods are available
34
37
  const loginMethods = useMemo(() => {
35
38
  const methods = [];
36
- if (settings.usernameEnabled !== false) methods.push("username");
37
- if (settings.emailEnabled !== false) methods.push("email");
38
- if (settings.phoneEnabled === true) methods.push("phone");
39
+ if (settings.username !== "none") methods.push("username");
40
+ if (settings.email !== "none") methods.push("email");
41
+ if (settings.phoneNumber !== "none") methods.push("phone");
39
42
  return methods;
40
43
  }, [settings]);
41
44
 
@@ -113,129 +116,193 @@ const Login = (props: LoginProps) => {
113
116
 
114
117
  const showOrDivider = credentialsProvider && externalLoginMethods.length > 0;
115
118
 
116
- return (
117
- <Flex flex={1} justify={"center"} align={"center"}>
118
- <Flex direction="column" gap={"sm"} w={360}>
119
- <Card withBorder p={"lg"} bg={"var(--alepha-elevated)"}>
120
- <Flex direction="column" gap={"md"}>
121
- {/* Realm branding */}
122
- {(settings.logoUrl ||
123
- settings.displayName ||
124
- settings.description) && (
125
- <Flex direction="column" 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
- </Flex>
146
- )}
119
+ const realmQuery = props.realmConfig.realmName
120
+ ? `?realm=${encodeURIComponent(props.realmConfig.realmName)}`
121
+ : "";
147
122
 
148
- {/* Credentials login form */}
149
- {credentialsProvider && (
150
- <>
151
- <form {...form.props}>
152
- <Flex direction="column" flex={1} gap={"md"}>
153
- <Control
154
- title={identifierTitle}
155
- input={form.input.identifier}
156
- icon={IconUser}
157
- text={{
158
- autoComplete: getAutoCompleteType(),
159
- }}
160
- />
161
- <Control
162
- title={tr("loginPassword")}
163
- input={form.input.password}
164
- icon={IconLock}
165
- password={{
166
- autoComplete: "current-password",
167
- }}
168
- />
169
- <ActionButton variant={"filled"} form={form}>
170
- {tr("loginSignIn")}
171
- </ActionButton>
172
- </Flex>
173
- </form>
174
- {settings.resetPasswordAllowed && (
175
- <Text size="sm" ta="center">
176
- <ActionButton
177
- href={router.path("resetPassword", {
178
- query: { realm: props.realmConfig.realmName },
179
- })}
180
- anchorProps={{ inherit: true }}
181
- >
182
- {tr("loginForgotPassword")}
183
- </ActionButton>
184
- </Text>
185
- )}
186
- </>
187
- )}
123
+ const formContent = (
124
+ <Flex direction="column" gap={"md"}>
125
+ {/* Realm branding */}
126
+ {(settings.logoUrl || settings.displayName || settings.description) && (
127
+ <Flex direction="column" gap={"xs"} align="center" mb="xs">
128
+ {settings.logoUrl && (
129
+ <Image
130
+ src={settings.logoUrl}
131
+ alt={settings.displayName || props.realmConfig.realmName}
132
+ h={48}
133
+ w="auto"
134
+ fit="contain"
135
+ />
136
+ )}
137
+ {settings.displayName && (
138
+ <Title order={4} ta="center">
139
+ {settings.displayName}
140
+ </Title>
141
+ )}
142
+ {settings.description && (
143
+ <Text size="sm" c="dimmed" ta="center">
144
+ {settings.description}
145
+ </Text>
146
+ )}
147
+ </Flex>
148
+ )}
188
149
 
189
- {/* OR divider - only when both credentials AND external methods exist */}
190
- {showOrDivider && (
191
- <Flex 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
- </Flex>
198
- )}
150
+ {/* Credentials login form */}
151
+ {credentialsProvider && (
152
+ <>
153
+ <form {...form.props}>
154
+ <Flex direction="column" flex={1} gap={"md"}>
155
+ <Control
156
+ label={identifierTitle}
157
+ input={form.input.identifier}
158
+ icon={IconUser}
159
+ text={{
160
+ autoComplete: getAutoCompleteType(),
161
+ }}
162
+ />
163
+ <Control
164
+ label={tr("loginPassword")}
165
+ input={form.input.password}
166
+ icon={IconLock}
167
+ password={{
168
+ autoComplete: "current-password",
169
+ }}
170
+ />
171
+ <ActionButton variant={"filled"} form={form}>
172
+ {tr("loginSignIn")}
173
+ </ActionButton>
174
+ </Flex>
175
+ </form>
176
+ {settings.resetPasswordAllowed && (
177
+ <Text size="sm" ta="center">
178
+ <ActionButton
179
+ href={`${props.resetPasswordPath ?? "/auth/reset-password"}${realmQuery}`}
180
+ anchorProps={{ inherit: true }}
181
+ >
182
+ {tr("loginForgotPassword")}
183
+ </ActionButton>
184
+ </Text>
185
+ )}
186
+ </>
187
+ )}
199
188
 
200
- {/* External login methods */}
201
- {externalLoginMethods.length > 0 && (
202
- <Flex direction="column" 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
- </Flex>
221
- )}
189
+ {/* OR divider - only when both credentials AND external methods exist */}
190
+ {showOrDivider && (
191
+ <Flex 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
+ </Flex>
198
+ )}
199
+
200
+ {/* External login methods */}
201
+ {externalLoginMethods.length > 0 && (
202
+ <Flex direction="column" 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
+ </Flex>
221
+ )}
222
222
 
223
- {/* Registration link */}
224
- {settings.registrationAllowed && (
225
- <Text size="sm" ta="center">
226
- {tr("loginNoAccount")}{" "}
227
- <ActionButton
228
- href={router.path("register", {
229
- query: { realm: props.realmConfig.realmName },
230
- })}
231
- anchorProps={{ inherit: true }}
223
+ {/* Registration link */}
224
+ {settings.registrationAllowed && (
225
+ <Text size="sm" ta="center">
226
+ {tr("loginNoAccount")}{" "}
227
+ <ActionButton
228
+ href={`${props.registerPath ?? "/auth/register"}${realmQuery}`}
229
+ anchorProps={{ inherit: true }}
230
+ >
231
+ {tr("loginSignUp")}
232
+ </ActionButton>
233
+ </Text>
234
+ )}
235
+ </Flex>
236
+ );
237
+
238
+ if (props.variant === "split") {
239
+ return (
240
+ <Flex flex={1} justify={"center"} align={"center"}>
241
+ <Card
242
+ withBorder
243
+ p={0}
244
+ w={720}
245
+ bg={"var(--alepha-elevated)"}
246
+ style={{ overflow: "hidden" }}
247
+ >
248
+ <Flex mih={480}>
249
+ {props.image ? (
250
+ <Flex
251
+ flex={1}
252
+ style={{
253
+ backgroundImage: `url(${props.image})`,
254
+ backgroundSize: "cover",
255
+ backgroundPosition: "center",
256
+ }}
257
+ />
258
+ ) : (
259
+ <Flex
260
+ flex={1}
261
+ justify="center"
262
+ align="center"
263
+ bg="var(--mantine-color-gray-light)"
264
+ style={{
265
+ borderRight: "1px solid var(--mantine-color-default-border)",
266
+ }}
267
+ >
268
+ <Flex
269
+ justify="center"
270
+ align="center"
271
+ w={120}
272
+ h={80}
273
+ style={{
274
+ border: "2px dashed var(--mantine-color-default-border)",
275
+ borderRadius: "var(--mantine-radius-sm)",
276
+ }}
232
277
  >
233
- {tr("loginSignUp")}
234
- </ActionButton>
235
- </Text>
278
+ <IconPhoto size={32} style={{ opacity: 0.3 }} />
279
+ </Flex>
280
+ </Flex>
236
281
  )}
282
+ <Flex
283
+ flex={1}
284
+ direction="column"
285
+ gap={"md"}
286
+ p={"xl"}
287
+ justify={"center"}
288
+ >
289
+ {formContent}
290
+ <ActionButton variant={"subtle"} href={"/"}>
291
+ {tr("loginCancel")}
292
+ </ActionButton>
293
+ </Flex>
237
294
  </Flex>
238
295
  </Card>
296
+ </Flex>
297
+ );
298
+ }
299
+
300
+ return (
301
+ <Flex flex={1} justify={"center"} align={"center"}>
302
+ <Flex direction="column" gap={"sm"} w={360}>
303
+ <Card withBorder p={"lg"} bg={"var(--alepha-elevated)"}>
304
+ {formContent}
305
+ </Card>
239
306
  <ActionButton variant={"subtle"} href={"/"}>
240
307
  {tr("loginCancel")}
241
308
  </ActionButton>