diff --git a/beszel/site/src/components/systems-table/systems-table-columns.tsx b/beszel/site/src/components/systems-table/systems-table-columns.tsx index ead56c1..28e0244 100644 --- a/beszel/site/src/components/systems-table/systems-table-columns.tsx +++ b/beszel/site/src/components/systems-table/systems-table-columns.tsx @@ -22,6 +22,7 @@ import { decimalString, formatBytes, formatTemperature, + getMeterState, isReadOnlyUser, parseSemVer, } from "@/lib/utils" @@ -53,6 +54,7 @@ import { } from "../ui/alert-dialog" import { buttonVariants } from "../ui/button" import { t } from "@lingui/core/macro" +import { MeterState } from "@/lib/enums" /** * @param viewMode - "table" or "grid" @@ -98,7 +100,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD accessorFn: ({ info }) => info.cpu, id: "cpu", name: () => t`CPU`, - cell: CellFormatter, + cell: TableCellWithMeter, Icon: CpuIcon, header: sortableHeader, }, @@ -107,7 +109,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD accessorFn: ({ info }) => info.mp, id: "memory", name: () => t`Memory`, - cell: CellFormatter, + cell: TableCellWithMeter, Icon: MemoryStickIcon, header: sortableHeader, }, @@ -115,7 +117,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD accessorFn: ({ info }) => info.dp, id: "disk", name: () => t`Disk`, - cell: CellFormatter, + cell: TableCellWithMeter, Icon: HardDriveIcon, header: sortableHeader, }, @@ -123,7 +125,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD accessorFn: ({ info }) => info.g, id: "gpu", name: () => "GPU", - cell: CellFormatter, + cell: TableCellWithMeter, Icon: GpuIcon, header: sortableHeader, }, @@ -157,17 +159,18 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD return null } - function getDotColor() { - const normalized = max / (sysInfo.t ?? 1) - if (status !== "up") return "bg-primary/30" - if (normalized < 0.7) return "bg-green-500" - if (normalized < 1) return "bg-yellow-500" - return "bg-red-600" - } + const normalizedLoad = max / (sysInfo.t ?? 1) + const threshold = getMeterState(normalizedLoad * 100) return (
- + {loadAverages?.map((la, i) => ( {decimalString(la, la >= 10 ? 1 : 2)} ))} @@ -280,10 +283,9 @@ function sortableHeader(context: HeaderContext) { ) } -function CellFormatter(info: CellContext) { - const userSettings = useStore($userSettings) +function TableCellWithMeter(info: CellContext) { const val = Number(info.getValue()) || 0 - const { colorWarn = 65, colorCrit = 90 } = userSettings + const threshold = getMeterState(val) return (
{decimalString(val, val >= 10 ? 1 : 2)}% @@ -292,8 +294,8 @@ function CellFormatter(info: CellContext) { className={cn( "absolute inset-0 w-full h-full origin-left", (info.row.original.status !== "up" && "bg-primary/30") || - (val < colorWarn && "bg-green-500") || - (val < colorCrit && "bg-yellow-500") || + (threshold === MeterState.Good && "bg-green-500") || + (threshold === MeterState.Warn && "bg-yellow-500") || "bg-red-600" )} style={{ diff --git a/beszel/site/src/lib/enums.ts b/beszel/site/src/lib/enums.ts index fc7f45e..6b3bf9b 100644 --- a/beszel/site/src/lib/enums.ts +++ b/beszel/site/src/lib/enums.ts @@ -21,3 +21,10 @@ export enum Unit { Celsius, Fahrenheit, } + +/** Meter state for color */ +export enum MeterState { + Good, + Warn, + Crit, +} diff --git a/beszel/site/src/lib/utils.ts b/beszel/site/src/lib/utils.ts index 5fc0453..7123b0f 100644 --- a/beszel/site/src/lib/utils.ts +++ b/beszel/site/src/lib/utils.ts @@ -20,7 +20,7 @@ import { useEffect, useState } from "react" import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react" import { EthernetIcon, HourglassIcon, ThermometerIcon } from "@/components/ui/icons" import { prependBasePath } from "@/components/router" -import { Unit } from "./enums" +import { MeterState, Unit } from "./enums" export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) @@ -507,3 +507,9 @@ export const parseSemVer = (semVer = ""): SemVer => { const parts = semVer.split(".").map(Number) return { major: parts?.[0] ?? 0, minor: parts?.[1] ?? 0, patch: parts?.[2] ?? 0 } } + +/** Get meter state from 0-100 value. Used for color coding meters. */ +export function getMeterState(value: number): MeterState { + const { colorWarn = 65, colorCrit = 90 } = $userSettings.get() + return value >= colorCrit ? MeterState.Crit : value >= colorWarn ? MeterState.Warn : MeterState.Good +}