Docker images / prepare-release (push) Successful in 10s
Docker images / backend-test (push) Successful in 2m23s
Docker images / frontend-image (push) Successful in 3m30s
Docker images / updater-image (push) Successful in 52s
Docker images / backend-image (push) Successful in 3m8s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 14s
Сопоставить клиентов с ifIndex как на карте трафика, чтобы KPI пользователей не обнулялся. На экране — разрез остальных измерений и сводная матрица. Co-authored-by: Cursor <[email protected]>
90 lines
2.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
"use client"
|
|
|
|
import { CompactDataGrid, type CompactDataGridColumn } from "@/components/data-grids/compact-data-grid"
|
|
import { Flag } from "@/components/flag"
|
|
import { Badge } from "@/components/reui/badge"
|
|
import { fmtBps, formatBytes } from "@/lib/fmt-rate"
|
|
import { cn } from "@/lib/utils"
|
|
import { STATISTICS_UNBOUND_USER_ID, type StatisticsBreakdownRow } from "@mmapp/contracts/statistics"
|
|
|
|
export type StatisticsSliceKind = "users" | "servers" | "interfaces" | "countries" | "services" | "asns"
|
|
|
|
export function StatisticsBreakdownDataGrid({
|
|
rows,
|
|
kind,
|
|
selectedId,
|
|
onRowClick,
|
|
isLoading,
|
|
density = "full",
|
|
}: {
|
|
rows: StatisticsBreakdownRow[]
|
|
kind: StatisticsSliceKind
|
|
selectedId?: string
|
|
onRowClick?: (row: StatisticsBreakdownRow) => void
|
|
isLoading?: boolean
|
|
density?: "full" | "mini"
|
|
}) {
|
|
const mini = density === "mini"
|
|
const columns: CompactDataGridColumn<StatisticsBreakdownRow>[] = [
|
|
{
|
|
id: "label",
|
|
header: "Имя",
|
|
accessorKey: "label",
|
|
cell: (row) => (
|
|
<span className={cn("flex items-center gap-2", selectedId === row.id && "font-medium")}>
|
|
{kind === "countries" && row.id !== "XX" && row.id !== STATISTICS_UNBOUND_USER_ID ? (
|
|
<Flag code={row.id} size={16} />
|
|
) : null}
|
|
<span className="truncate">{row.label || row.id}</span>
|
|
{selectedId === row.id ? (
|
|
<Badge variant="outline" size="sm">
|
|
слайс
|
|
</Badge>
|
|
) : null}
|
|
</span>
|
|
),
|
|
},
|
|
{
|
|
id: "bytes",
|
|
header: "Байты",
|
|
accessorKey: "bytes",
|
|
cell: (row) => <span className="tabular-nums">{formatBytes(row.bytes)}</span>,
|
|
},
|
|
...(!mini
|
|
? [
|
|
{
|
|
id: "packets",
|
|
header: "Пакеты",
|
|
accessorKey: "packets" as const,
|
|
cell: (row: StatisticsBreakdownRow) => (
|
|
<span className="tabular-nums">{row.packets.toLocaleString("ru-RU")}</span>
|
|
),
|
|
},
|
|
{
|
|
id: "bps",
|
|
header: "Средний bitrate",
|
|
accessorKey: "bps" as const,
|
|
cell: (row: StatisticsBreakdownRow) => <span className="tabular-nums">{fmtBps(row.bps)}</span>,
|
|
},
|
|
]
|
|
: []),
|
|
{
|
|
id: "percent",
|
|
header: "Доля",
|
|
accessorKey: "percent",
|
|
cell: (row) => <span className="tabular-nums">{row.percent.toFixed(1)}%</span>,
|
|
},
|
|
]
|
|
|
|
return (
|
|
<CompactDataGrid
|
|
data={rows}
|
|
columns={columns}
|
|
isLoading={isLoading}
|
|
emptyTitle="Нет трафика"
|
|
emptyDescription="За выбранный период и слайсы нет данных куба."
|
|
onRowClick={onRowClick}
|
|
/>
|
|
)
|
|
}
|