Skip to content

Commit 1c390a8

Browse files
authored
Merge pull request #3250 from bluewave-labs/fix/unit-formatting
Fix/unit formatting
2 parents 9caa6c8 + 43ac5b5 commit 1c390a8

File tree

10 files changed

+91
-76
lines changed

10 files changed

+91
-76
lines changed

client/src/Components/v2/monitors/MonitorStatBoxes.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import Stack from "@mui/material/Stack";
22
import { StatBox } from "@/Components/v2/design-elements";
33

4+
import prettyMilliseconds from "pretty-ms";
45
import { useTheme } from "@mui/material/styles";
56
import type { MonitorStats, Monitor } from "@/Types/Monitor";
6-
import { getHumanReadableDuration } from "@/Utils/timeUtilsLegacy.js";
77
import { getStatusPalette } from "@/Utils/MonitorUtils";
88
import { useTranslation } from "react-i18next";
99

@@ -32,11 +32,16 @@ export const MonitorStatBoxes = ({
3232

3333
// Determine time since last check
3434
const timeOfLastCheck = monitorStats?.lastCheckTimestamp || 0;
35-
const timeSinceLastCheck = Date.now() - timeOfLastCheck;
35+
const timeSinceLastCheck = Date.now() - timeOfLastCheck || 0;
3636

37-
const streakTime = getHumanReadableDuration(timeSinceLastFailure);
37+
const options = {
38+
secondsDecimalDigits: 0,
39+
millisecondsDecimalDigits: 0,
40+
};
3841

39-
const lastCheckTime = getHumanReadableDuration(timeSinceLastCheck);
42+
const streakTime = prettyMilliseconds(timeSinceLastFailure, options);
43+
44+
const lastCheckTime = prettyMilliseconds(timeSinceLastCheck, options);
4045
const palette = getStatusPalette(monitor?.status);
4146

4247
return (
@@ -55,7 +60,7 @@ export const MonitorStatBoxes = ({
5560
/>
5661
<StatBox
5762
title={t("pages.common.monitors.statBoxes.lastResponseTime")}
58-
subtitle={monitorStats?.lastResponseTime + " ms"}
63+
subtitle={prettyMilliseconds(monitorStats?.lastResponseTime ?? 0)}
5964
/>
6065

6166
{monitor?.type === "http" && (

client/src/Pages/Incidents/utils.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { Incident, IncidentSummaryItem } from "@/Types/Incident";
2-
import { getHumanReadableDuration } from "@/Utils/timeUtilsLegacy.js";
2+
import prettyMilliseconds from "pretty-ms";
33

44
type IncidentLike = Pick<Incident, "startTime" | "endTime" | "status">;
55

@@ -24,5 +24,8 @@ export const getIncidentsDuration = (incident: IncidentLike | IncidentSummaryIte
2424
return "-";
2525
}
2626

27-
return getHumanReadableDuration(durationMs);
27+
return prettyMilliseconds(durationMs, {
28+
secondsDecimalDigits: 0,
29+
millisecondsDecimalDigits: 0,
30+
});
2831
};

client/src/Pages/Infrastructure/Details/Components/Gauges.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import Stack from "@mui/material/Stack";
22
import { DetailGauge } from "@/Components/v2/design-elements";
33

4+
import prettyBytes from "pretty-bytes";
45
import { useTranslation } from "react-i18next";
5-
import { getGbs, getFrequency } from "@/Utils/InfraUtils";
6+
import { getFrequency } from "@/Utils/InfraUtils";
67
import { useTheme } from "@mui/material";
78
import useMediaQuery from "@mui/material/useMediaQuery";
89
import type { CheckSnapshot } from "@/Types/Check";
@@ -30,9 +31,9 @@ export const InfraDetailsGauges = ({
3031
title={t("pages.infrastructure.gauges.memory.title")}
3132
progress={(snapshot?.memory?.usage_percent || 0) * 100}
3233
upperLabel={t("pages.infrastructure.gauges.memory.upperLabel")}
33-
upperValue={getGbs(snapshot?.memory?.used_bytes || 0)}
34+
upperValue={prettyBytes(snapshot?.memory?.used_bytes || 0)}
3435
lowerLabel={t("pages.infrastructure.gauges.memory.lowerLabel")}
35-
lowerValue={getGbs(snapshot?.memory?.total_bytes || 0)}
36+
lowerValue={prettyBytes(snapshot?.memory?.total_bytes || 0)}
3637
/>
3738
<DetailGauge
3839
title={t("pages.infrastructure.gauges.cpu.title")}
@@ -50,9 +51,9 @@ export const InfraDetailsGauges = ({
5051
title={t("pages.infrastructure.gauges.disk.title", { idx })}
5152
progress={(disk.usage_percent || 0) * 100}
5253
upperLabel={t("pages.infrastructure.gauges.disk.upperLabel")}
53-
upperValue={getGbs(disk?.used_bytes || 0)}
54+
upperValue={prettyBytes(disk?.used_bytes || 0)}
5455
lowerLabel={t("pages.infrastructure.gauges.disk.lowerLabel")}
55-
lowerValue={getGbs(disk?.total_bytes || 0)}
56+
lowerValue={prettyBytes(disk?.total_bytes || 0)}
5657
/>
5758
);
5859
})}

client/src/Pages/Infrastructure/Details/Components/StatusBoxes.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
import Stack from "@mui/material/Stack";
22
import { StatBox } from "@/Components/v2/design-elements";
33

4+
import prettyBytes from "pretty-bytes";
45
import { useTheme } from "@mui/material";
56
import { useTranslation } from "react-i18next";
67
import type { Monitor } from "@/Types/Monitor";
7-
import {
8-
getAvgTemp,
9-
getCores,
10-
getFrequency,
11-
getGbs,
12-
getDiskTotalGbs,
13-
getOsAndPlatform,
14-
} from "@/Utils/InfraUtils";
8+
import { getAvgTemp, getCores, getFrequency, getOsAndPlatform } from "@/Utils/InfraUtils";
159

1610
export const StatusBoxes = ({ monitor }: { monitor: Monitor }) => {
1711
const { t } = useTranslation();
@@ -24,8 +18,9 @@ export const StatusBoxes = ({ monitor }: { monitor: Monitor }) => {
2418
const cpuFrequency = getFrequency(latestCheck?.cpu?.frequency);
2519
const cpuTemps = latestCheck?.cpu?.temperature ?? [];
2620
const cpuTemperature = getAvgTemp(cpuTemps);
27-
const memoryTotalBytes = getGbs(latestCheck?.memory?.total_bytes);
28-
const diskTotalBytes = getDiskTotalGbs(latestCheck?.disk);
21+
const memoryTotalBytes = latestCheck?.memory?.total_bytes ?? 0;
22+
const diskTotalBytes =
23+
latestCheck?.disk?.reduce((acc, disk) => acc + (disk.total_bytes || 0), 0) || 0;
2924
const os = getOsAndPlatform(latestCheck?.host);
3025

3126
const platform = latestCheck?.host?.platform ?? undefined;
@@ -58,11 +53,11 @@ export const StatusBoxes = ({ monitor }: { monitor: Monitor }) => {
5853
/>
5954
<StatBox
6055
title={t("pages.infrastructure.statBoxes.memory")}
61-
subtitle={memoryTotalBytes.toString()}
56+
subtitle={prettyBytes(memoryTotalBytes)}
6257
/>
6358
<StatBox
6459
title={t("pages.infrastructure.statBoxes.disk")}
65-
subtitle={diskTotalBytes.toString()}
60+
subtitle={prettyBytes(diskTotalBytes)}
6661
/>
6762
<StatBox
6863
title={t("pages.infrastructure.statBoxes.os")}

client/src/Pages/Logs/components/StatGauges.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Stack from "@mui/material/Stack";
22
import { DetailGauge } from "@/Components/v2/design-elements";
33

4+
import { getPercentage, formatPercentageFromWhole } from "@/Utils/FormatUtils";
45
import prettyBytes from "pretty-bytes";
56
import { useTranslation } from "react-i18next";
67
import { useTheme } from "@mui/material";
@@ -10,17 +11,6 @@ interface StatGaugesProps {
1011
diagnostics: Diagnostics | null;
1112
}
1213

13-
const getPercentage = (value: number, total: number) => {
14-
if (!value || !total) return 0;
15-
return (value / total) * 100;
16-
};
17-
18-
const formatPercentage = new Intl.NumberFormat("en-US", {
19-
style: "percent",
20-
minimumFractionDigits: 1,
21-
maximumFractionDigits: 1,
22-
});
23-
2414
export const StatGauges = ({ diagnostics }: StatGaugesProps) => {
2515
const theme = useTheme();
2616
const { t } = useTranslation();
@@ -51,33 +41,31 @@ export const StatGauges = ({ diagnostics }: StatGaugesProps) => {
5141
<DetailGauge
5242
title={t("pages.logs.diagnostics.gauges.heapAllocation")}
5343
progress={heapTotalSize}
54-
upperValue={formatPercentage.format(heapTotalSize / 100)}
44+
upperValue={formatPercentageFromWhole(heapTotalSize)}
5545
lowerLabel={t("pages.logs.diagnostics.gauges.total")}
5646
lowerValue={prettyBytes(diagnostics.v8HeapStats?.heapSizeLimitBytes ?? 0)}
5747
/>
5848
<DetailGauge
5949
title={t("pages.logs.diagnostics.gauges.heapUsage")}
6050
progress={heapUsedSize}
6151
upperLabel={t("pages.logs.diagnostics.gauges.availableMemoryPercentage")}
62-
upperValue={formatPercentage.format(heapUsedSize / 100)}
52+
upperValue={formatPercentageFromWhole(heapUsedSize)}
6353
lowerLabel={t("pages.logs.diagnostics.gauges.used")}
6454
lowerValue={prettyBytes(diagnostics.v8HeapStats?.usedHeapSizeBytes ?? 0)}
6555
/>
6656
<DetailGauge
6757
title={t("pages.logs.diagnostics.gauges.heapUtilization")}
6858
progress={actualHeapUsed}
6959
upperLabel={t("pages.logs.diagnostics.gauges.allocatedPercentage")}
70-
upperValue={formatPercentage.format(actualHeapUsed / 100)}
60+
upperValue={formatPercentageFromWhole(actualHeapUsed)}
7161
lowerLabel={t("pages.logs.diagnostics.gauges.total")}
7262
lowerValue={prettyBytes(diagnostics.v8HeapStats?.usedHeapSizeBytes ?? 0)}
7363
/>
7464
<DetailGauge
7565
title={t("pages.logs.diagnostics.gauges.instantCpuUsage")}
7666
progress={diagnostics.cpuUsage?.usagePercentage ?? 0}
7767
upperLabel={t("pages.logs.diagnostics.gauges.usedSPercentage")}
78-
upperValue={formatPercentage.format(
79-
(diagnostics.cpuUsage?.usagePercentage ?? 0) / 100
80-
)}
68+
upperValue={formatPercentageFromWhole(diagnostics.cpuUsage?.usagePercentage ?? 0)}
8169
/>
8270
</Stack>
8371
);

client/src/Pages/Maintenance/MaintenanceWindowTable.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import Typography from "@mui/material/Typography";
2-
import prettyMilliseconds from "pretty-ms";
32
import { Table, ValueLabel } from "@/Components/v2/design-elements";
43
import { Pagination } from "@/Components/v2/design-elements/Table";
54
import { ActionsMenu } from "@/Components/v2/actions-menu";
65
import { DialogInput } from "@/Components/v2/inputs/Dialog";
76

7+
import prettyMilliseconds from "pretty-ms";
88
import { useTheme } from "@mui/material";
99
import type { Header } from "@/Components/v2/design-elements/Table";
1010
import type { ActionMenuItem } from "@/Components/v2/actions-menu";
@@ -15,7 +15,6 @@ import { useSelector, useDispatch } from "react-redux";
1515
import type { RootState } from "@/Types/state";
1616
import Box from "@mui/material/Box";
1717
import { setRowsPerPage } from "@/Features/UI/uiSlice";
18-
import { formatDurationRounded } from "@/Utils/timeUtilsLegacy";
1918
import dayjs from "dayjs";
2019
import { useState } from "react";
2120
import { useDelete, usePatch } from "@/Hooks/UseApi";
@@ -153,7 +152,9 @@ export const MaintenanceWindowTable = ({
153152
id: "repeat",
154153
content: t("pages.maintenanceWindow.table.headers.repeat"),
155154
render: (row) =>
156-
row.repeat === 0 ? t("common.labels.na") : formatDurationRounded(row.repeat),
155+
row.repeat === 0
156+
? t("common.labels.na")
157+
: prettyMilliseconds(row.repeat, { verbose: true }),
157158
},
158159
{
159160
id: "actions",

client/src/Pages/Uptime/Details/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { useGet } from "@/Hooks/UseApi";
2020
import type { MonitorDetailsResponse } from "@/Types/Monitor";
2121
import type { ChecksResponse } from "@/Types/Check";
2222
import type { RootState } from "@/Types/state";
23-
import { formatDateWithTz } from "@/Utils/timeUtilsLegacy";
23+
import { formatDateWithTz } from "@/Utils/TimeUtils";
2424
import { t } from "i18next";
2525

2626
const certificateDateFormat = "MMM D, YYYY h A";

client/src/Utils/FormatUtils.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* Intl.NumberFormat instance for percentage formatting.
3+
* Reused across all formatting calls for performance.
4+
*/
5+
const percentageFormatter = new Intl.NumberFormat("en-US", {
6+
style: "percent",
7+
minimumFractionDigits: 1,
8+
maximumFractionDigits: 1,
9+
});
10+
11+
/**
12+
* Formats a decimal value as a percentage string.
13+
* @param value - Decimal value (e.g., 0.75 for 75%)
14+
* @returns Formatted percentage string (e.g., "75.0%")
15+
* @example
16+
* formatPercentage(0.75) // "75.0%"
17+
* formatPercentage(1) // "100.0%"
18+
* formatPercentage(0.5432) // "54.3%"
19+
*/
20+
export const formatPercentage = (value: number): string => {
21+
if (typeof value !== "number" || Number.isNaN(value)) {
22+
return "0.0%";
23+
}
24+
return percentageFormatter.format(value);
25+
};
26+
27+
/**
28+
* Formats a whole number percentage value as a percentage string.
29+
* @param value - Whole number percentage (e.g., 75 for 75%)
30+
* @returns Formatted percentage string (e.g., "75.0%")
31+
* @example
32+
* formatPercentageFromWhole(75) // "75.0%"
33+
* formatPercentageFromWhole(100) // "100.0%"
34+
* formatPercentageFromWhole(54.32) // "54.3%"
35+
*/
36+
export const formatPercentageFromWhole = (value: number): string => {
37+
return formatPercentage(value / 100);
38+
};
39+
40+
export const getPercentage = (value: number, total: number) => {
41+
if (!value || !total) return 0;
42+
return (value / total) * 100;
43+
};

client/src/Utils/InfraUtils.ts

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CheckDiskInfo, CheckHostInfo } from "@/Types/Check";
1+
import type { CheckHostInfo } from "@/Types/Check";
22

33
export const getFrequency = (frequency: number | undefined): string => {
44
if (!frequency) return "N/A";
@@ -18,32 +18,6 @@ export const getAvgTemp = (temps: number[]): string => {
1818
return `${avgTemp?.toFixed(2)} °C`;
1919
};
2020

21-
export const getGbs = (bytes: number | undefined): string => {
22-
if (!bytes) {
23-
return "N/A";
24-
}
25-
if (bytes === 0) {
26-
return "0 GB";
27-
}
28-
29-
const GB = bytes / (1024 * 1024 * 1024);
30-
const MB = bytes / (1024 * 1024);
31-
32-
if (GB >= 1) {
33-
return GB.toFixed(2) + " GB";
34-
} else {
35-
return MB.toFixed(2) + " MB";
36-
}
37-
};
38-
39-
export const getDiskTotalGbs = (disk?: Partial<CheckDiskInfo>[]): string => {
40-
if (!disk) {
41-
return getGbs(0);
42-
}
43-
const totalBytes = disk?.reduce((acc, disk) => acc + (disk.total_bytes || 0), 0) || 0;
44-
return getGbs(totalBytes);
45-
};
46-
4721
export const getOsAndPlatform = (hostInfo: CheckHostInfo | undefined): string => {
4822
if (!hostInfo) {
4923
return "N/A";

server/src/service/business/diagnosticService.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,15 @@ class DiagnosticService {
5555
};
5656

5757
const used = process.memoryUsage();
58-
const memoryUsage: Record<string, number> = {};
59-
for (const key of Object.keys(used) as Array<keyof NodeJS.MemoryUsage>) {
60-
memoryUsage[`${key}Mb`] = Math.round((used[key] / 1024 / 1024) * 100) / 100; // MB
61-
}
58+
59+
// In MB
60+
const memoryUsage: Record<keyof NodeJS.MemoryUsage, number> = {
61+
rss: Math.round((used.rss / 1024 / 1024) * 100) / 100,
62+
heapTotal: Math.round((used.heapTotal / 1024 / 1024) * 100) / 100,
63+
heapUsed: Math.round((used.heapUsed / 1024 / 1024) * 100) / 100,
64+
external: Math.round((used.external / 1024 / 1024) * 100) / 100,
65+
arrayBuffers: Math.round((used.arrayBuffers / 1024 / 1024) * 100) / 100,
66+
};
6267

6368
// CPU Usage
6469
const cpuMetrics = await this.getCPUUsage();

0 commit comments

Comments
 (0)