@alepha/ui 0.18.3 → 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 (163) hide show
  1. package/dist/admin/{AdminApiKeys-Dy_k-4Vd.js → AdminApiKeys-Bt1PjO6o.js} +3 -4
  2. package/dist/admin/{AdminApiKeys-Dy_k-4Vd.js.map → AdminApiKeys-Bt1PjO6o.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-CKiFMSSU.js → AdminAudits-C7c1CN4c.js} +3 -4
  4. package/dist/admin/{AdminAudits-CKiFMSSU.js.map → AdminAudits-C7c1CN4c.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-PhC_dZqo.js → AdminDashboard-C3RXpTp6.js} +3 -4
  6. package/dist/admin/{AdminDashboard-PhC_dZqo.js.map → AdminDashboard-C3RXpTp6.js.map} +1 -1
  7. package/dist/admin/{AdminFiles-DFTjijGp.js → AdminFiles-31ivR6Wq.js} +3 -4
  8. package/dist/admin/{AdminFiles-DFTjijGp.js.map → AdminFiles-31ivR6Wq.js.map} +1 -1
  9. package/dist/admin/{AdminJobDashboard-BL8gGPDp.js → AdminJobDashboard-BABLe7hL.js} +73 -25
  10. package/dist/admin/AdminJobDashboard-BABLe7hL.js.map +1 -0
  11. package/dist/admin/{AdminJobExecutions-D9E-CS-U.js → AdminJobExecutions-D-G8RIlr.js} +3 -4
  12. package/dist/admin/{AdminJobExecutions-D9E-CS-U.js.map → AdminJobExecutions-D-G8RIlr.js.map} +1 -1
  13. package/dist/admin/{AdminJobRegistry-Ci9ue1zC.js → AdminJobRegistry-oIS3K9NX.js} +3 -4
  14. package/dist/admin/{AdminJobRegistry-Ci9ue1zC.js.map → AdminJobRegistry-oIS3K9NX.js.map} +1 -1
  15. package/dist/admin/{AdminLayout-I6TlUMPc.js → AdminLayout-BmZ9mtXh.js} +8 -25
  16. package/dist/admin/AdminLayout-BmZ9mtXh.js.map +1 -0
  17. package/dist/admin/{AdminNotifications-ZPHCYrv7.js → AdminNotifications-DHdzksww.js} +3 -4
  18. package/dist/admin/{AdminNotifications-ZPHCYrv7.js.map → AdminNotifications-DHdzksww.js.map} +1 -1
  19. package/dist/admin/{AdminParameters-CqgvhRsb.js → AdminParameters-CyZQSXnN.js} +3 -12
  20. package/dist/admin/{AdminParameters-CqgvhRsb.js.map → AdminParameters-CyZQSXnN.js.map} +1 -1
  21. package/dist/admin/{AdminSessions-Bz5NRuoW.js → AdminSessions--xwELDSO.js} +3 -4
  22. package/dist/admin/{AdminSessions-Bz5NRuoW.js.map → AdminSessions--xwELDSO.js.map} +1 -1
  23. package/dist/admin/{AdminUserLayout-lXT6I0Qq.js → AdminUserLayout-DvBTG5gd.js} +72 -111
  24. package/dist/admin/AdminUserLayout-DvBTG5gd.js.map +1 -0
  25. package/dist/admin/{AdminUserProfile-vFBLoJ3h.js → AdminUserProfile-CzsPBl6Z.js} +7 -6
  26. package/dist/admin/AdminUserProfile-CzsPBl6Z.js.map +1 -0
  27. package/dist/admin/{AdminUserSessions-CT_YDim0.js → AdminUserSessions-C-aUnhVN.js} +3 -4
  28. package/dist/admin/{AdminUserSessions-CT_YDim0.js.map → AdminUserSessions-C-aUnhVN.js.map} +1 -1
  29. package/dist/admin/{AdminUsers-D1UfGya9.js → AdminUsers-BYwei5sj.js} +4 -4
  30. package/dist/admin/AdminUsers-BYwei5sj.js.map +1 -0
  31. package/dist/admin/{AuthLayout-_frhdgOO.js → AuthLayout-CkPGLJku.js} +3 -4
  32. package/dist/admin/{AuthLayout-_frhdgOO.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-xtNmQtGh.js → Login-DSBqNsZc.js} +5 -6
  36. package/dist/{auth/Login-BA1E8IZl.js.map → admin/Login-DSBqNsZc.js.map} +1 -1
  37. package/dist/admin/{Profile-_AtPUwAP.js → Profile-CDRjJo0P.js} +3 -5
  38. package/dist/{demo/Profile-DS5q4vOh.js.map → admin/Profile-CDRjJo0P.js.map} +1 -1
  39. package/dist/admin/{Register-JcCjHUUn.js → Register-4QGFOnfh.js} +5 -6
  40. package/dist/{demo/Register-B4hLBeEv.js.map → admin/Register-4QGFOnfh.js.map} +1 -1
  41. package/dist/admin/{ResetPassword-CwGBPLJO.js → ResetPassword-Gxc9L_mY.js} +4 -5
  42. package/dist/{auth/ResetPassword-DCtGcneA.js.map → admin/ResetPassword-Gxc9L_mY.js.map} +1 -1
  43. package/dist/admin/{VerifyEmail-hNxWejWf.js → VerifyEmail-D7G5NnaN.js} +4 -5
  44. package/dist/{auth/VerifyEmail-DkH7NBfn.js.map → admin/VerifyEmail-D7G5NnaN.js.map} +1 -1
  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-CYaRQ8O-.js → core-D1AbU50V.js} +24 -85
  48. package/dist/admin/core-D1AbU50V.js.map +1 -0
  49. package/dist/admin/index.d.ts +59 -10
  50. package/dist/admin/index.d.ts.map +1 -1
  51. package/dist/admin/index.js +35 -36
  52. package/dist/admin/index.js.map +1 -1
  53. package/dist/admin/rolldown-runtime-CiIaOW0V.js +13 -0
  54. package/dist/{demo/AuthLayout-Brri4A-L.js → auth/AuthLayout-CfRKcTqP.js} +3 -4
  55. package/dist/auth/{AuthLayout-AvLlcLjS.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-BA1E8IZl.js → Login-DJyweoPS.js} +5 -6
  59. package/dist/{demo/Login-C12N4oGs.js.map → auth/Login-DJyweoPS.js.map} +1 -1
  60. package/dist/{demo/Profile-DS5q4vOh.js → auth/Profile-Cy93pNTw.js} +3 -5
  61. package/dist/auth/{Profile-YcWdeuFz.js.map → Profile-Cy93pNTw.js.map} +1 -1
  62. package/dist/auth/{Register-CPhEO5MG.js → Register-CSqzzitW.js} +5 -6
  63. package/dist/{admin/Register-JcCjHUUn.js.map → auth/Register-CSqzzitW.js.map} +1 -1
  64. package/dist/{demo/ResetPassword-D8g9ha1N.js → auth/ResetPassword-B61QPlQi.js} +4 -5
  65. package/dist/{admin/ResetPassword-CwGBPLJO.js.map → auth/ResetPassword-B61QPlQi.js.map} +1 -1
  66. package/dist/auth/{VerifyEmail-DkH7NBfn.js → VerifyEmail-CqBJ11id.js} +4 -5
  67. package/dist/{admin/VerifyEmail-hNxWejWf.js.map → auth/VerifyEmail-CqBJ11id.js.map} +1 -1
  68. package/dist/auth/{core-D5jIAVF2.js → core-C6D3pazL.js} +22 -54
  69. package/dist/auth/core-C6D3pazL.js.map +1 -0
  70. package/dist/auth/index.d.ts +0 -6
  71. package/dist/auth/index.d.ts.map +1 -1
  72. package/dist/auth/index.js +13 -18
  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 +10 -4
  76. package/dist/core/index.d.ts.map +1 -1
  77. package/dist/core/index.js +21 -91
  78. package/dist/core/index.js.map +1 -1
  79. package/dist/{auth/AuthLayout-AvLlcLjS.js → demo/AuthLayout-Dq5tSLSc.js} +3 -4
  80. package/dist/demo/{AuthLayout-Brri4A-L.js.map → AuthLayout-Dq5tSLSc.js.map} +1 -1
  81. package/dist/demo/{DemoButton-wiCxZZ_L.js → DemoButton-_Ws2w-J0.js} +4 -5
  82. package/dist/demo/{DemoButton-wiCxZZ_L.js.map → DemoButton-_Ws2w-J0.js.map} +1 -1
  83. package/dist/demo/{DemoControlSelect-D7ILObVg.js → DemoControlSelect-ChP4ZOpQ.js} +4 -5
  84. package/dist/demo/{DemoControlSelect-D7ILObVg.js.map → DemoControlSelect-ChP4ZOpQ.js.map} +1 -1
  85. package/dist/demo/{DemoDataTable-DZ5Y8pFX.js → DemoDataTable-Hwf_UUni.js} +4 -5
  86. package/dist/demo/{DemoDataTable-DZ5Y8pFX.js.map → DemoDataTable-Hwf_UUni.js.map} +1 -1
  87. package/dist/demo/{DemoDialog-CUWdLHim.js → DemoDialog-B01OMVRd.js} +3 -4
  88. package/dist/demo/{DemoDialog-CUWdLHim.js.map → DemoDialog-B01OMVRd.js.map} +1 -1
  89. package/dist/demo/{DemoFlex-a8OhMMvq.js → DemoFlex-870PEl0V.js} +4 -5
  90. package/dist/demo/{DemoFlex-a8OhMMvq.js.map → DemoFlex-870PEl0V.js.map} +1 -1
  91. package/dist/demo/{DemoHeading-C13OVDfS.js → DemoHeading-C1YR27fz.js} +4 -5
  92. package/dist/demo/{DemoHeading-C13OVDfS.js.map → DemoHeading-C1YR27fz.js.map} +1 -1
  93. package/dist/demo/{DemoHome-D_De3UiT.js → DemoHome-DRbL2eGf.js} +4 -5
  94. package/dist/demo/{DemoHome-D_De3UiT.js.map → DemoHome-DRbL2eGf.js.map} +1 -1
  95. package/dist/demo/{DemoJsonViewer-B50s9aGM.js → DemoJsonViewer-DoABiqBW.js} +4 -5
  96. package/dist/demo/{DemoJsonViewer-B50s9aGM.js.map → DemoJsonViewer-DoABiqBW.js.map} +1 -1
  97. package/dist/demo/{DemoLayout-CHU8WTwO.js → DemoLayout-CN_PDCX2.js} +4 -5
  98. package/dist/demo/DemoLayout-CN_PDCX2.js.map +1 -0
  99. package/dist/demo/{DemoLogin-BBlrWpml.js → DemoLogin-B5x-ug3Q.js} +10 -11
  100. package/dist/demo/{DemoLogin-BBlrWpml.js.map → DemoLogin-B5x-ug3Q.js.map} +1 -1
  101. package/dist/demo/{DemoRegister-BuNE3_-f.js → DemoRegister-Q6sg2xuV.js} +10 -11
  102. package/dist/demo/{DemoRegister-BuNE3_-f.js.map → DemoRegister-Q6sg2xuV.js.map} +1 -1
  103. package/dist/demo/{DemoResetPassword-D_IjjjOJ.js → DemoResetPassword-DrqZfmEw.js} +10 -11
  104. package/dist/demo/{DemoResetPassword-D_IjjjOJ.js.map → DemoResetPassword-DrqZfmEw.js.map} +1 -1
  105. package/dist/demo/{DemoSidebar-Giy2HRBD.js → DemoSidebar-CfKS6w1o.js} +4 -5
  106. package/dist/demo/{DemoSidebar-Giy2HRBD.js.map → DemoSidebar-CfKS6w1o.js.map} +1 -1
  107. package/dist/demo/{DemoText-ubcw-vog.js → DemoText-pT6Gi5b5.js} +4 -5
  108. package/dist/demo/{DemoText-ubcw-vog.js.map → DemoText-pT6Gi5b5.js.map} +1 -1
  109. package/dist/demo/{DemoToast-9die_dYT.js → DemoToast-I13NBzQQ.js} +3 -4
  110. package/dist/demo/{DemoToast-9die_dYT.js.map → DemoToast-I13NBzQQ.js.map} +1 -1
  111. package/dist/demo/{DemoTypeForm-D_d6OVKL.js → DemoTypeForm-BqzcrtvN.js} +4 -5
  112. package/dist/demo/{DemoTypeForm-D_d6OVKL.js.map → DemoTypeForm-BqzcrtvN.js.map} +1 -1
  113. package/dist/demo/{DemoVerifyEmail-B43KlF4F.js → DemoVerifyEmail-HwD8xfQw.js} +10 -11
  114. package/dist/demo/{DemoVerifyEmail-B43KlF4F.js.map → DemoVerifyEmail-HwD8xfQw.js.map} +1 -1
  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-C12N4oGs.js → Login-CqG1iJbn.js} +5 -6
  118. package/dist/{admin/Login-xtNmQtGh.js.map → demo/Login-CqG1iJbn.js.map} +1 -1
  119. package/dist/{auth/Profile-YcWdeuFz.js → demo/Profile-C0ojJCaG.js} +3 -5
  120. package/dist/{admin/Profile-_AtPUwAP.js.map → demo/Profile-C0ojJCaG.js.map} +1 -1
  121. package/dist/demo/{Register-B4hLBeEv.js → Register-KKZwr_lL.js} +5 -6
  122. package/dist/{auth/Register-CPhEO5MG.js.map → demo/Register-KKZwr_lL.js.map} +1 -1
  123. package/dist/{auth/ResetPassword-DCtGcneA.js → demo/ResetPassword-DMrLFEtr.js} +4 -5
  124. package/dist/demo/{ResetPassword-D8g9ha1N.js.map → ResetPassword-DMrLFEtr.js.map} +1 -1
  125. package/dist/demo/{Showcase-D6Fxt4X4.js → Showcase-D49Wud2v.js} +3 -5
  126. package/dist/demo/{Showcase-D6Fxt4X4.js.map → Showcase-D49Wud2v.js.map} +1 -1
  127. package/dist/demo/{VerifyEmail-BjDo0cZA.js → VerifyEmail-BFCAFz6T.js} +4 -5
  128. package/dist/demo/{VerifyEmail-BjDo0cZA.js.map → VerifyEmail-BFCAFz6T.js.map} +1 -1
  129. package/dist/demo/{auth-ByVTreDl.js → auth-D9qTZzCa.js} +18 -35
  130. package/dist/demo/{auth-ByVTreDl.js.map → auth-D9qTZzCa.js.map} +1 -1
  131. package/dist/demo/{core-DFgB3yU4.js → core-DRtQklr3.js} +23 -86
  132. package/dist/demo/core-DRtQklr3.js.map +1 -0
  133. package/dist/demo/index.js +19 -22
  134. package/dist/demo/index.js.map +1 -1
  135. package/dist/demo/rolldown-runtime-CiIaOW0V.js +13 -0
  136. package/package.json +17 -17
  137. package/src/admin/AdminRouter.tsx +18 -1
  138. package/src/admin/atoms/adminUserAtom.ts +7 -0
  139. package/src/admin/components/AdminLayout.tsx +2 -14
  140. package/src/admin/components/jobs/AdminJobDashboard.tsx +51 -20
  141. package/src/admin/components/users/AdminUserLayout.tsx +84 -127
  142. package/src/admin/components/users/AdminUserProfile.tsx +5 -2
  143. package/src/admin/components/users/AdminUsers.tsx +1 -1
  144. package/src/core/components/Flex.tsx +24 -0
  145. package/src/core/components/buttons/ActionButton.tsx +1 -0
  146. package/src/core/components/dialogs/PromptDialog.tsx +1 -1
  147. package/src/core/components/layout/Breadcrumb.tsx +2 -2
  148. package/src/core/components/layout/DashboardShell.tsx +1 -1
  149. package/src/core/services/DialogService.tsx +2 -2
  150. package/src/core/styles.css +2 -1
  151. package/src/core/table/components/DataTable.tsx +0 -1
  152. package/dist/admin/AdminJobDashboard-BL8gGPDp.js.map +0 -1
  153. package/dist/admin/AdminLayout-I6TlUMPc.js.map +0 -1
  154. package/dist/admin/AdminUserLayout-lXT6I0Qq.js.map +0 -1
  155. package/dist/admin/AdminUserProfile-vFBLoJ3h.js.map +0 -1
  156. package/dist/admin/AdminUsers-D1UfGya9.js.map +0 -1
  157. package/dist/admin/core-CYaRQ8O-.js.map +0 -1
  158. package/dist/admin/rolldown-runtime-CjeV3_4I.js +0 -18
  159. package/dist/auth/core-D5jIAVF2.js.map +0 -1
  160. package/dist/auth/rolldown-runtime-CjeV3_4I.js +0 -18
  161. package/dist/demo/DemoLayout-CHU8WTwO.js.map +0 -1
  162. package/dist/demo/core-DFgB3yU4.js.map +0 -1
  163. package/dist/demo/rolldown-runtime-CjeV3_4I.js +0 -18
@@ -1 +1 @@
1
- {"version":3,"file":"AdminParameters-CqgvhRsb.js","names":["Flex","Text","Flex","Flex","Text","Flex","Text","Flex","Text","Flex","Text","Flex","Text"],"sources":["../../src/admin/components/parameters/types.ts","../../src/admin/components/parameters/ParameterDetailsConfigForm.tsx","../../src/admin/components/parameters/ParameterDetailsLoading.tsx","../../src/admin/components/parameters/ParameterDetails.tsx","../../src/admin/components/parameters/ParameterEmptyState.tsx","../../src/admin/components/parameters/ParameterHistory.tsx","../../src/admin/components/parameters/ParameterTreeNode.tsx","../../src/admin/components/parameters/ParameterTree.tsx","../../src/admin/components/parameters/AdminParameters.tsx"],"sourcesContent":["import type { ParameterResponse } from \"alepha/api/parameters\";\n\nexport interface ParameterValue {\n current?: ParameterResponse;\n next?: ParameterResponse;\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 } 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\ninterface Props {\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 ParameterDetailsConfigForm = (props: Props) => {\n const { l } = useI18n();\n\n // Get the current value to display (from saved version or default)\n const currentContent = useMemo(() => {\n if (props.configValue?.current?.content) {\n return props.configValue.current.content;\n }\n if (props.configValue?.currentValue !== undefined) {\n return props.configValue.currentValue;\n }\n return null;\n }, [props.configValue]);\n\n // Convert JSON Schema from API to TypeBox schema\n const schemaForForm = useMemo(() => {\n if (!props.configValue?.schema) {\n return t.object({});\n }\n try {\n return jsonSchemaToTypeBox(props.configValue.schema) as TObject;\n } catch {\n return t.object({});\n }\n }, [props.configValue?.schema]);\n\n const form = useForm(\n {\n schema: schemaForForm,\n initialValues: (currentContent ?? {}) as Record<string, unknown>,\n handler: async (values) => {\n await props.onSave(values as Record<string, unknown>);\n },\n },\n [props.selectedConfig, schemaForForm, currentContent],\n );\n\n // Check if we have a valid schema with properties\n const hasValidSchema = useMemo(() => {\n const schema = props.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 }, [props.configValue?.schema]);\n\n // Count the number of fields to determine column layout\n const fieldCount = useMemo(() => {\n const schema = props.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 }, [props.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 {props.configValue?.current?.changeDescription && (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Change Description\n </Text>\n <Text size=\"sm\">\n {props.configValue.current.changeDescription}\n </Text>\n </Flex>\n )}\n\n {props.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(props.configValue.current.updatedAt, {\n date: \"fromNow\",\n })}\n </Text>\n </Flex>\n {props.configValue.current.creatorName && (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated By\n </Text>\n <Text size=\"sm\">\n {props.configValue.current.creatorName}\n </Text>\n </Flex>\n )}\n </Flex>\n )}\n\n {!props.configValue?.current &&\n props.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 {props.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{props.configValue.next.version})\n </Text>\n </Flex>\n <Text size=\"xs\" c=\"dimmed\">\n Activates{\" \"}\n {l(props.configValue.next.activationDate, {\n date: \"fromNow\",\n })}\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }} fz=\"xs\">\n {formatJson(props.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={props.saving}\n >\n Reset\n </ActionButton>\n <ActionButton intent=\"primary\" form={form} loading={props.saving}>\n Save Changes\n </ActionButton>\n </Flex>\n </Flex>\n )}\n </Flex>\n </Flex>\n );\n};\n\nexport default ParameterDetailsConfigForm;\n","import { Flex } from \"@alepha/ui\";\nimport { Loader } from \"@mantine/core\";\n\n/**\n * Loading state for the parameter details panel.\n */\nconst ParameterDetailsLoading = () => (\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\nexport default ParameterDetailsLoading;\n","import ParameterDetailsConfigForm from \"./ParameterDetailsConfigForm.tsx\";\nimport ParameterDetailsLoading from \"./ParameterDetailsLoading.tsx\";\nimport type { ParameterValue } from \"./types.ts\";\n\ninterface Props {\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 * Parameter details panel.\n * Shows loading state or the config form.\n * Note: Empty state is handled by parent (AdminParameters).\n */\nconst ParameterDetails = (props: Props) => {\n // Loading state\n if (props.loading) {\n return <ParameterDetailsLoading />;\n }\n\n // Config form (selectedConfig is guaranteed to be non-null by parent)\n return (\n <ParameterDetailsConfigForm\n selectedConfig={props.selectedConfig!}\n configValue={props.configValue}\n saving={props.saving}\n onSave={props.onSave}\n />\n );\n};\n\nexport default ParameterDetails;\n","import { Flex, Text } from \"@alepha/ui\";\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 { ParameterResponse } from \"alepha/api/parameters\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { getStatusColor } from \"./types.ts\";\n\ninterface Props {\n selectedConfig: string | null;\n history: ParameterResponse[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\n/**\n * Parameter version history timeline panel.\n */\nconst ParameterHistory = (props: Props) => {\n const { l } = useI18n();\n\n const renderContent = () => {\n if (props.loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader size=\"sm\" />\n </Flex>\n );\n }\n\n if (props.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={props.history.findIndex((h) => h.status === \"current\")}\n bulletSize={24}\n lineWidth={2}\n >\n {props.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={() => props.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={220}\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 { Flex, Text } from \"@alepha/ui\";\nimport { Collapse, UnstyledButton } from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronRight,\n IconFolder,\n IconFolderOpen,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport type { ParameterTreeNode as ParameterTreeNodeData } from \"alepha/api/parameters\";\nimport { memo, useCallback, useState } from \"react\";\n\ninterface Props {\n node: ParameterTreeNodeData;\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 ParameterTreeNode = memo((props: Props) => {\n const [isHovered, setIsHovered] = useState(false);\n const hasChildren = props.node.children.length > 0;\n const isExpanded = props.expandedNodes.has(props.node.path);\n const isSelected = props.selectedConfig === props.node.path;\n const isLeaf = !hasChildren;\n\n const handleClick = useCallback(() => {\n if (hasChildren) {\n props.onToggle(props.node.path);\n } else {\n props.onSelect(props.node.path);\n }\n }, [hasChildren, props.node.path, props.onToggle, props.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 + props.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 {props.node.name}\n </Text>\n </Flex>\n </UnstyledButton>\n\n {hasChildren && (\n <Collapse in={isExpanded}>\n {props.node.children.map((child: ParameterTreeNodeData) => (\n <ParameterTreeNode\n key={child.path}\n node={child}\n level={props.level + 1}\n selectedConfig={props.selectedConfig}\n onSelect={props.onSelect}\n expandedNodes={props.expandedNodes}\n onToggle={props.onToggle}\n />\n ))}\n </Collapse>\n )}\n </Flex>\n );\n});\n\nParameterTreeNode.displayName = \"ParameterTreeNode\";\n\nexport default ParameterTreeNode;\n","import { ActionButton, Flex, Text } from \"@alepha/ui\";\nimport { ScrollArea, TextInput } from \"@mantine/core\";\nimport { IconRefresh, IconSearch } from \"@tabler/icons-react\";\nimport type { ParameterTreeNode as ParameterTreeNodeData } from \"alepha/api/parameters\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport ParameterTreeNode from \"./ParameterTreeNode.tsx\";\n\n/**\n * Filters tree nodes by search query.\n */\nconst filterTree = (\n nodes: ParameterTreeNodeData[],\n query: string,\n): ParameterTreeNodeData[] => {\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 ParameterTreeNodeData => node !== null);\n};\n\n/**\n * Collects all folder paths to expand by default.\n */\nconst collectAllFolderPaths = (nodes: ParameterTreeNodeData[]): Set<string> => {\n const paths = new Set<string>();\n\n const traverse = (nodeList: ParameterTreeNodeData[]) => {\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\ninterface Props {\n treeData: ParameterTreeNodeData[];\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\n/**\n * Parameter tree sidebar with search and refresh.\n */\nconst ParameterTree = (props: Props) => {\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() =>\n collectAllFolderPaths(props.treeData),\n );\n\n // Filter tree by search query\n const filteredTreeData = useMemo(\n () => filterTree(props.treeData, searchQuery),\n [props.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={props.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 <ParameterTreeNode\n key={node.path}\n node={node}\n level={0}\n selectedConfig={props.selectedConfig}\n onSelect={props.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 { Flex, Text, useToast } from \"@alepha/ui\";\nimport { Card } from \"@mantine/core\";\nimport { IconSettings } from \"@tabler/icons-react\";\nimport type {\n AdminParameterController,\n ParameterResponse,\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<ParameterResponse[]>([]);\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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"}
1
+ {"version":3,"file":"AdminParameters-CyZQSXnN.js","names":["Flex","Text","Flex","Flex","Text","Flex","Text","Flex","Text","Flex","Text","Flex","Text"],"sources":["../../src/admin/components/parameters/types.ts","../../src/admin/components/parameters/ParameterDetailsConfigForm.tsx","../../src/admin/components/parameters/ParameterDetailsLoading.tsx","../../src/admin/components/parameters/ParameterDetails.tsx","../../src/admin/components/parameters/ParameterEmptyState.tsx","../../src/admin/components/parameters/ParameterHistory.tsx","../../src/admin/components/parameters/ParameterTreeNode.tsx","../../src/admin/components/parameters/ParameterTree.tsx","../../src/admin/components/parameters/AdminParameters.tsx"],"sourcesContent":["import type { ParameterResponse } from \"alepha/api/parameters\";\n\nexport interface ParameterValue {\n current?: ParameterResponse;\n next?: ParameterResponse;\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 } 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\ninterface Props {\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 ParameterDetailsConfigForm = (props: Props) => {\n const { l } = useI18n();\n\n // Get the current value to display (from saved version or default)\n const currentContent = useMemo(() => {\n if (props.configValue?.current?.content) {\n return props.configValue.current.content;\n }\n if (props.configValue?.currentValue !== undefined) {\n return props.configValue.currentValue;\n }\n return null;\n }, [props.configValue]);\n\n // Convert JSON Schema from API to TypeBox schema\n const schemaForForm = useMemo(() => {\n if (!props.configValue?.schema) {\n return t.object({});\n }\n try {\n return jsonSchemaToTypeBox(props.configValue.schema) as TObject;\n } catch {\n return t.object({});\n }\n }, [props.configValue?.schema]);\n\n const form = useForm(\n {\n schema: schemaForForm,\n initialValues: (currentContent ?? {}) as Record<string, unknown>,\n handler: async (values) => {\n await props.onSave(values as Record<string, unknown>);\n },\n },\n [props.selectedConfig, schemaForForm, currentContent],\n );\n\n // Check if we have a valid schema with properties\n const hasValidSchema = useMemo(() => {\n const schema = props.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 }, [props.configValue?.schema]);\n\n // Count the number of fields to determine column layout\n const fieldCount = useMemo(() => {\n const schema = props.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 }, [props.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 {props.configValue?.current?.changeDescription && (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Change Description\n </Text>\n <Text size=\"sm\">\n {props.configValue.current.changeDescription}\n </Text>\n </Flex>\n )}\n\n {props.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(props.configValue.current.updatedAt, {\n date: \"fromNow\",\n })}\n </Text>\n </Flex>\n {props.configValue.current.creatorName && (\n <Flex>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated By\n </Text>\n <Text size=\"sm\">\n {props.configValue.current.creatorName}\n </Text>\n </Flex>\n )}\n </Flex>\n )}\n\n {!props.configValue?.current &&\n props.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 {props.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{props.configValue.next.version})\n </Text>\n </Flex>\n <Text size=\"xs\" c=\"dimmed\">\n Activates{\" \"}\n {l(props.configValue.next.activationDate, {\n date: \"fromNow\",\n })}\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }} fz=\"xs\">\n {formatJson(props.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={props.saving}\n >\n Reset\n </ActionButton>\n <ActionButton intent=\"primary\" form={form} loading={props.saving}>\n Save Changes\n </ActionButton>\n </Flex>\n </Flex>\n )}\n </Flex>\n </Flex>\n );\n};\n\nexport default ParameterDetailsConfigForm;\n","import { Flex } from \"@alepha/ui\";\nimport { Loader } from \"@mantine/core\";\n\n/**\n * Loading state for the parameter details panel.\n */\nconst ParameterDetailsLoading = () => (\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\nexport default ParameterDetailsLoading;\n","import ParameterDetailsConfigForm from \"./ParameterDetailsConfigForm.tsx\";\nimport ParameterDetailsLoading from \"./ParameterDetailsLoading.tsx\";\nimport type { ParameterValue } from \"./types.ts\";\n\ninterface Props {\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 * Parameter details panel.\n * Shows loading state or the config form.\n * Note: Empty state is handled by parent (AdminParameters).\n */\nconst ParameterDetails = (props: Props) => {\n // Loading state\n if (props.loading) {\n return <ParameterDetailsLoading />;\n }\n\n // Config form (selectedConfig is guaranteed to be non-null by parent)\n return (\n <ParameterDetailsConfigForm\n selectedConfig={props.selectedConfig!}\n configValue={props.configValue}\n saving={props.saving}\n onSave={props.onSave}\n />\n );\n};\n\nexport default ParameterDetails;\n","import { Flex, Text } from \"@alepha/ui\";\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 { ParameterResponse } from \"alepha/api/parameters\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { getStatusColor } from \"./types.ts\";\n\ninterface Props {\n selectedConfig: string | null;\n history: ParameterResponse[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\n/**\n * Parameter version history timeline panel.\n */\nconst ParameterHistory = (props: Props) => {\n const { l } = useI18n();\n\n const renderContent = () => {\n if (props.loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader size=\"sm\" />\n </Flex>\n );\n }\n\n if (props.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={props.history.findIndex((h) => h.status === \"current\")}\n bulletSize={24}\n lineWidth={2}\n >\n {props.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={() => props.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={220}\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 { Flex, Text } from \"@alepha/ui\";\nimport { Collapse, UnstyledButton } from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronRight,\n IconFolder,\n IconFolderOpen,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport type { ParameterTreeNode as ParameterTreeNodeData } from \"alepha/api/parameters\";\nimport { memo, useCallback, useState } from \"react\";\n\ninterface Props {\n node: ParameterTreeNodeData;\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 ParameterTreeNode = memo((props: Props) => {\n const [isHovered, setIsHovered] = useState(false);\n const hasChildren = props.node.children.length > 0;\n const isExpanded = props.expandedNodes.has(props.node.path);\n const isSelected = props.selectedConfig === props.node.path;\n const isLeaf = !hasChildren;\n\n const handleClick = useCallback(() => {\n if (hasChildren) {\n props.onToggle(props.node.path);\n } else {\n props.onSelect(props.node.path);\n }\n }, [hasChildren, props.node.path, props.onToggle, props.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 + props.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 {props.node.name}\n </Text>\n </Flex>\n </UnstyledButton>\n\n {hasChildren && (\n <Collapse in={isExpanded}>\n {props.node.children.map((child: ParameterTreeNodeData) => (\n <ParameterTreeNode\n key={child.path}\n node={child}\n level={props.level + 1}\n selectedConfig={props.selectedConfig}\n onSelect={props.onSelect}\n expandedNodes={props.expandedNodes}\n onToggle={props.onToggle}\n />\n ))}\n </Collapse>\n )}\n </Flex>\n );\n});\n\nParameterTreeNode.displayName = \"ParameterTreeNode\";\n\nexport default ParameterTreeNode;\n","import { ActionButton, Flex, Text } from \"@alepha/ui\";\nimport { ScrollArea, TextInput } from \"@mantine/core\";\nimport { IconRefresh, IconSearch } from \"@tabler/icons-react\";\nimport type { ParameterTreeNode as ParameterTreeNodeData } from \"alepha/api/parameters\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport ParameterTreeNode from \"./ParameterTreeNode.tsx\";\n\n/**\n * Filters tree nodes by search query.\n */\nconst filterTree = (\n nodes: ParameterTreeNodeData[],\n query: string,\n): ParameterTreeNodeData[] => {\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 ParameterTreeNodeData => node !== null);\n};\n\n/**\n * Collects all folder paths to expand by default.\n */\nconst collectAllFolderPaths = (nodes: ParameterTreeNodeData[]): Set<string> => {\n const paths = new Set<string>();\n\n const traverse = (nodeList: ParameterTreeNodeData[]) => {\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\ninterface Props {\n treeData: ParameterTreeNodeData[];\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\n/**\n * Parameter tree sidebar with search and refresh.\n */\nconst ParameterTree = (props: Props) => {\n const [searchQuery, setSearchQuery] = useState(\"\");\n const [expandedNodes, setExpandedNodes] = useState<Set<string>>(() =>\n collectAllFolderPaths(props.treeData),\n );\n\n // Filter tree by search query\n const filteredTreeData = useMemo(\n () => filterTree(props.treeData, searchQuery),\n [props.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={props.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 <ParameterTreeNode\n key={node.path}\n node={node}\n level={0}\n selectedConfig={props.selectedConfig}\n onSelect={props.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 { Flex, Text, useToast } from \"@alepha/ui\";\nimport { Card } from \"@mantine/core\";\nimport { IconSettings } from \"@tabler/icons-react\";\nimport type {\n AdminParameterController,\n ParameterResponse,\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<ParameterResponse[]>([]);\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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"}
@@ -1,4 +1,4 @@
1
- import { _ as ActionButton, b as useToast, l as Flex$1, m as useDialog, r as DataTable, s as Text$1 } from "./core-CYaRQ8O-.js";
1
+ import { _ as ActionButton, b as useToast, l as Flex$1, m as useDialog, r as DataTable, s as Text$1 } from "./core-D1AbU50V.js";
2
2
  import { t } from "alepha";
3
3
  import { useI18n } from "alepha/react/i18n";
4
4
  import { Badge } from "@mantine/core";
@@ -8,7 +8,6 @@ import { IconDeviceDesktop, IconDeviceMobile, IconDeviceTablet, IconTrash } from
8
8
  import { useRouter } from "alepha/react/router";
9
9
  import { useClient } from "alepha/react";
10
10
  import { sessions } from "alepha/api/users";
11
-
12
11
  //#region ../../src/admin/components/sessions/AdminSessions.tsx
13
12
  const filters = t.object({ userId: t.optional(t.uuid({ $control: { query: t.pick(sessions.schema, ["userId"]) } })) });
14
13
  const getDeviceIcon = (device) => {
@@ -132,7 +131,7 @@ const AdminSessions = (props) => {
132
131
  }, refreshKey)
133
132
  });
134
133
  };
135
-
136
134
  //#endregion
137
135
  export { AdminSessions as default };
138
- //# sourceMappingURL=AdminSessions-Bz5NRuoW.js.map
136
+
137
+ //# sourceMappingURL=AdminSessions--xwELDSO.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSessions-Bz5NRuoW.js","names":["Flex","Text"],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import {\n ActionButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} 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.tsx\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n});\n\nconst 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\nconst isExpired = (expiresAt: Date | string) =>\n new Date(expiresAt) < new Date();\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const dialog = useDialog();\n const toast = useToast();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const handleDelete = async (session: SessionEntity) => {\n const confirmed = await dialog.confirm({\n title: \"Revoke session\",\n message:\n \"Are you sure you want to revoke this session? The user will be signed out.\",\n });\n if (!confirmed) return;\n await client.deleteSession({\n params: { id: session.id },\n query: { userRealmName: props.userRealmName },\n });\n toast.success(\"Session revoked\");\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex p=\"md\" 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) => form.submit()}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n opacity: isExpired(item.expiresAt) ? 0.5 : 1,\n },\n })}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\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(\"adminUserProfile\", {\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 value: (item) => (\n <Flex gap={4} align=\"center\">\n {item.userAgent ? (\n <>\n {getDeviceIcon(item.userAgent.device)}\n <Text size=\"xs\">\n {item.userAgent.browser} / {item.userAgent.os}\n </Text>\n </>\n ) : (\n <Text size=\"xs\" muted>\n —\n </Text>\n )}\n </Flex>\n ),\n },\n ip: {\n label: \"IP\",\n value: (item) => (\n <Text size=\"xs\" ff=\"monospace\" muted>\n {item.ip || \"—\"}\n </Text>\n ),\n },\n expiresAt: {\n label: \"Status\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={isExpired(item.expiresAt) ? \"gray\" : \"green\"}\n >\n {isExpired(item.expiresAt) ? \"Expired\" : \"Active\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Created\",\n value: (item) => (\n <Text size=\"xs\" muted>\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n rowActions={(item) => [\n {\n label: \"Revoke session\",\n icon: IconTrash,\n color: \"red\",\n onClick: () => handleDelete(item),\n visible: !isExpired(item.expiresAt),\n },\n ]}\n />\n </Flex>\n );\n};\n\nexport default AdminSessions;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminSessions--xwELDSO.js","names":["Flex","Text"],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import {\n ActionButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} 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.tsx\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n});\n\nconst 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\nconst isExpired = (expiresAt: Date | string) =>\n new Date(expiresAt) < new Date();\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const dialog = useDialog();\n const toast = useToast();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const handleDelete = async (session: SessionEntity) => {\n const confirmed = await dialog.confirm({\n title: \"Revoke session\",\n message:\n \"Are you sure you want to revoke this session? The user will be signed out.\",\n });\n if (!confirmed) return;\n await client.deleteSession({\n params: { id: session.id },\n query: { userRealmName: props.userRealmName },\n });\n toast.success(\"Session revoked\");\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex p=\"md\" 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) => form.submit()}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n opacity: isExpired(item.expiresAt) ? 0.5 : 1,\n },\n })}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\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(\"adminUserProfile\", {\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 value: (item) => (\n <Flex gap={4} align=\"center\">\n {item.userAgent ? (\n <>\n {getDeviceIcon(item.userAgent.device)}\n <Text size=\"xs\">\n {item.userAgent.browser} / {item.userAgent.os}\n </Text>\n </>\n ) : (\n <Text size=\"xs\" muted>\n —\n </Text>\n )}\n </Flex>\n ),\n },\n ip: {\n label: \"IP\",\n value: (item) => (\n <Text size=\"xs\" ff=\"monospace\" muted>\n {item.ip || \"—\"}\n </Text>\n ),\n },\n expiresAt: {\n label: \"Status\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={isExpired(item.expiresAt) ? \"gray\" : \"green\"}\n >\n {isExpired(item.expiresAt) ? \"Expired\" : \"Active\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Created\",\n value: (item) => (\n <Text size=\"xs\" muted>\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n rowActions={(item) => [\n {\n label: \"Revoke session\",\n icon: IconTrash,\n color: \"red\",\n onClick: () => handleDelete(item),\n visible: !isExpired(item.expiresAt),\n },\n ]}\n />\n </Flex>\n );\n};\n\nexport default AdminSessions;\n"],"mappings":";;;;;;;;;;;AA+BA,MAAM,UAAU,EAAE,OAAO,EACvB,QAAQ,EAAE,SACR,EAAE,KAAK,EACL,UAAU,EACR,OAAO,EAAE,KAAK,SAAS,QAAQ,CAAC,SAAS,CAAC,EAC3C,EACF,CAAC,CACH,EACF,CAAC;AAEF,MAAM,iBAAiB,WAAoB;AACzC,SAAQ,QAAR;EACE,KAAK,SACH,QAAO,oBAAC,kBAAD,EAAkB,MAAM,IAAM,CAAA;EACvC,KAAK,SACH,QAAO,oBAAC,kBAAD,EAAkB,MAAM,IAAM,CAAA;EACvC,QACE,QAAO,oBAAC,mBAAD,EAAmB,MAAM,IAAM,CAAA;;;AAI5C,MAAM,aAAa,cACjB,IAAI,KAAK,UAAU,mBAAG,IAAI,MAAM;AAElC,MAAM,iBAAiB,UAA8B;CACnD,MAAM,SAAS,WAAmC;CAClD,MAAM,SAAS,WAAwB;CACvC,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,SAAS,WAAW;CAC1B,MAAM,QAAQ,UAAU;CACxB,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAE/C,MAAM,eAAe,OAAO,YAA2B;AAMrD,MAAI,CALc,MAAM,OAAO,QAAQ;GACrC,OAAO;GACP,SACE;GACH,CAAC,CACc;AAChB,QAAM,OAAO,cAAc;GACzB,QAAQ,EAAE,IAAI,QAAQ,IAAI;GAC1B,OAAO,EAAE,eAAe,MAAM,eAAe;GAC9C,CAAC;AACF,QAAM,QAAQ,kBAAkB;AAChC,iBAAe,MAAM,IAAI,EAAE;;AAG7B,QACE,oBAACA,QAAD;EAAM,GAAE;EAAK,MAAM;EAAG,WAAU;YAC9B,oBAAC,WAAD;GAEE,cAAA;GACA,aAAa;GACb,eAAe;IACb,kBAAkB;IAClB,SAAS;IACV;GACD,YAAY;IACV,mBAAmB;IACnB,iBAAiB;IAClB;GACD,iBAAiB,MAAM,QAAQ,SAAS,KAAK,QAAQ;GAC5C;GACT,eAAe,UAAU,EACvB,OAAO,EACL,SAAS,UAAU,KAAK,UAAU,GAAG,KAAM,GAC5C,EACF;GACD,OAAO,OAAO,YAAY;AAOxB,WANiB,MAAM,OAAO,aAAa,EACzC,OAAO;KACL,GAAG;KACH,eAAe,MAAM;KACtB,EACF,CAAC;;GAGJ,SAAS;IACP,QAAQ;KACN,OAAO;KACP,QAAQ,SACN,oBAAC,cAAD;MACE,SAAQ;MACR,MAAK;MACL,MAAM,OAAO,KAAK,oBAAoB,EACpC,QAAQ,EAAE,QAAQ,KAAK,QAAQ,EAChC,CAAC;gBAEF,qBAACC,QAAD;OAAM,MAAK;OAAK,IAAG;iBAAnB,CACG,KAAK,OAAO,MAAM,GAAG,EAAE,EAAC,MACpB;;MACM,CAAA;KAElB;IACD,WAAW;KACT,OAAO;KACP,QAAQ,SACN,oBAACD,QAAD;MAAM,KAAK;MAAG,OAAM;gBACjB,KAAK,YACJ,qBAAA,UAAA,EAAA,UAAA,CACG,cAAc,KAAK,UAAU,OAAO,EACrC,qBAACC,QAAD;OAAM,MAAK;iBAAX;QACG,KAAK,UAAU;QAAQ;QAAI,KAAK,UAAU;QACtC;SACN,EAAA,CAAA,GAEH,oBAACA,QAAD;OAAM,MAAK;OAAK,OAAA;iBAAM;OAEf,CAAA;MAEJ,CAAA;KAEV;IACD,IAAI;KACF,OAAO;KACP,QAAQ,SACN,oBAACA,QAAD;MAAM,MAAK;MAAK,IAAG;MAAY,OAAA;gBAC5B,KAAK,MAAM;MACP,CAAA;KAEV;IACD,WAAW;KACT,OAAO;KACP,QAAQ,SACN,oBAAC,OAAD;MACE,MAAK;MACL,SAAQ;MACR,OAAO,UAAU,KAAK,UAAU,GAAG,SAAS;gBAE3C,UAAU,KAAK,UAAU,GAAG,YAAY;MACnC,CAAA;KAEX;IACD,WAAW;KACT,OAAO;KACP,QAAQ,SACN,oBAACA,QAAD;MAAM,MAAK;MAAK,OAAA;gBACb,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;MAClC,CAAA;KAEV;IACF;GACD,aAAa,SAAS,CACpB;IACE,OAAO;IACP,MAAM;IACN,OAAO;IACP,eAAe,aAAa,KAAK;IACjC,SAAS,CAAC,UAAU,KAAK,UAAU;IACpC,CACF;GACD,EArGK,WAqGL;EACG,CAAA"}
@@ -1,12 +1,11 @@
1
- import { _ as ActionButton, b as useToast, l as Flex$1, m as useDialog, s as Text$1 } from "./core-CYaRQ8O-.js";
2
- import { t } from "alepha";
3
- import { ActionIcon, Avatar, Badge, Button, Flex, Loader, Menu, Tabs, Text, Tooltip } from "@mantine/core";
1
+ import { _ as ActionButton, b as useToast, l as Flex$1, m as useDialog } from "./core-D1AbU50V.js";
2
+ import { t as adminUserAtom } from "./adminUserAtom-DCi4wf-v.js";
3
+ import { ActionIcon, Avatar, Badge, Button, Flex, Menu, Tabs, Text, Tooltip } from "@mantine/core";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
- import { createContext, useCallback, useContext, useEffect, useState } from "react";
5
+ import { useCallback, useEffect } from "react";
6
6
  import { IconBan, IconChevronDown, IconChevronLeft, IconExternalLink, IconShieldCheck, IconTrash } from "@tabler/icons-react";
7
- import { NestedView, useActive, useRouter, useRouterState } from "alepha/react/router";
8
- import { useClient } from "alepha/react";
9
-
7
+ import { NestedView, useActive, useRouter } from "alepha/react/router";
8
+ import { useAlepha, useClient, useStore } from "alepha/react";
10
9
  //#region ../../src/admin/components/shared/AdminResourceHeaderMenuItem.tsx
11
10
  const AdminResourceHeaderMenuItem = (props) => {
12
11
  const { action } = props;
@@ -22,7 +21,6 @@ const AdminResourceHeaderMenuItem = (props) => {
22
21
  children: action.label
23
22
  });
24
23
  };
25
-
26
24
  //#endregion
27
25
  //#region ../../src/admin/components/shared/AdminResourceHeader.tsx
28
26
  const AdminResourceHeader = (props) => {
@@ -133,7 +131,6 @@ const AdminResourceHeader = (props) => {
133
131
  })]
134
132
  });
135
133
  };
136
-
137
134
  //#endregion
138
135
  //#region ../../src/admin/components/shared/AdminResourceTabsItem.tsx
139
136
  const AdminResourceTabsItem = (props) => {
@@ -152,7 +149,6 @@ const AdminResourceTabsItem = (props) => {
152
149
  children: [tab.label, tab.count !== void 0 && tab.count > 0 && ` (${tab.count})`]
153
150
  });
154
151
  };
155
-
156
152
  //#endregion
157
153
  //#region ../../src/admin/components/shared/AdminResourceTabs.tsx
158
154
  const AdminResourceTabs = (props) => {
@@ -163,140 +159,105 @@ const AdminResourceTabs = (props) => {
163
159
  children: [/* @__PURE__ */ jsx(Tabs.List, { children: tabs.map((tab) => /* @__PURE__ */ jsx(AdminResourceTabsItem, { tab }, tab.value)) }), children]
164
160
  });
165
161
  };
166
-
167
162
  //#endregion
168
163
  //#region ../../src/admin/components/users/AdminUserLayout.tsx
169
- const UserContext = createContext(null);
170
- const useUser = () => {
171
- const ctx = useContext(UserContext);
172
- if (!ctx) throw new Error("useUser must be used within AdminUserLayout");
173
- return ctx;
174
- };
175
- t.object({
176
- email: t.optional(t.email()),
177
- phoneNumber: t.optional(t.e164()),
178
- firstName: t.optional(t.string()),
179
- lastName: t.optional(t.string()),
180
- roles: t.optional(t.array(t.string())),
181
- enabled: t.optional(t.boolean())
182
- });
183
164
  const displayName = (u) => u.firstName || u.lastName ? `${u.firstName ?? ""} ${u.lastName ?? ""}`.trim() : u.username || u.email || "User";
184
165
  const AdminUserLayout = (props) => {
185
166
  const router = useRouter();
186
- const state = useRouterState();
187
167
  const client = useClient();
168
+ const alepha = useAlepha();
188
169
  const dialog = useDialog();
189
170
  const toast = useToast();
190
- const userId = state.params.userId;
191
- const [user, setUser] = useState(null);
192
- const [loading, setLoading] = useState(true);
171
+ const [user] = useStore(adminUserAtom);
172
+ useEffect(() => {
173
+ alepha.store.set(adminUserAtom, props.user);
174
+ }, [props.user]);
175
+ const currentUser = user ?? props.user;
176
+ const userId = currentUser.id;
193
177
  const realmQuery = { userRealmName: props.userRealmName };
194
- const loadUser = useCallback(async () => {
195
- setLoading(true);
196
- try {
197
- setUser(await client.getUser({
198
- params: { id: userId },
199
- query: realmQuery
200
- }));
201
- } finally {
202
- setLoading(false);
203
- }
178
+ useCallback(async () => {
179
+ const data = await client.getUser({
180
+ params: { id: userId },
181
+ query: realmQuery
182
+ });
183
+ alepha.store.set(adminUserAtom, data);
204
184
  }, [userId, client]);
205
- useEffect(() => {
206
- loadUser();
207
- }, [loadUser]);
208
185
  const handleToggleEnabled = async () => {
209
- if (!user) return;
210
- const action = user.enabled ? "disable" : "enable";
186
+ const action = currentUser.enabled ? "disable" : "enable";
211
187
  if (await dialog.confirm({
212
- title: `${user.enabled ? "Disable" : "Enable"} User`,
213
- message: `Are you sure you want to ${action} ${displayName(user)}?`
188
+ title: `${currentUser.enabled ? "Disable" : "Enable"} User`,
189
+ message: `Are you sure you want to ${action} ${displayName(currentUser)}?`
214
190
  })) {
215
- setUser(await client.updateUser({
216
- params: { id: user.id },
191
+ const updated = await client.updateUser({
192
+ params: { id: currentUser.id },
217
193
  query: realmQuery,
218
- body: { enabled: !user.enabled }
219
- }));
194
+ body: { enabled: !currentUser.enabled }
195
+ });
196
+ alepha.store.set(adminUserAtom, updated);
220
197
  toast.success({ title: `User ${action}d` });
221
198
  }
222
199
  };
223
200
  const handleDelete = async () => {
224
- if (!user) return;
225
201
  if (await dialog.confirm({
226
202
  title: "Delete User",
227
- message: `Are you sure you want to delete ${displayName(user)}? This cannot be undone.`,
203
+ message: `Are you sure you want to delete ${displayName(currentUser)}? This cannot be undone.`,
228
204
  confirmColor: "red",
229
205
  confirmLabel: "Delete"
230
206
  })) {
231
207
  await client.deleteUser({
232
- params: { id: user.id },
208
+ params: { id: currentUser.id },
233
209
  query: realmQuery
234
210
  });
235
211
  toast.success({ title: "User deleted" });
236
212
  router.push("adminUsers");
237
213
  }
238
214
  };
239
- if (loading) return /* @__PURE__ */ jsx(Flex$1, {
215
+ return /* @__PURE__ */ jsxs(Flex$1, {
240
216
  flex: 1,
241
- justify: "center",
242
- align: "center",
243
- children: /* @__PURE__ */ jsx(Loader, {})
244
- });
245
- if (!user) return /* @__PURE__ */ jsx(Flex$1, {
246
- flex: 1,
247
- justify: "center",
248
- align: "center",
249
- children: /* @__PURE__ */ jsx(Text$1, {
250
- c: "dimmed",
251
- children: "User not found"
252
- })
253
- });
254
- return /* @__PURE__ */ jsx(UserContext.Provider, {
255
- value: {
256
- user,
257
- reload: loadUser
217
+ direction: "column",
218
+ gap: "lg",
219
+ p: "md",
220
+ m: "md",
221
+ style: {
222
+ backgroundColor: "var(--mantine-color-body)",
223
+ borderRadius: "var(--mantine-radius-md)",
224
+ border: "1px solid var(--mantine-color-default-border)"
258
225
  },
259
- children: /* @__PURE__ */ jsxs(Flex$1, {
260
- flex: 1,
261
- direction: "column",
262
- gap: "lg",
263
- p: "md",
264
- children: [
265
- /* @__PURE__ */ jsx(AdminResourceHeader, {
266
- backHref: router.path("adminUsers"),
267
- backLabel: "Users",
268
- title: displayName(user),
269
- subtitle: user.email || user.username,
270
- status: {
271
- label: user.enabled ? "Active" : "Disabled",
272
- color: user.enabled ? "green" : "gray"
273
- },
274
- menuActions: [{
275
- label: user.enabled ? "Disable" : "Enable",
276
- icon: user.enabled ? IconBan : IconShieldCheck,
277
- onClick: handleToggleEnabled
278
- }, {
279
- label: "Delete",
280
- icon: IconTrash,
281
- color: "red",
282
- onClick: handleDelete
283
- }]
284
- }),
285
- /* @__PURE__ */ jsx(AdminResourceTabs, { tabs: [{
286
- value: "profile",
287
- label: "Profile",
288
- href: router.path("adminUserProfile", { params: { userId } })
226
+ children: [
227
+ /* @__PURE__ */ jsx(AdminResourceHeader, {
228
+ backHref: router.path("adminUsers"),
229
+ backLabel: "Users",
230
+ title: displayName(currentUser),
231
+ subtitle: currentUser.email || currentUser.username,
232
+ status: {
233
+ label: currentUser.enabled ? "Active" : "Disabled",
234
+ color: currentUser.enabled ? "green" : "gray"
235
+ },
236
+ menuActions: [{
237
+ label: currentUser.enabled ? "Disable" : "Enable",
238
+ icon: currentUser.enabled ? IconBan : IconShieldCheck,
239
+ onClick: handleToggleEnabled
289
240
  }, {
290
- value: "sessions",
291
- label: "Sessions",
292
- href: router.path("adminUserSessions", { params: { userId } })
293
- }] }),
294
- /* @__PURE__ */ jsx(NestedView, {})
295
- ]
296
- })
241
+ label: "Delete",
242
+ icon: IconTrash,
243
+ color: "red",
244
+ onClick: handleDelete
245
+ }]
246
+ }),
247
+ /* @__PURE__ */ jsx(AdminResourceTabs, { tabs: [{
248
+ value: "profile",
249
+ label: "Profile",
250
+ href: router.path("adminUserProfile", { params: { userId } })
251
+ }, {
252
+ value: "sessions",
253
+ label: "Sessions",
254
+ href: router.path("adminUserSessions", { params: { userId } })
255
+ }] }),
256
+ /* @__PURE__ */ jsx(NestedView, {})
257
+ ]
297
258
  });
298
259
  };
299
-
300
260
  //#endregion
301
- export { AdminUserLayout as default, useUser };
302
- //# sourceMappingURL=AdminUserLayout-lXT6I0Qq.js.map
261
+ export { AdminUserLayout as default };
262
+
263
+ //# sourceMappingURL=AdminUserLayout-DvBTG5gd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminUserLayout-DvBTG5gd.js","names":["Flex"],"sources":["../../src/admin/components/shared/AdminResourceHeaderMenuItem.tsx","../../src/admin/components/shared/AdminResourceHeader.tsx","../../src/admin/components/shared/AdminResourceTabsItem.tsx","../../src/admin/components/shared/AdminResourceTabs.tsx","../../src/admin/components/users/AdminUserLayout.tsx"],"sourcesContent":["import { Menu } from \"@mantine/core\";\nimport { useRouter } from \"alepha/react/router\";\nimport type { AdminResourceAction } from \"./AdminResourceHeader.tsx\";\n\nexport interface AdminResourceHeaderMenuItemProps {\n /**\n * Action configuration\n */\n action: AdminResourceAction;\n}\n\nconst AdminResourceHeaderMenuItem = (\n props: AdminResourceHeaderMenuItemProps,\n) => {\n const { action } = props;\n const router = useRouter();\n\n const menuItemProps: Record<string, unknown> = {};\n if (action.href) {\n Object.assign(menuItemProps, router.anchor(action.href));\n } else if (action.onClick) {\n menuItemProps.onClick = action.onClick;\n }\n\n return (\n <Menu.Item\n leftSection={action.icon ? <action.icon size={16} /> : undefined}\n color={action.color}\n disabled={action.disabled}\n {...menuItemProps}\n >\n {action.label}\n </Menu.Item>\n );\n};\n\nexport default AdminResourceHeaderMenuItem;\n","import { ActionButton } from \"@alepha/ui\";\nimport {\n ActionIcon,\n Avatar,\n Badge,\n Button,\n Flex,\n Menu,\n Text,\n Tooltip,\n} from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronLeft,\n IconExternalLink,\n} from \"@tabler/icons-react\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport AdminResourceHeaderMenuItem from \"./AdminResourceHeaderMenuItem.tsx\";\n\nexport interface AdminResourceAction {\n label: string;\n icon?: ComponentType<{ size?: number }>;\n onClick?: () => void;\n href?: string;\n color?: string;\n disabled?: boolean;\n loading?: boolean;\n variant?: \"filled\" | \"light\" | \"outline\" | \"subtle\";\n}\n\nexport interface AdminResourceHeaderProps {\n /**\n * Back navigation URL\n */\n backHref?: string;\n\n /**\n * Back navigation label\n */\n backLabel?: string;\n\n /**\n * Avatar content (letter, image URL, or custom node)\n */\n avatar?: string | ReactNode;\n\n /**\n * Avatar color\n */\n avatarColor?: string;\n\n /**\n * Resource title (e.g., user name)\n */\n title: string;\n\n /**\n * Secondary text (e.g., email)\n */\n subtitle?: string;\n\n /**\n * Tertiary identifier to copy (e.g., user ID)\n */\n identifier?: string;\n\n /**\n * Label for the identifier tooltip\n */\n identifierLabel?: string;\n\n /**\n * Status badge\n */\n status?: {\n label: string;\n color: \"green\" | \"red\" | \"yellow\" | \"blue\" | \"gray\";\n };\n\n /**\n * Additional badges (e.g., roles)\n */\n badges?: Array<{\n label: string;\n color?: string;\n variant?: \"filled\" | \"light\" | \"outline\" | \"dot\";\n }>;\n\n /**\n * Primary action button\n */\n primaryAction?: AdminResourceAction;\n\n /**\n * Menu actions (shown in dropdown)\n */\n menuActions?: AdminResourceAction[];\n\n /**\n * External link URL\n */\n externalUrl?: string;\n\n /**\n * Loading state\n */\n loading?: boolean;\n}\n\nconst AdminResourceHeader = (props: AdminResourceHeaderProps) => {\n const {\n backHref,\n backLabel = \"Back\",\n avatar,\n avatarColor = \"blue\",\n title,\n subtitle,\n identifier,\n identifierLabel = \"ID\",\n status,\n badges = [],\n primaryAction,\n menuActions = [],\n externalUrl,\n } = props;\n\n const renderAvatar = () => {\n if (typeof avatar === \"string\") {\n if (avatar.startsWith(\"http\") || avatar.startsWith(\"/\")) {\n return (\n <Avatar src={avatar} size={56} radius=\"md\" color={avatarColor} />\n );\n }\n return (\n <Avatar size={56} radius=\"md\" color={avatarColor}>\n {avatar}\n </Avatar>\n );\n }\n if (avatar) {\n return avatar;\n }\n return (\n <Avatar size={56} radius=\"md\" color={avatarColor}>\n {title.charAt(0).toUpperCase()}\n </Avatar>\n );\n };\n\n return (\n <Flex direction=\"column\" gap=\"xs\">\n {/* Breadcrumb / Back navigation */}\n {backHref && (\n <Flex>\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={backHref}\n leftSection={<IconChevronLeft size={14} />}\n c=\"dimmed\"\n >\n {backLabel}\n </ActionButton>\n </Flex>\n )}\n\n {/* Main header */}\n <Flex justify=\"space-between\" align=\"flex-start\" wrap=\"nowrap\">\n {/* Left: Avatar + Info */}\n <Flex gap=\"md\" wrap=\"nowrap\">\n {renderAvatar()}\n\n <Flex\n direction=\"column\"\n gap={2}\n justify=\"center\"\n style={{ minHeight: 56 }}\n >\n {/* Title row */}\n <Flex gap=\"xs\" align=\"center\">\n <Text size=\"md\" fw={600} lh={1.2}>\n {title}\n </Text>\n {status && (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={status.color}\n tt=\"lowercase\"\n >\n {status.label}\n </Badge>\n )}\n </Flex>\n\n {/* Subtitle */}\n {subtitle && (\n <Text size=\"xs\" c=\"dimmed\">\n {subtitle}\n </Text>\n )}\n </Flex>\n </Flex>\n\n {/* Right: Actions */}\n <Flex gap=\"xs\">\n {externalUrl && (\n <Tooltip label=\"Open in new tab\" openDelay={500}>\n <ActionIcon\n variant=\"subtle\"\n color=\"gray\"\n component=\"a\"\n href={externalUrl}\n target=\"_blank\"\n >\n <IconExternalLink size={18} />\n </ActionIcon>\n </Tooltip>\n )}\n\n {primaryAction && (\n <ActionButton\n variant={primaryAction.variant ?? \"light\"}\n color={primaryAction.color}\n onClick={primaryAction.onClick}\n href={primaryAction.href}\n loading={primaryAction.loading}\n disabled={primaryAction.disabled}\n leftSection={\n primaryAction.icon ? (\n <primaryAction.icon size={16} />\n ) : undefined\n }\n >\n {primaryAction.label}\n </ActionButton>\n )}\n\n {menuActions.length > 0 && (\n <Menu position=\"bottom-end\" shadow=\"md\" width={220}>\n <Menu.Target>\n <Button\n variant=\"default\"\n rightSection={<IconChevronDown size={16} />}\n >\n Actions\n </Button>\n </Menu.Target>\n <Menu.Dropdown>\n {menuActions.map((action, index) => (\n <AdminResourceHeaderMenuItem key={index} action={action} />\n ))}\n </Menu.Dropdown>\n </Menu>\n )}\n </Flex>\n </Flex>\n </Flex>\n );\n};\n\nexport default AdminResourceHeader;\n","import { Tabs } from \"@mantine/core\";\nimport { useActive, useRouter } from \"alepha/react/router\";\nimport type { AdminResourceTab } from \"./AdminResourceTabs.tsx\";\n\nexport interface AdminResourceTabsItemProps {\n /**\n * Tab configuration\n */\n tab: AdminResourceTab;\n}\n\nconst AdminResourceTabsItem = (props: AdminResourceTabsItemProps) => {\n const { tab } = props;\n const router = useRouter();\n const { isActive, isPending } = useActive({ href: tab.href });\n const anchorProps = router.anchor(tab.href);\n\n return (\n <Tabs.Tab\n value={tab.value}\n component=\"a\"\n leftSection={tab.icon ? <tab.icon size={16} /> : undefined}\n disabled={tab.disabled}\n data-active={isActive || undefined}\n style={{\n opacity: isPending ? 0.6 : 1,\n }}\n {...anchorProps}\n >\n {tab.label}\n {tab.count !== undefined && tab.count > 0 && ` (${tab.count})`}\n </Tabs.Tab>\n );\n};\n\nexport default AdminResourceTabsItem;\n","import { Tabs } from \"@mantine/core\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport AdminResourceTabsItem from \"./AdminResourceTabsItem.tsx\";\n\nexport interface AdminResourceTab {\n /**\n * Tab key/value\n */\n value: string;\n\n /**\n * Tab label\n */\n label: string;\n\n /**\n * Tab icon\n */\n icon?: ComponentType<{ size?: number }>;\n\n /**\n * Navigation href\n */\n href: string;\n\n /**\n * Whether tab is disabled\n */\n disabled?: boolean;\n\n /**\n * Badge count to show\n */\n count?: number;\n}\n\nexport interface AdminResourceTabsProps {\n /**\n * Array of tab configurations\n */\n tabs: AdminResourceTab[];\n\n /**\n * Currently active tab value\n */\n activeTab?: string;\n\n /**\n * Content to render below tabs\n */\n children?: ReactNode;\n}\n\nconst AdminResourceTabs = (props: AdminResourceTabsProps) => {\n const { tabs, activeTab, children } = props;\n\n return (\n <Tabs value={activeTab} variant=\"default\">\n <Tabs.List>\n {tabs.map((tab) => (\n <AdminResourceTabsItem key={tab.value} tab={tab} />\n ))}\n </Tabs.List>\n\n {children}\n </Tabs>\n );\n};\n\nexport default AdminResourceTabs;\n","import { Flex, useDialog, useToast } from \"@alepha/ui\";\nimport { IconBan, IconShieldCheck, IconTrash } from \"@tabler/icons-react\";\nimport type { AdminUserController, UserEntity } from \"alepha/api/users\";\nimport { useAlepha, useClient, useStore } from \"alepha/react\";\nimport { NestedView, useRouter } from \"alepha/react/router\";\nimport { useCallback, useEffect } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\nimport { adminUserAtom } from \"../../atoms/adminUserAtom.ts\";\nimport AdminResourceHeader from \"../shared/AdminResourceHeader.tsx\";\nimport AdminResourceTabs from \"../shared/AdminResourceTabs.tsx\";\n\nexport interface AdminUserLayoutProps {\n user: UserEntity;\n userRealmName?: string;\n}\n\nconst displayName = (u: UserEntity) =>\n u.firstName || u.lastName\n ? `${u.firstName ?? \"\"} ${u.lastName ?? \"\"}`.trim()\n : u.username || u.email || \"User\";\n\nconst AdminUserLayout = (props: AdminUserLayoutProps) => {\n const router = useRouter<AdminRouter>();\n const client = useClient<AdminUserController>();\n const alepha = useAlepha();\n const dialog = useDialog();\n const toast = useToast();\n const [user] = useStore(adminUserAtom);\n\n useEffect(() => {\n alepha.store.set(adminUserAtom, props.user);\n }, [props.user]);\n\n const currentUser = user ?? props.user;\n const userId = currentUser.id;\n const realmQuery = { userRealmName: props.userRealmName };\n\n const reload = useCallback(async () => {\n const data = await client.getUser({\n params: { id: userId },\n query: realmQuery,\n });\n alepha.store.set(adminUserAtom, data);\n }, [userId, client]);\n\n const handleToggleEnabled = async () => {\n const action = currentUser.enabled ? \"disable\" : \"enable\";\n const confirmed = await dialog.confirm({\n title: `${currentUser.enabled ? \"Disable\" : \"Enable\"} User`,\n message: `Are you sure you want to ${action} ${displayName(currentUser)}?`,\n });\n if (confirmed) {\n const updated = await client.updateUser({\n params: { id: currentUser.id },\n query: realmQuery,\n body: { enabled: !currentUser.enabled },\n });\n alepha.store.set(adminUserAtom, updated);\n toast.success({ title: `User ${action}d` });\n }\n };\n\n const handleDelete = async () => {\n const confirmed = await dialog.confirm({\n title: \"Delete User\",\n message: `Are you sure you want to delete ${displayName(currentUser)}? This cannot be undone.`,\n confirmColor: \"red\",\n confirmLabel: \"Delete\",\n });\n if (confirmed) {\n await client.deleteUser({\n params: { id: currentUser.id },\n query: realmQuery,\n });\n toast.success({ title: \"User deleted\" });\n router.push(\"adminUsers\");\n }\n };\n\n return (\n <Flex\n flex={1}\n direction=\"column\"\n gap=\"lg\"\n p=\"md\"\n m=\"md\"\n style={{\n backgroundColor: \"var(--mantine-color-body)\",\n borderRadius: \"var(--mantine-radius-md)\",\n border: \"1px solid var(--mantine-color-default-border)\",\n }}\n >\n <AdminResourceHeader\n backHref={router.path(\"adminUsers\")}\n backLabel=\"Users\"\n title={displayName(currentUser)}\n subtitle={currentUser.email || currentUser.username}\n status={{\n label: currentUser.enabled ? \"Active\" : \"Disabled\",\n color: currentUser.enabled ? \"green\" : \"gray\",\n }}\n menuActions={[\n {\n label: currentUser.enabled ? \"Disable\" : \"Enable\",\n icon: currentUser.enabled ? IconBan : IconShieldCheck,\n onClick: handleToggleEnabled,\n },\n {\n label: \"Delete\",\n icon: IconTrash,\n color: \"red\",\n onClick: handleDelete,\n },\n ]}\n />\n\n <AdminResourceTabs\n tabs={[\n {\n value: \"profile\",\n label: \"Profile\",\n href: router.path(\"adminUserProfile\", {\n params: { userId },\n }),\n },\n {\n value: \"sessions\",\n label: \"Sessions\",\n href: router.path(\"adminUserSessions\", {\n params: { userId },\n }),\n },\n ]}\n />\n\n <NestedView />\n </Flex>\n );\n};\n\nexport default AdminUserLayout;\n"],"mappings":"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"}
@@ -1,13 +1,14 @@
1
- import { d as DetailList, l as Flex$1 } from "./core-CYaRQ8O-.js";
2
- import { useUser } from "./AdminUserLayout-lXT6I0Qq.js";
1
+ import { d as DetailList, l as Flex$1 } from "./core-D1AbU50V.js";
2
+ import { t as adminUserAtom } from "./adminUserAtom-DCi4wf-v.js";
3
3
  import { useI18n } from "alepha/react/i18n";
4
4
  import { Badge } from "@mantine/core";
5
5
  import { jsx } from "react/jsx-runtime";
6
-
6
+ import { useStore } from "alepha/react";
7
7
  //#region ../../src/admin/components/users/AdminUserProfile.tsx
8
8
  const AdminUserProfile = () => {
9
- const { user } = useUser();
9
+ const [user] = useStore(adminUserAtom);
10
10
  const { l } = useI18n();
11
+ if (!user) return null;
11
12
  return /* @__PURE__ */ jsx(DetailList, { items: [
12
13
  {
13
14
  label: "ID",
@@ -63,7 +64,7 @@ const AdminUserProfile = () => {
63
64
  }
64
65
  ] });
65
66
  };
66
-
67
67
  //#endregion
68
68
  export { AdminUserProfile as default };
69
- //# sourceMappingURL=AdminUserProfile-vFBLoJ3h.js.map
69
+
70
+ //# sourceMappingURL=AdminUserProfile-CzsPBl6Z.js.map