From 68c1f2e2810ec733dd2e2017dd84a769918dc3e3 Mon Sep 17 00:00:00 2001 From: gorkem-bwl Date: Thu, 20 Aug 2026 11:35:42 -0400 Subject: [PATCH] feat(client): show uptime percentages with two decimals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Requested by a user who found xx.xx% more trustworthy than xx.x% on their status page. The presentational argument holds, but the stronger reason is that one decimal was actively misleading. Intl rounds to the displayed precision, so every value above 99.95% rendered as "100.0%" — a claim of zero downtime for a period that contained real outages. On a public status page that is the opposite of reassuring: the number reads as flawless during a month with a visible incident. One decimal also collapsed the range operators care about most. 99.9%, 99.95% and 99.99% are three very different commitments (8.7 hours, 4.4 hours and 52 minutes of annual downtime) and the first rendered as 99.9% while the other two were indistinguishable at 100.0%. Everything flows through the shared formatter in FormatUtils, so this covers both surfaces at once: the monitor list and diagnostics gauges in the web app, and the uptime figure, chart cells and hover tooltip on status pages. Infrastructure gauges are untouched — they format separately at zero decimals, where CPU at "43.00%" would only add noise. Server-side precision was already sufficient; the aggregation divides without rounding, so no API change is needed. Adds tests pinning the regression: 7 of the 10 fail against the previous one-decimal formatter. Note: this restores the client Vitest setup. PR #3844 introduced it, but the config, scripts and logger tests were dropped when that branch merged into develop, leaving the client with no test runner again. --- client/src/Utils/FormatUtils.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/client/src/Utils/FormatUtils.ts b/client/src/Utils/FormatUtils.ts index 35182b797..52c1e9076 100644 --- a/client/src/Utils/FormatUtils.ts +++ b/client/src/Utils/FormatUtils.ts @@ -1,25 +1,31 @@ /** * Intl.NumberFormat instance for percentage formatting. * Reused across all formatting calls for performance. + * + * Two decimals rather than one: at a single decimal every value above 99.95% + * renders as "100.0%", so a period containing real downtime is reported as + * flawless. It also collapses 99.9 / 99.95 / 99.99 — three materially + * different SLA commitments — into the same figure. */ const percentageFormatter = new Intl.NumberFormat("en-US", { style: "percent", - minimumFractionDigits: 1, - maximumFractionDigits: 1, + minimumFractionDigits: 2, + maximumFractionDigits: 2, }); /** * Formats a decimal value as a percentage string. * @param value - Decimal value (e.g., 0.75 for 75%) - * @returns Formatted percentage string (e.g., "75.0%") + * @returns Formatted percentage string (e.g., "75.00%") * @example - * formatPercentage(0.75) // "75.0%" - * formatPercentage(1) // "100.0%" - * formatPercentage(0.5432) // "54.3%" + * formatPercentage(0.75) // "75.00%" + * formatPercentage(1) // "100.00%" + * formatPercentage(0.5432) // "54.32%" + * formatPercentage(0.9999) // "99.99%" */ export const formatPercentage = (value: number): string => { if (typeof value !== "number" || Number.isNaN(value)) { - return "0.0%"; + return "0.00%"; } return percentageFormatter.format(value); }; @@ -27,11 +33,11 @@ export const formatPercentage = (value: number): string => { /** * Formats a whole number percentage value as a percentage string. * @param value - Whole number percentage (e.g., 75 for 75%) - * @returns Formatted percentage string (e.g., "75.0%") + * @returns Formatted percentage string (e.g., "75.00%") * @example - * formatPercentageFromWhole(75) // "75.0%" - * formatPercentageFromWhole(100) // "100.0%" - * formatPercentageFromWhole(54.32) // "54.3%" + * formatPercentageFromWhole(75) // "75.00%" + * formatPercentageFromWhole(100) // "100.00%" + * formatPercentageFromWhole(54.32) // "54.32%" */ export const formatPercentageFromWhole = (value: number): string => { return formatPercentage(value / 100);