@alepha/ui 0.15.3 → 0.15.5

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 (99) hide show
  1. package/dist/admin/AdminApiKeys-DsmGnHNh.js +3 -0
  2. package/dist/admin/AdminApiKeys-GMORg-1l.js +442 -0
  3. package/dist/admin/AdminApiKeys-GMORg-1l.js.map +1 -0
  4. package/dist/admin/AdminAudits-8SM96viT.js +3 -0
  5. package/dist/admin/{AdminAudits-Oh7iAfQa.js → AdminAudits-pkWrjq1Z.js} +2 -2
  6. package/dist/admin/{AdminAudits-Oh7iAfQa.js.map → AdminAudits-pkWrjq1Z.js.map} +1 -1
  7. package/dist/admin/AdminFiles-B56ocq4H.js +3 -0
  8. package/dist/admin/{AdminFiles-Cu8GHgQ3.js → AdminFiles-WeQbsCsl.js} +2 -2
  9. package/dist/admin/{AdminFiles-Cu8GHgQ3.js.map → AdminFiles-WeQbsCsl.js.map} +1 -1
  10. package/dist/admin/AdminJobs-B-q9iGO3.js +697 -0
  11. package/dist/admin/AdminJobs-B-q9iGO3.js.map +1 -0
  12. package/dist/admin/AdminJobs-CED1syCn.js +3 -0
  13. package/dist/admin/AdminLayout-BX4FIpXv.css +143 -0
  14. package/dist/admin/AdminLayout-BX4FIpXv.css.map +1 -0
  15. package/dist/admin/{AdminLayout-QJLIesuG.js → AdminLayout-BqZiXx4H.js} +3 -2
  16. package/dist/admin/AdminLayout-BqZiXx4H.js.map +1 -0
  17. package/dist/admin/AdminNotifications-B0B1rdc4.js +3 -0
  18. package/dist/admin/{AdminNotifications-CgYkBuG_.js → AdminNotifications-Ds5Un0NJ.js} +2 -2
  19. package/dist/admin/{AdminNotifications-CgYkBuG_.js.map → AdminNotifications-Ds5Un0NJ.js.map} +1 -1
  20. package/dist/admin/{AdminParameters-Cl-R0nXt.js → AdminParameters-BU3lATdJ.js} +1 -1
  21. package/dist/admin/{AdminParameters-hjNG_KXb.js → AdminParameters-CfDUpc78.js} +4 -4
  22. package/dist/admin/{AdminParameters-hjNG_KXb.js.map → AdminParameters-CfDUpc78.js.map} +1 -1
  23. package/dist/admin/AdminSessions-BDGK2MS6.js +3 -0
  24. package/dist/admin/{AdminSessions-Bey9cuy1.js → AdminSessions-DzIOxM3b.js} +2 -2
  25. package/dist/admin/{AdminSessions-Bey9cuy1.js.map → AdminSessions-DzIOxM3b.js.map} +1 -1
  26. package/dist/admin/{AdminUserAudits-C7AN9jx7.js → AdminUserAudits-CiUPN2BC.js} +2 -2
  27. package/dist/admin/{AdminUserAudits-C7AN9jx7.js.map → AdminUserAudits-CiUPN2BC.js.map} +1 -1
  28. package/dist/admin/{AdminUserAudits-Cp_ERd2g.js → AdminUserAudits-Cj79gENT.js} +1 -1
  29. package/dist/admin/{AdminUserCreate-BVIm4JdN.js → AdminUserCreate-BwQKr4xE.js} +2 -2
  30. package/dist/admin/{AdminUserCreate-BVIm4JdN.js.map → AdminUserCreate-BwQKr4xE.js.map} +1 -1
  31. package/dist/admin/{AdminUserCreate-C1aInRDk.js → AdminUserCreate-Cq-mUmBs.js} +1 -1
  32. package/dist/admin/{AdminUserDetails-Dcn3OwMC.js → AdminUserDetails-DRjVAPFd.js} +1 -1
  33. package/dist/admin/{AdminUserDetails-yM4x8JE6.js → AdminUserDetails-uqtC5aJ1.js} +2 -2
  34. package/dist/admin/{AdminUserDetails-yM4x8JE6.js.map → AdminUserDetails-uqtC5aJ1.js.map} +1 -1
  35. package/dist/admin/{AdminUserLayout-gb-nbggz.js → AdminUserLayout-CGzmHHby.js} +1 -1
  36. package/dist/admin/{AdminUserLayout-BnfBC1gD.js → AdminUserLayout-CiPay35T.js} +2 -2
  37. package/dist/admin/{AdminUserLayout-BnfBC1gD.js.map → AdminUserLayout-CiPay35T.js.map} +1 -1
  38. package/dist/admin/{AdminUserSessions-kmkXG-xf.js → AdminUserSessions-DAE8Nf1F.js} +2 -2
  39. package/dist/admin/{AdminUserSessions-kmkXG-xf.js.map → AdminUserSessions-DAE8Nf1F.js.map} +1 -1
  40. package/dist/admin/AdminUserSessions-DcdzuNZ9.js +3 -0
  41. package/dist/admin/AdminUserSettings-D7V6-ceX.js +3 -0
  42. package/dist/admin/{AdminUserSettings-DZ9iWhJW.js → AdminUserSettings-EbahaV2a.js} +2 -2
  43. package/dist/admin/{AdminUserSettings-DZ9iWhJW.js.map → AdminUserSettings-EbahaV2a.js.map} +1 -1
  44. package/dist/admin/AdminUsers-D9nyzGqQ.js +3 -0
  45. package/dist/admin/{AdminUsers-D6Y5K8Am.js → AdminUsers-Dcjh0KNW.js} +2 -2
  46. package/dist/admin/{AdminUsers-D6Y5K8Am.js.map → AdminUsers-Dcjh0KNW.js.map} +1 -1
  47. package/dist/admin/index.d.ts +39 -51
  48. package/dist/admin/index.d.ts.map +1 -1
  49. package/dist/admin/index.js +52 -169
  50. package/dist/admin/index.js.map +1 -1
  51. package/dist/auth/AuthLayout-BaD7RD2h.css +143 -0
  52. package/dist/auth/AuthLayout-BaD7RD2h.css.map +1 -0
  53. package/dist/auth/AuthLayout-Dj5K4SIN.js.map +1 -1
  54. package/dist/auth/index.d.ts +9 -1
  55. package/dist/auth/index.d.ts.map +1 -1
  56. package/dist/auth/index.js +1 -2
  57. package/dist/auth/index.js.map +1 -1
  58. package/dist/core/index.d.ts +13 -21
  59. package/dist/core/index.d.ts.map +1 -1
  60. package/dist/core/index.js +26 -38
  61. package/dist/core/index.js.map +1 -1
  62. package/dist/demo/{DemoLogin-S-b15cmE.js → DemoLogin-CvCG2WVh.js} +3 -1
  63. package/dist/demo/{DemoLogin-S-b15cmE.js.map → DemoLogin-CvCG2WVh.js.map} +1 -1
  64. package/dist/demo/{DemoRegister-B29MdAaZ.js → DemoRegister-CmeHbOAs.js} +3 -1
  65. package/dist/demo/{DemoRegister-B29MdAaZ.js.map → DemoRegister-CmeHbOAs.js.map} +1 -1
  66. package/dist/demo/{DemoResetPassword-CPTy88iK.js → DemoResetPassword-CKO5iA_6.js} +3 -1
  67. package/dist/demo/{DemoResetPassword-CPTy88iK.js.map → DemoResetPassword-CKO5iA_6.js.map} +1 -1
  68. package/dist/demo/index.js +3 -3
  69. package/package.json +3 -3
  70. package/src/admin/AdminRouter.ts +34 -0
  71. package/src/admin/components/AdminLayout.tsx +2 -0
  72. package/src/admin/components/jobs/AdminJobs.tsx +733 -119
  73. package/src/admin/components/keys/AdminApiKeys.tsx +537 -0
  74. package/src/admin/components/parameters/AdminParameters.tsx +2 -3
  75. package/src/admin/index.ts +3 -5
  76. package/src/auth/AuthRouter.ts +1 -2
  77. package/src/auth/components/AuthLayout.tsx +1 -0
  78. package/src/core/components/buttons/ActionButton.tsx +15 -2
  79. package/src/core/components/buttons/DarkModeButton.css +6 -0
  80. package/src/core/components/buttons/DarkModeButton.tsx +18 -71
  81. package/src/core/components/buttons/LanguageButton.tsx +2 -7
  82. package/src/core/components/buttons/ThemeButton.tsx +2 -6
  83. package/src/core/components/layout/AdminShell.tsx +17 -1
  84. package/src/core/components/layout/AppBar.tsx +5 -8
  85. package/src/core/index.ts +0 -1
  86. package/src/core/styles.css +1 -0
  87. package/src/demo/components/auth/DemoLogin.tsx +2 -0
  88. package/src/demo/components/auth/DemoRegister.tsx +2 -0
  89. package/src/demo/components/auth/DemoResetPassword.tsx +2 -0
  90. package/dist/admin/AdminAudits-BU-p1g7A.js +0 -3
  91. package/dist/admin/AdminFiles-Bg9feLFH.js +0 -3
  92. package/dist/admin/AdminLayout-BfeFXiul.js +0 -3
  93. package/dist/admin/AdminLayout-QJLIesuG.js.map +0 -1
  94. package/dist/admin/AdminNotifications-DmfGPqHe.js +0 -3
  95. package/dist/admin/AdminSessions-Cn4_jB04.js +0 -3
  96. package/dist/admin/AdminUserSessions-rvA0ztxn.js +0 -3
  97. package/dist/admin/AdminUserSettings-Dg-wTRzN.js +0 -3
  98. package/dist/admin/AdminUsers-RCaxccEW.js +0 -3
  99. package/src/admin/MainRouter.ts +0 -23
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminJobs-B-q9iGO3.js","names":[],"sources":["../../src/admin/components/jobs/AdminJobs.tsx"],"sourcesContent":["import {\n ActionButton,\n DataTable,\n Flex,\n Text,\n useDialog,\n useToast,\n} from \"@alepha/ui\";\nimport {\n ActionIcon,\n Badge,\n Box,\n Card,\n Group,\n Paper,\n Progress,\n RingProgress,\n ScrollArea,\n SimpleGrid,\n Skeleton,\n Stack,\n Tabs,\n ThemeIcon,\n Tooltip,\n useMantineTheme,\n} from \"@mantine/core\";\nimport {\n IconAlertTriangle,\n IconCircleCheck,\n IconCircleX,\n IconClock,\n IconPlayerPlay,\n IconRefresh,\n IconTerminal2,\n} from \"@tabler/icons-react\";\nimport { type Page, t } from \"alepha\";\nimport {\n type AdminJobController,\n type JobExecutionEntity,\n jobExecutions,\n} from \"alepha/api/jobs\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Types\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface JobStats {\n name: string;\n total: number;\n completed: number;\n failed: number;\n running: number;\n avgDuration: number;\n lastRun?: Date;\n lastStatus?: \"COMPLETED\" | \"FAILED\" | \"STARTED\";\n}\n\ninterface LogEntry {\n level: string;\n message: string;\n service: string;\n module: string;\n timestamp: number;\n data?: unknown;\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Utilities\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst formatDuration = (\n start: Date | string,\n end?: Date | string | null,\n): string => {\n const startTime = new Date(start).getTime();\n const endTime = end ? new Date(end).getTime() : Date.now();\n const duration = endTime - startTime;\n\n if (duration < 1000) return `${duration}ms`;\n if (duration < 60000) return `${(duration / 1000).toFixed(1)}s`;\n if (duration < 3600000)\n return `${Math.floor(duration / 60000)}m ${Math.floor((duration % 60000) / 1000)}s`;\n return `${Math.floor(duration / 3600000)}h ${Math.floor((duration % 3600000) / 60000)}m`;\n};\n\nconst getStatusColor = (status: string) => {\n switch (status) {\n case \"COMPLETED\":\n return \"teal\";\n case \"FAILED\":\n return \"red\";\n case \"STARTED\":\n return \"blue\";\n default:\n return \"gray\";\n }\n};\n\nconst getStatusIcon = (status: string, size = 14) => {\n switch (status) {\n case \"COMPLETED\":\n return <IconCircleCheck size={size} />;\n case \"FAILED\":\n return <IconCircleX size={size} />;\n case \"STARTED\":\n return <IconPlayerPlay size={size} />;\n default:\n return <IconClock size={size} />;\n }\n};\n\nconst getLogLevelColor = (level: string) => {\n switch (level) {\n case \"ERROR\":\n return \"red\";\n case \"WARN\":\n return \"yellow\";\n case \"INFO\":\n return \"blue\";\n case \"DEBUG\":\n return \"gray\";\n case \"TRACE\":\n return \"dimmed\";\n default:\n return \"dimmed\";\n }\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Job Card Component\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface JobCardProps {\n job: string;\n stats?: JobStats;\n isTriggering: boolean;\n onTrigger: (job: string) => void;\n onSelect: (job: string) => void;\n isSelected: boolean;\n}\n\nconst JobCard = (props: JobCardProps) => {\n const { job, stats, isTriggering, onTrigger, onSelect, isSelected } = props;\n const theme = useMantineTheme();\n\n const successRate = stats\n ? stats.total > 0\n ? (stats.completed / stats.total) * 100\n : 0\n : 0;\n\n return (\n <Card\n p=\"md\"\n radius=\"md\"\n withBorder\n onClick={() => onSelect(job)}\n style={{\n cursor: \"pointer\",\n borderColor: isSelected ? theme.colors.blue[6] : undefined,\n backgroundColor: isSelected\n ? \"var(--mantine-color-blue-light)\"\n : undefined,\n transition: \"all 150ms ease\",\n }}\n >\n <Group justify=\"space-between\" mb=\"xs\">\n <Group gap=\"xs\">\n <ThemeIcon\n size=\"sm\"\n radius=\"sm\"\n variant=\"light\"\n color={\n stats?.lastStatus ? getStatusColor(stats.lastStatus) : \"gray\"\n }\n >\n <IconTerminal2 size={14} />\n </ThemeIcon>\n <Text size=\"sm\" fw={600} ff=\"monospace\">\n {job}\n </Text>\n </Group>\n <Tooltip label=\"Trigger job manually\" position=\"left\">\n <ActionIcon\n size=\"sm\"\n variant=\"light\"\n color=\"blue\"\n loading={isTriggering}\n onClick={(e) => {\n e.stopPropagation();\n onTrigger(job);\n }}\n >\n <IconPlayerPlay size={12} />\n </ActionIcon>\n </Tooltip>\n </Group>\n\n {stats ? (\n <>\n <Group gap=\"lg\" mb=\"xs\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={500}>\n Total\n </Text>\n <Text size=\"lg\" fw={700} ff=\"monospace\">\n {stats.total}\n </Text>\n </Box>\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={500}>\n Success\n </Text>\n <Text size=\"lg\" fw={700} ff=\"monospace\" c=\"teal\">\n {stats.completed}\n </Text>\n </Box>\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={500}>\n Failed\n </Text>\n <Text size=\"lg\" fw={700} ff=\"monospace\" c=\"red\">\n {stats.failed}\n </Text>\n </Box>\n </Group>\n\n <Progress.Root size=\"sm\" radius=\"xs\">\n <Tooltip label={`${stats.completed} completed`}>\n <Progress.Section\n value={(stats.completed / Math.max(stats.total, 1)) * 100}\n color=\"teal\"\n />\n </Tooltip>\n <Tooltip label={`${stats.failed} failed`}>\n <Progress.Section\n value={(stats.failed / Math.max(stats.total, 1)) * 100}\n color=\"red\"\n />\n </Tooltip>\n <Tooltip label={`${stats.running} running`}>\n <Progress.Section\n value={(stats.running / Math.max(stats.total, 1)) * 100}\n color=\"blue\"\n />\n </Tooltip>\n </Progress.Root>\n\n {stats.lastRun && (\n <Text size=\"xs\" c=\"dimmed\" mt=\"xs\">\n Last run: {formatDuration(stats.lastRun, new Date())} ago\n </Text>\n )}\n </>\n ) : (\n <Stack gap=\"xs\">\n <Skeleton height={8} radius=\"xl\" />\n <Skeleton height={8} width=\"70%\" radius=\"xl\" />\n </Stack>\n )}\n </Card>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Execution Log Viewer\n// ─────────────────────────────────────────────────────────────────────────────\n\ninterface ExecutionLogViewerProps {\n logs?: LogEntry[];\n error?: string | null;\n}\n\nconst ExecutionLogViewer = (props: ExecutionLogViewerProps) => {\n const { logs, error } = props;\n\n if (!logs?.length && !error) {\n return (\n <Box p=\"md\">\n <Text size=\"sm\" c=\"dimmed\" ta=\"center\">\n No logs available\n </Text>\n </Box>\n );\n }\n\n return (\n <ScrollArea h={300} type=\"auto\">\n <Box\n p=\"md\"\n style={{\n fontFamily: \"var(--mantine-font-family-monospace)\",\n fontSize: \"12px\",\n lineHeight: 1.6,\n }}\n >\n {error && (\n <Paper p=\"sm\" mb=\"md\" bg=\"var(--mantine-color-red-light)\" radius=\"sm\">\n <Group gap=\"xs\" align=\"flex-start\">\n <IconAlertTriangle\n size={14}\n color=\"var(--mantine-color-red-filled)\"\n />\n <Text\n size=\"xs\"\n c=\"red\"\n style={{ whiteSpace: \"pre-wrap\", wordBreak: \"break-word\" }}\n >\n {error}\n </Text>\n </Group>\n </Paper>\n )}\n\n {logs?.map((log, index) => (\n <Group key={index} gap=\"sm\" align=\"flex-start\" mb={4} wrap=\"nowrap\">\n <Text size=\"xs\" c=\"dimmed\" style={{ minWidth: 80, flexShrink: 0 }}>\n {new Date(log.timestamp).toLocaleTimeString()}\n </Text>\n <Badge\n size=\"xs\"\n variant=\"light\"\n color={getLogLevelColor(log.level)}\n style={{ minWidth: 50 }}\n >\n {log.level}\n </Badge>\n <Text size=\"xs\" c=\"dimmed\" style={{ minWidth: 100, flexShrink: 0 }}>\n {log.module}\n </Text>\n <Text size=\"xs\" style={{ wordBreak: \"break-word\" }}>\n {log.message}\n </Text>\n </Group>\n ))}\n </Box>\n </ScrollArea>\n );\n};\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Main Component\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst AdminJobs = () => {\n const client = useClient<AdminJobController>();\n const { l } = useI18n();\n const toast = useToast();\n const dialog = useDialog();\n\n const [jobs, setJobs] = useState<string[]>([]);\n const [jobStats, setJobStats] = useState<Map<string, JobStats>>(new Map());\n const [selectedJob, setSelectedJob] = useState<string | null>(null);\n const [triggeringJobs, setTriggeringJobs] = useState<Set<string>>(new Set());\n const [refreshKey, setRefreshKey] = useState(0);\n const [loading, setLoading] = useState(true);\n const [activeTab, setActiveTab] = useState<string | null>(\"overview\");\n\n // Load jobs list\n useEffect(() => {\n const loadJobs = async () => {\n try {\n const jobList = await client.getJobs();\n setJobs(jobList);\n\n // Load stats for each job\n const statsMap = new Map<string, JobStats>();\n for (const job of jobList) {\n const executions = await client.getJobExecutions({\n query: { job, size: 100 },\n });\n\n const items = executions.content || [];\n const completed = items.filter(\n (e: JobExecutionEntity) => e.status === \"COMPLETED\",\n ).length;\n const failed = items.filter(\n (e: JobExecutionEntity) => e.status === \"FAILED\",\n ).length;\n const running = items.filter(\n (e: JobExecutionEntity) => e.status === \"STARTED\",\n ).length;\n\n const completedItems = items.filter(\n (e: JobExecutionEntity) => e.status === \"COMPLETED\" && e.finishedAt,\n );\n const avgDuration =\n completedItems.length > 0\n ? completedItems.reduce((acc: number, e: JobExecutionEntity) => {\n const duration =\n new Date(e.finishedAt!).getTime() -\n new Date(e.createdAt).getTime();\n return acc + duration;\n }, 0) / completedItems.length\n : 0;\n\n const lastItem = items[0];\n\n statsMap.set(job, {\n name: job,\n total: items.length,\n completed,\n failed,\n running,\n avgDuration,\n lastRun: lastItem?.createdAt\n ? new Date(lastItem.createdAt)\n : undefined,\n lastStatus: lastItem?.status as JobStats[\"lastStatus\"],\n });\n }\n\n setJobStats(statsMap);\n } catch (error) {\n toast.danger(\"Failed to load jobs\");\n } finally {\n setLoading(false);\n }\n };\n\n loadJobs();\n }, [refreshKey]);\n\n const handleTriggerJob = useCallback(\n async (job: string) => {\n const confirmed = await dialog.confirm({\n title: \"Trigger Job\",\n message: `Are you sure you want to trigger \"${job}\" manually?`,\n confirmLabel: \"Trigger\",\n confirmColor: \"blue\",\n });\n\n if (!confirmed) return;\n\n setTriggeringJobs((prev) => new Set(prev).add(job));\n\n try {\n await client.triggerJob({ body: { name: job } });\n toast.success(`Job \"${job}\" triggered successfully`);\n setRefreshKey((k) => k + 1);\n } catch (error) {\n toast.danger(`Failed to trigger job \"${job}\"`);\n } finally {\n setTriggeringJobs((prev) => {\n const next = new Set(prev);\n next.delete(job);\n return next;\n });\n }\n },\n [client, dialog, toast],\n );\n\n const filters = t.object({\n job: t.optional(\n t.string({\n $control: {\n query: t.pick(jobExecutions.schema, [\"job\"]),\n },\n }),\n ),\n status: t.optional(t.enum([\"STARTED\", \"FAILED\", \"COMPLETED\"])),\n });\n\n // Calculate global stats\n const globalStats = {\n total: Array.from(jobStats.values()).reduce((acc, s) => acc + s.total, 0),\n completed: Array.from(jobStats.values()).reduce(\n (acc, s) => acc + s.completed,\n 0,\n ),\n failed: Array.from(jobStats.values()).reduce((acc, s) => acc + s.failed, 0),\n running: Array.from(jobStats.values()).reduce(\n (acc, s) => acc + s.running,\n 0,\n ),\n };\n\n const successRate =\n globalStats.total > 0\n ? Math.round((globalStats.completed / globalStats.total) * 100)\n : 0;\n\n return (\n <Flex flex={1} direction=\"column\" gap=\"md\">\n {/* Header Stats */}\n <SimpleGrid cols={{ base: 2, sm: 4 }} spacing=\"md\">\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Total Jobs\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\">\n {jobs.length}\n </Text>\n </Box>\n <ThemeIcon size=\"lg\" radius=\"md\" variant=\"light\" color=\"blue\">\n <IconTerminal2 size={20} />\n </ThemeIcon>\n </Group>\n </Paper>\n\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Executions\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\">\n {globalStats.total}\n </Text>\n </Box>\n <ThemeIcon size=\"lg\" radius=\"md\" variant=\"light\" color=\"gray\">\n <IconClock size={20} />\n </ThemeIcon>\n </Group>\n </Paper>\n\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Success Rate\n </Text>\n <Text\n size=\"xl\"\n fw={700}\n ff=\"monospace\"\n c={\n successRate >= 90\n ? \"teal\"\n : successRate >= 70\n ? \"yellow\"\n : \"red\"\n }\n >\n {successRate}%\n </Text>\n </Box>\n <RingProgress\n size={48}\n thickness={4}\n roundCaps\n sections={[\n {\n value: successRate,\n color:\n successRate >= 90\n ? \"teal\"\n : successRate >= 70\n ? \"yellow\"\n : \"red\",\n },\n ]}\n />\n </Group>\n </Paper>\n\n <Paper p=\"md\" radius=\"md\" withBorder>\n <Group justify=\"space-between\">\n <Box>\n <Text size=\"xs\" c=\"dimmed\" tt=\"uppercase\" fw={600}>\n Running Now\n </Text>\n <Text size=\"xl\" fw={700} ff=\"monospace\" c=\"blue\">\n {globalStats.running}\n </Text>\n </Box>\n <ThemeIcon\n size=\"lg\"\n radius=\"md\"\n variant=\"light\"\n color={globalStats.running > 0 ? \"blue\" : \"gray\"}\n >\n <IconPlayerPlay size={20} />\n </ThemeIcon>\n </Group>\n </Paper>\n </SimpleGrid>\n\n {/* Tabs */}\n <Tabs value={activeTab} onChange={setActiveTab}>\n <Tabs.List>\n <Tabs.Tab value=\"overview\" leftSection={<IconTerminal2 size={14} />}>\n Jobs Overview\n </Tabs.Tab>\n <Tabs.Tab value=\"executions\" leftSection={<IconClock size={14} />}>\n Execution History\n </Tabs.Tab>\n </Tabs.List>\n\n <Tabs.Panel value=\"overview\" pt=\"md\">\n <Group justify=\"space-between\" mb=\"md\">\n <Text size=\"sm\" c=\"dimmed\">\n {jobs.length} registered job{jobs.length !== 1 ? \"s\" : \"\"}\n </Text>\n <ActionButton\n size=\"xs\"\n variant=\"light\"\n leftSection={<IconRefresh size={14} />}\n onClick={() => setRefreshKey((k) => k + 1)}\n >\n Refresh\n </ActionButton>\n </Group>\n\n {loading ? (\n <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing=\"md\">\n {[1, 2, 3].map((i) => (\n <Skeleton key={i} height={150} radius=\"md\" />\n ))}\n </SimpleGrid>\n ) : jobs.length === 0 ? (\n <Paper p=\"xl\" radius=\"md\" withBorder ta=\"center\">\n <IconTerminal2 size={48} color=\"var(--mantine-color-dimmed)\" />\n <Text size=\"lg\" fw={500} mt=\"md\">\n No jobs registered\n </Text>\n <Text size=\"sm\" c=\"dimmed\" mt=\"xs\">\n Jobs will appear here once they are defined using $job primitive\n </Text>\n </Paper>\n ) : (\n <SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing=\"md\">\n {jobs.map((job) => (\n <JobCard\n key={job}\n job={job}\n stats={jobStats.get(job)}\n isTriggering={triggeringJobs.has(job)}\n onTrigger={handleTriggerJob}\n onSelect={setSelectedJob}\n isSelected={selectedJob === job}\n />\n ))}\n </SimpleGrid>\n )}\n </Tabs.Panel>\n\n <Tabs.Panel value=\"executions\" pt=\"md\">\n <DataTable<JobExecutionEntity, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={15}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"sm\",\n verticalSpacing: \"sm\",\n highlightOnHover: true,\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"job\" || key === \"status\") {\n return form.submit();\n }\n }}\n filters={filters}\n items={async (filters) => {\n const response = await client.getJobExecutions({\n query: {\n ...filters,\n job: selectedJob || filters.job,\n },\n });\n\n return response as Page<JobExecutionEntity>;\n }}\n columns={{\n job: {\n label: \"Job\",\n value: (item) => (\n <Text size=\"sm\" fw={500} ff=\"monospace\">\n {item.job}\n </Text>\n ),\n },\n status: {\n label: \"Status\",\n fit: true,\n value: (item) => (\n <Badge\n size=\"sm\"\n variant=\"light\"\n color={getStatusColor(item.status)}\n leftSection={getStatusIcon(item.status, 12)}\n >\n {item.status}\n </Badge>\n ),\n },\n duration: {\n label: \"Duration\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\" ff=\"monospace\">\n {formatDuration(item.createdAt, item.finishedAt)}\n </Text>\n ),\n },\n logs: {\n label: \"Logs\",\n fit: true,\n value: (item) => {\n const logCount =\n (item.logs as LogEntry[] | undefined)?.length || 0;\n const errorCount =\n (item.logs as LogEntry[] | undefined)?.filter(\n (log) => log.level === \"ERROR\",\n ).length || 0;\n\n return (\n <Group gap={4}>\n <Badge size=\"xs\" variant=\"light\" color=\"gray\">\n {logCount} logs\n </Badge>\n {errorCount > 0 && (\n <Badge size=\"xs\" variant=\"light\" color=\"red\">\n {errorCount} errors\n </Badge>\n )}\n </Group>\n );\n },\n },\n error: {\n label: \"Error\",\n value: (item) =>\n item.error ? (\n <Tooltip label={item.error} multiline w={300}>\n <Text size=\"xs\" c=\"red\" lineClamp={1}>\n {item.error}\n </Text>\n </Tooltip>\n ) : (\n <Text size=\"xs\" c=\"dimmed\">\n —\n </Text>\n ),\n },\n createdAt: {\n label: \"Started\",\n fit: true,\n value: (item) => (\n <Text size=\"xs\" c=\"dimmed\">\n {l(item.createdAt, { date: \"fromNow\" })}\n </Text>\n ),\n },\n }}\n panel={(item) => (\n <Box bg=\"var(--mantine-color-dark-7)\" p={0}>\n <ExecutionLogViewer\n logs={item.logs as LogEntry[] | undefined}\n error={item.error}\n />\n </Box>\n )}\n canPanel={(item) => Boolean(item.logs?.length || item.error)}\n />\n </Tabs.Panel>\n </Tabs>\n </Flex>\n );\n};\n\nexport default AdminJobs;\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { t as AdminJobs_default } from "./AdminJobs-B-q9iGO3.js";
2
+
3
+ export { AdminJobs_default as default };
@@ -0,0 +1,143 @@
1
+ /* Dark Mode Toggle - SSR-safe icon switching */
2
+
3
+ [data-mantine-color-scheme="light"] .alepha-light-hidden,
4
+ [data-mantine-color-scheme="dark"] .alepha-dark-hidden {
5
+ display: none;
6
+ }
7
+
8
+
9
+ :root {
10
+ --alepha-background-light: var(--mantine-color-gray-1);
11
+ --alepha-background-dark: var(--mantine-color-dark-9);
12
+ --alepha-background: var(--alepha-background-light);
13
+
14
+ --alepha-surface-light: var(--mantine-color-gray-0);
15
+ --alepha-surface-dark: var(--mantine-color-dark-8);
16
+ --alepha-surface: var(--alepha-surface-light);
17
+
18
+ --alepha-elevated-light: var(--mantine-color-white);
19
+ --alepha-elevated-hover-light: #f6f5f5;
20
+ --alepha-elevated-dark: var(--mantine-color-dark-7);
21
+ --alepha-elevated-hover-dark: var(--mantine-color-dark-6);
22
+ --alepha-elevated: var(--alepha-elevated-light);
23
+ --alepha-elevated-hover: var(--alepha-elevated-hover-light);
24
+
25
+ --alepha-border: var(--mantine-color-default-border);
26
+
27
+ --alepha-text-light: #000000;
28
+ --alepha-text-dark: #ffffff;
29
+ --alepha-text-muted: #6c757d;
30
+ --alepha-text: var(--alepha-text-light);
31
+ }
32
+
33
+ #root {
34
+ display: flex;
35
+ background-color: var(--alepha-background);
36
+ color: var(--alepha-text);
37
+ min-height: 100dvh;
38
+ }
39
+
40
+ /* ------------------------------------------------------------------------------------------------------------------ */
41
+
42
+ [data-mantine-color-scheme="dark"] {
43
+ --alepha-surface: var(--alepha-surface-dark);
44
+ --alepha-elevated: var(--alepha-elevated-dark);
45
+ --alepha-elevated-hover: var(--alepha-elevated-hover-dark);
46
+ --alepha-background: var(--alepha-background-dark);
47
+ --alepha-text: var(--alepha-text-dark);
48
+ }
49
+
50
+ /* ------------------------------------------------------------------------------------------------------------------ */
51
+
52
+ .overflow-auto {
53
+ overflow: auto;
54
+ scrollbar-color: var(--alepha-text-muted) transparent;
55
+ scrollbar-width: thin;
56
+ }
57
+
58
+ /* Sidebar scroll area - subtle scrollbar that appears on hover */
59
+ .alepha-sidebar-scroll {
60
+ overflow-y: auto;
61
+ overflow-x: hidden;
62
+ scrollbar-width: thin;
63
+ scrollbar-color: transparent transparent;
64
+ }
65
+
66
+ .alepha-sidebar-scroll:hover {
67
+ scrollbar-color: var(--mantine-color-gray-4) transparent;
68
+ }
69
+
70
+ [data-mantine-color-scheme="dark"] .alepha-sidebar-scroll:hover {
71
+ scrollbar-color: var(--mantine-color-dark-4) transparent;
72
+ }
73
+
74
+ /* WebKit scrollbar styling */
75
+ .alepha-sidebar-scroll::-webkit-scrollbar {
76
+ width: 6px;
77
+ }
78
+
79
+ .alepha-sidebar-scroll::-webkit-scrollbar-track {
80
+ background: transparent;
81
+ }
82
+
83
+ .alepha-sidebar-scroll::-webkit-scrollbar-thumb {
84
+ background: transparent;
85
+ border-radius: 3px;
86
+ }
87
+
88
+ .alepha-sidebar-scroll:hover::-webkit-scrollbar-thumb {
89
+ background: var(--mantine-color-gray-4);
90
+ }
91
+
92
+ [data-mantine-color-scheme="dark"]
93
+ .alepha-sidebar-scroll:hover::-webkit-scrollbar-thumb {
94
+ background: var(--mantine-color-dark-4);
95
+ }
96
+
97
+ /* ------------------------------------------------------------------------------------------------------------------ */
98
+ /* Sidebar Transitions */
99
+
100
+ .alepha-sidebar-navbar {
101
+ transition: width 0.2s ease-in-out;
102
+ }
103
+
104
+ .alepha-sidebar-navbar[data-resizing="true"] {
105
+ transition: none;
106
+ }
107
+
108
+ .alepha-sidebar-main {
109
+ transition: padding-left 0.2s ease-in-out;
110
+ }
111
+
112
+ .alepha-sidebar-main[data-resizing="true"] {
113
+ transition: none;
114
+ }
115
+
116
+ /* ------------------------------------------------------------------------------------------------------------------ */
117
+ /* Modal Customizations */
118
+
119
+ /* Custom modal scale animation */
120
+ @keyframes modalScaleIn {
121
+ from {
122
+ opacity: 0;
123
+ transform: scale(0.9);
124
+ }
125
+ to {
126
+ opacity: 1;
127
+ transform: scale(1);
128
+ }
129
+ }
130
+
131
+ @keyframes modalScaleOut {
132
+ from {
133
+ opacity: 1;
134
+ transform: scale(1);
135
+ }
136
+ to {
137
+ opacity: 0;
138
+ transform: scale(0.95);
139
+ }
140
+ }
141
+
142
+
143
+ /*# sourceMappingURL=AdminLayout-BX4FIpXv.css.map*/
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminLayout-BX4FIpXv.css","names":[],"sources":["../../../../src/core/components/buttons/DarkModeButton.css","../../../../src/core/styles.css"],"sourcesContent":["/* Dark Mode Toggle - SSR-safe icon switching */\n\n[data-mantine-color-scheme=\"light\"] .alepha-light-hidden,\n[data-mantine-color-scheme=\"dark\"] .alepha-dark-hidden {\n display: none;\n}\n","@import \"@mantine/core/styles.css\";\n@import \"@mantine/nprogress/styles.css\";\n@import \"@mantine/spotlight/styles.css\";\n@import \"@mantine/dates/styles.css\";\n@import \"@mantine/notifications/styles.css\";\n@import \"./components/buttons/DarkModeButton.css\";\n\n:root {\n --alepha-background-light: var(--mantine-color-gray-1);\n --alepha-background-dark: var(--mantine-color-dark-9);\n --alepha-background: var(--alepha-background-light);\n\n --alepha-surface-light: var(--mantine-color-gray-0);\n --alepha-surface-dark: var(--mantine-color-dark-8);\n --alepha-surface: var(--alepha-surface-light);\n\n --alepha-elevated-light: var(--mantine-color-white);\n --alepha-elevated-hover-light: #f6f5f5;\n --alepha-elevated-dark: var(--mantine-color-dark-7);\n --alepha-elevated-hover-dark: var(--mantine-color-dark-6);\n --alepha-elevated: var(--alepha-elevated-light);\n --alepha-elevated-hover: var(--alepha-elevated-hover-light);\n\n --alepha-border: var(--mantine-color-default-border);\n\n --alepha-text-light: #000000;\n --alepha-text-dark: #ffffff;\n --alepha-text-muted: #6c757d;\n --alepha-text: var(--alepha-text-light);\n}\n\n#root {\n display: flex;\n background-color: var(--alepha-background);\n color: var(--alepha-text);\n min-height: 100dvh;\n}\n\n/* ------------------------------------------------------------------------------------------------------------------ */\n\n[data-mantine-color-scheme=\"dark\"] {\n --alepha-surface: var(--alepha-surface-dark);\n --alepha-elevated: var(--alepha-elevated-dark);\n --alepha-elevated-hover: var(--alepha-elevated-hover-dark);\n --alepha-background: var(--alepha-background-dark);\n --alepha-text: var(--alepha-text-dark);\n}\n\n/* ------------------------------------------------------------------------------------------------------------------ */\n\n.overflow-auto {\n overflow: auto;\n scrollbar-color: var(--alepha-text-muted) transparent;\n scrollbar-width: thin;\n}\n\n/* Sidebar scroll area - subtle scrollbar that appears on hover */\n.alepha-sidebar-scroll {\n overflow-y: auto;\n overflow-x: hidden;\n scrollbar-width: thin;\n scrollbar-color: transparent transparent;\n}\n\n.alepha-sidebar-scroll:hover {\n scrollbar-color: var(--mantine-color-gray-4) transparent;\n}\n\n[data-mantine-color-scheme=\"dark\"] .alepha-sidebar-scroll:hover {\n scrollbar-color: var(--mantine-color-dark-4) transparent;\n}\n\n/* WebKit scrollbar styling */\n.alepha-sidebar-scroll::-webkit-scrollbar {\n width: 6px;\n}\n\n.alepha-sidebar-scroll::-webkit-scrollbar-track {\n background: transparent;\n}\n\n.alepha-sidebar-scroll::-webkit-scrollbar-thumb {\n background: transparent;\n border-radius: 3px;\n}\n\n.alepha-sidebar-scroll:hover::-webkit-scrollbar-thumb {\n background: var(--mantine-color-gray-4);\n}\n\n[data-mantine-color-scheme=\"dark\"]\n .alepha-sidebar-scroll:hover::-webkit-scrollbar-thumb {\n background: var(--mantine-color-dark-4);\n}\n\n/* ------------------------------------------------------------------------------------------------------------------ */\n/* Sidebar Transitions */\n\n.alepha-sidebar-navbar {\n transition: width 0.2s ease-in-out;\n}\n\n.alepha-sidebar-navbar[data-resizing=\"true\"] {\n transition: none;\n}\n\n.alepha-sidebar-main {\n transition: padding-left 0.2s ease-in-out;\n}\n\n.alepha-sidebar-main[data-resizing=\"true\"] {\n transition: none;\n}\n\n/* ------------------------------------------------------------------------------------------------------------------ */\n/* Modal Customizations */\n\n/* Custom modal scale animation */\n@keyframes modalScaleIn {\n from {\n opacity: 0;\n transform: scale(0.9);\n }\n to {\n opacity: 1;\n transform: scale(1);\n }\n}\n\n@keyframes modalScaleOut {\n from {\n opacity: 1;\n transform: scale(1);\n }\n to {\n opacity: 0;\n transform: scale(0.95);\n }\n}\n"],"mappings":"AAAA;;AAEA;AACA;AACA;AACA;;;ACEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA"}
@@ -28,6 +28,7 @@ const AdminLayout = (props) => {
28
28
  position: "right"
29
29
  }
30
30
  ] },
31
+ sidebarResizable: true,
31
32
  sidebarProps: { autoPopulateMenu: { startsWith: "/admin" } },
32
33
  ...props.adminShellProps
33
34
  }) });
@@ -35,5 +36,5 @@ const AdminLayout = (props) => {
35
36
  var AdminLayout_default = AdminLayout;
36
37
 
37
38
  //#endregion
38
- export { AdminLayout_default as t };
39
- //# sourceMappingURL=AdminLayout-QJLIesuG.js.map
39
+ export { AdminLayout_default as default };
40
+ //# sourceMappingURL=AdminLayout-BqZiXx4H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdminLayout-BqZiXx4H.js","names":[],"sources":["../../src/admin/components/AdminLayout.tsx"],"sourcesContent":["import {\n ActionButton,\n AdminShell,\n type AdminShellProps,\n AlephaMantineProvider,\n OmnibarButton,\n} from \"@alepha/ui\";\nimport { UserButton } from \"@alepha/ui/auth\";\nimport { IconArrowLeft } from \"@tabler/icons-react\";\nimport \"../../core/styles.css\";\n\nexport interface AdminLayoutProps {\n adminShellProps?: AdminShellProps;\n}\n\nconst AdminLayout = (props: AdminLayoutProps) => {\n return (\n <AlephaMantineProvider>\n <AdminShell\n appBarProps={{\n items: [\n {\n element: (\n <ActionButton\n variant={\"subtle\"}\n icon={IconArrowLeft}\n href={\"/\"}\n />\n ),\n position: \"left\",\n },\n {\n element: <OmnibarButton />,\n position: \"center\",\n },\n {\n element: <UserButton />,\n position: \"right\",\n },\n {\n type: \"dark\",\n position: \"right\",\n },\n ],\n }}\n sidebarResizable\n sidebarProps={{\n autoPopulateMenu: {\n startsWith: \"/admin\",\n },\n }}\n {...props.adminShellProps}\n />\n </AlephaMantineProvider>\n );\n};\n\nexport default AdminLayout;\n"],"mappings":";;;;;;AAeA,MAAM,eAAe,UAA4B;AAC/C,QACE,oBAAC,mCACC,oBAAC;EACC,aAAa,EACX,OAAO;GACL;IACE,SACE,oBAAC;KACC,SAAS;KACT,MAAM;KACN,MAAM;MACN;IAEJ,UAAU;IACX;GACD;IACE,SAAS,oBAAC,kBAAgB;IAC1B,UAAU;IACX;GACD;IACE,SAAS,oBAAC,eAAa;IACvB,UAAU;IACX;GACD;IACE,MAAM;IACN,UAAU;IACX;GACF,EACF;EACD;EACA,cAAc,EACZ,kBAAkB,EAChB,YAAY,UACb,EACF;EACD,GAAI,MAAM;GACV,GACoB;;AAI5B,0BAAe"}
@@ -0,0 +1,3 @@
1
+ import { t as AdminNotifications_default } from "./AdminNotifications-Ds5Un0NJ.js";
2
+
3
+ export { AdminNotifications_default as default };
@@ -1,10 +1,10 @@
1
1
  import { DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconAlertCircle, IconCheck, IconClock, IconMail, IconMessage } from "@tabler/icons-react";
4
- import { jsx } from "react/jsx-runtime";
5
4
  import { Badge, Tooltip } from "@mantine/core";
6
5
  import { useClient } from "alepha/react";
7
6
  import { useI18n } from "alepha/react/i18n";
7
+ import { jsx } from "react/jsx-runtime";
8
8
 
9
9
  //#region ../../src/admin/components/notifications/AdminNotifications.tsx
10
10
  const AdminNotifications = () => {
@@ -151,4 +151,4 @@ var AdminNotifications_default = AdminNotifications;
151
151
 
152
152
  //#endregion
153
153
  export { AdminNotifications_default as t };
154
- //# sourceMappingURL=AdminNotifications-CgYkBuG_.js.map
154
+ //# sourceMappingURL=AdminNotifications-Ds5Un0NJ.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminNotifications-CgYkBuG_.js","names":[],"sources":["../../src/admin/components/notifications/AdminNotifications.tsx"],"sourcesContent":["import { 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\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\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
+ {"version":3,"file":"AdminNotifications-Ds5Un0NJ.js","names":[],"sources":["../../src/admin/components/notifications/AdminNotifications.tsx"],"sourcesContent":["import { 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\";\nimport { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\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,3 +1,3 @@
1
- import { t as AdminParameters_default } from "./AdminParameters-hjNG_KXb.js";
1
+ import { t as AdminParameters_default } from "./AdminParameters-CfDUpc78.js";
2
2
 
3
3
  export { AdminParameters_default as default };
@@ -1,10 +1,10 @@
1
1
  import { ActionButton, Flex, Text, TypeForm } from "@alepha/ui";
2
2
  import { jsonSchemaToTypeBox } from "alepha";
3
3
  import { IconChevronDown, IconChevronRight, IconClock, IconFolder, IconFolderOpen, IconHistory, IconRefresh, IconSettings } from "@tabler/icons-react";
4
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
4
  import { Badge, Box, Card, Code, Group, Loader, ScrollArea, Stack, Timeline, Tooltip, Tree, useTree } from "@mantine/core";
6
5
  import { useClient } from "alepha/react";
7
6
  import { useI18n } from "alepha/react/i18n";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
8
  import { useCallback, useEffect, useMemo, useState } from "react";
9
9
  import { useForm } from "alepha/react/form";
10
10
 
@@ -484,9 +484,9 @@ const AdminParameters = () => {
484
484
  setLoadingConfig(true);
485
485
  setLoadingHistory(true);
486
486
  try {
487
- const [current, historyData] = await Promise.all([client.getCurrent({ params: { name } }), client.getHistory({ params: { name } })]);
487
+ const [current] = await Promise.all([client.getCurrent({ params: { name } })]);
488
488
  setConfigValue(current);
489
- setHistory(historyData.versions);
489
+ setHistory([]);
490
490
  } finally {
491
491
  setLoadingConfig(false);
492
492
  setLoadingHistory(false);
@@ -572,4 +572,4 @@ var AdminParameters_default = AdminParameters;
572
572
 
573
573
  //#endregion
574
574
  export { AdminParameters_default as t };
575
- //# sourceMappingURL=AdminParameters-hjNG_KXb.js.map
575
+ //# sourceMappingURL=AdminParameters-CfDUpc78.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminParameters-hjNG_KXb.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 /**\n * Default value from the registered $config 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 configuration (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 { 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 { useForm } from \"alepha/react/form\";\nimport { useI18n } from \"alepha/react/i18n\";\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 { 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 { useI18n } from \"alepha/react/i18n\";\nimport { getStatusColor } from \"./types.ts\";\n\nexport interface ParameterHistoryProps {\n selectedConfig: string | null;\n history: Parameter[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\nconst ParameterHistory = ({\n 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 { 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 { useClient } from \"alepha/react\";\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
+ {"version":3,"file":"AdminParameters-CfDUpc78.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 /**\n * Default value from the registered $config 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 configuration (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 { 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 { useForm } from \"alepha/react/form\";\nimport { useI18n } from \"alepha/react/i18n\";\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 { 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 { useI18n } from \"alepha/react/i18n\";\nimport { getStatusColor } from \"./types.ts\";\n\nexport interface ParameterHistoryProps {\n selectedConfig: string | null;\n history: Parameter[];\n loading: boolean;\n onRollback: (version: number) => void;\n}\n\nconst ParameterHistory = ({\n 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 { 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 { useClient } from \"alepha/react\";\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] = await Promise.all([\n client.getCurrent({ params: { name } }),\n ]);\n setConfigValue(current);\n setHistory([]);\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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"}
@@ -0,0 +1,3 @@
1
+ import { t as AdminSessions_default } from "./AdminSessions-DzIOxM3b.js";
2
+
3
+ export { AdminSessions_default as default };
@@ -2,10 +2,10 @@ import { ActionButton, DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconDeviceDesktop, IconDeviceMobile, IconDeviceTablet, IconTrash } from "@tabler/icons-react";
4
4
  import { useRouter } from "alepha/react/router";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
5
  import { Badge, Group } from "@mantine/core";
7
6
  import { useClient } from "alepha/react";
8
7
  import { useI18n } from "alepha/react/i18n";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { useState } from "react";
10
10
  import { sessions } from "alepha/api/users";
11
11
 
@@ -148,4 +148,4 @@ var AdminSessions_default = AdminSessions;
148
148
 
149
149
  //#endregion
150
150
  export { AdminSessions_default as t };
151
- //# sourceMappingURL=AdminSessions-Bey9cuy1.js.map
151
+ //# sourceMappingURL=AdminSessions-DzIOxM3b.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSessions-Bey9cuy1.js","names":[],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import { 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 { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport { useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n });\n\n const getDeviceIcon = (device?: string) => {\n switch (device) {\n case \"MOBILE\":\n return <IconDeviceMobile size={14} />;\n case \"TABLET\":\n return <IconDeviceTablet size={14} />;\n default:\n return <IconDeviceDesktop size={14} />;\n }\n };\n\n const isExpired = (expiresAt: Date | string) => {\n return new Date(expiresAt) < new Date();\n };\n\n const handleDelete = async (sessionId: string) => {\n await client.deleteSession({\n params: { id: sessionId },\n query: { userRealmName: props.userRealmName },\n });\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<SessionEntity, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"userId\") {\n return form.submit();\n }\n }}\n filters={filters}\n tableTrProps={(item) => {\n if (isExpired(item.expiresAt)) {\n return {\n opacity: 0.5,\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n\n return response as Page<SessionEntity>;\n }}\n columns={{\n userId: {\n label: \"User\",\n value: (item) => (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={router.path(\"adminUserDetails\", {\n params: { userId: item.userId },\n })}\n >\n <Text size=\"xs\" ff=\"monospace\">\n {item.userId.slice(0, 8)}...\n </Text>\n </ActionButton>\n ),\n },\n userAgent: {\n label: \"Device\",\n fit: true,\n value: (item) => (\n <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
+ {"version":3,"file":"AdminSessions-DzIOxM3b.js","names":[],"sources":["../../src/admin/components/sessions/AdminSessions.tsx"],"sourcesContent":["import { 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 { useClient } from \"alepha/react\";\nimport { useI18n } from \"alepha/react/i18n\";\nimport { useRouter } from \"alepha/react/router\";\nimport { useState } from \"react\";\nimport type { AdminRouter } from \"../../AdminRouter.ts\";\n\nexport interface AdminSessionsProps {\n userRealmName?: string;\n}\n\nconst AdminSessions = (props: AdminSessionsProps) => {\n const client = useClient<AdminSessionController>();\n const router = useRouter<AdminRouter>();\n const { l } = useI18n();\n const [refreshKey, setRefreshKey] = useState(0);\n\n const filters = t.object({\n userId: t.optional(\n t.uuid({\n $control: {\n query: t.pick(sessions.schema, [\"userId\"]),\n },\n }),\n ),\n });\n\n const getDeviceIcon = (device?: string) => {\n switch (device) {\n case \"MOBILE\":\n return <IconDeviceMobile size={14} />;\n case \"TABLET\":\n return <IconDeviceTablet size={14} />;\n default:\n return <IconDeviceDesktop size={14} />;\n }\n };\n\n const isExpired = (expiresAt: Date | string) => {\n return new Date(expiresAt) < new Date();\n };\n\n const handleDelete = async (sessionId: string) => {\n await client.deleteSession({\n params: { id: sessionId },\n query: { userRealmName: props.userRealmName },\n });\n setRefreshKey((k) => k + 1);\n };\n\n return (\n <Flex flex={1} direction=\"column\">\n <DataTable<SessionEntity, typeof filters>\n key={refreshKey}\n submitOnInit\n defaultSize={10}\n typeFormProps={{\n skipSubmitButton: true,\n columns: 3,\n }}\n tableProps={{\n horizontalSpacing: \"xs\",\n verticalSpacing: \"xs\",\n }}\n onFilterChange={(key, _value, form) => {\n if (key === \"userId\") {\n return form.submit();\n }\n }}\n filters={filters}\n tableTrProps={(item) => {\n if (isExpired(item.expiresAt)) {\n return {\n opacity: 0.5,\n };\n }\n return {};\n }}\n items={async (filters) => {\n const response = await client.findSessions({\n query: {\n ...filters,\n userRealmName: props.userRealmName,\n },\n });\n\n return response as Page<SessionEntity>;\n }}\n columns={{\n userId: {\n label: \"User\",\n value: (item) => (\n <ActionButton\n variant=\"subtle\"\n size=\"xs\"\n href={router.path(\"adminUserDetails\", {\n params: { userId: item.userId },\n })}\n >\n <Text size=\"xs\" ff=\"monospace\">\n {item.userId.slice(0, 8)}...\n </Text>\n </ActionButton>\n ),\n },\n userAgent: {\n label: \"Device\",\n fit: true,\n value: (item) => (\n <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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"}
@@ -2,10 +2,10 @@ import { DataTable, Flex, Text } from "@alepha/ui";
2
2
  import { t } from "alepha";
3
3
  import { IconAlertTriangle, IconCheck, IconInfoCircle, IconX } from "@tabler/icons-react";
4
4
  import { useRouterState } from "alepha/react/router";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
5
  import { Badge, Group, Tooltip } from "@mantine/core";
7
6
  import { useClient } from "alepha/react";
8
7
  import { useI18n } from "alepha/react/i18n";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
9
 
10
10
  //#region ../../src/admin/components/users/AdminUserAudits.tsx
11
11
  const getSeverityColor = (severity) => {
@@ -175,4 +175,4 @@ var AdminUserAudits_default = AdminUserAudits;
175
175
 
176
176
  //#endregion
177
177
  export { AdminUserAudits_default as t };
178
- //# sourceMappingURL=AdminUserAudits-C7AN9jx7.js.map
178
+ //# sourceMappingURL=AdminUserAudits-CiUPN2BC.js.map