refactor: integrate PanelCard and enhance dashboard components for improved layout
CI / changes (push) Successful in 10s
CI / commitlint (push) Has been skipped
CI / openapi (push) Has been skipped
CI / web (push) Successful in 52s
CI / go (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / release (push) Successful in 4m7s
CI / changes (push) Successful in 10s
CI / commitlint (push) Has been skipped
CI / openapi (push) Has been skipped
CI / web (push) Successful in 52s
CI / go (push) Has been skipped
CI / bird2 (push) Has been skipped
CI / release (push) Successful in 4m7s
Refactored multiple dashboard components to utilize the new PanelCard for better organization and presentation. Updated the DashboardFramePanel, DashboardQuickLinks, and DashboardOperationsBreakdown components to streamline layouts and enhance user experience. Removed deprecated components and improved loading states in various sections, ensuring a cohesive interface throughout the application.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
import { createFileRoute, useSearch } from '@tanstack/react-router'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Activity, AlertTriangle, Bird, RefreshCw } from 'lucide-react'
|
||||
import { AlertTriangle, Bird, Database, FileText, RefreshCw } from 'lucide-react'
|
||||
|
||||
import { Button } from '@evobgp/ui/components/button'
|
||||
import { PanelCard } from '@/components/panel-card'
|
||||
import { Separator } from '@evobgp/ui/components/separator'
|
||||
import { BadgeTabs, TabsContent } from '@/components/badge-tabs'
|
||||
import { DataGridCard } from '@/components/data-grid-shell'
|
||||
import { StatusBadge } from '@/components/status-badge'
|
||||
@@ -16,6 +15,8 @@ import {
|
||||
import { MonitoringReadyGrid } from '@/components/monitoring/monitoring-ready-grid'
|
||||
import { PageHeader } from '@/components/page-header'
|
||||
import { QueryState } from '@/components/query-state'
|
||||
import { IllustratedEmptyState } from '@/components/patterns/illustrated-empty-state'
|
||||
import { SegmentedProgressCard } from '@/components/patterns/segmented-progress-card'
|
||||
import { AnalyticsDashboardSkeleton } from '@/components/skeletons'
|
||||
|
||||
import {
|
||||
@@ -164,52 +165,50 @@ function MonitoringComponent() {
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 lg:grid-cols-2">
|
||||
<PanelCard
|
||||
title={
|
||||
<span className="flex items-center gap-2">
|
||||
<Activity className="size-4" />
|
||||
Задачи
|
||||
</span>
|
||||
}
|
||||
description="Последние 100 задач · GET /v1/jobs"
|
||||
contentClassName="space-y-4 py-4"
|
||||
>
|
||||
<div className="flex flex-wrap gap-4 text-sm">
|
||||
<Metric label="Активных" value={jobs.filter((j) => j.status === 'running' || j.status === 'queued').length} />
|
||||
<Metric
|
||||
label="С ошибками"
|
||||
value={failed}
|
||||
valueClass={failed > 0 ? 'text-warning' : 'text-success'}
|
||||
/>
|
||||
<Metric label="В выборке" value={jobs.length} />
|
||||
</div>
|
||||
<Separator />
|
||||
{failedJobs.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
<p className="text-sm font-medium">Последние ошибки</p>
|
||||
<ul className="space-y-2">
|
||||
{failedJobs.map((job) => (
|
||||
<li key={job.job_id} className="rounded-lg border px-3 py-2 text-sm">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="font-medium">{jobKindRu(job.kind)}</p>
|
||||
<StatusBadge status={job.status} />
|
||||
</div>
|
||||
{job.error ? (
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{job.error.slice(0, 120)}
|
||||
{job.error.length > 120 ? '…' : ''}
|
||||
</p>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Критичных сбоев в последних 100 задачах нет.
|
||||
</p>
|
||||
)}
|
||||
</PanelCard>
|
||||
<div className="flex flex-col gap-4">
|
||||
<SegmentedProgressCard
|
||||
title="Задачи"
|
||||
description="Последние 100 задач · GET /v1/jobs"
|
||||
primary={{
|
||||
value: jobs.filter((j) => j.status === 'running' || j.status === 'queued').length,
|
||||
label: 'Активных',
|
||||
percent:
|
||||
jobs.length > 0
|
||||
? Math.round(
|
||||
(jobs.filter((j) => j.status === 'running' || j.status === 'queued').length /
|
||||
jobs.length) *
|
||||
100,
|
||||
)
|
||||
: 0,
|
||||
}}
|
||||
secondary={{
|
||||
value: failed,
|
||||
label: 'С ошибками',
|
||||
percent: jobs.length > 0 ? Math.round((failed / jobs.length) * 100) : 0,
|
||||
}}
|
||||
footer={`В выборке: ${jobs.length} задач`}
|
||||
/>
|
||||
{failedJobs.length > 0 ? (
|
||||
<PanelCard title="Последние ошибки" contentClassName="space-y-2 py-4">
|
||||
<ul className="space-y-2">
|
||||
{failedJobs.map((job) => (
|
||||
<li key={job.job_id} className="rounded-lg border px-3 py-2 text-sm">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="font-medium">{jobKindRu(job.kind)}</p>
|
||||
<StatusBadge status={job.status} />
|
||||
</div>
|
||||
{job.error ? (
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{job.error.slice(0, 120)}
|
||||
{job.error.length > 120 ? '…' : ''}
|
||||
</p>
|
||||
) : null}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</PanelCard>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<PanelCard
|
||||
title={
|
||||
@@ -246,28 +245,18 @@ function MonitoringComponent() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="postgres" className="mt-0">
|
||||
<PanelCard
|
||||
<IllustratedEmptyState
|
||||
icon={Database}
|
||||
title="PostgreSQL"
|
||||
description={
|
||||
<>
|
||||
Статус соединения и пул. PostgreSQL отображается в readiness-проверке на вкладке «Система»
|
||||
(check <code className="text-xs">postgres</code>).
|
||||
</>
|
||||
}
|
||||
contentClassName="py-4"
|
||||
description="Статус соединения и пул отображаются в readiness-проверке на вкладке «Система» (check postgres)."
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="runtime-logs" className="mt-0">
|
||||
<PanelCard
|
||||
<IllustratedEmptyState
|
||||
icon={FileText}
|
||||
title="Файловые логи"
|
||||
description={
|
||||
<>
|
||||
Логи API и pipeline настраиваются переменной <code className="text-xs">EVOBGP_LOG_*</code> и
|
||||
управляются в tenant-settings.
|
||||
</>
|
||||
}
|
||||
contentClassName="py-4"
|
||||
description="Логи API и pipeline настраиваются переменной EVOBGP_LOG_* и управляются в tenant-settings."
|
||||
/>
|
||||
</TabsContent>
|
||||
</BadgeTabs>
|
||||
@@ -275,15 +264,6 @@ function MonitoringComponent() {
|
||||
)
|
||||
}
|
||||
|
||||
function Metric({ label, value, valueClass }: { label: string; value: number | string; valueClass?: string }) {
|
||||
return (
|
||||
<div>
|
||||
<p className="text-muted-foreground">{label}</p>
|
||||
<p className={`text-2xl font-bold tabular-nums ${valueClass ?? ''}`}>{value}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function formatVersion(version?: VersionInfo | null): string {
|
||||
if (!version) return '—'
|
||||
return version.version ?? version.app ?? '—'
|
||||
|
||||
Reference in New Issue
Block a user