ui: fix race issue with meter threshold colors

also increase the default container width
This commit is contained in:
henrygd
2026-02-19 17:37:57 -05:00
parent d526ea61a9
commit daac287b9d
3 changed files with 15 additions and 14 deletions

View File

@@ -33,7 +33,6 @@ import {
decimalString, decimalString,
formatBytes, formatBytes,
formatTemperature, formatTemperature,
getMeterState,
parseSemVer, parseSemVer,
secondsToUptimeString, secondsToUptimeString,
} from "@/lib/utils" } from "@/lib/utils"
@@ -81,6 +80,10 @@ const STATUS_COLORS = {
[SystemStatus.Pending]: "bg-yellow-500", [SystemStatus.Pending]: "bg-yellow-500",
} as const } as const
function getMeterStateByThresholds(value: number, warn = 65, crit = 90): MeterState {
return value >= crit ? MeterState.Crit : value >= warn ? MeterState.Warn : MeterState.Good
}
/** /**
* @param viewMode - "table" or "grid" * @param viewMode - "table" or "grid"
* @returns - Column definitions for the systems table * @returns - Column definitions for the systems table
@@ -209,6 +212,7 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
header: sortableHeader, header: sortableHeader,
cell(info: CellContext<SystemRecord, unknown>) { cell(info: CellContext<SystemRecord, unknown>) {
const { info: sysInfo, status } = info.row.original const { info: sysInfo, status } = info.row.original
const { colorWarn = 65, colorCrit = 90 } = useStore($userSettings, { keys: ["colorWarn", "colorCrit"] })
// agent version // agent version
const { minor, patch } = parseSemVer(sysInfo.v) const { minor, patch } = parseSemVer(sysInfo.v)
let loadAverages = sysInfo.la let loadAverages = sysInfo.la
@@ -224,7 +228,7 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
} }
const normalizedLoad = max / (sysInfo.t ?? 1) const normalizedLoad = max / (sysInfo.t ?? 1)
const threshold = getMeterState(normalizedLoad * 100) const threshold = getMeterStateByThresholds(normalizedLoad * 100, colorWarn, colorCrit)
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">
@@ -463,8 +467,9 @@ function sortableHeader(context: HeaderContext<SystemRecord, unknown>) {
} }
function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) { function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
const { colorWarn = 65, colorCrit = 90 } = useStore($userSettings, { keys: ["colorWarn", "colorCrit"] })
const val = Number(info.getValue()) || 0 const val = Number(info.getValue()) || 0
const threshold = getMeterState(val) const threshold = getMeterStateByThresholds(val, colorWarn, colorCrit)
const meterClass = cn( const meterClass = cn(
"h-full", "h-full",
(info.row.original.status !== SystemStatus.Up && STATUS_COLORS.paused) || (info.row.original.status !== SystemStatus.Up && STATUS_COLORS.paused) ||
@@ -483,6 +488,7 @@ function TableCellWithMeter(info: CellContext<SystemRecord, unknown>) {
} }
function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) { function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
const { colorWarn = 65, colorCrit = 90 } = useStore($userSettings, { keys: ["colorWarn", "colorCrit"] })
const { info: sysInfo, status, id } = info.row.original const { info: sysInfo, status, id } = info.row.original
const extraFs = Object.entries(sysInfo.efs ?? {}) const extraFs = Object.entries(sysInfo.efs ?? {})
@@ -496,7 +502,7 @@ function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
extraFs.sort((a, b) => b[1] - a[1]) extraFs.sort((a, b) => b[1] - a[1])
function getIndicatorColor(pct: number) { function getIndicatorColor(pct: number) {
const threshold = getMeterState(pct) const threshold = getMeterStateByThresholds(pct, colorWarn, colorCrit)
return ( return (
(status !== SystemStatus.Up && STATUS_COLORS.paused) || (status !== SystemStatus.Up && STATUS_COLORS.paused) ||
(threshold === MeterState.Good && STATUS_COLORS.up) || (threshold === MeterState.Good && STATUS_COLORS.up) ||
@@ -514,7 +520,9 @@ function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
const extraDiskIndicators = const extraDiskIndicators =
status !== SystemStatus.Up status !== SystemStatus.Up
? [] ? []
: [...new Set(extraFs.map(([, pct]) => getMeterState(pct)))].sort().map((state) => stateColors[state]) : [...new Set(extraFs.map(([, pct]) => getMeterStateByThresholds(pct, colorWarn, colorCrit)))]
.sort()
.map((state) => stateColors[state])
return ( return (
<Tooltip> <Tooltip>

View File

@@ -6,7 +6,7 @@ import { useEffect, useState } from "react"
import { twMerge } from "tailwind-merge" import { twMerge } from "tailwind-merge"
import { toast } from "@/components/ui/use-toast" import { toast } from "@/components/ui/use-toast"
import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types" import type { ChartTimeData, FingerprintRecord, SemVer, SystemRecord } from "@/types"
import { HourFormat, MeterState, Unit } from "./enums" import { HourFormat, Unit } from "./enums"
import { $copyContent, $userSettings } from "./stores" import { $copyContent, $userSettings } from "./stores"
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
@@ -210,7 +210,6 @@ export function useBrowserStorage<T>(key: string, defaultValue: T, storageInterf
const [value, setValue] = useState(() => { const [value, setValue] = useState(() => {
return getStorageValue(key, defaultValue, storageInterface) return getStorageValue(key, defaultValue, storageInterface)
}) })
// biome-ignore lint/correctness/useExhaustiveDependencies: storageInterface won't change
useEffect(() => { useEffect(() => {
storageInterface?.setItem(key, JSON.stringify(value)) storageInterface?.setItem(key, JSON.stringify(value))
}, [key, value]) }, [key, value])
@@ -394,12 +393,6 @@ export function compareSemVer(a: SemVer, b: SemVer) {
return a.patch - b.patch return a.patch - b.patch
} }
/** 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
}
// biome-ignore lint/suspicious/noExplicitAny: any is used to allow any function to be passed in // biome-ignore lint/suspicious/noExplicitAny: any is used to allow any function to be passed in
export function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void { export function debounce<T extends (...args: any[]) => any>(func: T, wait: number): (...args: Parameters<T>) => void {
let timeout: ReturnType<typeof setTimeout> let timeout: ReturnType<typeof setTimeout>

View File

@@ -100,7 +100,7 @@ const Layout = () => {
<LoginPage /> <LoginPage />
</Suspense> </Suspense>
) : ( ) : (
<div style={{ "--container": `${userSettings.layoutWidth ?? 1500}px` } as React.CSSProperties}> <div style={{ "--container": `${userSettings.layoutWidth ?? 1580}px` } as React.CSSProperties}>
<div className="container"> <div className="container">
<Navbar /> <Navbar />
</div> </div>