diff --git a/beszel/site/src/components/systems-table/systems-table.tsx b/beszel/site/src/components/systems-table/systems-table.tsx index f449ad9..6c8d6eb 100644 --- a/beszel/site/src/components/systems-table/systems-table.tsx +++ b/beszel/site/src/components/systems-table/systems-table.tsx @@ -51,6 +51,8 @@ import { ServerIcon, CpuIcon, ChevronDownIcon, + LayoutGridIcon, + LayoutListIcon, } from "lucide-react" import { useEffect, useMemo, useState } from "react" import { $hubVersion, $systems, pb } from "@/lib/stores" @@ -64,18 +66,23 @@ import { useLingui } from "@lingui/react" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card" import { Input } from "../ui/input" +type ViewMode = 'table' | 'grid' + function CellFormatter(info: CellContext) { const val = info.getValue() as number return ( -
+
{decimalString(val, 1)}%
@@ -103,6 +110,7 @@ export default function SystemsTable() { const [sorting, setSorting] = useState([]) const [columnFilters, setColumnFilters] = useState([]) const [columnVisibility, setColumnVisibility] = useLocalStorage("cols", {}) + const [viewMode, setViewMode] = useLocalStorage('viewMode', 'table') const { i18n } = useLingui() useEffect(() => { @@ -131,7 +139,7 @@ export default function SystemsTable() { "bg-primary/40": status === "paused", "bg-yellow-500": status === "pending", })} - style={{ marginBottom: "-1px" }} + style={{ marginBottom: "-2px" }} >
-
+
setFilter(e.target.value)} className="px-4" /> + + {viewMode === 'grid' && ( + + + + + + {table.getAllColumns().map((column) => { + if (column.id === t`Actions` || !column.getCanSort()) return null + + const isCurrentSort = sorting[0]?.id === column.id + const sortDirection = sorting[0]?.desc ? '↓' : '↑' + + return ( + { + const isDesc = sorting[0]?.id === column.id && !sorting[0]?.desc + setSorting([{ id: column.id, desc: isDesc }]) + }} + > + {column.id} {isCurrentSort && sortDirection} + + ) + })} + {sorting.length > 0 && ( + <> + + setSorting([])}> + Clear Sort + + + )} + + + )} @@ -349,62 +418,210 @@ export default function SystemsTable() {
- -
- - - {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) => ( - + {viewMode === 'table' ? ( +
+
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + + ) })} - onClick={(e) => { - 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. - - - )} - -
-
-
+ ))} + + + {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. + + + )} + + + + ) : ( +
+ {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.original.name} + +
+ {table.getColumn(t`Actions`)?.getIsVisible() && ( +
+ + + + + + + + { + pb.collection("systems").update(row.original.id, { + status: row.original.status === "paused" ? "pending" : "paused", + }) + }} + > + {row.original.status === "paused" ? ( + <> + + Resume + + ) : ( + <> + + Pause + + )} + + copyToClipboard(row.original.host)}> + + Copy host + + + + + + Delete + + + + + + + + Are you sure you want to delete {row.original.name}? + + + + This action cannot be undone. This will permanently delete all current records for {row.original.name} from + the database. + + + + + + Cancel + + pb.collection("systems").delete(row.original.id)} + > + Continue + + + + +
+ )} +
+
+ + {table.getAllColumns().map((column) => { + if (!column.getIsVisible() || column.id === t`System` || column.id === t`Actions`) return null + const cell = row.getAllCells().find(cell => cell.column.id === column.id) + if (!cell) return null + + const icon = (() => { + switch (column.id) { + case t`CPU`: return + case t`Memory`: return + case t`Disk`: return + case t`Net`: return + case t`Agent`: return + default: return null + } + })() + + return ( +
+ {icon} +
+ {column.id}: +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+
+ ) + })} +
+
+ )) + ) : ( +
+ No systems found. +
+ )} +
+ )} + ) }