diff --git a/frontend-modern/src/features/proxmox/ProxmoxNodesTable.tsx b/frontend-modern/src/features/proxmox/ProxmoxNodesTable.tsx
index 38085f53e..61f3e97bd 100644
--- a/frontend-modern/src/features/proxmox/ProxmoxNodesTable.tsx
+++ b/frontend-modern/src/features/proxmox/ProxmoxNodesTable.tsx
@@ -17,7 +17,8 @@ import {
} from '@/components/shared/Table';
import { getSimpleStatusIndicator } from '@/utils/status';
import { asTrimmedString } from '@/utils/stringUtils';
-import { normalizeDiskArray } from '@/utils/format';
+import { formatBytes, formatPercent, normalizeDiskArray } from '@/utils/format';
+import { getMetricColorRgba } from '@/utils/metricThresholds';
import { buildMetricKeyForUnifiedResource } from '@/utils/metricsKeys';
import { useWorkloadTableMetricHistory } from '@/components/Workloads/useWorkloadTableMetricHistory';
import { getWorkloadTableLayoutMode } from '@/components/Workloads/guestRowModel';
@@ -118,6 +119,86 @@ const formatPercentLabel = (value: number | null | undefined): string => {
return `${Math.round(Math.max(0, normalized))}%`;
};
+const getDiskUsagePercent = (disk: Disk): number => {
+ if (disk.total > 0) return (disk.used / disk.total) * 100;
+ if (Number.isFinite(disk.usage)) return disk.usage <= 1 ? disk.usage * 100 : disk.usage;
+ return 0;
+};
+
+const getDiskShortLabel = (disk: Disk, index: number): string => {
+ const raw = (disk.mountpoint || disk.device || `Disk ${index + 1}`).trim();
+ if (raw.startsWith('/dev/')) return raw.slice('/dev/'.length);
+ if (raw === '/') return '/';
+ if (raw.startsWith('/')) {
+ const parts = raw.split('/').filter(Boolean);
+ if (parts.length > 0) return parts[parts.length - 1];
+ }
+ return raw;
+};
+
+const getWorstDiskPercent = (disks: Disk[]): number => {
+ let worst = 0;
+ for (const disk of disks) {
+ const pct = getDiskUsagePercent(disk);
+ if (pct > worst) worst = pct;
+ }
+ return worst;
+};
+
+const DISK_COUNT_BADGE_BASE =
+ 'inline-flex items-center gap-1.5 rounded-md bg-surface-alt px-2 py-0.5 text-[11px] font-medium text-base-content';
+
+const ProxmoxHostDiskSubRow: Component<{
+ disk: Disk;
+ index: number;
+ visibleColumns: ProxmoxHostTableColumn[];
+}> = (subProps) => {
+ const percent = () => getDiskUsagePercent(subProps.disk);
+ const fillPercent = () => Math.min(percent(), 100);
+ const color = () => getMetricColorRgba(percent(), 'disk');
+ const label = () => getDiskShortLabel(subProps.disk, subProps.index);
+ const fullPath = () =>
+ subProps.disk.mountpoint || subProps.disk.device || `Disk ${subProps.index + 1}`;
+ const usedLabel = () => formatBytes(subProps.disk.used);
+ const totalLabel = () =>
+ subProps.disk.total > 0 ? formatBytes(subProps.disk.total) : '—';
+
+ const diskIdx = () => subProps.visibleColumns.findIndex((c) => c.id === 'disk');
+ const leadingColumns = () => subProps.visibleColumns.slice(0, diskIdx());
+ const trailingSpan = () => subProps.visibleColumns.length - diskIdx();
+
+ return (
+
+
+ {(column) => (
+
+ )}
+
+
+
+
└
+
{label()}
+
+
+ {formatPercent(percent())}
+
+
+ {usedLabel()} / {totalLabel()}
+
+
+
+
+ );
+};
+
export const ProxmoxNodesTable: Component<{
nodes: Resource[];
guests: Resource[];
@@ -205,6 +286,7 @@ export const ProxmoxNodesTable: Component<{
? node.memory.current
: undefined;
const diskPercent = () => node.disk?.current ?? 0;
+ const diskList = (): Disk[] => normalizeDiskArray(node.agent?.disks) ?? [];
const aggregateDisk = (): Disk | undefined =>
node.disk
? ({
@@ -333,10 +415,32 @@ export const ProxmoxNodesTable: Component<{
}
>
-
+ 1}
+ fallback={
+
+ }
+ >
+
+
+ {diskList().length} disks
+
+
}
>
@@ -392,9 +496,22 @@ export const ProxmoxNodesTable: Component<{
};
return (
-
- {(column) => renderColumnCell(column)}
-
+ <>
+
+ {(column) => renderColumnCell(column)}
+
+ 1}>
+
+ {(disk, index) => (
+
+ )}
+
+
+ >
);
}}