feat(ui): integrate KpiStatGrid for enhanced statistics display
Docker images / prepare-release (push) Successful in 9s
Docker images / backend-image (push) Successful in 1m43s
Docker images / frontend-image (push) Successful in 2m58s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 39s
Docker images / publish-release (push) Successful in 8s
Docker images / prepare-release (push) Successful in 9s
Docker images / backend-image (push) Successful in 1m43s
Docker images / frontend-image (push) Successful in 2m58s
Docker images / notify-webhook (push) Skipped
Docker images / updater-image (push) Successful in 39s
Docker images / publish-release (push) Successful in 8s
Replaced existing KPI display implementations across multiple pages with the new KpiStatGrid component for a more consistent and visually appealing presentation of statistics. Updated the Backups, BGP, Certificates, Communities, Containers, Dashboard, and Data Collection pages to utilize the KpiStatGrid, improving the overall user experience and maintainability of the codebase. Additionally, added new dependencies in package.json for required libraries.
This commit is contained in:
@@ -1,15 +1,13 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react"
|
||||
import { useCallback, useEffect, useMemo, useState } from "react"
|
||||
import { usePathname } from "next/navigation"
|
||||
import Link from "next/link"
|
||||
import { PageHeader } from "@/components/page-header"
|
||||
import { OpsPanel } from "@/components/ops-panel"
|
||||
import { Frame, FramePanel } from "@/components/reui/frame"
|
||||
import { IconTile } from "@/components/reui/icon-tile"
|
||||
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
|
||||
import { StatusDot } from "@/components/status-dot"
|
||||
import { StatusBadge } from "@/components/status-badge"
|
||||
import { Sparkline } from "@/components/sparkline"
|
||||
import { LatencyChart } from "@/components/dashboard/latency-chart"
|
||||
import { BandwidthChart } from "@/components/dashboard/bandwidth-chart"
|
||||
import { InternetPathMapCard } from "@/components/dashboard/internet-path-map"
|
||||
@@ -47,49 +45,6 @@ function makeApiFetch(backendUrl: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
label, value, unit, delta, deltaDir, spark, sparkColor, icon,
|
||||
}: {
|
||||
label: string; value: string; unit?: string; delta?: string
|
||||
deltaDir?: "up" | "down"; spark?: number[]; sparkColor?: string
|
||||
icon?: ReactNode
|
||||
}) {
|
||||
return (
|
||||
<Frame className="h-full">
|
||||
<FramePanel className="relative isolate flex h-full flex-col overflow-hidden">
|
||||
<div className="relative z-10 flex items-start gap-3">
|
||||
{icon ? (
|
||||
<IconTile
|
||||
variant="elevated"
|
||||
aria-hidden="true"
|
||||
className="size-10.5 text-muted-foreground"
|
||||
>
|
||||
{icon}
|
||||
</IconTile>
|
||||
) : null}
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<p className="text-muted-foreground text-sm font-medium">{label}</p>
|
||||
<div className="flex items-baseline gap-1.5">
|
||||
<span className="text-2xl leading-none font-bold tabular-nums tracking-tight">{value}</span>
|
||||
{unit ? <span className="text-muted-foreground text-sm">{unit}</span> : null}
|
||||
</div>
|
||||
{delta ? (
|
||||
<p className={`text-xs mt-1 flex items-center gap-1 ${deltaDir === "up" ? "text-[var(--status-online-fg)]" : deltaDir === "down" ? "text-[var(--status-offline-fg)]" : "text-muted-foreground"}`}>
|
||||
{delta}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
{spark && spark.length > 1 ? (
|
||||
<div className="absolute right-4 bottom-4 opacity-60">
|
||||
<Sparkline data={spark} width={80} height={32} color={sparkColor ?? "currentColor"} filled />
|
||||
</div>
|
||||
) : null}
|
||||
</FramePanel>
|
||||
</Frame>
|
||||
)
|
||||
}
|
||||
|
||||
function fmtIntRu(n: number): string {
|
||||
return n.toLocaleString("ru-RU")
|
||||
}
|
||||
@@ -725,49 +680,53 @@ export default function DashboardPage() {
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className="p-6 flex flex-col gap-6">
|
||||
|
||||
{/* KPI row */}
|
||||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
||||
<StatCard
|
||||
label="Серверы онлайн"
|
||||
value={dashboardKpi.servers.value}
|
||||
unit={dashboardKpi.servers.unit}
|
||||
delta={dashboardKpi.servers.delta}
|
||||
deltaDir={dashboardKpi.servers.deltaDir}
|
||||
spark={dashboardKpi.servers.spark}
|
||||
sparkColor={dashboardKpi.servers.sparkColor}
|
||||
icon={<ServerIcon aria-hidden />}
|
||||
/>
|
||||
<StatCard
|
||||
label="Активные фильтры"
|
||||
value={dashboardKpi.filters.value}
|
||||
unit={dashboardKpi.filters.unit}
|
||||
delta={dashboardKpi.filters.delta}
|
||||
deltaDir={dashboardKpi.filters.deltaDir}
|
||||
spark={dashboardKpi.filters.spark}
|
||||
sparkColor={dashboardKpi.filters.sparkColor}
|
||||
icon={<FilterIcon aria-hidden />}
|
||||
/>
|
||||
<StatCard
|
||||
label="BGP-префиксы"
|
||||
value={dashboardKpi.bgp.value}
|
||||
unit={dashboardKpi.bgp.unit}
|
||||
delta={dashboardKpi.bgp.delta}
|
||||
deltaDir={dashboardKpi.bgp.deltaDir}
|
||||
spark={dashboardKpi.bgp.spark}
|
||||
sparkColor={dashboardKpi.bgp.sparkColor}
|
||||
icon={<GitMergeIcon aria-hidden />}
|
||||
/>
|
||||
<StatCard
|
||||
label="Активные алерты"
|
||||
value={dashboardKpi.alerts.value}
|
||||
unit={dashboardKpi.alerts.unit}
|
||||
delta={dashboardKpi.alerts.delta}
|
||||
deltaDir={dashboardKpi.alerts.deltaDir}
|
||||
spark={dashboardKpi.alerts.spark}
|
||||
sparkColor={dashboardKpi.alerts.sparkColor}
|
||||
icon={<BellIcon aria-hidden />}
|
||||
/>
|
||||
</div>
|
||||
<KpiStatGrid
|
||||
aria-label="Сводка дашборда"
|
||||
items={[
|
||||
{
|
||||
id: "servers",
|
||||
label: "Серверы онлайн",
|
||||
value: dashboardKpi.servers.unit
|
||||
? `${dashboardKpi.servers.value} ${dashboardKpi.servers.unit}`
|
||||
: dashboardKpi.servers.value,
|
||||
hint: dashboardKpi.servers.delta,
|
||||
icon: <ServerIcon className="size-4" />,
|
||||
iconClassName: "text-muted-foreground",
|
||||
variant: dashboardKpi.servers.deltaDir === "down" ? "warning" : "default",
|
||||
},
|
||||
{
|
||||
id: "filters",
|
||||
label: "Активные фильтры",
|
||||
value: dashboardKpi.filters.unit
|
||||
? `${dashboardKpi.filters.value} ${dashboardKpi.filters.unit}`
|
||||
: dashboardKpi.filters.value,
|
||||
hint: dashboardKpi.filters.delta,
|
||||
icon: <FilterIcon className="size-4" />,
|
||||
iconClassName: "text-info",
|
||||
},
|
||||
{
|
||||
id: "bgp",
|
||||
label: "BGP-префиксы",
|
||||
value: dashboardKpi.bgp.unit
|
||||
? `${dashboardKpi.bgp.value} ${dashboardKpi.bgp.unit}`
|
||||
: dashboardKpi.bgp.value,
|
||||
hint: dashboardKpi.bgp.delta,
|
||||
icon: <GitMergeIcon className="size-4" />,
|
||||
iconClassName: "text-primary",
|
||||
},
|
||||
{
|
||||
id: "alerts",
|
||||
label: "Активные алерты",
|
||||
value: dashboardKpi.alerts.unit
|
||||
? `${dashboardKpi.alerts.value} ${dashboardKpi.alerts.unit}`
|
||||
: dashboardKpi.alerts.value,
|
||||
hint: dashboardKpi.alerts.delta,
|
||||
icon: <BellIcon className="size-4" />,
|
||||
iconClassName: dashboardKpi.alerts.deltaDir === "down" ? "text-destructive" : "text-muted-foreground",
|
||||
variant: dashboardKpi.alerts.deltaDir === "down" ? "destructive" : "default",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* Latency chart + Events */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-[2fr_1fr] gap-4">
|
||||
|
||||
Reference in New Issue
Block a user