diff --git a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts index 26cc6129..652909a2 100644 --- a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts +++ b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts @@ -1,7 +1,12 @@ +import { useEffect, useRef } from "react"; import { getClient } from "@/api/AxiosClient"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { statusIsNotFinalized } from "@/routes/tasks/types"; -import { keepPreviousData, useQuery } from "@tanstack/react-query"; +import { + keepPreviousData, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import { WorkflowRunTimelineItem } from "../types/workflowRunTypes"; import { useWorkflowRunWithWorkflowQuery } from "./useWorkflowRunWithWorkflowQuery"; import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery"; @@ -10,11 +15,32 @@ import { useFirstParam } from "@/hooks/useFirstParam"; function useWorkflowRunTimelineQuery() { const workflowRunId = useFirstParam("workflowRunId", "runId"); const credentialGetter = useCredentialGetter(); + const queryClient = useQueryClient(); const { data: globalWorkflows } = useGlobalWorkflowsQuery(); - const { data: workflowRun } = useWorkflowRunWithWorkflowQuery(); + const { data: workflowRun, dataUpdatedAt } = + useWorkflowRunWithWorkflowQuery(); const workflow = workflowRun?.workflow; const workflowPermanentId = workflow?.workflow_permanent_id; + // Track when workflow run data was last updated + const prevDataUpdatedAtRef = useRef(dataUpdatedAt); + + // Refetch timeline whenever the workflow run query gets new data. + // This keeps the timeline perfectly synchronized with the workflow run status, + // ensuring we never miss updates (e.g., when workflow completes). + useEffect(() => { + if ( + dataUpdatedAt !== prevDataUpdatedAtRef.current && + workflowPermanentId && + workflowRunId + ) { + queryClient.invalidateQueries({ + queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId], + }); + } + prevDataUpdatedAtRef.current = dataUpdatedAt; + }, [dataUpdatedAt, workflowPermanentId, workflowRunId, queryClient]); + return useQuery>({ queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId], queryFn: async () => { @@ -33,8 +59,8 @@ function useWorkflowRunTimelineQuery() { ) .then((response) => response.data); }, - refetchInterval: - workflowRun && statusIsNotFinalized(workflowRun) ? 5000 : false, + // No independent refetchInterval - timeline follows workflow run query's timing + // via the useEffect above that invalidates on dataUpdatedAt changes placeholderData: keepPreviousData, refetchOnMount: workflowRun && statusIsNotFinalized(workflowRun) ? "always" : false,