feat(editor): visible feedback for Test run — status pill + View link
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 <id> · View execution →' (auto-clears in 6s) or 'Test run failed: <msg>' (auto-clears in 8s).
This commit is contained in:
@@ -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<void>(`/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<ReturnType<typeof setTimeout> | 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() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{testStatus.kind !== "idle" && (
|
||||
<div
|
||||
role="status"
|
||||
className={
|
||||
"px-4 py-2 text-sm border-b flex items-center gap-2 " +
|
||||
(testStatus.kind === "success"
|
||||
? "bg-emerald-950/50 border-emerald-900 text-emerald-200"
|
||||
: testStatus.kind === "error"
|
||||
? "bg-red-950/50 border-red-900 text-red-200"
|
||||
: "bg-slate-900 border-slate-800 text-slate-300")
|
||||
}
|
||||
>
|
||||
{testStatus.kind === "running" && <>Running test execution…</>}
|
||||
{testStatus.kind === "success" && (
|
||||
<>
|
||||
Started execution <code className="font-mono text-xs">{testStatus.executionId}</code>
|
||||
<Link
|
||||
to={`/workflows/${id}/executions`}
|
||||
className="ml-auto underline hover:text-emerald-100"
|
||||
>
|
||||
View execution →
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
{testStatus.kind === "error" && <>Test run failed: {testStatus.message}</>}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex-1 flex">
|
||||
<NodePalette />
|
||||
<div className="flex-1">
|
||||
|
||||
Reference in New Issue
Block a user