Frontend: unified /runs URL (#3912)
This commit is contained in:
BIN
skyvern-frontend/public/404-sad-dragon-md.png
Normal file
BIN
skyvern-frontend/public/404-sad-dragon-md.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 286 KiB |
@@ -1,3 +1,5 @@
|
|||||||
|
import { WorkflowApiResponse } from "@/routes/workflows/types/workflowTypes";
|
||||||
|
|
||||||
export const ArtifactType = {
|
export const ArtifactType = {
|
||||||
Recording: "recording",
|
Recording: "recording",
|
||||||
ActionScreenshot: "screenshot_action",
|
ActionScreenshot: "screenshot_action",
|
||||||
@@ -374,6 +376,32 @@ export type WorkflowRunStatusApiResponse = {
|
|||||||
max_screenshot_scrolls: number | null;
|
max_screenshot_scrolls: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type WorkflowRunStatusApiResponseWithWorkflow = {
|
||||||
|
workflow_id: string;
|
||||||
|
workflow_run_id: string;
|
||||||
|
status: Status;
|
||||||
|
proxy_location: ProxyLocation | null;
|
||||||
|
webhook_callback_url: string | null;
|
||||||
|
extra_http_headers: Record<string, string> | null;
|
||||||
|
created_at: string;
|
||||||
|
finished_at: string;
|
||||||
|
modified_at: string;
|
||||||
|
parameters: Record<string, unknown>;
|
||||||
|
screenshot_urls: Array<string> | null;
|
||||||
|
recording_url: string | null;
|
||||||
|
outputs: Record<string, unknown> | null;
|
||||||
|
failure_reason: string | null;
|
||||||
|
webhook_failure_reason: string | null;
|
||||||
|
downloaded_file_urls: Array<string> | null;
|
||||||
|
total_steps: number | null;
|
||||||
|
total_cost: number | null;
|
||||||
|
task_v2: TaskV2 | null;
|
||||||
|
workflow_title: string | null;
|
||||||
|
browser_session_id: string | null;
|
||||||
|
max_screenshot_scrolls: number | null;
|
||||||
|
workflow: WorkflowApiResponse;
|
||||||
|
};
|
||||||
|
|
||||||
export type TaskGenerationApiResponse = {
|
export type TaskGenerationApiResponse = {
|
||||||
suggested_title: string | null;
|
suggested_title: string | null;
|
||||||
url: string | null;
|
url: string | null;
|
||||||
|
|||||||
63
skyvern-frontend/src/components/Status404.css
Normal file
63
skyvern-frontend/src/components/Status404.css
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
@keyframes roll-right-404 {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0rem) translateY(1rem) rotate(-60deg);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(10rem) translateY(1rem) rotate(0deg);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-roll-right-404 {
|
||||||
|
animation: roll-right-404 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fade-in-404 {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in-404 {
|
||||||
|
animation: fade-in-404 1s ease-out 0.5s forwards;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-fade-in-slow-404 {
|
||||||
|
animation: fade-in-404 1s ease-out 1.7s forwards;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes orbit-404 {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg) translateX(6.5rem) rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg) translateX(6.5rem) rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-orbit-404 {
|
||||||
|
animation: orbit-404 8s linear infinite;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-glow-404 {
|
||||||
|
0%,
|
||||||
|
100% {
|
||||||
|
filter: brightness(1) saturate(1);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
filter: brightness(1.5) saturate(1.3);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pulse-glow-404 {
|
||||||
|
animation: pulse-glow-404 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
26
skyvern-frontend/src/components/Status404.tsx
Normal file
26
skyvern-frontend/src/components/Status404.tsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import "./Status404.css";
|
||||||
|
|
||||||
|
function Status404() {
|
||||||
|
return (
|
||||||
|
<div className="fixed inset-0 z-50 bg-background">
|
||||||
|
<div className="absolute flex h-full w-full items-center justify-center">
|
||||||
|
<div className="animate-roll-right-404 relative flex h-[13rem] w-[13rem] flex-col items-center justify-center rounded-full bg-white/5 text-xl font-bold text-white">
|
||||||
|
<div className="animate-fade-in-404">404</div>
|
||||||
|
<div className="opacity-50">Not Found</div>
|
||||||
|
<div className="animate-orbit-404 absolute h-full w-full">
|
||||||
|
<div className="animate-fade-in-slow-404 relative h-[2rem] w-[2rem] translate-x-[5.5rem] translate-y-[5.5rem] rounded-full bg-white/10" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="absolute flex h-full w-full items-center justify-center">
|
||||||
|
<img
|
||||||
|
src="/404-sad-dragon-md.png"
|
||||||
|
alt="404 Not Found"
|
||||||
|
className="max-h-screen max-w-2xl object-contain"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Status404 };
|
||||||
17
skyvern-frontend/src/hooks/useFirstParam.ts
Normal file
17
skyvern-frontend/src/hooks/useFirstParam.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a list of parameter names, returns the value of the first one that exists in the URL parameters.
|
||||||
|
*/
|
||||||
|
const useFirstParam = (...paramNames: string[]) => {
|
||||||
|
const params = useParams();
|
||||||
|
for (const name of paramNames) {
|
||||||
|
const value = params[name];
|
||||||
|
if (value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { useFirstParam };
|
||||||
@@ -28,6 +28,7 @@ import { WorkflowRunRecording } from "./routes/workflows/workflowRun/WorkflowRun
|
|||||||
import { WorkflowRunCode } from "@/routes/workflows/workflowRun/WorkflowRunCode";
|
import { WorkflowRunCode } from "@/routes/workflows/workflowRun/WorkflowRunCode";
|
||||||
import { DebugStoreProvider } from "@/store/DebugStoreContext";
|
import { DebugStoreProvider } from "@/store/DebugStoreContext";
|
||||||
import { CredentialsPage } from "@/routes/credentials/CredentialsPage.tsx";
|
import { CredentialsPage } from "@/routes/credentials/CredentialsPage.tsx";
|
||||||
|
import { RunRouter } from "@/routes/runs/RunRouter";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
@@ -42,6 +43,20 @@ const router = createBrowserRouter([
|
|||||||
</DebugStoreProvider>
|
</DebugStoreProvider>
|
||||||
),
|
),
|
||||||
children: [
|
children: [
|
||||||
|
{
|
||||||
|
path: "runs",
|
||||||
|
element: <PageLayout />,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
index: true,
|
||||||
|
element: <HistoryPage />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "runs/:runId/*",
|
||||||
|
element: <RunRouter />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "browser-sessions",
|
path: "browser-sessions",
|
||||||
element: <BrowserSessions />,
|
element: <BrowserSessions />,
|
||||||
|
|||||||
@@ -183,10 +183,7 @@ function RunHistory() {
|
|||||||
key={run.workflow_run_id}
|
key={run.workflow_run_id}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={(event) => {
|
onClick={(event) => {
|
||||||
handleNavigate(
|
handleNavigate(event, `/runs/${run.workflow_run_id}`);
|
||||||
event,
|
|
||||||
`/workflows/${run.workflow_permanent_id}/${run.workflow_run_id}/overview`,
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TableCell
|
<TableCell
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ function SideNav() {
|
|||||||
icon: <LightningBoltIcon className="size-6" />,
|
icon: <LightningBoltIcon className="size-6" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "History",
|
label: "Runs",
|
||||||
to: "/history",
|
to: "/runs",
|
||||||
icon: <CounterClockwiseClockIcon className="size-6" />,
|
icon: <CounterClockwiseClockIcon className="size-6" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
94
skyvern-frontend/src/routes/runs/RunRouter.tsx
Normal file
94
skyvern-frontend/src/routes/runs/RunRouter.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* A router component that handles both workflow runs (wr_xxx) and task runs (tsk_xxx)
|
||||||
|
* under the /runs/:runId path, discriminating based on ID prefix.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Navigate, Route, Routes, useParams } from "react-router-dom";
|
||||||
|
|
||||||
|
import { PageLayout } from "@/components/PageLayout";
|
||||||
|
import { Status404 } from "@/components/Status404";
|
||||||
|
import { StepArtifactsLayout } from "@/routes/tasks/detail/StepArtifactsLayout";
|
||||||
|
import { TaskActions } from "@/routes/tasks/detail/TaskActions";
|
||||||
|
import { TaskDetails } from "@/routes/tasks/detail/TaskDetails";
|
||||||
|
import { TaskParameters } from "@/routes/tasks/detail/TaskParameters";
|
||||||
|
import { TaskRecording } from "@/routes/tasks/detail/TaskRecording";
|
||||||
|
import { WorkflowRun } from "@/routes/workflows/WorkflowRun";
|
||||||
|
import { WorkflowPostRunParameters } from "@/routes/workflows/workflowRun/WorkflowPostRunParameters";
|
||||||
|
import { WorkflowRunOutput } from "@/routes/workflows/workflowRun/WorkflowRunOutput";
|
||||||
|
import { WorkflowRunOverview } from "@/routes/workflows/workflowRun/WorkflowRunOverview";
|
||||||
|
import { WorkflowRunRecording } from "@/routes/workflows/workflowRun/WorkflowRunRecording";
|
||||||
|
import { WorkflowRunCode } from "@/routes/workflows/workflowRun/WorkflowRunCode";
|
||||||
|
import { WorkflowsPageLayout } from "@/routes/workflows/WorkflowsPageLayout";
|
||||||
|
import { useTaskV2Query } from "@/routes/runs/useTaskV2Query";
|
||||||
|
|
||||||
|
function RunRouter() {
|
||||||
|
let { runId } = useParams();
|
||||||
|
|
||||||
|
const { data: task_v2, isLoading } = useTaskV2Query({
|
||||||
|
id: runId?.startsWith("tsk_v2") ? runId : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (runId?.startsWith("tsk_v2")) {
|
||||||
|
if (isLoading) {
|
||||||
|
return <div>Fetching task details...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!task_v2) {
|
||||||
|
console.error("Task for %s not found", runId);
|
||||||
|
return <Status404 />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowRunId = task_v2.workflow_run_id;
|
||||||
|
|
||||||
|
if (!workflowRunId) {
|
||||||
|
console.error("Workflow run ID for Task V2 %s not found", runId);
|
||||||
|
return <Status404 />;
|
||||||
|
}
|
||||||
|
|
||||||
|
runId = workflowRunId;
|
||||||
|
|
||||||
|
return <Navigate to={`/runs/${workflowRunId}`} replace />;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runId?.startsWith("wr_")) {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route element={<WorkflowsPageLayout />}>
|
||||||
|
<Route element={<WorkflowRun />}>
|
||||||
|
<Route index element={<Navigate to="overview" replace />} />
|
||||||
|
<Route path="blocks" element={<Navigate to="overview" replace />} />
|
||||||
|
<Route path="overview" element={<WorkflowRunOverview />} />
|
||||||
|
<Route path="output" element={<WorkflowRunOutput />} />
|
||||||
|
<Route path="parameters" element={<WorkflowPostRunParameters />} />
|
||||||
|
<Route path="recording" element={<WorkflowRunRecording />} />
|
||||||
|
<Route
|
||||||
|
path="code"
|
||||||
|
element={<WorkflowRunCode showCacheKeyValueSelector={true} />}
|
||||||
|
/>
|
||||||
|
</Route>
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (runId?.startsWith("tsk_")) {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route element={<PageLayout />}>
|
||||||
|
<Route element={<TaskDetails />}>
|
||||||
|
<Route index element={<Navigate to="actions" replace />} />
|
||||||
|
<Route path="actions" element={<TaskActions />} />
|
||||||
|
<Route path="recording" element={<TaskRecording />} />
|
||||||
|
<Route path="parameters" element={<TaskParameters />} />
|
||||||
|
<Route path="diagnostics" element={<StepArtifactsLayout />} />
|
||||||
|
</Route>
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback (should not reach here due to earlier check)
|
||||||
|
return <Status404 />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { RunRouter };
|
||||||
23
skyvern-frontend/src/routes/runs/useTaskV2Query.ts
Normal file
23
skyvern-frontend/src/routes/runs/useTaskV2Query.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { getClient } from "@/api/AxiosClient";
|
||||||
|
import { TaskV2 } from "@/api/types";
|
||||||
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useTaskV2Query({ id }: Props) {
|
||||||
|
const credentialGetter = useCredentialGetter();
|
||||||
|
|
||||||
|
return useQuery<TaskV2>({
|
||||||
|
queryKey: ["task_v2", id],
|
||||||
|
queryFn: async () => {
|
||||||
|
const client = await getClient(credentialGetter, "v2");
|
||||||
|
return client.get(`/tasks/${id}`).then((response) => response.data);
|
||||||
|
},
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useTaskV2Query };
|
||||||
@@ -2,16 +2,17 @@ import { StepNavigation } from "./StepNavigation";
|
|||||||
import { StepArtifacts } from "./StepArtifacts";
|
import { StepArtifacts } from "./StepArtifacts";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { StepApiResponse } from "@/api/types";
|
import { StepApiResponse } from "@/api/types";
|
||||||
import { useParams, useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { getClient } from "@/api/AxiosClient";
|
import { getClient } from "@/api/AxiosClient";
|
||||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
import { apiPathPrefix } from "@/util/env";
|
import { apiPathPrefix } from "@/util/env";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
function StepArtifactsLayout() {
|
function StepArtifactsLayout() {
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
const step = Number(searchParams.get("step")) || 0;
|
const step = Number(searchParams.get("step")) || 0;
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const { taskId } = useParams();
|
const taskId = useFirstParam("taskId", "runId");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: steps,
|
data: steps,
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import { getClient } from "@/api/AxiosClient";
|
|||||||
import { StepApiResponse } from "@/api/types";
|
import { StepApiResponse } from "@/api/types";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useParams, useSearchParams } from "react-router-dom";
|
import { useSearchParams } from "react-router-dom";
|
||||||
import { PAGE_SIZE } from "../constants";
|
import { PAGE_SIZE } from "../constants";
|
||||||
import { CheckboxIcon, CrossCircledIcon } from "@radix-ui/react-icons";
|
import { CheckboxIcon, CrossCircledIcon } from "@radix-ui/react-icons";
|
||||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
import { apiPathPrefix } from "@/util/env";
|
import { apiPathPrefix } from "@/util/env";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
activeIndex: number;
|
activeIndex: number;
|
||||||
@@ -14,7 +15,7 @@ type Props = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function StepNavigation({ activeIndex, onActiveIndexChange }: Props) {
|
function StepNavigation({ activeIndex, onActiveIndexChange }: Props) {
|
||||||
const { taskId } = useParams();
|
const taskId = useFirstParam("taskId", "runId");
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1;
|
const page = searchParams.get("page") ? Number(searchParams.get("page")) : 1;
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
useQueryClient,
|
useQueryClient,
|
||||||
} from "@tanstack/react-query";
|
} from "@tanstack/react-query";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import {
|
import {
|
||||||
statusIsFinalized,
|
statusIsFinalized,
|
||||||
statusIsNotFinalized,
|
statusIsNotFinalized,
|
||||||
@@ -21,6 +20,7 @@ import {
|
|||||||
import { ActionScreenshot } from "./ActionScreenshot";
|
import { ActionScreenshot } from "./ActionScreenshot";
|
||||||
import { useActions } from "./hooks/useActions";
|
import { useActions } from "./hooks/useActions";
|
||||||
import { ScrollableActionList } from "./ScrollableActionList";
|
import { ScrollableActionList } from "./ScrollableActionList";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
const formatter = Intl.NumberFormat("en-US", {
|
const formatter = Intl.NumberFormat("en-US", {
|
||||||
style: "currency",
|
style: "currency",
|
||||||
@@ -38,7 +38,7 @@ let socket: WebSocket | null = null;
|
|||||||
const wssBaseUrl = import.meta.env.VITE_WSS_BASE_URL;
|
const wssBaseUrl = import.meta.env.VITE_WSS_BASE_URL;
|
||||||
|
|
||||||
function TaskActions() {
|
function TaskActions() {
|
||||||
const { taskId } = useParams();
|
const taskId = useFirstParam("taskId", "runId");
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const [streamImgSrc, setStreamImgSrc] = useState<string>("");
|
const [streamImgSrc, setStreamImgSrc] = useState<string>("");
|
||||||
const [selectedAction, setSelectedAction] = useState<
|
const [selectedAction, setSelectedAction] = useState<
|
||||||
@@ -157,7 +157,7 @@ function TaskActions() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { data: actions, isLoading: actionsIsLoading } = useActions({
|
const { data: actions, isLoading: actionsIsLoading } = useActions({
|
||||||
id: taskId,
|
id: taskId ?? undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (taskIsLoading || actionsIsLoading || stepsIsLoading) {
|
if (taskIsLoading || actionsIsLoading || stepsIsLoading) {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AxiosError } from "axios";
|
||||||
import { getClient } from "@/api/AxiosClient";
|
import { getClient } from "@/api/AxiosClient";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import {
|
||||||
@@ -6,6 +7,7 @@ import {
|
|||||||
TaskApiResponse,
|
TaskApiResponse,
|
||||||
WorkflowRunStatusApiResponse,
|
WorkflowRunStatusApiResponse,
|
||||||
} from "@/api/types";
|
} from "@/api/types";
|
||||||
|
import { Status404 } from "@/components/Status404";
|
||||||
import { StatusBadge } from "@/components/StatusBadge";
|
import { StatusBadge } from "@/components/StatusBadge";
|
||||||
import { SwitchBarNavigation } from "@/components/SwitchBarNavigation";
|
import { SwitchBarNavigation } from "@/components/SwitchBarNavigation";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -33,10 +35,11 @@ import { type ApiCommandOptions } from "@/util/apiCommands";
|
|||||||
import { buildTaskRunPayload } from "@/util/taskRunPayload";
|
import { buildTaskRunPayload } from "@/util/taskRunPayload";
|
||||||
import { PlayIcon, ReloadIcon } from "@radix-ui/react-icons";
|
import { PlayIcon, ReloadIcon } from "@radix-ui/react-icons";
|
||||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { Link, Outlet, useParams } from "react-router-dom";
|
import { Link, Outlet } from "react-router-dom";
|
||||||
import { statusIsFinalized } from "../types";
|
import { statusIsFinalized } from "../types";
|
||||||
import { MAX_STEPS_DEFAULT } from "../constants";
|
import { MAX_STEPS_DEFAULT } from "../constants";
|
||||||
import { useTaskQuery } from "./hooks/useTaskQuery";
|
import { useTaskQuery } from "./hooks/useTaskQuery";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
function createTaskRequestObject(values: TaskApiResponse) {
|
function createTaskRequestObject(values: TaskApiResponse) {
|
||||||
return {
|
return {
|
||||||
@@ -52,7 +55,7 @@ function createTaskRequestObject(values: TaskApiResponse) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function TaskDetails() {
|
function TaskDetails() {
|
||||||
const { taskId } = useParams();
|
const taskId = useFirstParam("taskId", "runId");
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const apiCredential = useApiCredential();
|
const apiCredential = useApiCredential();
|
||||||
@@ -62,7 +65,7 @@ function TaskDetails() {
|
|||||||
isLoading: taskIsLoading,
|
isLoading: taskIsLoading,
|
||||||
isError: taskIsError,
|
isError: taskIsError,
|
||||||
error: taskError,
|
error: taskError,
|
||||||
} = useTaskQuery({ id: taskId });
|
} = useTaskQuery({ id: taskId ?? undefined });
|
||||||
|
|
||||||
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
||||||
useQuery<WorkflowRunStatusApiResponse>({
|
useQuery<WorkflowRunStatusApiResponse>({
|
||||||
@@ -132,6 +135,12 @@ function TaskDetails() {
|
|||||||
const [replayOpen, setReplayOpen] = useState(false);
|
const [replayOpen, setReplayOpen] = useState(false);
|
||||||
|
|
||||||
if (taskIsError) {
|
if (taskIsError) {
|
||||||
|
const status = (taskError as AxiosError | undefined)?.response?.status;
|
||||||
|
|
||||||
|
if (status === 404) {
|
||||||
|
return <Status404 />;
|
||||||
|
}
|
||||||
|
|
||||||
return <div>Error: {taskError?.message}</div>;
|
return <div>Error: {taskError?.message}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,9 +302,7 @@ function TaskDetails() {
|
|||||||
) : (
|
) : (
|
||||||
workflow &&
|
workflow &&
|
||||||
workflowRun && (
|
workflowRun && (
|
||||||
<Link
|
<Link to={`/runs/${workflowRun.workflow_run_id}`}>
|
||||||
to={`/workflows/${workflow.workflow_permanent_id}/${workflowRun.workflow_run_id}/overview`}
|
|
||||||
>
|
|
||||||
{workflow.title}
|
{workflow.title}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
|||||||
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
||||||
import { MAX_SCREENSHOT_SCROLLS_DEFAULT } from "@/routes/workflows/editor/nodes/Taskv2Node/types";
|
import { MAX_SCREENSHOT_SCROLLS_DEFAULT } from "@/routes/workflows/editor/nodes/Taskv2Node/types";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useParams } from "react-router-dom";
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
function TaskParameters() {
|
function TaskParameters() {
|
||||||
const { taskId } = useParams();
|
const taskId = useFirstParam("taskId", "runId");
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const {
|
const {
|
||||||
data: task,
|
data: task,
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { getClient } from "@/api/AxiosClient";
|
|||||||
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { getRecordingURL } from "./artifactUtils";
|
import { getRecordingURL } from "./artifactUtils";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
import { TaskApiResponse } from "@/api/types";
|
import { TaskApiResponse } from "@/api/types";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
function TaskRecording() {
|
function TaskRecording() {
|
||||||
const { taskId } = useParams();
|
const taskId = useFirstParam("taskId", "runId");
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
|||||||
@@ -246,9 +246,7 @@ function RunWorkflowForm({
|
|||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: ["runs"],
|
queryKey: ["runs"],
|
||||||
});
|
});
|
||||||
navigate(
|
navigate(`/runs/${response.data.workflow_run_id}`);
|
||||||
`/workflows/${workflowPermanentId}/${response.data.workflow_run_id}/overview`,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
onError: (error: AxiosError) => {
|
onError: (error: AxiosError) => {
|
||||||
const detail = (error.response?.data as { detail?: string })?.detail;
|
const detail = (error.response?.data as { detail?: string })?.detail;
|
||||||
|
|||||||
@@ -189,15 +189,13 @@ function WorkflowPage() {
|
|||||||
if (event.ctrlKey || event.metaKey) {
|
if (event.ctrlKey || event.metaKey) {
|
||||||
window.open(
|
window.open(
|
||||||
window.location.origin +
|
window.location.origin +
|
||||||
`/workflows/${workflowPermanentId}/${workflowRun.workflow_run_id}/overview`,
|
`/runs/${workflowRun.workflow_run_id}`,
|
||||||
"_blank",
|
"_blank",
|
||||||
"noopener,noreferrer",
|
"noopener,noreferrer",
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
navigate(
|
navigate(`/runs/${workflowRun.workflow_run_id}`);
|
||||||
`/workflows/${workflowPermanentId}/${workflowRun.workflow_run_id}/overview`,
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AxiosError } from "axios";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { getClient } from "@/api/AxiosClient";
|
import { getClient } from "@/api/AxiosClient";
|
||||||
import { ProxyLocation, Status } from "@/api/types";
|
import { ProxyLocation, Status } from "@/api/types";
|
||||||
@@ -7,6 +8,7 @@ import {
|
|||||||
type SwitchBarNavigationOption,
|
type SwitchBarNavigationOption,
|
||||||
} from "@/components/SwitchBarNavigation";
|
} from "@/components/SwitchBarNavigation";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Status404 } from "@/components/Status404";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
@@ -32,8 +34,7 @@ import {
|
|||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import { Link, Outlet, useParams, useSearchParams } from "react-router-dom";
|
import { Link, Outlet, useParams, useSearchParams } from "react-router-dom";
|
||||||
import { statusIsFinalized, statusIsRunningOrQueued } from "../tasks/types";
|
import { statusIsFinalized, statusIsRunningOrQueued } from "../tasks/types";
|
||||||
import { useWorkflowQuery } from "./hooks/useWorkflowQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "./hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { useWorkflowRunQuery } from "./hooks/useWorkflowRunQuery";
|
|
||||||
import { WorkflowRunTimeline } from "./workflowRun/WorkflowRunTimeline";
|
import { WorkflowRunTimeline } from "./workflowRun/WorkflowRunTimeline";
|
||||||
import { useWorkflowRunTimelineQuery } from "./hooks/useWorkflowRunTimelineQuery";
|
import { useWorkflowRunTimelineQuery } from "./hooks/useWorkflowRunTimelineQuery";
|
||||||
import { findActiveItem } from "./workflowRun/workflowTimelineUtils";
|
import { findActiveItem } from "./workflowRun/workflowTimelineUtils";
|
||||||
@@ -54,23 +55,22 @@ function WorkflowRun() {
|
|||||||
const embed = searchParams.get("embed");
|
const embed = searchParams.get("embed");
|
||||||
const isEmbedded = embed === "true";
|
const isEmbedded = embed === "true";
|
||||||
const active = searchParams.get("active");
|
const active = searchParams.get("active");
|
||||||
const { workflowRunId, workflowPermanentId } = useParams();
|
const { workflowRunId } = useParams();
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const apiCredential = useApiCredential();
|
const apiCredential = useApiCredential();
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { data: workflow, isLoading: workflowIsLoading } = useWorkflowQuery({
|
|
||||||
workflowPermanentId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const cacheKey = workflow?.cache_key ?? "";
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: workflowRun,
|
data: workflowRun,
|
||||||
isLoading: workflowRunIsLoading,
|
isLoading: workflowRunIsLoading,
|
||||||
isFetched,
|
isFetched,
|
||||||
} = useWorkflowRunQuery();
|
error,
|
||||||
|
} = useWorkflowRunWithWorkflowQuery();
|
||||||
|
|
||||||
|
const status = (error as AxiosError | undefined)?.response?.status;
|
||||||
|
const workflow = workflowRun?.workflow;
|
||||||
|
const workflowPermanentId = workflow?.workflow_permanent_id;
|
||||||
|
const cacheKey = workflow?.cache_key ?? "";
|
||||||
const isFinalized = workflowRun ? statusIsFinalized(workflowRun) : null;
|
const isFinalized = workflowRun ? statusIsFinalized(workflowRun) : null;
|
||||||
|
|
||||||
const [hasPublishedCode, setHasPublishedCode] = useState(false);
|
const [hasPublishedCode, setHasPublishedCode] = useState(false);
|
||||||
@@ -155,7 +155,7 @@ function WorkflowRun() {
|
|||||||
workflowRun?.proxy_location ?? ProxyLocation.Residential;
|
workflowRun?.proxy_location ?? ProxyLocation.Residential;
|
||||||
const maxScreenshotScrolls = workflowRun?.max_screenshot_scrolls ?? null;
|
const maxScreenshotScrolls = workflowRun?.max_screenshot_scrolls ?? null;
|
||||||
|
|
||||||
const title = workflowIsLoading ? (
|
const title = workflowRunIsLoading ? (
|
||||||
<Skeleton className="h-9 w-48" />
|
<Skeleton className="h-9 w-48" />
|
||||||
) : (
|
) : (
|
||||||
<h1 className="text-3xl">
|
<h1 className="text-3xl">
|
||||||
@@ -292,6 +292,10 @@ function WorkflowRun() {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (status === 404) {
|
||||||
|
return <Status404 />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{!isEmbedded && (
|
{!isEmbedded && (
|
||||||
|
|||||||
@@ -4,14 +4,19 @@ import { statusIsNotFinalized } from "@/routes/tasks/types";
|
|||||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom";
|
||||||
import { WorkflowRunTimelineItem } from "../types/workflowRunTypes";
|
import { WorkflowRunTimelineItem } from "../types/workflowRunTypes";
|
||||||
import { useWorkflowRunQuery } from "./useWorkflowRunQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "./useWorkflowRunWithWorkflowQuery";
|
||||||
import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery";
|
import { useGlobalWorkflowsQuery } from "./useGlobalWorkflowsQuery";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
function useWorkflowRunTimelineQuery() {
|
function useWorkflowRunTimelineQuery() {
|
||||||
const { workflowRunId, workflowPermanentId } = useParams();
|
const workflowRunId = useFirstParam("workflowRunId", "runId");
|
||||||
|
const { workflowPermanentId: workflowPermanentIdParam } = useParams();
|
||||||
const credentialGetter = useCredentialGetter();
|
const credentialGetter = useCredentialGetter();
|
||||||
const { data: globalWorkflows } = useGlobalWorkflowsQuery();
|
const { data: globalWorkflows } = useGlobalWorkflowsQuery();
|
||||||
const { data: workflowRun } = useWorkflowRunQuery();
|
const { data: workflowRun } = useWorkflowRunWithWorkflowQuery();
|
||||||
|
|
||||||
|
const workflowPermanentId =
|
||||||
|
workflowPermanentIdParam ?? workflowRun?.workflow?.workflow_permanent_id;
|
||||||
|
|
||||||
return useQuery<Array<WorkflowRunTimelineItem>>({
|
return useQuery<Array<WorkflowRunTimelineItem>>({
|
||||||
queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId],
|
queryKey: ["workflowRunTimeline", workflowPermanentId, workflowRunId],
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import { getClient } from "@/api/AxiosClient";
|
||||||
|
import { WorkflowRunStatusApiResponseWithWorkflow } from "@/api/types";
|
||||||
|
import { useCredentialGetter } from "@/hooks/useCredentialGetter";
|
||||||
|
import {
|
||||||
|
statusIsNotFinalized,
|
||||||
|
statusIsRunningOrQueued,
|
||||||
|
} from "@/routes/tasks/types";
|
||||||
|
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||||
|
import { useFirstParam } from "@/hooks/useFirstParam";
|
||||||
|
|
||||||
|
function useWorkflowRunWithWorkflowQuery() {
|
||||||
|
const workflowRunId = useFirstParam("workflowRunId", "runId");
|
||||||
|
const credentialGetter = useCredentialGetter();
|
||||||
|
|
||||||
|
return useQuery<WorkflowRunStatusApiResponseWithWorkflow>({
|
||||||
|
queryKey: ["workflowRun", workflowRunId],
|
||||||
|
queryFn: async () => {
|
||||||
|
const client = await getClient(credentialGetter, "sans-api-v1");
|
||||||
|
return client
|
||||||
|
.get(`/workflows/runs/${workflowRunId}`)
|
||||||
|
.then((response) => response.data);
|
||||||
|
},
|
||||||
|
refetchInterval: (query) => {
|
||||||
|
if (!query.state.data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (statusIsNotFinalized(query.state.data)) {
|
||||||
|
return 5000;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
// required for OS-level notifications to work (workflow run completion)
|
||||||
|
refetchIntervalInBackground: true,
|
||||||
|
placeholderData: keepPreviousData,
|
||||||
|
refetchOnMount: (query) => {
|
||||||
|
if (!query.state.data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return statusIsRunningOrQueued(query.state.data) ? "always" : false;
|
||||||
|
},
|
||||||
|
refetchOnWindowFocus: (query) => {
|
||||||
|
if (!query.state.data) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return statusIsRunningOrQueued(query.state.data);
|
||||||
|
},
|
||||||
|
enabled: !!workflowRunId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useWorkflowRunWithWorkflowQuery };
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { CodeEditor } from "../components/CodeEditor";
|
import { CodeEditor } from "../components/CodeEditor";
|
||||||
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
|
import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea";
|
||||||
import { useActiveWorkflowRunItem } from "./useActiveWorkflowRunItem";
|
import { useActiveWorkflowRunItem } from "./useActiveWorkflowRunItem";
|
||||||
@@ -18,7 +18,7 @@ function WorkflowPostRunParameters() {
|
|||||||
useWorkflowRunTimelineQuery();
|
useWorkflowRunTimelineQuery();
|
||||||
const [activeItem] = useActiveWorkflowRunItem();
|
const [activeItem] = useActiveWorkflowRunItem();
|
||||||
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
||||||
useWorkflowRunQuery();
|
useWorkflowRunWithWorkflowQuery();
|
||||||
const parameters = workflowRun?.parameters ?? {};
|
const parameters = workflowRun?.parameters ?? {};
|
||||||
|
|
||||||
if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
|
if (workflowRunIsLoading || workflowRunTimelineIsLoading) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -15,8 +14,7 @@ import { statusIsFinalized } from "@/routes/tasks/types";
|
|||||||
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
||||||
import { useBlockScriptsQuery } from "@/routes/workflows/hooks/useBlockScriptsQuery";
|
import { useBlockScriptsQuery } from "@/routes/workflows/hooks/useBlockScriptsQuery";
|
||||||
import { useCacheKeyValuesQuery } from "@/routes/workflows/hooks/useCacheKeyValuesQuery";
|
import { useCacheKeyValuesQuery } from "@/routes/workflows/hooks/useCacheKeyValuesQuery";
|
||||||
import { useWorkflowQuery } from "@/routes/workflows/hooks/useWorkflowQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "@/routes/workflows/hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { useWorkflowRunQuery } from "@/routes/workflows/hooks/useWorkflowRunQuery";
|
|
||||||
import { constructCacheKeyValue } from "@/routes/workflows/editor/utils";
|
import { constructCacheKeyValue } from "@/routes/workflows/editor/utils";
|
||||||
import { getCode, getOrderedBlockLabels } from "@/routes/workflows/utils";
|
import { getCode, getOrderedBlockLabels } from "@/routes/workflows/utils";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
@@ -30,11 +28,9 @@ interface Props {
|
|||||||
function WorkflowRunCode(props?: Props) {
|
function WorkflowRunCode(props?: Props) {
|
||||||
const showCacheKeyValueSelector = props?.showCacheKeyValueSelector ?? false;
|
const showCacheKeyValueSelector = props?.showCacheKeyValueSelector ?? false;
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { workflowPermanentId } = useParams();
|
const { data: workflowRun } = useWorkflowRunWithWorkflowQuery();
|
||||||
const { data: workflowRun } = useWorkflowRunQuery();
|
const workflow = workflowRun?.workflow;
|
||||||
const { data: workflow } = useWorkflowQuery({
|
const workflowPermanentId = workflow?.workflow_permanent_id;
|
||||||
workflowPermanentId,
|
|
||||||
});
|
|
||||||
const cacheKey = workflow?.cache_key ?? "";
|
const cacheKey = workflow?.cache_key ?? "";
|
||||||
const [cacheKeyValue, setCacheKeyValue] = useState(
|
const [cacheKeyValue, setCacheKeyValue] = useState(
|
||||||
cacheKey === ""
|
cacheKey === ""
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FileIcon } from "@radix-ui/react-icons";
|
import { FileIcon } from "@radix-ui/react-icons";
|
||||||
import { CodeEditor } from "../components/CodeEditor";
|
import { CodeEditor } from "../components/CodeEditor";
|
||||||
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { useActiveWorkflowRunItem } from "./useActiveWorkflowRunItem";
|
import { useActiveWorkflowRunItem } from "./useActiveWorkflowRunItem";
|
||||||
import {
|
import {
|
||||||
hasExtractedInformation,
|
hasExtractedInformation,
|
||||||
@@ -17,7 +17,7 @@ function WorkflowRunOutput() {
|
|||||||
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
||||||
useWorkflowRunTimelineQuery();
|
useWorkflowRunTimelineQuery();
|
||||||
const [activeItem] = useActiveWorkflowRunItem();
|
const [activeItem] = useActiveWorkflowRunItem();
|
||||||
const { data: workflowRun } = useWorkflowRunQuery();
|
const { data: workflowRun } = useWorkflowRunWithWorkflowQuery();
|
||||||
|
|
||||||
if (workflowRunTimelineIsLoading) {
|
if (workflowRunTimelineIsLoading) {
|
||||||
return <div>Loading...</div>;
|
return <div>Loading...</div>;
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ import { BrowserStream } from "@/components/BrowserStream";
|
|||||||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||||||
import { ActionScreenshot } from "@/routes/tasks/detail/ActionScreenshot";
|
import { ActionScreenshot } from "@/routes/tasks/detail/ActionScreenshot";
|
||||||
import { statusIsFinalized } from "@/routes/tasks/types";
|
import { statusIsFinalized } from "@/routes/tasks/types";
|
||||||
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { useParams } from "react-router-dom";
|
|
||||||
import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuery";
|
import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuery";
|
||||||
import {
|
import {
|
||||||
isAction,
|
isAction,
|
||||||
@@ -37,17 +36,16 @@ export type WorkflowRunOverviewActiveElement =
|
|||||||
function WorkflowRunOverview() {
|
function WorkflowRunOverview() {
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const active = searchParams.get("active");
|
const active = searchParams.get("active");
|
||||||
const { workflowPermanentId } = useParams<{
|
|
||||||
workflowPermanentId: string;
|
|
||||||
}>();
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
||||||
useWorkflowRunQuery();
|
useWorkflowRunWithWorkflowQuery();
|
||||||
|
|
||||||
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
||||||
useWorkflowRunTimelineQuery();
|
useWorkflowRunTimelineQuery();
|
||||||
|
|
||||||
const workflowRunId = workflowRun?.workflow_run_id;
|
const workflowRunId = workflowRun?.workflow_run_id;
|
||||||
|
const workflow = workflowRun?.workflow;
|
||||||
|
const workflowPermanentId = workflow?.workflow_permanent_id;
|
||||||
|
|
||||||
const invalidateQueries = useCallback(() => {
|
const invalidateQueries = useCallback(() => {
|
||||||
if (workflowRunId) {
|
if (workflowRunId) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { artifactApiBaseUrl } from "@/util/env";
|
import { artifactApiBaseUrl } from "@/util/env";
|
||||||
|
|
||||||
function WorkflowRunRecording() {
|
function WorkflowRunRecording() {
|
||||||
const { data: workflowRun } = useWorkflowRunQuery();
|
const { data: workflowRun } = useWorkflowRunWithWorkflowQuery();
|
||||||
let recordingURL = workflowRun?.recording_url;
|
let recordingURL = workflowRun?.recording_url;
|
||||||
if (recordingURL?.startsWith("file://")) {
|
if (recordingURL?.startsWith("file://")) {
|
||||||
recordingURL = `${artifactApiBaseUrl}/artifact/recording?path=${recordingURL.slice(7)}`;
|
recordingURL = `${artifactApiBaseUrl}/artifact/recording?path=${recordingURL.slice(7)}`;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Skeleton } from "@/components/ui/skeleton";
|
|||||||
import { statusIsFinalized, statusIsNotFinalized } from "@/routes/tasks/types";
|
import { statusIsFinalized, statusIsNotFinalized } from "@/routes/tasks/types";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
import { DotFilledIcon } from "@radix-ui/react-icons";
|
import { DotFilledIcon } from "@radix-ui/react-icons";
|
||||||
import { useWorkflowRunQuery } from "../hooks/useWorkflowRunQuery";
|
import { useWorkflowRunWithWorkflowQuery } from "../hooks/useWorkflowRunWithWorkflowQuery";
|
||||||
import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuery";
|
import { useWorkflowRunTimelineQuery } from "../hooks/useWorkflowRunTimelineQuery";
|
||||||
import {
|
import {
|
||||||
isBlockItem,
|
isBlockItem,
|
||||||
@@ -36,7 +36,7 @@ function WorkflowRunTimeline({
|
|||||||
onBlockItemSelected,
|
onBlockItemSelected,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
const { data: workflowRun, isLoading: workflowRunIsLoading } =
|
||||||
useWorkflowRunQuery();
|
useWorkflowRunWithWorkflowQuery();
|
||||||
|
|
||||||
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
const { data: workflowRunTimeline, isLoading: workflowRunTimelineIsLoading } =
|
||||||
useWorkflowRunTimelineQuery();
|
useWorkflowRunTimelineQuery();
|
||||||
|
|||||||
Reference in New Issue
Block a user