"use client" import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" import { useEffect, useMemo, useState } from "react" import { type ColumnDef, useTable, } from "@tanstack/react-table" import type { FullRoute } from "@/lib/route-optimizer-data" import { Flag } from "@/components/flag" import { cn } from "@/lib/utils" import { DataGridShell } from "@/components/data-grids/shared/data-grid-shell" import { DATA_GRID_CELL_PAD, DATA_GRID_CELL_PAD_FIRST, DATA_GRID_CELL_PAD_LAST, } from "@/components/data-grids/shared/data-grid-layout" import { DataGridSortHeader } from "@/components/data-grids/shared/data-grid-sort-header" import { ArrowRightIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" function Chip({ children, color }: { children: React.ReactNode; color?: string }) { return ( {children} ) } function ProbChip({ prob, best }: { prob: number; best?: boolean }) { return ( {prob} б. ) } function ConfChip({ conf }: { conf: string }) { const map: Record = { HIGH: "bg-info/10 text-info border-info/20", MEDIUM: "bg-warning/10 text-warning border-warning/20", LOW: "bg-destructive/10 text-destructive border-destructive/20", } return {conf} } interface RouteOptimizerFullRoutesDataGridProps { routes: FullRoute[] bestId?: string } function RouteOptimizerFullRoutesDataGrid({ routes, bestId, }: RouteOptimizerFullRoutesDataGridProps) { const [expanded, setExpanded] = useState(false) const visibleRoutes = expanded ? routes : routes.slice(0, 5) const indexed = useMemo( () => visibleRoutes.map((r, index) => ({ ...r, _index: index })), [visibleRoutes], ) const columns = useMemo[]>( () => [ { id: "index", size: 112, header: () => # Маршрут, enableSorting: false, cell: ({ row }) => { const isBest = row.original.id === bestId || row.original._index === 0 return (
{row.original._index + 1} {isBest && ( Лучший )}
) }, meta: { headerClassName: DATA_GRID_CELL_PAD_FIRST, cellClassName: DATA_GRID_CELL_PAD_FIRST }, }, { id: "wanJh", size: 264, header: () => WAN → JH, enableSorting: false, cell: ({ row }) => { const r = row.original return (
{r.wan.name}
) }, meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD }, }, { id: "jhExit", size: 240, header: () => JH → Exit, enableSorting: false, cell: ({ row }) => { const r = row.original return (
{r.exit.label} ({r.exit.site})
Модель: {r.je.pingMs} мс · ↓{r.je.dlMbps} ↑{r.je.ulMbps}
) }, meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD }, }, { id: "totalPing", size: 144, accessorFn: (row) => row.hw.pingMs + row.je.pingMs, header: ({ column }) => ( ), cell: ({ row }) => { const totalPing = row.original.hw.pingMs + row.original.je.pingMs return ( {totalPing} мс ) }, meta: { headerTitle: "Ping модели", headerClassName: cn(DATA_GRID_CELL_PAD, "text-center"), cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "bw", size: 144, header: () => ( BW (мин) ), enableSorting: false, cell: ({ row }) => { const r = row.original const minDl = Math.min(r.hw.dlMbps, r.je.dlMbps) const minUl = Math.min(r.hw.ulMbps, r.je.ulMbps) return (
↓{minDl}
↑{minUl}
) }, meta: { headerClassName: cn(DATA_GRID_CELL_PAD, "text-center"), cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "score", size: 80, accessorKey: "score", header: ({ column }) => ( ), cell: ({ row }) => ( {row.original.score} ), meta: { headerTitle: "Score", headerClassName: cn(DATA_GRID_CELL_PAD, "text-center"), cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "prob", size: 104, accessorKey: "probabilityOptimal", header: ({ column }) => ( ), cell: ({ row }) => { const isBest = row.original.id === bestId || row.original._index === 0 return (
) }, meta: { headerTitle: "Индекс", headerClassName: cn(DATA_GRID_CELL_PAD, "text-center"), cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "confidence", size: 176, accessorKey: "confidence", header: ({ column }) => ( ), cell: ({ row }) => (
), meta: { headerTitle: "Категория модели", headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: cn(DATA_GRID_CELL_PAD_LAST, "text-center"), }, }, ], [bestId], ) const table = useTable({ features: dataGridFeatures, manualPagination: false, autoResetPageIndex: false, initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["index"], end: [] } }, data: indexed, columns, getRowId: (row) => row.id, }) const { pageIndex, pageSize } = table.state.pagination useEffect(() => { const lastPage = Math.max(0, Math.ceil(indexed.length / pageSize) - 1) if (pageIndex > lastPage) table.setPageIndex(lastPage) }, [indexed.length, pageIndex, pageSize, table]) return (
50 || pageSize !== 50)} tableClassNames={{ headerRow: "border-b bg-muted/20", bodyRow: cn("group/row hover:bg-muted/30", "has-[[data-route-best=true]]:bg-success/5"), }} tableLayout={{ dense: true, width: "fixed", columnsPinnable: true }} > {routes.length > 5 && ( )}
) } export { RouteOptimizerFullRoutesDataGrid, type RouteOptimizerFullRoutesDataGridProps }