diff --git a/skyvern-frontend/src/hooks/useFirstParam.ts b/skyvern-frontend/src/hooks/useFirstParam.ts index d57206f5..1f9f39a6 100644 --- a/skyvern-frontend/src/hooks/useFirstParam.ts +++ b/skyvern-frontend/src/hooks/useFirstParam.ts @@ -11,7 +11,7 @@ const useFirstParam = (...paramNames: string[]) => { return value; } } - return null; + return undefined; }; export { useFirstParam }; diff --git a/skyvern-frontend/src/routes/history/RunHistory.tsx b/skyvern-frontend/src/routes/history/RunHistory.tsx index 5eb1e860..0b434621 100644 --- a/skyvern-frontend/src/routes/history/RunHistory.tsx +++ b/skyvern-frontend/src/routes/history/RunHistory.tsx @@ -29,6 +29,7 @@ import { useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import { getClient } from "@/api/AxiosClient"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; +import * as env from "@/util/env"; function isTask(run: Task | WorkflowRunApiResponse): run is Task { return "task_id" in run; @@ -185,7 +186,9 @@ function RunHistory() { onClick={(event) => { handleNavigate( event, - `/workflows/${run.workflow_permanent_id}/${run.workflow_run_id}/overview`, + env.useNewRunsUrl + ? `/runs/${run.workflow_run_id}` + : `/workflows/${run.workflow_permanent_id}/${run.workflow_run_id}/overview`, ); }} > diff --git a/skyvern-frontend/src/routes/runs/RunRouter.tsx b/skyvern-frontend/src/routes/runs/RunRouter.tsx index 275934c0..061ca095 100644 --- a/skyvern-frontend/src/routes/runs/RunRouter.tsx +++ b/skyvern-frontend/src/routes/runs/RunRouter.tsx @@ -4,6 +4,7 @@ */ import { Navigate, Route, Routes, useParams } from "react-router-dom"; +import { useMemo } from "react"; import { PageLayout } from "@/components/PageLayout"; import { Status404 } from "@/components/Status404"; @@ -22,12 +23,67 @@ import { WorkflowsPageLayout } from "@/routes/workflows/WorkflowsPageLayout"; import { useTaskV2Query } from "@/routes/runs/useTaskV2Query"; function RunRouter() { - let { runId } = useParams(); + const { runId } = useParams(); const { data: task_v2, isLoading } = useTaskV2Query({ id: runId?.startsWith("tsk_v2") ? runId : undefined, }); + const runType = runId?.startsWith("tsk_v2") + ? "redirect" + : runId?.startsWith("wr_") + ? "workflow" + : runId?.startsWith("tsk_") + ? "task" + : null; + + const routes = useMemo(() => { + if (runType === "workflow") { + return ( + + }> + }> + } /> + } + /> + } /> + } /> + } + /> + } /> + } + /> + + + + ); + } + + if (runType === "task") { + return ( + + }> + }> + } /> + } /> + } /> + } /> + } /> + + + + ); + } + + return ; + }, [runType]); + if (runId?.startsWith("tsk_v2")) { if (isLoading) { return
Fetching task details...
; @@ -45,50 +101,10 @@ function RunRouter() { return ; } - runId = workflowRunId; - return ; } - if (runId?.startsWith("wr_")) { - return ( - - }> - }> - } /> - } /> - } /> - } /> - } /> - } /> - } - /> - - - - ); - } - - if (runId?.startsWith("tsk_")) { - return ( - - }> - }> - } /> - } /> - } /> - } /> - } /> - - - - ); - } - - // Fallback (should not reach here due to earlier check) - return ; + return routes; } export { RunRouter }; diff --git a/skyvern-frontend/src/routes/tasks/create/retry/RetryTask.tsx b/skyvern-frontend/src/routes/tasks/create/retry/RetryTask.tsx index 123ef149..774a1feb 100644 --- a/skyvern-frontend/src/routes/tasks/create/retry/RetryTask.tsx +++ b/skyvern-frontend/src/routes/tasks/create/retry/RetryTask.tsx @@ -1,9 +1,9 @@ -import { useParams } from "react-router-dom"; import { useTaskQuery } from "../../detail/hooks/useTaskQuery"; import { CreateNewTaskForm } from "../CreateNewTaskForm"; +import { useFirstParam } from "@/hooks/useFirstParam"; function RetryTask() { - const { taskId } = useParams(); + const taskId = useFirstParam("taskId", "runId"); const { data: task, isLoading } = useTaskQuery({ id: taskId }); if (isLoading) { diff --git a/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx b/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx index f628a519..25db0eaf 100644 --- a/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx +++ b/skyvern-frontend/src/routes/tasks/detail/TaskDetails.tsx @@ -40,6 +40,7 @@ import { statusIsFinalized } from "../types"; import { MAX_STEPS_DEFAULT } from "../constants"; import { useTaskQuery } from "./hooks/useTaskQuery"; import { useFirstParam } from "@/hooks/useFirstParam"; +import * as env from "@/util/env"; function createTaskRequestObject(values: TaskApiResponse) { return { @@ -303,7 +304,11 @@ function TaskDetails() { workflow && workflowRun && ( {workflow.title} diff --git a/skyvern-frontend/src/routes/workflows/RunWorkflowForm.tsx b/skyvern-frontend/src/routes/workflows/RunWorkflowForm.tsx index 0ee81d5f..a378f925 100644 --- a/skyvern-frontend/src/routes/workflows/RunWorkflowForm.tsx +++ b/skyvern-frontend/src/routes/workflows/RunWorkflowForm.tsx @@ -49,6 +49,7 @@ import { getLabelForWorkflowParameterType } from "./editor/workflowEditorUtils"; import { WorkflowParameter } from "./types/workflowTypes"; import { WorkflowParameterInput } from "./WorkflowParameterInput"; import { TestWebhookDialog } from "@/components/TestWebhookDialog"; +import * as env from "@/util/env"; // Utility function to omit specified keys from an object function omit, K extends keyof T>( @@ -247,7 +248,9 @@ function RunWorkflowForm({ queryKey: ["runs"], }); navigate( - `/workflows/${workflowPermanentId}/${response.data.workflow_run_id}/overview`, + env.useNewRunsUrl + ? `/runs/${response.data.workflow_run_id}` + : `/workflows/${workflowPermanentId}/${response.data.workflow_run_id}/overview`, ); }, onError: (error: AxiosError) => { diff --git a/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx b/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx index c493e123..2da9c8e1 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowPage.tsx @@ -49,6 +49,7 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import { RunParametersDialog } from "./workflowRun/RunParametersDialog"; +import * as env from "@/util/env"; function WorkflowPage() { const { workflowPermanentId } = useParams(); @@ -186,18 +187,19 @@ function WorkflowPage() { { + const url = env.useNewRunsUrl + ? `/runs/${workflowRun.workflow_run_id}` + : `/workflows/${workflowPermanentId}/${workflowRun.workflow_run_id}/overview`; + if (event.ctrlKey || event.metaKey) { window.open( - window.location.origin + - `/workflows/${workflowPermanentId}/${workflowRun.workflow_run_id}/overview`, + window.location.origin + url, "_blank", "noopener,noreferrer", ); return; } - navigate( - `/workflows/${workflowPermanentId}/${workflowRun.workflow_run_id}/overview`, - ); + navigate(url); }} className="cursor-pointer" > diff --git a/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx b/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx index c1e1a2f6..1902b255 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx @@ -32,7 +32,7 @@ import { ReloadIcon, } from "@radix-ui/react-icons"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { Link, Outlet, useParams, useSearchParams } from "react-router-dom"; +import { Link, Outlet, useSearchParams } from "react-router-dom"; import { statusIsFinalized, statusIsRunningOrQueued } from "../tasks/types"; import { useWorkflowRunWithWorkflowQuery } from "./hooks/useWorkflowRunWithWorkflowQuery"; import { WorkflowRunTimeline } from "./workflowRun/WorkflowRunTimeline"; @@ -44,6 +44,7 @@ import { cn } from "@/util/utils"; import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area"; import { ApiWebhookActionsMenu } from "@/components/ApiWebhookActionsMenu"; import { WebhookReplayDialog } from "@/components/WebhookReplayDialog"; +import { useFirstParam } from "@/hooks/useFirstParam"; import { type ApiCommandOptions } from "@/util/apiCommands"; import { useBlockScriptsQuery } from "@/routes/workflows/hooks/useBlockScriptsQuery"; import { constructCacheKeyValue } from "@/routes/workflows/editor/utils"; @@ -55,7 +56,7 @@ function WorkflowRun() { const embed = searchParams.get("embed"); const isEmbedded = embed === "true"; const active = searchParams.get("active"); - const { workflowRunId } = useParams(); + const workflowRunId = useFirstParam("workflowRunId", "runId"); const credentialGetter = useCredentialGetter(); const apiCredential = useApiCredential(); const queryClient = useQueryClient(); diff --git a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts index acca3655..26cc6129 100644 --- a/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts +++ b/skyvern-frontend/src/routes/workflows/hooks/useWorkflowRunTimelineQuery.ts @@ -2,7 +2,6 @@ import { getClient } from "@/api/AxiosClient"; import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { statusIsNotFinalized } from "@/routes/tasks/types"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; -import { useParams } from "react-router-dom"; import { WorkflowRunTimelineItem } from "../types/workflowRunTypes"; import { useWorkflowRunWithWorkflowQuery } from "./useWorkflowRunWithWorkflowQuery"; import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery"; @@ -10,13 +9,11 @@ import { useFirstParam } from "@/hooks/useFirstParam"; function useWorkflowRunTimelineQuery() { const workflowRunId = useFirstParam("workflowRunId", "runId"); - const { workflowPermanentId: workflowPermanentIdParam } = useParams(); const credentialGetter = useCredentialGetter(); const { data: globalWorkflows } = useGlobalWorkflowsQuery(); const { data: workflowRun } = useWorkflowRunWithWorkflowQuery(); - - const workflowPermanentId = - workflowPermanentIdParam ?? workflowRun?.workflow?.workflow_permanent_id; + const workflow = workflowRun?.workflow; + const workflowPermanentId = workflow?.workflow_permanent_id; return useQuery>({ queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId], diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunStream.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunStream.tsx index c67e3f8c..00b4f5e3 100644 --- a/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunStream.tsx +++ b/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunStream.tsx @@ -1,5 +1,5 @@ import { Status } from "@/api/types"; -import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery"; +import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery"; import { ZoomableImage } from "@/components/ZoomableImage"; import { useEffect, useState } from "react"; import { statusIsNotFinalized } from "@/routes/tasks/types"; @@ -25,12 +25,14 @@ const wssBaseUrl = import.meta.env.VITE_WSS_BASE_URL; function WorkflowRunStream(props?: Props) { const alwaysShowStream = props?.alwaysShowStream ?? false; - const { data: workflowRun } = useWorkflowRunQuery(); + const { data: workflowRun } = useWorkflowRunWithWorkflowQuery(); const [streamImgSrc, setStreamImgSrc] = useState(""); const showStream = alwaysShowStream || (workflowRun && statusIsNotFinalized(workflowRun)); const credentialGetter = useCredentialGetter(); - const { workflowRunId, workflowPermanentId } = useParams(); + const { workflowRunId } = useParams(); + const workflow = workflowRun?.workflow; + const workflowPermanentId = workflow?.workflow_permanent_id; const queryClient = useQueryClient(); useEffect(() => { @@ -73,6 +75,9 @@ function WorkflowRunStream(props?: Props) { queryClient.invalidateQueries({ queryKey: ["workflowRun", workflowPermanentId, workflowRunId], }); + queryClient.invalidateQueries({ + queryKey: ["workflowRun", workflowRunId], + }); queryClient.invalidateQueries({ queryKey: ["workflowTasks", workflowRunId], }); diff --git a/skyvern-frontend/src/util/env.ts b/skyvern-frontend/src/util/env.ts index a18b21ca..6170b31e 100644 --- a/skyvern-frontend/src/util/env.ts +++ b/skyvern-frontend/src/util/env.ts @@ -94,6 +94,8 @@ function clearRuntimeApiKey(): void { } } +const useNewRunsUrl = true as const; + export { apiBaseUrl, runsApiBaseUrl, @@ -106,4 +108,5 @@ export { getRuntimeApiKey, persistRuntimeApiKey, clearRuntimeApiKey, + useNewRunsUrl, };