From 37d7902fcde2dbbedc19cdbaf877d28bff0287c1 Mon Sep 17 00:00:00 2001 From: Waleed Date: Wed, 10 Dec 2025 20:41:26 -0800 Subject: [PATCH] fix(dashboard): prevent dashboard from getting unmounted when on the logs page (#2298) --- .../logs/components/dashboard/dashboard.tsx | 14 +- .../app/workspace/[workspaceId]/logs/logs.tsx | 407 +++++++++--------- 2 files changed, 216 insertions(+), 205 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/dashboard.tsx b/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/dashboard.tsx index a5da603a7c..fa3f683bdd 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/dashboard.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/components/dashboard/dashboard.tsx @@ -289,9 +289,19 @@ export default function Dashboard({ const executions = metricsQuery.data?.workflows ?? [] const aggregateSegments = metricsQuery.data?.aggregateSegments ?? [] - const loading = metricsQuery.isLoading const error = metricsQuery.error?.message ?? null + /** + * Loading state logic using TanStack Query best practices: + * - isPending: true when there's no cached data (initial load only) + * - isFetching: true when any fetch is in progress + * - isPlaceholderData: true when showing stale data from keepPreviousData + * + * We only show skeleton on initial load (isPending + no data). + * For subsequent fetches, keepPreviousData shows stale content while fetching. + */ + const showSkeleton = metricsQuery.isPending && !metricsQuery.data + // Check if any filters are actually applied const hasActiveFilters = useMemo( () => @@ -747,7 +757,7 @@ export default function Dashboard({ } }, [refreshTrigger]) - if (loading) { + if (showSkeleton) { return } diff --git a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx index a45336609d..c8bb71a155 100644 --- a/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx +++ b/apps/sim/app/workspace/[workspaceId]/logs/logs.tsx @@ -408,215 +408,216 @@ export default function Logs() { /> - {/* Dashboard view */} - {isDashboardView && ( -
- -
- )} + {/* Dashboard view - always mounted to preserve state and query cache */} +
+ +
{/* Main content area with table - only show in logs view */} - {!isDashboardView && ( -
- {/* Table container */} -
- {/* Table header */} -
-
- - Date - - - Time - - - Status - - - Workflow - - - Cost - - - Trigger - - - Duration - -
-
- - {/* Table body - scrollable */} -
- {logsQuery.isLoading && !logsQuery.data ? ( -
-
- - Loading logs... -
-
- ) : logsQuery.isError ? ( -
-
- - - Error: {logsQuery.error?.message || 'Failed to load logs'} - -
-
- ) : logs.length === 0 ? ( -
-
- No logs found -
-
- ) : ( -
- {logs.map((log) => { - const formattedDate = formatDate(log.createdAt) - const isSelected = selectedLog?.id === log.id - const baseLevel = (log.level || 'info').toLowerCase() - const isError = baseLevel === 'error' - const isPending = !isError && log.hasPendingPause === true - const isRunning = !isError && !isPending && log.duration === null - - return ( -
handleLogClick(log)} - > -
- {/* Date */} - - {formattedDate.compactDate} - - - {/* Time */} - - {formattedDate.compactTime} - - - {/* Status */} -
- -
- - {/* Workflow */} -
-
- - {log.workflow?.name || 'Unknown'} - -
- - {/* Cost */} - - {typeof log.cost?.total === 'number' - ? `$${log.cost.total.toFixed(4)}` - : '—'} - - - {/* Trigger */} -
- {log.trigger ? ( - - ) : ( - - — - - )} -
- - {/* Duration */} -
- - {formatDuration(log.duration) || '—'} - -
-
- - {/* Resume Link */} - {isPending && - log.executionId && - (log.workflow?.id || log.workflowId) && ( - e.stopPropagation()} - > - - - )} -
- ) - })} - - {/* Infinite scroll loader */} - {logsQuery.hasNextPage && ( -
-
- {logsQuery.isFetchingNextPage ? ( - <> - - Loading more... - - ) : ( - Scroll to load more - )} -
-
- )} -
- )} +
+ {/* Table container */} +
+ {/* Table header */} +
+
+ + Date + + + Time + + + Status + + + Workflow + + + Cost + + + Trigger + + + Duration +
- {/* Log Details - rendered inside table container */} - 0} - /> + {/* Table body - scrollable */} +
+ {logsQuery.isLoading && !logsQuery.data ? ( +
+
+ + Loading logs... +
+
+ ) : logsQuery.isError ? ( +
+
+ + + Error: {logsQuery.error?.message || 'Failed to load logs'} + +
+
+ ) : logs.length === 0 ? ( +
+
+ No logs found +
+
+ ) : ( +
+ {logs.map((log) => { + const formattedDate = formatDate(log.createdAt) + const isSelected = selectedLog?.id === log.id + const baseLevel = (log.level || 'info').toLowerCase() + const isError = baseLevel === 'error' + const isPending = !isError && log.hasPendingPause === true + const isRunning = !isError && !isPending && log.duration === null + + return ( +
handleLogClick(log)} + > +
+ {/* Date */} + + {formattedDate.compactDate} + + + {/* Time */} + + {formattedDate.compactTime} + + + {/* Status */} +
+ +
+ + {/* Workflow */} +
+
+ + {log.workflow?.name || 'Unknown'} + +
+ + {/* Cost */} + + {typeof log.cost?.total === 'number' + ? `$${log.cost.total.toFixed(4)}` + : '—'} + + + {/* Trigger */} +
+ {log.trigger ? ( + + ) : ( + + — + + )} +
+ + {/* Duration */} +
+ + {formatDuration(log.duration) || '—'} + +
+
+ + {/* Resume Link */} + {isPending && log.executionId && (log.workflow?.id || log.workflowId) && ( + e.stopPropagation()} + > + + + )} +
+ ) + })} + + {/* Infinite scroll loader */} + {logsQuery.hasNextPage && ( +
+
+ {logsQuery.isFetchingNextPage ? ( + <> + + Loading more... + + ) : ( + Scroll to load more + )} +
+
+ )} +
+ )} +
- )} + + {/* Log Details - rendered inside table container */} + 0} + /> +