fix(system-health): improve responsive styles (#2566)

This commit is contained in:
Meier Lukas
2025-03-11 22:15:01 +01:00
committed by GitHub
parent a53e7aaee5
commit 268daee4a6
8 changed files with 297 additions and 277 deletions

View File

@@ -31,6 +31,7 @@ const running = (total: number, current: Resource) => {
export const ClusterHealthMonitoring = ({ export const ClusterHealthMonitoring = ({
integrationId, integrationId,
options, options,
width,
}: WidgetComponentProps<"healthMonitoring"> & { integrationId: string }) => { }: WidgetComponentProps<"healthMonitoring"> & { integrationId: string }) => {
const t = useI18n(); const t = useI18n();
const [healthData] = clientApi.widget.healthMonitoring.getClusterHealthStatus.useSuspenseQuery( const [healthData] = clientApi.widget.healthMonitoring.getClusterHealthStatus.useSuspenseQuery(
@@ -72,14 +73,15 @@ export const ClusterHealthMonitoring = ({
const cpuPercent = maxCpu ? (usedCpu / maxCpu) * 100 : 0; const cpuPercent = maxCpu ? (usedCpu / maxCpu) * 100 : 0;
const memPercent = maxMem ? (usedMem / maxMem) * 100 : 0; const memPercent = maxMem ? (usedMem / maxMem) * 100 : 0;
const isTiny = width < 256;
return ( return (
<Stack h="100%"> <Stack h="100%" p="xs" gap={isTiny ? "xs" : "md"}>
<Group justify="center" wrap="nowrap" pt="md"> <Group justify="center" wrap="nowrap">
<Text fz="md" tt="uppercase" fw={700} c="dimmed" ta="center"> <Text fz={isTiny ? 8 : "xs"} tt="uppercase" fw={700} c="dimmed" ta="center">
{formatUptime(uptime, t)} {formatUptime(uptime, t)}
</Text> </Text>
</Group> </Group>
<SummaryHeader cpu={cpuPercent} memory={memPercent} /> <SummaryHeader cpu={cpuPercent} memory={memPercent} isTiny={isTiny} />
<Accordion variant="contained" chevronPosition="right" multiple defaultValue={["node"]}> <Accordion variant="contained" chevronPosition="right" multiple defaultValue={["node"]}>
<ResourceAccordionItem <ResourceAccordionItem
value="node" value="node"
@@ -90,8 +92,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.nodes.length, totalCount: healthData.nodes.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement, sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})} })}
isTiny={isTiny}
> >
<ResourceTable type="node" data={healthData.nodes} /> <ResourceTable type="node" data={healthData.nodes} isTiny={isTiny} />
</ResourceAccordionItem> </ResourceAccordionItem>
<ResourceAccordionItem <ResourceAccordionItem
@@ -103,8 +106,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.vms.length, totalCount: healthData.vms.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement, sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})} })}
isTiny={isTiny}
> >
<ResourceTable type="qemu" data={healthData.vms} /> <ResourceTable type="qemu" data={healthData.vms} isTiny={isTiny} />
</ResourceAccordionItem> </ResourceAccordionItem>
<ResourceAccordionItem <ResourceAccordionItem
@@ -116,8 +120,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.lxcs.length, totalCount: healthData.lxcs.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement, sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})} })}
isTiny={isTiny}
> >
<ResourceTable type="lxc" data={healthData.lxcs} /> <ResourceTable type="lxc" data={healthData.lxcs} isTiny={isTiny} />
</ResourceAccordionItem> </ResourceAccordionItem>
<ResourceAccordionItem <ResourceAccordionItem
@@ -129,8 +134,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.storages.length, totalCount: healthData.storages.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement, sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})} })}
isTiny={isTiny}
> >
<ResourceTable type="storage" data={healthData.storages} /> <ResourceTable type="storage" data={healthData.storages} isTiny={isTiny} />
</ResourceAccordionItem> </ResourceAccordionItem>
</Accordion> </Accordion>
</Stack> </Stack>
@@ -140,45 +146,50 @@ export const ClusterHealthMonitoring = ({
interface SummaryHeaderProps { interface SummaryHeaderProps {
cpu: number; cpu: number;
memory: number; memory: number;
isTiny: boolean;
} }
const SummaryHeader = ({ cpu, memory }: SummaryHeaderProps) => { const SummaryHeader = ({ cpu, memory, isTiny }: SummaryHeaderProps) => {
const t = useI18n(); const t = useI18n();
return ( return (
<Center> <Center>
<Group wrap="nowrap"> <Group wrap="wrap" justify="center" gap="xs">
<Flex direction="row"> <Flex direction="row">
<RingProgress <RingProgress
roundCaps roundCaps
size={60} size={isTiny ? 32 : 48}
thickness={6} thickness={isTiny ? 2 : 4}
label={ label={
<Center> <Center>
<IconCpu /> <IconCpu size={isTiny ? 12 : 20} />
</Center> </Center>
} }
sections={[{ value: cpu, color: cpu > 75 ? "orange" : "green" }]} sections={[{ value: cpu, color: cpu > 75 ? "orange" : "green" }]}
/> />
<Stack align="center" justify="center" gap={0}> <Stack align="center" justify="center" gap={0}>
<Text fw={500}>{t("widget.healthMonitoring.cluster.summary.cpu")}</Text> <Text fw={500} size={isTiny ? "xs" : "sm"}>
<Text>{cpu.toFixed(1)}%</Text> {t("widget.healthMonitoring.cluster.summary.cpu")}
</Text>
<Text size={isTiny ? "8px" : "xs"}>{cpu.toFixed(1)}%</Text>
</Stack> </Stack>
</Flex> </Flex>
<Flex> <Flex>
<RingProgress <RingProgress
roundCaps roundCaps
size={60} size={isTiny ? 32 : 48}
thickness={6} thickness={isTiny ? 2 : 4}
label={ label={
<Center> <Center>
<IconBrain /> <IconBrain size={isTiny ? 12 : 20} />
</Center> </Center>
} }
sections={[{ value: memory, color: memory > 75 ? "orange" : "green" }]} sections={[{ value: memory, color: memory > 75 ? "orange" : "green" }]}
/> />
<Stack align="center" justify="center" gap={0}> <Stack align="center" justify="center" gap={0}>
<Text fw={500}>{t("widget.healthMonitoring.cluster.summary.memory")}</Text> <Text size={isTiny ? "xs" : "sm"} fw={500}>
<Text>{memory.toFixed(1)}%</Text> {t("widget.healthMonitoring.cluster.summary.memory")}
</Text>
<Text size={isTiny ? "8px" : "xs"}>{memory.toFixed(1)}%</Text>
</Stack> </Stack>
</Flex> </Flex>
</Group> </Group>

View File

@@ -13,6 +13,7 @@ interface ResourceAccordionItemProps {
activeCount: number; activeCount: number;
totalCount: number; totalCount: number;
}; };
isTiny: boolean;
} }
export const ResourceAccordionItem = ({ export const ResourceAccordionItem = ({
@@ -21,13 +22,14 @@ export const ResourceAccordionItem = ({
icon: Icon, icon: Icon,
badge, badge,
children, children,
isTiny,
}: PropsWithChildren<ResourceAccordionItemProps>) => { }: PropsWithChildren<ResourceAccordionItemProps>) => {
return ( return (
<Accordion.Item value={value}> <Accordion.Item value={value}>
<Accordion.Control icon={<Icon />}> <Accordion.Control icon={isTiny ? null : <Icon size={16} />}>
<Group style={{ rowGap: "0" }}> <Group style={{ rowGap: "0" }} gap="xs">
<Text>{title}</Text> <Text size="xs">{title}</Text>
<Badge variant="dot" color={badge.color} size="lg"> <Badge variant="dot" color={badge.color} size="xs">
{badge.activeCount} / {badge.totalCount} {badge.activeCount} / {badge.totalCount}
</Badge> </Badge>
</Group> </Group>

View File

@@ -1,4 +1,4 @@
import { Group, Indicator, Popover, Table, Text } from "@mantine/core"; import { Group, Indicator, Popover, Table, TableTbody, TableThead, TableTr, Text } from "@mantine/core";
import type { Resource } from "@homarr/integrations/types"; import type { Resource } from "@homarr/integrations/types";
import { useI18n } from "@homarr/translation/client"; import { useI18n } from "@homarr/translation/client";
@@ -8,36 +8,47 @@ import { ResourcePopover } from "./resource-popover";
interface ResourceTableProps { interface ResourceTableProps {
type: Resource["type"]; type: Resource["type"];
data: Resource[]; data: Resource[];
isTiny: boolean;
} }
export const ResourceTable = ({ type, data }: ResourceTableProps) => { export const ResourceTable = ({ type, data, isTiny }: ResourceTableProps) => {
const t = useI18n(); const t = useI18n();
return ( return (
<Table highlightOnHover> <Table highlightOnHover>
<thead> <TableThead>
<tr> <TableTr fz={isTiny ? "8px" : "xs"}>
<Table.Th ta="start">{t("widget.healthMonitoring.cluster.table.header.name")}</Table.Th> <Table.Th ta="start" p={0}>
{t("widget.healthMonitoring.cluster.table.header.name")}
</Table.Th>
{type !== "storage" ? ( {type !== "storage" ? (
<Table.Th ta="start">{t("widget.healthMonitoring.cluster.table.header.cpu")}</Table.Th> <Table.Th ta="start" p={0}>
{t("widget.healthMonitoring.cluster.table.header.cpu")}
</Table.Th>
) : null} ) : null}
{type !== "storage" ? ( {type !== "storage" ? (
<Table.Th ta="start">{t("widget.healthMonitoring.cluster.table.header.memory")}</Table.Th> <Table.Th ta="start" p={0}>
{t("widget.healthMonitoring.cluster.table.header.memory")}
</Table.Th>
) : null} ) : null}
{type === "storage" ? ( {type === "storage" ? (
<Table.Th ta="start">{t("widget.healthMonitoring.cluster.table.header.node")}</Table.Th> <Table.Th ta="start" p={0}>
{t("widget.healthMonitoring.cluster.table.header.node")}
</Table.Th>
) : null} ) : null}
</tr> </TableTr>
</thead> </TableThead>
<tbody> <TableTbody>
{data.map((item) => { {data.map((item) => {
return ( return (
<ResourcePopover key={item.name} item={item}> <ResourcePopover key={item.name} item={item}>
<Popover.Target> <Popover.Target>
<tr> <TableTr fz={isTiny ? "8px" : "xs"}>
<td> <td>
<Group wrap="nowrap"> <Group wrap="nowrap" gap={isTiny ? 8 : "xs"}>
<Indicator size={14} children={null} color={item.isRunning ? "green" : "yellow"} /> <Indicator size={isTiny ? 4 : 8} children={null} color={item.isRunning ? "green" : "yellow"} />
<Text lineClamp={1}>{item.name}</Text> <Text lineClamp={1} fz={isTiny ? "8px" : "xs"}>
{item.name}
</Text>
</Group> </Group>
</td> </td>
{item.type === "storage" ? ( {item.type === "storage" ? (
@@ -50,12 +61,12 @@ export const ResourceTable = ({ type, data }: ResourceTableProps) => {
</td> </td>
</> </>
)} )}
</tr> </TableTr>
</Popover.Target> </Popover.Target>
</ResourcePopover> </ResourcePopover>
); );
})} })}
</tbody> </TableTbody>
</Table> </Table>
); );
}; };

View File

@@ -31,30 +31,20 @@ export default function HealthMonitoringWidget(props: WidgetComponentProps<"heal
} }
return ( return (
<ScrollArea <ScrollArea h="100%">
h="100%"
styles={{
viewport: {
'& div[style="min-width: 100%"]': {
display: "flex !important",
height: "100%",
},
},
}}
>
<Tabs defaultValue={props.options.defaultTab} variant="outline"> <Tabs defaultValue={props.options.defaultTab} variant="outline">
<Tabs.List grow> <Tabs.List grow>
<Tabs.Tab value="system"> <Tabs.Tab value="system" fz="xs">
<b>{t("widget.healthMonitoring.tab.system")}</b> <b>{t("widget.healthMonitoring.tab.system")}</b>
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab value="cluster"> <Tabs.Tab value="cluster" fz="xs">
<b>{t("widget.healthMonitoring.tab.cluster")}</b> <b>{t("widget.healthMonitoring.tab.cluster")}</b>
</Tabs.Tab> </Tabs.Tab>
</Tabs.List> </Tabs.List>
<Tabs.Panel mt="lg" value="system"> <Tabs.Panel value="system">
<SystemHealthMonitoring {...props} integrationIds={otherIntegrationIds} /> <SystemHealthMonitoring {...props} integrationIds={otherIntegrationIds} />
</Tabs.Panel> </Tabs.Panel>
<Tabs.Panel mt="lg" value="cluster"> <Tabs.Panel value="cluster">
<ClusterHealthMonitoring integrationId={proxmoxIntegrationId} {...props} /> <ClusterHealthMonitoring integrationId={proxmoxIntegrationId} {...props} />
</Tabs.Panel> </Tabs.Panel>
</Tabs> </Tabs>

View File

@@ -1,24 +1,22 @@
import { Box, Center, RingProgress, Text } from "@mantine/core"; import { Center, RingProgress, Text } from "@mantine/core";
import { useElementSize } from "@mantine/hooks";
import { IconCpu } from "@tabler/icons-react"; import { IconCpu } from "@tabler/icons-react";
import { progressColor } from "../system-health"; import { progressColor } from "../system-health";
export const CpuRing = ({ cpuUtilization }: { cpuUtilization: number }) => { export const CpuRing = ({ cpuUtilization, isTiny }: { cpuUtilization: number; isTiny: boolean }) => {
const { width, ref } = useElementSize();
const fallbackWidth = width || 1; // See https://github.com/homarr-labs/homarr/issues/2196
return ( return (
<Box ref={ref} w="100%" h="100%" className="health-monitoring-cpu">
<RingProgress <RingProgress
className="health-monitoring-cpu-utilization" className="health-monitoring-cpu"
roundCaps roundCaps
size={fallbackWidth * 0.95} size={isTiny ? 50 : 100}
thickness={fallbackWidth / 10} thickness={isTiny ? 4 : 8}
label={ label={
<Center style={{ flexDirection: "column" }}> <Center style={{ flexDirection: "column" }}>
<Text className="health-monitoring-cpu-utilization-value" size="sm">{`${cpuUtilization.toFixed(2)}%`}</Text> <Text
<IconCpu className="health-monitoring-cpu-utilization-icon" size={30} /> className="health-monitoring-cpu-utilization-value"
size={isTiny ? "8px" : "xs"}
>{`${cpuUtilization.toFixed(2)}%`}</Text>
<IconCpu className="health-monitoring-cpu-utilization-icon" size={isTiny ? 8 : 16} />
</Center> </Center>
} }
sections={[ sections={[
@@ -28,6 +26,5 @@ export const CpuRing = ({ cpuUtilization }: { cpuUtilization: number }) => {
}, },
]} ]}
/> />
</Box>
); );
}; };

View File

@@ -1,30 +1,33 @@
import { Box, Center, RingProgress, Text } from "@mantine/core"; import { Center, RingProgress, Text } from "@mantine/core";
import { useElementSize } from "@mantine/hooks";
import { IconCpu } from "@tabler/icons-react"; import { IconCpu } from "@tabler/icons-react";
import { progressColor } from "../system-health"; import { progressColor } from "../system-health";
export const CpuTempRing = ({ fahrenheit, cpuTemp }: { fahrenheit: boolean; cpuTemp: number | undefined }) => { export const CpuTempRing = ({
const { width, ref } = useElementSize(); fahrenheit,
const fallbackWidth = width || 1; // See https://github.com/homarr-labs/homarr/issues/2196 cpuTemp,
isTiny,
}: {
fahrenheit: boolean;
cpuTemp: number | undefined;
isTiny: boolean;
}) => {
if (!cpuTemp) { if (!cpuTemp) {
return null; return null;
} }
return ( return (
<Box ref={ref} w="100%" h="100%" className="health-monitoring-cpu-temperature">
<RingProgress <RingProgress
className="health-monitoring-cpu-temp" className="health-monitoring-cpu-temperature"
roundCaps roundCaps
size={fallbackWidth * 0.95} size={isTiny ? 50 : 100}
thickness={fallbackWidth / 10} thickness={isTiny ? 4 : 8}
label={ label={
<Center style={{ flexDirection: "column" }}> <Center style={{ flexDirection: "column" }}>
<Text className="health-monitoring-cpu-temp-value" size="sm"> <Text className="health-monitoring-cpu-temp-value" size={isTiny ? "8px" : "xs"}>
{fahrenheit ? `${(cpuTemp * 1.8 + 32).toFixed(1)}°F` : `${cpuTemp.toFixed(1)}°C`} {fahrenheit ? `${(cpuTemp * 1.8 + 32).toFixed(1)}°F` : `${cpuTemp.toFixed(1)}°C`}
</Text> </Text>
<IconCpu className="health-monitoring-cpu-temp-icon" size={30} /> <IconCpu className="health-monitoring-cpu-temp-icon" size={isTiny ? 8 : 16} />
</Center> </Center>
} }
sections={[ sections={[
@@ -34,6 +37,5 @@ export const CpuTempRing = ({ fahrenheit, cpuTemp }: { fahrenheit: boolean; cpuT
}, },
]} ]}
/> />
</Box>
); );
}; };

View File

@@ -1,27 +1,23 @@
import { Box, Center, RingProgress, Text } from "@mantine/core"; import { Center, RingProgress, Text } from "@mantine/core";
import { useElementSize } from "@mantine/hooks";
import { IconBrain } from "@tabler/icons-react"; import { IconBrain } from "@tabler/icons-react";
import { progressColor } from "../system-health"; import { progressColor } from "../system-health";
export const MemoryRing = ({ available, used }: { available: string; used: string }) => { export const MemoryRing = ({ available, used, isTiny }: { available: string; used: string; isTiny: boolean }) => {
const { width, ref } = useElementSize();
const fallbackWidth = width || 1; // See https://github.com/homarr-labs/homarr/issues/2196
const memoryUsage = formatMemoryUsage(available, used); const memoryUsage = formatMemoryUsage(available, used);
return ( return (
<Box ref={ref} w="100%" h="100%" className="health-monitoring-memory">
<RingProgress <RingProgress
className="health-monitoring-memory-use" className="health-monitoring-memory"
roundCaps roundCaps
size={fallbackWidth * 0.95} size={isTiny ? 50 : 100}
thickness={fallbackWidth / 10} thickness={isTiny ? 4 : 8}
label={ label={
<Center style={{ flexDirection: "column" }}> <Center style={{ flexDirection: "column" }}>
<Text className="health-monitoring-memory-value" size="sm"> <Text className="health-monitoring-memory-value" size={isTiny ? "8px" : "xs"}>
{memoryUsage.memUsed.GB}GiB {memoryUsage.memUsed.GB}GiB
</Text> </Text>
<IconBrain className="health-monitoring-memory-icon" size={30} /> <IconBrain className="health-monitoring-memory-icon" size={isTiny ? 8 : 16} />
</Center> </Center>
} }
sections={[ sections={[
@@ -32,7 +28,6 @@ export const MemoryRing = ({ available, used }: { available: string; used: strin
}, },
]} ]}
/> />
</Box>
); );
}; };

View File

@@ -44,7 +44,11 @@ import classes from "./system-health.module.css";
dayjs.extend(duration); dayjs.extend(duration);
export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetComponentProps<"healthMonitoring">) => { export const SystemHealthMonitoring = ({
options,
integrationIds,
width,
}: WidgetComponentProps<"healthMonitoring">) => {
const t = useI18n(); const t = useI18n();
const [healthData] = clientApi.widget.healthMonitoring.getSystemHealthStatus.useSuspenseQuery( const [healthData] = clientApi.widget.healthMonitoring.getSystemHealthStatus.useSuspenseQuery(
{ {
@@ -79,6 +83,8 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon
}, },
); );
const isTiny = width < 256;
return ( return (
<Stack h="100%" gap="sm" className="health-monitoring"> <Stack h="100%" gap="sm" className="health-monitoring">
{healthData.map(({ integrationId, integrationName, healthInfo, updatedAt }) => { {healthData.map(({ integrationId, integrationName, healthInfo, updatedAt }) => {
@@ -91,22 +97,17 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon
h="100%" h="100%"
className={`health-monitoring-information health-monitoring-${integrationName}`} className={`health-monitoring-information health-monitoring-${integrationName}`}
p="sm" p="sm"
pos="relative"
> >
<Box className="health-monitoring-information-card" p="sm"> <Box className="health-monitoring-information-card-section" pos="absolute" top={8} right={8}>
<Flex
className="health-monitoring-information-card-elements"
justify="space-between"
align="center"
key={integrationId}
>
<Box className="health-monitoring-information-card-section">
<Indicator <Indicator
className="health-monitoring-updates-reboot-indicator" className="health-monitoring-updates-reboot-indicator"
inline inline
processing processing
styles={{ indicator: { pointerEvents: "none" } }}
color={healthInfo.rebootRequired ? "red" : healthInfo.availablePkgUpdates > 0 ? "blue" : "gray"} color={healthInfo.rebootRequired ? "red" : healthInfo.availablePkgUpdates > 0 ? "blue" : "gray"}
position="top-end" position="top-end"
size="md" size={16}
label={healthInfo.availablePkgUpdates > 0 ? healthInfo.availablePkgUpdates : undefined} label={healthInfo.availablePkgUpdates > 0 ? healthInfo.availablePkgUpdates : undefined}
disabled={!healthInfo.rebootRequired && healthInfo.availablePkgUpdates === 0} disabled={!healthInfo.rebootRequired && healthInfo.availablePkgUpdates === 0}
> >
@@ -114,7 +115,7 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon
className="health-monitoring-information-icon-avatar" className="health-monitoring-information-icon-avatar"
variant={"light"} variant={"light"}
color="var(--mantine-color-text)" color="var(--mantine-color-text)"
size={40} size="sm"
radius={board.itemRadius} radius={board.itemRadius}
> >
<IconInfoCircle className="health-monitoring-information-icon" size={30} onClick={open} /> <IconInfoCircle className="health-monitoring-information-icon" size={30} onClick={open} />
@@ -158,28 +159,30 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon
{t("widget.healthMonitoring.popover.minute")} {healthInfo.loadAverage["1min"]}% {t("widget.healthMonitoring.popover.minute")} {healthInfo.loadAverage["1min"]}%
</List.Item> </List.Item>
<List.Item className="health-monitoring-information-load-average-5min"> <List.Item className="health-monitoring-information-load-average-5min">
{t("widget.healthMonitoring.popover.minutes", { count: 5 })}{" "} {t("widget.healthMonitoring.popover.minutes", { count: 5 })} {healthInfo.loadAverage["5min"]}%
{healthInfo.loadAverage["5min"]}%
</List.Item> </List.Item>
<List.Item className="health-monitoring-information-load-average-15min"> <List.Item className="health-monitoring-information-load-average-15min">
{t("widget.healthMonitoring.popover.minutes", { count: 15 })}{" "} {t("widget.healthMonitoring.popover.minutes", { count: 15 })} {healthInfo.loadAverage["15min"]}%
{healthInfo.loadAverage["15min"]}%
</List.Item> </List.Item>
</List> </List>
</List> </List>
</Stack> </Stack>
</Modal> </Modal>
</Box> </Box>
{options.cpu && <CpuRing cpuUtilization={healthInfo.cpuUtilization} />} <Flex className="health-monitoring-information-card-elements" justify="center" align="center" wrap="wrap">
{options.cpu && <CpuTempRing fahrenheit={options.fahrenheit} cpuTemp={healthInfo.cpuTemp} />} {options.cpu && <CpuRing cpuUtilization={healthInfo.cpuUtilization} isTiny={isTiny} />}
{options.memory && <MemoryRing available={healthInfo.memAvailable} used={healthInfo.memUsed} />} {options.cpu && (
<CpuTempRing fahrenheit={options.fahrenheit} cpuTemp={healthInfo.cpuTemp} isTiny={isTiny} />
)}
{options.memory && (
<MemoryRing available={healthInfo.memAvailable} used={healthInfo.memUsed} isTiny={isTiny} />
)}
</Flex> </Flex>
{ {
<Text className="health-monitoring-status-update-time" c="dimmed" size="sm" ta="center"> <Text className="health-monitoring-status-update-time" c="dimmed" size="xs" ta="center">
{t("widget.healthMonitoring.popover.lastSeen", { lastSeen: dayjs(updatedAt).fromNow() })} {t("widget.healthMonitoring.popover.lastSeen", { lastSeen: dayjs(updatedAt).fromNow() })}
</Text> </Text>
} }
</Box>
{options.fileSystem && {options.fileSystem &&
disksData.map((disk) => { disksData.map((disk) => {
return ( return (
@@ -188,32 +191,40 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon
`health-monitoring-disk-card health-monitoring-disk-card-${integrationName}`, `health-monitoring-disk-card health-monitoring-disk-card-${integrationName}`,
classes.card, classes.card,
)} )}
style={{ overflow: "visible" }}
key={disk.deviceName} key={disk.deviceName}
radius={board.itemRadius} radius={board.itemRadius}
p="sm" p="xs"
> >
<Flex className="health-monitoring-disk-status" justify="space-between" align="center" mb="sm"> <Stack gap="sm">
<Group gap="xs"> <Group
className="health-monitoring-disk-status"
justify="space-between"
align="center"
wrap="wrap"
gap={8}
>
<Group gap={4} wrap="nowrap">
<IconServer className="health-monitoring-disk-icon" size="1rem" /> <IconServer className="health-monitoring-disk-icon" size="1rem" />
<Text className="dihealth-monitoring-disk-name" size={"md"}> <Text className="dihealth-monitoring-disk-name" size="xs">
{disk.deviceName} {disk.deviceName}
</Text> </Text>
</Group> </Group>
<Group gap="xs"> <Group gap={4} wrap="nowrap">
<IconTemperature className="health-monitoring-disk-temperature-icon" size="1rem" /> <IconTemperature className="health-monitoring-disk-temperature-icon" size="1rem" />
<Text className="health-monitoring-disk-temperature-value" size="md"> <Text className="health-monitoring-disk-temperature-value" size="xs">
{options.fahrenheit {options.fahrenheit
? `${(disk.temperature * 1.8 + 32).toFixed(1)}°F` ? `${(disk.temperature * 1.8 + 32).toFixed(1)}°F`
: `${disk.temperature}°C`} : `${disk.temperature}°C`}
</Text> </Text>
</Group> </Group>
<Group gap="xs"> <Group gap={4} wrap="nowrap">
<IconFileReport className="health-monitoring-disk-status-icon" size="1rem" /> <IconFileReport className="health-monitoring-disk-status-icon" size="1rem" />
<Text className="health-monitoring-disk-status-value" size="md"> <Text className="health-monitoring-disk-status-value" size="xs">
{disk.overallStatus ? disk.overallStatus : "N/A"} {disk.overallStatus ? disk.overallStatus : "N/A"}
</Text> </Text>
</Group> </Group>
</Flex> </Group>
<Progress.Root className="health-monitoring-disk-use" radius={board.itemRadius} h="md"> <Progress.Root className="health-monitoring-disk-use" radius={board.itemRadius} h="md">
<Tooltip label={disk.used}> <Tooltip label={disk.used}>
<Progress.Section <Progress.Section
@@ -245,6 +256,7 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon
</Progress.Section> </Progress.Section>
</Tooltip> </Tooltip>
</Progress.Root> </Progress.Root>
</Stack>
</Card> </Card>
); );
})} })}