diff --git a/skyvern-frontend/package-lock.json b/skyvern-frontend/package-lock.json index 9f4343011..3dafd0575 100644 --- a/skyvern-frontend/package-lock.json +++ b/skyvern-frontend/package-lock.json @@ -16,6 +16,7 @@ "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", @@ -1125,6 +1126,36 @@ } } }, + "node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collapsible": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", + "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-id": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-arrow": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz", @@ -1372,25 +1403,24 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz", - "integrity": "sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz", + "integrity": "sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg==", "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/primitive": "1.0.1", - "@radix-ui/react-compose-refs": "1.0.1", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-id": "1.0.1", - "@radix-ui/react-presence": "1.0.1", - "@radix-ui/react-primitive": "1.0.3", - "@radix-ui/react-use-controllable-state": "1.0.1", - "@radix-ui/react-use-layout-effect": "1.0.1" + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -1401,6 +1431,163 @@ } } }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-presence": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", + "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", diff --git a/skyvern-frontend/package.json b/skyvern-frontend/package.json index f84f83a5a..ac625fde7 100644 --- a/skyvern-frontend/package.json +++ b/skyvern-frontend/package.json @@ -24,6 +24,7 @@ "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-aspect-ratio": "^1.0.3", "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-collapsible": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", diff --git a/skyvern-frontend/src/api/types.ts b/skyvern-frontend/src/api/types.ts index fd252ad69..8406c2e53 100644 --- a/skyvern-frontend/src/api/types.ts +++ b/skyvern-frontend/src/api/types.ts @@ -277,6 +277,7 @@ export type WorkflowRunStatusApiResponse = { screenshot_urls: Array | null; recording_url: string | null; outputs: Record | null; + failure_reason: string | null; }; export type TaskGenerationApiResponse = { diff --git a/skyvern-frontend/src/components/ui/collapsible.tsx b/skyvern-frontend/src/components/ui/collapsible.tsx new file mode 100644 index 000000000..5c28cbcc3 --- /dev/null +++ b/skyvern-frontend/src/components/ui/collapsible.tsx @@ -0,0 +1,9 @@ +import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"; + +const Collapsible = CollapsiblePrimitive.Root; + +const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger; + +const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent; + +export { Collapsible, CollapsibleTrigger, CollapsibleContent }; diff --git a/skyvern-frontend/src/routes/workflows/WorkflowBlockCollapsibleContent.tsx b/skyvern-frontend/src/routes/workflows/WorkflowBlockCollapsibleContent.tsx new file mode 100644 index 000000000..1a9bcf256 --- /dev/null +++ b/skyvern-frontend/src/routes/workflows/WorkflowBlockCollapsibleContent.tsx @@ -0,0 +1,188 @@ +import { Status, TaskApiResponse } from "@/api/types"; +import { StatusBadge } from "@/components/StatusBadge"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible"; +import { TableCell, TableRow } from "@/components/ui/table"; +import { basicLocalTimeFormat, basicTimeFormat } from "@/util/timeFormat"; +import { ChevronDownIcon, ChevronRightIcon } from "@radix-ui/react-icons"; +import { useState } from "react"; +import { cn } from "@/util/utils"; +import { CodeEditor } from "./components/CodeEditor"; +import { AutoResizingTextarea } from "@/components/AutoResizingTextarea/AutoResizingTextarea"; + +type Props = { + task: TaskApiResponse; + onNavigate: (event: React.MouseEvent, id: string) => void; +}; + +function WorkflowBlockCollapsibleContent({ task, onNavigate }: Props) { + const [open, setOpen] = useState(false); + const [activeTab, setActiveTab] = useState(0); + + const showExtractedInformation = task?.status === Status.Completed; + const extractedInformation = showExtractedInformation ? ( + + ) : null; + + const showFailureReason = + task?.status === Status.Failed || + task?.status === Status.Terminated || + task?.status === Status.TimedOut; + const failureReason = showFailureReason ? ( + + ) : null; + + return ( + + <> + + + +
+ {open ? ( + + ) : ( + + )} +
+
+
+ + {task.request.title} + + { + onNavigate(event, task.task_id); + }} + > + {task.task_id} + + onNavigate(event, task.task_id)} + > + {task.request.url} + + onNavigate(event, task.task_id)} + > + + + onNavigate(event, task.task_id)} + title={basicTimeFormat(task.created_at)} + > + {basicLocalTimeFormat(task.created_at)} + +
+ + + +
+
+
{ + setActiveTab(0); + }} + > + {showExtractedInformation + ? "Extracted Information" + : showFailureReason + ? "Failure Reason" + : ""} +
+
{ + setActiveTab(1); + }} + > + Navigation Goal +
+
{ + setActiveTab(2); + }} + > + Parameters +
+
+
+ {activeTab === 0 && + (showExtractedInformation + ? extractedInformation + : showFailureReason + ? failureReason + : null)} + {activeTab === 1 && ( + + )} + {activeTab === 2 && ( + + )} +
+
+
+
+
+ +
+ ); +} + +export { WorkflowBlockCollapsibleContent }; diff --git a/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx b/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx index 177f6bd17..5c21586d1 100644 --- a/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx +++ b/skyvern-frontend/src/routes/workflows/WorkflowRun.tsx @@ -8,6 +8,16 @@ import { StatusBadge } from "@/components/StatusBadge"; import { ZoomableImage } from "@/components/ZoomableImage"; import { AspectRatio } from "@/components/ui/aspect-ratio"; import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { @@ -33,8 +43,6 @@ import { useCredentialGetter } from "@/hooks/useCredentialGetter"; import { copyText } from "@/util/copyText"; import { apiBaseUrl, envCredential } from "@/util/env"; import { - basicLocalTimeFormat, - basicTimeFormat, localTimeFormatWithShortDate, timeFormatWithShortDate, } from "@/util/timeFormat"; @@ -60,25 +68,15 @@ import { useParams, useSearchParams, } from "react-router-dom"; -import { TaskActions } from "../tasks/list/TaskActions"; import { TaskListSkeletonRows } from "../tasks/list/TaskListSkeletonRows"; import { statusIsFinalized, statusIsNotFinalized, statusIsRunningOrQueued, } from "../tasks/types"; +import { WorkflowBlockCollapsibleContent } from "./WorkflowBlockCollapsibleContent"; import { CodeEditor } from "./components/CodeEditor"; import { useWorkflowQuery } from "./hooks/useWorkflowQuery"; -import { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; type StreamMessage = { task_id: string; @@ -199,6 +197,8 @@ function WorkflowRun() { const workflowRunIsFinalized = workflowRun && statusIsFinalized(workflowRun); + const showStream = workflowRun && statusIsNotFinalized(workflowRun); + useEffect(() => { if (!workflowRunIsRunningOrQueued) { return; @@ -336,25 +336,37 @@ function WorkflowRun() { const parameters = workflowRun?.parameters ?? {}; + const title = workflowIsLoading ? ( + + ) : ( +

{workflow?.title}

+ ); + + const workflowFailureReason = workflowRun?.failure_reason ? ( +
+
Workflow Failure Reason
+
{workflowRun.failure_reason}
+
+ ) : null; + return (
-

{workflowRunId}

+ {title} {workflowRunIsLoading ? ( ) : workflowRun ? ( ) : null}
-

- {workflowIsLoading ? ( - - ) : ( - workflow?.title - )} -

+

{workflowRunId}

@@ -442,7 +454,8 @@ function WorkflowRun() { )}
- {workflowRun && statusIsNotFinalized(workflowRun) && ( + {workflowFailureReason} + {showStream && (
{getStream()} @@ -507,17 +520,18 @@ function WorkflowRun() {
- + - - ID + + + Task Title + ID URL Status - + Created At - @@ -525,7 +539,7 @@ function WorkflowRun() { ) : workflowTasks?.length === 0 ? ( - No tasks + Could not find any tasks ) : ( workflowTasks @@ -534,44 +548,11 @@ function WorkflowRun() { ) .map((task) => { return ( - - - handleNavigate(event, task.task_id) - } - > - {task.task_id} - - - handleNavigate(event, task.task_id) - } - > - {task.request.url} - - - handleNavigate(event, task.task_id) - } - > - - - - handleNavigate(event, task.task_id) - } - title={basicTimeFormat(task.created_at)} - > - {basicLocalTimeFormat(task.created_at)} - - - - - + ); }) )} @@ -620,8 +601,10 @@ function WorkflowRun() { return (
- {typeof value === "string" ? ( - + {typeof value === "string" || + typeof value === "number" || + typeof value === "boolean" ? ( + ) : (