feat: manual refresh on sidebar navigation when LIVE mode is off
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m9s
CI / docker (push) Successful in 57s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 37s

When autoRefresh is disabled, sidebar clicks now invalidate all
queries (queryClient.invalidateQueries()), triggering a re-fetch.
This gives users "click to refresh" behavior instead of stale data.

When LIVE mode is on, queries already poll at intervals, so no
invalidation is needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-03 11:01:29 +02:00
parent c16f0e62ed
commit 37c10ae0a6

View File

@@ -16,6 +16,7 @@ import {
} from '@cameleer/design-system'; } from '@cameleer/design-system';
import type { SearchResult, SidebarTreeNode } from '@cameleer/design-system'; import type { SearchResult, SidebarTreeNode } from '@cameleer/design-system';
import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X } from 'lucide-react'; import { Box, Settings, FileText, ChevronRight, Square, Pause, Star, X } from 'lucide-react';
import { useQueryClient } from '@tanstack/react-query';
import { useRouteCatalog } from '../api/queries/catalog'; import { useRouteCatalog } from '../api/queries/catalog';
import { useAgents } from '../api/queries/agents'; import { useAgents } from '../api/queries/agents';
import { useSearchExecutions, useAttributeKeys } from '../api/queries/executions'; import { useSearchExecutions, useAttributeKeys } from '../api/queries/executions';
@@ -270,7 +271,8 @@ const SK_COLLAPSED = 'sidebar:collapsed';
function LayoutContent() { function LayoutContent() {
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
const { timeRange } = useGlobalFilters(); const queryClient = useQueryClient();
const { timeRange, autoRefresh } = useGlobalFilters();
const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString()); const { data: catalog } = useRouteCatalog(timeRange.start.toISOString(), timeRange.end.toISOString());
const { data: agents } = useAgents(); const { data: agents } = useAgents();
const { data: attributeKeys } = useAttributeKeys(); const { data: attributeKeys } = useAttributeKeys();
@@ -329,12 +331,13 @@ function LayoutContent() {
} }
if (appsOpen) { if (appsOpen) {
// Already open — navigate to all applications // Already open — navigate to all applications
if (!autoRefresh) queryClient.invalidateQueries();
navigate(`/${scope.tab}`); navigate(`/${scope.tab}`);
} else { } else {
setAppsOpen(true); setAppsOpen(true);
writeCollapsed(SK_APPS, true); writeCollapsed(SK_APPS, true);
} }
}, [isAdminPage, appsOpen, navigate, scope.tab]); }, [isAdminPage, appsOpen, navigate, scope.tab, autoRefresh, queryClient]);
const toggleAdmin = useCallback(() => { const toggleAdmin = useCallback(() => {
if (!isAdminPage) { if (!isAdminPage) {
@@ -557,6 +560,11 @@ function LayoutContent() {
const handleSidebarNavigate = useCallback((path: string) => { const handleSidebarNavigate = useCallback((path: string) => {
const state = { sidebarReveal: path }; const state = { sidebarReveal: path };
// When not auto-refreshing, treat navigation as a manual refresh
if (!autoRefresh) {
queryClient.invalidateQueries();
}
const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/); const appMatch = path.match(/^\/apps\/([^/]+)(?:\/(.+))?$/);
if (appMatch) { if (appMatch) {
const [, sAppId, sRouteId] = appMatch; const [, sAppId, sRouteId] = appMatch;
@@ -572,7 +580,7 @@ function LayoutContent() {
} }
navigate(path, { state }); navigate(path, { state });
}, [navigate, scope.tab]); }, [navigate, scope.tab, autoRefresh, queryClient]);
// --- Render ------------------------------------------------------- // --- Render -------------------------------------------------------
const camelLogo = ( const camelLogo = (