diff --git a/ui/src/components/resource-table.tsx b/ui/src/components/resource-table.tsx index bfae039e..118c47ac 100644 --- a/ui/src/components/resource-table.tsx +++ b/ui/src/components/resource-table.tsx @@ -132,17 +132,34 @@ export function ResourceTable({ }) const [refreshInterval, setRefreshInterval] = useState(5000) - const [selectedNamespace, setSelectedNamespace] = useState< - string | undefined - >(() => { - // Try to get the stored namespace from localStorage - const storedNamespace = localStorage.getItem( + const [selectedNamespaces, setSelectedNamespaces] = useState(() => { + const stored = localStorage.getItem( + localStorage.getItem('current-cluster') + 'selectedNamespaces' + ) + if (clusterScope) { + return [] + } + if (stored) { + try { + const parsed = JSON.parse(stored) + return Array.isArray(parsed) ? parsed : [parsed] + } catch { + return [stored] + } + } + const oldNamespace = localStorage.getItem( localStorage.getItem('current-cluster') + 'selectedNamespace' ) - return clusterScope - ? undefined // No namespace for cluster scope - : storedNamespace || 'default' // Default to 'default' if not set + return oldNamespace ? [oldNamespace] : ['default'] }) + + const apiNamespace = useMemo(() => { + if (selectedNamespaces.length === 0) return undefined + if (selectedNamespaces.length === 1 && selectedNamespaces[0] !== '_all') { + return selectedNamespaces[0] + } + return '_all' + }, [selectedNamespaces]) const [useSSE, setUseSSE] = useState(false) const { isLoading: queryLoading, @@ -152,7 +169,7 @@ export function ResourceTable({ refetch: queryRefetch, } = useResources( resourceType ?? (resourceName.toLowerCase() as ResourceType), - selectedNamespace, + apiNamespace, { refreshInterval: useSSE ? 0 : refreshInterval, // disable polling when SSE reduce: true, // Fetch reduced data for performance @@ -171,7 +188,7 @@ export function ResourceTable({ } = useResourcesWatch( (resourceType ?? (resourceName.toLowerCase() as ResourceType)) as ResourceType, - selectedNamespace, + apiNamespace, { reduce: true, enabled: useSSE } ) @@ -218,21 +235,19 @@ export function ResourceTable({ setPagination((prev) => ({ ...prev, pageIndex: 0 })) }, [columnFilters, searchQuery]) - // Handle namespace change const handleNamespaceChange = useCallback( - (value: string) => { - if (setSelectedNamespace) { - localStorage.setItem( - localStorage.getItem('current-cluster') + 'selectedNamespace', - value - ) - setSelectedNamespace(value) - // Reset pagination and search when changing namespace - setPagination({ pageIndex: 0, pageSize: pagination.pageSize }) - setSearchQuery('') - } + (namespaces: string[]) => { + const currentCluster = localStorage.getItem('current-cluster') + localStorage.setItem( + currentCluster + 'selectedNamespaces', + JSON.stringify(namespaces) + ) + localStorage.removeItem(currentCluster + 'selectedNamespace') + setSelectedNamespaces(namespaces) + setPagination({ pageIndex: 0, pageSize: pagination.pageSize }) + setSearchQuery('') }, - [setSelectedNamespace, pagination.pageSize] + [pagination.pageSize] ) // Add namespace column when showing all namespaces @@ -262,12 +277,10 @@ export function ResourceTable({ const baseColumns = [selectColumn, ...columns] - // Only add namespace column if not cluster scope, showing all namespaces, - // and there isn't already a namespace column in the provided columns - if (!clusterScope && selectedNamespace === '_all') { - // Check if namespace column already exists in the provided columns + const showMultipleNamespaces = selectedNamespaces.length > 1 || + (selectedNamespaces.length === 1 && selectedNamespaces[0] === '_all') + if (!clusterScope && showMultipleNamespaces) { const hasNamespaceColumn = columns.some((col) => { - // Check if the column accesses namespace data if ('accessorKey' in col && col.accessorKey === 'metadata.namespace') { return true } @@ -277,13 +290,11 @@ export function ResourceTable({ return false }) - // Only add namespace column if it doesn't already exist if (!hasNamespaceColumn) { const namespaceColumn = { id: 'namespace', header: t('resourceTable.namespace'), accessorFn: (row: T) => { - // Try to get namespace from metadata.namespace const metadata = (row as { metadata?: { namespace?: string } }) ?.metadata return metadata?.namespace || '-' @@ -295,14 +306,13 @@ export function ResourceTable({ ), } - // Insert namespace column after select and first column (typically name) const columnsWithNamespace = [...baseColumns] columnsWithNamespace.splice(2, 0, namespaceColumn) return columnsWithNamespace } } return baseColumns - }, [columns, clusterScope, selectedNamespace, t]) + }, [columns, clusterScope, selectedNamespaces, t]) const data = useMemo(() => { if (useSSE) return watchData @@ -315,7 +325,24 @@ export function ResourceTable({ : (queryError as unknown as Error | null) const refetch = useSSE ? reconnectSSE : queryRefetch - const memoizedData = useMemo(() => (data || []) as T[], [data]) + const filteredData = useMemo(() => { + const allData = (data || []) as T[] + if (selectedNamespaces.length === 0) return allData + if (selectedNamespaces.length === 1 && selectedNamespaces[0] === '_all') { + return allData + } + if (selectedNamespaces.length === 1) { + return allData + } + const selectedSet = new Set(selectedNamespaces) + return allData.filter((item) => { + const metadata = (item as { metadata?: { namespace?: string } })?.metadata + const namespace = metadata?.namespace + return namespace && selectedSet.has(namespace) + }) + }, [data, selectedNamespaces]) + + const memoizedData = useMemo(() => filteredData, [filteredData]) useEffect(() => { if (!useSSE && error) { @@ -462,8 +489,14 @@ export function ResourceTable({

Retrieving data - {!clusterScope && selectedNamespace - ? ` from ${selectedNamespace === '_all' ? 'All Namespaces' : `namespace ${selectedNamespace}`}` + {!clusterScope && selectedNamespaces.length > 0 + ? ` from ${ + selectedNamespaces.length === 1 && selectedNamespaces[0] === '_all' + ? 'All Namespaces' + : selectedNamespaces.length === 1 + ? `namespace ${selectedNamespaces[0]}` + : `${selectedNamespaces.length} namespaces` + }` : ''}

@@ -494,7 +527,9 @@ export function ResourceTable({ ? `No results match your search query: "${searchQuery}"` : clusterScope ? `There are no ${resourceName.toLowerCase()} found` - : `There are no ${resourceName.toLowerCase()} in the ${selectedNamespace} namespace`} + : selectedNamespaces.length === 1 && selectedNamespaces[0] !== '_all' + ? `There are no ${resourceName.toLowerCase()} in the ${selectedNamespaces[0]} namespace` + : `There are no ${resourceName.toLowerCase()} in the selected namespaces`}

{searchQuery && ( + + ))} + + )} - {sortedNamespaces?.map((ns: Namespace) => ( - - {ns.metadata!.name} - - ))} - - + + ) }