fix(web): move autosave hooks above early return (React #310)
Server Deploy / deploy (push) Successful in 1m37s

This commit is contained in:
2026-07-21 11:55:20 +01:00
parent b5f30bc7c8
commit 2b7ef98dff
2 changed files with 28 additions and 24 deletions
+25 -24
View File
@@ -105,14 +105,35 @@ export default function WorkflowBuilder() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [secretGroups]);
if (!wf) {
return <div className="p-8 text-text-secondary">Loading</div>;
}
// Keep a ref to the latest workflow so an in-flight save can tell whether
// the user edited again while the request was on the wire.
wfRef.current = wf;
// Autosave: debounce 800ms after any change to the workflow (step added,
// removed, reordered, or edited) and persist. Diffing the serialized state
// against the last saved snapshot skips no-op saves and the initial load.
// Must stay above the early return below so hook order is stable.
useEffect(() => {
if (!wf || savedSnapshotRef.current === null) return;
if (JSON.stringify(wf) === savedSnapshotRef.current) return;
const t = setTimeout(() => {
save();
}, 800);
return () => clearTimeout(t);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wf]);
// Re-render every 15s so the "Saved … ago" label stays current.
useEffect(() => {
if (!lastSaved) return;
const iv = setInterval(() => setTick((n) => n + 1), 15000);
return () => clearInterval(iv);
}, [lastSaved]);
if (!wf) {
return <div className="p-8 text-text-secondary">Loading</div>;
}
const libById = (sid?: string) => (sid ? library?.find((l) => l.step_id === sid) : undefined);
const sortedSteps = [...wf.steps].sort((a, b) => a.order - b.order);
@@ -162,26 +183,6 @@ export default function WorkflowBuilder() {
}
};
// Autosave: debounce 800ms after any change to the workflow (step added,
// removed, reordered, or edited) and persist. Diffing the serialized state
// against the last saved snapshot skips no-op saves and the initial load.
useEffect(() => {
if (!wf || savedSnapshotRef.current === null) return;
if (JSON.stringify(wf) === savedSnapshotRef.current) return;
const t = setTimeout(() => {
save();
}, 800);
return () => clearTimeout(t);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [wf]);
// Re-render every 15s so the "Saved … ago" label stays current.
useEffect(() => {
if (!lastSaved) return;
const iv = setInterval(() => setTick((n) => n + 1), 15000);
return () => clearInterval(iv);
}, [lastSaved]);
const run = async () => {
setRunning(true);
setError(null);