diff --git a/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx b/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx index c2abec2b8..db9718990 100644 --- a/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx +++ b/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx @@ -31,6 +31,7 @@ const running = (total: number, current: Resource) => { export const ClusterHealthMonitoring = ({ integrationId, options, + width, }: WidgetComponentProps<"healthMonitoring"> & { integrationId: string }) => { const t = useI18n(); const [healthData] = clientApi.widget.healthMonitoring.getClusterHealthStatus.useSuspenseQuery( @@ -72,14 +73,15 @@ export const ClusterHealthMonitoring = ({ const cpuPercent = maxCpu ? (usedCpu / maxCpu) * 100 : 0; const memPercent = maxMem ? (usedMem / maxMem) * 100 : 0; + const isTiny = width < 256; return ( - - - + + + {formatUptime(uptime, t)} - + - + - + - + - + @@ -140,45 +146,50 @@ export const ClusterHealthMonitoring = ({ interface SummaryHeaderProps { cpu: number; memory: number; + isTiny: boolean; } -const SummaryHeader = ({ cpu, memory }: SummaryHeaderProps) => { +const SummaryHeader = ({ cpu, memory, isTiny }: SummaryHeaderProps) => { const t = useI18n(); return (
- + - +
} sections={[{ value: cpu, color: cpu > 75 ? "orange" : "green" }]} /> - {t("widget.healthMonitoring.cluster.summary.cpu")} - {cpu.toFixed(1)}% + + {t("widget.healthMonitoring.cluster.summary.cpu")} + + {cpu.toFixed(1)}% - + } sections={[{ value: memory, color: memory > 75 ? "orange" : "green" }]} /> - {t("widget.healthMonitoring.cluster.summary.memory")} - {memory.toFixed(1)}% + + {t("widget.healthMonitoring.cluster.summary.memory")} + + {memory.toFixed(1)}%
diff --git a/packages/widgets/src/health-monitoring/cluster/resource-accordion-item.tsx b/packages/widgets/src/health-monitoring/cluster/resource-accordion-item.tsx index 717822e60..5c6aa9dc4 100644 --- a/packages/widgets/src/health-monitoring/cluster/resource-accordion-item.tsx +++ b/packages/widgets/src/health-monitoring/cluster/resource-accordion-item.tsx @@ -13,6 +13,7 @@ interface ResourceAccordionItemProps { activeCount: number; totalCount: number; }; + isTiny: boolean; } export const ResourceAccordionItem = ({ @@ -21,13 +22,14 @@ export const ResourceAccordionItem = ({ icon: Icon, badge, children, + isTiny, }: PropsWithChildren) => { return ( - }> - - {title} - + }> + + {title} + {badge.activeCount} / {badge.totalCount} diff --git a/packages/widgets/src/health-monitoring/cluster/resource-table.tsx b/packages/widgets/src/health-monitoring/cluster/resource-table.tsx index bc8226f37..8f3637f19 100644 --- a/packages/widgets/src/health-monitoring/cluster/resource-table.tsx +++ b/packages/widgets/src/health-monitoring/cluster/resource-table.tsx @@ -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 { useI18n } from "@homarr/translation/client"; @@ -8,36 +8,47 @@ import { ResourcePopover } from "./resource-popover"; interface ResourceTableProps { type: Resource["type"]; data: Resource[]; + isTiny: boolean; } -export const ResourceTable = ({ type, data }: ResourceTableProps) => { +export const ResourceTable = ({ type, data, isTiny }: ResourceTableProps) => { const t = useI18n(); return ( - - - {t("widget.healthMonitoring.cluster.table.header.name")} + + + + {t("widget.healthMonitoring.cluster.table.header.name")} + {type !== "storage" ? ( - {t("widget.healthMonitoring.cluster.table.header.cpu")} + + {t("widget.healthMonitoring.cluster.table.header.cpu")} + ) : null} {type !== "storage" ? ( - {t("widget.healthMonitoring.cluster.table.header.memory")} + + {t("widget.healthMonitoring.cluster.table.header.memory")} + ) : null} {type === "storage" ? ( - {t("widget.healthMonitoring.cluster.table.header.node")} + + {t("widget.healthMonitoring.cluster.table.header.node")} + ) : null} - - - + + + {data.map((item) => { return ( - + {item.type === "storage" ? ( @@ -50,12 +61,12 @@ export const ResourceTable = ({ type, data }: ResourceTableProps) => { )} - + ); })} - +
- - - {item.name} + + + + {item.name} +
); }; diff --git a/packages/widgets/src/health-monitoring/component.tsx b/packages/widgets/src/health-monitoring/component.tsx index 837e04504..d1ee3dd28 100644 --- a/packages/widgets/src/health-monitoring/component.tsx +++ b/packages/widgets/src/health-monitoring/component.tsx @@ -31,30 +31,20 @@ export default function HealthMonitoringWidget(props: WidgetComponentProps<"heal } return ( - + - + {t("widget.healthMonitoring.tab.system")} - + {t("widget.healthMonitoring.tab.cluster")} - + - + diff --git a/packages/widgets/src/health-monitoring/rings/cpu-ring.tsx b/packages/widgets/src/health-monitoring/rings/cpu-ring.tsx index 0069c6d96..ff1746eea 100644 --- a/packages/widgets/src/health-monitoring/rings/cpu-ring.tsx +++ b/packages/widgets/src/health-monitoring/rings/cpu-ring.tsx @@ -1,33 +1,30 @@ -import { Box, Center, RingProgress, Text } from "@mantine/core"; -import { useElementSize } from "@mantine/hooks"; +import { Center, RingProgress, Text } from "@mantine/core"; import { IconCpu } from "@tabler/icons-react"; import { progressColor } from "../system-health"; -export const CpuRing = ({ cpuUtilization }: { cpuUtilization: number }) => { - const { width, ref } = useElementSize(); - const fallbackWidth = width || 1; // See https://github.com/homarr-labs/homarr/issues/2196 - +export const CpuRing = ({ cpuUtilization, isTiny }: { cpuUtilization: number; isTiny: boolean }) => { return ( - - - {`${cpuUtilization.toFixed(2)}%`} - - - } - sections={[ - { - value: Number(cpuUtilization.toFixed(2)), - color: progressColor(Number(cpuUtilization.toFixed(2))), - }, - ]} - /> - + + {`${cpuUtilization.toFixed(2)}%`} + + + } + sections={[ + { + value: Number(cpuUtilization.toFixed(2)), + color: progressColor(Number(cpuUtilization.toFixed(2))), + }, + ]} + /> ); }; diff --git a/packages/widgets/src/health-monitoring/rings/cpu-temp-ring.tsx b/packages/widgets/src/health-monitoring/rings/cpu-temp-ring.tsx index a70f26a90..2c6a2b254 100644 --- a/packages/widgets/src/health-monitoring/rings/cpu-temp-ring.tsx +++ b/packages/widgets/src/health-monitoring/rings/cpu-temp-ring.tsx @@ -1,39 +1,41 @@ -import { Box, Center, RingProgress, Text } from "@mantine/core"; -import { useElementSize } from "@mantine/hooks"; +import { Center, RingProgress, Text } from "@mantine/core"; import { IconCpu } from "@tabler/icons-react"; import { progressColor } from "../system-health"; -export const CpuTempRing = ({ fahrenheit, cpuTemp }: { fahrenheit: boolean; cpuTemp: number | undefined }) => { - const { width, ref } = useElementSize(); - const fallbackWidth = width || 1; // See https://github.com/homarr-labs/homarr/issues/2196 - +export const CpuTempRing = ({ + fahrenheit, + cpuTemp, + isTiny, +}: { + fahrenheit: boolean; + cpuTemp: number | undefined; + isTiny: boolean; +}) => { if (!cpuTemp) { return null; } return ( - - - - {fahrenheit ? `${(cpuTemp * 1.8 + 32).toFixed(1)}°F` : `${cpuTemp.toFixed(1)}°C`} - - - - } - sections={[ - { - value: cpuTemp, - color: progressColor(cpuTemp), - }, - ]} - /> - + + + {fahrenheit ? `${(cpuTemp * 1.8 + 32).toFixed(1)}°F` : `${cpuTemp.toFixed(1)}°C`} + + + + } + sections={[ + { + value: cpuTemp, + color: progressColor(cpuTemp), + }, + ]} + /> ); }; diff --git a/packages/widgets/src/health-monitoring/rings/memory-ring.tsx b/packages/widgets/src/health-monitoring/rings/memory-ring.tsx index 2d8ce4d68..e19ad0fe6 100644 --- a/packages/widgets/src/health-monitoring/rings/memory-ring.tsx +++ b/packages/widgets/src/health-monitoring/rings/memory-ring.tsx @@ -1,38 +1,33 @@ -import { Box, Center, RingProgress, Text } from "@mantine/core"; -import { useElementSize } from "@mantine/hooks"; +import { Center, RingProgress, Text } from "@mantine/core"; import { IconBrain } from "@tabler/icons-react"; import { progressColor } from "../system-health"; -export const MemoryRing = ({ available, used }: { available: string; used: string }) => { - const { width, ref } = useElementSize(); - const fallbackWidth = width || 1; // See https://github.com/homarr-labs/homarr/issues/2196 +export const MemoryRing = ({ available, used, isTiny }: { available: string; used: string; isTiny: boolean }) => { const memoryUsage = formatMemoryUsage(available, used); return ( - - - - {memoryUsage.memUsed.GB}GiB - - - - } - sections={[ - { - value: Number(memoryUsage.memUsed.percent), - color: progressColor(Number(memoryUsage.memUsed.percent)), - tooltip: `${memoryUsage.memUsed.percent}%`, - }, - ]} - /> - + + + {memoryUsage.memUsed.GB}GiB + + + + } + sections={[ + { + value: Number(memoryUsage.memUsed.percent), + color: progressColor(Number(memoryUsage.memUsed.percent)), + tooltip: `${memoryUsage.memUsed.percent}%`, + }, + ]} + /> ); }; diff --git a/packages/widgets/src/health-monitoring/system-health.tsx b/packages/widgets/src/health-monitoring/system-health.tsx index 82109f87a..e2f064ebd 100644 --- a/packages/widgets/src/health-monitoring/system-health.tsx +++ b/packages/widgets/src/health-monitoring/system-health.tsx @@ -44,7 +44,11 @@ import classes from "./system-health.module.css"; dayjs.extend(duration); -export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetComponentProps<"healthMonitoring">) => { +export const SystemHealthMonitoring = ({ + options, + integrationIds, + width, +}: WidgetComponentProps<"healthMonitoring">) => { const t = useI18n(); const [healthData] = clientApi.widget.healthMonitoring.getSystemHealthStatus.useSuspenseQuery( { @@ -79,6 +83,8 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon }, ); + const isTiny = width < 256; + return ( {healthData.map(({ integrationId, integrationName, healthInfo, updatedAt }) => { @@ -91,95 +97,92 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon h="100%" className={`health-monitoring-information health-monitoring-${integrationName}`} p="sm" + pos="relative" > - - + 0 ? "blue" : "gray"} + position="top-end" + size={16} + label={healthInfo.availablePkgUpdates > 0 ? healthInfo.availablePkgUpdates : undefined} + disabled={!healthInfo.rebootRequired && healthInfo.availablePkgUpdates === 0} > - - 0 ? "blue" : "gray"} - position="top-end" - size="md" - label={healthInfo.availablePkgUpdates > 0 ? healthInfo.availablePkgUpdates : undefined} - disabled={!healthInfo.rebootRequired && healthInfo.availablePkgUpdates === 0} - > - - - - - - - - - }> - {t("widget.healthMonitoring.popover.processor", { cpuModelName: healthInfo.cpuModelName })} - - }> - {t("widget.healthMonitoring.popover.memory", { memory: memoryUsage.memTotal.GB })} - - }> - {t("widget.healthMonitoring.popover.memoryAvailable", { - memoryAvailable: memoryUsage.memFree.GB, - percent: memoryUsage.memFree.percent, - })} - - }> - {t("widget.healthMonitoring.popover.version", { - version: healthInfo.version, - })} - - }> - {formatUptime(healthInfo.uptime, t)} - - }> - {t("widget.healthMonitoring.popover.loadAverage")} - - }> - - {t("widget.healthMonitoring.popover.minute")} {healthInfo.loadAverage["1min"]}% - - - {t("widget.healthMonitoring.popover.minutes", { count: 5 })}{" "} - {healthInfo.loadAverage["5min"]}% - - - {t("widget.healthMonitoring.popover.minutes", { count: 15 })}{" "} - {healthInfo.loadAverage["15min"]}% - - - - - - - {options.cpu && } - {options.cpu && } - {options.memory && } - - { - - {t("widget.healthMonitoring.popover.lastSeen", { lastSeen: dayjs(updatedAt).fromNow() })} - - } + + + + + + + + + }> + {t("widget.healthMonitoring.popover.processor", { cpuModelName: healthInfo.cpuModelName })} + + }> + {t("widget.healthMonitoring.popover.memory", { memory: memoryUsage.memTotal.GB })} + + }> + {t("widget.healthMonitoring.popover.memoryAvailable", { + memoryAvailable: memoryUsage.memFree.GB, + percent: memoryUsage.memFree.percent, + })} + + }> + {t("widget.healthMonitoring.popover.version", { + version: healthInfo.version, + })} + + }> + {formatUptime(healthInfo.uptime, t)} + + }> + {t("widget.healthMonitoring.popover.loadAverage")} + + }> + + {t("widget.healthMonitoring.popover.minute")} {healthInfo.loadAverage["1min"]}% + + + {t("widget.healthMonitoring.popover.minutes", { count: 5 })} {healthInfo.loadAverage["5min"]}% + + + {t("widget.healthMonitoring.popover.minutes", { count: 15 })} {healthInfo.loadAverage["15min"]}% + + + + + + + {options.cpu && } + {options.cpu && ( + + )} + {options.memory && ( + + )} + + { + + {t("widget.healthMonitoring.popover.lastSeen", { lastSeen: dayjs(updatedAt).fromNow() })} + + } {options.fileSystem && disksData.map((disk) => { return ( @@ -188,63 +191,72 @@ export const SystemHealthMonitoring = ({ options, integrationIds }: WidgetCompon `health-monitoring-disk-card health-monitoring-disk-card-${integrationName}`, classes.card, )} + style={{ overflow: "visible" }} key={disk.deviceName} radius={board.itemRadius} - p="sm" + p="xs" > - - - - - {disk.deviceName} - - - - - - {options.fahrenheit - ? `${(disk.temperature * 1.8 + 32).toFixed(1)}°F` - : `${disk.temperature}°C`} - - - - - - {disk.overallStatus ? disk.overallStatus : "N/A"} - - - - - - - - {t("widget.healthMonitoring.popover.used")} - - - - - = 1 - ? `${(Number(disk.available) / 1024 ** 4).toFixed(2)} TiB` - : `${(Number(disk.available) / 1024 ** 3).toFixed(2)} GiB` - } + + - + + + {disk.deviceName} + + + + + + {options.fahrenheit + ? `${(disk.temperature * 1.8 + 32).toFixed(1)}°F` + : `${disk.temperature}°C`} + + + + + + {disk.overallStatus ? disk.overallStatus : "N/A"} + + +
+ + + + + {t("widget.healthMonitoring.popover.used")} + + + + + = 1 + ? `${(Number(disk.available) / 1024 ** 4).toFixed(2)} TiB` + : `${(Number(disk.available) / 1024 ** 3).toFixed(2)} GiB` + } > - - {t("widget.healthMonitoring.popover.available")} - - - - + + + {t("widget.healthMonitoring.popover.available")} + + + + +
); })}