@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
@@ -0,0 +1,519 @@
1
+ import type { DetailListItem } from "@alepha/ui";
2
+ import {
3
+ ActionButton,
4
+ DataTable,
5
+ DetailList,
6
+ Flex,
7
+ Text,
8
+ useToast,
9
+ } from "@alepha/ui";
10
+ import { Badge, Code, Paper } from "@mantine/core";
11
+ import { IconRefresh } from "@tabler/icons-react";
12
+ import { type Page, t } from "alepha";
13
+ import type {
14
+ AdminNotificationController,
15
+ NotificationDetailResource,
16
+ NotificationResource,
17
+ } from "alepha/api/notifications";
18
+ import type { LogEntry } from "alepha/logger";
19
+ import { useClient } from "alepha/react";
20
+ import { useI18n } from "alepha/react/i18n";
21
+ import { useCallback, useEffect, useState } from "react";
22
+
23
+ // ─────────────────────────────────────────────────────────────────────────────
24
+
25
+ const formatDuration = (
26
+ start: Date | string,
27
+ end?: Date | string | null,
28
+ ): string => {
29
+ const startTime = new Date(start).getTime();
30
+ const endTime = end ? new Date(end).getTime() : Date.now();
31
+ const duration = endTime - startTime;
32
+
33
+ if (duration < 1000) return `${duration}ms`;
34
+ if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`;
35
+ if (duration < 3600000)
36
+ return `${Math.floor(duration / 60000)}m ${Math.floor((duration % 60000) / 1000)}s`;
37
+ return `${Math.floor(duration / 3600000)}h ${Math.floor((duration % 3600000) / 60000)}m`;
38
+ };
39
+
40
+ // ─────────────────────────────────────────────────────────────────────────────
41
+
42
+ const notificationFilters = t.object({
43
+ status: t.optional(
44
+ t.enum([
45
+ "pending",
46
+ "scheduled",
47
+ "retrying",
48
+ "running",
49
+ "completed",
50
+ "failed",
51
+ "dead",
52
+ "cancelled",
53
+ ]),
54
+ ),
55
+ });
56
+
57
+ // ─────────────────────────────────────────────────────────────────────────────
58
+
59
+ const AdminNotifications = () => {
60
+ const client = useClient<AdminNotificationController>();
61
+ const { l } = useI18n();
62
+
63
+ return (
64
+ <Flex p="md" flex={1} direction="column" gap="md">
65
+ <DataTable<NotificationResource, typeof notificationFilters>
66
+ submitOnInit
67
+ defaultSize={20}
68
+ typeFormProps={{
69
+ skipSubmitButton: true,
70
+ columns: 3,
71
+ }}
72
+ tableProps={{
73
+ horizontalSpacing: "sm",
74
+ verticalSpacing: "sm",
75
+ }}
76
+ onFilterChange={(_key, _value, form) => form.submit()}
77
+ filters={notificationFilters}
78
+ defaultFilters={["status"]}
79
+ items={async (filters) => {
80
+ const response = await client.findNotifications({
81
+ query: { ...filters },
82
+ });
83
+ return response as Page<NotificationResource>;
84
+ }}
85
+ columns={{
86
+ status: {
87
+ label: "Status",
88
+ value: (item) => {
89
+ const color =
90
+ item.status === "completed"
91
+ ? "green"
92
+ : item.status === "running"
93
+ ? "blue"
94
+ : item.status === "failed" || item.status === "dead"
95
+ ? "red"
96
+ : item.status === "cancelled"
97
+ ? "yellow"
98
+ : "gray";
99
+ return (
100
+ <Badge size="sm" variant="light" color={color}>
101
+ {item.status}
102
+ </Badge>
103
+ );
104
+ },
105
+ },
106
+ template: {
107
+ label: "Template",
108
+ value: (item) => (
109
+ <Text size="sm" fw={500} ff="monospace">
110
+ {item.template ?? "\u2014"}
111
+ </Text>
112
+ ),
113
+ },
114
+ type: {
115
+ label: "Type",
116
+ value: (item) => (
117
+ <Badge
118
+ size="sm"
119
+ variant="light"
120
+ color={item.type === "email" ? "blue" : "teal"}
121
+ >
122
+ {item.type ?? "\u2014"}
123
+ </Badge>
124
+ ),
125
+ },
126
+ contact: {
127
+ label: "Contact",
128
+ value: (item) => (
129
+ <Text size="xs" c="dimmed" lineClamp={1}>
130
+ {item.contact ?? "\u2014"}
131
+ </Text>
132
+ ),
133
+ },
134
+ category: {
135
+ label: "Category",
136
+ defaultHidden: true,
137
+ value: (item) => (
138
+ <Text size="xs" c="dimmed">
139
+ {item.category ?? "\u2014"}
140
+ </Text>
141
+ ),
142
+ },
143
+ flags: {
144
+ label: "Flags",
145
+ defaultHidden: true,
146
+ value: (item) => (
147
+ <Flex gap={4}>
148
+ {item.critical && (
149
+ <Badge size="xs" variant="light" color="red">
150
+ critical
151
+ </Badge>
152
+ )}
153
+ {item.sensitive && (
154
+ <Badge size="xs" variant="light" color="orange">
155
+ sensitive
156
+ </Badge>
157
+ )}
158
+ {!item.critical && !item.sensitive && (
159
+ <Text size="xs" c="dimmed">
160
+ {"\u2014"}
161
+ </Text>
162
+ )}
163
+ </Flex>
164
+ ),
165
+ },
166
+ createdAt: {
167
+ label: "Created",
168
+ value: (item) => (
169
+ <Text size="xs" c="dimmed">
170
+ {l(item.createdAt, { date: "fromNow" })}
171
+ </Text>
172
+ ),
173
+ },
174
+ duration: {
175
+ label: "Duration",
176
+ defaultHidden: true,
177
+ value: (item) => (
178
+ <Text size="xs" c="dimmed" ff="monospace">
179
+ {item.startedAt &&
180
+ (item.completedAt || item.status === "running")
181
+ ? formatDuration(item.startedAt, item.completedAt)
182
+ : "\u2014"}
183
+ </Text>
184
+ ),
185
+ },
186
+ error: {
187
+ label: "Error",
188
+ defaultHidden: true,
189
+ value: (item) => (
190
+ <Text size="xs" c="dimmed" lineClamp={1}>
191
+ {item.error ?? "\u2014"}
192
+ </Text>
193
+ ),
194
+ },
195
+ }}
196
+ panel={{
197
+ can: (item) => Boolean(item.error),
198
+ render: (item) => (
199
+ <Flex direction="column" gap="sm" p="sm">
200
+ {item.error && (
201
+ <Flex direction="column" gap={2}>
202
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
203
+ Error
204
+ </Text>
205
+ <Paper p="xs" radius="sm" withBorder>
206
+ <Text
207
+ size="sm"
208
+ style={{
209
+ whiteSpace: "pre-wrap",
210
+ wordBreak: "break-word",
211
+ }}
212
+ >
213
+ {item.error}
214
+ </Text>
215
+ </Paper>
216
+ </Flex>
217
+ )}
218
+ </Flex>
219
+ ),
220
+ }}
221
+ drawer={(item) => <NotificationDetailContent item={item} />}
222
+ />
223
+ </Flex>
224
+ );
225
+ };
226
+
227
+ // ─────────────────────────────────────────────────────────────────────────────
228
+
229
+ const NotificationDetailContent = ({
230
+ item,
231
+ }: {
232
+ item: NotificationResource;
233
+ }) => {
234
+ const client = useClient<AdminNotificationController>();
235
+ const { l } = useI18n();
236
+ const toast = useToast();
237
+ const [detail, setDetail] = useState<NotificationDetailResource | null>(null);
238
+ const [loading, setLoading] = useState(false);
239
+
240
+ const loadDetail = useCallback(
241
+ async (id: string) => {
242
+ setDetail(null);
243
+ setLoading(true);
244
+ try {
245
+ const data = await client.getNotification({ params: { id } });
246
+ setDetail(data);
247
+ } catch {
248
+ toast.danger("Failed to load notification details");
249
+ } finally {
250
+ setLoading(false);
251
+ }
252
+ },
253
+ [client, toast],
254
+ );
255
+
256
+ useEffect(() => {
257
+ loadDetail(item.id);
258
+ }, [item.id, loadDetail]);
259
+
260
+ if (loading) {
261
+ return (
262
+ <Flex align="center" justify="center" py="xl">
263
+ <Text c="dimmed">Loading...</Text>
264
+ </Flex>
265
+ );
266
+ }
267
+
268
+ if (!detail) return null;
269
+
270
+ const rendered = detail.rendered as Record<string, unknown> | undefined;
271
+
272
+ const detailItems: DetailListItem[] = [
273
+ {
274
+ label: "ID",
275
+ value: (
276
+ <Text size="sm" ff="monospace">
277
+ {detail.id}
278
+ </Text>
279
+ ),
280
+ copyable: detail.id,
281
+ },
282
+ {
283
+ label: "Status",
284
+ value: (
285
+ <Text size="sm" tt="capitalize">
286
+ {detail.status}
287
+ </Text>
288
+ ),
289
+ },
290
+ {
291
+ label: "Template",
292
+ value: (
293
+ <Text size="sm" ff="monospace">
294
+ {detail.template}
295
+ </Text>
296
+ ),
297
+ },
298
+ {
299
+ label: "Type",
300
+ value: (
301
+ <Badge
302
+ size="sm"
303
+ variant="light"
304
+ color={detail.type === "email" ? "blue" : "teal"}
305
+ >
306
+ {detail.type}
307
+ </Badge>
308
+ ),
309
+ },
310
+ {
311
+ label: "Contact",
312
+ value: detail.contact,
313
+ },
314
+ {
315
+ label: "Category",
316
+ value: detail.category,
317
+ hidden: !detail.category,
318
+ },
319
+ {
320
+ label: "Critical",
321
+ value: detail.critical ? "Yes" : "No",
322
+ hidden: !detail.critical,
323
+ },
324
+ {
325
+ label: "Sensitive",
326
+ value: detail.sensitive ? "Yes" : "No",
327
+ hidden: !detail.sensitive,
328
+ },
329
+ {
330
+ label: "Created",
331
+ value: String(l(detail.createdAt, { date: "lll" })),
332
+ },
333
+ {
334
+ label: "Started",
335
+ value: detail.startedAt
336
+ ? String(l(detail.startedAt, { date: "lll" }))
337
+ : undefined,
338
+ hidden: !detail.startedAt,
339
+ },
340
+ {
341
+ label: "Duration",
342
+ value:
343
+ detail.startedAt &&
344
+ (detail.completedAt || detail.status === "running") ? (
345
+ <Text size="sm" ff="monospace">
346
+ {formatDuration(detail.startedAt, detail.completedAt)}
347
+ </Text>
348
+ ) : undefined,
349
+ hidden: !(
350
+ detail.startedAt &&
351
+ (detail.completedAt || detail.status === "running")
352
+ ),
353
+ },
354
+ ];
355
+
356
+ return (
357
+ <Flex direction="column" gap="md">
358
+ {/* Header */}
359
+ <Flex align="center" gap="sm">
360
+ <Text fw={600} ff="monospace">
361
+ {detail.template}
362
+ </Text>
363
+ <Badge
364
+ size="sm"
365
+ variant="light"
366
+ color={detail.type === "email" ? "blue" : "teal"}
367
+ >
368
+ {detail.type}
369
+ </Badge>
370
+ <Text size="sm" tt="capitalize" c="dimmed">
371
+ {detail.status}
372
+ </Text>
373
+ </Flex>
374
+
375
+ {/* Actions */}
376
+ <Flex gap="xs">
377
+ <ActionButton
378
+ tooltip="Refresh"
379
+ variant="light"
380
+ size="xs"
381
+ icon={IconRefresh}
382
+ onClick={() => loadDetail(item.id)}
383
+ />
384
+ </Flex>
385
+
386
+ {/* Details */}
387
+ <Paper p="sm" radius="md" withBorder>
388
+ <Text size="sm" fw={600} mb="xs">
389
+ Details
390
+ </Text>
391
+ <DetailList items={detailItems} columns={2} />
392
+ </Paper>
393
+
394
+ {/* Rendered Content */}
395
+ {rendered && (
396
+ <Paper p="sm" radius="md" withBorder>
397
+ <Text size="sm" fw={600} mb="xs">
398
+ Content
399
+ </Text>
400
+ {rendered.type === "email" && (
401
+ <Flex direction="column" gap="xs">
402
+ <Flex direction="column" gap={2}>
403
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
404
+ To
405
+ </Text>
406
+ <Text size="sm">{String(rendered.to ?? "")}</Text>
407
+ </Flex>
408
+ <Flex direction="column" gap={2}>
409
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
410
+ Subject
411
+ </Text>
412
+ <Text size="sm">{String(rendered.subject ?? "")}</Text>
413
+ </Flex>
414
+ <Flex direction="column" gap={2}>
415
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
416
+ Body
417
+ </Text>
418
+ <Paper p="xs" radius="sm" withBorder>
419
+ <Text
420
+ size="sm"
421
+ style={{
422
+ whiteSpace: "pre-wrap",
423
+ wordBreak: "break-word",
424
+ }}
425
+ >
426
+ {String(rendered.body ?? "")}
427
+ </Text>
428
+ </Paper>
429
+ </Flex>
430
+ </Flex>
431
+ )}
432
+ {rendered.type === "sms" && (
433
+ <Flex direction="column" gap="xs">
434
+ <Flex direction="column" gap={2}>
435
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
436
+ To
437
+ </Text>
438
+ <Text size="sm">{String(rendered.to ?? "")}</Text>
439
+ </Flex>
440
+ <Flex direction="column" gap={2}>
441
+ <Text size="xs" c="dimmed" tt="uppercase" fw={600}>
442
+ Message
443
+ </Text>
444
+ <Paper p="xs" radius="sm" withBorder>
445
+ <Text
446
+ size="sm"
447
+ style={{
448
+ whiteSpace: "pre-wrap",
449
+ wordBreak: "break-word",
450
+ }}
451
+ >
452
+ {String(rendered.message ?? "")}
453
+ </Text>
454
+ </Paper>
455
+ </Flex>
456
+ </Flex>
457
+ )}
458
+ </Paper>
459
+ )}
460
+
461
+ {/* Variables */}
462
+ {detail.variables && Object.keys(detail.variables).length > 0 && (
463
+ <Paper p="sm" radius="md" withBorder>
464
+ <Text size="sm" fw={600} mb="xs">
465
+ Variables
466
+ </Text>
467
+ <Code block>{JSON.stringify(detail.variables, null, 2)}</Code>
468
+ </Paper>
469
+ )}
470
+
471
+ {/* Error */}
472
+ {detail.error && (
473
+ <Paper p="sm" radius="md" withBorder>
474
+ <Text size="sm" fw={600} mb="xs">
475
+ Error
476
+ </Text>
477
+ <Paper p="xs" radius="sm" withBorder>
478
+ <Text
479
+ size="sm"
480
+ style={{
481
+ whiteSpace: "pre-wrap",
482
+ wordBreak: "break-word",
483
+ }}
484
+ >
485
+ {detail.error}
486
+ </Text>
487
+ </Paper>
488
+ </Paper>
489
+ )}
490
+
491
+ {/* Logs */}
492
+ {detail.logs && detail.logs.length > 0 && (
493
+ <Paper p="sm" radius="md" withBorder>
494
+ <Text size="sm" fw={600} mb="xs">
495
+ Logs ({detail.logs.length})
496
+ </Text>
497
+ <Flex
498
+ direction="column"
499
+ style={{ maxHeight: 300, overflowY: "auto" }}
500
+ >
501
+ {detail.logs.map((log: LogEntry, i: number) => (
502
+ <Flex key={i} gap="sm" py={2}>
503
+ <Badge size="xs" variant="default">
504
+ {log.level}
505
+ </Badge>
506
+ <Text size="xs" c="dimmed" ff="monospace">
507
+ {new Date(log.timestamp).toLocaleTimeString()}
508
+ </Text>
509
+ <Text size="xs">{log.message}</Text>
510
+ </Flex>
511
+ ))}
512
+ </Flex>
513
+ </Paper>
514
+ )}
515
+ </Flex>
516
+ );
517
+ };
518
+
519
+ export default AdminNotifications;