mirror of
https://github.com/henrygd/beszel.git
synced 2025-12-17 10:46:16 +01:00
Refactor unit preferences and update chart components
* Refactor user settings to use enum for unit preferences (temperature, network, disk). * Update chart components to utilize new unit formatting functions * Remove deprecated conversion functions and streamline unit handling across charts. * Enhance settings page to allow user selection of unit preferences with updated labels.
This commit is contained in:
@@ -3,7 +3,7 @@ import { toast } from "@/components/ui/use-toast"
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import { $alerts, $copyContent, $systems, $userSettings, pb } from "./stores"
|
||||
import { AlertInfo, AlertRecord, ChartTimeData, ChartTimes, FingerprintRecord, SystemRecord, TemperatureUnit, TemperatureConversion, SpeedUnit, SpeedConversion } from "@/types"
|
||||
import { AlertInfo, AlertRecord, ChartTimeData, ChartTimes, FingerprintRecord, SystemRecord } from "@/types"
|
||||
import { RecordModel, RecordSubscription } from "pocketbase"
|
||||
import { WritableAtom } from "nanostores"
|
||||
import { timeDay, timeHour } from "d3-time"
|
||||
@@ -11,6 +11,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"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@@ -230,12 +231,15 @@ export function toFixedWithoutTrailingZeros(num: number, digits: number) {
|
||||
}
|
||||
|
||||
export function toFixedFloat(num: number, digits: number) {
|
||||
return parseFloat(num.toFixed(digits))
|
||||
return parseFloat((digits === 0 ? Math.ceil(num) : num).toFixed(digits))
|
||||
}
|
||||
|
||||
let decimalFormatters: Map<number, Intl.NumberFormat> = new Map()
|
||||
/** Format number to x decimal places */
|
||||
export function decimalString(num: number, digits = 2) {
|
||||
if (digits === 0) {
|
||||
return Math.ceil(num).toString()
|
||||
}
|
||||
let formatter = decimalFormatters.get(digits)
|
||||
if (!formatter) {
|
||||
formatter = new Intl.NumberFormat(undefined, {
|
||||
@@ -266,143 +270,93 @@ export function useLocalStorage<T>(key: string, defaultValue: T) {
|
||||
return [value, setValue]
|
||||
}
|
||||
|
||||
/** Convert temperature from Celsius to the specified unit */
|
||||
export function convertTemperature(
|
||||
celsius: number,
|
||||
unit: TemperatureUnit = "celsius"
|
||||
): TemperatureConversion {
|
||||
switch (unit) {
|
||||
case "fahrenheit":
|
||||
return { value: (celsius * 9) / 5 + 32, symbol: "°F" }
|
||||
default:
|
||||
return { value: celsius, symbol: "°C" }
|
||||
/** Format temperature to user's preferred unit */
|
||||
export function formatTemperature(celsius: number, unit?: Unit): { value: number; unit: string } {
|
||||
if (!unit) {
|
||||
unit = $userSettings.get().unitTemp || Unit.Celsius
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert network speed from MB/s to the specified unit */
|
||||
export function convertNetworkSpeed(
|
||||
mbps: number,
|
||||
unit: SpeedUnit = "mbps"
|
||||
): SpeedConversion {
|
||||
switch (unit) {
|
||||
case "bps": {
|
||||
const bps = mbps * 8 * 1_000_000 // Convert MB/s to bits per second
|
||||
|
||||
// Format large numbers appropriately
|
||||
if (bps >= 1_000_000_000) {
|
||||
return {
|
||||
value: bps / 1_000_000_000,
|
||||
symbol: " Gbps",
|
||||
display: `${decimalString(bps / 1_000_000_000, bps >= 10_000_000_000 ? 0 : 1)} Gbps`,
|
||||
}
|
||||
} else if (bps >= 1_000_000) {
|
||||
return {
|
||||
value: bps / 1_000_000,
|
||||
symbol: " Mbps",
|
||||
display: `${decimalString(bps / 1_000_000, bps >= 10_000_000 ? 0 : 1)} Mbps`,
|
||||
}
|
||||
} else if (bps >= 1_000) {
|
||||
return {
|
||||
value: bps / 1_000,
|
||||
symbol: " Kbps",
|
||||
display: `${decimalString(bps / 1_000, bps >= 10_000 ? 0 : 1)} Kbps`,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
value: bps,
|
||||
symbol: " bps",
|
||||
display: `${Math.round(bps)} bps`,
|
||||
}
|
||||
}
|
||||
// need loose equality check due to form data being strings
|
||||
if (unit == Unit.Fahrenheit) {
|
||||
return {
|
||||
value: celsius * 1.8 + 32,
|
||||
unit: "°F",
|
||||
}
|
||||
default:
|
||||
return {
|
||||
value: mbps,
|
||||
symbol: " MB/s",
|
||||
display: `${decimalString(mbps, mbps >= 100 ? 1 : 2)} MB/s`,
|
||||
}
|
||||
}
|
||||
return {
|
||||
value: celsius,
|
||||
unit: "°C",
|
||||
}
|
||||
}
|
||||
|
||||
/** Convert disk speed from MB/s to the specified unit */
|
||||
export function convertDiskSpeed(
|
||||
mbps: number,
|
||||
unit: SpeedUnit = "mbps"
|
||||
): SpeedConversion {
|
||||
switch (unit) {
|
||||
case "bps": {
|
||||
const bps = mbps * 8 * 1_000_000 // Convert MB/s to bits per second
|
||||
/** Format bytes to user's preferred unit */
|
||||
export function formatBytes(
|
||||
size: number,
|
||||
perSecond = false,
|
||||
unit = Unit.Bytes,
|
||||
isMegabytes = false
|
||||
): { value: number; unit: string } {
|
||||
// Convert MB to bytes if isMegabytes is true
|
||||
if (isMegabytes) size *= 1024 * 1024
|
||||
|
||||
// Format large numbers appropriately
|
||||
if (bps >= 1_000_000_000) {
|
||||
return {
|
||||
value: bps / 1_000_000_000,
|
||||
symbol: " Gbps",
|
||||
display: `${decimalString(bps / 1_000_000_000, bps >= 10_000_000_000 ? 0 : 1)} Gbps`,
|
||||
}
|
||||
} else if (bps >= 1_000_000) {
|
||||
return {
|
||||
value: bps / 1_000_000,
|
||||
symbol: " Mbps",
|
||||
display: `${decimalString(bps / 1_000_000, bps >= 10_000_000 ? 0 : 1)} Mbps`,
|
||||
}
|
||||
} else if (bps >= 1_000) {
|
||||
return {
|
||||
value: bps / 1_000,
|
||||
symbol: " Kbps",
|
||||
display: `${decimalString(bps / 1_000, bps >= 10_000 ? 0 : 1)} Kbps`,
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
value: bps,
|
||||
symbol: " bps",
|
||||
display: `${Math.round(bps)} bps`,
|
||||
}
|
||||
// need loose equality check due to form data being strings
|
||||
if (unit == Unit.Bits) {
|
||||
const bits = size * 8
|
||||
const suffix = perSecond ? "ps" : ""
|
||||
if (bits < 1000) return { value: bits, unit: `b${suffix}` }
|
||||
if (bits < 1_000_000) return { value: bits / 1_000, unit: `Kb${suffix}` }
|
||||
if (bits < 1_000_000_000)
|
||||
return {
|
||||
value: bits / 1_000_000,
|
||||
unit: `Mb${suffix}`,
|
||||
}
|
||||
if (bits < 1_000_000_000_000)
|
||||
return {
|
||||
value: bits / 1_000_000_000,
|
||||
unit: `Gb${suffix}`,
|
||||
}
|
||||
return {
|
||||
value: bits / 1_000_000_000_000,
|
||||
unit: `Tb${suffix}`,
|
||||
}
|
||||
default:
|
||||
return {
|
||||
value: mbps,
|
||||
symbol: " MB/s",
|
||||
display: `${decimalString(mbps, mbps >= 100 ? 1 : 2)} MB/s`,
|
||||
}
|
||||
}
|
||||
// bytes
|
||||
const suffix = perSecond ? "/s" : ""
|
||||
if (size < 100) return { value: size, unit: `B${suffix}` }
|
||||
if (size < 1000 * 1024) return { value: size / 1024, unit: `KB${suffix}` }
|
||||
if (size < 1000 * 1024 ** 2)
|
||||
return {
|
||||
value: size / 1024 ** 2,
|
||||
unit: `MB${suffix}`,
|
||||
}
|
||||
if (size < 1000 * 1024 ** 3)
|
||||
return {
|
||||
value: size / 1024 ** 3,
|
||||
unit: `GB${suffix}`,
|
||||
}
|
||||
return {
|
||||
value: size / 1024 ** 4,
|
||||
unit: `TB${suffix}`,
|
||||
}
|
||||
}
|
||||
|
||||
/** Fetch or create user settings in database */
|
||||
export async function updateUserSettings() {
|
||||
try {
|
||||
const req = await pb.collection("user_settings").getFirstListItem("", { fields: "settings" })
|
||||
$userSettings.set(req.settings)
|
||||
return
|
||||
} catch (e) {
|
||||
console.log("get settings", e)
|
||||
console.error("get settings", e)
|
||||
}
|
||||
// create user settings if error fetching existing
|
||||
try {
|
||||
const createdSettings = await pb.collection("user_settings").create({ user: pb.authStore.record!.id })
|
||||
$userSettings.set(createdSettings.settings)
|
||||
} catch (e) {
|
||||
console.log("create settings", e)
|
||||
console.error("create settings", e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value and unit of size (TB, GB, or MB) for a given size
|
||||
* @param n size in gigabytes or megabytes
|
||||
* @param isGigabytes boolean indicating if n represents gigabytes (true) or megabytes (false)
|
||||
* @returns an object containing the value and unit of size
|
||||
*/
|
||||
export const getSizeAndUnit = (n: number, isGigabytes = true) => {
|
||||
const sizeInGB = isGigabytes ? n : n / 1_000
|
||||
|
||||
if (sizeInGB >= 1_000) {
|
||||
return { v: sizeInGB / 1_000, u: " TB" }
|
||||
} else if (sizeInGB >= 1) {
|
||||
return { v: sizeInGB, u: " GB" }
|
||||
}
|
||||
return { v: isGigabytes ? sizeInGB * 1_000 : n, u: " MB" }
|
||||
}
|
||||
|
||||
export const chartMargin = { top: 12 }
|
||||
|
||||
export const alertInfo: Record<string, AlertInfo> = {
|
||||
|
||||
Reference in New Issue
Block a user