mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-22 11:13:26 +00:00
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user