"use client" import { DataGridContainer, DataGridScrollArea, DataGridTable, dataGridFeatures, type DataGridFeatures } from "@/components/reui/data-grid" import { useEffect, useMemo, type ReactNode } from "react" import { type ColumnDef, useTable, } from "@tanstack/react-table" import type { WireGuardPeer } from "@/lib/data" 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 { fmtBytes, truncKey } from "@/components/data-grids/wireguard-peers-detail" import { ArrowDownIcon, ArrowUpIcon, CodeXmlIcon, KeyRoundIcon, Trash2Icon, UsersIcon, } from "lucide-react" export interface WgPeerRow extends WireGuardPeer { id: string ifaceId: string ifaceName: string serverId: string serverName: string serverCountry: string } interface WireguardPeersGridProps { peers: WgPeerRow[] compactServer?: boolean emptyAction?: ReactNode onDeletePeer?: (row: WgPeerRow) => void onExportPeer?: (row: WgPeerRow) => void } function WireguardPeersGrid({ peers, compactServer = false, emptyAction, onDeletePeer, onExportPeer, }: WireguardPeersGridProps) { const columns = useMemo[]>(() => { const cols: ColumnDef[] = [ { id: "peer", size: 144, accessorKey: "publicKey", header: ({ column }) => ( ), cell: ({ row }) => { const peer = row.original return (
{peer.name || truncKey(peer.publicKey)}
) }, meta: { headerTitle: "Пир", headerClassName: DATA_GRID_CELL_PAD_FIRST, cellClassName: DATA_GRID_CELL_PAD_FIRST, }, }, { id: "iface", size: 120, accessorKey: "ifaceName", header: ({ column }) => , cell: ({ row }) => ( {row.original.ifaceName} ), meta: { headerTitle: "Интерфейс", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, ] if (!compactServer) { cols.push({ id: "server", size: 128, accessorKey: "serverName", header: ({ column }) => , cell: ({ row }) => (
{row.original.serverName}
), meta: { headerTitle: "Сервер", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }) } cols.push( { id: "allowedIps", size: 136, accessorFn: (row) => row.allowedIps.join(", "), header: ({ column }) => , cell: ({ row }) => ( {row.original.allowedIps.join(", ") || "—"} ), meta: { headerTitle: "Allowed IPs", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, { id: "handshake", size: 144, accessorKey: "latestHandshake", header: ({ column }) => , cell: ({ row }) => ( {row.original.latestHandshake?.trim() || "Нет данных"} ), meta: { headerTitle: "Handshake", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, { id: "transfer", size: 160, header: () => ( RX / TX ), enableSorting: false, cell: ({ row }) => (
), meta: { headerTitle: "RX / TX", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, { id: "endpoint", size: 144, accessorKey: "endpoint", header: ({ column }) => , cell: ({ row }) => ( {row.original.endpoint ?? "—"} ), meta: { headerTitle: "Endpoint", headerClassName: DATA_GRID_CELL_PAD, cellClassName: DATA_GRID_CELL_PAD, }, }, { id: "actions", header: () => Действия, enableSorting: false, size: 112, cell: ({ row }) => { const peer = row.original return (
{onExportPeer ? ( ) : null} {onDeletePeer ? ( ) : null}
) }, meta: { headerClassName: DATA_GRID_CELL_PAD_LAST, cellClassName: DATA_GRID_CELL_PAD_LAST, }, }, ) return cols }, [compactServer, onDeletePeer, onExportPeer]) const table = useTable({ features: dataGridFeatures, manualPagination: false, autoResetPageIndex: false, initialState: { pagination: { pageIndex: 0, pageSize: 50 }, columnPinning: { start: ["peer"], end: [] } }, data: peers, columns, getRowId: (row) => JSON.stringify([row.serverId, row.ifaceId, row.id]), }) const { pageIndex, pageSize } = table.state.pagination useEffect(() => { const lastPage = Math.max(0, Math.ceil(peers.length / pageSize) - 1) if (pageIndex > lastPage) table.setPageIndex(lastPage) }, [peers.length, pageIndex, pageSize, table]) if (peers.length === 0) { return ( } title="Нет пиров" description="Добавьте пира к интерфейсу или сбросьте поиск" action={emptyAction} className="border-0 py-16" /> ) } return ( 50 || pageSize !== 50} tableLayout={{ dense: true, columnsPinnable: true }} tableClassNames={{ headerRow: "border-b border-border", bodyRow: cn("group/row"), }} > ) } export { WireguardPeersGrid, type WireguardPeersGridProps }