fix(system-health): improve responsive styles (#2566)
This commit is contained in:
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user