"use client" import { useMemo, type ReactNode } from "react" import { type ColumnDef, getCoreRowModel, getExpandedRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table" import type { WireGuardInterface } from "@/lib/data" import { Flag } from "@/components/flag" import { cn } from "@/lib/utils" import { Badge } from "@/components/reui/badge" import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" 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 { WireGuardPeersDetail } from "@/components/data-grids/wireguard-peers-detail" import { EmptyState } from "@/components/empty-state" import { ChevronDownIcon, ChevronRightIcon, CodeXmlIcon, MoreHorizontalIcon, PlusIcon, PowerIcon, ShieldCheckIcon, Trash2Icon, } from "lucide-react" export interface WgIfaceWithServer extends WireGuardInterface { serverId: string serverName: string serverCountry: string } interface WireguardDataGridProps { interfaces: WgIfaceWithServer[] compactServer?: boolean emptyAction?: ReactNode onExport: (iface: WgIfaceWithServer) => void onAddPeer?: (iface: WgIfaceWithServer) => void onToggle?: (iface: WgIfaceWithServer) => void onDelete?: (iface: WgIfaceWithServer) => void onDeletePeer?: (iface: WgIfaceWithServer, peerId: string) => void onExportPeer?: (iface: WgIfaceWithServer, peerId: string) => void } function WireguardDataGrid({ interfaces, compactServer = false, emptyAction, onExport, onAddPeer, onToggle, onDelete, onDeletePeer, onExportPeer, }: WireguardDataGridProps) { const columns = useMemo[]>( () => [ { id: "name", accessorKey: "name", header: ({ column }) => ( ), cell: ({ row }) => { const iface = row.original const expanded = row.getIsExpanded() const onlinePeers = iface.peers.filter((p) => !!p.latestHandshake).length return (
{expanded ? ( ) : ( )}
{iface.name}
{!compactServer ? (
{iface.serverName}
) : null}

{onlinePeers}/{iface.peers.length} пиров

) }, meta: { headerTitle: compactServer ? "Интерфейс" : "Интерфейс / Сервер", headerClassName: DATA_GRID_CELL_PAD_FIRST, cellClassName: DATA_GRID_CELL_PAD_FIRST, expandedContent: (row: WgIfaceWithServer) => ( onDeletePeer(row, peerId) : undefined} onExportPeer={onExportPeer ? (peerId) => onExportPeer(row, peerId) : undefined} /> ), }, }, { id: "listenPort", accessorKey: "listenPort", header: ({ column }) => , cell: ({ row }) => ( {row.original.listenPort} ), meta: { headerTitle: "Порт", headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "mtu", accessorKey: "mtu", header: ({ column }) => , cell: ({ row }) => {row.original.mtu}, meta: { headerTitle: "MTU", headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "peers", header: () => ( Пиры ), enableSorting: false, cell: ({ row }) => { const iface = row.original const onlinePeers = iface.peers.filter((p) => !!p.latestHandshake).length return ( {onlinePeers} /{iface.peers.length} ) }, meta: { headerTitle: "Пиры", headerClassName: DATA_GRID_CELL_PAD, cellClassName: cn(DATA_GRID_CELL_PAD, "text-center"), }, }, { id: "status", accessorKey: "status", header: ({ column }) => , cell: ({ row }) => ( {row.original.status === "up" ? "UP" : "DOWN"} ), meta: { headerTitle: "Статус", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, { id: "actions", header: () => Действия, cell: ({ row }) => { const iface = row.original return (
e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} > e.stopPropagation()} aria-label={`Действия: ${iface.name}`} > } /> onExport(iface)}> Экспорт {onAddPeer && ( onAddPeer(iface)}> Добавить пира )} {onToggle && ( <> onToggle(iface)}> {iface.enabled ? "Отключить" : "Включить"} )} {onDelete && ( <> onDelete(iface)}> Удалить )}
) }, enableSorting: false, size: 56, meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST, }, }, ], [compactServer, onExport, onAddPeer, onToggle, onDelete, onDeletePeer, onExportPeer], ) const table = useReactTable({ data: interfaces, columns, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), getExpandedRowModel: getExpandedRowModel(), getRowId: (row) => row.id, getRowCanExpand: () => true, }) if (interfaces.length === 0) { return ( } title="Нет WireGuard интерфейсов" description="Добавьте первый интерфейс или сбросьте фильтры" action={emptyAction} className="border-0 py-16" /> ) } return ( table.getRow(row.id).toggleExpanded()} tableClassNames={{ headerRow: "border-b border-border", bodyRow: cn("group/row", "[&[data-disabled=true]]:opacity-50"), }} /> ) } export { WireguardDataGrid, type WireguardDataGridProps }