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

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:
Denozordec
2026-07-09 21:39:20 +07:00
parent 1a142e68a9
commit a3f3ffd672
161 changed files with 17496 additions and 605 deletions
@@ -1,13 +1,5 @@
import {
ArrowDownUp,
Network,
RefreshCw,
ShieldCheck,
Timer,
} from 'lucide-react'
import { SectionCards, type SectionCardItem } from '@/components/section-cards'
import { SectionCardsSkeleton } from '@/components/skeletons'
import { KpiSparklineCard, type KpiSparklineMetric } from '@/components/patterns/kpi-sparkline-card'
import { kpiGridClassName } from '@/lib/ui-surface'
import { formatDateTime, moduleIntervalLabel } from '@/lib/modules/display'
import {
communityLabel,
@@ -15,6 +7,7 @@ import {
moduleDohProfileIds,
} from '@/lib/modules/helpers'
import { dohPolicyRu } from '@/lib/ui-labels'
import { SectionCardsSkeleton } from '@/components/skeletons'
import type { AsEntry, BgpCommunity, DohProfile, ModuleRow } from '@/types/api'
interface ModuleKpiCardsProps {
@@ -25,6 +18,12 @@ interface ModuleKpiCardsProps {
loading?: boolean
}
function syntheticSparkline(seed: number): number[] {
return Array.from({ length: 9 }, (_, i) =>
Math.round(seed * (0.8 + (i / 9) * 0.2 + Math.sin(i) * 0.05)),
)
}
export function ModuleKpiCards({
mod,
communities,
@@ -33,52 +32,67 @@ export function ModuleKpiCards({
loading = false,
}: ModuleKpiCardsProps) {
if (loading || !mod) {
return <SectionCardsSkeleton count={5} />
return <SectionCardsSkeleton count={4} />
}
const asPrefixTotal = asEntries.reduce((acc, entry) => acc + (entry.prefix_count ?? 0), 0)
const dohIds = moduleDohProfileIds(mod)
const items: SectionCardItem[] = [
const metrics: KpiSparklineMetric[] = [
{
label: 'Приоритет',
id: 'priority',
title: 'Приоритет',
label: 'Порядок в ревизии',
value: String(mod.priority ?? 0),
hint: 'порядок в сборке ревизии',
icon: <ArrowDownUp className="size-3.5" />,
delta: mod.enabled !== false ? 'активен' : 'выключен',
deltaVariant: mod.enabled !== false ? 'success-light' : 'outline',
detail: mod.type,
tone: 'info',
sparkline: syntheticSparkline(mod.priority ?? 1),
},
{
label: 'Интервал',
id: 'interval',
title: 'Интервал',
label: 'Обновление',
value: moduleIntervalLabel(mod),
hint: 'refresh_interval_sec / cron',
icon: <Timer className="size-3.5" />,
delta: formatDateTime(mod.last_refreshed_at) || 'никогда',
deltaVariant: 'primary-light',
detail: 'last refresh',
tone: 'warning',
sparkline: syntheticSparkline(12),
},
{
label: 'DoH',
value: mod.type === 'DOMAINS' ? dohPolicyRu(mod.doh_resolver_policy) : '—',
hint:
mod.type === 'DOMAINS'
? dohIds.length
? dohIds.map((id) => dohProfileLabel(id, dohProfiles)).join('; ')
: 'Системный DNS'
: 'не применимо',
icon: <Network className="size-3.5" />,
id: 'prefixes',
title: 'Префиксы',
label: mod.type === 'AS_PREFIXES' ? 'AS entries' : 'Записи',
value: mod.type === 'AS_PREFIXES' ? String(asPrefixTotal) : String(asEntries.length),
delta: `${asEntries.length} AS`,
deltaVariant: 'success-light',
detail: 'в модуле',
tone: 'success',
sparkline: syntheticSparkline(asPrefixTotal || asEntries.length || 1),
},
{
label: 'Community по умолч.',
value: communityLabel(mod.default_community_id, communities),
hint: 'для записей без своего community',
icon: <ShieldCheck className="size-3.5" />,
},
{
label: 'Последнее обновление',
value: formatDateTime(mod.last_refreshed_at),
hint:
mod.type === 'AS_PREFIXES'
? `ASN: ${asEntries.length}, префиксов: ${asPrefixTotal}`
: 'время последнего refresh',
icon: <RefreshCw className="size-3.5" />,
id: 'policy',
title: 'DoH / BGP',
label: communityLabel(mod.default_community_id, communities),
value: dohIds.length > 0 ? String(dohIds.length) : '—',
delta: dohPolicyRu(mod.doh_resolver_policy),
deltaVariant: 'info-light',
detail:
dohIds.length > 0
? dohIds.map((id) => dohProfileLabel(id, dohProfiles)).join(', ')
: 'без DoH',
tone: 'info',
sparkline: syntheticSparkline(dohIds.length || 2),
},
]
return <SectionCards items={items} />
return (
<section aria-label="KPI модуля" className={kpiGridClassName}>
{metrics.map((metric) => (
<KpiSparklineCard key={metric.id} metric={metric} />
))}
</section>
)
}