fix(instances): keep workspace-scoped filters in sync with active workspace
The per-column "Node" filter memoized its option list against TanStack's Column object, which keeps the same identity across data updates, so the checklist stayed frozen at whatever nodes were visible when the popover first mounted instead of reflecting the current (workspace-scoped) rows. Switching the active workspace also left the server/node/column filters holding values from the previous workspace, which could filter the newly scoped VM list down to nothing.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { Column, FilterFn, RowData } from '@tanstack/react-table';
|
||||
import { ListFilter, X } from 'lucide-react';
|
||||
@@ -49,13 +48,18 @@ export function ColumnFilter<TData>({ column, variant = 'text', formatOption }:
|
||||
? Array.isArray(filterValue) && filterValue.length > 0
|
||||
: !!filterValue;
|
||||
|
||||
const uniqueValues = useMemo(() => {
|
||||
if (variant !== 'select') return [];
|
||||
return Array.from(column.getFacetedUniqueValues().keys())
|
||||
.filter((v): v is string => v != null && String(v).length > 0)
|
||||
.map(String)
|
||||
.sort();
|
||||
}, [variant, column]);
|
||||
// Not memoized on `column` — TanStack keeps the same Column object identity
|
||||
// across data updates, so a useMemo keyed on it would freeze these options
|
||||
// at whatever the table's data looked like when this popover first mounted
|
||||
// (e.g. before a workspace switch re-scoped the rows). getFacetedUniqueValues()
|
||||
// is already memoized internally against the actual row data.
|
||||
const uniqueValues =
|
||||
variant === 'select'
|
||||
? Array.from(column.getFacetedUniqueValues().keys())
|
||||
.filter((v): v is string => v != null && String(v).length > 0)
|
||||
.map(String)
|
||||
.sort()
|
||||
: [];
|
||||
|
||||
const selected = (Array.isArray(filterValue) ? filterValue : []) as string[];
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ import { formatBytes, vmTypeLabel, formatUptime, formatVmConfig } from '@/lib/fo
|
||||
import { apiClient } from '@/lib/api-client';
|
||||
import { useDeployTasksStore } from '@/stores/deploy-tasks-store';
|
||||
import { useBulkTasksStore } from '@/stores/bulk-tasks-store';
|
||||
import { useWorkspaceStore } from '@/stores/workspace-store';
|
||||
import type { VMInstance } from '@/types';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
@@ -365,6 +366,26 @@ export default function InstancesPage() {
|
||||
});
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
// Reset every filter that can reference a server/node identity when the
|
||||
// active workspace changes — those values belong to whatever workspace was
|
||||
// active when they were picked, and silently keeping them can filter the
|
||||
// new workspace's (now-scoped) VM list down to nothing.
|
||||
const activeWorkspaceId = useWorkspaceStore((s) => s.activeWorkspaceId);
|
||||
// Compares against the previous *value* (not a one-shot "first render" flag)
|
||||
// so React 18 StrictMode's dev-only double effect invocation on mount can't
|
||||
// misfire this as a real workspace change.
|
||||
const prevWorkspaceIdRef = useRef(activeWorkspaceId);
|
||||
useEffect(() => {
|
||||
if (prevWorkspaceIdRef.current === activeWorkspaceId) return;
|
||||
prevWorkspaceIdRef.current = activeWorkspaceId;
|
||||
setServerFilter('all');
|
||||
localStorage.setItem('instances-server-filter', 'all');
|
||||
setNodeFilter('all');
|
||||
localStorage.setItem('instances-node-filter', 'all');
|
||||
setColumnFilters([]);
|
||||
setRowSelection({});
|
||||
}, [activeWorkspaceId]);
|
||||
|
||||
// Persist sorting + column filters across reloads
|
||||
useEffect(() => {
|
||||
localStorage.setItem('instances-sorting', JSON.stringify(sorting));
|
||||
|
||||
Reference in New Issue
Block a user