@@ -144,6 +169,32 @@ export default function SystemsTable() {
+
+
+
+ Status
+
+
+ setStatusFilter(value as StatusFilter)}
+ >
+ e.preventDefault()} className="gap-2">
+ All Systems
+
+ e.preventDefault()} className="gap-2">
+ Up
+
+ e.preventDefault()} className="gap-2">
+ Down
+
+ e.preventDefault()} className="gap-2">
+ Paused
+
+
+
+
@@ -210,7 +261,7 @@ export default function SystemsTable() {
)
- }, [visibleColumns.length, sorting, viewMode, locale])
+ }, [visibleColumns.length, sorting, viewMode, locale, statusFilter])
return (
@@ -331,7 +382,7 @@ const SystemCard = memo(
>
-
+
@@ -356,7 +407,9 @@ const SystemCard = memo(
const { Icon, name } = column.columnDef as ColumnDef
return (
- {Icon &&
}
+ {column.id === "lastSeen" ? (
+
+ ) : Icon &&
}
{name()}:
{flexRender(cell.column.columnDef.cell, cell.getContext())}
diff --git a/beszel/site/src/lib/utils.ts b/beszel/site/src/lib/utils.ts
index 678494b..3437708 100644
--- a/beszel/site/src/lib/utils.ts
+++ b/beszel/site/src/lib/utils.ts
@@ -57,7 +57,7 @@ export const updateSystemList = (() => {
try {
const records = await pb
.collection
("systems")
- .getFullList({ sort: "+name", fields: "id,name,host,port,info,status" })
+ .getFullList({ sort: "+name", fields: "id,name,host,port,info,status,updated" })
if (records.length) {
$systems.set(records)
@@ -357,6 +357,20 @@ export const chartMargin = { top: 12 }
*/
export const getHostDisplayValue = (system: SystemRecord): string => system.host.slice(system.host.lastIndexOf("/") + 1)
+export function formatUptimeString(uptimeSeconds: number): string {
+ if (!uptimeSeconds || isNaN(uptimeSeconds)) return "";
+ if (uptimeSeconds < 3600) {
+ const mins = Math.trunc(uptimeSeconds / 60);
+ return mins === 1 ? "1 minute" : `${mins} minutes`;
+ } else if (uptimeSeconds < 172800) {
+ const hours = Math.trunc(uptimeSeconds / 3600);
+ return hours === 1 ? "1 hour" : `${hours} hours`;
+ } else {
+ const days = Math.trunc(uptimeSeconds / 86400);
+ return days === 1 ? "1 day" : `${days} days`;
+ }
+}
+
/** Generate a random token for the agent */
export const generateToken = () => {
try {
diff --git a/beszel/site/src/types.d.ts b/beszel/site/src/types.d.ts
index 6e01e56..b264645 100644
--- a/beszel/site/src/types.d.ts
+++ b/beszel/site/src/types.d.ts
@@ -29,6 +29,8 @@ export interface SystemRecord extends RecordModel {
port: string
info: SystemInfo
v: string
+ updated: string
+
}
export interface SystemInfo {
diff --git a/site/src/components/systems-table/systems-table.tsx b/site/src/components/systems-table/systems-table.tsx
new file mode 100644
index 0000000..57c177e
--- /dev/null
+++ b/site/src/components/systems-table/systems-table.tsx
@@ -0,0 +1,93 @@
+import { memo, useMemo } from "react"
+import { Row, TableType } from "@tanstack/react-table"
+import { useLingui } from "@lingui/react"
+import { cn } from "@/lib/utils"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Link } from "@/components/ui/link"
+import { getPagePath } from "@/lib/page-path"
+import { useRouter } from "next/router"
+import { flexRender } from "@tanstack/react-table"
+import { ColumnDef } from "@tanstack/table-core"
+import { SystemRecord } from "@/lib/types"
+import { IndicatorDot } from "@/components/indicator-dot"
+import { AlertsButton } from "@/components/alerts-button"
+import { ActionsButton } from "@/components/actions-button"
+import { EyeIcon } from "@/components/icons"
+
+const SystemCard = memo(
+ ({ row, table, colLength }: { row: Row; table: TableType; colLength: number }) => {
+ const system = row.original
+ const { t } = useLingui()
+
+ return useMemo(() => {
+ return (
+
+
+
+
+
+
+
+ {system.name}
+
+
+
+ {table.getColumn("actions")?.getIsVisible() && (
+
+ )}
+
+
+
+ {table.getAllColumns().map((column) => {
+ if (!column.getIsVisible() || column.id === "system" || column.id === "actions") return null
+ const cell = row.getAllCells().find((cell) => cell.column.id === column.id)
+ if (!cell) return null
+ // @ts-ignore
+ const { Icon, name } = column.columnDef as ColumnDef
+
+ // Special case for 'lastSeen' column: add EyeIcon before value
+ if (column.id === "lastSeen") {
+ return (
+
+
+
+
{name()}:
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+
+ )
+ }
+
+ return (
+
+ {Icon &&
}
+
+
{name()}:
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+
+ )
+ })}
+
+
+ {row.original.name}
+
+
+ )
+ }, [system, colLength, t])
+ }
+)
\ No newline at end of file