fix: enable node click filtering for any number of nodes

addresses #356 - node click filtering now works with:
- 1-4 nodes (regular cards - already worked)
- 5-9 nodes (compact cards - now fixed)
- 10+ nodes (ultra-compact list - now fixed)

clicking any node box filters VMs to that node only, regardless of how many nodes are displayed
This commit is contained in:
Pulse Monitor
2025-08-25 21:50:47 +00:00
parent 25dbc0a689
commit 45ee845b8a
2 changed files with 72 additions and 12 deletions
@@ -8,6 +8,8 @@ import { useWebSocket } from '@/App';
interface CompactNodeCardProps {
node: Node;
variant: 'compact' | 'ultra-compact';
onClick?: () => void;
isSelected?: boolean;
}
const CompactNodeCard: Component<CompactNodeCardProps> = (props) => {
@@ -55,11 +57,14 @@ const CompactNodeCard: Component<CompactNodeCardProps> = (props) => {
if (props.variant === 'ultra-compact') {
// Single line format for 10+ nodes
return (
<div class={`flex items-center gap-2 px-3 py-1.5 bg-white dark:bg-gray-800 rounded border ${
!isOnline() ? 'border-red-500' :
alertStyles.hasAlert ? 'border-orange-500' :
'border-gray-200 dark:border-gray-700'
} hover:shadow-sm transition-shadow`}>
<div
class={`flex items-center gap-2 px-3 py-1.5 bg-white dark:bg-gray-800 rounded border ${
props.isSelected ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20' :
!isOnline() ? 'border-red-500' :
alertStyles.hasAlert ? 'border-orange-500' :
'border-gray-200 dark:border-gray-700'
} hover:shadow-sm transition-all cursor-pointer hover:scale-[1.01]`}
onClick={props.onClick}>
{/* Status dot */}
<span class={`w-2 h-2 rounded-full ${isOnline() ? 'bg-green-500' : 'bg-red-500'}`} />
@@ -101,11 +106,14 @@ const CompactNodeCard: Component<CompactNodeCardProps> = (props) => {
// Compact bar format for 5-9 nodes
return (
<div class={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border ${
!isOnline() ? 'border-red-500' :
alertStyles.hasAlert ? 'border-orange-500' :
'border-gray-200 dark:border-gray-700'
} p-3`}>
<div
class={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border ${
props.isSelected ? 'border-blue-500 bg-blue-50 dark:bg-blue-900/20' :
!isOnline() ? 'border-red-500' :
alertStyles.hasAlert ? 'border-orange-500' :
'border-gray-200 dark:border-gray-700'
} p-3 cursor-pointer transition-all hover:scale-[1.02]`}
onClick={props.onClick}>
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<span class={`w-2 h-2 rounded-full ${isOnline() ? 'bg-green-500' : 'bg-red-500'}`} />
@@ -355,7 +355,33 @@ export function Dashboard(props: DashboardProps) {
<For each={props.nodes}>
{(node) => (
<ComponentErrorBoundary name="CompactNodeCard">
<CompactNodeCard node={node} variant="compact" />
<CompactNodeCard
node={node}
variant="compact"
isSelected={search().includes(`node:${node.name}`)}
onClick={() => {
const currentSearch = search();
const nodeFilter = `node:${node.name}`;
// Check if this node filter is already in the search
if (currentSearch.includes(nodeFilter)) {
// Remove the node filter
setSearch(currentSearch.replace(nodeFilter, '').trim().replace(/,\s*,/g, ',').replace(/^,|,$/g, ''));
setIsSearchLocked(false);
} else {
// Clear any existing node: filters and add the new one
const cleanedSearch = currentSearch.replace(/node:\w+/g, '').trim().replace(/,\s*,/g, ',').replace(/^,|,$/g, '');
const newSearch = cleanedSearch ? `${cleanedSearch}, ${nodeFilter}` : nodeFilter;
setSearch(newSearch);
setIsSearchLocked(true);
// Expand filters if collapsed
if (!showFilters()) {
setShowFilters(true);
}
}
}}
/>
</ComponentErrorBoundary>
)}
</For>
@@ -368,7 +394,33 @@ export function Dashboard(props: DashboardProps) {
<For each={props.nodes}>
{(node) => (
<ComponentErrorBoundary name="CompactNodeCard">
<CompactNodeCard node={node} variant="ultra-compact" />
<CompactNodeCard
node={node}
variant="ultra-compact"
isSelected={search().includes(`node:${node.name}`)}
onClick={() => {
const currentSearch = search();
const nodeFilter = `node:${node.name}`;
// Check if this node filter is already in the search
if (currentSearch.includes(nodeFilter)) {
// Remove the node filter
setSearch(currentSearch.replace(nodeFilter, '').trim().replace(/,\s*,/g, ',').replace(/^,|,$/g, ''));
setIsSearchLocked(false);
} else {
// Clear any existing node: filters and add the new one
const cleanedSearch = currentSearch.replace(/node:\w+/g, '').trim().replace(/,\s*,/g, ',').replace(/^,|,$/g, '');
const newSearch = cleanedSearch ? `${cleanedSearch}, ${nodeFilter}` : nodeFilter;
setSearch(newSearch);
setIsSearchLocked(true);
// Expand filters if collapsed
if (!showFilters()) {
setShowFilters(true);
}
}
}}
/>
</ComponentErrorBoundary>
)}
</For>