diff --git a/internal/site/src/components/routes/system.tsx b/internal/site/src/components/routes/system.tsx index 86fd1ffe..1c94bd53 100644 --- a/internal/site/src/components/routes/system.tsx +++ b/internal/site/src/components/routes/system.tsx @@ -45,6 +45,7 @@ import { debounce, decimalString, formatBytes, + secondsToString, getHostDisplayValue, listen, parseSemVer, @@ -358,21 +359,13 @@ export default memo(function SystemDetail({ id }: { id: string }) { value: system.info.k, }, } - let uptime: React.ReactNode + let uptime: string if (system.info.u < 3600) { - uptime = ( - - ) - } else if (system.info.u < 172800) { - uptime = + uptime = secondsToString(system.info.u, "minute") + } else if (system.info.u * 360000) { + uptime = secondsToString(system.info.u, "hour") } else { - uptime = + uptime = secondsToString(system.info.u, "day") } return [ { value: getHostDisplayValue(system), Icon: GlobeIcon }, diff --git a/internal/site/src/components/routes/system/smart-table.tsx b/internal/site/src/components/routes/system/smart-table.tsx index 2a3ca0f7..07d4069a 100644 --- a/internal/site/src/components/routes/system/smart-table.tsx +++ b/internal/site/src/components/routes/system/smart-table.tsx @@ -11,7 +11,7 @@ import { SortingState, useReactTable, } from "@tanstack/react-table" -import { Activity, Box, Clock, HardDrive, HashIcon, CpuIcon, BinaryIcon, RotateCwIcon, LoaderCircleIcon, CheckCircle2Icon, XCircleIcon, ArrowLeftRightIcon, ArrowUpDownIcon } from "lucide-react" +import { Activity, Box, Clock, HardDrive, HashIcon, CpuIcon, BinaryIcon, RotateCwIcon, LoaderCircleIcon, CheckCircle2Icon, XCircleIcon, ArrowLeftRightIcon } from "lucide-react" import { Card, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle } from "@/components/ui/sheet" import { Input } from "@/components/ui/input" @@ -27,7 +27,7 @@ import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { pb } from "@/lib/api" import { SmartData, SmartAttribute } from "@/types" -import { formatBytes, toFixedFloat, formatTemperature, cn } from "@/lib/utils" +import { formatBytes, toFixedFloat, formatTemperature, cn, secondsToString } from "@/lib/utils" import { Trans } from "@lingui/react/macro" import { ThermometerIcon } from "@/components/ui/icons" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" @@ -172,8 +172,8 @@ export const columns: ColumnDef[] = [ accessorKey: "powerOnHours", invertSorting: true, header: ({ column }) => , - cell: ({ row }) => { - const hours = row.getValue("powerOnHours") as number | undefined + cell: ({ getValue }) => { + const hours = (getValue() ?? 0) as number if (!hours && hours !== 0) { return ( @@ -181,11 +181,11 @@ export const columns: ColumnDef[] = [ ) } - const days = Math.floor(hours / 24) + const seconds = hours * 3600 return ( - {hours.toLocaleString()} hours - {days} days + {secondsToString(seconds, "hour")} + {secondsToString(seconds, "day")} ) }, @@ -234,7 +234,6 @@ function HeaderButton({ column, name, Icon }: { column: Column; name: > {Icon && } {name} - ) } @@ -342,7 +341,7 @@ export default function DisksTable({ systemId }: { systemId: string }) { onClick={() => openSheet(row.original)} > {row.getVisibleCells().map((cell) => ( - + {flexRender( cell.column.columnDef.cell, cell.getContext() diff --git a/internal/site/src/lib/utils.ts b/internal/site/src/lib/utils.ts index 3c707077..706f54a0 100644 --- a/internal/site/src/lib/utils.ts +++ b/internal/site/src/lib/utils.ts @@ -1,4 +1,4 @@ -import { t } from "@lingui/core/macro" +import { plural, t } from "@lingui/core/macro" import { type ClassValue, clsx } from "clsx" import { listenKeys } from "nanostores" import { timeDay, timeHour, timeMinute } from "d3-time" @@ -111,18 +111,17 @@ export const updateFavicon = (() => { - ${ - downCount > 0 && - ` + ${downCount > 0 && + ` ${downCount} ` - } + } ` const blob = new Blob([svg], { type: "image/svg+xml" }) const url = URL.createObjectURL(blob) - ;(document.querySelector("link[rel='icon']") as HTMLLinkElement).href = url + ; (document.querySelector("link[rel='icon']") as HTMLLinkElement).href = url } })() @@ -429,3 +428,17 @@ export function runOnce any>(fn: T): T { return state.result }) as T } + +/** Format seconds to hours, minutes, or seconds */ +export function secondsToString(seconds: number, unit: "hour" | "minute" | "day"): string { + const count = Math.floor(seconds / (unit === "hour" ? 3600 : unit === "minute" ? 60 : 86400)) + const countString = count.toLocaleString() + switch (unit) { + case "minute": + return plural(count, { one: `${countString} minute`, few: `${countString} minutes`, many: `${countString} minutes`, other: `${countString} minutes` }) + case "hour": + return plural(count, { one: `${countString} hour`, other: `${countString} hours` }) + case "day": + return plural(count, { one: `${countString} day`, other: `${countString} days` }) + } +} \ No newline at end of file