@alepha/ui 0.16.1 → 0.17.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 (218) hide show
  1. package/dist/admin/{AdminApiKeys-GMORg-1l.js → AdminApiKeys-CF_qOO3u.js} +20 -19
  2. package/dist/admin/AdminApiKeys-CF_qOO3u.js.map +1 -0
  3. package/dist/admin/{AdminAudits-pkWrjq1Z.js → AdminAudits-BQno3hZG.js} +7 -7
  4. package/dist/admin/AdminAudits-BQno3hZG.js.map +1 -0
  5. package/dist/admin/{AdminFiles-WeQbsCsl.js → AdminFiles-kvuUaASF.js} +3 -4
  6. package/dist/admin/{AdminFiles-WeQbsCsl.js.map → AdminFiles-kvuUaASF.js.map} +1 -1
  7. package/dist/admin/AdminJobDashboard-CrPxp0W1.js +485 -0
  8. package/dist/admin/AdminJobDashboard-CrPxp0W1.js.map +1 -0
  9. package/dist/admin/AdminJobExecutions-D-b4Zt7W.js +678 -0
  10. package/dist/admin/AdminJobExecutions-D-b4Zt7W.js.map +1 -0
  11. package/dist/admin/AdminJobRegistry-CNX5cpDx.js +301 -0
  12. package/dist/admin/AdminJobRegistry-CNX5cpDx.js.map +1 -0
  13. package/dist/admin/{AdminLayout-BqZiXx4H.js → AdminLayout-e-ZP5nWw.js} +6 -9
  14. package/dist/admin/AdminLayout-e-ZP5nWw.js.map +1 -0
  15. package/dist/admin/{AdminNotifications-Ds5Un0NJ.js → AdminNotifications-DeHJFf6W.js} +3 -4
  16. package/dist/admin/{AdminNotifications-Ds5Un0NJ.js.map → AdminNotifications-DeHJFf6W.js.map} +1 -1
  17. package/dist/admin/AdminParameters-iQE8o7a7.js +774 -0
  18. package/dist/admin/AdminParameters-iQE8o7a7.js.map +1 -0
  19. package/dist/admin/{AdminSessions-DzIOxM3b.js → AdminSessions-oKJCbd7w.js} +5 -6
  20. package/dist/admin/AdminSessions-oKJCbd7w.js.map +1 -0
  21. package/dist/admin/{AdminUserAudits-CiUPN2BC.js → AdminUserAudits-BNCEle_E.js} +6 -7
  22. package/dist/admin/AdminUserAudits-BNCEle_E.js.map +1 -0
  23. package/dist/admin/{AdminUserCreate-BwQKr4xE.js → AdminUserCreate-CgqeFwCt.js} +6 -6
  24. package/dist/admin/AdminUserCreate-CgqeFwCt.js.map +1 -0
  25. package/dist/admin/{AdminUserDetails-uqtC5aJ1.js → AdminUserDetails-DDe1A1GP.js} +30 -28
  26. package/dist/admin/AdminUserDetails-DDe1A1GP.js.map +1 -0
  27. package/dist/admin/{AdminUserLayout-CiPay35T.js → AdminUserLayout-HAlobhWf.js} +20 -19
  28. package/dist/admin/AdminUserLayout-HAlobhWf.js.map +1 -0
  29. package/dist/admin/{AdminUserSessions-DAE8Nf1F.js → AdminUserSessions-Bq1LnVLf.js} +5 -6
  30. package/dist/admin/AdminUserSessions-Bq1LnVLf.js.map +1 -0
  31. package/dist/admin/{AdminUserSettings-EbahaV2a.js → AdminUserSettings-BRsBZoxV.js} +10 -9
  32. package/dist/admin/AdminUserSettings-BRsBZoxV.js.map +1 -0
  33. package/dist/admin/{AdminUsers-Dcjh0KNW.js → AdminUsers-D71kIOSn.js} +6 -7
  34. package/dist/admin/AdminUsers-D71kIOSn.js.map +1 -0
  35. package/dist/admin/index.d.ts +21 -85
  36. package/dist/admin/index.d.ts.map +1 -1
  37. package/dist/admin/index.js +66 -88
  38. package/dist/admin/index.js.map +1 -1
  39. package/dist/auth/{AuthLayout-Dj5K4SIN.js → AuthLayout-CdJcrPs4.js} +2 -3
  40. package/dist/auth/{AuthLayout-Dj5K4SIN.js.map → AuthLayout-CdJcrPs4.js.map} +1 -1
  41. package/dist/{demo/IconGoogle-CbBF8Hqq.js → auth/IconGoogle-Bm18QD2q.js} +2 -4
  42. package/dist/auth/{IconGoogle-DpSlPZ1u.js.map → IconGoogle-Bm18QD2q.js.map} +1 -1
  43. package/dist/auth/{Login-BBqTosqZ.js → Login-BS_FYTy0.js} +19 -13
  44. package/dist/auth/Login-BS_FYTy0.js.map +1 -0
  45. package/dist/auth/{Profile-Bxj8Nwom.js → Profile-CjDsW378.js} +17 -12
  46. package/dist/auth/Profile-CjDsW378.js.map +1 -0
  47. package/dist/auth/{Register-Ce675Crg.js → Register-C5eqzAaD.js} +27 -17
  48. package/dist/auth/Register-C5eqzAaD.js.map +1 -0
  49. package/dist/auth/{ResetPassword-DWdt7c40.js → ResetPassword-XifinVao.js} +17 -10
  50. package/dist/auth/ResetPassword-XifinVao.js.map +1 -0
  51. package/dist/auth/{VerifyEmail-CI4JwByV.js → VerifyEmail-DTgbeJOO.js} +9 -6
  52. package/dist/auth/VerifyEmail-DTgbeJOO.js.map +1 -0
  53. package/dist/auth/index.d.ts +18 -14
  54. package/dist/auth/index.d.ts.map +1 -1
  55. package/dist/auth/index.js +19 -18
  56. package/dist/auth/index.js.map +1 -1
  57. package/dist/auth/rolldown-runtime-CjeV3_4I.js +18 -0
  58. package/dist/core/index.d.ts +182 -92
  59. package/dist/core/index.d.ts.map +1 -1
  60. package/dist/core/index.js +789 -476
  61. package/dist/core/index.js.map +1 -1
  62. package/dist/demo/DemoDataTable-lnBKWBf8.js +362 -0
  63. package/dist/demo/DemoDataTable-lnBKWBf8.js.map +1 -0
  64. package/dist/demo/{DemoHome-Cce2bWmg.js → DemoHome-CUMZsYaH.js} +6 -6
  65. package/dist/demo/DemoHome-CUMZsYaH.js.map +1 -0
  66. package/dist/demo/{DemoJsonViewer-Dgdk3Txb.js → DemoJsonViewer-_uokbGaW.js} +18 -19
  67. package/dist/demo/DemoJsonViewer-_uokbGaW.js.map +1 -0
  68. package/dist/demo/{DemoLayout-B20TEuhV.js → DemoLayout-DHVoacE6.js} +4 -5
  69. package/dist/demo/DemoLayout-DHVoacE6.js.map +1 -0
  70. package/dist/demo/{DemoLogin-CvCG2WVh.js → DemoLogin-DjJ9314c.js} +27 -24
  71. package/dist/demo/DemoLogin-DjJ9314c.js.map +1 -0
  72. package/dist/demo/{DemoRegister-CmeHbOAs.js → DemoRegister-DzkJ5M83.js} +39 -32
  73. package/dist/demo/DemoRegister-DzkJ5M83.js.map +1 -0
  74. package/dist/demo/{DemoResetPassword-CKO5iA_6.js → DemoResetPassword-DWh4_BpQ.js} +30 -26
  75. package/dist/demo/DemoResetPassword-DWh4_BpQ.js.map +1 -0
  76. package/dist/demo/{DemoSidebar-MVmQKfMt.js → DemoSidebar-C1csnGhX.js} +4 -5
  77. package/dist/demo/DemoSidebar-C1csnGhX.js.map +1 -0
  78. package/dist/demo/{DemoTypeForm-w-qtfRlC.js → DemoTypeForm-CWz6fJrJ.js} +4 -5
  79. package/dist/demo/DemoTypeForm-CWz6fJrJ.js.map +1 -0
  80. package/dist/demo/{DemoVerifyEmail-C8FFJT5A.js → DemoVerifyEmail-DbU_tCj8.js} +16 -16
  81. package/dist/demo/DemoVerifyEmail-DbU_tCj8.js.map +1 -0
  82. package/dist/{auth/IconGoogle-DpSlPZ1u.js → demo/IconGoogle-Ch1m3Uzl.js} +2 -4
  83. package/dist/demo/{IconGoogle-CbBF8Hqq.js.map → IconGoogle-Ch1m3Uzl.js.map} +1 -1
  84. package/dist/demo/{Showcase-CQrMWars.js → Showcase-BzoXNlCn.js} +11 -13
  85. package/dist/demo/Showcase-BzoXNlCn.js.map +1 -0
  86. package/dist/demo/index.d.ts +3 -70
  87. package/dist/demo/index.d.ts.map +1 -1
  88. package/dist/demo/index.js +11 -15
  89. package/dist/demo/index.js.map +1 -1
  90. package/dist/json/index.js +2 -2
  91. package/dist/json/index.js.map +1 -1
  92. package/package.json +11 -5
  93. package/src/admin/AdminRouter.ts +51 -29
  94. package/src/admin/components/AdminLayout.tsx +6 -9
  95. package/src/admin/components/audits/AdminAudits.tsx +5 -5
  96. package/src/admin/components/jobs/AdminJobDashboard.tsx +455 -0
  97. package/src/admin/components/jobs/AdminJobExecutions.tsx +693 -0
  98. package/src/admin/components/jobs/AdminJobRegistry.tsx +325 -0
  99. package/src/admin/components/keys/AdminApiKeys.tsx +28 -31
  100. package/src/admin/components/parameters/AdminParameters.tsx +156 -78
  101. package/src/admin/components/parameters/ParameterDetails.tsx +173 -108
  102. package/src/admin/components/parameters/ParameterEmptyState.tsx +27 -0
  103. package/src/admin/components/parameters/ParameterHistory.tsx +22 -35
  104. package/src/admin/components/parameters/ParameterTree.tsx +283 -109
  105. package/src/admin/components/parameters/types.ts +3 -3
  106. package/src/admin/components/sessions/AdminSessions.tsx +3 -3
  107. package/src/admin/components/shared/AdminResourceHeader.tsx +20 -16
  108. package/src/admin/components/users/AdminUserAudits.tsx +5 -5
  109. package/src/admin/components/users/AdminUserCreate.tsx +3 -3
  110. package/src/admin/components/users/AdminUserDetails.tsx +51 -53
  111. package/src/admin/components/users/AdminUserLayout.tsx +7 -7
  112. package/src/admin/components/users/AdminUserSessions.tsx +3 -3
  113. package/src/admin/components/users/AdminUserSettings.tsx +9 -9
  114. package/src/admin/components/users/AdminUsers.tsx +5 -5
  115. package/src/admin/components/verifications/AdminVerifications.tsx +3 -3
  116. package/src/admin/index.ts +0 -24
  117. package/src/admin/primitives/$uiAdmin.ts +2 -2
  118. package/src/auth/AuthRouter.ts +1 -0
  119. package/src/auth/components/Login.tsx +13 -13
  120. package/src/auth/components/Profile.tsx +17 -26
  121. package/src/auth/components/Register.tsx +21 -31
  122. package/src/auth/components/ResetPassword.tsx +13 -22
  123. package/src/auth/components/VerifyEmail.tsx +5 -5
  124. package/src/auth/components/buttons/UserButton.tsx +14 -4
  125. package/src/core/components/buttons/ActionButton.tsx +13 -17
  126. package/src/core/components/buttons/DarkModeButton.tsx +8 -4
  127. package/src/core/components/buttons/ToggleSidebarButton.tsx +3 -5
  128. package/src/core/components/data/ErrorViewer.tsx +15 -15
  129. package/src/core/components/dialogs/AlertDialog.tsx +3 -3
  130. package/src/core/components/dialogs/ConfirmDialog.tsx +3 -3
  131. package/src/core/components/dialogs/PromptDialog.tsx +3 -3
  132. package/src/core/components/form/Control.tsx +19 -32
  133. package/src/core/components/form/ControlArray.tsx +206 -96
  134. package/src/core/components/form/ControlObject.tsx +3 -3
  135. package/src/core/components/form/ControlQueryBuilder.tsx +20 -22
  136. package/src/core/components/form/ControlSelect.tsx +4 -0
  137. package/src/core/components/form/TypeForm.browser.spec.tsx +727 -0
  138. package/src/core/components/form/TypeForm.tsx +7 -0
  139. package/src/core/components/layout/AlephaMantineProvider.tsx +1 -0
  140. package/src/core/components/layout/Breadcrumb.tsx +91 -0
  141. package/src/core/components/layout/{AdminShell.tsx → DashboardShell.tsx} +77 -32
  142. package/src/core/components/layout/Omnibar.tsx +2 -1
  143. package/src/core/components/layout/Sidebar.tsx +63 -19
  144. package/src/core/components/table/ColumnPicker.tsx +47 -31
  145. package/src/core/components/table/DataTable.tsx +277 -201
  146. package/src/core/components/table/DataTableFilters.tsx +8 -0
  147. package/src/core/components/table/DataTableToolbar.tsx +98 -5
  148. package/src/core/components/table/FilterPicker.tsx +28 -26
  149. package/src/core/components/table/types.ts +52 -37
  150. package/src/core/components/table/useTableSelection.ts +83 -0
  151. package/src/core/constants/ui.ts +1 -1
  152. package/src/core/helpers/renderIcon.tsx +5 -2
  153. package/src/core/index.ts +9 -5
  154. package/src/core/styles.css +8 -7
  155. package/src/core/utils/parseInput.ts +1 -0
  156. package/src/core/utils/string.ts +28 -4
  157. package/src/demo/components/DemoHome.tsx +5 -5
  158. package/src/demo/components/DemoLayout.tsx +6 -2
  159. package/src/demo/components/core/DemoDataTable.tsx +209 -5
  160. package/src/demo/components/json/DemoJsonViewer.tsx +1 -1
  161. package/src/demo/components/shared/MacWindow.tsx +7 -7
  162. package/src/demo/components/shared/Showcase.tsx +3 -3
  163. package/src/demo/index.ts +0 -11
  164. package/src/json/components/JsonViewer.tsx +3 -3
  165. package/dist/admin/AdminApiKeys-DsmGnHNh.js +0 -3
  166. package/dist/admin/AdminApiKeys-GMORg-1l.js.map +0 -1
  167. package/dist/admin/AdminAudits-8SM96viT.js +0 -3
  168. package/dist/admin/AdminAudits-pkWrjq1Z.js.map +0 -1
  169. package/dist/admin/AdminFiles-B56ocq4H.js +0 -3
  170. package/dist/admin/AdminJobs-B-q9iGO3.js +0 -697
  171. package/dist/admin/AdminJobs-B-q9iGO3.js.map +0 -1
  172. package/dist/admin/AdminJobs-CED1syCn.js +0 -3
  173. package/dist/admin/AdminLayout-BqZiXx4H.js.map +0 -1
  174. package/dist/admin/AdminNotifications-B0B1rdc4.js +0 -3
  175. package/dist/admin/AdminParameters-BU3lATdJ.js +0 -3
  176. package/dist/admin/AdminParameters-CfDUpc78.js +0 -575
  177. package/dist/admin/AdminParameters-CfDUpc78.js.map +0 -1
  178. package/dist/admin/AdminSessions-BDGK2MS6.js +0 -3
  179. package/dist/admin/AdminSessions-DzIOxM3b.js.map +0 -1
  180. package/dist/admin/AdminUserAudits-CiUPN2BC.js.map +0 -1
  181. package/dist/admin/AdminUserAudits-Cj79gENT.js +0 -3
  182. package/dist/admin/AdminUserCreate-BwQKr4xE.js.map +0 -1
  183. package/dist/admin/AdminUserCreate-Cq-mUmBs.js +0 -3
  184. package/dist/admin/AdminUserDetails-DRjVAPFd.js +0 -3
  185. package/dist/admin/AdminUserDetails-uqtC5aJ1.js.map +0 -1
  186. package/dist/admin/AdminUserLayout-CGzmHHby.js +0 -3
  187. package/dist/admin/AdminUserLayout-CiPay35T.js.map +0 -1
  188. package/dist/admin/AdminUserSessions-DAE8Nf1F.js.map +0 -1
  189. package/dist/admin/AdminUserSessions-DcdzuNZ9.js +0 -3
  190. package/dist/admin/AdminUserSettings-D7V6-ceX.js +0 -3
  191. package/dist/admin/AdminUserSettings-EbahaV2a.js.map +0 -1
  192. package/dist/admin/AdminUsers-D9nyzGqQ.js +0 -3
  193. package/dist/admin/AdminUsers-Dcjh0KNW.js.map +0 -1
  194. package/dist/auth/Login-BBqTosqZ.js.map +0 -1
  195. package/dist/auth/Login-CoU63mMR.js +0 -4
  196. package/dist/auth/Profile-Bxj8Nwom.js.map +0 -1
  197. package/dist/auth/Register-BV_oa_AK.js +0 -4
  198. package/dist/auth/Register-Ce675Crg.js.map +0 -1
  199. package/dist/auth/ResetPassword-D5wC8GAA.js +0 -3
  200. package/dist/auth/ResetPassword-DWdt7c40.js.map +0 -1
  201. package/dist/auth/VerifyEmail-CI4JwByV.js.map +0 -1
  202. package/dist/auth/VerifyEmail-DAfqVm5s.js +0 -3
  203. package/dist/demo/DemoDataTable-CguplbR7.js +0 -150
  204. package/dist/demo/DemoDataTable-CguplbR7.js.map +0 -1
  205. package/dist/demo/DemoHome-Cce2bWmg.js.map +0 -1
  206. package/dist/demo/DemoHome-DC9qkMNe.js +0 -3
  207. package/dist/demo/DemoJsonViewer-DIssGVlJ.js +0 -4
  208. package/dist/demo/DemoJsonViewer-Dgdk3Txb.js.map +0 -1
  209. package/dist/demo/DemoLayout-B20TEuhV.js.map +0 -1
  210. package/dist/demo/DemoLayout-DSRyf4qJ.js +0 -3
  211. package/dist/demo/DemoLogin-CvCG2WVh.js.map +0 -1
  212. package/dist/demo/DemoRegister-CmeHbOAs.js.map +0 -1
  213. package/dist/demo/DemoResetPassword-CKO5iA_6.js.map +0 -1
  214. package/dist/demo/DemoSidebar-MVmQKfMt.js.map +0 -1
  215. package/dist/demo/DemoTypeForm-w-qtfRlC.js.map +0 -1
  216. package/dist/demo/DemoVerifyEmail-C8FFJT5A.js.map +0 -1
  217. package/dist/demo/Showcase-CQrMWars.js.map +0 -1
  218. package/src/admin/components/jobs/AdminJobs.tsx +0 -772
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminParameters-iQE8o7a7.js","names":["Flex","Flex","Flex"],"sources":["../../src/admin/components/parameters/types.ts","../../src/admin/components/parameters/ParameterDetails.tsx","../../src/admin/components/parameters/ParameterEmptyState.tsx","../../src/admin/components/parameters/ParameterHistory.tsx","../../src/admin/components/parameters/ParameterTree.tsx","../../src/admin/components/parameters/AdminParameters.tsx"],"sourcesContent":["import type { Parameter } from \"alepha/api/parameters\";\n\nexport interface ParameterValue {\n current?: Parameter;\n next?: Parameter;\n /**\n * Default value from the registered $parameter primitive.\n */\n defaultValue?: unknown;\n /**\n * Current in-memory value (may be default if never saved).\n */\n currentValue?: unknown;\n /**\n * TypeBox/JSON schema for the parameter (as JSON from API).\n */\n schema?: Record<string, unknown>;\n}\n\nexport const getStatusColor = (status: string) => {\n switch (status) {\n case \"current\":\n return \"green\";\n case \"next\":\n return \"blue\";\n case \"future\":\n return \"cyan\";\n case \"expired\":\n return \"gray\";\n default:\n return \"gray\";\n }\n};\n\nexport const formatJson = (obj: unknown): string => {\n try {\n return JSON.stringify(obj, null, 2);\n } catch {\n return String(obj);\n }\n};\n","import { ActionButton, Flex, Text, TypeForm } from \"@alepha/ui\";\nimport { Card, Code, Loader } from \"@mantine/core\";\nimport { IconClock } from \"@tabler/icons-react\";\nimport { jsonSchemaToTypeBox, type TObject, t } from \"alepha\";\nimport { useForm } from \"alepha/react/form\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useMemo } from \"react\";\nimport { formatJson, type ParameterValue } from \"./types.ts\";\n\nexport interface ParameterDetailsProps {\n selectedConfig: string | null;\n configValue: ParameterValue | null;\n loading: boolean;\n saving: boolean;\n onSave: (values: Record<string, unknown>) => Promise<void>;\n}\n\n/**\n * Loading state.\n */\nconst LoadingState = () => (\n <Flex\n flex={1}\n h=\"100%\"\n p=\"md\"\n style={{\n overflow: \"hidden\",\n minWidth: 0,\n display: \"flex\",\n }}\n >\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Loader size=\"sm\" />\n </Flex>\n </Flex>\n);\n\ninterface ConfigFormProps {\n selectedConfig: string;\n configValue: ParameterValue | null;\n saving: boolean;\n onSave: (values: Record<string, unknown>) => Promise<void>;\n}\n\n/**\n * The actual form component - only rendered when a config is selected.\n */\nconst ConfigForm = ({\n selectedConfig,\n configValue,\n saving,\n onSave,\n}: ConfigFormProps) => {\n const { l } = useI18n();\n\n // Get the current value to display (from saved version or default)\n const currentContent = useMemo(() => {\n if (configValue?.current?.content) {\n return configValue.current.content;\n }\n if (configValue?.currentValue !== undefined) {\n return configValue.currentValue;\n }\n return null;\n }, [configValue]);\n\n // Convert JSON Schema from API to TypeBox schema\n const schemaForForm = useMemo(() => {\n if (!configValue?.schema) {\n return t.object({});\n }\n try {\n return jsonSchemaToTypeBox(configValue.schema) as TObject;\n } catch {\n return t.object({});\n }\n }, [configValue?.schema]);\n\n const form = useForm(\n {\n schema: schemaForForm,\n initialValues: (currentContent ?? {}) as Record<string, unknown>,\n handler: async (values) => {\n await onSave(values as Record<string, unknown>);\n },\n },\n [selectedConfig, schemaForForm, currentContent],\n );\n\n // Check if we have a valid schema with properties\n const hasValidSchema = useMemo(() => {\n const schema = configValue?.schema;\n return (\n schema &&\n typeof schema === \"object\" &&\n \"properties\" in schema &&\n Object.keys(schema.properties as object).length > 0\n );\n }, [configValue?.schema]);\n\n // Count the number of fields to determine column layout\n const fieldCount = useMemo(() => {\n const schema = configValue?.schema;\n if (\n schema &&\n typeof schema === \"object\" &&\n \"properties\" in schema &&\n schema.properties\n ) {\n return Object.keys(schema.properties as object).length;\n }\n return 0;\n }, [configValue?.schema]);\n\n // Determine optimal column count based on field count\n const columns = useMemo(() => {\n if (fieldCount <= 2) return 1;\n if (fieldCount <= 6) return 2;\n return 3;\n }, [fieldCount]);\n\n return (\n <Flex\n flex={1}\n h=\"100%\"\n style={{\n overflow: \"hidden\",\n minWidth: 0,\n display: \"flex\",\n }}\n >\n <Flex direction=\"column\" h=\"100%\" w=\"100%\" style={{ minHeight: 0 }}>\n {/* Content */}\n <Flex\n flex={1}\n p=\"md\"\n className=\"overflow-auto\"\n style={{ minHeight: 0 }}\n >\n {currentContent !== null ? (\n <Flex direction=\"column\" gap=\"lg\">\n {/* Form or JSON view */}\n <Flex>\n {hasValidSchema ? (\n <TypeForm\n form={form}\n columns={columns}\n skipSubmitButton\n fill={false}\n />\n ) : (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Current Value\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }}>\n {formatJson(currentContent)}\n </Code>\n </Flex>\n )}\n </Flex>\n\n {/* Metadata */}\n {configValue?.current?.changeDescription && (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Change Description\n </Text>\n <Text size=\"sm\">{configValue.current.changeDescription}</Text>\n </Flex>\n )}\n\n {configValue?.current && (\n <Flex gap=\"xl\">\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated\n </Text>\n <Text size=\"sm\">\n {l(configValue.current.updatedAt, { date: \"fromNow\" })}\n </Text>\n </Flex>\n {configValue.current.creatorName && (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated By\n </Text>\n <Text size=\"sm\">{configValue.current.creatorName}</Text>\n </Flex>\n )}\n </Flex>\n )}\n\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Text size=\"xs\" c=\"dimmed\">\n This configuration is using its default value. No versions\n have been saved to the database yet.\n </Text>\n )}\n\n {/* Scheduled update preview */}\n {configValue?.next && (\n <Card withBorder p=\"sm\" bg=\"var(--mantine-color-blue-light)\">\n <Flex direction=\"column\" gap=\"xs\">\n <Flex gap=\"xs\">\n <IconClock\n size={14}\n color=\"var(--mantine-color-blue-6)\"\n />\n <Text size=\"xs\" fw={500} c=\"blue\">\n Scheduled Update (v{configValue.next.version})\n </Text>\n </Flex>\n <Text size=\"xs\" c=\"dimmed\">\n Activates{\" \"}\n {l(configValue.next.activationDate, {\n date: \"fromNow\",\n })}\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }} fz=\"xs\">\n {formatJson(configValue.next.content)}\n </Code>\n </Flex>\n </Card>\n )}\n </Flex>\n ) : (\n <Flex justify=\"center\" align=\"center\" h={200}>\n <Text c=\"dimmed\" size=\"sm\">\n No current value\n </Text>\n </Flex>\n )}\n </Flex>\n\n {/* Footer with actions */}\n {hasValidSchema && currentContent !== null && (\n <Flex\n p=\"md\"\n style={{\n flexShrink: 0,\n borderTop: \"1px solid var(--mantine-color-default-border)\",\n }}\n >\n <Flex justify=\"flex-end\" gap=\"sm\">\n <ActionButton\n variant=\"subtle\"\n onClick={() => form.reset({} as any)}\n disabled={saving}\n >\n Reset\n </ActionButton>\n <ActionButton intent=\"primary\" form={form} loading={saving}>\n Save Changes\n </ActionButton>\n </Flex>\n </Flex>\n )}\n </Flex>\n </Flex>\n );\n};\n\n/**\n * Parameter details panel.\n * Shows loading state or the config form.\n * Note: Empty state is handled by parent (AdminParameters).\n */\nconst ParameterDetails = ({\n selectedConfig,\n configValue,\n loading,\n saving,\n onSave,\n}: ParameterDetailsProps) => {\n // Loading state\n if (loading) {\n return <LoadingState />;\n }\n\n // Config form (selectedConfig is guaranteed to be non-null by parent)\n return (\n <ConfigForm\n selectedConfig={selectedConfig!}\n configValue={configValue}\n saving={saving}\n onSave={onSave}\n />\n );\n};\n\nexport default ParameterDetails;\n","import { Text } from \"@alepha/ui\";\nimport { Flex } from \"@mantine/core\";\nimport { IconArrowLeft } from \"@tabler/icons-react\";\n\n/**\n * Empty state displayed when no parameter is selected.\n * Invites user to select a parameter from the tree.\n */\nconst ParameterEmptyState = () => {\n return (\n <Flex flex={1} p={\"xl\"} align=\"center\">\n <Flex direction=\"column\" align=\"center\" gap=\"md\">\n <IconArrowLeft size={32} color=\"var(--mantine-color-dimmed)\" />\n <Flex direction=\"column\" align=\"center\" gap={4}>\n <Text fw={500} c=\"dimmed\">\n No Parameter Selected\n </Text>\n <Text size=\"xs\" c=\"dimmed\" ta=\"center\" maw={240}>\n Choose a parameter from the tree to view and edit its configuration\n </Text>\n </Flex>\n </Flex>\n </Flex>\n );\n};\n\nexport default ParameterEmptyState;\n","import { ActionButton, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Loader, ScrollArea, Timeline } from \"@mantine/core\";\nimport { IconHistory } from \"@tabler/icons-react\";\nimport type { Parameter } from \"alepha/api/parameters\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { getStatusColor } from \"./types.ts\";\n\nexport interface ParameterHistoryProps {\n selectedConfig: string | null;\n history: Parameter[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\nconst ParameterHistory = ({\n history,\n loading,\n onRollback,\n}: ParameterHistoryProps) => {\n const { l } = useI18n();\n\n const renderContent = () => {\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader size=\"sm\" />\n </Flex>\n );\n }\n\n if (history.length === 0) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n Empty\n </Text>\n </Flex>\n );\n }\n\n return (\n <ScrollArea flex={1} offsetScrollbars>\n <Timeline\n active={history.findIndex((h) => h.status === \"current\")}\n bulletSize={24}\n lineWidth={2}\n >\n {history.map((version) => (\n <Timeline.Item\n key={version.id}\n bullet={\n <Text size=\"xs\" fw={500}>\n {version.version}\n </Text>\n }\n title={\n <Flex gap=\"xs\">\n <Text size=\"xs\" fw={500}>\n Version {version.version}\n </Text>\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getStatusColor(version.status)}\n >\n {version.status}\n </Badge>\n </Flex>\n }\n >\n <Flex direction=\"column\" gap={4} mt={4}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(version.createdAt, { date: \"fromNow\" })}\n </Text>\n {version.changeDescription && (\n <Text size=\"xs\" lineClamp={2}>\n {version.changeDescription}\n </Text>\n )}\n {version.creatorName && (\n <Text size=\"xs\" c=\"dimmed\">\n by {version.creatorName}\n </Text>\n )}\n {version.migrationLog && (\n <Badge size=\"xs\" variant=\"outline\" color=\"orange\">\n Schema Changed\n </Badge>\n )}\n {version.status === \"expired\" && (\n <ActionButton\n size=\"compact-xs\"\n variant=\"subtle\"\n onClick={() => onRollback(version.version)}\n >\n Rollback to this version\n </ActionButton>\n )}\n </Flex>\n </Timeline.Item>\n ))}\n </Timeline>\n </ScrollArea>\n );\n };\n\n return (\n <Flex\n w={160}\n h=\"100%\"\n p=\"xs\"\n style={{\n flexShrink: 0,\n overflow: \"hidden\",\n display: \"flex\",\n flexDirection: \"column\",\n borderLeft: \"1px solid var(--mantine-color-default-border)\",\n }}\n >\n <Flex direction=\"column\" gap=\"xs\" h=\"100%\" style={{ minHeight: 0 }}>\n <Flex gap=\"xs\">\n <IconHistory size={16} color=\"var(--mantine-color-dimmed)\" />\n <Text size=\"sm\" fw={500}>\n History\n </Text>\n </Flex>\n {renderContent()}\n </Flex>\n </Flex>\n );\n};\n\nexport default ParameterHistory;\n","import { ActionButton, Text } from \"@alepha/ui\";\nimport {\n Collapse,\n Flex,\n ScrollArea,\n TextInput,\n UnstyledButton,\n} from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronRight,\n IconFolder,\n IconFolderOpen,\n IconRefresh,\n IconSearch,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport type { ParameterTreeNode } from \"alepha/api/parameters\";\nimport { memo, useCallback, useMemo, useState } from \"react\";\n\nexport interface ParameterTreeProps {\n treeData: ParameterTreeNode[];\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\n/**\n * Filters tree nodes by search query.\n */\nconst filterTree = (\n nodes: ParameterTreeNode[],\n query: string,\n): ParameterTreeNode[] => {\n if (!query.trim()) return nodes;\n\n const lowerQuery = query.toLowerCase();\n\n return nodes\n .map((node) => {\n const filteredChildren = filterTree(node.children, query);\n const nameMatches = node.name.toLowerCase().includes(lowerQuery);\n const pathMatches = node.path.toLowerCase().includes(lowerQuery);\n\n if (nameMatches || pathMatches || filteredChildren.length > 0) {\n return {\n ...node,\n children: filteredChildren,\n };\n }\n\n return null;\n })\n .filter((node): node is ParameterTreeNode => node !== null);\n};\n\ninterface TreeNodeProps {\n node: ParameterTreeNode;\n level: number;\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n expandedNodes: Set<string>;\n onToggle: (path: string) => void;\n}\n\n/**\n * Memoized tree node to prevent unnecessary re-renders.\n */\nconst TreeNode = memo(\n ({\n node,\n level,\n selectedConfig,\n onSelect,\n expandedNodes,\n onToggle,\n }: TreeNodeProps) => {\n const [isHovered, setIsHovered] = useState(false);\n const hasChildren = node.children.length > 0;\n const isExpanded = expandedNodes.has(node.path);\n const isSelected = selectedConfig === node.path;\n const isLeaf = !hasChildren;\n\n const handleClick = useCallback(() => {\n if (hasChildren) {\n onToggle(node.path);\n } else {\n onSelect(node.path);\n }\n }, [hasChildren, node.path, onToggle, onSelect]);\n\n const handleMouseEnter = useCallback(() => setIsHovered(true), []);\n const handleMouseLeave = useCallback(() => setIsHovered(false), []);\n\n return (\n <Flex>\n <UnstyledButton\n onClick={handleClick}\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n w=\"100%\"\n style={{ display: \"block\" }}\n >\n <Flex\n gap={6}\n wrap=\"nowrap\"\n p=\"4px 8px\"\n pl={8 + level * 16}\n style={{\n borderRadius: \"var(--mantine-radius-sm)\",\n backgroundColor:\n isSelected || isHovered\n ? \"var(--mantine-color-default-hover)\"\n : undefined,\n }}\n >\n {hasChildren ? (\n <>\n <Flex\n style={{\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n width: 16,\n }}\n >\n {isExpanded ? (\n <IconChevronDown\n size={14}\n color=\"var(--mantine-color-dimmed)\"\n />\n ) : (\n <IconChevronRight\n size={14}\n color=\"var(--mantine-color-dimmed)\"\n />\n )}\n </Flex>\n {isExpanded ? (\n <IconFolderOpen\n size={16}\n color=\"var(--mantine-color-dimmed)\"\n style={{ flexShrink: 0 }}\n />\n ) : (\n <IconFolder\n size={16}\n color=\"var(--mantine-color-dimmed)\"\n style={{ flexShrink: 0 }}\n />\n )}\n </>\n ) : (\n <>\n <Flex w={16} />\n <IconSettings\n size={16}\n color={\n isSelected\n ? \"var(--mantine-color-blue-6)\"\n : \"var(--mantine-color-dimmed)\"\n }\n style={{ flexShrink: 0 }}\n />\n </>\n )}\n <Text\n size=\"sm\"\n fw={isSelected ? 600 : 400}\n c={isSelected ? undefined : isLeaf ? undefined : \"dimmed\"}\n style={{\n whiteSpace: \"nowrap\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n }}\n >\n {node.name}\n </Text>\n </Flex>\n </UnstyledButton>\n\n {hasChildren && (\n <Collapse in={isExpanded}>\n {node.children.map((child: ParameterTreeNode) => (\n <TreeNode\n key={child.path}\n node={child}\n level={level + 1}\n selectedConfig={selectedConfig}\n onSelect={onSelect}\n expandedNodes={expandedNodes}\n onToggle={onToggle}\n />\n ))}\n </Collapse>\n )}\n </Flex>\n );\n },\n);\n\nTreeNode.displayName = \"TreeNode\";\n\n/**\n * Collects all folder paths to expand by default.\n */\nconst collectAllFolderPaths = (nodes: ParameterTreeNode[]): Set<string> => {\n const paths = new Set<string>();\n\n const traverse = (nodeList: ParameterTreeNode[]) => {\n for (const node of nodeList) {\n if (node.children.length > 0) {\n paths.add(node.path);\n traverse(node.children);\n }\n }\n };\n\n traverse(nodes);\n return paths;\n};\n\nconst ParameterTree = ({\n treeData,\n selectedConfig,\n onSelect,\n onRefresh,\n}: ParameterTreeProps) => {\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() =>\n collectAllFolderPaths(treeData),\n );\n\n // Filter tree by search query\n const filteredTreeData = useMemo(\n () => filterTree(treeData, searchQuery),\n [treeData, searchQuery],\n );\n\n const handleToggle = useCallback((path: string) => {\n setExpandedNodes((prev) => {\n const next = new Set(prev);\n if (next.has(path)) {\n next.delete(path);\n } else {\n next.add(path);\n }\n return next;\n });\n }, []);\n\n const handleSearchChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n setSearchQuery(e.currentTarget.value);\n },\n [],\n );\n\n return (\n <Flex\n w={280}\n h=\"100%\"\n p=\"sm\"\n style={{\n flexShrink: 0,\n display: \"flex\",\n flexDirection: \"column\",\n borderRight: \"1px solid var(--mantine-color-default-border)\",\n }}\n >\n <Flex direction=\"column\" gap=\"sm\" h=\"100%\" style={{ minHeight: 0 }}>\n <Flex justify=\"space-between\" gap=\"xs\">\n <Text size=\"sm\" fw={600}>\n Parameters\n </Text>\n <ActionButton\n variant=\"subtle\"\n size=\"compact-xs\"\n onClick={onRefresh}\n tooltip=\"Refresh\"\n >\n <IconRefresh size={14} />\n </ActionButton>\n </Flex>\n\n <TextInput\n placeholder=\"Search...\"\n size=\"xs\"\n leftSection={<IconSearch size={14} />}\n value={searchQuery}\n onChange={handleSearchChange}\n />\n\n <ScrollArea flex={1} offsetScrollbars style={{ minHeight: 0 }}>\n {filteredTreeData.length === 0 ? (\n <Text size=\"xs\" c=\"dimmed\" ta=\"center\" py=\"md\">\n {searchQuery ? \"No matching parameters\" : \"No parameters\"}\n </Text>\n ) : (\n <Flex direction=\"column\" gap={0} style={{ gap: 1 }}>\n {filteredTreeData.map((node) => (\n <TreeNode\n key={node.path}\n node={node}\n level={0}\n selectedConfig={selectedConfig}\n onSelect={onSelect}\n expandedNodes={expandedNodes}\n onToggle={handleToggle}\n />\n ))}\n </Flex>\n )}\n </ScrollArea>\n </Flex>\n </Flex>\n );\n};\n\nexport default ParameterTree;\n","import { Text, useToast } from \"@alepha/ui\";\nimport { Card, Flex } from \"@mantine/core\";\nimport { IconSettings } from \"@tabler/icons-react\";\nimport type {\n AdminParameterController,\n Parameter,\n ParameterTreeNode,\n} from \"alepha/api/parameters\";\nimport { useClient } from \"alepha/react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport ParameterDetails from \"./ParameterDetails.tsx\";\nimport ParameterEmptyState from \"./ParameterEmptyState.tsx\";\nimport ParameterHistory from \"./ParameterHistory.tsx\";\nimport ParameterTree from \"./ParameterTree.tsx\";\nimport type { ParameterValue } from \"./types.ts\";\n\nexport interface AdminParametersProps {\n treeData: ParameterTreeNode[];\n}\n\nconst AdminParameters = ({\n treeData: initialTreeData,\n}: AdminParametersProps) => {\n const client = useClient<AdminParameterController>();\n const toast = useToast();\n\n // State\n const [treeData, setTreeData] =\n useState<ParameterTreeNode[]>(initialTreeData);\n const [selectedConfig, setSelectedConfig] = useState<string | null>(null);\n const [configValue, setConfigValue] = useState<ParameterValue | null>(null);\n const [history, setHistory] = useState<Parameter[]>([]);\n const [loadingConfig, setLoadingConfig] = useState(false);\n const [loadingHistory, setLoadingHistory] = useState(false);\n const [saving, setSaving] = useState(false);\n\n // Refresh tree data\n const handleRefresh = useCallback(async () => {\n try {\n const tree = await client.getParameterTree({});\n setTreeData(tree as ParameterTreeNode[]);\n } catch (error) {\n toast.danger({\n title: \"Failed to refresh parameters\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n }, [client, toast]);\n\n // Load config value and history when selection changes\n const loadConfigDetails = useCallback(\n async (name: string) => {\n setLoadingConfig(true);\n setLoadingHistory(true);\n\n try {\n const [currentResponse, historyResponse] = await Promise.all([\n client.getCurrent({ params: { name } }),\n client.getHistory({ params: { name } }),\n ]);\n setConfigValue(currentResponse);\n setHistory(historyResponse.versions);\n } catch (error) {\n toast.danger({\n title: \"Failed to load configuration\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n setConfigValue(null);\n setHistory([]);\n } finally {\n setLoadingConfig(false);\n setLoadingHistory(false);\n }\n },\n [client, toast],\n );\n\n // Handle save\n const handleSave = useCallback(\n async (values: Record<string, unknown>) => {\n if (!selectedConfig || !configValue) return;\n\n setSaving(true);\n try {\n await client.createVersion({\n params: { name: selectedConfig },\n body: {\n content: values,\n schemaHash: \"\", // Schema hash is computed server-side when empty\n changeDescription: \"Updated via admin UI\",\n },\n });\n\n toast.success({\n title: \"Configuration saved\",\n message: `${selectedConfig} has been updated`,\n });\n\n // Reload details\n await loadConfigDetails(selectedConfig);\n } catch (error) {\n toast.danger({\n title: \"Failed to save configuration\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw error;\n } finally {\n setSaving(false);\n }\n },\n [client, selectedConfig, configValue, loadConfigDetails, toast],\n );\n\n // Load details when selection changes\n useEffect(() => {\n if (selectedConfig) {\n loadConfigDetails(selectedConfig);\n } else {\n setConfigValue(null);\n setHistory([]);\n }\n }, [selectedConfig, loadConfigDetails]);\n\n // Handle rollback\n const handleRollback = useCallback(\n async (version: number) => {\n if (!selectedConfig) return;\n\n try {\n await client.rollback({\n params: { name: selectedConfig },\n body: { targetVersion: version },\n });\n\n toast.success({\n title: \"Rollback successful\",\n message: `${selectedConfig} rolled back to version ${version}`,\n });\n\n // Reload details\n await loadConfigDetails(selectedConfig);\n } catch (error) {\n toast.danger({\n title: \"Rollback failed\",\n message: error instanceof Error ? error.message : \"Unknown error\",\n });\n }\n },\n [client, selectedConfig, loadConfigDetails, toast],\n );\n\n // Empty state when no configs exist\n if (treeData.length === 0) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Flex direction=\"column\" align=\"center\" gap=\"xs\">\n <IconSettings\n size={48}\n stroke={1.5}\n color=\"var(--mantine-color-dimmed)\"\n />\n <Text c=\"dimmed\">No Parameters Found</Text>\n <Text size=\"xs\" c=\"dimmed\" ta=\"center\" maw={400}>\n Define parameters using the $parameter primitive to manage dynamic\n application settings. Parameters will appear here once created.\n </Text>\n </Flex>\n </Flex>\n );\n }\n\n return (\n <Flex flex={1} p=\"md\">\n <Card\n withBorder\n p={0}\n w={\"100%\"}\n style={{\n flexDirection: \"row\",\n }}\n >\n <ParameterTree\n treeData={treeData}\n selectedConfig={selectedConfig}\n onSelect={setSelectedConfig}\n onRefresh={handleRefresh}\n />\n\n {selectedConfig ? (\n <>\n <ParameterDetails\n selectedConfig={selectedConfig}\n configValue={configValue}\n loading={loadingConfig}\n saving={saving}\n onSave={handleSave}\n />\n\n <ParameterHistory\n selectedConfig={selectedConfig}\n history={history}\n loading={loadingHistory}\n onRollback={handleRollback}\n />\n </>\n ) : (\n <ParameterEmptyState />\n )}\n </Card>\n </Flex>\n );\n};\n\nexport default AdminParameters;\n"],"mappings":"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"}
@@ -2,10 +2,10 @@ import { ActionButton, DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconDeviceDesktop, IconDeviceMobile, IconDeviceTablet, IconTrash } from "@tabler/icons-react";
4
4
  import { useRouter } from "alepha/react/router";
5
- import { Badge, Group } from "@mantine/core";
5
+ import { Badge } from "@mantine/core";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { useClient } from "alepha/react";
7
8
  import { useI18n } from "alepha/react/i18n";
8
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { useState } from "react";
10
10
  import { sessions } from "alepha/api/users";
11
11
 
@@ -78,7 +78,7 @@ const AdminSessions = (props) => {
78
78
  userAgent: {
79
79
  label: "Device",
80
80
  fit: true,
81
- value: (item) => /* @__PURE__ */ jsx(Group, {
81
+ value: (item) => /* @__PURE__ */ jsx(Flex, {
82
82
  gap: 4,
83
83
  children: item.userAgent ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Badge, {
84
84
  size: "xs",
@@ -144,8 +144,7 @@ const AdminSessions = (props) => {
144
144
  }, refreshKey)
145
145
  });
146
146
  };
147
- var AdminSessions_default = AdminSessions;
148
147
 
149
148
  //#endregion
150
- export { AdminSessions_default as t };
151
- //# sourceMappingURL=AdminSessions-DzIOxM3b.js.map
149
+ export { AdminSessions as default };
150
+ //# sourceMappingURL=AdminSessions-oKJCbd7w.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminSessions-oKJCbd7w.js","names":[],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import { ActionButton, DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport {\n IconDeviceDesktop,\n IconDeviceMobile,\n IconDeviceTablet,\n IconTrash,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport {\n type AdminSessionController,\n type SessionEntity,\n sessions,\n} from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport { useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n });\n\n const getDeviceIcon = (device?: string) => {\n switch (device) {\n case \"MOBILE\":\n return <IconDeviceMobile size={14} />;\n case \"TABLET\":\n return <IconDeviceTablet size={14} />;\n default:\n return <IconDeviceDesktop size={14} />;\n }\n };\n\n const isExpired = (expiresAt: Date | string) => {\n return new Date(expiresAt) < new Date();\n };\n\n const handleDelete = async (sessionId: string) => {\n await client.deleteSession({\n params: { id: sessionId },\n query: { userRealmName: props.userRealmName },\n });\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<SessionEntity, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"userId\") {\n return form.submit();\n }\n }}\n filters={filters}\n tableTrProps={(item) => {\n if (isExpired(item.expiresAt)) {\n return {\n opacity: 0.5,\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n\n return response as Page<SessionEntity>;\n }}\n columns={{\n userId: {\n label: \"User\",\n value: (item) => (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={router.path(\"adminUserDetails\", {\n params: { userId: item.userId },\n })}\n >\n <Text size=\"xs\" ff=\"monospace\">\n {item.userId.slice(0, 8)}...\n </Text>\n </ActionButton>\n ),\n },\n userAgent: {\n label: \"Device\",\n fit: true,\n value: (item) => (\n <Flex gap={4}>\n {item.userAgent ? (\n <>\n <Badge\n size=\"xs\"\n variant=\"light\"\n leftSection={getDeviceIcon(item.userAgent.device)}\n >\n {item.userAgent.device}\n </Badge>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userAgent.browser} / {item.userAgent.os}\n </Text>\n </>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n )}\n </Flex>\n ),\n },\n ip: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" ff=\"monospace\" c=\"dimmed\">\n {item.ip || \"-\"}\n </Text>\n ),\n },\n expiresAt: {\n label: \"Status\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={isExpired(item.expiresAt) ? \"red\" : \"green\"}\n >\n {isExpired(item.expiresAt) ? \"Expired\" : \"Active\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n actions: {\n label: \"\",\n fit: true,\n value: (item) => (\n <ActionButton\n size=\"xs\"\n variant=\"subtle\"\n color=\"red\"\n onClick={() => handleDelete(item.id)}\n >\n <IconTrash size={14} />\n </ActionButton>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminSessions;\n"],"mappings":"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"}
@@ -2,10 +2,10 @@ import { DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconAlertTriangle, IconCheck, IconInfoCircle, IconX } from "@tabler/icons-react";
4
4
  import { useRouterState } from "alepha/react/router";
5
- import { Badge, Group, Tooltip } from "@mantine/core";
5
+ import { Badge, Tooltip } from "@mantine/core";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import { useClient } from "alepha/react";
7
8
  import { useI18n } from "alepha/react/i18n";
8
- import { jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region ../../src/admin/components/users/AdminUserAudits.tsx
11
11
  const getSeverityColor = (severity) => {
@@ -120,7 +120,7 @@ const AdminUserAudits = (_props) => {
120
120
  success: {
121
121
  label: "Status",
122
122
  fit: true,
123
- value: (item) => item.success ? /* @__PURE__ */ jsxs(Group, {
123
+ value: (item) => item.success ? /* @__PURE__ */ jsxs(Flex, {
124
124
  gap: 4,
125
125
  children: [/* @__PURE__ */ jsx(IconCheck, {
126
126
  size: 14,
@@ -132,7 +132,7 @@ const AdminUserAudits = (_props) => {
132
132
  })]
133
133
  }) : /* @__PURE__ */ jsx(Tooltip, {
134
134
  label: item.errorMessage || "Failed",
135
- children: /* @__PURE__ */ jsxs(Group, {
135
+ children: /* @__PURE__ */ jsxs(Flex, {
136
136
  gap: 4,
137
137
  children: [/* @__PURE__ */ jsx(IconX, {
138
138
  size: 14,
@@ -171,8 +171,7 @@ const AdminUserAudits = (_props) => {
171
171
  })
172
172
  });
173
173
  };
174
- var AdminUserAudits_default = AdminUserAudits;
175
174
 
176
175
  //#endregion
177
- export { AdminUserAudits_default as t };
178
- //# sourceMappingURL=AdminUserAudits-CiUPN2BC.js.map
176
+ export { AdminUserAudits as default };
177
+ //# sourceMappingURL=AdminUserAudits-BNCEle_E.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminUserAudits-BNCEle_E.js","names":[],"sources":["../../src/admin/components/users/AdminUserAudits.tsx"],"sourcesContent":["import { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouterState } from \"alepha/react/router\";\n\nexport interface AdminUserAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst AdminUserAudits = (_props: AdminUserAuditsProps) => {\n const state = useRouterState();\n const client = useClient<AdminAuditController>();\n const { l } = useI18n();\n const userId = state.params.userId as string;\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={15}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n items={async (query) => {\n const response = await client.findByUser({\n params: { userId },\n query,\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color=\"grape\">\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={item.resourceId || \"N/A\"}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <Flex gap={4}>\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n <Text size=\"xs\" c=\"green\">\n Success\n </Text>\n </Flex>\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <Flex gap={4}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n <Text size=\"xs\" c=\"red\">\n Failed\n </Text>\n </Flex>\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminUserAudits;\n"],"mappings":"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"}
@@ -1,9 +1,9 @@
1
1
  import { ActionButton, Control, Flex } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { useRouter } from "alepha/react/router";
4
- import { Card, Stack, Text as Text$1 } from "@mantine/core";
5
- import { useClient } from "alepha/react";
4
+ import { Card, Text as Text$1 } from "@mantine/core";
6
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { useClient } from "alepha/react";
7
7
  import { useForm } from "alepha/react/form";
8
8
 
9
9
  //#region ../../src/admin/components/users/AdminUserCreate.tsx
@@ -46,7 +46,8 @@ const AdminUserCreate = (props) => {
46
46
  w: "100%",
47
47
  children: /* @__PURE__ */ jsx("form", {
48
48
  ...form.props,
49
- children: /* @__PURE__ */ jsxs(Stack, {
49
+ children: /* @__PURE__ */ jsxs(Flex, {
50
+ direction: "column",
50
51
  gap: "md",
51
52
  children: [
52
53
  /* @__PURE__ */ jsx(Text$1, {
@@ -97,8 +98,7 @@ const AdminUserCreate = (props) => {
97
98
  })
98
99
  });
99
100
  };
100
- var AdminUserCreate_default = AdminUserCreate;
101
101
 
102
102
  //#endregion
103
- export { AdminUserCreate_default as t };
104
- //# sourceMappingURL=AdminUserCreate-BwQKr4xE.js.map
103
+ export { AdminUserCreate as default };
104
+ //# sourceMappingURL=AdminUserCreate-CgqeFwCt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminUserCreate-CgqeFwCt.js","names":["Text"],"sources":["../../src/admin/components/users/AdminUserCreate.tsx"],"sourcesContent":["import { ActionButton, Control, Flex } from \"@alepha/ui\";\nimport { Card, Text } from \"@mantine/core\";\nimport { t } from \"alepha\";\nimport type { AdminUserController } from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { useForm } from \"alepha/react/form\";\nimport { useRouter } from \"alepha/react/router\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminUserCreateProps {\n userRealmName?: string;\n}\n\nconst AdminUserCreate = (props: AdminUserCreateProps) => {\n const client = useClient<AdminUserController>();\n const router = useRouter<AdminRouter>();\n\n const form = useForm({\n schema: t.object({\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n pattern: \"^[a-zA-Z0-9._-]+$\",\n }),\n ),\n email: t.optional(t.email()),\n phoneNumber: t.optional(t.e164()),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n roles: t.optional(t.array(t.string())),\n enabled: t.optional(t.boolean()),\n password: t.optional(t.string({ minLength: 8 })),\n }),\n handler: async (data) => {\n const user = await client.createUser({\n query: {\n userRealmName: props.userRealmName,\n },\n body: {\n ...data,\n enabled: data.enabled ?? true,\n },\n });\n\n await router.push(\"adminUserDetails\", {\n params: { userId: user.id },\n });\n },\n });\n\n return (\n <Flex flex={1} p=\"md\">\n <Card withBorder p=\"lg\" maw={600} w=\"100%\">\n <form {...form.props}>\n <Flex direction=\"column\" gap=\"md\">\n <Text size=\"lg\" fw={500}>\n Create New User\n </Text>\n\n <Control title=\"Username\" input={form.input.username} />\n\n <Control title=\"Email\" input={form.input.email} />\n\n <Control title=\"Phone Number\" input={form.input.phoneNumber} />\n\n <Control title=\"First Name\" input={form.input.firstName} />\n\n <Control title=\"Last Name\" input={form.input.lastName} />\n\n <Control title=\"Password\" input={form.input.password} password />\n\n <Control title=\"Roles\" input={form.input.roles} />\n\n <Control title=\"Enabled\" input={form.input.enabled} />\n\n <ActionButton form={form}>Create User</ActionButton>\n </Flex>\n </form>\n </Card>\n </Flex>\n );\n};\n\nexport default AdminUserCreate;\n"],"mappings":";;;;;;;;;AAaA,MAAM,mBAAmB,UAAgC;CACvD,MAAM,SAAS,WAAgC;CAC/C,MAAM,SAAS,WAAwB;CAEvC,MAAM,OAAO,QAAQ;EACnB,QAAQ,EAAE,OAAO;GACf,UAAU,EAAE,SACV,EAAE,UAAU;IACV,WAAW;IACX,WAAW;IACX,SAAS;IACV,CAAC,CACH;GACD,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;GAC5B,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC;GACjC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC;GACjC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;GAChC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;GACtC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;GAChC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC,CAAC;GACjD,CAAC;EACF,SAAS,OAAO,SAAS;GACvB,MAAM,OAAO,MAAM,OAAO,WAAW;IACnC,OAAO,EACL,eAAe,MAAM,eACtB;IACD,MAAM;KACJ,GAAG;KACH,SAAS,KAAK,WAAW;KAC1B;IACF,CAAC;AAEF,SAAM,OAAO,KAAK,oBAAoB,EACpC,QAAQ,EAAE,QAAQ,KAAK,IAAI,EAC5B,CAAC;;EAEL,CAAC;AAEF,QACE,oBAAC;EAAK,MAAM;EAAG,GAAE;YACf,oBAAC;GAAK;GAAW,GAAE;GAAK,KAAK;GAAK,GAAE;aAClC,oBAAC;IAAK,GAAI,KAAK;cACb,qBAAC;KAAK,WAAU;KAAS,KAAI;;MAC3B,oBAACA;OAAK,MAAK;OAAK,IAAI;iBAAK;QAElB;MAEP,oBAAC;OAAQ,OAAM;OAAW,OAAO,KAAK,MAAM;QAAY;MAExD,oBAAC;OAAQ,OAAM;OAAQ,OAAO,KAAK,MAAM;QAAS;MAElD,oBAAC;OAAQ,OAAM;OAAe,OAAO,KAAK,MAAM;QAAe;MAE/D,oBAAC;OAAQ,OAAM;OAAa,OAAO,KAAK,MAAM;QAAa;MAE3D,oBAAC;OAAQ,OAAM;OAAY,OAAO,KAAK,MAAM;QAAY;MAEzD,oBAAC;OAAQ,OAAM;OAAW,OAAO,KAAK,MAAM;OAAU;QAAW;MAEjE,oBAAC;OAAQ,OAAM;OAAQ,OAAO,KAAK,MAAM;QAAS;MAElD,oBAAC;OAAQ,OAAM;OAAU,OAAO,KAAK,MAAM;QAAW;MAEtD,oBAAC;OAAmB;iBAAM;QAA0B;;MAC/C;KACF;IACF;GACF"}
@@ -2,15 +2,15 @@ import { ActionButton, ClipboardButton, Control } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconActivity, IconCalendar, IconCheck, IconDevices, IconKey, IconShieldCheck, IconUser, IconX } from "@tabler/icons-react";
4
4
  import { useRouterState } from "alepha/react/router";
5
- import { Badge, Box, Card, Center, Divider, Grid, Group, Loader, Paper, SimpleGrid, Stack, Text as Text$1, ThemeIcon } from "@mantine/core";
5
+ import { Badge, Card, Center, Divider, Flex as Flex$1, Grid, Loader, Paper, SimpleGrid, Text as Text$1, ThemeIcon } from "@mantine/core";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import { useClient } from "alepha/react";
7
8
  import { useI18n } from "alepha/react/i18n";
8
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { useEffect, useState } from "react";
10
10
  import { useForm } from "alepha/react/form";
11
11
 
12
12
  //#region ../../src/admin/components/users/AdminUserDetails.tsx
13
- const DataRow = ({ label, value, copyValue }) => /* @__PURE__ */ jsxs(Group, {
13
+ const DataRow = ({ label, value, copyValue }) => /* @__PURE__ */ jsxs(Flex$1, {
14
14
  justify: "space-between",
15
15
  py: 8,
16
16
  wrap: "nowrap",
@@ -20,7 +20,7 @@ const DataRow = ({ label, value, copyValue }) => /* @__PURE__ */ jsxs(Group, {
20
20
  c: "dimmed",
21
21
  style: { flexShrink: 0 },
22
22
  children: label
23
- }), /* @__PURE__ */ jsxs(Group, {
23
+ }), /* @__PURE__ */ jsxs(Flex$1, {
24
24
  gap: 6,
25
25
  wrap: "nowrap",
26
26
  style: { minWidth: 0 },
@@ -42,7 +42,7 @@ const StatCard = ({ icon, label, value, color }) => /* @__PURE__ */ jsx(Paper, {
42
42
  p: "md",
43
43
  radius: "md",
44
44
  withBorder: true,
45
- children: /* @__PURE__ */ jsxs(Group, {
45
+ children: /* @__PURE__ */ jsxs(Flex$1, {
46
46
  gap: "sm",
47
47
  children: [/* @__PURE__ */ jsx(ThemeIcon, {
48
48
  size: "lg",
@@ -50,7 +50,7 @@ const StatCard = ({ icon, label, value, color }) => /* @__PURE__ */ jsx(Paper, {
50
50
  variant: "light",
51
51
  color,
52
52
  children: icon
53
- }), /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsx(Text$1, {
53
+ }), /* @__PURE__ */ jsxs(Flex$1, { children: [/* @__PURE__ */ jsx(Text$1, {
54
54
  size: "xl",
55
55
  fw: 700,
56
56
  lh: 1,
@@ -119,7 +119,8 @@ const AdminUserDetails = (props) => {
119
119
  if (!user) return /* @__PURE__ */ jsx(Center, {
120
120
  flex: 1,
121
121
  py: "xl",
122
- children: /* @__PURE__ */ jsxs(Stack, {
122
+ children: /* @__PURE__ */ jsxs(Flex$1, {
123
+ direction: "column",
123
124
  align: "center",
124
125
  gap: "xs",
125
126
  children: [/* @__PURE__ */ jsx(IconUser, {
@@ -132,7 +133,8 @@ const AdminUserDetails = (props) => {
132
133
  })
133
134
  });
134
135
  const displayName = user.firstName && user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName || user.lastName || null;
135
- return /* @__PURE__ */ jsxs(Stack, {
136
+ return /* @__PURE__ */ jsxs(Flex$1, {
137
+ direction: "column",
136
138
  gap: "md",
137
139
  children: [
138
140
  /* @__PURE__ */ jsxs(SimpleGrid, {
@@ -177,7 +179,7 @@ const AdminUserDetails = (props) => {
177
179
  radius: "md",
178
180
  withBorder: true,
179
181
  h: "100%",
180
- children: [/* @__PURE__ */ jsx(Group, {
182
+ children: [/* @__PURE__ */ jsx(Flex$1, {
181
183
  justify: "space-between",
182
184
  p: "md",
183
185
  pb: 0,
@@ -186,7 +188,7 @@ const AdminUserDetails = (props) => {
186
188
  size: "sm",
187
189
  children: "Account Details"
188
190
  })
189
- }), /* @__PURE__ */ jsxs(Box, {
191
+ }), /* @__PURE__ */ jsxs(Flex$1, {
190
192
  px: "md",
191
193
  pb: "md",
192
194
  children: [
@@ -202,7 +204,7 @@ const AdminUserDetails = (props) => {
202
204
  }),
203
205
  /* @__PURE__ */ jsx(DataRow, {
204
206
  label: "Email",
205
- value: /* @__PURE__ */ jsxs(Group, {
207
+ value: /* @__PURE__ */ jsxs(Flex$1, {
206
208
  gap: 6,
207
209
  children: [/* @__PURE__ */ jsx(Text$1, {
208
210
  size: "sm",
@@ -228,7 +230,7 @@ const AdminUserDetails = (props) => {
228
230
  }),
229
231
  /* @__PURE__ */ jsx(DataRow, {
230
232
  label: "Status",
231
- value: /* @__PURE__ */ jsxs(Group, {
233
+ value: /* @__PURE__ */ jsxs(Flex$1, {
232
234
  gap: 4,
233
235
  children: [/* @__PURE__ */ jsx(ThemeIcon, {
234
236
  size: 16,
@@ -256,7 +258,7 @@ const AdminUserDetails = (props) => {
256
258
  radius: "md",
257
259
  withBorder: true,
258
260
  h: "100%",
259
- children: [/* @__PURE__ */ jsxs(Group, {
261
+ children: [/* @__PURE__ */ jsxs(Flex$1, {
260
262
  justify: "space-between",
261
263
  p: "md",
262
264
  pb: 0,
@@ -270,11 +272,12 @@ const AdminUserDetails = (props) => {
270
272
  onClick: () => setEditing(true),
271
273
  children: "Edit"
272
274
  })]
273
- }), editing ? /* @__PURE__ */ jsx(Box, {
275
+ }), editing ? /* @__PURE__ */ jsx(Flex$1, {
274
276
  p: "md",
275
277
  children: /* @__PURE__ */ jsx("form", {
276
278
  ...form.props,
277
- children: /* @__PURE__ */ jsxs(Stack, {
279
+ children: /* @__PURE__ */ jsxs(Flex$1, {
280
+ direction: "column",
278
281
  gap: "sm",
279
282
  children: [
280
283
  /* @__PURE__ */ jsxs(SimpleGrid, {
@@ -302,7 +305,7 @@ const AdminUserDetails = (props) => {
302
305
  input: form.input.roles
303
306
  }),
304
307
  /* @__PURE__ */ jsx(Divider, {}),
305
- /* @__PURE__ */ jsxs(Group, {
308
+ /* @__PURE__ */ jsxs(Flex$1, {
306
309
  justify: "flex-end",
307
310
  gap: "xs",
308
311
  children: [/* @__PURE__ */ jsx(ActionButton, {
@@ -319,7 +322,7 @@ const AdminUserDetails = (props) => {
319
322
  ]
320
323
  })
321
324
  })
322
- }) : /* @__PURE__ */ jsxs(Box, {
325
+ }) : /* @__PURE__ */ jsxs(Flex$1, {
323
326
  px: "md",
324
327
  pb: "md",
325
328
  children: [
@@ -337,7 +340,7 @@ const AdminUserDetails = (props) => {
337
340
  }),
338
341
  /* @__PURE__ */ jsx(DataRow, {
339
342
  label: "Roles",
340
- value: user.roles.length > 0 ? /* @__PURE__ */ jsx(Group, {
343
+ value: user.roles.length > 0 ? /* @__PURE__ */ jsx(Flex$1, {
341
344
  gap: 4,
342
345
  children: user.roles.map((role) => /* @__PURE__ */ jsx(Badge, {
343
346
  size: "xs",
@@ -358,7 +361,7 @@ const AdminUserDetails = (props) => {
358
361
  padding: 0,
359
362
  radius: "md",
360
363
  withBorder: true,
361
- children: [/* @__PURE__ */ jsx(Group, {
364
+ children: [/* @__PURE__ */ jsx(Flex$1, {
362
365
  justify: "space-between",
363
366
  p: "md",
364
367
  pb: 0,
@@ -374,8 +377,8 @@ const AdminUserDetails = (props) => {
374
377
  },
375
378
  p: "md",
376
379
  children: [
377
- /* @__PURE__ */ jsxs(Box, { children: [
378
- /* @__PURE__ */ jsxs(Group, {
380
+ /* @__PURE__ */ jsxs(Flex$1, { children: [
381
+ /* @__PURE__ */ jsxs(Flex$1, {
379
382
  gap: 6,
380
383
  mb: 4,
381
384
  children: [/* @__PURE__ */ jsx(IconCalendar, {
@@ -398,8 +401,8 @@ const AdminUserDetails = (props) => {
398
401
  children: l(user.createdAt, { date: "fromNow" })
399
402
  })
400
403
  ] }),
401
- /* @__PURE__ */ jsxs(Box, { children: [
402
- /* @__PURE__ */ jsxs(Group, {
404
+ /* @__PURE__ */ jsxs(Flex$1, { children: [
405
+ /* @__PURE__ */ jsxs(Flex$1, {
403
406
  gap: 6,
404
407
  mb: 4,
405
408
  children: [/* @__PURE__ */ jsx(IconCalendar, {
@@ -422,7 +425,7 @@ const AdminUserDetails = (props) => {
422
425
  children: l(user.updatedAt, { date: "fromNow" })
423
426
  })
424
427
  ] }),
425
- /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Group, {
428
+ /* @__PURE__ */ jsxs(Flex$1, { children: [/* @__PURE__ */ jsxs(Flex$1, {
426
429
  gap: 6,
427
430
  mb: 4,
428
431
  children: [/* @__PURE__ */ jsx(IconDevices, {
@@ -438,7 +441,7 @@ const AdminUserDetails = (props) => {
438
441
  c: "dimmed",
439
442
  children: "—"
440
443
  })] }),
441
- /* @__PURE__ */ jsxs(Box, { children: [/* @__PURE__ */ jsxs(Group, {
444
+ /* @__PURE__ */ jsxs(Flex$1, { children: [/* @__PURE__ */ jsxs(Flex$1, {
442
445
  gap: 6,
443
446
  mb: 4,
444
447
  children: [/* @__PURE__ */ jsx(IconCheck, {
@@ -468,8 +471,7 @@ const AdminUserDetails = (props) => {
468
471
  ]
469
472
  });
470
473
  };
471
- var AdminUserDetails_default = AdminUserDetails;
472
474
 
473
475
  //#endregion
474
- export { AdminUserDetails_default as t };
475
- //# sourceMappingURL=AdminUserDetails-uqtC5aJ1.js.map
476
+ export { AdminUserDetails as default };
477
+ //# sourceMappingURL=AdminUserDetails-DDe1A1GP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminUserDetails-DDe1A1GP.js","names":["Flex","Text"],"sources":["../../src/admin/components/users/AdminUserDetails.tsx"],"sourcesContent":["import { ActionButton, ClipboardButton, Control } from \"@alepha/ui\";\nimport {\n Badge,\n Card,\n Center,\n Divider,\n Flex,\n Grid,\n Loader,\n Paper,\n SimpleGrid,\n Text,\n ThemeIcon,\n} from \"@mantine/core\";\nimport {\n IconActivity,\n IconCalendar,\n IconCheck,\n IconDevices,\n IconKey,\n IconShieldCheck,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { t } from \"alepha\";\nimport type { AdminUserController, UserEntity } from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { useForm } from \"alepha/react/form\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouterState } from \"alepha/react/router\";\nimport { type ReactNode, useEffect, useState } from \"react\";\n\nexport interface AdminUserDetailsProps {\n userRealmName?: string;\n}\n\ninterface DataRowProps {\n label: string;\n value: ReactNode;\n copyValue?: string;\n}\n\nconst DataRow = ({ label, value, copyValue }: DataRowProps) => (\n <Flex\n justify=\"space-between\"\n py={8}\n wrap=\"nowrap\"\n style={{ borderBottom: \"1px solid var(--mantine-color-default-border)\" }}\n >\n <Text size=\"sm\" c=\"dimmed\" style={{ flexShrink: 0 }}>\n {label}\n </Text>\n <Flex gap={6} wrap=\"nowrap\" style={{ minWidth: 0 }}>\n {typeof value === \"string\" ? (\n <Text size=\"sm\" fw={500} truncate style={{ maxWidth: 220 }}>\n {value || \"—\"}\n </Text>\n ) : (\n value\n )}\n {copyValue && (\n <ClipboardButton\n value={copyValue}\n size=\"xs\"\n variant=\"subtle\"\n c=\"dimmed\"\n />\n )}\n </Flex>\n </Flex>\n);\n\ninterface StatCardProps {\n icon: ReactNode;\n label: string;\n value: string | number;\n color: string;\n}\n\nconst StatCard = ({ icon, label, value, color }: StatCardProps) => (\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Flex gap=\"sm\">\n <ThemeIcon size=\"lg\" radius=\"md\" variant=\"light\" color={color}>\n {icon}\n </ThemeIcon>\n <Flex>\n <Text size=\"xl\" fw={700} lh={1}>\n {value}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {label}\n </Text>\n </Flex>\n </Flex>\n </Paper>\n);\n\nconst AdminUserDetails = (props: AdminUserDetailsProps) => {\n const state = useRouterState();\n const client = useClient<AdminUserController>();\n const { l } = useI18n();\n const userId = state.params.userId as string;\n\n const [user, setUser] = useState<UserEntity | null>(null);\n const [loading, setLoading] = useState(true);\n const [editing, setEditing] = useState(false);\n\n useEffect(() => {\n const loadUser = async () => {\n try {\n const data = await client.getUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n });\n setUser(data);\n } finally {\n setLoading(false);\n }\n };\n\n loadUser();\n }, [userId]);\n\n const form = useForm({\n schema: t.object({\n email: t.optional(t.email()),\n phoneNumber: t.optional(t.e164()),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n roles: t.optional(t.array(t.string())),\n enabled: t.optional(t.boolean()),\n }),\n handler: async (data) => {\n const updated = await client.updateUser({\n params: { id: userId },\n query: { userRealmName: props.userRealmName },\n body: data,\n });\n setUser(updated);\n setEditing(false);\n },\n });\n\n useEffect(() => {\n if (user) {\n form.input.email?.set(user.email ?? \"\");\n form.input.phoneNumber?.set(user.phoneNumber ?? \"\");\n form.input.firstName?.set(user.firstName ?? \"\");\n form.input.lastName?.set(user.lastName ?? \"\");\n form.input.roles?.set(user.roles ?? []);\n form.input.enabled?.set(user.enabled);\n }\n }, [user]);\n\n if (loading) {\n return (\n <Center flex={1} py=\"xl\">\n <Loader />\n </Center>\n );\n }\n\n if (!user) {\n return (\n <Center flex={1} py=\"xl\">\n <Flex direction=\"column\" align=\"center\" gap=\"xs\">\n <IconUser size={48} opacity={0.3} />\n <Text c=\"dimmed\">User not found</Text>\n </Flex>\n </Center>\n );\n }\n\n const displayName =\n user.firstName && user.lastName\n ? `${user.firstName} ${user.lastName}`\n : user.firstName || user.lastName || null;\n\n return (\n <Flex direction=\"column\" gap=\"md\">\n {/* Stats Overview */}\n <SimpleGrid cols={{ base: 2, sm: 4 }}>\n <StatCard\n icon={<IconDevices size={18} />}\n label=\"Sessions\"\n value={0}\n color=\"blue\"\n />\n <StatCard\n icon={<IconActivity size={18} />}\n label=\"API Calls\"\n value={0}\n color=\"green\"\n />\n <StatCard\n icon={<IconKey size={18} />}\n label=\"Failed Logins\"\n value={0}\n color=\"orange\"\n />\n <StatCard\n icon={<IconShieldCheck size={18} />}\n label=\"Roles\"\n value={user.roles.length}\n color=\"violet\"\n />\n </SimpleGrid>\n\n <Grid>\n {/* Left Column - Account Details */}\n <Grid.Col span={{ base: 12, md: 6 }}>\n <Card padding={0} radius=\"md\" withBorder h=\"100%\">\n <Flex justify=\"space-between\" p=\"md\" pb={0}>\n <Text fw={600} size=\"sm\">\n Account Details\n </Text>\n </Flex>\n <Flex px=\"md\" pb=\"md\">\n <DataRow label=\"User ID\" value={user.id} copyValue={user.id} />\n <DataRow\n label=\"Username\"\n value={user.username || \"—\"}\n copyValue={user.username}\n />\n <DataRow\n label=\"Email\"\n value={\n <Flex gap={6}>\n <Text size=\"sm\" fw={500}>\n {user.email || \"—\"}\n </Text>\n {user.email && (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={user.emailVerified ? \"green\" : \"orange\"}\n >\n {user.emailVerified ? \"verified\" : \"unverified\"}\n </Badge>\n )}\n </Flex>\n }\n copyValue={user.email}\n />\n <DataRow\n label=\"Phone\"\n value={user.phoneNumber || \"—\"}\n copyValue={user.phoneNumber}\n />\n <DataRow label=\"Realm\" value={user.realm} />\n <DataRow\n label=\"Status\"\n value={\n <Flex gap={4}>\n <ThemeIcon\n size={16}\n radius=\"xl\"\n color={user.enabled ? \"green\" : \"red\"}\n variant=\"filled\"\n >\n {user.enabled ? (\n <IconCheck size={10} />\n ) : (\n <IconX size={10} />\n )}\n </ThemeIcon>\n <Text size=\"sm\" fw={500}>\n {user.enabled ? \"Active\" : \"Disabled\"}\n </Text>\n </Flex>\n }\n />\n </Flex>\n </Card>\n </Grid.Col>\n\n {/* Right Column - Personal Info */}\n <Grid.Col span={{ base: 12, md: 6 }}>\n <Card padding={0} radius=\"md\" withBorder h=\"100%\">\n <Flex justify=\"space-between\" p=\"md\" pb={0}>\n <Text fw={600} size=\"sm\">\n Personal Information\n </Text>\n {!editing && (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n onClick={() => setEditing(true)}\n >\n Edit\n </ActionButton>\n )}\n </Flex>\n\n {editing ? (\n <Flex p=\"md\">\n <form {...form.props}>\n <Flex direction=\"column\" gap=\"sm\">\n <SimpleGrid cols={2}>\n <Control\n title=\"First Name\"\n input={form.input.firstName}\n />\n <Control title=\"Last Name\" input={form.input.lastName} />\n </SimpleGrid>\n <SimpleGrid cols={2}>\n <Control title=\"Email\" input={form.input.email} />\n <Control title=\"Phone\" input={form.input.phoneNumber} />\n </SimpleGrid>\n <Control title=\"Roles\" input={form.input.roles} />\n <Divider />\n <Flex justify=\"flex-end\" gap=\"xs\">\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n onClick={() => setEditing(false)}\n >\n Cancel\n </ActionButton>\n <ActionButton size=\"xs\" form={form}>\n Save\n </ActionButton>\n </Flex>\n </Flex>\n </form>\n </Flex>\n ) : (\n <Flex px=\"md\" pb=\"md\">\n <DataRow label=\"First Name\" value={user.firstName || \"—\"} />\n <DataRow label=\"Last Name\" value={user.lastName || \"—\"} />\n <DataRow label=\"Display Name\" value={displayName || \"—\"} />\n <DataRow\n label=\"Roles\"\n value={\n user.roles.length > 0 ? (\n <Flex gap={4}>\n {user.roles.map((role) => (\n <Badge key={role} size=\"xs\" variant=\"light\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : (\n <Text size=\"sm\" c=\"dimmed\">\n No roles\n </Text>\n )\n }\n />\n </Flex>\n )}\n </Card>\n </Grid.Col>\n </Grid>\n\n {/* Timeline */}\n <Card padding={0} radius=\"md\" withBorder>\n <Flex justify=\"space-between\" p=\"md\" pb={0}>\n <Text fw={600} size=\"sm\">\n Activity Timeline\n </Text>\n </Flex>\n <SimpleGrid cols={{ base: 2, sm: 4 }} p=\"md\">\n <Flex>\n <Flex gap={6} mb={4}>\n <IconCalendar size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Created\n </Text>\n </Flex>\n <Text size=\"sm\" fw={500}>\n {l(user.createdAt, { date: \"ll\" })}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {l(user.createdAt, { date: \"fromNow\" })}\n </Text>\n </Flex>\n <Flex>\n <Flex gap={6} mb={4}>\n <IconCalendar size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Updated\n </Text>\n </Flex>\n <Text size=\"sm\" fw={500}>\n {l(user.updatedAt, { date: \"ll\" })}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {l(user.updatedAt, { date: \"fromNow\" })}\n </Text>\n </Flex>\n <Flex>\n <Flex gap={6} mb={4}>\n <IconDevices size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Last Login\n </Text>\n </Flex>\n <Text size=\"sm\" c=\"dimmed\">\n —\n </Text>\n </Flex>\n <Flex>\n <Flex gap={6} mb={4}>\n <IconCheck size={14} style={{ opacity: 0.5 }} />\n <Text size=\"xs\" c=\"dimmed\">\n Email Verified\n </Text>\n </Flex>\n {user.emailVerified ? (\n <>\n <Text size=\"sm\" fw={500}>\n {l(user.updatedAt, { date: \"ll\" })}\n </Text>\n <Text size=\"xs\" c=\"dimmed\">\n {l(user.updatedAt, { date: \"fromNow\" })}\n </Text>\n </>\n ) : (\n <Text size=\"sm\" c=\"dimmed\">\n Not verified\n </Text>\n )}\n </Flex>\n </SimpleGrid>\n </Card>\n </Flex>\n );\n};\n\nexport default AdminUserDetails;\n"],"mappings":"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"}