feat(web): align ops screens with ReUI PRO kit and Frame surface

OpsDashboard+afterKpi на dashboard; FrameDataGrid вместо DataGridCard; KpiStatGrid вместо SectionCards; SettingsShell без Separator. Preview: dashboard-1, stats-12, data-grid-filtering-2, settings-16.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
Denozordec
2026-07-31 12:15:59 +07:00
co-authored by Cursor
parent 53ce80c9ff
commit c3369059af
20 changed files with 216 additions and 198 deletions
+56 -63
View File
@@ -8,16 +8,14 @@ import { Skeleton } from '@evobgp/ui/components/skeleton'
import { DashboardActivityTimeline } from '@/components/dashboard/dashboard-activity-timeline'
import { DashboardFramePanel } from '@/components/dashboard/dashboard-frame-panel'
import { DashboardKpiGrid } from '@/components/dashboard/dashboard-kpi-grid'
import { buildDashboardKpiCards } from '@/components/dashboard/dashboard-kpi-grid'
import { DashboardModulesGrid } from '@/components/dashboard/dashboard-modules-grid'
import { DashboardNetworkHealth } from '@/components/dashboard/dashboard-network-health'
import { DashboardOperationsBreakdown } from '@/components/dashboard/dashboard-operations-breakdown'
import { DashboardQuickLinks } from '@/components/dashboard/dashboard-quick-links'
import { DashboardRecentJobsGrid } from '@/components/dashboard/dashboard-recent-jobs-grid'
import { DashboardRecentRevisionsGrid } from '@/components/dashboard/dashboard-recent-revisions-grid'
import { PageHeader } from '@/components/page-header'
import { AnalyticsDashboardSkeleton } from '@/components/skeletons'
import { OpsDashboard } from '@/components/reui-kit'
import { chartPanelGridClassName, dashboardMainSidebarClassName } from '@/lib/ui-surface'
import {
moduleNameById,
@@ -38,6 +36,11 @@ function parseShowQuickActions(value: unknown): boolean {
return true
}
/**
* Dashboard — OpsDashboard kit (KPI → QuickActions → charts → queue).
* @see https://reui.io/preview/base/dashboard-1
* @see https://reui.io/preview/base/stats-12
*/
function DashboardComponent() {
const [lastUpdated, setLastUpdated] = useState<Date | null>(null)
const settingsQ = useQuery(settingsQueryOptions())
@@ -74,39 +77,28 @@ function DashboardComponent() {
const jobs = jobsQ.data?.items ?? []
const nameById = moduleNameById(modules)
const activityLoading =
refreshing && jobs.length === 0 && revisions.length === 0
const activityLoading = refreshing && jobs.length === 0 && revisions.length === 0
const kpiCards = buildDashboardKpiCards({ modules, peers, speakers, jobs })
return (
<div className="flex flex-col gap-5">
<PageHeader
title="Обзор"
description={
lastUpdated
? `Состояние панели управления EvoBGP. Обновлено: ${lastUpdated.toLocaleTimeString('ru-RU')}`
: 'Состояние панели управления EvoBGP.'
}
actions={
<Button variant="outline" size="sm" onClick={refetchAll} disabled={refreshing}>
<RefreshCw className={refreshing ? 'animate-spin' : ''} />
Обновить
</Button>
}
/>
{initialLoading ? (
<AnalyticsDashboardSkeleton />
) : (
<OpsDashboard
title="Обзор"
description={
lastUpdated
? `Состояние панели управления EvoBGP. Обновлено: ${lastUpdated.toLocaleTimeString('ru-RU')}`
: 'Состояние панели управления EvoBGP.'
}
headerActions={
<Button variant="outline" size="sm" onClick={refetchAll} disabled={refreshing}>
<RefreshCw className={refreshing ? 'animate-spin' : ''} />
Обновить
</Button>
}
kpiCards={kpiCards}
isLoading={initialLoading}
afterKpi={showQuickActions ? <DashboardQuickLinks /> : null}
charts={
<>
<DashboardKpiGrid
modules={modules}
peers={peers}
speakers={speakers}
jobs={jobs}
/>
{showQuickActions ? <DashboardQuickLinks /> : null}
<div className={dashboardMainSidebarClassName}>
<div className="xl:col-span-8">
<DashboardModulesGrid modules={modules} isLoading={refreshing} />
@@ -120,39 +112,40 @@ function DashboardComponent() {
/>
</div>
</div>
<div className={chartPanelGridClassName}>
<DashboardNetworkHealth peers={peers} speakers={speakers} jobs={jobs} />
<DashboardOperationsBreakdown jobs={jobs} modules={modules} />
</div>
</>
)}
<div className="grid gap-4 lg:grid-cols-2">
<DashboardFramePanel
title="Недавние задачи"
description="Последние фоновые операции"
className="h-full"
>
{activityLoading ? (
<Skeleton className="m-4 h-24 w-auto" />
) : (
<DashboardRecentJobsGrid jobs={jobs.slice(0, 10)} nameById={nameById} isLoading={refreshing} />
)}
</DashboardFramePanel>
<DashboardFramePanel
title="Последние ревизии"
description="История конфигураций"
className="h-full"
>
{activityLoading ? (
<Skeleton className="m-4 h-24 w-auto" />
) : (
<DashboardRecentRevisionsGrid revisions={revisions} isLoading={refreshing} />
)}
</DashboardFramePanel>
</div>
</div>
}
queueTitle="Недавняя активность"
queueDescription="Задачи и ревизии плоскости управления"
queue={
<div className="grid gap-4 lg:grid-cols-2">
<DashboardFramePanel
title="Недавние задачи"
description="Последние фоновые операции"
className="h-full"
>
{activityLoading ? (
<Skeleton className="m-4 h-24 w-auto" />
) : (
<DashboardRecentJobsGrid jobs={jobs.slice(0, 10)} nameById={nameById} isLoading={refreshing} />
)}
</DashboardFramePanel>
<DashboardFramePanel
title="Последние ревизии"
description="История конфигураций"
className="h-full"
>
{activityLoading ? (
<Skeleton className="m-4 h-24 w-auto" />
) : (
<DashboardRecentRevisionsGrid revisions={revisions} isLoading={refreshing} />
)}
</DashboardFramePanel>
</div>
}
/>
)
}