From 5d04c0a2d465ad10b2642bb8acc3fb9316a1d4b9 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 28 Aug 2026 19:53:34 +0000 Subject: [PATCH] =?UTF-8?q?feat(editor):=20visible=20feedback=20for=20Test?= =?UTF-8?q?=20run=20=E2=80=94=20status=20pill=20+=20View=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before, the Test run button fired and returned an execution id but there was no on-screen confirmation, so users assumed nothing happened. Adds an inline status bar under the toolbar that shows 'Running…', then either 'Started execution · View execution →' (auto-clears in 6s) or 'Test run failed: ' (auto-clears in 8s). --- frontend/src/pages/EditorPage.tsx | 53 ++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/EditorPage.tsx b/frontend/src/pages/EditorPage.tsx index 2a74c31..5b97934 100644 --- a/frontend/src/pages/EditorPage.tsx +++ b/frontend/src/pages/EditorPage.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { Link, useParams } from "react-router-dom"; import type { Edge, Node } from "@xyflow/react"; import { useNodeTypes, useWorkflow } from "@/api/hooks"; @@ -82,6 +82,19 @@ export default function EditorPage() { () => apiFetch(`/api/workflows/${id}/deactivate`, { method: "POST" }), { onSuccess: () => id && invalidate(qk.workflow(id)) }, ); + const [testStatus, setTestStatus] = useState< + | { kind: "idle" } + | { kind: "running" } + | { kind: "success"; executionId: string } + | { kind: "error"; message: string } + >({ kind: "idle" }); + const clearTimer = useRef | null>(null); + useEffect(() => () => { if (clearTimer.current) clearTimeout(clearTimer.current); }, []); + function scheduleClear(ms: number) { + if (clearTimer.current) clearTimeout(clearTimer.current); + clearTimer.current = setTimeout(() => setTestStatus({ kind: "idle" }), ms); + } + const testRun = useApiMutation( () => { const triggers = nodes.filter((n) => @@ -93,6 +106,17 @@ export default function EditorPage() { { method: "POST", body: { startFromNodeId: startFrom, triggerData: {} } }, ); }, + { + onMutate: () => setTestStatus({ kind: "running" }), + onSuccess: (res) => { + setTestStatus({ kind: "success", executionId: res.executionId }); + scheduleClear(6000); + }, + onError: (err) => { + setTestStatus({ kind: "error", message: err instanceof Error ? err.message : String(err) }); + scheduleClear(8000); + }, + }, ); if (wf.isLoading || !wf.data) { @@ -144,6 +168,33 @@ export default function EditorPage() { )} + {testStatus.kind !== "idle" && ( +
+ {testStatus.kind === "running" && <>Running test execution…} + {testStatus.kind === "success" && ( + <> + Started execution {testStatus.executionId} + + View execution → + + + )} + {testStatus.kind === "error" && <>Test run failed: {testStatus.message}} +
+ )}