@alepha/ui 0.15.4 → 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.
- package/dist/admin/AdminApiKeys-DsmGnHNh.js +3 -0
- package/dist/admin/AdminApiKeys-GMORg-1l.js +442 -0
- package/dist/admin/AdminApiKeys-GMORg-1l.js.map +1 -0
- package/dist/admin/AdminAudits-8SM96viT.js +3 -0
- package/dist/admin/{AdminAudits-Oh7iAfQa.js → AdminAudits-pkWrjq1Z.js} +2 -2
- package/dist/admin/{AdminAudits-Oh7iAfQa.js.map → AdminAudits-pkWrjq1Z.js.map} +1 -1
- package/dist/admin/AdminFiles-B56ocq4H.js +3 -0
- package/dist/admin/{AdminFiles-Cu8GHgQ3.js → AdminFiles-WeQbsCsl.js} +2 -2
- package/dist/admin/{AdminFiles-Cu8GHgQ3.js.map → AdminFiles-WeQbsCsl.js.map} +1 -1
- package/dist/admin/AdminJobs-B-q9iGO3.js +697 -0
- package/dist/admin/AdminJobs-B-q9iGO3.js.map +1 -0
- package/dist/admin/AdminJobs-CED1syCn.js +3 -0
- package/dist/admin/AdminLayout-BX4FIpXv.css +143 -0
- package/dist/admin/AdminLayout-BX4FIpXv.css.map +1 -0
- package/dist/admin/{AdminLayout-QJLIesuG.js → AdminLayout-BqZiXx4H.js} +3 -2
- package/dist/admin/AdminLayout-BqZiXx4H.js.map +1 -0
- package/dist/admin/AdminNotifications-B0B1rdc4.js +3 -0
- package/dist/admin/{AdminNotifications-CgYkBuG_.js → AdminNotifications-Ds5Un0NJ.js} +2 -2
- package/dist/admin/{AdminNotifications-CgYkBuG_.js.map → AdminNotifications-Ds5Un0NJ.js.map} +1 -1
- package/dist/admin/{AdminParameters-Cl-R0nXt.js → AdminParameters-BU3lATdJ.js} +1 -1
- package/dist/admin/{AdminParameters-hjNG_KXb.js → AdminParameters-CfDUpc78.js} +4 -4
- package/dist/admin/{AdminParameters-hjNG_KXb.js.map → AdminParameters-CfDUpc78.js.map} +1 -1
- package/dist/admin/AdminSessions-BDGK2MS6.js +3 -0
- package/dist/admin/{AdminSessions-Bey9cuy1.js → AdminSessions-DzIOxM3b.js} +2 -2
- package/dist/admin/{AdminSessions-Bey9cuy1.js.map → AdminSessions-DzIOxM3b.js.map} +1 -1
- package/dist/admin/{AdminUserAudits-C7AN9jx7.js → AdminUserAudits-CiUPN2BC.js} +2 -2
- package/dist/admin/{AdminUserAudits-C7AN9jx7.js.map → AdminUserAudits-CiUPN2BC.js.map} +1 -1
- package/dist/admin/{AdminUserAudits-Cp_ERd2g.js → AdminUserAudits-Cj79gENT.js} +1 -1
- package/dist/admin/{AdminUserCreate-BVIm4JdN.js → AdminUserCreate-BwQKr4xE.js} +2 -2
- package/dist/admin/{AdminUserCreate-BVIm4JdN.js.map → AdminUserCreate-BwQKr4xE.js.map} +1 -1
- package/dist/admin/{AdminUserCreate-C1aInRDk.js → AdminUserCreate-Cq-mUmBs.js} +1 -1
- package/dist/admin/{AdminUserDetails-Dcn3OwMC.js → AdminUserDetails-DRjVAPFd.js} +1 -1
- package/dist/admin/{AdminUserDetails-yM4x8JE6.js → AdminUserDetails-uqtC5aJ1.js} +2 -2
- package/dist/admin/{AdminUserDetails-yM4x8JE6.js.map → AdminUserDetails-uqtC5aJ1.js.map} +1 -1
- package/dist/admin/{AdminUserLayout-gb-nbggz.js → AdminUserLayout-CGzmHHby.js} +1 -1
- package/dist/admin/{AdminUserLayout-BnfBC1gD.js → AdminUserLayout-CiPay35T.js} +2 -2
- package/dist/admin/{AdminUserLayout-BnfBC1gD.js.map → AdminUserLayout-CiPay35T.js.map} +1 -1
- package/dist/admin/{AdminUserSessions-kmkXG-xf.js → AdminUserSessions-DAE8Nf1F.js} +2 -2
- package/dist/admin/{AdminUserSessions-kmkXG-xf.js.map → AdminUserSessions-DAE8Nf1F.js.map} +1 -1
- package/dist/admin/AdminUserSessions-DcdzuNZ9.js +3 -0
- package/dist/admin/AdminUserSettings-D7V6-ceX.js +3 -0
- package/dist/admin/{AdminUserSettings-DZ9iWhJW.js → AdminUserSettings-EbahaV2a.js} +2 -2
- package/dist/admin/{AdminUserSettings-DZ9iWhJW.js.map → AdminUserSettings-EbahaV2a.js.map} +1 -1
- package/dist/admin/AdminUsers-D9nyzGqQ.js +3 -0
- package/dist/admin/{AdminUsers-D6Y5K8Am.js → AdminUsers-Dcjh0KNW.js} +2 -2
- package/dist/admin/{AdminUsers-D6Y5K8Am.js.map → AdminUsers-Dcjh0KNW.js.map} +1 -1
- package/dist/admin/index.d.ts +24 -36
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +52 -169
- package/dist/admin/index.js.map +1 -1
- package/dist/auth/AuthLayout-BaD7RD2h.css +143 -0
- package/dist/auth/AuthLayout-BaD7RD2h.css.map +1 -0
- package/dist/auth/AuthLayout-Dj5K4SIN.js.map +1 -1
- package/dist/auth/index.d.ts +9 -1
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1 -2
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +13 -21
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +26 -38
- package/dist/core/index.js.map +1 -1
- package/dist/demo/{DemoLogin-S-b15cmE.js → DemoLogin-CvCG2WVh.js} +3 -1
- package/dist/demo/{DemoLogin-S-b15cmE.js.map → DemoLogin-CvCG2WVh.js.map} +1 -1
- package/dist/demo/{DemoRegister-B29MdAaZ.js → DemoRegister-CmeHbOAs.js} +3 -1
- package/dist/demo/{DemoRegister-B29MdAaZ.js.map → DemoRegister-CmeHbOAs.js.map} +1 -1
- package/dist/demo/{DemoResetPassword-CPTy88iK.js → DemoResetPassword-CKO5iA_6.js} +3 -1
- package/dist/demo/{DemoResetPassword-CPTy88iK.js.map → DemoResetPassword-CKO5iA_6.js.map} +1 -1
- package/dist/demo/index.js +3 -3
- package/package.json +3 -3
- package/src/admin/AdminRouter.ts +34 -0
- package/src/admin/components/AdminLayout.tsx +2 -0
- package/src/admin/components/jobs/AdminJobs.tsx +733 -119
- package/src/admin/components/keys/AdminApiKeys.tsx +537 -0
- package/src/admin/components/parameters/AdminParameters.tsx +2 -3
- package/src/admin/index.ts +3 -5
- package/src/auth/AuthRouter.ts +1 -2
- package/src/auth/components/AuthLayout.tsx +1 -0
- package/src/core/components/buttons/ActionButton.tsx +15 -2
- package/src/core/components/buttons/DarkModeButton.css +6 -0
- package/src/core/components/buttons/DarkModeButton.tsx +18 -71
- package/src/core/components/buttons/LanguageButton.tsx +2 -7
- package/src/core/components/buttons/ThemeButton.tsx +2 -6
- package/src/core/components/layout/AdminShell.tsx +17 -1
- package/src/core/components/layout/AppBar.tsx +5 -8
- package/src/core/index.ts +0 -1
- package/src/core/styles.css +1 -0
- package/src/demo/components/auth/DemoLogin.tsx +2 -0
- package/src/demo/components/auth/DemoRegister.tsx +2 -0
- package/src/demo/components/auth/DemoResetPassword.tsx +2 -0
- package/dist/admin/AdminAudits-BU-p1g7A.js +0 -3
- package/dist/admin/AdminFiles-Bg9feLFH.js +0 -3
- package/dist/admin/AdminLayout-BfeFXiul.js +0 -3
- package/dist/admin/AdminLayout-QJLIesuG.js.map +0 -1
- package/dist/admin/AdminNotifications-DmfGPqHe.js +0 -3
- package/dist/admin/AdminSessions-Cn4_jB04.js +0 -3
- package/dist/admin/AdminUserSessions-rvA0ztxn.js +0 -3
- package/dist/admin/AdminUserSettings-Dg-wTRzN.js +0 -3
- package/dist/admin/AdminUsers-RCaxccEW.js +0 -3
- 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":";;;;;;;;;;;AAyEA,MAAM,kBACJ,OACA,QACW;CACX,MAAM,YAAY,IAAI,KAAK,MAAM,CAAC,SAAS;CAE3C,MAAM,YADU,MAAM,IAAI,KAAK,IAAI,CAAC,SAAS,GAAG,KAAK,KAAK,IAC/B;AAE3B,KAAI,WAAW,IAAM,QAAO,GAAG,SAAS;AACxC,KAAI,WAAW,IAAO,QAAO,IAAI,WAAW,KAAM,QAAQ,EAAE,CAAC;AAC7D,KAAI,WAAW,KACb,QAAO,GAAG,KAAK,MAAM,WAAW,IAAM,CAAC,IAAI,KAAK,MAAO,WAAW,MAAS,IAAK,CAAC;AACnF,QAAO,GAAG,KAAK,MAAM,WAAW,KAAQ,CAAC,IAAI,KAAK,MAAO,WAAW,OAAW,IAAM,CAAC;;AAGxF,MAAM,kBAAkB,WAAmB;AACzC,SAAQ,QAAR;EACE,KAAK,YACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,UACH,QAAO;EACT,QACE,QAAO;;;AAIb,MAAM,iBAAiB,QAAgB,OAAO,OAAO;AACnD,SAAQ,QAAR;EACE,KAAK,YACH,QAAO,oBAAC,mBAAsB,OAAQ;EACxC,KAAK,SACH,QAAO,oBAAC,eAAkB,OAAQ;EACpC,KAAK,UACH,QAAO,oBAAC,kBAAqB,OAAQ;EACvC,QACE,QAAO,oBAAC,aAAgB,OAAQ;;;AAItC,MAAM,oBAAoB,UAAkB;AAC1C,SAAQ,OAAR;EACE,KAAK,QACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,KAAK,QACH,QAAO;EACT,QACE,QAAO;;;AAiBb,MAAM,WAAW,UAAwB;CACvC,MAAM,EAAE,KAAK,OAAO,cAAc,WAAW,UAAU,eAAe;CACtE,MAAM,QAAQ,iBAAiB;AAEX,UAChB,MAAM,QAAQ,KACX,MAAM,YAAY,MAAM,QAAS;AAIxC,QACE,qBAAC;EACC,GAAE;EACF,QAAO;EACP;EACA,eAAe,SAAS,IAAI;EAC5B,OAAO;GACL,QAAQ;GACR,aAAa,aAAa,MAAM,OAAO,KAAK,KAAK;GACjD,iBAAiB,aACb,oCACA;GACJ,YAAY;GACb;aAED,qBAAC;GAAM,SAAQ;GAAgB,IAAG;cAChC,qBAAC;IAAM,KAAI;eACT,oBAAC;KACC,MAAK;KACL,QAAO;KACP,SAAQ;KACR,OACE,OAAO,aAAa,eAAe,MAAM,WAAW,GAAG;eAGzD,oBAAC,iBAAc,MAAM,KAAM;MACjB,EACZ,oBAAC;KAAK,MAAK;KAAK,IAAI;KAAK,IAAG;eACzB;MACI;KACD,EACR,oBAAC;IAAQ,OAAM;IAAuB,UAAS;cAC7C,oBAAC;KACC,MAAK;KACL,SAAQ;KACR,OAAM;KACN,SAAS;KACT,UAAU,MAAM;AACd,QAAE,iBAAiB;AACnB,gBAAU,IAAI;;eAGhB,oBAAC,kBAAe,MAAM,KAAM;MACjB;KACL;IACJ,EAEP,QACC;GACE,qBAAC;IAAM,KAAI;IAAK,IAAG;;KACjB,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;gBACzB,MAAM;OACF,IACH;KACN,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;MAAY,GAAE;gBACvC,MAAM;OACF,IACH;KACN,qBAAC,kBACC,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,IAAG;MAAY,IAAI;gBAAK;OAE5C,EACP,oBAAC;MAAK,MAAK;MAAK,IAAI;MAAK,IAAG;MAAY,GAAE;gBACvC,MAAM;OACF,IACH;;KACA;GAER,qBAAC,SAAS;IAAK,MAAK;IAAK,QAAO;;KAC9B,oBAAC;MAAQ,OAAO,GAAG,MAAM,UAAU;gBACjC,oBAAC,SAAS;OACR,OAAQ,MAAM,YAAY,KAAK,IAAI,MAAM,OAAO,EAAE,GAAI;OACtD,OAAM;QACN;OACM;KACV,oBAAC;MAAQ,OAAO,GAAG,MAAM,OAAO;gBAC9B,oBAAC,SAAS;OACR,OAAQ,MAAM,SAAS,KAAK,IAAI,MAAM,OAAO,EAAE,GAAI;OACnD,OAAM;QACN;OACM;KACV,oBAAC;MAAQ,OAAO,GAAG,MAAM,QAAQ;gBAC/B,oBAAC,SAAS;OACR,OAAQ,MAAM,UAAU,KAAK,IAAI,MAAM,OAAO,EAAE,GAAI;OACpD,OAAM;QACN;OACM;;KACI;GAEf,MAAM,WACL,qBAAC;IAAK,MAAK;IAAK,GAAE;IAAS,IAAG;;KAAK;KACtB,eAAe,MAAM,yBAAS,IAAI,MAAM,CAAC;KAAC;;KAChD;MAER,GAEH,qBAAC;GAAM,KAAI;cACT,oBAAC;IAAS,QAAQ;IAAG,QAAO;KAAO,EACnC,oBAAC;IAAS,QAAQ;IAAG,OAAM;IAAM,QAAO;KAAO;IACzC;GAEL;;AAaX,MAAM,sBAAsB,UAAmC;CAC7D,MAAM,EAAE,MAAM,UAAU;AAExB,KAAI,CAAC,MAAM,UAAU,CAAC,MACpB,QACE,oBAAC;EAAI,GAAE;YACL,oBAAC;GAAK,MAAK;GAAK,GAAE;GAAS,IAAG;aAAS;IAEhC;GACH;AAIV,QACE,oBAAC;EAAW,GAAG;EAAK,MAAK;YACvB,qBAAC;GACC,GAAE;GACF,OAAO;IACL,YAAY;IACZ,UAAU;IACV,YAAY;IACb;cAEA,SACC,oBAAC;IAAM,GAAE;IAAK,IAAG;IAAK,IAAG;IAAiC,QAAO;cAC/D,qBAAC;KAAM,KAAI;KAAK,OAAM;gBACpB,oBAAC;MACC,MAAM;MACN,OAAM;OACN,EACF,oBAAC;MACC,MAAK;MACL,GAAE;MACF,OAAO;OAAE,YAAY;OAAY,WAAW;OAAc;gBAEzD;OACI;MACD;KACF,EAGT,MAAM,KAAK,KAAK,UACf,qBAAC;IAAkB,KAAI;IAAK,OAAM;IAAa,IAAI;IAAG,MAAK;;KACzD,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,OAAO;OAAE,UAAU;OAAI,YAAY;OAAG;gBAC9D,IAAI,KAAK,IAAI,UAAU,CAAC,oBAAoB;OACxC;KACP,oBAAC;MACC,MAAK;MACL,SAAQ;MACR,OAAO,iBAAiB,IAAI,MAAM;MAClC,OAAO,EAAE,UAAU,IAAI;gBAEtB,IAAI;OACC;KACR,oBAAC;MAAK,MAAK;MAAK,GAAE;MAAS,OAAO;OAAE,UAAU;OAAK,YAAY;OAAG;gBAC/D,IAAI;OACA;KACP,oBAAC;MAAK,MAAK;MAAK,OAAO,EAAE,WAAW,cAAc;gBAC/C,IAAI;OACA;;MAjBG,MAkBJ,CACR;IACE;GACK;;AAQjB,MAAM,kBAAkB;CACtB,MAAM,SAAS,WAA+B;CAC9C,MAAM,EAAE,MAAM,SAAS;CACvB,MAAM,QAAQ,UAAU;CACxB,MAAM,SAAS,WAAW;CAE1B,MAAM,CAAC,MAAM,WAAW,SAAmB,EAAE,CAAC;CAC9C,MAAM,CAAC,UAAU,eAAe,yBAAgC,IAAI,KAAK,CAAC;CAC1E,MAAM,CAAC,aAAa,kBAAkB,SAAwB,KAAK;CACnE,MAAM,CAAC,gBAAgB,qBAAqB,yBAAsB,IAAI,KAAK,CAAC;CAC5E,MAAM,CAAC,YAAY,iBAAiB,SAAS,EAAE;CAC/C,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,WAAW,gBAAgB,SAAwB,WAAW;AAGrE,iBAAgB;EACd,MAAM,WAAW,YAAY;AAC3B,OAAI;IACF,MAAM,UAAU,MAAM,OAAO,SAAS;AACtC,YAAQ,QAAQ;IAGhB,MAAM,2BAAW,IAAI,KAAuB;AAC5C,SAAK,MAAM,OAAO,SAAS;KAKzB,MAAM,SAJa,MAAM,OAAO,iBAAiB,EAC/C,OAAO;MAAE;MAAK,MAAM;MAAK,EAC1B,CAAC,EAEuB,WAAW,EAAE;KACtC,MAAM,YAAY,MAAM,QACrB,MAA0B,EAAE,WAAW,YACzC,CAAC;KACF,MAAM,SAAS,MAAM,QAClB,MAA0B,EAAE,WAAW,SACzC,CAAC;KACF,MAAM,UAAU,MAAM,QACnB,MAA0B,EAAE,WAAW,UACzC,CAAC;KAEF,MAAM,iBAAiB,MAAM,QAC1B,MAA0B,EAAE,WAAW,eAAe,EAAE,WAC1D;KACD,MAAM,cACJ,eAAe,SAAS,IACpB,eAAe,QAAQ,KAAa,MAA0B;AAI5D,aAAO,OAFL,IAAI,KAAK,EAAE,WAAY,CAAC,SAAS,GACjC,IAAI,KAAK,EAAE,UAAU,CAAC,SAAS;QAEhC,EAAE,GAAG,eAAe,SACvB;KAEN,MAAM,WAAW,MAAM;AAEvB,cAAS,IAAI,KAAK;MAChB,MAAM;MACN,OAAO,MAAM;MACb;MACA;MACA;MACA;MACA,SAAS,UAAU,YACf,IAAI,KAAK,SAAS,UAAU,GAC5B;MACJ,YAAY,UAAU;MACvB,CAAC;;AAGJ,gBAAY,SAAS;YACd,OAAO;AACd,UAAM,OAAO,sBAAsB;aAC3B;AACR,eAAW,MAAM;;;AAIrB,YAAU;IACT,CAAC,WAAW,CAAC;CAEhB,MAAM,mBAAmB,YACvB,OAAO,QAAgB;AAQrB,MAAI,CAPc,MAAM,OAAO,QAAQ;GACrC,OAAO;GACP,SAAS,qCAAqC,IAAI;GAClD,cAAc;GACd,cAAc;GACf,CAAC,CAEc;AAEhB,qBAAmB,SAAS,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC;AAEnD,MAAI;AACF,SAAM,OAAO,WAAW,EAAE,MAAM,EAAE,MAAM,KAAK,EAAE,CAAC;AAChD,SAAM,QAAQ,QAAQ,IAAI,0BAA0B;AACpD,kBAAe,MAAM,IAAI,EAAE;WACpB,OAAO;AACd,SAAM,OAAO,0BAA0B,IAAI,GAAG;YACtC;AACR,sBAAmB,SAAS;IAC1B,MAAM,OAAO,IAAI,IAAI,KAAK;AAC1B,SAAK,OAAO,IAAI;AAChB,WAAO;KACP;;IAGN;EAAC;EAAQ;EAAQ;EAAM,CACxB;CAED,MAAM,UAAU,EAAE,OAAO;EACvB,KAAK,EAAE,SACL,EAAE,OAAO,EACP,UAAU,EACR,OAAO,EAAE,KAAK,cAAc,QAAQ,CAAC,MAAM,CAAC,EAC7C,EACF,CAAC,CACH;EACD,QAAQ,EAAE,SAAS,EAAE,KAAK;GAAC;GAAW;GAAU;GAAY,CAAC,CAAC;EAC/D,CAAC;CAGF,MAAM,cAAc;EAClB,OAAO,MAAM,KAAK,SAAS,QAAQ,CAAC,CAAC,QAAQ,KAAK,MAAM,MAAM,EAAE,OAAO,EAAE;EACzE,WAAW,MAAM,KAAK,SAAS,QAAQ,CAAC,CAAC,QACtC,KAAK,MAAM,MAAM,EAAE,WACpB,EACD;EACD,QAAQ,MAAM,KAAK,SAAS,QAAQ,CAAC,CAAC,QAAQ,KAAK,MAAM,MAAM,EAAE,QAAQ,EAAE;EAC3E,SAAS,MAAM,KAAK,SAAS,QAAQ,CAAC,CAAC,QACpC,KAAK,MAAM,MAAM,EAAE,SACpB,EACD;EACF;CAED,MAAM,cACJ,YAAY,QAAQ,IAChB,KAAK,MAAO,YAAY,YAAY,YAAY,QAAS,IAAI,GAC7D;AAEN,QACE,qBAAC;EAAK,MAAM;EAAG,WAAU;EAAS,KAAI;aAEpC,qBAAC;GAAW,MAAM;IAAE,MAAM;IAAG,IAAI;IAAG;GAAE,SAAQ;;IAC5C,oBAAC;KAAM,GAAE;KAAK,QAAO;KAAK;eACxB,qBAAC;MAAM,SAAQ;iBACb,qBAAC,kBACC,oBAAC;OAAK,MAAK;OAAK,GAAE;OAAS,IAAG;OAAY,IAAI;iBAAK;QAE5C,EACP,oBAAC;OAAK,MAAK;OAAK,IAAI;OAAK,IAAG;iBACzB,KAAK;QACD,IACH,EACN,oBAAC;OAAU,MAAK;OAAK,QAAO;OAAK,SAAQ;OAAQ,OAAM;iBACrD,oBAAC,iBAAc,MAAM,KAAM;QACjB;OACN;MACF;IAER,oBAAC;KAAM,GAAE;KAAK,QAAO;KAAK;eACxB,qBAAC;MAAM,SAAQ;iBACb,qBAAC,kBACC,oBAAC;OAAK,MAAK;OAAK,GAAE;OAAS,IAAG;OAAY,IAAI;iBAAK;QAE5C,EACP,oBAAC;OAAK,MAAK;OAAK,IAAI;OAAK,IAAG;iBACzB,YAAY;QACR,IACH,EACN,oBAAC;OAAU,MAAK;OAAK,QAAO;OAAK,SAAQ;OAAQ,OAAM;iBACrD,oBAAC,aAAU,MAAM,KAAM;QACb;OACN;MACF;IAER,oBAAC;KAAM,GAAE;KAAK,QAAO;KAAK;eACxB,qBAAC;MAAM,SAAQ;iBACb,qBAAC,kBACC,oBAAC;OAAK,MAAK;OAAK,GAAE;OAAS,IAAG;OAAY,IAAI;iBAAK;QAE5C,EACP,qBAAC;OACC,MAAK;OACL,IAAI;OACJ,IAAG;OACH,GACE,eAAe,KACX,SACA,eAAe,KACb,WACA;kBAGP,aAAY;QACR,IACH,EACN,oBAAC;OACC,MAAM;OACN,WAAW;OACX;OACA,UAAU,CACR;QACE,OAAO;QACP,OACE,eAAe,KACX,SACA,eAAe,KACb,WACA;QACT,CACF;QACD;OACI;MACF;IAER,oBAAC;KAAM,GAAE;KAAK,QAAO;KAAK;eACxB,qBAAC;MAAM,SAAQ;iBACb,qBAAC,kBACC,oBAAC;OAAK,MAAK;OAAK,GAAE;OAAS,IAAG;OAAY,IAAI;iBAAK;QAE5C,EACP,oBAAC;OAAK,MAAK;OAAK,IAAI;OAAK,IAAG;OAAY,GAAE;iBACvC,YAAY;QACR,IACH,EACN,oBAAC;OACC,MAAK;OACL,QAAO;OACP,SAAQ;OACR,OAAO,YAAY,UAAU,IAAI,SAAS;iBAE1C,oBAAC,kBAAe,MAAM,KAAM;QAClB;OACN;MACF;;IACG,EAGb,qBAAC;GAAK,OAAO;GAAW,UAAU;;IAChC,qBAAC,KAAK,mBACJ,oBAAC,KAAK;KAAI,OAAM;KAAW,aAAa,oBAAC,iBAAc,MAAM,KAAM;eAAE;MAE1D,EACX,oBAAC,KAAK;KAAI,OAAM;KAAa,aAAa,oBAAC,aAAU,MAAM,KAAM;eAAE;MAExD,IACD;IAEZ,qBAAC,KAAK;KAAM,OAAM;KAAW,IAAG;gBAC9B,qBAAC;MAAM,SAAQ;MAAgB,IAAG;iBAChC,qBAAC;OAAK,MAAK;OAAK,GAAE;;QACf,KAAK;QAAO;QAAgB,KAAK,WAAW,IAAI,MAAM;;QAClD,EACP,oBAAC;OACC,MAAK;OACL,SAAQ;OACR,aAAa,oBAAC,eAAY,MAAM,KAAM;OACtC,eAAe,eAAe,MAAM,IAAI,EAAE;iBAC3C;QAEc;OACT,EAEP,UACC,oBAAC;MAAW,MAAM;OAAE,MAAM;OAAG,IAAI;OAAG,IAAI;OAAG;MAAE,SAAQ;gBAClD;OAAC;OAAG;OAAG;OAAE,CAAC,KAAK,MACd,oBAAC;OAAiB,QAAQ;OAAK,QAAO;SAAvB,EAA8B,CAC7C;OACS,GACX,KAAK,WAAW,IAClB,qBAAC;MAAM,GAAE;MAAK,QAAO;MAAK;MAAW,IAAG;;OACtC,oBAAC;QAAc,MAAM;QAAI,OAAM;SAAgC;OAC/D,oBAAC;QAAK,MAAK;QAAK,IAAI;QAAK,IAAG;kBAAK;SAE1B;OACP,oBAAC;QAAK,MAAK;QAAK,GAAE;QAAS,IAAG;kBAAK;SAE5B;;OACD,GAER,oBAAC;MAAW,MAAM;OAAE,MAAM;OAAG,IAAI;OAAG,IAAI;OAAG;MAAE,SAAQ;gBAClD,KAAK,KAAK,QACT,oBAAC;OAEM;OACL,OAAO,SAAS,IAAI,IAAI;OACxB,cAAc,eAAe,IAAI,IAAI;OACrC,WAAW;OACX,UAAU;OACV,YAAY,gBAAgB;SANvB,IAOL,CACF;OACS;MAEJ;IAEb,oBAAC,KAAK;KAAM,OAAM;KAAa,IAAG;eAChC,oBAAC;MAEC;MACA,aAAa;MACb,eAAe;OACb,kBAAkB;OAClB,SAAS;OACV;MACD,YAAY;OACV,mBAAmB;OACnB,iBAAiB;OACjB,kBAAkB;OACnB;MACD,iBAAiB,KAAK,QAAQ,SAAS;AACrC,WAAI,QAAQ,SAAS,QAAQ,SAC3B,QAAO,KAAK,QAAQ;;MAGf;MACT,OAAO,OAAO,YAAY;AAQxB,cAPiB,MAAM,OAAO,iBAAiB,EAC7C,OAAO;QACL,GAAG;QACH,KAAK,eAAe,QAAQ;QAC7B,EACF,CAAC;;MAIJ,SAAS;OACP,KAAK;QACH,OAAO;QACP,QAAQ,SACN,oBAAC;SAAK,MAAK;SAAK,IAAI;SAAK,IAAG;mBACzB,KAAK;UACD;QAEV;OACD,QAAQ;QACN,OAAO;QACP,KAAK;QACL,QAAQ,SACN,oBAAC;SACC,MAAK;SACL,SAAQ;SACR,OAAO,eAAe,KAAK,OAAO;SAClC,aAAa,cAAc,KAAK,QAAQ,GAAG;mBAE1C,KAAK;UACA;QAEX;OACD,UAAU;QACR,OAAO;QACP,KAAK;QACL,QAAQ,SACN,oBAAC;SAAK,MAAK;SAAK,GAAE;SAAS,IAAG;mBAC3B,eAAe,KAAK,WAAW,KAAK,WAAW;UAC3C;QAEV;OACD,MAAM;QACJ,OAAO;QACP,KAAK;QACL,QAAQ,SAAS;SACf,MAAM,WACH,KAAK,MAAiC,UAAU;SACnD,MAAM,aACH,KAAK,MAAiC,QACpC,QAAQ,IAAI,UAAU,QACxB,CAAC,UAAU;AAEd,gBACE,qBAAC;UAAM,KAAK;qBACV,qBAAC;WAAM,MAAK;WAAK,SAAQ;WAAQ,OAAM;sBACpC,UAAS;YACJ,EACP,aAAa,KACZ,qBAAC;WAAM,MAAK;WAAK,SAAQ;WAAQ,OAAM;sBACpC,YAAW;YACN;WAEJ;;QAGb;OACD,OAAO;QACL,OAAO;QACP,QAAQ,SACN,KAAK,QACH,oBAAC;SAAQ,OAAO,KAAK;SAAO;SAAU,GAAG;mBACvC,oBAAC;UAAK,MAAK;UAAK,GAAE;UAAM,WAAW;oBAChC,KAAK;WACD;UACC,GAEV,oBAAC;SAAK,MAAK;SAAK,GAAE;mBAAS;UAEpB;QAEZ;OACD,WAAW;QACT,OAAO;QACP,KAAK;QACL,QAAQ,SACN,oBAAC;SAAK,MAAK;SAAK,GAAE;mBACf,EAAE,KAAK,WAAW,EAAE,MAAM,WAAW,CAAC;UAClC;QAEV;OACF;MACD,QAAQ,SACN,oBAAC;OAAI,IAAG;OAA8B,GAAG;iBACvC,oBAAC;QACC,MAAM,KAAK;QACX,OAAO,KAAK;SACZ;QACE;MAER,WAAW,SAAS,QAAQ,KAAK,MAAM,UAAU,KAAK,MAAM;QAtHvD,WAuHL;MACS;;IACR;GACF;;AAIX,wBAAe"}
|
|
@@ -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
|
|
39
|
-
//# sourceMappingURL=AdminLayout-
|
|
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"}
|
|
@@ -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-
|
|
154
|
+
//# sourceMappingURL=AdminNotifications-Ds5Un0NJ.js.map
|
package/dist/admin/{AdminNotifications-CgYkBuG_.js.map → AdminNotifications-Ds5Un0NJ.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminNotifications-
|
|
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,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
|
|
487
|
+
const [current] = await Promise.all([client.getCurrent({ params: { name } })]);
|
|
488
488
|
setConfigValue(current);
|
|
489
|
-
setHistory(
|
|
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-
|
|
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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"}
|
|
@@ -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-
|
|
151
|
+
//# sourceMappingURL=AdminSessions-DzIOxM3b.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminSessions-
|
|
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-
|
|
178
|
+
//# sourceMappingURL=AdminUserAudits-CiUPN2BC.js.map
|