From 8f3d7c8ea2ded72fdee277be642fb3bafc70e1df Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Thu, 30 Jan 2025 21:28:28 +0800 Subject: [PATCH 1/2] Put success/failure indicators in block items of timelines (#1680) --- .../workflows/workflowRun/ActionCard.tsx | 2 +- .../workflows/workflowRun/ThoughtCard.tsx | 2 +- .../WorkflowRunTimelineBlockItem.tsx | 58 ++++++++++++++----- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/ActionCard.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/ActionCard.tsx index 86202f8820..db93a08cb3 100644 --- a/skyvern-frontend/src/routes/workflows/workflowRun/ActionCard.tsx +++ b/skyvern-frontend/src/routes/workflows/workflowRun/ActionCard.tsx @@ -19,7 +19,7 @@ function ActionCard({ action, onClick, active, index }: Props) { if (element && active) { element.scrollIntoView({ behavior: "smooth", - block: "center", + block: "start", }); } // this should only run once at mount. diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/ThoughtCard.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/ThoughtCard.tsx index 10d3c538e7..9d6551ae0b 100644 --- a/skyvern-frontend/src/routes/workflows/workflowRun/ThoughtCard.tsx +++ b/skyvern-frontend/src/routes/workflows/workflowRun/ThoughtCard.tsx @@ -15,7 +15,7 @@ function ThoughtCard({ thought, onClick, active }: Props) { if (element && active) { element.scrollIntoView({ behavior: "smooth", - block: "center", + block: "start", }); } // this should only run once at mount. diff --git a/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunTimelineBlockItem.tsx b/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunTimelineBlockItem.tsx index d00cc141cc..fea16fca7f 100644 --- a/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunTimelineBlockItem.tsx +++ b/skyvern-frontend/src/routes/workflows/workflowRun/WorkflowRunTimelineBlockItem.tsx @@ -1,4 +1,9 @@ -import { CubeIcon, ExternalLinkIcon } from "@radix-ui/react-icons"; +import { + CheckCircledIcon, + CrossCircledIcon, + CubeIcon, + ExternalLinkIcon, +} from "@radix-ui/react-icons"; import { workflowBlockTitle } from "../editor/nodes/types"; import { WorkflowBlockIcon } from "../editor/nodes/WorkflowBlockIcon"; import { @@ -15,6 +20,7 @@ import { cn } from "@/util/utils"; import { isTaskVariantBlock } from "../types/workflowTypes"; import { Link } from "react-router-dom"; import { useCallback } from "react"; +import { Status } from "@/api/types"; type Props = { activeItem: WorkflowRunOverviewActiveElement; @@ -55,13 +61,23 @@ function WorkflowRunTimelineBlockItem({ ) { element.scrollIntoView({ behavior: "smooth", - block: "center", + block: "start", }); } // this should only run once at mount. // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + const showStatusIndicator = block.status !== null; + const showSuccessIndicator = + showStatusIndicator && block.status === Status.Completed; + const showFailureIndicator = + showStatusIndicator && + (block.status === Status.Failed || + block.status === Status.Terminated || + block.status === Status.TimedOut || + block.status === Status.Canceled); + return (
{workflowBlockTitle[block.block_type]}
-
- {showDiagnosticLink ? ( - -
- - Diagnostics -
- - ) : ( - <> - - Block - +
+ {showFailureIndicator && ( +
+ +
+ )} + {showSuccessIndicator && ( +
+ +
)} +
+ {showDiagnosticLink ? ( + +
+ + Diagnostics +
+ + ) : ( + <> + + Block + + )} +
{block.description ? ( From 4c50f6f49d7516f5eec20489a2baeae43c95bf19 Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Thu, 30 Jan 2025 22:09:43 +0800 Subject: [PATCH 2/2] Improve wait time input (#1681) --- .../editor/nodes/WaitNode/WaitNode.tsx | 16 ++++------------ .../workflows/editor/nodes/WaitNode/types.ts | 4 ++-- .../workflows/editor/workflowEditorUtils.ts | 18 +++++++++++++++--- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/WaitNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/WaitNode.tsx index f1b89e81ca..c138145876 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/WaitNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/WaitNode.tsx @@ -10,8 +10,8 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import { WorkflowBlockIcon } from "../WorkflowBlockIcon"; import type { WaitNode } from "./types"; -import { WorkflowBlockInput } from "@/components/WorkflowBlockInput"; import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow"; +import { Input } from "@/components/ui/input"; function WaitNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); @@ -89,18 +89,10 @@ function WaitNode({ id, data }: NodeProps) { ) : null} - - { - if (!editable) { - return; - } - handleChange("waitInSeconds", Number(value)); + onChange={(event) => { + handleChange("waitInSeconds", event.target.value); }} className="nopan text-xs" /> diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/types.ts b/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/types.ts index 7720aea576..9063f75c92 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/types.ts +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/WaitNode/types.ts @@ -2,7 +2,7 @@ import type { Node } from "@xyflow/react"; import { NodeBaseData } from "../types"; export type WaitNodeData = NodeBaseData & { - waitInSeconds: number; + waitInSeconds: string; }; export type WaitNode = Node; @@ -11,7 +11,7 @@ export const waitNodeDefaultData: WaitNodeData = { label: "", continueOnFailure: false, editable: true, - waitInSeconds: 1, + waitInSeconds: "1", }; export function isWaitNode(node: Node): node is WaitNode { diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 8d627f5db9..f215b7d4b4 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -82,7 +82,7 @@ import { isExtractionNode, } from "./nodes/ExtractionNode/types"; import { loginNodeDefaultData } from "./nodes/LoginNode/types"; -import { waitNodeDefaultData } from "./nodes/WaitNode/types"; +import { isWaitNode, waitNodeDefaultData } from "./nodes/WaitNode/types"; import { fileDownloadNodeDefaultData } from "./nodes/FileDownloadNode/types"; import { ProxyLocation } from "@/api/types"; import { pdfParserNodeDefaultData } from "./nodes/PDFParserNode/types"; @@ -301,7 +301,7 @@ function convertToNode( type: "wait", data: { ...commonData, - waitInSeconds: block.wait_sec ?? 1, + waitInSeconds: String(block.wait_sec ?? 1), }, }; } @@ -962,7 +962,7 @@ function getWorkflowBlock(node: WorkflowBlockNode): BlockYAML { return { ...base, block_type: "wait", - wait_sec: node.data.waitInSeconds, + wait_sec: Number(node.data.waitInSeconds), }; } case "fileDownload": { @@ -1809,6 +1809,18 @@ function getWorkflowErrors(nodes: Array): Array { } }); + const waitNodes = nodes.filter(isWaitNode); + waitNodes.forEach((node) => { + const waitTimeString = node.data.waitInSeconds.trim(); + + const decimalRegex = new RegExp("^\\d+$"); + const isNumber = decimalRegex.test(waitTimeString); + + if (!isNumber) { + errors.push(`${node.data.label}: Invalid input for wait time.`); + } + }); + return errors; }