@alepha/ui 0.14.2 → 0.14.4
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.
- package/dist/admin/AdminAudits-DIrCCPk3.js.map +1 -1
- package/dist/admin/AdminNotifications-cIbywWKi.js.map +1 -1
- package/dist/admin/AdminParameters-D-q3Qmhv.js.map +1 -1
- package/dist/admin/AdminSessions-vOgkrQ2U.js.map +1 -1
- package/dist/admin/AdminUserAudits-CSsN1fIC.js.map +1 -1
- package/dist/admin/AdminUserCreate-B72nu-3W.js.map +1 -1
- package/dist/admin/AdminUserDetails-CKM2IEMr.js +475 -0
- package/dist/admin/AdminUserDetails-CKM2IEMr.js.map +1 -0
- package/dist/admin/{AdminUserDetails-z1y8kJeB.js → AdminUserDetails-Zib_B6Al.js} +1 -1
- package/dist/admin/{AdminUserLayout-DyQYacQQ.js → AdminUserLayout-BNBOEiAO.js} +1 -1
- package/dist/admin/AdminUserLayout-D7En9UBq.js +334 -0
- package/dist/admin/AdminUserLayout-D7En9UBq.js.map +1 -0
- package/dist/admin/AdminUserSessions-DEaGu6n6.js.map +1 -1
- package/dist/admin/{AdminUserSettings-CR7MxX_R.js → AdminUserSettings-Di73D7g2.js} +6 -5
- package/dist/admin/AdminUserSettings-Di73D7g2.js.map +1 -0
- package/dist/admin/AdminUserSettings-yI-JECf5.js +3 -0
- package/dist/admin/AdminUsers-BnGIRvmV.js.map +1 -1
- package/dist/admin/index.d.ts +10 -10
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +18 -18
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/index.js +4 -4
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +6 -5
- package/dist/core/index.js.map +1 -1
- package/package.json +11 -11
- package/src/admin/AdminRouter.ts +23 -20
- package/src/admin/MainRouter.ts +1 -1
- package/src/admin/components/audits/AdminAudits.tsx +2 -2
- package/src/admin/components/jobs/AdminJobs.tsx +2 -2
- package/src/admin/components/notifications/AdminNotifications.tsx +2 -2
- package/src/admin/components/parameters/AdminParameters.tsx +2 -2
- package/src/admin/components/sessions/AdminSessions.tsx +2 -2
- package/src/admin/components/shared/AdminResourceHeader.tsx +281 -0
- package/src/admin/components/shared/AdminResourceTabs.tsx +94 -0
- package/src/admin/components/shared/index.ts +10 -0
- package/src/admin/components/users/AdminUserAudits.tsx +2 -2
- package/src/admin/components/users/AdminUserCreate.tsx +2 -2
- package/src/admin/components/users/AdminUserDetails.tsx +337 -85
- package/src/admin/components/users/AdminUserLayout.tsx +164 -108
- package/src/admin/components/users/AdminUserSessions.tsx +2 -2
- package/src/admin/components/users/AdminUserSettings.tsx +10 -5
- package/src/admin/components/users/AdminUsers.tsx +6 -2
- package/src/auth/AuthRouter.ts +4 -4
- package/src/core/components/form/TypeForm.tsx +3 -2
- package/src/core/components/layout/AlephaMantineProvider.tsx +5 -1
- package/src/core/components/layout/Sidebar.tsx +9 -6
- package/dist/admin/AdminUserDetails-BCt8Su-4.js +0 -222
- package/dist/admin/AdminUserDetails-BCt8Su-4.js.map +0 -1
- package/dist/admin/AdminUserLayout-Ck0GLRE5.js +0 -151
- package/dist/admin/AdminUserLayout-Ck0GLRE5.js.map +0 -1
- package/dist/admin/AdminUserSettings-CE66UTIP.js +0 -3
- package/dist/admin/AdminUserSettings-CR7MxX_R.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminAudits-DIrCCPk3.js","names":[],"sources":["../../src/admin/components/audits/AdminAudits.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Stack, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AuditController, AuditEntity } from \"alepha/api/audits\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst getTypeColor = (type: string) => {\n switch (type) {\n case \"auth\":\n return \"blue\";\n case \"user\":\n return \"grape\";\n case \"security\":\n return \"red\";\n case \"system\":\n return \"orange\";\n case \"access\":\n return \"cyan\";\n case \"payment\":\n return \"green\";\n case \"order\":\n return \"teal\";\n default:\n return \"gray\";\n }\n};\n\nconst AdminAudits = (props: AdminAuditsProps) => {\n const client = useClient<AuditController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n resourceType: t.optional(t.text()),\n search: t.optional(t.text()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={20}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n cursor: item.userId ? \"pointer\" : \"default\",\n opacity: item.success ? 1 : 0.85,\n },\n onClick: () => {\n if (item.userId) {\n router.go(\"adminUserDetails\", {\n params: { userId: item.userId },\n });\n }\n },\n })}\n items={async (query) => {\n const response = await client.findAudits({\n query: {\n ...query,\n userRealm: props.userRealmName,\n },\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color={getTypeColor(item.type)}>\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n user: {\n label: \"User\",\n fit: true,\n value: (item) =>\n item.userId ? (\n <Tooltip\n label={\n <Stack gap={2}>\n <Text size=\"xs\">{item.userEmail || \"No email\"}</Text>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userRealm || \"default\"}\n </Text>\n </Stack>\n }\n >\n <Group gap={4}>\n <IconUser size={12} />\n <Text size=\"xs\" lineClamp={1} maw={100}>\n {item.userEmail?.split(\"@\")[0] || item.userId.slice(0, 8)}\n </Text>\n </Group>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n System\n </Text>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={`${item.resourceType}: ${item.resourceId}`}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminAudits;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminAudits-DIrCCPk3.js","names":[],"sources":["../../src/admin/components/audits/AdminAudits.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Stack, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconUser,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst getTypeColor = (type: string) => {\n switch (type) {\n case \"auth\":\n return \"blue\";\n case \"user\":\n return \"grape\";\n case \"security\":\n return \"red\";\n case \"system\":\n return \"orange\";\n case \"access\":\n return \"cyan\";\n case \"payment\":\n return \"green\";\n case \"order\":\n return \"teal\";\n default:\n return \"gray\";\n }\n};\n\nconst AdminAudits = (props: AdminAuditsProps) => {\n const client = useClient<AdminAuditController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n resourceType: t.optional(t.text()),\n search: t.optional(t.text()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={20}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n tableTrProps={(item) => ({\n style: {\n cursor: item.userId ? \"pointer\" : \"default\",\n opacity: item.success ? 1 : 0.85,\n },\n onClick: () => {\n if (item.userId) {\n router.go(\"adminUserDetails\", {\n params: { userId: item.userId },\n });\n }\n },\n })}\n items={async (query) => {\n const response = await client.findAudits({\n query: {\n ...query,\n userRealm: props.userRealmName,\n },\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color={getTypeColor(item.type)}>\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n user: {\n label: \"User\",\n fit: true,\n value: (item) =>\n item.userId ? (\n <Tooltip\n label={\n <Stack gap={2}>\n <Text size=\"xs\">{item.userEmail || \"No email\"}</Text>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userRealm || \"default\"}\n </Text>\n </Stack>\n }\n >\n <Group gap={4}>\n <IconUser size={12} />\n <Text size=\"xs\" lineClamp={1} maw={100}>\n {item.userEmail?.split(\"@\")[0] || item.userId.slice(0, 8)}\n </Text>\n </Group>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n System\n </Text>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={`${item.resourceType}: ${item.resourceId}`}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminAudits;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminNotifications-cIbywWKi.js","names":["filters"],"sources":["../../src/admin/components/notifications/AdminNotifications.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconClock,\n IconMail,\n IconMessage,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type {\n
|
|
1
|
+
{"version":3,"file":"AdminNotifications-cIbywWKi.js","names":["filters"],"sources":["../../src/admin/components/notifications/AdminNotifications.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertCircle,\n IconCheck,\n IconClock,\n IconMail,\n IconMessage,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type {\n AdminNotificationController,\n NotificationEntity,\n} from \"alepha/api/notifications\";\n\nconst AdminNotifications = () => {\n const client = useClient<AdminNotificationController>();\n const { l } = useI18n();\n\n const filters = t.object({\n type: t.optional(\n t.enum([\"email\", \"sms\"], {\n title: \"Type\",\n }),\n ),\n status: t.optional(\n t.enum([\"pending\", \"sent\", \"failed\"], {\n title: \"Status\",\n }),\n ),\n template: t.optional(\n t.string({\n title: \"Template\",\n }),\n ),\n contact: t.optional(\n t.string({\n title: \"Contact\",\n }),\n ),\n });\n\n const getStatus = (item: NotificationEntity) => {\n if (item.error) return \"failed\";\n if (item.sentAt) return \"sent\";\n return \"pending\";\n };\n\n const getStatusBadge = (item: NotificationEntity) => {\n const status = getStatus(item);\n switch (status) {\n case \"sent\":\n return (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color=\"green\"\n leftSection={<IconCheck size={12} />}\n >\n Sent\n </Badge>\n );\n case \"failed\":\n return (\n <Tooltip label={item.error?.message} multiline maw={300}>\n <Badge\n size=\"sm\"\n variant=\"light\"\n color=\"red\"\n leftSection={<IconAlertCircle size={12} />}\n >\n Failed\n </Badge>\n </Tooltip>\n );\n default:\n return (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color=\"yellow\"\n leftSection={<IconClock size={12} />}\n >\n Pending\n </Badge>\n );\n }\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<NotificationEntity, typeof filters>\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(_key, _value, form) => {\n return form.submit();\n }}\n filters={filters}\n tableTrProps={(item) => {\n const status = getStatus(item);\n if (status === \"failed\") {\n return {\n bg: \"var(--mantine-color-red-light)\",\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findNotifications({\n query: filters,\n });\n\n return response as Page<NotificationEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"outline\"\n leftSection={\n item.type === \"email\" ? (\n <IconMail size={12} />\n ) : (\n <IconMessage size={12} />\n )\n }\n >\n {item.type.toUpperCase()}\n </Badge>\n ),\n },\n template: {\n label: \"Template\",\n value: (item) => (\n <Text size=\"sm\" fw={500}>\n {item.template}\n </Text>\n ),\n },\n contact: {\n label: \"Contact\",\n value: (item) => (\n <Text size=\"sm\" ff=\"monospace\">\n {item.contact}\n </Text>\n ),\n },\n category: {\n label: \"Category\",\n fit: true,\n value: (item) =>\n item.category ? (\n <Badge size=\"xs\" variant=\"light\">\n {item.category}\n </Badge>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n status: {\n label: \"Status\",\n fit: true,\n value: (item) => getStatusBadge(item),\n },\n sentAt: {\n label: \"Sent\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {item.sentAt ? l(item.sentAt, { date: \"fromNow\" }) : \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminNotifications;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminParameters-D-q3Qmhv.js","names":["ParameterTree","ParameterDetails","ParameterHistory"],"sources":["../../src/admin/components/parameters/types.ts","../../src/admin/components/parameters/ParameterDetails.tsx","../../src/admin/components/parameters/ParameterHistory.tsx","../../src/admin/components/parameters/ParameterTree.tsx","../../src/admin/components/parameters/AdminParameters.tsx"],"sourcesContent":["import type { Parameter } from \"alepha/api/parameters\";\n\nexport interface ConfigValue {\n current?: Parameter;\n next?: Parameter;\n /** Default value from the registered $config primitive */\n defaultValue?: unknown;\n /** Current in-memory value (may be default if never saved) */\n currentValue?: unknown;\n /** TypeBox/JSON schema for the configuration (as JSON from API) */\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 { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { Flex, Text, TypeForm } from \"@alepha/ui\";\nimport {\n Badge,\n Box,\n Card,\n Code,\n Group,\n Loader,\n ScrollArea,\n Stack,\n} from \"@mantine/core\";\nimport { IconClock, IconSettings } from \"@tabler/icons-react\";\nimport { jsonSchemaToTypeBox, type TObject } from \"alepha\";\nimport { useMemo } from \"react\";\nimport { type ConfigValue, formatJson } from \"./types.ts\";\n\nexport interface ParameterDetailsProps {\n selectedConfig: string | null;\n configValue: ConfigValue | null;\n loading: boolean;\n}\n\nconst ParameterDetails = ({\n selectedConfig,\n configValue,\n loading,\n}: ParameterDetailsProps) => {\n const { l } = useI18n();\n\n // Get the current value to display (from saved version or default)\n const currentContent = useMemo(() => {\n if (configValue?.current?.content) {\n return configValue.current.content;\n }\n if (configValue?.currentValue !== undefined) {\n return configValue.currentValue;\n }\n return null;\n }, [configValue]);\n\n // Convert JSON Schema from API to TypeBox schema\n const schemaForForm = useMemo(() => {\n if (!configValue?.schema) {\n return { type: \"object\", properties: {} } as unknown as TObject;\n }\n return jsonSchemaToTypeBox(configValue.schema) as TObject;\n }, [configValue?.schema]);\n\n const form = useForm(\n {\n schema: schemaForForm,\n initialValues: (currentContent ?? {}) as Record<string, unknown>,\n handler: async () => {\n // Read-only for now\n },\n },\n [selectedConfig, schemaForForm, currentContent],\n );\n\n // Check if we have a valid schema with properties\n const hasValidSchema = useMemo(() => {\n const schema = configValue?.schema;\n return (\n schema &&\n typeof schema === \"object\" &&\n \"properties\" in schema &&\n Object.keys(schema.properties ?? {}).length > 0\n );\n }, [configValue?.schema]);\n\n if (!selectedConfig) {\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Stack align=\"center\" gap=\"xs\">\n <IconSettings\n size={32}\n stroke={1.5}\n color=\"var(--mantine-color-dimmed)\"\n />\n <Text c=\"dimmed\" size=\"sm\">\n Select a parameter to view its value\n </Text>\n </Stack>\n </Flex>\n </Card>\n );\n }\n\n if (loading) {\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Loader size=\"sm\" />\n </Flex>\n </Card>\n );\n }\n\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Stack gap=\"md\" h=\"100%\">\n <Group justify=\"space-between\">\n <Stack gap={2}>\n <Text size=\"sm\" fw={500}>\n {selectedConfig}\n </Text>\n {configValue?.current && (\n <Group gap=\"xs\">\n <Badge size=\"xs\" color=\"green\" variant=\"light\">\n v{configValue.current.version}\n </Badge>\n {configValue.next && (\n <Badge size=\"xs\" color=\"blue\" variant=\"light\">\n Next: v{configValue.next.version}\n </Badge>\n )}\n </Group>\n )}\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Badge size=\"xs\" color=\"yellow\" variant=\"light\">\n Default\n </Badge>\n )}\n </Stack>\n </Group>\n\n <ScrollArea flex={1} offsetScrollbars>\n {currentContent !== null ? (\n <Stack gap=\"md\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Current Value\n </Text>\n {hasValidSchema ? (\n <TypeForm\n form={form}\n columns={1}\n skipSubmitButton\n skipFormElement\n />\n ) : (\n <Code block style={{ whiteSpace: \"pre-wrap\" }}>\n {formatJson(currentContent)}\n </Code>\n )}\n </Box>\n\n {configValue?.current?.changeDescription && (\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Change Description\n </Text>\n <Text size=\"sm\">{configValue.current.changeDescription}</Text>\n </Box>\n )}\n\n {configValue?.current && (\n <Group gap=\"xl\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated\n </Text>\n <Text size=\"sm\">\n {l(configValue.current.updatedAt, { date: \"fromNow\" })}\n </Text>\n </Box>\n {configValue.current.creatorName && (\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated By\n </Text>\n <Text size=\"sm\">{configValue.current.creatorName}</Text>\n </Box>\n )}\n </Group>\n )}\n\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Text size=\"xs\" c=\"dimmed\">\n This configuration is using its default value. No versions\n have been saved to the database yet.\n </Text>\n )}\n\n {configValue?.next && (\n <Card withBorder bg=\"blue.0\" p=\"sm\">\n <Stack gap=\"xs\">\n <Group gap=\"xs\">\n <IconClock\n size={14}\n color=\"var(--mantine-color-blue-6)\"\n />\n <Text size=\"xs\" fw={500} c=\"blue.7\">\n Scheduled Update (v{configValue.next.version})\n </Text>\n </Group>\n <Text size=\"xs\" c=\"dimmed\">\n Activates{\" \"}\n {l(configValue.next.activationDate, {\n date: \"fromNow\",\n })}\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }} fz=\"xs\">\n {formatJson(configValue.next.content)}\n </Code>\n </Stack>\n </Card>\n )}\n </Stack>\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 </ScrollArea>\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterDetails;\n","import { useI18n } from \"@alepha/react/i18n\";\nimport { ActionButton, Flex, Text } from \"@alepha/ui\";\nimport {\n Badge,\n Card,\n Group,\n Loader,\n ScrollArea,\n Stack,\n Timeline,\n} from \"@mantine/core\";\nimport { IconHistory } from \"@tabler/icons-react\";\nimport type { Parameter } from \"alepha/api/parameters\";\nimport { getStatusColor } from \"./types.ts\";\n\nexport interface ParameterHistoryProps {\n selectedConfig: string | null;\n history: Parameter[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\nconst ParameterHistory = ({\n selectedConfig,\n history,\n loading,\n onRollback,\n}: ParameterHistoryProps) => {\n const { l } = useI18n();\n\n const renderContent = () => {\n if (!selectedConfig) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n Select a parameter\n </Text>\n </Flex>\n );\n }\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader size=\"sm\" />\n </Flex>\n );\n }\n\n if (history.length === 0) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n No history\n </Text>\n </Flex>\n );\n }\n\n return (\n <ScrollArea flex={1} offsetScrollbars>\n <Timeline\n active={history.findIndex((h) => h.status === \"current\")}\n bulletSize={24}\n lineWidth={2}\n >\n {history.map((version) => (\n <Timeline.Item\n key={version.id}\n bullet={\n <Text size=\"xs\" fw={500}>\n {version.version}\n </Text>\n }\n title={\n <Group 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 </Group>\n }\n >\n <Stack gap={4} mt={4}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(version.createdAt, { date: \"fromNow\" })}\n </Text>\n {version.changeDescription && (\n <Text size=\"xs\" lineClamp={2}>\n {version.changeDescription}\n </Text>\n )}\n {version.creatorName && (\n <Text size=\"xs\" c=\"dimmed\">\n by {version.creatorName}\n </Text>\n )}\n {version.migrationLog && (\n <Badge size=\"xs\" variant=\"outline\" color=\"orange\">\n Schema Changed\n </Badge>\n )}\n {version.status === \"expired\" && (\n <ActionButton\n size=\"compact-xs\"\n variant=\"subtle\"\n onClick={() => onRollback(version.version)}\n >\n Rollback to this version\n </ActionButton>\n )}\n </Stack>\n </Timeline.Item>\n ))}\n </Timeline>\n </ScrollArea>\n );\n };\n\n return (\n <Card\n withBorder\n w={300}\n h=\"100%\"\n style={{ flexShrink: 0, overflow: \"hidden\" }}\n >\n <Stack gap=\"xs\" h=\"100%\">\n <Group gap=\"xs\">\n <IconHistory size={16} color=\"var(--mantine-color-dimmed)\" />\n <Text size=\"sm\" fw={500}>\n Version History\n </Text>\n </Group>\n {renderContent()}\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterHistory;\n","import { ActionButton, Text } from \"@alepha/ui\";\nimport {\n Box,\n Card,\n Group,\n ScrollArea,\n Stack,\n Tooltip,\n Tree,\n type TreeNodeData,\n useTree,\n} from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronRight,\n IconFolder,\n IconFolderOpen,\n IconRefresh,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport type { ConfigTreeNode } from \"alepha/api/parameters\";\nimport { type HTMLAttributes, useMemo } from \"react\";\n\nexport interface ParameterTreeProps {\n treeData: ConfigTreeNode[];\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\nconst ParameterTree = ({\n treeData,\n selectedConfig,\n onSelect,\n onRefresh,\n}: ParameterTreeProps) => {\n const tree = useTree({\n initialExpandedState: {},\n });\n\n const mantineTreeData = useMemo((): TreeNodeData[] => {\n const convert = (nodes: ConfigTreeNode[]): TreeNodeData[] => {\n return nodes.map((node) => ({\n value: node.path,\n label: node.name,\n children: node.children.length > 0 ? convert(node.children) : undefined,\n }));\n };\n return convert(treeData);\n }, [treeData]);\n\n const renderNode = ({\n node,\n expanded,\n hasChildren,\n elementProps,\n }: {\n node: TreeNodeData;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: HTMLAttributes<HTMLDivElement>;\n }) => {\n const isLeaf = !hasChildren;\n const isSelected = selectedConfig === node.value;\n\n return (\n <Group\n gap=\"xs\"\n wrap=\"nowrap\"\n {...elementProps}\n onClick={(e) => {\n elementProps.onClick?.(e);\n if (isLeaf) {\n onSelect(node.value);\n }\n }}\n style={{\n ...elementProps.style,\n cursor: isLeaf ? \"pointer\" : \"default\",\n backgroundColor: isSelected\n ? \"var(--mantine-color-blue-light)\"\n : undefined,\n borderRadius: \"var(--mantine-radius-sm)\",\n paddingTop: 4,\n paddingBottom: 4,\n paddingRight: 8,\n }}\n >\n {hasChildren ? (\n <>\n {expanded ? (\n <IconChevronDown size={14} color=\"var(--mantine-color-dimmed)\" />\n ) : (\n <IconChevronRight size={14} color=\"var(--mantine-color-dimmed)\" />\n )}\n {expanded ? (\n <IconFolderOpen size={16} color=\"var(--mantine-color-blue-6)\" />\n ) : (\n <IconFolder size={16} color=\"var(--mantine-color-blue-6)\" />\n )}\n </>\n ) : (\n <>\n <Box w={14} />\n <IconSettings size={16} color=\"var(--mantine-color-gray-6)\" />\n </>\n )}\n <Text size=\"sm\" fw={isSelected ? 500 : 400}>\n {node.label}\n </Text>\n </Group>\n );\n };\n\n return (\n <Card withBorder w={280} h=\"100%\" style={{ flexShrink: 0 }}>\n <Stack gap=\"xs\" h=\"100%\">\n <Group justify=\"space-between\">\n <Text size=\"sm\" fw={500}>\n Parameters\n </Text>\n <Tooltip label=\"Refresh\">\n <ActionButton\n variant=\"subtle\"\n size=\"compact-xs\"\n onClick={onRefresh}\n >\n <IconRefresh size={14} />\n </ActionButton>\n </Tooltip>\n </Group>\n <ScrollArea flex={1} offsetScrollbars>\n <Tree\n data={mantineTreeData}\n tree={tree}\n levelOffset={20}\n expandOnClick\n renderNode={renderNode}\n />\n </ScrollArea>\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterTree;\n","import { useClient } from \"@alepha/react\";\nimport { Flex, Text } from \"@alepha/ui\";\nimport { Loader, Stack } from \"@mantine/core\";\nimport { IconSettings } from \"@tabler/icons-react\";\nimport type {\n ConfigController,\n ConfigTreeNode,\n Parameter,\n} from \"alepha/api/parameters\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport ParameterDetails from \"./ParameterDetails.tsx\";\nimport ParameterHistory from \"./ParameterHistory.tsx\";\nimport ParameterTree from \"./ParameterTree.tsx\";\nimport type { ConfigValue } from \"./types.ts\";\n\nconst AdminParameters = () => {\n const client = useClient<ConfigController>();\n\n // State\n const [treeData, setTreeData] = useState<ConfigTreeNode[]>([]);\n const [selectedConfig, setSelectedConfig] = useState<string | null>(null);\n const [configValue, setConfigValue] = useState<ConfigValue | null>(null);\n const [history, setHistory] = useState<Parameter[]>([]);\n const [loading, setLoading] = useState(true);\n const [loadingConfig, setLoadingConfig] = useState(false);\n const [loadingHistory, setLoadingHistory] = useState(false);\n\n // Load tree data\n const loadTree = useCallback(async () => {\n try {\n const tree = await client.getConfigTree({});\n setTreeData(tree as ConfigTreeNode[]);\n } finally {\n setLoading(false);\n }\n }, []);\n\n // Load config value and history when selection changes\n const loadConfigDetails = useCallback(async (name: string) => {\n setLoadingConfig(true);\n setLoadingHistory(true);\n\n try {\n const [current, historyData] = await Promise.all([\n client.getCurrent({ params: { name } }),\n client.getHistory({ params: { name } }),\n ]);\n setConfigValue(current);\n setHistory(historyData.versions);\n } finally {\n setLoadingConfig(false);\n setLoadingHistory(false);\n }\n }, []);\n\n // Initial load\n useEffect(() => {\n loadTree();\n }, [loadTree]);\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 = async (version: number) => {\n if (!selectedConfig) return;\n\n await client.rollback({\n params: { name: selectedConfig },\n body: { targetVersion: version },\n });\n\n // Reload details\n await loadConfigDetails(selectedConfig);\n };\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader />\n </Flex>\n );\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 <Stack 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 $config primitive to manage dynamic\n application settings. Parameters will appear here once created.\n </Text>\n </Stack>\n </Flex>\n );\n }\n\n return (\n <Flex flex={1} gap={\"xs\"} h=\"100%\">\n <ParameterTree\n treeData={treeData}\n selectedConfig={selectedConfig}\n onSelect={setSelectedConfig}\n onRefresh={loadTree}\n />\n\n <ParameterDetails\n selectedConfig={selectedConfig}\n configValue={configValue}\n loading={loadingConfig}\n />\n\n <ParameterHistory\n selectedConfig={selectedConfig}\n history={history}\n loading={loadingHistory}\n onRollback={handleRollback}\n />\n </Flex>\n );\n};\n\nexport default AdminParameters;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminParameters-D-q3Qmhv.js","names":["ParameterTree","ParameterDetails","ParameterHistory"],"sources":["../../src/admin/components/parameters/types.ts","../../src/admin/components/parameters/ParameterDetails.tsx","../../src/admin/components/parameters/ParameterHistory.tsx","../../src/admin/components/parameters/ParameterTree.tsx","../../src/admin/components/parameters/AdminParameters.tsx"],"sourcesContent":["import type { Parameter } from \"alepha/api/parameters\";\n\nexport interface ConfigValue {\n current?: Parameter;\n next?: Parameter;\n /** Default value from the registered $config primitive */\n defaultValue?: unknown;\n /** Current in-memory value (may be default if never saved) */\n currentValue?: unknown;\n /** TypeBox/JSON schema for the configuration (as JSON from API) */\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 { useForm } from \"@alepha/react/form\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { Flex, Text, TypeForm } from \"@alepha/ui\";\nimport {\n Badge,\n Box,\n Card,\n Code,\n Group,\n Loader,\n ScrollArea,\n Stack,\n} from \"@mantine/core\";\nimport { IconClock, IconSettings } from \"@tabler/icons-react\";\nimport { jsonSchemaToTypeBox, type TObject } from \"alepha\";\nimport { useMemo } from \"react\";\nimport { type ConfigValue, formatJson } from \"./types.ts\";\n\nexport interface ParameterDetailsProps {\n selectedConfig: string | null;\n configValue: ConfigValue | null;\n loading: boolean;\n}\n\nconst ParameterDetails = ({\n selectedConfig,\n configValue,\n loading,\n}: ParameterDetailsProps) => {\n const { l } = useI18n();\n\n // Get the current value to display (from saved version or default)\n const currentContent = useMemo(() => {\n if (configValue?.current?.content) {\n return configValue.current.content;\n }\n if (configValue?.currentValue !== undefined) {\n return configValue.currentValue;\n }\n return null;\n }, [configValue]);\n\n // Convert JSON Schema from API to TypeBox schema\n const schemaForForm = useMemo(() => {\n if (!configValue?.schema) {\n return { type: \"object\", properties: {} } as unknown as TObject;\n }\n return jsonSchemaToTypeBox(configValue.schema) as TObject;\n }, [configValue?.schema]);\n\n const form = useForm(\n {\n schema: schemaForForm,\n initialValues: (currentContent ?? {}) as Record<string, unknown>,\n handler: async () => {\n // Read-only for now\n },\n },\n [selectedConfig, schemaForForm, currentContent],\n );\n\n // Check if we have a valid schema with properties\n const hasValidSchema = useMemo(() => {\n const schema = configValue?.schema;\n return (\n schema &&\n typeof schema === \"object\" &&\n \"properties\" in schema &&\n Object.keys(schema.properties ?? {}).length > 0\n );\n }, [configValue?.schema]);\n\n if (!selectedConfig) {\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Stack align=\"center\" gap=\"xs\">\n <IconSettings\n size={32}\n stroke={1.5}\n color=\"var(--mantine-color-dimmed)\"\n />\n <Text c=\"dimmed\" size=\"sm\">\n Select a parameter to view its value\n </Text>\n </Stack>\n </Flex>\n </Card>\n );\n }\n\n if (loading) {\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Flex flex={1} justify=\"center\" align=\"center\" h=\"100%\">\n <Loader size=\"sm\" />\n </Flex>\n </Card>\n );\n }\n\n return (\n <Card withBorder flex={1} h=\"100%\" style={{ overflow: \"hidden\" }}>\n <Stack gap=\"md\" h=\"100%\">\n <Group justify=\"space-between\">\n <Stack gap={2}>\n <Text size=\"sm\" fw={500}>\n {selectedConfig}\n </Text>\n {configValue?.current && (\n <Group gap=\"xs\">\n <Badge size=\"xs\" color=\"green\" variant=\"light\">\n v{configValue.current.version}\n </Badge>\n {configValue.next && (\n <Badge size=\"xs\" color=\"blue\" variant=\"light\">\n Next: v{configValue.next.version}\n </Badge>\n )}\n </Group>\n )}\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Badge size=\"xs\" color=\"yellow\" variant=\"light\">\n Default\n </Badge>\n )}\n </Stack>\n </Group>\n\n <ScrollArea flex={1} offsetScrollbars>\n {currentContent !== null ? (\n <Stack gap=\"md\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Current Value\n </Text>\n {hasValidSchema ? (\n <TypeForm\n form={form}\n columns={1}\n skipSubmitButton\n skipFormElement\n />\n ) : (\n <Code block style={{ whiteSpace: \"pre-wrap\" }}>\n {formatJson(currentContent)}\n </Code>\n )}\n </Box>\n\n {configValue?.current?.changeDescription && (\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={4}>\n Change Description\n </Text>\n <Text size=\"sm\">{configValue.current.changeDescription}</Text>\n </Box>\n )}\n\n {configValue?.current && (\n <Group gap=\"xl\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated\n </Text>\n <Text size=\"sm\">\n {l(configValue.current.updatedAt, { date: \"fromNow\" })}\n </Text>\n </Box>\n {configValue.current.creatorName && (\n <Box>\n <Text size=\"xs\" c=\"dimmed\" mb={2}>\n Updated By\n </Text>\n <Text size=\"sm\">{configValue.current.creatorName}</Text>\n </Box>\n )}\n </Group>\n )}\n\n {!configValue?.current &&\n configValue?.currentValue !== undefined && (\n <Text size=\"xs\" c=\"dimmed\">\n This configuration is using its default value. No versions\n have been saved to the database yet.\n </Text>\n )}\n\n {configValue?.next && (\n <Card withBorder bg=\"blue.0\" p=\"sm\">\n <Stack gap=\"xs\">\n <Group gap=\"xs\">\n <IconClock\n size={14}\n color=\"var(--mantine-color-blue-6)\"\n />\n <Text size=\"xs\" fw={500} c=\"blue.7\">\n Scheduled Update (v{configValue.next.version})\n </Text>\n </Group>\n <Text size=\"xs\" c=\"dimmed\">\n Activates{\" \"}\n {l(configValue.next.activationDate, {\n date: \"fromNow\",\n })}\n </Text>\n <Code block style={{ whiteSpace: \"pre-wrap\" }} fz=\"xs\">\n {formatJson(configValue.next.content)}\n </Code>\n </Stack>\n </Card>\n )}\n </Stack>\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 </ScrollArea>\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterDetails;\n","import { useI18n } from \"@alepha/react/i18n\";\nimport { ActionButton, Flex, Text } from \"@alepha/ui\";\nimport {\n Badge,\n Card,\n Group,\n Loader,\n ScrollArea,\n Stack,\n Timeline,\n} from \"@mantine/core\";\nimport { IconHistory } from \"@tabler/icons-react\";\nimport type { Parameter } from \"alepha/api/parameters\";\nimport { getStatusColor } from \"./types.ts\";\n\nexport interface ParameterHistoryProps {\n selectedConfig: string | null;\n history: Parameter[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\nconst ParameterHistory = ({\n selectedConfig,\n history,\n loading,\n onRollback,\n}: ParameterHistoryProps) => {\n const { l } = useI18n();\n\n const renderContent = () => {\n if (!selectedConfig) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n Select a parameter\n </Text>\n </Flex>\n );\n }\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader size=\"sm\" />\n </Flex>\n );\n }\n\n if (history.length === 0) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Text c=\"dimmed\" size=\"xs\">\n No history\n </Text>\n </Flex>\n );\n }\n\n return (\n <ScrollArea flex={1} offsetScrollbars>\n <Timeline\n active={history.findIndex((h) => h.status === \"current\")}\n bulletSize={24}\n lineWidth={2}\n >\n {history.map((version) => (\n <Timeline.Item\n key={version.id}\n bullet={\n <Text size=\"xs\" fw={500}>\n {version.version}\n </Text>\n }\n title={\n <Group 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 </Group>\n }\n >\n <Stack gap={4} mt={4}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(version.createdAt, { date: \"fromNow\" })}\n </Text>\n {version.changeDescription && (\n <Text size=\"xs\" lineClamp={2}>\n {version.changeDescription}\n </Text>\n )}\n {version.creatorName && (\n <Text size=\"xs\" c=\"dimmed\">\n by {version.creatorName}\n </Text>\n )}\n {version.migrationLog && (\n <Badge size=\"xs\" variant=\"outline\" color=\"orange\">\n Schema Changed\n </Badge>\n )}\n {version.status === \"expired\" && (\n <ActionButton\n size=\"compact-xs\"\n variant=\"subtle\"\n onClick={() => onRollback(version.version)}\n >\n Rollback to this version\n </ActionButton>\n )}\n </Stack>\n </Timeline.Item>\n ))}\n </Timeline>\n </ScrollArea>\n );\n };\n\n return (\n <Card\n withBorder\n w={300}\n h=\"100%\"\n style={{ flexShrink: 0, overflow: \"hidden\" }}\n >\n <Stack gap=\"xs\" h=\"100%\">\n <Group gap=\"xs\">\n <IconHistory size={16} color=\"var(--mantine-color-dimmed)\" />\n <Text size=\"sm\" fw={500}>\n Version History\n </Text>\n </Group>\n {renderContent()}\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterHistory;\n","import { ActionButton, Text } from \"@alepha/ui\";\nimport {\n Box,\n Card,\n Group,\n ScrollArea,\n Stack,\n Tooltip,\n Tree,\n type TreeNodeData,\n useTree,\n} from \"@mantine/core\";\nimport {\n IconChevronDown,\n IconChevronRight,\n IconFolder,\n IconFolderOpen,\n IconRefresh,\n IconSettings,\n} from \"@tabler/icons-react\";\nimport type { ConfigTreeNode } from \"alepha/api/parameters\";\nimport { type HTMLAttributes, useMemo } from \"react\";\n\nexport interface ParameterTreeProps {\n treeData: ConfigTreeNode[];\n selectedConfig: string | null;\n onSelect: (name: string) => void;\n onRefresh: () => void;\n}\n\nconst ParameterTree = ({\n treeData,\n selectedConfig,\n onSelect,\n onRefresh,\n}: ParameterTreeProps) => {\n const tree = useTree({\n initialExpandedState: {},\n });\n\n const mantineTreeData = useMemo((): TreeNodeData[] => {\n const convert = (nodes: ConfigTreeNode[]): TreeNodeData[] => {\n return nodes.map((node) => ({\n value: node.path,\n label: node.name,\n children: node.children.length > 0 ? convert(node.children) : undefined,\n }));\n };\n return convert(treeData);\n }, [treeData]);\n\n const renderNode = ({\n node,\n expanded,\n hasChildren,\n elementProps,\n }: {\n node: TreeNodeData;\n expanded: boolean;\n hasChildren: boolean;\n elementProps: HTMLAttributes<HTMLDivElement>;\n }) => {\n const isLeaf = !hasChildren;\n const isSelected = selectedConfig === node.value;\n\n return (\n <Group\n gap=\"xs\"\n wrap=\"nowrap\"\n {...elementProps}\n onClick={(e) => {\n elementProps.onClick?.(e);\n if (isLeaf) {\n onSelect(node.value);\n }\n }}\n style={{\n ...elementProps.style,\n cursor: isLeaf ? \"pointer\" : \"default\",\n backgroundColor: isSelected\n ? \"var(--mantine-color-blue-light)\"\n : undefined,\n borderRadius: \"var(--mantine-radius-sm)\",\n paddingTop: 4,\n paddingBottom: 4,\n paddingRight: 8,\n }}\n >\n {hasChildren ? (\n <>\n {expanded ? (\n <IconChevronDown size={14} color=\"var(--mantine-color-dimmed)\" />\n ) : (\n <IconChevronRight size={14} color=\"var(--mantine-color-dimmed)\" />\n )}\n {expanded ? (\n <IconFolderOpen size={16} color=\"var(--mantine-color-blue-6)\" />\n ) : (\n <IconFolder size={16} color=\"var(--mantine-color-blue-6)\" />\n )}\n </>\n ) : (\n <>\n <Box w={14} />\n <IconSettings size={16} color=\"var(--mantine-color-gray-6)\" />\n </>\n )}\n <Text size=\"sm\" fw={isSelected ? 500 : 400}>\n {node.label}\n </Text>\n </Group>\n );\n };\n\n return (\n <Card withBorder w={280} h=\"100%\" style={{ flexShrink: 0 }}>\n <Stack gap=\"xs\" h=\"100%\">\n <Group justify=\"space-between\">\n <Text size=\"sm\" fw={500}>\n Parameters\n </Text>\n <Tooltip label=\"Refresh\">\n <ActionButton\n variant=\"subtle\"\n size=\"compact-xs\"\n onClick={onRefresh}\n >\n <IconRefresh size={14} />\n </ActionButton>\n </Tooltip>\n </Group>\n <ScrollArea flex={1} offsetScrollbars>\n <Tree\n data={mantineTreeData}\n tree={tree}\n levelOffset={20}\n expandOnClick\n renderNode={renderNode}\n />\n </ScrollArea>\n </Stack>\n </Card>\n );\n};\n\nexport default ParameterTree;\n","import { useClient } from \"@alepha/react\";\nimport { Flex, Text } from \"@alepha/ui\";\nimport { Loader, Stack } from \"@mantine/core\";\nimport { IconSettings } from \"@tabler/icons-react\";\nimport type {\n AdminConfigController,\n ConfigTreeNode,\n Parameter,\n} from \"alepha/api/parameters\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport ParameterDetails from \"./ParameterDetails.tsx\";\nimport ParameterHistory from \"./ParameterHistory.tsx\";\nimport ParameterTree from \"./ParameterTree.tsx\";\nimport type { ConfigValue } from \"./types.ts\";\n\nconst AdminParameters = () => {\n const client = useClient<AdminConfigController>();\n\n // State\n const [treeData, setTreeData] = useState<ConfigTreeNode[]>([]);\n const [selectedConfig, setSelectedConfig] = useState<string | null>(null);\n const [configValue, setConfigValue] = useState<ConfigValue | null>(null);\n const [history, setHistory] = useState<Parameter[]>([]);\n const [loading, setLoading] = useState(true);\n const [loadingConfig, setLoadingConfig] = useState(false);\n const [loadingHistory, setLoadingHistory] = useState(false);\n\n // Load tree data\n const loadTree = useCallback(async () => {\n try {\n const tree = await client.getConfigTree({});\n setTreeData(tree as ConfigTreeNode[]);\n } finally {\n setLoading(false);\n }\n }, []);\n\n // Load config value and history when selection changes\n const loadConfigDetails = useCallback(async (name: string) => {\n setLoadingConfig(true);\n setLoadingHistory(true);\n\n try {\n const [current, historyData] = await Promise.all([\n client.getCurrent({ params: { name } }),\n client.getHistory({ params: { name } }),\n ]);\n setConfigValue(current);\n setHistory(historyData.versions);\n } finally {\n setLoadingConfig(false);\n setLoadingHistory(false);\n }\n }, []);\n\n // Initial load\n useEffect(() => {\n loadTree();\n }, [loadTree]);\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 = async (version: number) => {\n if (!selectedConfig) return;\n\n await client.rollback({\n params: { name: selectedConfig },\n body: { targetVersion: version },\n });\n\n // Reload details\n await loadConfigDetails(selectedConfig);\n };\n\n if (loading) {\n return (\n <Flex flex={1} justify=\"center\" align=\"center\">\n <Loader />\n </Flex>\n );\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 <Stack 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 $config primitive to manage dynamic\n application settings. Parameters will appear here once created.\n </Text>\n </Stack>\n </Flex>\n );\n }\n\n return (\n <Flex flex={1} gap={\"xs\"} h=\"100%\">\n <ParameterTree\n treeData={treeData}\n selectedConfig={selectedConfig}\n onSelect={setSelectedConfig}\n onRefresh={loadTree}\n />\n\n <ParameterDetails\n selectedConfig={selectedConfig}\n configValue={configValue}\n loading={loadingConfig}\n />\n\n <ParameterHistory\n selectedConfig={selectedConfig}\n history={history}\n loading={loadingHistory}\n onRollback={handleRollback}\n />\n </Flex>\n );\n};\n\nexport default AdminParameters;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSessions-vOgkrQ2U.js","names":["filters"],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group } 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
|
|
1
|
+
{"version":3,"file":"AdminSessions-vOgkrQ2U.js","names":["filters"],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group } 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 { useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n });\n\n const getDeviceIcon = (device?: string) => {\n switch (device) {\n case \"MOBILE\":\n return <IconDeviceMobile size={14} />;\n case \"TABLET\":\n return <IconDeviceTablet size={14} />;\n default:\n return <IconDeviceDesktop size={14} />;\n }\n };\n\n const isExpired = (expiresAt: Date | string) => {\n return new Date(expiresAt) < new Date();\n };\n\n const handleDelete = async (sessionId: string) => {\n await client.deleteSession({\n params: { id: sessionId },\n query: { userRealmName: props.userRealmName },\n });\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<SessionEntity, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"userId\") {\n return form.submit();\n }\n }}\n filters={filters}\n tableTrProps={(item) => {\n if (isExpired(item.expiresAt)) {\n return {\n opacity: 0.5,\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n\n return response as Page<SessionEntity>;\n }}\n columns={{\n userId: {\n label: \"User\",\n value: (item) => (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={router.path(\"adminUserDetails\", {\n params: { userId: item.userId },\n })}\n >\n <Text size=\"xs\" ff=\"monospace\">\n {item.userId.slice(0, 8)}...\n </Text>\n </ActionButton>\n ),\n },\n userAgent: {\n label: \"Device\",\n fit: true,\n value: (item) => (\n <Group gap={4}>\n {item.userAgent ? (\n <>\n <Badge\n size=\"xs\"\n variant=\"light\"\n leftSection={getDeviceIcon(item.userAgent.device)}\n >\n {item.userAgent.device}\n </Badge>\n <Text size=\"xs\" c=\"dimmed\">\n {item.userAgent.browser} / {item.userAgent.os}\n </Text>\n </>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n )}\n </Group>\n ),\n },\n ip: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" ff=\"monospace\" c=\"dimmed\">\n {item.ip || \"-\"}\n </Text>\n ),\n },\n expiresAt: {\n label: \"Status\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={isExpired(item.expiresAt) ? \"red\" : \"green\"}\n >\n {isExpired(item.expiresAt) ? \"Expired\" : \"Active\"}\n </Badge>\n ),\n },\n createdAt: {\n label: \"Created\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n actions: {\n label: \"\",\n fit: true,\n value: (item) => (\n <ActionButton\n size=\"xs\"\n variant=\"subtle\"\n color=\"red\"\n onClick={() => handleDelete(item.id)}\n >\n <IconTrash size={14} />\n </ActionButton>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminSessions;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserAudits-CSsN1fIC.js","names":[],"sources":["../../src/admin/components/users/AdminUserAudits.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouterState } from \"@alepha/react/router\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type {
|
|
1
|
+
{"version":3,"file":"AdminUserAudits-CSsN1fIC.js","names":[],"sources":["../../src/admin/components/users/AdminUserAudits.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useI18n } from \"@alepha/react/i18n\";\nimport { useRouterState } from \"@alepha/react/router\";\nimport { DataTable, Flex, Text } from \"@alepha/ui\";\nimport { Badge, Group, Tooltip } from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCheck,\n IconInfoCircle,\n IconX,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport type { AdminAuditController, AuditEntity } from \"alepha/api/audits\";\n\nexport interface AdminUserAuditsProps {\n userRealmName?: string;\n}\n\nconst getSeverityColor = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return \"red\";\n case \"warning\":\n return \"yellow\";\n default:\n return \"blue\";\n }\n};\n\nconst getSeverityIcon = (severity: string) => {\n switch (severity) {\n case \"critical\":\n return <IconAlertTriangle size={12} />;\n case \"warning\":\n return <IconAlertTriangle size={12} />;\n default:\n return <IconInfoCircle size={12} />;\n }\n};\n\nconst AdminUserAudits = (_props: AdminUserAuditsProps) => {\n const state = useRouterState();\n const client = useClient<AdminAuditController>();\n const { l } = useI18n();\n const userId = state.params.userId as string;\n\n const filters = t.object({\n type: t.optional(t.text()),\n action: t.optional(t.text()),\n severity: t.optional(t.enum([\"info\", \"warning\", \"critical\"])),\n success: t.optional(t.boolean()),\n from: t.optional(t.datetime()),\n to: t.optional(t.datetime()),\n });\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<AuditEntity, typeof filters>\n submitOnInit\n defaultSize={15}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 4,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n striped: false,\n highlightOnHover: true,\n }}\n filters={filters}\n items={async (query) => {\n const response = await client.findByUser({\n params: { userId },\n query,\n });\n return response as Page<AuditEntity>;\n }}\n columns={{\n type: {\n label: \"Type\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"light\" color=\"grape\">\n {item.type}\n </Badge>\n ),\n },\n action: {\n label: \"Action\",\n fit: true,\n value: (item) => (\n <Badge size=\"sm\" variant=\"outline\">\n {item.action}\n </Badge>\n ),\n },\n severity: {\n label: \"Severity\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={getSeverityColor(item.severity)}\n leftSection={getSeverityIcon(item.severity)}\n >\n {item.severity}\n </Badge>\n ),\n },\n description: {\n label: \"Description\",\n value: (item) => (\n <Text size=\"sm\" lineClamp={1}>\n {item.description || \"-\"}\n </Text>\n ),\n },\n resource: {\n label: \"Resource\",\n fit: true,\n value: (item) =>\n item.resourceType ? (\n <Tooltip label={item.resourceId || \"N/A\"}>\n <Badge size=\"xs\" variant=\"dot\" color=\"gray\">\n {item.resourceType}\n </Badge>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n -\n </Text>\n ),\n },\n success: {\n label: \"Status\",\n fit: true,\n value: (item) =>\n item.success ? (\n <Group gap={4}>\n <IconCheck size={14} color=\"var(--mantine-color-green-6)\" />\n <Text size=\"xs\" c=\"green\">\n Success\n </Text>\n </Group>\n ) : (\n <Tooltip label={item.errorMessage || \"Failed\"}>\n <Group gap={4}>\n <IconX size={14} color=\"var(--mantine-color-red-6)\" />\n <Text size=\"xs\" c=\"red\">\n Failed\n </Text>\n </Group>\n </Tooltip>\n ),\n },\n ipAddress: {\n label: \"IP\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {item.ipAddress || \"-\"}\n </Text>\n ),\n },\n createdAt: {\n label: \"Time\",\n fit: true,\n value: (item) => (\n <Tooltip label={l(item.createdAt, { date: \"medium\" })}>\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n </Tooltip>\n ),\n },\n }}\n />\n </Flex>\n );\n};\n\nexport default AdminUserAudits;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminUserCreate-B72nu-3W.js","names":["Text"],"sources":["../../src/admin/components/users/AdminUserCreate.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control, Flex } from \"@alepha/ui\";\nimport { Card, Stack, Text } from \"@mantine/core\";\nimport { t } from \"alepha\";\nimport type {
|
|
1
|
+
{"version":3,"file":"AdminUserCreate-B72nu-3W.js","names":["Text"],"sources":["../../src/admin/components/users/AdminUserCreate.tsx"],"sourcesContent":["import { useClient } from \"@alepha/react\";\nimport { useForm } from \"@alepha/react/form\";\nimport { useRouter } from \"@alepha/react/router\";\nimport { ActionButton, Control, Flex } from \"@alepha/ui\";\nimport { Card, Stack, Text } from \"@mantine/core\";\nimport { t } from \"alepha\";\nimport type { AdminUserController } from \"alepha/api/users\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminUserCreateProps {\n userRealmName?: string;\n}\n\nconst AdminUserCreate = (props: AdminUserCreateProps) => {\n const client = useClient<AdminUserController>();\n const router = useRouter<AdminRouter>();\n\n const form = useForm({\n schema: t.object({\n username: t.optional(\n t.shortText({\n minLength: 3,\n maxLength: 50,\n pattern: \"^[a-zA-Z0-9._-]+$\",\n }),\n ),\n email: t.optional(t.email()),\n phoneNumber: t.optional(t.e164()),\n firstName: t.optional(t.string()),\n lastName: t.optional(t.string()),\n roles: t.optional(t.array(t.string())),\n enabled: t.optional(t.boolean()),\n password: t.optional(t.string({ minLength: 8 })),\n }),\n handler: async (data) => {\n const user = await client.createUser({\n query: {\n userRealmName: props.userRealmName,\n },\n body: {\n ...data,\n enabled: data.enabled ?? true,\n },\n });\n\n await router.go(\"adminUserDetails\", {\n params: { userId: user.id },\n });\n },\n });\n\n return (\n <Flex flex={1} p=\"md\">\n <Card withBorder p=\"lg\" maw={600} w=\"100%\">\n <form {...form.props}>\n <Stack gap=\"md\">\n <Text size=\"lg\" fw={500}>\n Create New User\n </Text>\n\n <Control title=\"Username\" input={form.input.username} />\n\n <Control title=\"Email\" input={form.input.email} />\n\n <Control title=\"Phone Number\" input={form.input.phoneNumber} />\n\n <Control title=\"First Name\" input={form.input.firstName} />\n\n <Control title=\"Last Name\" input={form.input.lastName} />\n\n <Control title=\"Password\" input={form.input.password} password />\n\n <Control title=\"Roles\" input={form.input.roles} />\n\n <Control title=\"Enabled\" input={form.input.enabled} />\n\n <ActionButton form={form}>Create User</ActionButton>\n </Stack>\n </form>\n </Card>\n </Flex>\n );\n};\n\nexport default AdminUserCreate;\n"],"mappings":";;;;;;;;;AAaA,MAAM,mBAAmB,UAAgC;CACvD,MAAM,SAAS,WAAgC;CAC/C,MAAM,SAAS,WAAwB;CAEvC,MAAM,OAAO,QAAQ;EACnB,QAAQ,EAAE,OAAO;GACf,UAAU,EAAE,SACV,EAAE,UAAU;IACV,WAAW;IACX,WAAW;IACX,SAAS;IACV,CAAC,CACH;GACD,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC;GAC5B,aAAa,EAAE,SAAS,EAAE,MAAM,CAAC;GACjC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC;GACjC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAC;GAChC,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;GACtC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;GAChC,UAAU,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,CAAC,CAAC;GACjD,CAAC;EACF,SAAS,OAAO,SAAS;GACvB,MAAM,OAAO,MAAM,OAAO,WAAW;IACnC,OAAO,EACL,eAAe,MAAM,eACtB;IACD,MAAM;KACJ,GAAG;KACH,SAAS,KAAK,WAAW;KAC1B;IACF,CAAC;AAEF,SAAM,OAAO,GAAG,oBAAoB,EAClC,QAAQ,EAAE,QAAQ,KAAK,IAAI,EAC5B,CAAC;;EAEL,CAAC;AAEF,QACE,oBAAC;EAAK,MAAM;EAAG,GAAE;YACf,oBAAC;GAAK;GAAW,GAAE;GAAK,KAAK;GAAK,GAAE;aAClC,oBAAC;IAAK,GAAI,KAAK;cACb,qBAAC;KAAM,KAAI;;MACT,oBAACA;OAAK,MAAK;OAAK,IAAI;iBAAK;QAElB;MAEP,oBAAC;OAAQ,OAAM;OAAW,OAAO,KAAK,MAAM;QAAY;MAExD,oBAAC;OAAQ,OAAM;OAAQ,OAAO,KAAK,MAAM;QAAS;MAElD,oBAAC;OAAQ,OAAM;OAAe,OAAO,KAAK,MAAM;QAAe;MAE/D,oBAAC;OAAQ,OAAM;OAAa,OAAO,KAAK,MAAM;QAAa;MAE3D,oBAAC;OAAQ,OAAM;OAAY,OAAO,KAAK,MAAM;QAAY;MAEzD,oBAAC;OAAQ,OAAM;OAAW,OAAO,KAAK,MAAM;OAAU;QAAW;MAEjE,oBAAC;OAAQ,OAAM;OAAQ,OAAO,KAAK,MAAM;QAAS;MAElD,oBAAC;OAAQ,OAAM;OAAU,OAAO,KAAK,MAAM;QAAW;MAEtD,oBAAC;OAAmB;iBAAM;QAA0B;;MAC9C;KACH;IACF;GACF;;AAIX,8BAAe"}
|