feat: добавить рабочий dashboard admin

- добавлен Mantine theme provider и AppShell layout\n- сгенерирован Backend API клиент и добавлены infra/business хуки\n- добавлены таблица assets, detail/presets panels и create asset modal
This commit is contained in:
2026-05-05 15:02:55 +03:00
parent 72f9386f57
commit 6a018826f5
50 changed files with 2870 additions and 120 deletions

View File

@@ -0,0 +1,126 @@
import { Anchor, Badge, Code, Group, Paper, ScrollArea, Skeleton, Stack, Table, Text, Title } from "@mantine/core"
import { ASSET_STATUS_COLORS, VARIANT_STATUS_COLORS } from "../../config/dashboard.config"
import { formatDateTime } from "../../lib/format-date"
import type { AssetDetailPanelProps } from "./types/asset-detail-panel-props.type"
/**
* Детали выбранного asset и его variants.
*
* Используется для:
* - отображения source metadata
* - отображения статусов generated variants
*/
export const AssetDetailPanel = (props: AssetDetailPanelProps) => {
const { overview, publicId } = props
const { asset, variants } = overview
if (!publicId) {
return (
<Paper bg="white" p="xl" radius="xl" shadow="xs" withBorder>
<Title order={2} size="h3">
Asset detail
</Title>
<Text c="dimmed" mt="sm">
Выберите asset из таблицы, чтобы увидеть source URL и variants.
</Text>
</Paper>
)
}
return (
<Paper bg="white" p="xl" radius="xl" shadow="xs" withBorder>
<Group align="start" justify="space-between" mb="lg">
<div>
<Title order={2} size="h3">
Asset detail
</Title>
<Text c="dimmed" fz="sm">
{publicId}
</Text>
</div>
{asset ? (
<Badge color={ASSET_STATUS_COLORS[asset.status] ?? "gray"} radius="xl" variant="light">
{asset.status}
</Badge>
) : null}
</Group>
{overview.isLoading ? (
<Skeleton height={260} radius="lg" />
) : asset ? (
<Stack gap="lg">
<Stack gap={6}>
<Text c="dimmed" fz="sm">
Source URL
</Text>
<Anchor href={asset.sourceUrl} target="_blank">
{asset.sourceUrl}
</Anchor>
</Stack>
<Group gap="xs">
<Badge color="violet" radius="xl" variant="light">
v{asset.currentVersion}
</Badge>
<Badge color="gray" radius="xl" variant="light">
{asset.sourceHost}
</Badge>
<Badge color="gray" radius="xl" variant="light">
updated {formatDateTime(asset.updatedAt)}
</Badge>
</Group>
<Stack gap="sm">
<Group justify="space-between">
<Title order={3} size="h4">
Variants
</Title>
<Badge radius="xl" variant="light">
{variants.length}
</Badge>
</Group>
{variants.length > 0 ? (
<ScrollArea>
<Table verticalSpacing="sm">
<Table.Thead>
<Table.Tr>
<Table.Th>Preset</Table.Th>
<Table.Th>Format</Table.Th>
<Table.Th>Size</Table.Th>
<Table.Th>Status</Table.Th>
</Table.Tr>
</Table.Thead>
<Table.Tbody>
{variants.map((variant) => (
<Table.Tr key={variant.id}>
<Table.Td>
<Code>{variant.preset}</Code>
</Table.Td>
<Table.Td>{variant.format}</Table.Td>
<Table.Td>
{variant.width}x{variant.height || "auto"} q{variant.quality}
</Table.Td>
<Table.Td>
<Badge color={VARIANT_STATUS_COLORS[variant.status] ?? "gray"} radius="xl" variant="light">
{variant.status}
</Badge>
</Table.Td>
</Table.Tr>
))}
</Table.Tbody>
</Table>
</ScrollArea>
) : (
<Text c="dimmed">Variants для текущей версии пока не созданы.</Text>
)}
</Stack>
</Stack>
) : (
<Text c="dimmed">Asset не найден или ещё загружается.</Text>
)}
</Paper>
)
}