From 93faaacbd1e090dd4e48bd2bc7684d1747bccfa7 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Wed, 29 Oct 2025 10:37:18 +0000 Subject: [PATCH] Simplify metric bar labels --- .../src/components/Backups/UnifiedBackups.tsx | 12 +++++------ .../src/components/Dashboard/GuestRow.tsx | 18 ++++++++--------- .../src/components/Docker/DockerHosts.tsx | 4 ++-- .../components/Docker/DockerUnifiedTable.tsx | 20 +++++++++---------- .../src/components/Hosts/HostsOverview.tsx | 12 +++++------ .../src/components/Storage/Storage.tsx | 20 +++++++++---------- .../components/shared/NodeSummaryTable.tsx | 8 ++++---- frontend-modern/src/utils/format.ts | 15 ++++---------- 8 files changed, 51 insertions(+), 58 deletions(-) diff --git a/frontend-modern/src/components/Backups/UnifiedBackups.tsx b/frontend-modern/src/components/Backups/UnifiedBackups.tsx index c51c5ac86..1d702da83 100644 --- a/frontend-modern/src/components/Backups/UnifiedBackups.tsx +++ b/frontend-modern/src/components/Backups/UnifiedBackups.tsx @@ -1,7 +1,7 @@ import { Component, createSignal, Show, For, createMemo, createEffect } from 'solid-js'; import { useNavigate } from '@solidjs/router'; import { useWebSocket } from '@/App'; -import { formatBytes, formatAbsoluteTime, formatRelativeTime, formatUptime } from '@/utils/format'; +import { formatBytes, formatAbsoluteTime, formatRelativeTime, formatUptime, formatPercent } from '@/utils/format'; import { createLocalStorageBooleanSignal, STORAGE_KEYS } from '@/utils/localStorage'; import { parseFilterStack, evaluateFilterStack } from '@/utils/searchQuery'; import { UnifiedNodeSelector } from '@/components/shared/UnifiedNodeSelector'; @@ -1302,15 +1302,15 @@ const UnifiedBackups: Component = () => { - + { diff --git a/frontend-modern/src/components/Dashboard/GuestRow.tsx b/frontend-modern/src/components/Dashboard/GuestRow.tsx index 401886051..24515d4e7 100644 --- a/frontend-modern/src/components/Dashboard/GuestRow.tsx +++ b/frontend-modern/src/components/Dashboard/GuestRow.tsx @@ -1,6 +1,6 @@ import { createMemo, createSignal, createEffect, on, Show, For } from 'solid-js'; import type { VM, Container } from '@/types/api'; -import { formatBytes, formatUptime } from '@/utils/format'; +import { formatBytes, formatPercent, formatUptime } from '@/utils/format'; import { MetricBar } from './MetricBar'; import { IOMetric } from './IOMetric'; import { TagBadges } from './TagBadges'; @@ -113,7 +113,7 @@ export function GuestRow(props: GuestRowProps) { if (!props.guest.memory) return undefined; const used = props.guest.memory.used ?? 0; const total = props.guest.memory.total ?? 0; - return `${formatBytes(used)}/${formatBytes(total)}`; + return `${formatBytes(used, 0)}/${formatBytes(total, 0)}`; }); const memoryExtraLines = createMemo(() => { if (!props.guest.memory) return undefined; @@ -124,11 +124,11 @@ export function GuestRow(props: GuestRowProps) { props.guest.memory.balloon > 0 && props.guest.memory.balloon !== total ) { - lines.push(`Balloon: ${formatBytes(props.guest.memory.balloon)}`); + lines.push(`Balloon: ${formatBytes(props.guest.memory.balloon, 0)}`); } if (props.guest.memory.swapTotal && props.guest.memory.swapTotal > 0) { const swapUsed = props.guest.memory.swapUsed ?? 0; - lines.push(`Swap: ${formatBytes(swapUsed)} / ${formatBytes(props.guest.memory.swapTotal)}`); + lines.push(`Swap: ${formatBytes(swapUsed, 0)} / ${formatBytes(props.guest.memory.swapTotal, 0)}`); } return lines.length > 0 ? lines : undefined; }); @@ -584,10 +584,10 @@ export function GuestRow(props: GuestRowProps) { -}> -}> @@ -621,10 +621,10 @@ export function GuestRow(props: GuestRowProps) { > = (props) => { ? clampPercent((memoryUsed / memoryTotal) * 100) : 0; const memoryLabel = - memoryTotal > 0 ? `${formatBytes(memoryUsed)} / ${formatBytes(memoryTotal)}` : undefined; + memoryTotal > 0 ? `${formatBytes(memoryUsed, 0)} / ${formatBytes(memoryTotal, 0)}` : undefined; let diskPercent = 0; let diskLabel: string | undefined; @@ -110,7 +110,7 @@ export const DockerHosts: Component = (props) => { ); if (totals.total > 0) { diskPercent = clampPercent((totals.used / totals.total) * 100); - diskLabel = `${formatBytes(totals.used)} / ${formatBytes(totals.total)}`; + diskLabel = `${formatBytes(totals.used, 0)} / ${formatBytes(totals.total, 0)}`; } } diff --git a/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx b/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx index e6bb22b6d..61377cfc5 100644 --- a/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx +++ b/frontend-modern/src/components/Docker/DockerUnifiedTable.tsx @@ -533,9 +533,9 @@ const DockerContainerRow: Component<{ const memPercent = () => Math.max(0, Math.min(100, container.memoryPercent ?? 0)); const memUsageLabel = () => { if (!container.memoryUsageBytes) return undefined; - const used = formatBytes(container.memoryUsageBytes); + const used = formatBytes(container.memoryUsageBytes, 0); const limit = container.memoryLimitBytes - ? formatBytes(container.memoryLimitBytes) + ? formatBytes(container.memoryLimitBytes, 0) : undefined; return limit ? `${used} / ${limit}` : used; }; @@ -695,7 +695,7 @@ const DockerContainerRow: Component<{ {statusLabel()} - + 0} fallback={} @@ -703,7 +703,7 @@ const DockerContainerRow: Component<{ - + 0} fallback={} @@ -1132,8 +1132,8 @@ const DockerServiceRow: Component<{ {badge.label} - — - — + — + — {(service.runningTasks ?? 0)}/{service.desiredTasks ?? 0} @@ -1451,8 +1451,8 @@ const DockerUnifiedTable: Component = (props) => { } > - - + +
- - diff --git a/frontend-modern/src/components/shared/NodeSummaryTable.tsx b/frontend-modern/src/components/shared/NodeSummaryTable.tsx index 08e206e4b..0081c8127 100644 --- a/frontend-modern/src/components/shared/NodeSummaryTable.tsx +++ b/frontend-modern/src/components/shared/NodeSummaryTable.tsx @@ -195,12 +195,12 @@ export const NodeSummaryTable: Component = (props) => { if (item.type === 'pve') { const node = item.data as Node; if (!node.disk) return undefined; - return `${formatBytes(node.disk.used)}/${formatBytes(node.disk.total)}`; + return `${formatBytes(node.disk.used, 0)}/${formatBytes(node.disk.total, 0)}`; } const pbs = item.data as PBSInstance; if (!pbs.datastores || pbs.datastores.length === 0) return undefined; const totals = getPbsTotals(pbs); - return `${formatBytes(totals.used)}/${formatBytes(totals.total)}`; + return `${formatBytes(totals.used, 0)}/${formatBytes(totals.total, 0)}`; }; const getTemperatureValue = (item: SortableItem) => { @@ -583,9 +583,9 @@ export const NodeSummaryTable: Component = (props) => { label={formatPercent(memoryPercentValue ?? 0)} sublabel={ isPVE && node!.memory - ? `${formatBytes(node!.memory.used)}/${formatBytes(node!.memory.total)}` + ? `${formatBytes(node!.memory.used, 0)}/${formatBytes(node!.memory.total, 0)}` : isPBS && pbs!.memoryTotal - ? `${formatBytes(pbs!.memoryUsed)}/${formatBytes(pbs!.memoryTotal)}` + ? `${formatBytes(pbs!.memoryUsed, 0)}/${formatBytes(pbs!.memoryTotal, 0)}` : undefined } type="memory" diff --git a/frontend-modern/src/utils/format.ts b/frontend-modern/src/utils/format.ts index 08196c692..d2feb5235 100644 --- a/frontend-modern/src/utils/format.ts +++ b/frontend-modern/src/utils/format.ts @@ -17,19 +17,12 @@ export function formatSpeed(bytesPerSecond: number, decimals = 0): string { export function formatPercent(value: number): string { if (!Number.isFinite(value)) return '0%'; - const abs = Math.abs(value); - - if (abs >= 10) { - return `${Math.round(value)}%`; + if (abs === 0) return '0%'; + if (abs < 0.5) { + return '0%'; } - - if (abs >= 1) { - return `${value.toFixed(1)}%`; - } - - // Preserve tiny signals without overly long labels - return `${value.toFixed(2)}%`; + return `${Math.round(value)}%`; } export function formatUptime(seconds: number): string {
@@ -1467,10 +1467,10 @@ const DockerUnifiedTable: Component = (props) => { Status + CPU + Memory diff --git a/frontend-modern/src/components/Hosts/HostsOverview.tsx b/frontend-modern/src/components/Hosts/HostsOverview.tsx index db22c8816..e6e1dbae4 100644 --- a/frontend-modern/src/components/Hosts/HostsOverview.tsx +++ b/frontend-modern/src/components/Hosts/HostsOverview.tsx @@ -2,7 +2,7 @@ import type { Component } from 'solid-js'; import { For, Show, createMemo, createSignal, createEffect, on, onMount, onCleanup } from 'solid-js'; import { useNavigate } from '@solidjs/router'; import type { Host } from '@/types/api'; -import { formatBytes, formatRelativeTime, formatUptime } from '@/utils/format'; +import { formatBytes, formatPercent, formatRelativeTime, formatUptime } from '@/utils/format'; import { Card } from '@/components/shared/Card'; import { ScrollableTable } from '@/components/shared/ScrollableTable'; import { EmptyState } from '@/components/shared/EmptyState'; @@ -211,8 +211,8 @@ export const HostsOverview: Component = (props) => { {(host) => { const cpuPercent = () => host.cpuUsage ?? 0; const memPercent = () => host.memory?.usage ?? 0; - const memUsed = () => formatBytes(host.memory?.used ?? 0); - const memTotal = () => formatBytes(host.memory?.total ?? 0); + const memUsed = () => formatBytes(host.memory?.used ?? 0, 0); + const memTotal = () => formatBytes(host.memory?.total ?? 0, 0); // Drawer state const [drawerOpen, setDrawerOpen] = createSignal(drawerState.get(host.id) ?? false); @@ -301,7 +301,7 @@ export const HostsOverview: Component = (props) => { fallback={} > @@ -413,14 +413,14 @@ export const HostsOverview: Component = (props) => {
{disk.mountpoint || disk.device} - {formatBytes(disk.used ?? 0)} / {formatBytes(disk.total ?? 0)} + {formatBytes(disk.used ?? 0, 0)} / {formatBytes(disk.total ?? 0, 0)}
0}>
diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx index 392e6e908..eb6adb972 100644 --- a/frontend-modern/src/components/Storage/Storage.tsx +++ b/frontend-modern/src/components/Storage/Storage.tsx @@ -2,7 +2,7 @@ import { Component, For, Show, createSignal, createMemo, createEffect } from 'so import { useNavigate } from '@solidjs/router'; import { useWebSocket } from '@/App'; import { getAlertStyles } from '@/utils/alerts'; -import { formatBytes } from '@/utils/format'; +import { formatBytes, formatPercent } from '@/utils/format'; import type { Storage as StorageType, CephCluster } from '@/types/api'; import { ComponentErrorBoundary } from '@/components/ErrorBoundary'; import { UnifiedNodeSelector } from '@/components/shared/UnifiedNodeSelector'; @@ -858,7 +858,7 @@ const Storage: Component = () => { const used = Math.max(0, cluster.usedBytes || 0); const percent = total > 0 ? (used / total) * 100 : 0; parts.push( - `${formatBytes(used)} / ${formatBytes(total)} (${percent.toFixed(1)}%)`, + `${formatBytes(used, 0)} / ${formatBytes(total, 0)} (${formatPercent(percent)})`, ); if ( Number.isFinite(cluster.numOsds) && @@ -883,7 +883,7 @@ const Storage: Component = () => { if (totals.total > 0) { const percent = (totals.used / totals.total) * 100; parts.push( - `${formatBytes(totals.used)} / ${formatBytes(totals.total)} (${percent.toFixed(1)}%)`, + `${formatBytes(totals.used, 0)} / ${formatBytes(totals.total, 0)} (${formatPercent(percent)})`, ); } } @@ -900,7 +900,7 @@ const Storage: Component = () => { if (!pool) return ''; const total = Math.max(1, pool.storedBytes + pool.availableBytes); const percent = total > 0 ? (pool.storedBytes / total) * 100 : 0; - return `${pool.name}: ${percent.toFixed(1)}%`; + return `${pool.name}: ${formatPercent(percent)}`; }) .filter(Boolean) .join(', '); @@ -917,7 +917,7 @@ const Storage: Component = () => { const total = Math.max(1, item.total || 0); const used = Math.max(0, item.used || 0); const percent = total > 0 ? (used / total) * 100 : 0; - return `${item.name}: ${percent.toFixed(1)}%`; + return `${item.name}: ${formatPercent(percent)}`; }) .filter(Boolean) .join(', '); @@ -1124,18 +1124,18 @@ const Storage: Component = () => { /> - {usagePercent.toFixed(0)}% ( - {formatBytes(storage.used || 0)}/ - {formatBytes(storage.total || 0)}) + {formatPercent(usagePercent)} ( + {formatBytes(storage.used || 0, 0)}/ + {formatBytes(storage.total || 0, 0)})
- {formatBytes(storage.total || 0)} + {formatBytes(storage.total || 0, 0)}