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
@@ -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 (
<Stack h="100%">
<Group justify="center" wrap="nowrap" pt="md">
<Text fz="md" tt="uppercase" fw={700} c="dimmed" ta="center">
<Stack h="100%" p="xs" gap={isTiny ? "xs" : "md"}>
<Group justify="center" wrap="nowrap">
<Text fz={isTiny ? 8 : "xs"} tt="uppercase" fw={700} c="dimmed" ta="center">
{formatUptime(uptime, t)}
</Text>
</Group>
<SummaryHeader cpu={cpuPercent} memory={memPercent} />
<SummaryHeader cpu={cpuPercent} memory={memPercent} isTiny={isTiny} />
<Accordion variant="contained" chevronPosition="right" multiple defaultValue={["node"]}>
<ResourceAccordionItem
value="node"
@@ -90,8 +92,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.nodes.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})}
isTiny={isTiny}
>
<ResourceTable type="node" data={healthData.nodes} />
<ResourceTable type="node" data={healthData.nodes} isTiny={isTiny} />
</ResourceAccordionItem>
<ResourceAccordionItem
@@ -103,8 +106,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.vms.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})}
isTiny={isTiny}
>
<ResourceTable type="qemu" data={healthData.vms} />
<ResourceTable type="qemu" data={healthData.vms} isTiny={isTiny} />
</ResourceAccordionItem>
<ResourceAccordionItem
@@ -116,8 +120,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.lxcs.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})}
isTiny={isTiny}
>
<ResourceTable type="lxc" data={healthData.lxcs} />
<ResourceTable type="lxc" data={healthData.lxcs} isTiny={isTiny} />
</ResourceAccordionItem>
<ResourceAccordionItem
@@ -129,8 +134,9 @@ export const ClusterHealthMonitoring = ({
totalCount: healthData.storages.length,
sectionIndicatorRequirement: options.sectionIndicatorRequirement,
})}
isTiny={isTiny}
>
<ResourceTable type="storage" data={healthData.storages} />
<ResourceTable type="storage" data={healthData.storages} isTiny={isTiny} />
</ResourceAccordionItem>
</Accordion>
</Stack>
@@ -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 (
<Center>
<Group wrap="nowrap">
<Group wrap="wrap" justify="center" gap="xs">
<Flex direction="row">
<RingProgress
roundCaps
size={60}
thickness={6}
size={isTiny ? 32 : 48}
thickness={isTiny ? 2 : 4}
label={
<Center>
<IconCpu />
<IconCpu size={isTiny ? 12 : 20} />
</Center>
}
sections={[{ value: cpu, color: cpu > 75 ? "orange" : "green" }]}
/>
<Stack align="center" justify="center" gap={0}>
<Text fw={500}>{t("widget.healthMonitoring.cluster.summary.cpu")}</Text>
<Text>{cpu.toFixed(1)}%</Text>
<Text fw={500} size={isTiny ? "xs" : "sm"}>
{t("widget.healthMonitoring.cluster.summary.cpu")}
</Text>
<Text size={isTiny ? "8px" : "xs"}>{cpu.toFixed(1)}%</Text>
</Stack>
</Flex>
<Flex>
<RingProgress
roundCaps
size={60}
thickness={6}
size={isTiny ? 32 : 48}
thickness={isTiny ? 2 : 4}
label={
<Center>
<IconBrain />
<IconBrain size={isTiny ? 12 : 20} />
</Center>
}
sections={[{ value: memory, color: memory > 75 ? "orange" : "green" }]}
/>
<Stack align="center" justify="center" gap={0}>
<Text fw={500}>{t("widget.healthMonitoring.cluster.summary.memory")}</Text>
<Text>{memory.toFixed(1)}%</Text>
<Text size={isTiny ? "xs" : "sm"} fw={500}>
{t("widget.healthMonitoring.cluster.summary.memory")}
</Text>
<Text size={isTiny ? "8px" : "xs"}>{memory.toFixed(1)}%</Text>
</Stack>
</Flex>
</Group>
@@ -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<ResourceAccordionItemProps>) => {
return (
<Accordion.Item value={value}>
<Accordion.Control icon={<Icon />}>
<Group style={{ rowGap: "0" }}>
<Text>{title}</Text>
<Badge variant="dot" color={badge.color} size="lg">
<Accordion.Control icon={isTiny ? null : <Icon size={16} />}>
<Group style={{ rowGap: "0" }} gap="xs">
<Text size="xs">{title}</Text>
<Badge variant="dot" color={badge.color} size="xs">
{badge.activeCount} / {badge.totalCount}
</Badge>
</Group>
@@ -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 (
<Table highlightOnHover>
<thead>
<tr>
<Table.Th ta="start">{t("widget.healthMonitoring.cluster.table.header.name")}</Table.Th>
<TableThead>
<TableTr fz={isTiny ? "8px" : "xs"}>
<Table.Th ta="start" p={0}>
{t("widget.healthMonitoring.cluster.table.header.name")}
</Table.Th>
{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}
{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}
{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}
</tr>
</thead>
<tbody>
</TableTr>
</TableThead>
<TableTbody>
{data.map((item) => {
return (
<ResourcePopover key={item.name} item={item}>
<Popover.Target>
<tr>
<TableTr fz={isTiny ? "8px" : "xs"}>
<td>
<Group wrap="nowrap">
<Indicator size={14} children={null} color={item.isRunning ? "green" : "yellow"} />
<Text lineClamp={1}>{item.name}</Text>
<Group wrap="nowrap" gap={isTiny ? 8 : "xs"}>
<Indicator size={isTiny ? 4 : 8} children={null} color={item.isRunning ? "green" : "yellow"} />
<Text lineClamp={1} fz={isTiny ? "8px" : "xs"}>
{item.name}
</Text>
</Group>
</td>
{item.type === "storage" ? (
@@ -50,12 +61,12 @@ export const ResourceTable = ({ type, data }: ResourceTableProps) => {
</td>
</>
)}
</tr>
</TableTr>
</Popover.Target>
</ResourcePopover>
);
})}
</tbody>
</TableTbody>
</Table>
);
};