Files
image-platform/apps/admin/src/screens/dashboard/dashboard.screen.tsx

97 lines
3.8 KiB
TypeScript
Raw Normal View History

import { Alert, Button, Group, Paper, Stack, Text, Title } from "@mantine/core"
import { useDisclosure } from "@mantine/hooks"
import cl from "clsx"
import { useState } from "react"
import { assetsFactory } from "business/assets"
import { DASHBOARD_PIPELINE } from "./config/dashboard.config"
import { AssetDetailPanel } from "./parts/asset-detail-panel"
import { AssetsTable } from "./parts/assets-table"
import { CreateAssetModal } from "./parts/create-asset-modal"
import { PresetsPanel } from "./parts/presets-panel"
import { SummaryCards } from "./parts/summary-cards"
import styles from "./styles/dashboard.module.css"
import type { DashboardScreenProps } from "./types/dashboard.type"
const assets = assetsFactory()
/**
* Стартовый dashboard admin-приложения.
*
* Используется для:
* - отображения стартового состояния admin MVP
* - обзора будущих разделов и пайплайна генерации
*/
export const DashboardScreen = (props: DashboardScreenProps) => {
const { className, ...rootAttrs } = props
const [selectedPublicId, setSelectedPublicId] = useState<string | null>(null)
const [isCreateAssetOpen, createAssetModal] = useDisclosure(false)
const dashboard = assets.useAssetsDashboard()
const createAsset = assets.useCreateAsset()
const effectivePublicId = selectedPublicId ?? dashboard.assets[0]?.publicId ?? null
const overview = assets.useAssetOverview(effectivePublicId)
return (
<section {...rootAttrs} className={cl(styles.root, className)}>
<Stack gap="lg">
<Paper className={styles.hero} p={{ base: "xl", md: 42 }} radius="xl" shadow="xs" withBorder>
<Group align="flex-end" justify="space-between" gap="xl">
<div className={styles.heroContent}>
<Text className={styles.eyebrow}>Image Platform Admin</Text>
<Title className={styles.title}>Control plane для image delivery</Title>
<Text className={styles.lead}>
Управление allowed hosts, assets, source versions, presets и variant generation без
прямого доступа к storage-слою.
</Text>
</div>
<Button className={styles.primaryAction} onClick={createAssetModal.open} radius="xl" size="md">
Create asset
</Button>
</Group>
</Paper>
<SummaryCards isLoading={dashboard.isLoading} summary={dashboard.summary} />
<Group gap="xs" role="list" aria-label="Пайплайн генерации изображений">
{DASHBOARD_PIPELINE.map((step) => (
<Text className={styles.pipelineStep} key={step} role="listitem">
{step}
</Text>
))}
</Group>
{dashboard.error ? (
<Alert color="red" radius="lg" title="Backend API недоступен">
Проверьте, что backend запущен на `localhost:3001`, а Vite proxy доступен по `/api`.
</Alert>
) : null}
<div className={styles.workbench}>
<AssetsTable
assets={dashboard.assets}
isLoading={dashboard.isLoading}
onSelect={setSelectedPublicId}
selectedPublicId={effectivePublicId}
/>
<AssetDetailPanel overview={overview} publicId={effectivePublicId} />
</div>
<PresetsPanel
allowedSourceHosts={dashboard.allowedSourceHosts}
custom={dashboard.custom}
isLoading={dashboard.isLoading}
presets={dashboard.presets}
/>
</Stack>
<CreateAssetModal
action={createAsset}
onClose={createAssetModal.close}
onCreated={setSelectedPublicId}
opened={isCreateAssetOpen}
/>
</section>
)
}