import { CellContext, ColumnDef, ColumnFiltersState, getFilteredRowModel, SortingState, getSortedRowModel, flexRender, VisibilityState, getCoreRowModel, useReactTable, Column, } from "@tanstack/react-table" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" import { Button, buttonVariants } from "@/components/ui/button" import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { SystemRecord } from "@/types" import { MoreHorizontalIcon, ArrowUpDownIcon, MemoryStickIcon, CopyIcon, PauseCircleIcon, PlayCircleIcon, Trash2Icon, WifiIcon, HardDriveIcon, ServerIcon, CpuIcon, ChevronDownIcon, } from "lucide-react" import { useEffect, useMemo, useState } from "react" import { $hubVersion, $systems, pb } from "@/lib/stores" import { useStore } from "@nanostores/react" import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils" import AlertsButton from "../alerts/alert-button" import { navigate } from "../router" import { EthernetIcon } from "../ui/icons" import { Trans, t } from "@lingui/macro" import { useLingui } from "@lingui/react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card" import { Input } from "../ui/input" function CellFormatter(info: CellContext) { const val = info.getValue() as number return (
{decimalString(val, 1)}%
) } function sortableHeader(column: Column, Icon: any, hideSortIcon = false) { return ( ) } export default function SystemsTable() { const data = useStore($systems) const hubVersion = useStore($hubVersion) const [filter, setFilter] = useState() const [sorting, setSorting] = useState([]) const [columnFilters, setColumnFilters] = useState([]) const [columnVisibility, setColumnVisibility] = useLocalStorage("cols", {}) const { i18n } = useLingui() useEffect(() => { if (filter !== undefined) { table.getColumn(t`System`)?.setFilterValue(filter) } }, [filter]) const columns = useMemo(() => { return [ { // size: 200, size: 200, minSize: 0, accessorKey: "name", id: t`System`, enableHiding: false, cell: (info) => { const { status } = info.row.original return ( ) }, header: ({ column }) => sortableHeader(column, ServerIcon), }, { accessorKey: "info.cpu", id: t`CPU`, invertSorting: true, cell: CellFormatter, header: ({ column }) => sortableHeader(column, CpuIcon), }, { accessorKey: "info.mp", id: t`Memory`, invertSorting: true, cell: CellFormatter, header: ({ column }) => sortableHeader(column, MemoryStickIcon), }, { accessorKey: "info.dp", id: t`Disk`, invertSorting: true, cell: CellFormatter, header: ({ column }) => sortableHeader(column, HardDriveIcon), }, { accessorFn: (originalRow) => originalRow.info.b || 0, id: t`Net`, invertSorting: true, size: 115, header: ({ column }) => sortableHeader(column, EthernetIcon), cell: (info) => { const val = info.getValue() as number return ( {decimalString(val, val >= 100 ? 1 : 2)} MB/s ) }, }, { accessorKey: "info.v", id: t`Agent`, invertSorting: true, size: 50, header: ({ column }) => sortableHeader(column, WifiIcon, true), cell: (info) => { const version = info.getValue() as string if (!version || !hubVersion) { return null } return ( {info.getValue() as string} ) }, }, { id: t({ message: "Actions", comment: "Table column" }), size: 120, cell: ({ row }) => { const { id, name, status, host } = row.original return (
{ pb.collection("systems").update(id, { status: status === "paused" ? "pending" : "paused", }) }} > {status === "paused" ? ( <> Resume ) : ( <> Pause )} copyToClipboard(host)}> Copy host Delete Are you sure you want to delete {name}? This action cannot be undone. This will permanently delete all current records for {name} from the database. Cancel pb.collection("systems").delete(id)} > Continue
) }, }, ] as ColumnDef[] }, [hubVersion, i18n.locale]) const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel(), onSortingChange: setSorting, getSortedRowModel: getSortedRowModel(), onColumnFiltersChange: setColumnFilters, getFilteredRowModel: getFilteredRowModel(), onColumnVisibilityChange: setColumnVisibility, state: { sorting, columnFilters, columnVisibility, }, defaultColumn: { minSize: 0, size: Number.MAX_SAFE_INTEGER, maxSize: Number.MAX_SAFE_INTEGER, }, }) return (
All Systems Updated in real time. Click on a system to view information.
setFilter(e.target.value)} className="px-4" /> {table .getAllColumns() .filter((column) => column.getCanHide()) .map((column) => { return ( column.toggleVisibility(!!value)} > {column.id} ) })}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => { return ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ) })} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( { const target = e.target as HTMLElement if (!target.closest("[data-nolink]") && e.currentTarget.contains(target)) { navigate(`/system/${encodeURIComponent(row.original.name)}`) } }} > {row.getVisibleCells().map((cell) => ( 10 ? "py-2" : "py-2.5")} > {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( No systems found. )}
) }