diff --git a/frontend-modern/src/components/Dashboard/Dashboard.tsx b/frontend-modern/src/components/Dashboard/Dashboard.tsx index 0e09784a1..511465869 100644 --- a/frontend-modern/src/components/Dashboard/Dashboard.tsx +++ b/frontend-modern/src/components/Dashboard/Dashboard.tsx @@ -98,17 +98,16 @@ export function Dashboard(props: DashboardProps) { } }); - // Create a mapping from node name to host URL - const nodeHostMap = createMemo(() => { - const map: Record = {}; + // Create a mapping from node instance ID to node object + const nodeByInstance = createMemo(() => { + const map: Record = {}; props.nodes.forEach((node) => { - if (node.host) { - map[node.name] = node.host; - } + map[node.id] = node; }); return map; }); + // Persist filter states to localStorage createEffect(() => { localStorage.setItem('dashboardViewMode', viewMode()); @@ -356,13 +355,14 @@ export function Dashboard(props: DashboardProps) { return groups; } - // Original grouped by node logic + // Group by node instance ID (not hostname) to handle nodes with duplicate names const groups: Record = {}; guests.forEach((guest) => { - if (!groups[guest.node]) { - groups[guest.node] = []; + const instanceId = guest.instance; // Use unique instance ID instead of hostname + if (!groups[instanceId]) { + groups[instanceId] = []; } - groups[guest.node].push(guest); + groups[instanceId].push(guest); }); // Sort within each node group @@ -557,10 +557,10 @@ export function Dashboard(props: DashboardProps) { const diskPercent = () => node.disk ? Math.round((node.disk.used / node.disk.total) * 100) : 0; - // Count VMs and containers for this node - const nodeVMs = () => props.vms.filter((vm) => vm.node === node.name).length; + // Count VMs and containers for this node (use instance ID to handle duplicate node names) + const nodeVMs = () => props.vms.filter((vm) => vm.instance === node.id).length; const nodeContainers = () => - props.containers.filter((ct) => ct.node === node.name).length; + props.containers.filter((ct) => ct.instance === node.id).length; const isSelected = () => search().includes(`node:${node.name}`); @@ -915,25 +915,29 @@ export function Dashboard(props: DashboardProps) { a.localeCompare(b))} + each={Object.entries(groupedGuests()).sort(([instanceIdA], [instanceIdB]) => { + // Sort by node name for display, not instance ID + const nodeA = nodeByInstance()[instanceIdA]; + const nodeB = nodeByInstance()[instanceIdB]; + return (nodeA?.name || '').localeCompare(nodeB?.name || ''); + })} fallback={<>} > - {([node, guests]) => ( + {([instanceId, guests]) => { + const node = nodeByInstance()[instanceId]; + return ( <> - {node} + {node.name} @@ -962,7 +966,8 @@ export function Dashboard(props: DashboardProps) { )} - )} + ); + }} diff --git a/frontend-modern/src/components/Storage/Storage.tsx b/frontend-modern/src/components/Storage/Storage.tsx index a3738561b..30ae9b23b 100644 --- a/frontend-modern/src/components/Storage/Storage.tsx +++ b/frontend-modern/src/components/Storage/Storage.tsx @@ -20,17 +20,16 @@ const Storage: Component = () => { const [sortKey, setSortKey] = createSignal('name'); const [sortDirection, setSortDirection] = createSignal<'asc' | 'desc'>('asc'); - // Create a mapping from node name to host URL - const nodeHostMap = createMemo(() => { - const map: Record = {}; + // Create a mapping from node instance ID to node object + const nodeByInstance = createMemo(() => { + const map: Record = {}; (state.nodes || []).forEach((node) => { - if (node.host) { - map[node.name] = node.host; - } + map[node.id] = node; }); return map; }); + const sortKeyOptions: { value: StorageSortKey; label: string }[] = [ { value: 'name', label: 'Name' }, { value: 'node', label: 'Node' }, @@ -243,10 +242,12 @@ const Storage: Component = () => { const mode = viewMode(); if (mode === 'node') { + // Group by instance ID (not node name) to handle duplicate node names const groups: Record = {}; storage.forEach((s) => { - if (!groups[s.node]) groups[s.node] = []; - groups[s.node].push(s); + const key = s.instance; // Use unique instance ID instead of hostname + if (!groups[key]) groups[key] = []; + groups[key].push(s); }); return groups; } else { @@ -553,34 +554,42 @@ const Storage: Component = () => { a.localeCompare(b))} + each={Object.entries(groupedStorage()).sort(([instanceIdA], [instanceIdB]) => { + // Sort by node name for display when in node mode + if (viewMode() === 'node') { + const nodeA = nodeByInstance()[instanceIdA]; + const nodeB = nodeByInstance()[instanceIdB]; + return (nodeA?.name || '').localeCompare(nodeB?.name || ''); + } + // Sort by storage name when in storage mode + return instanceIdA.localeCompare(instanceIdB); + })} > - {([groupName, storages]) => ( - <> - {/* Group Header */} - - - - - {groupName} - - - - + {([groupKey, storages]) => { + const node = viewMode() === 'node' ? nodeByInstance()[groupKey] : null; + return ( + <> + {/* Group Header */} + + {(validNode) => ( + + + + {validNode().name} + + + + )} + {/* Storage Rows */} }> @@ -817,7 +826,8 @@ const Storage: Component = () => { }} - )} + ); + }}