From 939df8f6d11bb36f71854425e4fdc4ca0724da31 Mon Sep 17 00:00:00 2001 From: Meier Lukas Date: Sat, 24 May 2025 17:49:49 +0200 Subject: [PATCH] feat(cluster-health): add visibility options (#3210) --- packages/old-import/src/widgets/options.ts | 15 ++ packages/translation/src/lang/en.json | 6 + .../cluster/cluster-health.tsx | 230 ++++++++++-------- .../widgets/src/health-monitoring/index.ts | 112 +++++++-- 4 files changed, 236 insertions(+), 127 deletions(-) diff --git a/packages/old-import/src/widgets/options.ts b/packages/old-import/src/widgets/options.ts index 793a4caa7..24ad0e61f 100644 --- a/packages/old-import/src/widgets/options.ts +++ b/packages/old-import/src/widgets/options.ts @@ -157,6 +157,21 @@ const optionMapping: OptionMapping = { defaultTab: (oldOptions) => ("defaultTabState" in oldOptions ? oldOptions.defaultTabState : undefined), sectionIndicatorRequirement: (oldOptions) => "sectionIndicatorColor" in oldOptions ? oldOptions.sectionIndicatorColor : undefined, + showUptime: () => undefined, + visibleClusterSections: (oldOptions) => { + if (!("showNode" in oldOptions)) return undefined; + + const oldKeys = { + showNode: "node" as const, + showLXCs: "lxc" as const, + showVM: "qemu" as const, + showStorage: "storage" as const, + } satisfies Partial>; + + return objectEntries(oldKeys) + .filter(([key]) => oldOptions[key]) + .map(([_, section]) => section); + }, }, mediaTranscoding: { defaultView: (oldOptions) => oldOptions.defaultView, diff --git a/packages/translation/src/lang/en.json b/packages/translation/src/lang/en.json index 1df588680..50239cbac 100644 --- a/packages/translation/src/lang/en.json +++ b/packages/translation/src/lang/en.json @@ -1757,12 +1757,18 @@ "memory": { "label": "Show Memory Info" }, + "showUptime": { + "label": "Show Uptime" + }, "fileSystem": { "label": "Show Filesystem Info" }, "defaultTab": { "label": "Default tab" }, + "visibleClusterSections": { + "label": "Visible cluster sections" + }, "sectionIndicatorRequirement": { "label": "Section indicator requirement" } diff --git a/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx b/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx index db9718990..3ffa99bdc 100644 --- a/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx +++ b/packages/widgets/src/health-monitoring/cluster/cluster-health.tsx @@ -72,126 +72,156 @@ export const ClusterHealthMonitoring = ({ const cpuPercent = maxCpu ? (usedCpu / maxCpu) * 100 : 0; const memPercent = maxMem ? (usedMem / maxMem) * 100 : 0; + const defaultValue = [options.visibleClusterSections.at(0) ?? "node"]; const isTiny = width < 256; return ( - - - {formatUptime(uptime, t)} - - - - - - - + {options.showUptime && ( + + + {formatUptime(uptime, t)} + + + )} + + {options.visibleClusterSections.length >= 1 && ( + + {options.visibleClusterSections.includes("node") && ( + + + + )} - - - + {options.visibleClusterSections.includes("qemu") && ( + + + + )} - - - + {options.visibleClusterSections.includes("lxc") && ( + + + + )} - - - - + {options.visibleClusterSections.includes("storage") && ( + + + + )} + + )} ); }; interface SummaryHeaderProps { - cpu: number; - memory: number; + cpu: { value: number; hidden: boolean }; + memory: { value: number; hidden: boolean }; isTiny: boolean; } const SummaryHeader = ({ cpu, memory, isTiny }: SummaryHeaderProps) => { const t = useI18n(); + + if (cpu.hidden && memory.hidden) return null; + return (
- - - -
- } - sections={[{ value: cpu, color: cpu > 75 ? "orange" : "green" }]} - /> - - - {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)}% - - + {!cpu.hidden && ( + + + + + } + sections={[{ value: cpu.value, color: cpu.value > 75 ? "orange" : "green" }]} + /> + + + {t("widget.healthMonitoring.cluster.summary.cpu")} + + {cpu.value.toFixed(1)}% + + + )} + {!memory.hidden && ( + + + + + } + sections={[{ value: memory.value, color: memory.value > 75 ? "orange" : "green" }]} + /> + + + {t("widget.healthMonitoring.cluster.summary.memory")} + + {memory.value.toFixed(1)}% + + + )} ); diff --git a/packages/widgets/src/health-monitoring/index.ts b/packages/widgets/src/health-monitoring/index.ts index b3bc28980..8ed669750 100644 --- a/packages/widgets/src/health-monitoring/index.ts +++ b/packages/widgets/src/health-monitoring/index.ts @@ -8,34 +8,92 @@ import { optionsBuilder } from "../options"; export const { definition, componentLoader } = createWidgetDefinition("healthMonitoring", { icon: IconHeartRateMonitor, createOptions() { - return optionsBuilder.from((factory) => ({ - fahrenheit: factory.switch({ - defaultValue: false, + return optionsBuilder.from( + (factory) => ({ + fahrenheit: factory.switch({ + defaultValue: false, + }), + cpu: factory.switch({ + defaultValue: true, + }), + memory: factory.switch({ + defaultValue: true, + }), + showUptime: factory.switch({ + defaultValue: true, + }), + fileSystem: factory.switch({ + defaultValue: true, + }), + visibleClusterSections: factory.multiSelect({ + options: [ + { + value: "node", + label: (t) => t("widget.healthMonitoring.cluster.resource.node.name"), + }, + { + value: "qemu", + label: (t) => t("widget.healthMonitoring.cluster.resource.qemu.name"), + }, + { + value: "lxc", + label: (t) => t("widget.healthMonitoring.cluster.resource.lxc.name"), + }, + { + value: "storage", + label: (t) => t("widget.healthMonitoring.cluster.resource.storage.name"), + }, + ] as const, + defaultValue: ["node", "qemu", "lxc", "storage"] as const, + }), + defaultTab: factory.select({ + defaultValue: "system", + options: [ + { value: "system", label: "System" }, + { value: "cluster", label: "Cluster" }, + ] as const, + }), + sectionIndicatorRequirement: factory.select({ + defaultValue: "all", + options: [ + { value: "all", label: "All active" }, + { value: "any", label: "Any active" }, + ] as const, + }), }), - cpu: factory.switch({ - defaultValue: true, - }), - memory: factory.switch({ - defaultValue: true, - }), - fileSystem: factory.switch({ - defaultValue: true, - }), - defaultTab: factory.select({ - defaultValue: "system", - options: [ - { value: "system", label: "System" }, - { value: "cluster", label: "Cluster" }, - ] as const, - }), - sectionIndicatorRequirement: factory.select({ - defaultValue: "all", - options: [ - { value: "all", label: "All active" }, - { value: "any", label: "Any active" }, - ] as const, - }), - })); + { + fahrenheit: { + shouldHide(_, integrationKinds) { + // File system is only shown on system health tab + return integrationKinds.every((kind) => kind === "proxmox") || integrationKinds.length === 0; + }, + }, + fileSystem: { + shouldHide(_, integrationKinds) { + // File system is only shown on system health tab + return integrationKinds.every((kind) => kind === "proxmox") || integrationKinds.length === 0; + }, + }, + showUptime: { + shouldHide(_, integrationKinds) { + // Uptime is only shown on cluster health tab + return !integrationKinds.includes("proxmox"); + }, + }, + sectionIndicatorRequirement: { + shouldHide(_, integrationKinds) { + // Section indicator requirement is only shown on cluster health tab + return !integrationKinds.includes("proxmox"); + }, + }, + visibleClusterSections: { + shouldHide(_, integrationKinds) { + // Cluster sections are only shown on cluster health tab + return !integrationKinds.includes("proxmox"); + }, + }, + }, + ); }, supportedIntegrations: getIntegrationKindsByCategory("healthMonitoring"), errors: {