@alepha/ui 0.19.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/dist/admin/{AdminApiKeys-Bt1PjO6o.js → AdminApiKeys-C2ze85eD.js} +2 -2
  2. package/dist/admin/{AdminApiKeys-Bt1PjO6o.js.map → AdminApiKeys-C2ze85eD.js.map} +1 -1
  3. package/dist/admin/{AdminAudits-C7c1CN4c.js → AdminAudits-BIj81e4k.js} +2 -2
  4. package/dist/admin/{AdminAudits-C7c1CN4c.js.map → AdminAudits-BIj81e4k.js.map} +1 -1
  5. package/dist/admin/{AdminDashboard-C3RXpTp6.js → AdminDashboard-PMVzrwSu.js} +2 -2
  6. package/dist/admin/{AdminDashboard-C3RXpTp6.js.map → AdminDashboard-PMVzrwSu.js.map} +1 -1
  7. package/dist/admin/AdminFiles-Bq03BLt-.js +189 -0
  8. package/dist/admin/AdminFiles-Bq03BLt-.js.map +1 -0
  9. package/dist/admin/{AdminJobExecutions-D-G8RIlr.js → AdminJobs-D1_QGCDy.js} +400 -356
  10. package/dist/admin/AdminJobs-D1_QGCDy.js.map +1 -0
  11. package/dist/admin/{AdminLayout-BmZ9mtXh.js → AdminLayout-BNiwiw2D.js} +2 -2
  12. package/dist/admin/{AdminLayout-BmZ9mtXh.js.map → AdminLayout-BNiwiw2D.js.map} +1 -1
  13. package/dist/admin/{AdminNotifications-DHdzksww.js → AdminNotifications-DSKQtUfn.js} +84 -122
  14. package/dist/admin/AdminNotifications-DSKQtUfn.js.map +1 -0
  15. package/dist/admin/{AdminParameters-CyZQSXnN.js → AdminParameters-CoB7EhyM.js} +2 -2
  16. package/dist/admin/{AdminParameters-CyZQSXnN.js.map → AdminParameters-CoB7EhyM.js.map} +1 -1
  17. package/dist/admin/{AdminSessions--xwELDSO.js → AdminSessions-DFbFcrJQ.js} +2 -2
  18. package/dist/admin/{AdminSessions--xwELDSO.js.map → AdminSessions-DFbFcrJQ.js.map} +1 -1
  19. package/dist/admin/{AdminUserLayout-DvBTG5gd.js → AdminUserLayout-fSfi3KMm.js} +3 -3
  20. package/dist/admin/{AdminUserLayout-DvBTG5gd.js.map → AdminUserLayout-fSfi3KMm.js.map} +1 -1
  21. package/dist/admin/{AdminUserProfile-CzsPBl6Z.js → AdminUserProfile-_C-h8vUK.js} +3 -3
  22. package/dist/admin/{AdminUserProfile-CzsPBl6Z.js.map → AdminUserProfile-_C-h8vUK.js.map} +1 -1
  23. package/dist/admin/{AdminUserSessions-C-aUnhVN.js → AdminUserSessions-KpJHIeQo.js} +2 -2
  24. package/dist/admin/{AdminUserSessions-C-aUnhVN.js.map → AdminUserSessions-KpJHIeQo.js.map} +1 -1
  25. package/dist/admin/{AdminUsers-BYwei5sj.js → AdminUsers-DcVrzdQP.js} +2 -2
  26. package/dist/admin/{AdminUsers-BYwei5sj.js.map → AdminUsers-DcVrzdQP.js.map} +1 -1
  27. package/dist/admin/{AuthLayout-CkPGLJku.js → AuthLayout-CazfLzcf.js} +2 -2
  28. package/dist/admin/{AuthLayout-CkPGLJku.js.map → AuthLayout-CazfLzcf.js.map} +1 -1
  29. package/dist/admin/{Login-DSBqNsZc.js → Login-CaMjUrDP.js} +2 -2
  30. package/dist/admin/{Login-DSBqNsZc.js.map → Login-CaMjUrDP.js.map} +1 -1
  31. package/dist/admin/{Profile-CDRjJo0P.js → Profile-Ca4fZX15.js} +2 -2
  32. package/dist/{auth/Profile-Cy93pNTw.js.map → admin/Profile-Ca4fZX15.js.map} +1 -1
  33. package/dist/admin/{Register-4QGFOnfh.js → Register-C5DyKWPO.js} +2 -2
  34. package/dist/{demo/Register-KKZwr_lL.js.map → admin/Register-C5DyKWPO.js.map} +1 -1
  35. package/dist/admin/{ResetPassword-Gxc9L_mY.js → ResetPassword-BA5sAgXo.js} +2 -2
  36. package/dist/{auth/ResetPassword-B61QPlQi.js.map → admin/ResetPassword-BA5sAgXo.js.map} +1 -1
  37. package/dist/admin/{VerifyEmail-D7G5NnaN.js → VerifyEmail-DKNXROj_.js} +2 -2
  38. package/dist/{auth/VerifyEmail-CqBJ11id.js.map → admin/VerifyEmail-DKNXROj_.js.map} +1 -1
  39. package/dist/admin/{adminUserAtom-DCi4wf-v.js → adminUserAtom-BLNc7XbT.js} +1 -1
  40. package/dist/admin/{adminUserAtom-DCi4wf-v.js.map → adminUserAtom-BLNc7XbT.js.map} +1 -1
  41. package/dist/admin/{core-D1AbU50V.js → core-CJCEx18C.js} +111 -4
  42. package/dist/admin/core-CJCEx18C.js.map +1 -0
  43. package/dist/admin/index.d.ts +21 -3
  44. package/dist/admin/index.d.ts.map +1 -1
  45. package/dist/admin/index.js +29 -58
  46. package/dist/admin/index.js.map +1 -1
  47. package/dist/auth/{AuthLayout-CfRKcTqP.js → AuthLayout-vXPcCVzp.js} +2 -2
  48. package/dist/auth/{AuthLayout-CfRKcTqP.js.map → AuthLayout-vXPcCVzp.js.map} +1 -1
  49. package/dist/auth/{Login-DJyweoPS.js → Login-Dg08QR20.js} +2 -2
  50. package/dist/{demo/Login-CqG1iJbn.js.map → auth/Login-Dg08QR20.js.map} +1 -1
  51. package/dist/{demo/Profile-C0ojJCaG.js → auth/Profile-Bb5O1yeh.js} +2 -2
  52. package/dist/{admin/Profile-CDRjJo0P.js.map → auth/Profile-Bb5O1yeh.js.map} +1 -1
  53. package/dist/auth/{Register-CSqzzitW.js → Register-B2AN71NC.js} +2 -2
  54. package/dist/auth/{Register-CSqzzitW.js.map → Register-B2AN71NC.js.map} +1 -1
  55. package/dist/{demo/ResetPassword-DMrLFEtr.js → auth/ResetPassword-BLxwzbDj.js} +2 -2
  56. package/dist/{admin/ResetPassword-Gxc9L_mY.js.map → auth/ResetPassword-BLxwzbDj.js.map} +1 -1
  57. package/dist/auth/{VerifyEmail-CqBJ11id.js → VerifyEmail-CSDOk3Zm.js} +2 -2
  58. package/dist/{demo/VerifyEmail-BFCAFz6T.js.map → auth/VerifyEmail-CSDOk3Zm.js.map} +1 -1
  59. package/dist/auth/{core-C6D3pazL.js → core-DuGkjPiU.js} +2 -1
  60. package/dist/auth/core-DuGkjPiU.js.map +1 -0
  61. package/dist/auth/index.d.ts +20 -0
  62. package/dist/auth/index.d.ts.map +1 -1
  63. package/dist/auth/index.js +11 -11
  64. package/dist/core/index.d.ts +69 -17
  65. package/dist/core/index.d.ts.map +1 -1
  66. package/dist/core/index.js +110 -8
  67. package/dist/core/index.js.map +1 -1
  68. package/dist/demo/{AuthLayout-Dq5tSLSc.js → AuthLayout-DPsOOG4u.js} +2 -2
  69. package/dist/demo/{AuthLayout-Dq5tSLSc.js.map → AuthLayout-DPsOOG4u.js.map} +1 -1
  70. package/dist/demo/{DemoButton-_Ws2w-J0.js → DemoButton-wzcqGk4u.js} +3 -3
  71. package/dist/demo/{DemoButton-_Ws2w-J0.js.map → DemoButton-wzcqGk4u.js.map} +1 -1
  72. package/dist/demo/{DemoControlSelect-ChP4ZOpQ.js → DemoControlSelect-CMWvQ6Gm.js} +3 -3
  73. package/dist/demo/{DemoControlSelect-ChP4ZOpQ.js.map → DemoControlSelect-CMWvQ6Gm.js.map} +1 -1
  74. package/dist/demo/{DemoDataTable-Hwf_UUni.js → DemoDataTable-CHsAP3e2.js} +3 -3
  75. package/dist/demo/{DemoDataTable-Hwf_UUni.js.map → DemoDataTable-CHsAP3e2.js.map} +1 -1
  76. package/dist/demo/{DemoDialog-B01OMVRd.js → DemoDialog-Co2IePxX.js} +2 -2
  77. package/dist/demo/{DemoDialog-B01OMVRd.js.map → DemoDialog-Co2IePxX.js.map} +1 -1
  78. package/dist/demo/{DemoFlex-870PEl0V.js → DemoFlex-OEwQt5do.js} +3 -3
  79. package/dist/demo/{DemoFlex-870PEl0V.js.map → DemoFlex-OEwQt5do.js.map} +1 -1
  80. package/dist/demo/DemoHeading-Db-XkQIK.js +69 -0
  81. package/dist/demo/DemoHeading-Db-XkQIK.js.map +1 -0
  82. package/dist/demo/{DemoHome-DRbL2eGf.js → DemoHome-Cyp29ygy.js} +2 -2
  83. package/dist/demo/{DemoHome-DRbL2eGf.js.map → DemoHome-Cyp29ygy.js.map} +1 -1
  84. package/dist/demo/{DemoJsonViewer-DoABiqBW.js → DemoJsonViewer-DXtCeMzH.js} +3 -3
  85. package/dist/demo/{DemoJsonViewer-DoABiqBW.js.map → DemoJsonViewer-DXtCeMzH.js.map} +1 -1
  86. package/dist/demo/{DemoLayout-CN_PDCX2.js → DemoLayout-hh9VmZQP.js} +2 -2
  87. package/dist/demo/{DemoLayout-CN_PDCX2.js.map → DemoLayout-hh9VmZQP.js.map} +1 -1
  88. package/dist/demo/{DemoLogin-B5x-ug3Q.js → DemoLogin-DX7mnmkh.js} +13 -8
  89. package/dist/demo/{DemoLogin-B5x-ug3Q.js.map → DemoLogin-DX7mnmkh.js.map} +1 -1
  90. package/dist/demo/{DemoRegister-Q6sg2xuV.js → DemoRegister-DVcZl04m.js} +13 -8
  91. package/dist/demo/{DemoRegister-Q6sg2xuV.js.map → DemoRegister-DVcZl04m.js.map} +1 -1
  92. package/dist/demo/{DemoResetPassword-DrqZfmEw.js → DemoResetPassword-CPENlZH5.js} +13 -8
  93. package/dist/demo/{DemoResetPassword-DrqZfmEw.js.map → DemoResetPassword-CPENlZH5.js.map} +1 -1
  94. package/dist/demo/{DemoSidebar-CfKS6w1o.js → DemoSidebar-CGu7DZeM.js} +3 -3
  95. package/dist/demo/{DemoSidebar-CfKS6w1o.js.map → DemoSidebar-CGu7DZeM.js.map} +1 -1
  96. package/dist/demo/{DemoText-pT6Gi5b5.js → DemoText-DYUJ7bY_.js} +3 -3
  97. package/dist/demo/{DemoText-pT6Gi5b5.js.map → DemoText-DYUJ7bY_.js.map} +1 -1
  98. package/dist/demo/{DemoToast-I13NBzQQ.js → DemoToast-CgdnZNvx.js} +2 -2
  99. package/dist/demo/{DemoToast-I13NBzQQ.js.map → DemoToast-CgdnZNvx.js.map} +1 -1
  100. package/dist/demo/{DemoTypeForm-BqzcrtvN.js → DemoTypeForm-Pims-cGa.js} +3 -3
  101. package/dist/demo/{DemoTypeForm-BqzcrtvN.js.map → DemoTypeForm-Pims-cGa.js.map} +1 -1
  102. package/dist/demo/{DemoVerifyEmail-HwD8xfQw.js → DemoVerifyEmail-C7B3xxch.js} +8 -8
  103. package/dist/demo/{DemoVerifyEmail-HwD8xfQw.js.map → DemoVerifyEmail-C7B3xxch.js.map} +1 -1
  104. package/dist/demo/{Login-CqG1iJbn.js → Login-pwMF4TUj.js} +2 -2
  105. package/dist/{auth/Login-DJyweoPS.js.map → demo/Login-pwMF4TUj.js.map} +1 -1
  106. package/dist/{auth/Profile-Cy93pNTw.js → demo/Profile-BliZapZS.js} +2 -2
  107. package/dist/demo/{Profile-C0ojJCaG.js.map → Profile-BliZapZS.js.map} +1 -1
  108. package/dist/demo/{Register-KKZwr_lL.js → Register-CiwAT7Hy.js} +2 -2
  109. package/dist/{admin/Register-4QGFOnfh.js.map → demo/Register-CiwAT7Hy.js.map} +1 -1
  110. package/dist/{auth/ResetPassword-B61QPlQi.js → demo/ResetPassword-l9Vg4JE-.js} +2 -2
  111. package/dist/demo/{ResetPassword-DMrLFEtr.js.map → ResetPassword-l9Vg4JE-.js.map} +1 -1
  112. package/dist/demo/{Showcase-D49Wud2v.js → Showcase-CX6bDgwe.js} +2 -2
  113. package/dist/demo/{Showcase-D49Wud2v.js.map → Showcase-CX6bDgwe.js.map} +1 -1
  114. package/dist/demo/{VerifyEmail-BFCAFz6T.js → VerifyEmail-CAB-OS7i.js} +2 -2
  115. package/dist/{admin/VerifyEmail-D7G5NnaN.js.map → demo/VerifyEmail-CAB-OS7i.js.map} +1 -1
  116. package/dist/demo/{auth-D9qTZzCa.js → auth-uegJAdKu.js} +8 -8
  117. package/dist/demo/{auth-D9qTZzCa.js.map → auth-uegJAdKu.js.map} +1 -1
  118. package/dist/demo/{core-DRtQklr3.js → core-B4LVHzPn.js} +111 -9
  119. package/dist/demo/core-B4LVHzPn.js.map +1 -0
  120. package/dist/demo/index.js +19 -19
  121. package/dist/demo/index.js.map +1 -1
  122. package/package.json +6 -9
  123. package/src/admin/AdminRouter.tsx +5 -37
  124. package/src/admin/components/files/AdminFiles.tsx +123 -1
  125. package/src/admin/components/jobs/{AdminJobExecutions.tsx → AdminJobs.tsx} +450 -317
  126. package/src/admin/components/notifications/AdminNotifications.tsx +11 -25
  127. package/src/core/components/Section.tsx +109 -0
  128. package/src/core/components/SectionHeader.tsx +106 -0
  129. package/src/core/index.ts +4 -1
  130. package/src/core/table/components/DataTable.tsx +5 -1
  131. package/src/demo/DemoRouter.ts +1 -1
  132. package/src/demo/components/auth/DemoLogin.tsx +5 -0
  133. package/src/demo/components/auth/DemoRegister.tsx +5 -0
  134. package/src/demo/components/auth/DemoResetPassword.tsx +5 -0
  135. package/src/demo/components/core/DemoHeading.tsx +56 -3
  136. package/dist/admin/AdminFiles-31ivR6Wq.js +0 -110
  137. package/dist/admin/AdminFiles-31ivR6Wq.js.map +0 -1
  138. package/dist/admin/AdminJobDashboard-BABLe7hL.js +0 -402
  139. package/dist/admin/AdminJobDashboard-BABLe7hL.js.map +0 -1
  140. package/dist/admin/AdminJobExecutions-D-G8RIlr.js.map +0 -1
  141. package/dist/admin/AdminJobRegistry-oIS3K9NX.js +0 -269
  142. package/dist/admin/AdminJobRegistry-oIS3K9NX.js.map +0 -1
  143. package/dist/admin/AdminNotifications-DHdzksww.js.map +0 -1
  144. package/dist/admin/core-D1AbU50V.js.map +0 -1
  145. package/dist/auth/core-C6D3pazL.js.map +0 -1
  146. package/dist/demo/DemoHeading-C1YR27fz.js +0 -17
  147. package/dist/demo/DemoHeading-C1YR27fz.js.map +0 -1
  148. package/dist/demo/core-DRtQklr3.js.map +0 -1
  149. package/src/admin/components/jobs/AdminJobDashboard.tsx +0 -380
  150. package/src/admin/components/jobs/AdminJobRegistry.tsx +0 -301
  151. package/src/core/components/Heading.tsx +0 -19
@@ -1 +1 @@
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
+ {"version":3,"file":"AdminParameters-CoB7EhyM.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-D1AbU50V.js";
1
+ import { c as Text$1, h as useDialog, r as DataTable, u as Flex$1, v as ActionButton, x as useToast } from "./core-CJCEx18C.js";
2
2
  import { t } from "alepha";
3
3
  import { useI18n } from "alepha/react/i18n";
4
4
  import { Badge } from "@mantine/core";
@@ -134,4 +134,4 @@ const AdminSessions = (props) => {
134
134
  //#endregion
135
135
  export { AdminSessions as default };
136
136
 
137
- //# sourceMappingURL=AdminSessions--xwELDSO.js.map
137
+ //# sourceMappingURL=AdminSessions-DFbFcrJQ.js.map
@@ -1 +1 @@
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":"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"}
1
+ {"version":3,"file":"AdminSessions-DFbFcrJQ.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,5 +1,5 @@
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";
1
+ import { h as useDialog, u as Flex$1, v as ActionButton, x as useToast } from "./core-CJCEx18C.js";
2
+ import { t as adminUserAtom } from "./adminUserAtom-BLNc7XbT.js";
3
3
  import { ActionIcon, Avatar, Badge, Button, Flex, Menu, Tabs, Text, Tooltip } from "@mantine/core";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useCallback, useEffect } from "react";
@@ -260,4 +260,4 @@ const AdminUserLayout = (props) => {
260
260
  //#endregion
261
261
  export { AdminUserLayout as default };
262
262
 
263
- //# sourceMappingURL=AdminUserLayout-DvBTG5gd.js.map
263
+ //# sourceMappingURL=AdminUserLayout-fSfi3KMm.js.map
@@ -1 +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
+ {"version":3,"file":"AdminUserLayout-fSfi3KMm.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,5 +1,5 @@
1
- import { d as DetailList, l as Flex$1 } from "./core-D1AbU50V.js";
2
- import { t as adminUserAtom } from "./adminUserAtom-DCi4wf-v.js";
1
+ import { f as DetailList, u as Flex$1 } from "./core-CJCEx18C.js";
2
+ import { t as adminUserAtom } from "./adminUserAtom-BLNc7XbT.js";
3
3
  import { useI18n } from "alepha/react/i18n";
4
4
  import { Badge } from "@mantine/core";
5
5
  import { jsx } from "react/jsx-runtime";
@@ -67,4 +67,4 @@ const AdminUserProfile = () => {
67
67
  //#endregion
68
68
  export { AdminUserProfile as default };
69
69
 
70
- //# sourceMappingURL=AdminUserProfile-CzsPBl6Z.js.map
70
+ //# sourceMappingURL=AdminUserProfile-_C-h8vUK.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminUserProfile-CzsPBl6Z.js","names":["Flex"],"sources":["../../src/admin/components/users/AdminUserProfile.tsx"],"sourcesContent":["import { DetailList, Flex } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport { useStore } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { adminUserAtom } from \"../../atoms/adminUserAtom.ts\";\n\nconst AdminUserProfile = () => {\n const [user] = useStore(adminUserAtom);\n const { l } = useI18n();\n\n if (!user) return null;\n\n return (\n <DetailList\n items={[\n { label: \"ID\", value: user.id, copyable: user.id },\n { label: \"Username\", value: user.username },\n { label: \"Email\", value: user.email },\n {\n label: \"Email Verified\",\n value: user.emailVerified ? \"Yes\" : \"No\",\n },\n { label: \"Phone\", value: user.phoneNumber },\n { label: \"First Name\", value: user.firstName },\n { label: \"Last Name\", value: user.lastName },\n { label: \"Realm\", value: user.realm },\n {\n label: \"Roles\",\n value:\n user.roles.length > 0 ? (\n <Flex gap={4}>\n {user.roles.map((role) => (\n <Badge key={role} size=\"xs\" variant=\"default\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : null,\n },\n {\n label: \"Created\",\n value: String(l(user.createdAt, { date: \"lll\" })),\n },\n {\n label: \"Updated\",\n value: String(l(user.updatedAt, { date: \"lll\" })),\n },\n ]}\n />\n );\n};\n\nexport default AdminUserProfile;\n"],"mappings":";;;;;;;AAMA,MAAM,yBAAyB;CAC7B,MAAM,CAAC,QAAQ,SAAS,cAAc;CACtC,MAAM,EAAE,MAAM,SAAS;AAEvB,KAAI,CAAC,KAAM,QAAO;AAElB,QACE,oBAAC,YAAD,EACE,OAAO;EACL;GAAE,OAAO;GAAM,OAAO,KAAK;GAAI,UAAU,KAAK;GAAI;EAClD;GAAE,OAAO;GAAY,OAAO,KAAK;GAAU;EAC3C;GAAE,OAAO;GAAS,OAAO,KAAK;GAAO;EACrC;GACE,OAAO;GACP,OAAO,KAAK,gBAAgB,QAAQ;GACrC;EACD;GAAE,OAAO;GAAS,OAAO,KAAK;GAAa;EAC3C;GAAE,OAAO;GAAc,OAAO,KAAK;GAAW;EAC9C;GAAE,OAAO;GAAa,OAAO,KAAK;GAAU;EAC5C;GAAE,OAAO;GAAS,OAAO,KAAK;GAAO;EACrC;GACE,OAAO;GACP,OACE,KAAK,MAAM,SAAS,IAClB,oBAACA,QAAD;IAAM,KAAK;cACR,KAAK,MAAM,KAAK,SACf,oBAAC,OAAD;KAAkB,MAAK;KAAK,SAAQ;eACjC;KACK,EAFI,KAEJ,CACR;IACG,CAAA,GACL;GACP;EACD;GACE,OAAO;GACP,OAAO,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;GAClD;EACD;GACE,OAAO;GACP,OAAO,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;GAClD;EACF,EACD,CAAA"}
1
+ {"version":3,"file":"AdminUserProfile-_C-h8vUK.js","names":["Flex"],"sources":["../../src/admin/components/users/AdminUserProfile.tsx"],"sourcesContent":["import { DetailList, Flex } from \"@alepha/ui\";\nimport { Badge } from \"@mantine/core\";\nimport { useStore } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { adminUserAtom } from \"../../atoms/adminUserAtom.ts\";\n\nconst AdminUserProfile = () => {\n const [user] = useStore(adminUserAtom);\n const { l } = useI18n();\n\n if (!user) return null;\n\n return (\n <DetailList\n items={[\n { label: \"ID\", value: user.id, copyable: user.id },\n { label: \"Username\", value: user.username },\n { label: \"Email\", value: user.email },\n {\n label: \"Email Verified\",\n value: user.emailVerified ? \"Yes\" : \"No\",\n },\n { label: \"Phone\", value: user.phoneNumber },\n { label: \"First Name\", value: user.firstName },\n { label: \"Last Name\", value: user.lastName },\n { label: \"Realm\", value: user.realm },\n {\n label: \"Roles\",\n value:\n user.roles.length > 0 ? (\n <Flex gap={4}>\n {user.roles.map((role) => (\n <Badge key={role} size=\"xs\" variant=\"default\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : null,\n },\n {\n label: \"Created\",\n value: String(l(user.createdAt, { date: \"lll\" })),\n },\n {\n label: \"Updated\",\n value: String(l(user.updatedAt, { date: \"lll\" })),\n },\n ]}\n />\n );\n};\n\nexport default AdminUserProfile;\n"],"mappings":";;;;;;;AAMA,MAAM,yBAAyB;CAC7B,MAAM,CAAC,QAAQ,SAAS,cAAc;CACtC,MAAM,EAAE,MAAM,SAAS;AAEvB,KAAI,CAAC,KAAM,QAAO;AAElB,QACE,oBAAC,YAAD,EACE,OAAO;EACL;GAAE,OAAO;GAAM,OAAO,KAAK;GAAI,UAAU,KAAK;GAAI;EAClD;GAAE,OAAO;GAAY,OAAO,KAAK;GAAU;EAC3C;GAAE,OAAO;GAAS,OAAO,KAAK;GAAO;EACrC;GACE,OAAO;GACP,OAAO,KAAK,gBAAgB,QAAQ;GACrC;EACD;GAAE,OAAO;GAAS,OAAO,KAAK;GAAa;EAC3C;GAAE,OAAO;GAAc,OAAO,KAAK;GAAW;EAC9C;GAAE,OAAO;GAAa,OAAO,KAAK;GAAU;EAC5C;GAAE,OAAO;GAAS,OAAO,KAAK;GAAO;EACrC;GACE,OAAO;GACP,OACE,KAAK,MAAM,SAAS,IAClB,oBAACA,QAAD;IAAM,KAAK;cACR,KAAK,MAAM,KAAK,SACf,oBAAC,OAAD;KAAkB,MAAK;KAAK,SAAQ;eACjC;KACK,EAFI,KAEJ,CACR;IACG,CAAA,GACL;GACP;EACD;GACE,OAAO;GACP,OAAO,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;GAClD;EACD;GACE,OAAO;GACP,OAAO,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;GAClD;EACF,EACD,CAAA"}
@@ -1,4 +1,4 @@
1
- import { b as useToast, l as Flex$1, m as useDialog, r as DataTable, s as Text$1 } from "./core-D1AbU50V.js";
1
+ import { c as Text$1, h as useDialog, r as DataTable, u as Flex$1, x as useToast } from "./core-CJCEx18C.js";
2
2
  import { t } from "alepha";
3
3
  import { useI18n } from "alepha/react/i18n";
4
4
  import { Badge } from "@mantine/core";
@@ -103,4 +103,4 @@ const AdminUserSessions = (props) => {
103
103
  //#endregion
104
104
  export { AdminUserSessions as default };
105
105
 
106
- //# sourceMappingURL=AdminUserSessions-C-aUnhVN.js.map
106
+ //# sourceMappingURL=AdminUserSessions-KpJHIeQo.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminUserSessions-C-aUnhVN.js","names":["Flex","Text"],"sources":["../../src/admin/components/users/AdminUserSessions.tsx"],"sourcesContent":["import { DataTable, Flex, Text, useDialog, useToast } 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 type { AdminSessionController, SessionEntity } from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouterState } from \"alepha/react/router\";\nimport { useState } from \"react\";\n\nexport interface AdminUserSessionsProps {\n userRealmName?: string;\n}\n\nconst emptyFilters = t.object({});\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 AdminUserSessions = (props: AdminUserSessionsProps) => {\n const state = useRouterState();\n const userId = state.params.userId as string;\n const client = useClient<AdminSessionController>();\n const { l } = useI18n();\n const dialog = useDialog();\n const toast = useToast();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const handleDeleteSession = 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 on this device.\",\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 <DataTable<SessionEntity, typeof emptyFilters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n filters={emptyFilters}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\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 userId,\n userRealmName: props.userRealmName,\n },\n });\n return response as Page<SessionEntity>;\n }}\n columns={{\n device: {\n label: \"Device\",\n value: (item) => (\n <Flex gap={4} align=\"center\">\n {getDeviceIcon(item.userAgent?.device)}\n <Text size=\"xs\">\n {item.userAgent\n ? `${item.userAgent.browser} / ${item.userAgent.os}`\n : \"—\"}\n </Text>\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 status: {\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: () => handleDeleteSession(item),\n visible: !isExpired(item.expiresAt),\n },\n ]}\n />\n );\n};\n\nexport default AdminUserSessions;\n"],"mappings":";;;;;;;;;;AAmBA,MAAM,eAAe,EAAE,OAAO,EAAE,CAAC;AAEjC,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,qBAAqB,UAAkC;CAE3D,MAAM,SADQ,gBAAgB,CACT,OAAO;CAC5B,MAAM,SAAS,WAAmC;CAClD,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,SAAS,WAAW;CAC1B,MAAM,QAAQ,UAAU;CACxB,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAE/C,MAAM,sBAAsB,OAAO,YAA2B;AAM5D,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,oBAAC,WAAD;EAEE,cAAA;EACA,aAAa;EACb,SAAS;EACT,YAAY;GACV,mBAAmB;GACnB,iBAAiB;GAClB;EACD,eAAe,UAAU,EACvB,OAAO,EACL,SAAS,UAAU,KAAK,UAAU,GAAG,KAAM,GAC5C,EACF;EACD,OAAO,OAAO,YAAY;AAQxB,UAPiB,MAAM,OAAO,aAAa,EACzC,OAAO;IACL,GAAG;IACH;IACA,eAAe,MAAM;IACtB,EACF,CAAC;;EAGJ,SAAS;GACP,QAAQ;IACN,OAAO;IACP,QAAQ,SACN,qBAACA,QAAD;KAAM,KAAK;KAAG,OAAM;eAApB,CACG,cAAc,KAAK,WAAW,OAAO,EACtC,oBAACC,QAAD;MAAM,MAAK;gBACR,KAAK,YACF,GAAG,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,OAC9C;MACC,CAAA,CACF;;IAEV;GACD,IAAI;IACF,OAAO;IACP,QAAQ,SACN,oBAACA,QAAD;KAAM,MAAK;KAAK,IAAG;KAAY,OAAA;eAC5B,KAAK,MAAM;KACP,CAAA;IAEV;GACD,QAAQ;IACN,OAAO;IACP,QAAQ,SACN,oBAAC,OAAD;KACE,MAAK;KACL,SAAQ;KACR,OAAO,UAAU,KAAK,UAAU,GAAG,SAAS;eAE3C,UAAU,KAAK,UAAU,GAAG,YAAY;KACnC,CAAA;IAEX;GACD,WAAW;IACT,OAAO;IACP,QAAQ,SACN,oBAACA,QAAD;KAAM,MAAK;KAAK,OAAA;eACb,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;KAClC,CAAA;IAEV;GACF;EACD,aAAa,SAAS,CACpB;GACE,OAAO;GACP,MAAM;GACN,OAAO;GACP,eAAe,oBAAoB,KAAK;GACxC,SAAS,CAAC,UAAU,KAAK,UAAU;GACpC,CACF;EACD,EA3EK,WA2EL"}
1
+ {"version":3,"file":"AdminUserSessions-KpJHIeQo.js","names":["Flex","Text"],"sources":["../../src/admin/components/users/AdminUserSessions.tsx"],"sourcesContent":["import { DataTable, Flex, Text, useDialog, useToast } 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 type { AdminSessionController, SessionEntity } from \"alepha/api/users\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouterState } from \"alepha/react/router\";\nimport { useState } from \"react\";\n\nexport interface AdminUserSessionsProps {\n userRealmName?: string;\n}\n\nconst emptyFilters = t.object({});\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 AdminUserSessions = (props: AdminUserSessionsProps) => {\n const state = useRouterState();\n const userId = state.params.userId as string;\n const client = useClient<AdminSessionController>();\n const { l } = useI18n();\n const dialog = useDialog();\n const toast = useToast();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const handleDeleteSession = 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 on this device.\",\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 <DataTable<SessionEntity, typeof emptyFilters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n filters={emptyFilters}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\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 userId,\n userRealmName: props.userRealmName,\n },\n });\n return response as Page<SessionEntity>;\n }}\n columns={{\n device: {\n label: \"Device\",\n value: (item) => (\n <Flex gap={4} align=\"center\">\n {getDeviceIcon(item.userAgent?.device)}\n <Text size=\"xs\">\n {item.userAgent\n ? `${item.userAgent.browser} / ${item.userAgent.os}`\n : \"—\"}\n </Text>\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 status: {\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: () => handleDeleteSession(item),\n visible: !isExpired(item.expiresAt),\n },\n ]}\n />\n );\n};\n\nexport default AdminUserSessions;\n"],"mappings":";;;;;;;;;;AAmBA,MAAM,eAAe,EAAE,OAAO,EAAE,CAAC;AAEjC,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,qBAAqB,UAAkC;CAE3D,MAAM,SADQ,gBAAgB,CACT,OAAO;CAC5B,MAAM,SAAS,WAAmC;CAClD,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,SAAS,WAAW;CAC1B,MAAM,QAAQ,UAAU;CACxB,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAE/C,MAAM,sBAAsB,OAAO,YAA2B;AAM5D,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,oBAAC,WAAD;EAEE,cAAA;EACA,aAAa;EACb,SAAS;EACT,YAAY;GACV,mBAAmB;GACnB,iBAAiB;GAClB;EACD,eAAe,UAAU,EACvB,OAAO,EACL,SAAS,UAAU,KAAK,UAAU,GAAG,KAAM,GAC5C,EACF;EACD,OAAO,OAAO,YAAY;AAQxB,UAPiB,MAAM,OAAO,aAAa,EACzC,OAAO;IACL,GAAG;IACH;IACA,eAAe,MAAM;IACtB,EACF,CAAC;;EAGJ,SAAS;GACP,QAAQ;IACN,OAAO;IACP,QAAQ,SACN,qBAACA,QAAD;KAAM,KAAK;KAAG,OAAM;eAApB,CACG,cAAc,KAAK,WAAW,OAAO,EACtC,oBAACC,QAAD;MAAM,MAAK;gBACR,KAAK,YACF,GAAG,KAAK,UAAU,QAAQ,KAAK,KAAK,UAAU,OAC9C;MACC,CAAA,CACF;;IAEV;GACD,IAAI;IACF,OAAO;IACP,QAAQ,SACN,oBAACA,QAAD;KAAM,MAAK;KAAK,IAAG;KAAY,OAAA;eAC5B,KAAK,MAAM;KACP,CAAA;IAEV;GACD,QAAQ;IACN,OAAO;IACP,QAAQ,SACN,oBAAC,OAAD;KACE,MAAK;KACL,SAAQ;KACR,OAAO,UAAU,KAAK,UAAU,GAAG,SAAS;eAE3C,UAAU,KAAK,UAAU,GAAG,YAAY;KACnC,CAAA;IAEX;GACD,WAAW;IACT,OAAO;IACP,QAAQ,SACN,oBAACA,QAAD;KAAM,MAAK;KAAK,OAAA;eACb,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;KAClC,CAAA;IAEV;GACF;EACD,aAAa,SAAS,CACpB;GACE,OAAO;GACP,MAAM;GACN,OAAO;GACP,eAAe,oBAAoB,KAAK;GACxC,SAAS,CAAC,UAAU,KAAK,UAAU;GACpC,CACF;EACD,EA3EK,WA2EL"}
@@ -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-D1AbU50V.js";
1
+ import { c as Text$1, h as useDialog, r as DataTable, u as Flex$1, v as ActionButton, x as useToast } from "./core-CJCEx18C.js";
2
2
  import { t } from "alepha";
3
3
  import { useI18n } from "alepha/react/i18n";
4
4
  import { Avatar, Badge } from "@mantine/core";
@@ -203,4 +203,4 @@ const AdminUsers = (props) => {
203
203
  //#endregion
204
204
  export { AdminUsers as default };
205
205
 
206
- //# sourceMappingURL=AdminUsers-BYwei5sj.js.map
206
+ //# sourceMappingURL=AdminUsers-DcVrzdQP.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminUsers-BYwei5sj.js","names":["Flex","Text"],"sources":["../../src/admin/components/users/AdminUsers.tsx"],"sourcesContent":["import {\n ActionButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} from \"@alepha/ui\";\nimport { Avatar, Badge } from \"@mantine/core\";\nimport {\n IconEye,\n IconTrash,\n IconUserOff,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminUserController, UserEntity } 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 AdminUsersProps {\n userRealmName?: string;\n}\n\nconst filters = t.object({\n query: t.optional(t.text()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n});\n\nconst AdminUsers = (props: AdminUsersProps) => {\n const client = useClient<AdminUserController>();\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 refresh = () => setRefreshKey((k) => k + 1);\n\n const handleToggleEnabled = async (user: UserEntity) => {\n const enabled = !user.enabled;\n const confirmed = await dialog.confirm({\n title: enabled ? \"Enable user\" : \"Disable user\",\n message: enabled\n ? `Enable ${user.email || user.username || \"this user\"}?`\n : `Disable ${user.email || user.username || \"this user\"}? They will no longer be able to sign in.`,\n });\n if (!confirmed) return;\n await client.updateUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n body: { enabled },\n });\n toast.success({\n message: enabled ? \"User enabled\" : \"User disabled\",\n });\n refresh();\n };\n\n const handleDelete = async (user: UserEntity) => {\n const confirmed = await dialog.confirm({\n title: \"Delete user\",\n message: `Permanently delete ${user.email || user.username || \"this user\"}? This action cannot be undone.`,\n });\n if (!confirmed) return;\n await client.deleteUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n });\n toast.success({ message: \"User deleted\" });\n refresh();\n };\n\n const handleBulkDisable = async (\n items: UserEntity[],\n clearSelection: () => void,\n ) => {\n const enabledUsers = items.filter((u) => u.enabled);\n if (enabledUsers.length === 0) {\n toast.danger({ message: \"No active users in selection\" });\n return;\n }\n const confirmed = await dialog.confirm({\n title: \"Disable users\",\n message: `Disable ${enabledUsers.length} user(s)? They will no longer be able to sign in.`,\n });\n if (!confirmed) return;\n for (const user of enabledUsers) {\n await client.updateUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n body: { enabled: false },\n });\n }\n toast.success({\n message: `${enabledUsers.length} user(s) disabled`,\n });\n clearSelection();\n refresh();\n };\n\n return (\n <Flex p={\"md\"} flex={1} direction=\"column\">\n <DataTable<UserEntity, typeof filters>\n withCheckbox\n withExport\n checkboxActions={[\n {\n intent: \"danger\",\n label: \"Disable selected\",\n icon: <IconUserOff />,\n onClick: (ctx) =>\n handleBulkDisable(ctx.selectedItems, ctx.clearSelection),\n },\n ]}\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n defaultFilters={[\"query\"]}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(_key, _value, form) => form.submit()}\n filters={filters}\n items={async (filters) => {\n const response = await client.findUsers({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n return response as Page<UserEntity>;\n }}\n columns={{\n user: {\n label: \"User\",\n value: (item) => {\n const name =\n `${item.firstName || \"\"} ${item.lastName || \"\"}`.trim() ||\n item.username ||\n \"Anonymous\";\n\n return (\n <ActionButton\n variant={\"transparent\"}\n href={`/admin/users/${item.id}`}\n >\n <Flex gap={\"xs\"} centerY>\n <Avatar size={\"sm\"} name={name} />\n <Flex col align={\"start\"}>\n <Text size={\"sm\"} bold>\n {name}\n </Text>\n <Text size=\"xs\" muted>\n {item.email || \"\\u2014\"}\n </Text>\n </Flex>\n </Flex>\n </ActionButton>\n );\n },\n },\n username: {\n label: \"Username\",\n defaultHidden: true,\n value: (item) => (\n <Text size=\"sm\" ff=\"monospace\">\n {item.username || \"\\u2014\"}\n </Text>\n ),\n },\n roles: {\n label: \"Roles\",\n value: (item) =>\n item.roles.length > 0 ? (\n <Flex gap={4}>\n {item.roles.map((role: string) => (\n <Badge key={role} size=\"xs\" variant=\"default\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : (\n <Text size=\"xs\" muted>\n No roles\n </Text>\n ),\n },\n enabled: {\n label: \"Status\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={item.enabled ? \"green\" : \"red\"}\n >\n {item.enabled ? \"Active\" : \"Disabled\"}\n </Badge>\n ),\n },\n emailVerified: {\n label: \"Email\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={item.emailVerified ? \"green\" : \"gray\"}\n >\n {item.emailVerified ? \"Verified\" : \"Unverified\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Joined\",\n sortable: true,\n sortKey: \"createdAt\",\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: \"View profile\",\n icon: IconEye,\n onClick: () =>\n router.push(\"adminUserProfile\", {\n params: { userId: item.id },\n }),\n },\n {\n label: item.enabled ? \"Disable user\" : \"Enable user\",\n icon: item.enabled ? IconUserOff : IconUserPlus,\n color: item.enabled ? \"red\" : \"green\",\n onClick: () => handleToggleEnabled(item),\n },\n {\n label: \"Delete user\",\n icon: IconTrash,\n color: \"red\",\n onClick: () => handleDelete(item),\n },\n ]}\n />\n </Flex>\n );\n};\n\nexport default AdminUsers;\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminUsers-DcVrzdQP.js","names":["Flex","Text"],"sources":["../../src/admin/components/users/AdminUsers.tsx"],"sourcesContent":["import {\n ActionButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} from \"@alepha/ui\";\nimport { Avatar, Badge } from \"@mantine/core\";\nimport {\n IconEye,\n IconTrash,\n IconUserOff,\n IconUserPlus,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminUserController, UserEntity } 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 AdminUsersProps {\n userRealmName?: string;\n}\n\nconst filters = t.object({\n query: t.optional(t.text()),\n enabled: t.optional(t.boolean()),\n emailVerified: t.optional(t.boolean()),\n});\n\nconst AdminUsers = (props: AdminUsersProps) => {\n const client = useClient<AdminUserController>();\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 refresh = () => setRefreshKey((k) => k + 1);\n\n const handleToggleEnabled = async (user: UserEntity) => {\n const enabled = !user.enabled;\n const confirmed = await dialog.confirm({\n title: enabled ? \"Enable user\" : \"Disable user\",\n message: enabled\n ? `Enable ${user.email || user.username || \"this user\"}?`\n : `Disable ${user.email || user.username || \"this user\"}? They will no longer be able to sign in.`,\n });\n if (!confirmed) return;\n await client.updateUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n body: { enabled },\n });\n toast.success({\n message: enabled ? \"User enabled\" : \"User disabled\",\n });\n refresh();\n };\n\n const handleDelete = async (user: UserEntity) => {\n const confirmed = await dialog.confirm({\n title: \"Delete user\",\n message: `Permanently delete ${user.email || user.username || \"this user\"}? This action cannot be undone.`,\n });\n if (!confirmed) return;\n await client.deleteUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n });\n toast.success({ message: \"User deleted\" });\n refresh();\n };\n\n const handleBulkDisable = async (\n items: UserEntity[],\n clearSelection: () => void,\n ) => {\n const enabledUsers = items.filter((u) => u.enabled);\n if (enabledUsers.length === 0) {\n toast.danger({ message: \"No active users in selection\" });\n return;\n }\n const confirmed = await dialog.confirm({\n title: \"Disable users\",\n message: `Disable ${enabledUsers.length} user(s)? They will no longer be able to sign in.`,\n });\n if (!confirmed) return;\n for (const user of enabledUsers) {\n await client.updateUser({\n params: { id: user.id },\n query: { userRealmName: props.userRealmName },\n body: { enabled: false },\n });\n }\n toast.success({\n message: `${enabledUsers.length} user(s) disabled`,\n });\n clearSelection();\n refresh();\n };\n\n return (\n <Flex p={\"md\"} flex={1} direction=\"column\">\n <DataTable<UserEntity, typeof filters>\n withCheckbox\n withExport\n checkboxActions={[\n {\n intent: \"danger\",\n label: \"Disable selected\",\n icon: <IconUserOff />,\n onClick: (ctx) =>\n handleBulkDisable(ctx.selectedItems, ctx.clearSelection),\n },\n ]}\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n defaultFilters={[\"query\"]}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(_key, _value, form) => form.submit()}\n filters={filters}\n items={async (filters) => {\n const response = await client.findUsers({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n return response as Page<UserEntity>;\n }}\n columns={{\n user: {\n label: \"User\",\n value: (item) => {\n const name =\n `${item.firstName || \"\"} ${item.lastName || \"\"}`.trim() ||\n item.username ||\n \"Anonymous\";\n\n return (\n <ActionButton\n variant={\"transparent\"}\n href={`/admin/users/${item.id}`}\n >\n <Flex gap={\"xs\"} centerY>\n <Avatar size={\"sm\"} name={name} />\n <Flex col align={\"start\"}>\n <Text size={\"sm\"} bold>\n {name}\n </Text>\n <Text size=\"xs\" muted>\n {item.email || \"\\u2014\"}\n </Text>\n </Flex>\n </Flex>\n </ActionButton>\n );\n },\n },\n username: {\n label: \"Username\",\n defaultHidden: true,\n value: (item) => (\n <Text size=\"sm\" ff=\"monospace\">\n {item.username || \"\\u2014\"}\n </Text>\n ),\n },\n roles: {\n label: \"Roles\",\n value: (item) =>\n item.roles.length > 0 ? (\n <Flex gap={4}>\n {item.roles.map((role: string) => (\n <Badge key={role} size=\"xs\" variant=\"default\">\n {role}\n </Badge>\n ))}\n </Flex>\n ) : (\n <Text size=\"xs\" muted>\n No roles\n </Text>\n ),\n },\n enabled: {\n label: \"Status\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={item.enabled ? \"green\" : \"red\"}\n >\n {item.enabled ? \"Active\" : \"Disabled\"}\n </Badge>\n ),\n },\n emailVerified: {\n label: \"Email\",\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={item.emailVerified ? \"green\" : \"gray\"}\n >\n {item.emailVerified ? \"Verified\" : \"Unverified\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Joined\",\n sortable: true,\n sortKey: \"createdAt\",\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: \"View profile\",\n icon: IconEye,\n onClick: () =>\n router.push(\"adminUserProfile\", {\n params: { userId: item.id },\n }),\n },\n {\n label: item.enabled ? \"Disable user\" : \"Enable user\",\n icon: item.enabled ? IconUserOff : IconUserPlus,\n color: item.enabled ? \"red\" : \"green\",\n onClick: () => handleToggleEnabled(item),\n },\n {\n label: \"Delete user\",\n icon: IconTrash,\n color: \"red\",\n onClick: () => handleDelete(item),\n },\n ]}\n />\n </Flex>\n );\n};\n\nexport default AdminUsers;\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { y as AlephaMantineProvider } from "./core-D1AbU50V.js";
1
+ import { b as AlephaMantineProvider } from "./core-CJCEx18C.js";
2
2
  import { Flex } from "@mantine/core";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { NestedView } from "alepha/react/router";
@@ -18,4 +18,4 @@ const AuthLayout = () => {
18
18
  //#endregion
19
19
  export { AuthLayout as default };
20
20
 
21
- //# sourceMappingURL=AuthLayout-CkPGLJku.js.map
21
+ //# sourceMappingURL=AuthLayout-CazfLzcf.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthLayout-CkPGLJku.js","names":[],"sources":["../../src/auth/components/AuthLayout.tsx"],"sourcesContent":["import { AlephaMantineProvider } from \"@alepha/ui\";\nimport { Flex } from \"@mantine/core\";\nimport { NestedView } from \"alepha/react/router\";\n\nconst AuthLayout = () => {\n return (\n <AlephaMantineProvider omnibar={false}>\n <Flex flex={1} align={\"center\"} h={\"100vh\"} justify={\"center\"}>\n <NestedView />\n </Flex>\n </AlephaMantineProvider>\n );\n};\n\nexport default AuthLayout;\n"],"mappings":";;;;;AAIA,MAAM,mBAAmB;AACvB,QACE,oBAAC,uBAAD;EAAuB,SAAS;YAC9B,oBAAC,MAAD;GAAM,MAAM;GAAG,OAAO;GAAU,GAAG;GAAS,SAAS;aACnD,oBAAC,YAAD,EAAc,CAAA;GACT,CAAA;EACe,CAAA"}
1
+ {"version":3,"file":"AuthLayout-CazfLzcf.js","names":[],"sources":["../../src/auth/components/AuthLayout.tsx"],"sourcesContent":["import { AlephaMantineProvider } from \"@alepha/ui\";\nimport { Flex } from \"@mantine/core\";\nimport { NestedView } from \"alepha/react/router\";\n\nconst AuthLayout = () => {\n return (\n <AlephaMantineProvider omnibar={false}>\n <Flex flex={1} align={\"center\"} h={\"100vh\"} justify={\"center\"}>\n <NestedView />\n </Flex>\n </AlephaMantineProvider>\n );\n};\n\nexport default AuthLayout;\n"],"mappings":";;;;;AAIA,MAAM,mBAAmB;AACvB,QACE,oBAAC,uBAAD;EAAuB,SAAS;YAC9B,oBAAC,MAAD;GAAM,MAAM;GAAG,OAAO;GAAU,GAAG;GAAS,SAAS;aACnD,oBAAC,YAAD,EAAc,CAAA;GACT,CAAA;EACe,CAAA"}
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-CiIaOW0V.js";
2
- import { _ as ActionButton, a as Control, n as capitalize } from "./core-D1AbU50V.js";
2
+ import { a as Control, n as capitalize, v as ActionButton } from "./core-CJCEx18C.js";
3
3
  import { n as IconGithub, t as IconGoogle } from "./IconGoogle-8Nkx6yax.js";
4
4
  import { AlephaError, t } from "alepha";
5
5
  import { FormValidationError, useForm } from "alepha/react/form";
@@ -271,4 +271,4 @@ const leftSection = (name) => {
271
271
  //#endregion
272
272
  export { Login_exports as t };
273
273
 
274
- //# sourceMappingURL=Login-DSBqNsZc.js.map
274
+ //# sourceMappingURL=Login-CaMjUrDP.js.map