mirror of
https://github.com/henrygd/beszel.git
synced 2026-09-26 19:37:47 +02:00
add signal strength indicator dot to all systems table
This commit is contained in:
@@ -38,7 +38,7 @@ import {
|
|||||||
secondsToUptimeString,
|
secondsToUptimeString,
|
||||||
} from "@/lib/utils"
|
} from "@/lib/utils"
|
||||||
import { batteryStateTranslations } from "@/lib/i18n"
|
import { batteryStateTranslations } from "@/lib/i18n"
|
||||||
import { connectedWiFi, strongestWiFi, strongestWiFiSignal } from "@/lib/wifi"
|
import { connectedWiFi, strongestWiFi, strongestWiFiSignal, wifiSignalState } from "@/lib/wifi"
|
||||||
import type { SystemRecord, WiFi } from "@/types"
|
import type { SystemRecord, WiFi } from "@/types"
|
||||||
import { SystemDialog } from "../add-system"
|
import { SystemDialog } from "../add-system"
|
||||||
import AlertButton from "../alerts/alert-button"
|
import AlertButton from "../alerts/alert-button"
|
||||||
@@ -363,7 +363,6 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const displayedConnections = viewMode === "table" ? [strongest] : connections
|
const displayedConnections = viewMode === "table" ? [strongest] : connections
|
||||||
const signal = (wifi: WiFi) => (wifi.r === undefined ? "—" : `${wifi.r} dBm`)
|
|
||||||
return (
|
return (
|
||||||
<Tooltip>
|
<Tooltip>
|
||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
@@ -373,9 +372,7 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
className="flex flex-col gap-0.5 min-w-0 py-1 relative z-10"
|
className="flex flex-col gap-0.5 min-w-0 py-1 relative z-10"
|
||||||
>
|
>
|
||||||
{displayedConnections.map(([id, wifi]) => (
|
{displayedConnections.map(([id, wifi]) => (
|
||||||
<span key={id} className="tabular-nums whitespace-nowrap">
|
<WiFiSignal key={id} wifi={wifi} />
|
||||||
{signal(wifi)}
|
|
||||||
</span>
|
|
||||||
))}
|
))}
|
||||||
{viewMode === "table" && connections.length > 1 && (
|
{viewMode === "table" && connections.length > 1 && (
|
||||||
<span className="text-xs text-muted-foreground">+{connections.length - 1}</span>
|
<span className="text-xs text-muted-foreground">+{connections.length - 1}</span>
|
||||||
@@ -389,8 +386,8 @@ export function SystemsTableColumns(viewMode: "table" | "grid"): ColumnDef<Syste
|
|||||||
<div className="text-[0.65rem] max-w-40 text-muted-foreground uppercase tracking-wide truncate">
|
<div className="text-[0.65rem] max-w-40 text-muted-foreground uppercase tracking-wide truncate">
|
||||||
{id}
|
{id}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2 items-center tabular-nums text-xs">
|
<div className="flex gap-2 items-center text-xs">
|
||||||
<span className="shrink-0">{signal(wifi)}</span>
|
<WiFiSignal wifi={wifi} className="shrink-0" />
|
||||||
{wifi.s && <span className="truncate max-w-40">{wifi.s}</span>}
|
{wifi.s && <span className="truncate max-w-40">{wifi.s}</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -702,6 +699,22 @@ function DiskCellWithMultiple(info: CellContext<SystemRecord, unknown>) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function WiFiSignal({ wifi, className }: { wifi: WiFi; className?: ClassValue }) {
|
||||||
|
const state = wifi.r === undefined ? undefined : wifiSignalState(wifi.r)
|
||||||
|
return (
|
||||||
|
<span className={cn("flex items-center gap-1.5 tabular-nums whitespace-nowrap", className)}>
|
||||||
|
<span
|
||||||
|
className={cn("block size-2 rounded-full shrink-0", {
|
||||||
|
[STATUS_COLORS[SystemStatus.Up]]: state === MeterState.Good,
|
||||||
|
[STATUS_COLORS[SystemStatus.Pending]]: state === MeterState.Warn,
|
||||||
|
[STATUS_COLORS[SystemStatus.Down]]: state === MeterState.Crit,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
{wifi.r === undefined ? "—" : `${wifi.r} dBm`}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
export function IndicatorDot({ system, className }: { system: SystemRecord; className?: ClassValue }) {
|
export function IndicatorDot({ system, className }: { system: SystemRecord; className?: ClassValue }) {
|
||||||
className ||= STATUS_COLORS[system.status as keyof typeof STATUS_COLORS] || ""
|
className ||= STATUS_COLORS[system.status as keyof typeof STATUS_COLORS] || ""
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { expect, test } from "bun:test"
|
import { expect, test } from "bun:test"
|
||||||
import { connectedWiFi, strongestWiFiSignal, wifiColor } from "./wifi"
|
import { MeterState } from "@/lib/enums"
|
||||||
|
import { connectedWiFi, strongestWiFiSignal, wifiColor, wifiSignalState } from "./wifi"
|
||||||
import type { SystemInfo } from "@/types"
|
import type { SystemInfo } from "@/types"
|
||||||
|
|
||||||
const system = (wf?: SystemInfo["wf"], status: "up" | "down" = "up") => ({ status, info: { wf } as SystemInfo })
|
const system = (wf?: SystemInfo["wf"], status: "up" | "down" = "up") => ({ status, info: { wf } as SystemInfo })
|
||||||
@@ -26,3 +27,11 @@ test("strongestWiFiSignal returns the strongest current native RSSI", () => {
|
|||||||
expect(strongestWiFiSignal(system({ wlan0: {} }))).toBeUndefined()
|
expect(strongestWiFiSignal(system({ wlan0: {} }))).toBeUndefined()
|
||||||
expect(strongestWiFiSignal(system({ wlan0: { r: -48 } }, "down"))).toBeUndefined()
|
expect(strongestWiFiSignal(system({ wlan0: { r: -48 } }, "down"))).toBeUndefined()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("wifiSignalState thresholds", () => {
|
||||||
|
expect(wifiSignalState(-40)).toBe(MeterState.Good)
|
||||||
|
expect(wifiSignalState(-65)).toBe(MeterState.Good)
|
||||||
|
expect(wifiSignalState(-66)).toBe(MeterState.Warn)
|
||||||
|
expect(wifiSignalState(-75)).toBe(MeterState.Warn)
|
||||||
|
expect(wifiSignalState(-76)).toBe(MeterState.Crit)
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { MeterState } from "@/lib/enums"
|
||||||
import type { SystemRecord, WiFi } from "@/types"
|
import type { SystemRecord, WiFi } from "@/types"
|
||||||
|
|
||||||
// Current system info is independent of the selected historical chart window.
|
// Current system info is independent of the selected historical chart window.
|
||||||
@@ -21,6 +22,11 @@ export function strongestWiFiSignal(system: Pick<SystemRecord, "status" | "info"
|
|||||||
return strongestWiFi(connectedWiFi(system))?.[1].r
|
return strongestWiFi(connectedWiFi(system))?.[1].r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Signal quality for an RSSI reading: good at -65 dBm or stronger, warn down to -75 dBm, crit below. */
|
||||||
|
export function wifiSignalState(rssi: number): MeterState {
|
||||||
|
return rssi >= -65 ? MeterState.Good : rssi >= -75 ? MeterState.Warn : MeterState.Crit
|
||||||
|
}
|
||||||
|
|
||||||
export function wifiColor(id: string): string {
|
export function wifiColor(id: string): string {
|
||||||
let hash = 0
|
let hash = 0
|
||||||
for (const char of id) hash = (Math.imul(hash, 31) + char.charCodeAt(0)) | 0
|
for (const char of id) hash = (Math.imul(hash, 31) + char.charCodeAt(0)) | 0
|
||||||
|
|||||||
Reference in New Issue
Block a user