fix(frontend): Update isGraphRunning state when manually executing graphs (#11489)

This PR fixes an issue where the `isGraphRunning` state wasn't being
updated when users manually executed graphs. This caused the UI to not
show proper feedback that a graph was running, such as the running
background animation. The fix adds a `setIsGraphRunning` method to the
graph store and ensures it's called both when starting execution (for
immediate UI feedback) and when errors occur (to reset the state).

### Changes 🏗️

- **Added `setIsGraphRunning` method to graphStore**: Provides a way to
manually control the graph running state
- **Set running state on manual execution**: Updates `isGraphRunning` to
`true` immediately when users click the Run button, providing instant UI
feedback
- **Reset state on execution errors**: Ensures `isGraphRunning` is set
back to `false` if the graph execution fails, preventing the UI from
showing incorrect running state
- **Improved UI responsiveness**: Users now see immediate visual
feedback (like the running background) when they start a graph execution
- **Cleaned up unused import**: Removed unnecessary `flowID` from
useQueryStates in Flow component
- I’ve also changed the colour of the running background to a lighter
shade of purple.

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
- [x] Click Run button and verify the running background appears
immediately
  - [x] Test with graphs that require input values via the input dialog
  - [x] Verify running state is cleared when execution completes
- [x] Test error scenarios and confirm running state is reset on failure
- [x] Confirm all UI elements that depend on `isGraphRunning` update
correctly
- [x] Test multiple consecutive runs to ensure state management is
consistent
This commit is contained in:
Abhimanyu Yadav
2025-12-02 18:12:47 +05:30
committed by GitHub
parent e4102bf0fb
commit b8d67fc2a9
6 changed files with 33 additions and 4 deletions

View File

@@ -19,6 +19,9 @@ export const useRunGraph = () => {
const hasCredentials = useGraphStore(
useShallow((state) => state.hasCredentials),
);
const setIsGraphRunning = useGraphStore(
useShallow((state) => state.setIsGraphRunning),
);
const [openRunInputDialog, setOpenRunInputDialog] = useState(false);
const [{ flowID, flowVersion, flowExecutionID }, setQueryStates] =
@@ -38,6 +41,8 @@ export const useRunGraph = () => {
});
},
onError: (error: any) => {
// Reset running state on error
setIsGraphRunning(false);
toast({
title: (error.detail as string) ?? "An unexpected error occurred.",
description: "An unexpected error occurred.",
@@ -67,6 +72,8 @@ export const useRunGraph = () => {
if (hasInputs() || hasCredentials()) {
setOpenRunInputDialog(true);
} else {
// Optimistically set running state immediately for responsive UI
setIsGraphRunning(true);
await executeGraph({
graphId: flowID ?? "",
graphVersion: flowVersion || null,

View File

@@ -18,6 +18,7 @@ export const useRunInputDialog = ({
const credentialsSchema = useGraphStore(
(state) => state.credentialsInputSchema,
);
const setIsGraphRunning = useGraphStore((state) => state.setIsGraphRunning);
const [openCronSchedulerDialog, setOpenCronSchedulerDialog] = useState(false);
const [inputValues, setInputValues] = useState<Record<string, any>>({});
@@ -41,6 +42,8 @@ export const useRunInputDialog = ({
});
},
onError: (error) => {
// Reset running state on error
setIsGraphRunning(false);
toast({
title: (error.detail as string) ?? "An unexpected error occurred.",
description: "An unexpected error occurred.",
@@ -78,6 +81,8 @@ export const useRunInputDialog = ({
graphVersion: flowVersion || null,
data: { inputs: inputValues, credentials_inputs: credentialValues },
});
// Optimistically set running state immediately for responsive UI
setIsGraphRunning(true);
setIsOpen(false);
};

View File

@@ -107,6 +107,7 @@ export const Flow = () => {
)}
</ReactFlow>
</div>
{/* TODO: Need to update it in future - also do not send executionId as prop - rather use useQueryState inside the component */}
<FloatingReviewsPanel
executionId={flowExecutionID || undefined}
graphId={flowID || undefined}

View File

@@ -21,7 +21,7 @@ export const RunningBackground = () => {
borderWidth: "15px",
borderStyle: "solid",
borderColor: "transparent",
borderImage: "linear-gradient(to right, #BC82F3, #BC82F3) 1",
borderImage: "linear-gradient(to right, #D4A5F9, #D4A5F9) 1",
}}
></div>
<div
@@ -30,7 +30,7 @@ export const RunningBackground = () => {
borderWidth: "10px",
borderStyle: "solid",
borderColor: "transparent",
borderImage: "linear-gradient(to right, #BC82F3, #BC82F3) 1",
borderImage: "linear-gradient(to right, #D4A5F9, #D4A5F9) 1",
}}
></div>
<div
@@ -39,7 +39,7 @@ export const RunningBackground = () => {
borderWidth: "6px",
borderStyle: "solid",
borderColor: "transparent",
borderImage: "linear-gradient(to right, #BC82F3, #BC82F3) 1",
borderImage: "linear-gradient(to right, #D4A5F9, #D4A5F9) 1",
}}
></div>
<div
@@ -48,7 +48,7 @@ export const RunningBackground = () => {
borderWidth: "6px",
borderStyle: "solid",
borderColor: "transparent",
borderImage: "linear-gradient(to right, #BC82F3, #BC82F3) 1",
borderImage: "linear-gradient(to right, #D4A5F9, #D4A5F9) 1",
}}
></div>
</div>

View File

@@ -16,6 +16,7 @@ import { useGraphStore } from "../../../stores/graphStore";
import { useReactFlow } from "@xyflow/react";
import { useControlPanelStore } from "../../../stores/controlPanelStore";
import { useHistoryStore } from "../../../stores/historyStore";
import { AgentExecutionStatus } from "@/app/api/__generated__/models/agentExecutionStatus";
export const useFlow = () => {
const [isLocked, setIsLocked] = useState(false);
@@ -30,6 +31,9 @@ export const useFlow = () => {
const setGraphSchemas = useGraphStore(
useShallow((state) => state.setGraphSchemas),
);
const setGraphExecutionStatus = useGraphStore(
useShallow((state) => state.setGraphExecutionStatus),
);
const updateEdgeBeads = useEdgeStore(
useShallow((state) => state.updateEdgeBeads),
);
@@ -154,6 +158,13 @@ export const useFlow = () => {
customNodes,
]);
// update graph execution status
useEffect(() => {
if (executionDetails) {
setGraphExecutionStatus(executionDetails.status as AgentExecutionStatus);
}
}, [executionDetails]);
useEffect(() => {
if (customNodes.length > 0 && graph?.links) {
const timer = setTimeout(() => {

View File

@@ -5,6 +5,7 @@ interface GraphStore {
graphExecutionStatus: AgentExecutionStatus | undefined;
isGraphRunning: boolean;
setGraphExecutionStatus: (status: AgentExecutionStatus | undefined) => void;
setIsGraphRunning: (isRunning: boolean) => void;
inputSchema: Record<string, any> | null;
credentialsInputSchema: Record<string, any> | null;
@@ -38,6 +39,10 @@ export const useGraphStore = create<GraphStore>((set, get) => ({
});
},
setIsGraphRunning: (isRunning: boolean) => {
set({ isGraphRunning: isRunning });
},
setGraphSchemas: (inputSchema, credentialsInputSchema, outputSchema) =>
set({ inputSchema, credentialsInputSchema, outputSchema }),