Files
homarr/packages/widgets/src/system-disks/component.tsx
T

107 lines
3.7 KiB
TypeScript

"use client";
import { Box, Card, Group, Stack, useMantineColorScheme } from "@mantine/core";
import { clientApi } from "@homarr/api/client";
import { useRequiredBoard } from "@homarr/boards/context";
import { humanFileSize } from "@homarr/common";
import { useI18n } from "@homarr/translation/client";
import type { WidgetComponentProps } from "../definition";
import { NoIntegrationDataError } from "../errors/no-data-integration";
type DisplayMode = WidgetComponentProps<"systemDisks">["options"]["displayMode"];
const getDisplayText = (item: { used: string; available: string; percentage: number }, displayMode: DisplayMode) => {
switch (displayMode) {
case "percentage":
return `${Math.round(item.percentage)}%`;
case "absolute": {
const availableInBytes = Number(item.available);
const availableText = Number.isFinite(availableInBytes)
? humanFileSize(Math.round(availableInBytes))
: item.available;
return `${item.used} / ${availableText}`;
}
case "free":
return `${Math.round(100 - item.percentage)}% free`;
default:
return `${Math.round(item.percentage)}%`;
}
};
export default function SystemResources({ integrationIds, options }: WidgetComponentProps<"systemDisks">) {
const queryInput = { integrationIds };
const [data] = clientApi.widget.healthMonitoring.getSystemHealthStatus.useSuspenseQuery(queryInput);
const utils = clientApi.useUtils();
const board = useRequiredBoard();
const scheme = useMantineColorScheme();
const t = useI18n();
const lastItem = data.at(-1);
if (!lastItem) return null;
const { fileSystem, smart } = lastItem.healthInfo;
clientApi.widget.healthMonitoring.subscribeSystemHealthStatus.useSubscription(queryInput, {
onData(data) {
utils.widget.healthMonitoring.getSystemHealthStatus.setData(queryInput, (oldData) =>
oldData?.map((item) => (item.integrationId === data.integrationId ? { ...item, ...data } : item)),
);
},
});
if (fileSystem.length === 0) {
throw new NoIntegrationDataError();
}
return (
<Stack gap="xs" p="xs" h="100%">
{fileSystem.map((item) => {
const smartItem = smart.find((smart) => smart.deviceName === item.deviceName);
const healthy = smartItem?.healthy ?? true; // fall back to healthy if no information is available
return (
<Card
radius={board.itemRadius}
py={"xs"}
bg={scheme.colorScheme === "dark" ? "dark.7" : "gray.1"}
key={`disk-${item.deviceName}`}
style={{ overflow: "hidden", position: "relative" }}
>
<Group justify="space-between" style={{ zIndex: 1 }}>
<div>
<p style={{ margin: 0 }}>
<b>{item.deviceName}</b>
</p>
<p style={{ margin: 0 }}>
<span>{getDisplayText(item, options.displayMode)}</span>
{!healthy && <span style={{ marginLeft: 5 }}>{t("widget.systemDisks.status.unhealthy")}</span>}
</p>
</div>
<div>
{smartItem?.temperature && options.showTemperatureIfAvailable ? (
<p style={{ margin: 0 }}>{smartItem.temperature}°C</p>
) : null}
</div>
</Group>
<Box
bg={healthy ? "green" : "red"}
style={{
position: "absolute",
top: 0,
left: 0,
width: `${item.percentage}%`,
height: "100%",
zIndex: 0,
display: options.showBackgroundBar ? "block" : "none",
}}
></Box>
</Card>
);
})}
</Stack>
);
}