"use client" import { dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" import { useMemo, useState } from "react" import { type ColumnDef, useTable, } from "@tanstack/react-table" import { Flag } from "@/components/flag" import { cn } from "@/lib/utils" import { Button } from "@/components/ui/button" 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 { EmptyState } from "@/components/empty-state" import { AlertCircleIcon, ChevronDownIcon, ChevronRightIcon, PencilIcon, RouteIcon, TrashIcon, } from "lucide-react" export interface RecursiveRouteEndpoint { id: string gateway: string distance: number scope: number | null targetScope: number | null checkGateway: string country: string disabled: boolean } export interface RecursiveRouteGroup { id?: string key: string dstAddress: string routingTable: string comment: string endpoints: RecursiveRouteEndpoint[] } const INFER_COUNTRIES = [ { code: "RU", keys: ["MSK", "SPB", "RTK", "MTS", "VPSVILLE", "IHOR"] }, { code: "SE", keys: ["SWE", "STO"] }, { code: "FI", keys: ["HEL", "FIN"] }, { code: "DE", keys: ["FRA", "GER", "DE"] }, { code: "NL", keys: ["AMS", "NLD", "NL"] }, { code: "SG", keys: ["SGP", "SIN", "SG"] }, { code: "TR", keys: ["TUR", "TR"] }, { code: "US", keys: ["USA", "US", "NYC", "LAX"] }, ] function inferCountry(name: string): string | null { const upper = name.toUpperCase() for (const c of INFER_COUNTRIES) { if (c.keys.some((k) => upper.includes(k))) return c.code } return null } function RouteGroupExpandedDetail({ group }: { group: RecursiveRouteGroup }) { const sorted = [...group.endpoints].sort((a, b) => a.distance - b.distance) return (
Route: {group.dstAddress} Table:{" "} {group.routingTable || "main"} {group.comment && {group.comment}}
{sorted.map((ep, idx) => (
{ep.country && ( )} Endpoint {idx + 1}
distance: {ep.distance}

{ep.gateway}

scope: {ep.scope ?? "—"} t.scope: {ep.targetScope ?? "—"} check: {ep.checkGateway || "—"} {ep.disabled && Отключён}
))}
) } interface RecursiveRoutesDataGridProps { groups: RecursiveRouteGroup[] expandedKey?: string | null onExpandedChange?: (key: string | null) => void onEdit: (group: RecursiveRouteGroup) => void onDelete: (group: RecursiveRouteGroup) => void mutationsDisabled?: boolean emptyTitle?: string } function RecursiveRoutesDataGrid({ groups, expandedKey, onExpandedChange, onEdit, onDelete, mutationsDisabled = false, emptyTitle = "Нет маршрутов", }: RecursiveRoutesDataGridProps) { const [confirmDeleteKey, setConfirmDeleteKey] = useState(null) const expanded = useMemo(() => { if (!expandedKey) return {} const g = groups.find((x) => x.key === expandedKey) return g ? { [(g.id ?? g.key)]: true } : {} }, [expandedKey, groups]) const columns = useMemo[]>( () => [ { id: "route", accessorKey: "dstAddress", header: ({ column }) => ( ), cell: ({ row }) => { const g = row.original const isExpanded = expandedKey === g.key return ( ) }, meta: { headerTitle: "Route / Comment", headerClassName: DATA_GRID_CELL_PAD_FIRST, cellClassName: DATA_GRID_CELL_PAD_FIRST, expandedContent: (group: RecursiveRouteGroup) => ( ), }, }, { id: "gateways", header: () => Gateways, enableSorting: false, cell: ({ row }) => { const sorted = [...row.original.endpoints].sort((a, b) => a.distance - b.distance) return (
{sorted.map(ep => { const code = ep.country return (
) })}
) }, meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD }, }, { id: "epCount", header: () => EP, enableSorting: false, cell: ({ row }) => ( {row.original.endpoints.length} ), meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD }, }, { id: "priority", header: () => Priority, enableSorting: false, cell: ({ row }) => { const bestDistance = Math.min(...row.original.endpoints.map((ep) => ep.distance)) return d{bestDistance} }, meta: { headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD }, }, { id: "table", accessorKey: "routingTable", header: ({ column }) => , cell: ({ row }) => ( {row.original.routingTable || "main"} ), meta: { headerTitle: "Table", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, { id: "actions", header: () => Действия, enableSorting: false, cell: ({ row }) => { const g = row.original const confirmDel = confirmDeleteKey === g.key return (
e.stopPropagation()} >
) }, size: 80, meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST }, }, ], [confirmDeleteKey, expandedKey, onDelete, onEdit, onExpandedChange, mutationsDisabled], ) const table = useTable({ features: dataGridFeatures, manualPagination: true, data: groups, columns, getRowId: (row) => row.id ?? row.key, getRowCanExpand: () => true, state: { expanded }, }) if (groups.length === 0) { return (