refactor: add getMeterState function

This commit is contained in:
henrygd
2025-08-02 23:44:07 -04:00
parent 5f7950b474
commit 8af3a0eb5b
3 changed files with 33 additions and 18 deletions

View File

@@ -22,6 +22,7 @@ import {
decimalString, decimalString,
formatBytes, formatBytes,
formatTemperature, formatTemperature,
getMeterState,
isReadOnlyUser, isReadOnlyUser,
parseSemVer, parseSemVer,
} from "@/lib/utils" } from "@/lib/utils"
@@ -53,6 +54,7 @@ import {
} from "../ui/alert-dialog" } from "../ui/alert-dialog"
import { buttonVariants } from "../ui/button" import { buttonVariants } from "../ui/button"
import { t } from "@lingui/core/macro" import { t } from "@lingui/core/macro"
import { MeterState } from "@/lib/enums"
/** /**
* @param viewMode - "table" or "grid" * @param viewMode - "table" or "grid"
@@ -98,7 +100,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
accessorFn: ({ info }) => info.cpu, accessorFn: ({ info }) => info.cpu,
id: "cpu", id: "cpu",
name: () => t`CPU`, name: () => t`CPU`,
cell: CellFormatter, cell: TableCellWithMeter,
Icon: CpuIcon, Icon: CpuIcon,
header: sortableHeader, header: sortableHeader,
}, },
@@ -107,7 +109,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
accessorFn: ({ info }) => info.mp, accessorFn: ({ info }) => info.mp,
id: "memory", id: "memory",
name: () => t`Memory`, name: () => t`Memory`,
cell: CellFormatter, cell: TableCellWithMeter,
Icon: MemoryStickIcon, Icon: MemoryStickIcon,
header: sortableHeader, header: sortableHeader,
}, },
@@ -115,7 +117,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
accessorFn: ({ info }) => info.dp, accessorFn: ({ info }) => info.dp,
id: "disk", id: "disk",
name: () => t`Disk`, name: () => t`Disk`,
cell: CellFormatter, cell: TableCellWithMeter,
Icon: HardDriveIcon, Icon: HardDriveIcon,
header: sortableHeader, header: sortableHeader,
}, },
@@ -123,7 +125,7 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
accessorFn: ({ info }) => info.g, accessorFn: ({ info }) => info.g,
id: "gpu", id: "gpu",
name: () => "GPU", name: () => "GPU",
cell: CellFormatter, cell: TableCellWithMeter,
Icon: GpuIcon, Icon: GpuIcon,
header: sortableHeader, header: sortableHeader,
}, },
@@ -157,17 +159,18 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD
return null return null
} }
function getDotColor() { const normalizedLoad = max / (sysInfo.t ?? 1)
const normalized = max / (sysInfo.t ?? 1) const threshold = getMeterState(normalizedLoad * 100)
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"
}
return ( return (
<div className="flex items-center gap-[.35em] w-full tabular-nums tracking-tight"> <div className="flex items-center gap-[.35em] w-full tabular-nums tracking-tight">
<span className={cn("inline-block size-2 rounded-full me-0.5", getDotColor())} /> <span
className={cn("inline-block size-2 rounded-full me-0.5", {
"bg-green-500": threshold === MeterState.Good,
"bg-yellow-500": threshold === MeterState.Warn,
"bg-red-600": threshold === MeterState.Crit,
})}
/>
{loadAverages?.map((la, i) => ( {loadAverages?.map((la, i) => (
<span key={i}>{decimalString(la, la >= 10 ? 1 : 2)}</span> <span key={i}>{decimalString(la, la >= 10 ? 1 : 2)}</span>
))} ))}
@@ -280,10 +283,9 @@ function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
) )
} }
function CellFormatter(info: CellContext<SystemRecord, unknown>) { function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
const userSettings = useStore($userSettings)
const val = Number(info.getValue()) || 0 const val = Number(info.getValue()) || 0
const { colorWarn = 65, colorCrit = 90 } = userSettings const threshold = getMeterState(val)
return ( return (
<div className="flex gap-2 items-center tabular-nums tracking-tight"> <div className="flex gap-2 items-center tabular-nums tracking-tight">
<span className="min-w-8">{decimalString(val, val >= 10 ? 1 : 2)}%</span> <span className="min-w-8">{decimalString(val, val >= 10 ? 1 : 2)}%</span>
@@ -292,8 +294,8 @@ function CellFormatter(info: CellContext<SystemRecord, unknown>) {
className={cn( className={cn(
"absolute inset-0 w-full h-full origin-left", "absolute inset-0 w-full h-full origin-left",
(info.row.original.status !== "up" && "bg-primary/30") || (info.row.original.status !== "up" && "bg-primary/30") ||
(val < colorWarn && "bg-green-500") || (threshold === MeterState.Good && "bg-green-500") ||
(val < colorCrit && "bg-yellow-500") || (threshold === MeterState.Warn && "bg-yellow-500") ||
"bg-red-600" "bg-red-600"
)} )}
style={{ style={{

View File

@@ -21,3 +21,10 @@ export enum Unit {
Celsius, Celsius,
Fahrenheit, Fahrenheit,
} }
/** Meter state for color */
export enum MeterState {
Good,
Warn,
Crit,
}

View File

@@ -20,7 +20,7 @@ import { useEffect, useState } from "react"
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react" import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react"
import { EthernetIcon, HourglassIcon, ThermometerIcon } from "@/components/ui/icons" import { EthernetIcon, HourglassIcon, ThermometerIcon } from "@/components/ui/icons"
import { prependBasePath } from "@/components/router" import { prependBasePath } from "@/components/router"
import { Unit } from "./enums" import { MeterState, Unit } from "./enums"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs))
@@ -507,3 +507,9 @@ export const parseSemVer = (semVer = ""): SemVer => {
const parts = semVer.split(".").map(Number) const parts = semVer.split(".").map(Number)
return { major: parts?.[0] ?? 0, minor: parts?.[1] ?? 0, patch: parts?.[2] ?? 0 } 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
}