Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import MinerMeasurement from "./MinerMeasurement";
import UnsupportedMetric from "./UnsupportedMetric";
import type { MinerStateSnapshot } from "@/protoFleet/api/generated/fleetmanagement/v1/fleetmanagement_pb";
import { getMinerMeasurement } from "@/protoFleet/features/fleetManagement/utils/getMinerMeasurement";
import { getLatestMeasurementWithData } from "@/shared/utils/measurementUtils";
import { formatPowerKW } from "@/shared/utils/stringUtils";
import { INACTIVE_PLACEHOLDER } from "@/shared/constants";
import SkeletonBar from "@/shared/components/SkeletonBar";

type MinerPowerUsageProps = {
miner: MinerStateSnapshot;
Expand All @@ -16,7 +20,15 @@ const MinerPowerUsage = ({ miner }: MinerPowerUsageProps) => {
return <UnsupportedMetric message="This miner's firmware doesn't share this data." />;
}

return <MinerMeasurement measurement={powerUsage} unit="kW" />;
if (powerUsage === undefined) return <SkeletonBar className="w-full pr-10" />;
if (powerUsage === null) return <>{INACTIVE_PLACEHOLDER}</>;
if (powerUsage.length === 0) return null;

const latestValue = getLatestMeasurementWithData(powerUsage)?.value;
if (latestValue === undefined) return <>{INACTIVE_PLACEHOLDER}</>;

const { value, unit } = formatPowerKW(latestValue);
return <>{value} {unit}</>;
Comment on lines +23 to +31

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MinerPowerUsage now re-implements the same loading/offline/empty/latest-value logic that already exists in MinerMeasurement. To avoid future drift between measurement cells, consider refactoring MinerMeasurement to accept a formatter (or render prop) so MinerPowerUsage can keep the shared state handling while still using adaptive units.

Copilot uses AI. Check for mistakes.
};

export default MinerPowerUsage;
19 changes: 19 additions & 0 deletions client/src/shared/utils/stringUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
addCommas,
convertToSentenceCase,
convertToTitleCase,
formatPowerKW,
getDisplayValue,
getMacAddressDisplay,
padLeft,
Expand Down Expand Up @@ -161,6 +162,24 @@ describe("convertToSentenceCase", () => {
});
});

describe("formatPowerKW", () => {
it("shows W for values below 1 kW", () => {
expect(formatPowerKW(0.0125)).toEqual({ value: "12.5", unit: "W" });
expect(formatPowerKW(0)).toEqual({ value: "0.0", unit: "W" });
expect(formatPowerKW(0.999)).toEqual({ value: "999.0", unit: "W" });
});

it("shows kW for values at or above 1 kW", () => {
expect(formatPowerKW(1)).toEqual({ value: "1.0", unit: "kW" });
expect(formatPowerKW(3.2)).toEqual({ value: "3.2", unit: "kW" });
expect(formatPowerKW(12.5)).toEqual({ value: "12.5", unit: "kW" });
});

it("applies comma separators for large kW values", () => {
expect(formatPowerKW(1200)).toEqual({ value: "1,200.0", unit: "kW" });
});
});

describe("convertToTitleCase", () => {
it("should capitalize the first letter of each word", () => {
const input = "hello world";
Expand Down
12 changes: 12 additions & 0 deletions client/src/shared/utils/stringUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@ export const padLeft = (value: number, length: number) => {
return value.toString().padStart(length, "0");
};

/**
* Formats a power value in kW with adaptive units.
* Values >= 1 kW are shown in kW (1 decimal place).
* Values < 1 kW are converted to W (rounded to nearest integer).
*/
export const formatPowerKW = (kw: number): { value: string; unit: string } => {
if (kw >= 1) {
return { value: separateByCommas(kw.toFixed(1)), unit: "kW" };
}
return { value: separateByCommas((kw * 1000).toFixed(1)), unit: "W" };
Comment on lines +35 to +44

Copilot AI Apr 28, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says values < 1 kW are converted to W "(rounded to nearest integer)", but the implementation uses toFixed(1) (one decimal place). Update the comment to match the actual formatting behavior (or adjust the implementation to match the comment).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

};

export const stripLeadingSlash = (str: string) => {
return str.startsWith("/") ? str.substring(1) : str;
};
Expand Down
Loading