Files
cloudflare-domain-manager/apps/web/src/components/reui-kit/ops-dashboard.tsx
T
DenozordecandCursor 9c00b268dc
quality / changes (push) Successful in 8s
quality / api (push) Skipped
quality / commitlint (push) Skipped
quality / docker-check (push) Skipped
CD / update-wiki (push) Successful in 5s
quality / web (push) Successful in 1m6s
CD / quality (push) Successful in 1m21s
CD / publish (push) Successful in 2m36s
fix(ui): выровнять очередь внимания дашборда под Frame и IconTile
Соседние Frame-колонки как в EvoBGP вместо вложенной оболочки; compact empty через IconTile elevated; счётчики ReUI Badge.

Co-authored-by: Cursor <[email protected]>
2026-08-19 01:11:36 +07:00

87 lines
2.5 KiB
TypeScript

import type { ReactNode } from 'react'
import { Skeleton } from '@cfdm/ui/components/skeleton'
import { KpiStatGrid, type KpiStatCard } from './kpi-stat-grid'
export type OpsKpiCard = KpiStatCard
interface OpsDashboardProps {
kpiCards: OpsKpiCard[]
/** Optional slot under KPI (e.g. QuickActionGrid) */
afterKpi?: ReactNode
charts: ReactNode
queue: ReactNode
queueTitle?: string
queueDescription?: string
isLoading?: boolean
}
/**
* Ops dashboard: KPI → optional Quick Actions → charts → queue.
* Queue is a sibling Frame grid — never wrap Frames inside another Frame.
* @see https://reui.io/preview/base/dashboard-1
* @see https://reui.io/preview/base/stats-12
* @see https://reui.io/docs/components/base/frame
*/
const rootClassName =
'text-foreground @container flex w-full flex-col gap-2 md:gap-3'
function OpsDashboardSkeleton() {
return (
<div className={rootClassName}>
<KpiStatGrid cards={[]} isLoading skeletonCount={4} />
<div className="grid gap-2 @3xl:grid-cols-2">
<Skeleton className="h-64 w-full rounded-xl" />
<Skeleton className="h-64 w-full rounded-xl" />
</div>
<div className="grid gap-2 @3xl:grid-cols-3">
<Skeleton className="h-40 w-full rounded-xl" />
<Skeleton className="h-40 w-full rounded-xl" />
<Skeleton className="h-40 w-full rounded-xl" />
</div>
</div>
)
}
export function OpsDashboard({
kpiCards,
afterKpi,
charts,
queue,
queueTitle = 'Требуют внимания',
queueDescription = 'Проблемы health-check, истекающие сертификаты и домены без группы',
isLoading = false,
}: OpsDashboardProps) {
if (isLoading) {
return <OpsDashboardSkeleton />
}
return (
<div className={rootClassName}>
<section aria-label="Ключевые метрики">
<KpiStatGrid cards={kpiCards} skeletonCount={4} />
</section>
{afterKpi}
<section
aria-label="Аналитика"
className="grid min-w-0 items-start gap-2 @3xl:grid-cols-2"
>
{charts}
</section>
<section aria-label={queueTitle} className="flex min-w-0 flex-col gap-2">
<div className="flex min-w-0 flex-col gap-1">
<h2 className="text-sm font-semibold tracking-tight">{queueTitle}</h2>
{queueDescription ? (
<p className="text-muted-foreground max-w-prose text-sm">
{queueDescription}
</p>
) : null}
</div>
{queue}
</section>
</div>
)
}