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.
50 lines
1.6 KiB
TypeScript
50 lines
1.6 KiB
TypeScript
/**
|
|
* 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: 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.00%")
|
|
* @example
|
|
* 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.00%";
|
|
}
|
|
return percentageFormatter.format(value);
|
|
};
|
|
|
|
/**
|
|
* 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.00%")
|
|
* @example
|
|
* formatPercentageFromWhole(75) // "75.00%"
|
|
* formatPercentageFromWhole(100) // "100.00%"
|
|
* formatPercentageFromWhole(54.32) // "54.32%"
|
|
*/
|
|
export const formatPercentageFromWhole = (value: number): string => {
|
|
return formatPercentage(value / 100);
|
|
};
|
|
|
|
export const getPercentage = (value: number, total: number) => {
|
|
if (!value || !total) return 0;
|
|
return (value / total) * 100;
|
|
};
|