diff --git a/src/components/LXCList.js b/src/components/LXCList.js index cf3ef0d..b0c08c2 100644 --- a/src/components/LXCList.js +++ b/src/components/LXCList.js @@ -8,30 +8,51 @@ import StatusBadge from './StatusBadge'; */ export default function LXCList({ lxc, onPowerAction, onOpenConsole }) { const [expandedNodes, setExpandedNodes] = useState({}); + const [filter, setFilter] = useState(''); if (!lxc?.length) { return

No LXCs found.

; } - const groups = Object.groupBy(lxc, (l) => l.node); + const filtered = filter + ? lxc.filter((item) => + [item.name, item.node, item.status].some((v) => v.toLowerCase().includes(filter.toLowerCase()) + )) + : lxc; + + const groups = Object.groupBy(filtered, (item) => item.node); const sortedNodes = Object.keys(groups).sort(); - function toggleNode(node) { - setExpandedNodes((prev) => ({ ...prev, [node]: !prev[node] })); - } + const total = filtered.length; + const running = filtered.filter((item) => item.status === 'running').length; return (
+ {filter && ( +
+ {running} running / {total - running} stopped + · + {total} of {lxc.length} +
+ )} + + setFilter(e.target.value)} + className="w-full rounded-md border border-border-card bg-bg-card px-3 py-2 text-sm text-text-primary placeholder-text-secondary focus:border-accent focus:outline-none" + /> + {sortedNodes.map((node) => { - const running = groups[node].filter((l) => l.status === 'running'); - const stopped = groups[node].filter((l) => l.status !== 'running'); + const running = groups[node].filter((item) => item.status === 'running'); + const stopped = groups[node].filter((item) => item.status !== 'running'); const expanded = expandedNodes[node] ?? false; return (
- {/* Node header */} - {/* Node body */} {expanded && ( <> {running.length > 0 && ( @@ -69,24 +89,9 @@ export default function LXCList({ lxc, onPowerAction, onOpenConsole }) { {formatBytes(item.disk?.used || 0)} - - - + + + ))} @@ -118,18 +123,8 @@ export default function LXCList({ lxc, onPowerAction, onOpenConsole }) { {formatBytes(item.disk?.used || 0)} - - + + ))} @@ -143,6 +138,10 @@ export default function LXCList({ lxc, onPowerAction, onOpenConsole }) {
); })} + + {!total && ( +

No matches for "{filter}"

+ )}
); } diff --git a/src/components/VMList.js b/src/components/VMList.js index 65a2f7f..5543cfd 100644 --- a/src/components/VMList.js +++ b/src/components/VMList.js @@ -8,24 +8,41 @@ import StatusBadge from './StatusBadge'; */ export default function VMList({ vms, onPowerAction, onOpenConsole }) { const [expandedNodes, setExpandedNodes] = useState({}); + const [filter, setFilter] = useState(''); if (!vms?.length) { return

No VMs found.

; } - const groups = Object.groupBy(vms, (vm) => vm.node); + const filtered = filter + ? vms.filter((vm) => + [vm.name, vm.node, vm.status].some((v) => v.toLowerCase().includes(filter.toLowerCase()))) + : vms; + + const groups = Object.groupBy(filtered, (vm) => vm.node); const sortedNodes = Object.keys(groups).sort(); - function toggleNode(node) { - setExpandedNodes((prev) => ({ ...prev, [node]: !prev[node] })); - } - - function allExpanded() { - return sortedNodes.every((n) => expandedNodes[n]); - } + const total = filtered.length; + const running = filtered.filter((vm) => vm.status === 'running').length; return (
+ {filter && ( +
+ {running} running / {total - running} stopped + · + {total} of {vms.length} +
+ )} + + setFilter(e.target.value)} + className="w-full rounded-md border border-border-card bg-bg-card px-3 py-2 text-sm text-text-primary placeholder-text-secondary focus:border-accent focus:outline-none" + /> + {sortedNodes.map((node) => { const running = groups[node].filter((vm) => vm.status === 'running'); const stopped = groups[node].filter((vm) => vm.status !== 'running'); @@ -33,9 +50,8 @@ export default function VMList({ vms, onPowerAction, onOpenConsole }) { return (
- {/* Node header */} - {/* Node body */} {expanded && ( <> {running.length > 0 && ( @@ -73,24 +88,9 @@ export default function VMList({ vms, onPowerAction, onOpenConsole }) { {formatBytes(vm.disk?.used || 0)} - - - + + + ))} @@ -122,18 +122,8 @@ export default function VMList({ vms, onPowerAction, onOpenConsole }) { {formatBytes(vm.disk?.used || 0)} - - + + ))} @@ -147,6 +137,10 @@ export default function VMList({ vms, onPowerAction, onOpenConsole }) {
); })} + + {!total && ( +

No matches for "{filter}"

+ )}
); }