replace workflow parameter React Context with a zustand store; use everywhere (#3187)

This commit is contained in:
Jonathan Dobson
2025-08-14 08:04:48 -04:00
committed by GitHub
parent 2556d04e70
commit a2f6b9e539
16 changed files with 269 additions and 263 deletions

View File

@@ -1,11 +1,10 @@
import { PlusIcon } from "@radix-ui/react-icons";
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { useState, useEffect } from "react";
import { Cross2Icon } from "@radix-ui/react-icons";
import "./workflow-block-input-set.css";
import { useWorkflowParametersState } from "@/routes/workflows/editor/useWorkflowParametersState";
type Props = {
onChange: (parameterKeys: Set<string>) => void;
nodeId: string;
@@ -16,7 +15,7 @@ function WorkflowBlockInputSet(props: Props) {
const { nodeId, onChange, values } = props;
const [parameterKeys, setParameterKeys] = useState<Set<string>>(values);
const hasKeys = parameterKeys.size > 0;
const [workflowParameters] = useWorkflowParametersState();
const { parameters: workflowParameters } = useWorkflowParametersStore();
const availableParameterKeys = new Set(
workflowParameters.map((parameter) => parameter.key),
);

View File

@@ -7,7 +7,7 @@ import {
} from "@/components/ui/select";
import { Skeleton } from "@/components/ui/skeleton";
import { useCredentialsQuery } from "../hooks/useCredentialsQuery";
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { WorkflowParameterValueType } from "../types/workflowTypes";
import { PlusIcon } from "@radix-ui/react-icons";
import {
@@ -24,7 +24,7 @@ type Props = {
function CredentialParameterSourceSelector({ value, onChange }: Props) {
const { data: credentials, isFetching } = useCredentialsQuery();
const { setIsOpen, setType } = useCredentialModalState();
const [workflowParameters] = useWorkflowParametersState();
const { parameters: workflowParameters } = useWorkflowParametersStore();
const workflowParametersOfTypeCredentialId = workflowParameters.filter(
(parameter) =>
parameter.parameterType === "workflow" &&

View File

@@ -1,5 +1,5 @@
import { useNodes } from "@xyflow/react";
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { AppNode, isWorkflowBlockNode } from "../editor/nodes";
import { getOutputParameterKey } from "../editor/workflowEditorUtils";
import {
@@ -16,7 +16,7 @@ type Props = {
};
function SourceParameterKeySelector({ value, onChange }: Props) {
const [workflowParameters] = useWorkflowParametersState();
const { parameters: workflowParameters } = useWorkflowParametersStore();
const nodes = useNodes<AppNode>();
const contextParameterKeys = workflowParameters
.filter((parameter) => parameter.parameterType !== "credential")

View File

@@ -16,6 +16,7 @@ import {
useWorkflowSave,
type WorkflowSaveData,
} from "@/store/WorkflowHasChangesStore";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { useWorkflowTitleStore } from "@/store/WorkflowTitleStore";
import { ReloadIcon } from "@radix-ui/react-icons";
import {
@@ -223,7 +224,7 @@ type Props = {
onNodesChange: (changes: Array<NodeChange<AppNode>>) => void;
onEdgesChange: (changes: Array<EdgeChange>) => void;
initialTitle: string;
initialParameters: ParametersState;
// initialParameters: ParametersState;
workflow: WorkflowApiResponse;
onDebuggableBlockCountChange: (count: number) => void;
onMouseDownCapture?: () => void;
@@ -238,7 +239,7 @@ function FlowRenderer({
onNodesChange,
onEdgesChange,
initialTitle,
initialParameters,
// initialParameters,
workflow,
onDebuggableBlockCountChange,
onMouseDownCapture,
@@ -247,7 +248,8 @@ function FlowRenderer({
const reactFlowInstance = useReactFlow();
const debugStore = useDebugStore();
const { title, initializeTitle } = useWorkflowTitleStore();
const [parameters] = useState<ParametersState>(initialParameters);
// const [parameters] = useState<ParametersState>(initialParameters);
const parameters = useWorkflowParametersStore((state) => state.parameters);
const nodesInitialized = useNodesInitialized();
const [shouldConstrainPan, setShouldConstrainPan] = useState(false);
const onNodesChangeTimeoutRef = useRef<NodeJS.Timeout | null>(null);

View File

@@ -1,3 +1,4 @@
import { useEffect } from "react";
import { useParams } from "react-router-dom";
import { ReactFlowProvider } from "@xyflow/react";
@@ -6,6 +7,7 @@ import { WorkflowSettings } from "../types/workflowTypes";
import { getElements } from "./workflowEditorUtils";
import { getInitialParameters } from "./utils";
import { Workspace } from "./Workspace";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
function WorkflowDebugger() {
const { workflowPermanentId } = useParams();
@@ -13,6 +15,17 @@ function WorkflowDebugger() {
workflowPermanentId,
});
const setParameters = useWorkflowParametersStore(
(state) => state.setParameters,
);
useEffect(() => {
if (workflow) {
const initialParameters = getInitialParameters(workflow);
setParameters(initialParameters);
}
}, [workflow, setParameters]);
if (!workflow) {
return null;
}
@@ -42,7 +55,6 @@ function WorkflowDebugger() {
<Workspace
initialEdges={elements.edges}
initialNodes={elements.nodes}
initialParameters={getInitialParameters(workflow)}
initialTitle={workflow.title}
showBrowser={true}
workflow={workflow}

View File

@@ -1,10 +1,12 @@
import { ReactFlowProvider } from "@xyflow/react";
import { useParams } from "react-router-dom";
import { useEffect } from "react";
import { useWorkflowQuery } from "../hooks/useWorkflowQuery";
import { getElements } from "./workflowEditorUtils";
import { LogoMinimized } from "@/components/LogoMinimized";
import { WorkflowSettings } from "../types/workflowTypes";
import { useGlobalWorkflowsQuery } from "../hooks/useGlobalWorkflowsQuery";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { getInitialParameters } from "./utils";
import { Workspace } from "./Workspace";
@@ -18,6 +20,17 @@ function WorkflowEditor() {
const { data: globalWorkflows, isLoading: isGlobalWorkflowsLoading } =
useGlobalWorkflowsQuery();
const setParameters = useWorkflowParametersStore(
(state) => state.setParameters,
);
useEffect(() => {
if (workflow) {
const initialParameters = getInitialParameters(workflow);
setParameters(initialParameters);
}
}, [workflow, setParameters]);
if (isLoading || isGlobalWorkflowsLoading) {
return (
<div className="flex h-screen w-full items-center justify-center">
@@ -60,7 +73,6 @@ function WorkflowEditor() {
<Workspace
initialEdges={elements.edges}
initialNodes={elements.nodes}
initialParameters={getInitialParameters(workflow)}
initialTitle={workflow.title}
showBrowser={false}
workflow={workflow}

View File

@@ -1,13 +0,0 @@
import { createContext } from "react";
import { ParametersState } from "./types";
type WorkflowParametersState = [
ParametersState,
React.Dispatch<React.SetStateAction<ParametersState>>,
];
const WorkflowParametersStateContext = createContext<
WorkflowParametersState | undefined
>(undefined);
export { WorkflowParametersStateContext };

View File

@@ -44,7 +44,6 @@ import { FlowRenderer, type FlowRendererProps } from "./FlowRenderer";
import { AppNode, isWorkflowBlockNode, WorkflowBlockNode } from "./nodes";
import { WorkflowNodeLibraryPanel } from "./panels/WorkflowNodeLibraryPanel";
import { WorkflowParametersPanel } from "./panels/WorkflowParametersPanel";
import { ParametersState } from "./types";
import { getWorkflowErrors } from "./workflowEditorUtils";
import { WorkflowHeader } from "./WorkflowHeader";
import {
@@ -55,16 +54,12 @@ import {
layout,
startNode,
} from "./workflowEditorUtils";
import { WorkflowParametersStateContext } from "./WorkflowParametersStateContext";
const Constants = {
NewBrowserCooldown: 30000,
} as const;
type Props = Pick<
FlowRendererProps,
"initialTitle" | "initialParameters" | "workflow"
> & {
type Props = Pick<FlowRendererProps, "initialTitle" | "workflow"> & {
initialNodes: Array<AppNode>;
initialEdges: Array<Edge>;
showBrowser?: boolean;
@@ -82,15 +77,12 @@ function Workspace({
initialNodes,
initialEdges,
initialTitle,
initialParameters,
showBrowser = false,
workflow,
}: Props) {
const { blockLabel, workflowPermanentId } = useParams();
const { workflowPanelState, setWorkflowPanelState, closeWorkflowPanel } =
useWorkflowPanelStore();
const [parameters, setParameters] =
useState<ParametersState>(initialParameters);
const debugStore = useDebugStore();
const [debuggableBlockCount, setDebuggableBlockCount] = useState(0);
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
@@ -335,208 +327,204 @@ function Workspace({
}
return (
<WorkflowParametersStateContext.Provider
value={[parameters, setParameters]}
>
<div className="relative h-full w-full">
<Dialog
open={openDialogue}
onOpenChange={(open) => {
if (!open && cycleBrowser.isPending) {
return;
}
setOpenDialogue(open);
}}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Cycle (Get a new browser)</DialogTitle>
<DialogDescription>
<div className="pb-2 pt-4 text-sm text-slate-400">
{cycleBrowser.isPending ? (
<>
Cooking you up a fresh browser...
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
</>
) : (
"Abandon this browser for a new one. Are you sure?"
)}
</div>
</DialogDescription>
</DialogHeader>
<DialogFooter>
{!cycleBrowser.isPending && (
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
)}
<Button
variant="default"
onClick={() => {
cycleBrowser.mutate(workflowPermanentId!);
}}
disabled={cycleBrowser.isPending}
>
Yes, Continue{" "}
{cycleBrowser.isPending && (
<ReloadIcon className="ml-2 size-4 animate-spin" />
<div className="relative h-full w-full">
<Dialog
open={openDialogue}
onOpenChange={(open) => {
if (!open && cycleBrowser.isPending) {
return;
}
setOpenDialogue(open);
}}
>
<DialogContent>
<DialogHeader>
<DialogTitle>Cycle (Get a new browser)</DialogTitle>
<DialogDescription>
<div className="pb-2 pt-4 text-sm text-slate-400">
{cycleBrowser.isPending ? (
<>
Cooking you up a fresh browser...
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
</>
) : (
"Abandon this browser for a new one. Are you sure?"
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
</DialogDescription>
</DialogHeader>
<DialogFooter>
{!cycleBrowser.isPending && (
<DialogClose asChild>
<Button variant="secondary">Cancel</Button>
</DialogClose>
)}
<Button
variant="default"
onClick={() => {
cycleBrowser.mutate(workflowPermanentId!);
}}
disabled={cycleBrowser.isPending}
>
Yes, Continue{" "}
{cycleBrowser.isPending && (
<ReloadIcon className="ml-2 size-4 animate-spin" />
)}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* header panel */}
<div
className="absolute left-6 right-6 top-8 h-20"
style={{ zIndex: rankedItems.header ?? 3 }}
onMouseDownCapture={() => {
promote("header");
}}
>
<WorkflowHeader
debuggableBlockCount={debuggableBlockCount}
saving={workflowChangesStore.saveIsPending}
parametersPanelOpen={
{/* header panel */}
<div
className="absolute left-6 right-6 top-8 h-20"
style={{ zIndex: rankedItems.header ?? 3 }}
onMouseDownCapture={() => {
promote("header");
}}
>
<WorkflowHeader
debuggableBlockCount={debuggableBlockCount}
saving={workflowChangesStore.saveIsPending}
parametersPanelOpen={
workflowPanelState.active &&
workflowPanelState.content === "parameters"
}
onParametersClick={() => {
if (
workflowPanelState.active &&
workflowPanelState.content === "parameters"
}
onParametersClick={() => {
if (
workflowPanelState.active &&
workflowPanelState.content === "parameters"
) {
closeWorkflowPanel();
promote("header");
} else {
setWorkflowPanelState({
active: true,
content: "parameters",
});
promote("dropdown");
}
}}
onSave={async () => {
const errors = getWorkflowErrors(nodes);
if (errors.length > 0) {
toast({
title: "Can not save workflow because of errors:",
description: (
<div className="space-y-2">
{errors.map((error) => (
<p key={error}>{error}</p>
))}
</div>
),
variant: "destructive",
});
return;
}
await saveWorkflow.mutateAsync();
}}
onRun={() => {
) {
closeWorkflowPanel();
promote("header");
}}
/>
</div>
{/* sub panels */}
{workflowPanelState.active && (
<div
className="absolute right-6 top-[7.75rem]"
style={{ zIndex: rankedItems.dropdown ?? 2 }}
onMouseDownCapture={() => {
} else {
setWorkflowPanelState({
active: true,
content: "parameters",
});
promote("dropdown");
}}
>
{workflowPanelState.content === "parameters" && (
<WorkflowParametersPanel
onMouseDownCapture={() => {
promote("dropdown");
}}
/>
)}
{workflowPanelState.content === "nodeLibrary" && (
<WorkflowNodeLibraryPanel
onMouseDownCapture={() => {
promote("dropdown");
}}
onNodeClick={(props) => {
addNode(props);
}}
/>
)}
</div>
)}
}
}}
onSave={async () => {
const errors = getWorkflowErrors(nodes);
if (errors.length > 0) {
toast({
title: "Can not save workflow because of errors:",
description: (
<div className="space-y-2">
{errors.map((error) => (
<p key={error}>{error}</p>
))}
</div>
),
variant: "destructive",
});
return;
}
await saveWorkflow.mutateAsync();
}}
onRun={() => {
closeWorkflowPanel();
promote("header");
}}
/>
</div>
{debugStore.isDebugMode && (
<div
className="absolute right-6 top-[8.5rem] h-[calc(100vh-9.5rem)]"
style={{ zIndex: rankedItems.history ?? 1 }}
onMouseDownCapture={() => {
closeWorkflowPanel();
promote("history");
}}
>
<div className="pointer-events-none absolute right-0 top-0 flex h-full w-[400px] flex-col items-end justify-end">
<div className="pointer-events-auto relative h-full w-full overflow-hidden rounded-xl border-2 border-slate-500">
<WorkflowDebuggerRun />
</div>
{/* sub panels */}
{workflowPanelState.active && (
<div
className="absolute right-6 top-[7.75rem]"
style={{ zIndex: rankedItems.dropdown ?? 2 }}
onMouseDownCapture={() => {
promote("dropdown");
}}
>
{workflowPanelState.content === "parameters" && (
<WorkflowParametersPanel
onMouseDownCapture={() => {
promote("dropdown");
}}
/>
)}
{workflowPanelState.content === "nodeLibrary" && (
<WorkflowNodeLibraryPanel
onMouseDownCapture={() => {
promote("dropdown");
}}
onNodeClick={(props) => {
addNode(props);
}}
/>
)}
</div>
)}
{debugStore.isDebugMode && (
<div
className="absolute right-6 top-[8.5rem] h-[calc(100vh-9.5rem)]"
style={{ zIndex: rankedItems.history ?? 1 }}
onMouseDownCapture={() => {
closeWorkflowPanel();
promote("history");
}}
>
<div className="pointer-events-none absolute right-0 top-0 flex h-full w-[400px] flex-col items-end justify-end">
<div className="pointer-events-auto relative h-full w-full overflow-hidden rounded-xl border-2 border-slate-500">
<WorkflowDebuggerRun />
</div>
</div>
)}
</div>
)}
{/* infinite canvas */}
<FlowRenderer
nodes={nodes}
edges={edges}
setNodes={setNodes}
setEdges={setEdges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
initialTitle={initialTitle}
initialParameters={initialParameters}
workflow={workflow}
onDebuggableBlockCountChange={(c) => setDebuggableBlockCount(c)}
onMouseDownCapture={() => promote("infiniteCanvas")}
zIndex={rankedItems.infiniteCanvas}
/>
{/* infinite canvas */}
<FlowRenderer
nodes={nodes}
edges={edges}
setNodes={setNodes}
setEdges={setEdges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
initialTitle={initialTitle}
// initialParameters={initialParameters}
workflow={workflow}
onDebuggableBlockCountChange={(c) => setDebuggableBlockCount(c)}
onMouseDownCapture={() => promote("infiniteCanvas")}
zIndex={rankedItems.infiniteCanvas}
/>
{/* browser */}
{showBrowser && (
<FloatingWindow
title={browserTitle}
bounded={false}
initialPosition={initialBrowserPosition}
initialWidth={initialWidth}
initialHeight={initialHeight}
showMaximizeButton={true}
showMinimizeButton={true}
showPowerButton={blockLabel === undefined && showPowerButton}
showReloadButton={true}
zIndex={rankedItems.browserWindow ?? 4}
// --
onCycle={handleOnCycle}
onFocus={() => promote("browserWindow")}
>
{activeDebugSession &&
activeDebugSession.browser_session_id &&
!cycleBrowser.isPending ? (
<BrowserStream
interactive={interactor === "human"}
browserSessionId={activeDebugSession.browser_session_id}
/>
) : (
<div className="flex h-full w-full flex-col items-center justify-center gap-2 pb-2 pt-4 text-sm text-slate-400">
Connecting to your browser...
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
</div>
)}
</FloatingWindow>
)}
</div>
</WorkflowParametersStateContext.Provider>
{/* browser */}
{showBrowser && (
<FloatingWindow
title={browserTitle}
bounded={false}
initialPosition={initialBrowserPosition}
initialWidth={initialWidth}
initialHeight={initialHeight}
showMaximizeButton={true}
showMinimizeButton={true}
showPowerButton={blockLabel === undefined && showPowerButton}
showReloadButton={true}
zIndex={rankedItems.browserWindow ?? 4}
// --
onCycle={handleOnCycle}
onFocus={() => promote("browserWindow")}
>
{activeDebugSession &&
activeDebugSession.browser_session_id &&
!cycleBrowser.isPending ? (
<BrowserStream
interactive={interactor === "human"}
browserSessionId={activeDebugSession.browser_session_id}
/>
) : (
<div className="flex h-full w-full flex-col items-center justify-center gap-2 pb-2 pt-4 text-sm text-slate-400">
Connecting to your browser...
<AnimatedWave text=".‧₊˚ ⋅ ✨★ ‧₊˚ ⋅" />
</div>
)}
</FloatingWindow>
)}
</div>
);
}

View File

@@ -9,7 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton";
import { useCredentialsQuery } from "@/routes/workflows/hooks/useCredentialsQuery";
import CloudContext from "@/store/CloudContext";
import { useContext } from "react";
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { CredentialsModal } from "@/routes/credentials/CredentialsModal";
import { PlusIcon } from "@radix-ui/react-icons";
import {
@@ -30,8 +30,10 @@ type Props = {
function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
const { setIsOpen, setType } = useCredentialModalState();
const nodes = useNodes<AppNode>();
const [workflowParameters, setWorkflowParameters] =
useWorkflowParametersState();
const {
parameters: workflowParameters,
setParameters: setWorkflowParameters,
} = useWorkflowParametersStore();
const credentialParameters = workflowParameters.filter(
(parameter) =>
parameter.parameterType === "credential" ||
@@ -170,16 +172,14 @@ function LoginBlockCredentialSelector({ nodeId, value, onChange }: Props) {
<CredentialsModal
onCredentialCreated={(id) => {
onChange?.(id);
setWorkflowParameters((prev) => {
return [
...prev,
{
parameterType: "credential",
credentialId: id,
key: id,
},
];
});
setWorkflowParameters([
...workflowParameters,
{
parameterType: "credential",
credentialId: id,
key: id,
},
]);
}}
/>
</>

View File

@@ -1,5 +1,5 @@
import { MultiSelect } from "@/components/ui/multi-select";
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { HelpTooltip } from "@/components/HelpTooltip";
import { helpTooltips } from "../../helpContent";
@@ -14,7 +14,7 @@ function ParametersMultiSelect({
parameters,
onParametersChange,
}: Props) {
const [workflowParameters] = useWorkflowParametersState();
const { parameters: workflowParameters } = useWorkflowParametersStore();
const keys = workflowParameters
.map((parameter) => parameter.key)
.concat(availableOutputParameters);

View File

@@ -1,5 +1,5 @@
import { MultiSelect } from "@/components/ui/multi-select";
import { useWorkflowParametersState } from "../../useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { HelpTooltip } from "@/components/HelpTooltip";
import { helpTooltips } from "../../helpContent";
@@ -14,7 +14,7 @@ function TaskNodeParametersPanel({
parameters,
onParametersChange,
}: Props) {
const [workflowParameters] = useWorkflowParametersState();
const { parameters: workflowParameters } = useWorkflowParametersStore();
const keys = workflowParameters
.map((parameter) => parameter.key)
.concat(availableOutputParameters);

View File

@@ -1,5 +1,5 @@
import { useEdges, useNodes } from "@xyflow/react";
import { useWorkflowParametersState } from "../useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { AppNode } from ".";
import { getAvailableOutputParameterKeys } from "../workflowEditorUtils";
import { PlusIcon } from "@radix-ui/react-icons";
@@ -15,7 +15,7 @@ type Props = {
function WorkflowBlockParameterSelect({ nodeId, onAdd }: Props) {
const [content, setContent] = useState("parameters");
const [workflowParameters] = useWorkflowParametersState();
const { parameters: workflowParameters } = useWorkflowParametersStore();
const nodes = useNodes<AppNode>();
const edges = useEdges();
const outputParameterKeys = getAvailableOutputParameterKeys(

View File

@@ -1,5 +1,4 @@
import { useState } from "react";
import { useWorkflowParametersState } from "../useWorkflowParametersState";
import { WorkflowParameterAddPanel } from "./WorkflowParameterAddPanel";
import { ParametersState } from "../types";
import { WorkflowParameterEditPanel } from "./WorkflowParameterEditPanel";
@@ -26,6 +25,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { useReactFlow } from "@xyflow/react";
import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
import { ScrollArea, ScrollAreaViewport } from "@/components/ui/scroll-area";
import {
WorkflowEditorParameterType,
@@ -44,8 +44,10 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
const setHasChanges = useWorkflowHasChangesStore(
(state) => state.setHasChanges,
);
const [workflowParameters, setWorkflowParameters] =
useWorkflowParametersState();
const {
parameters: workflowParameters,
setParameters: setWorkflowParameters,
} = useWorkflowParametersStore();
const [operationPanelState, setOperationPanelState] = useState<{
active: boolean;
operation: "add" | "edit";

View File

@@ -1,14 +0,0 @@
import { useContext } from "react";
import { WorkflowParametersStateContext } from "./WorkflowParametersStateContext";
function useWorkflowParametersState() {
const value = useContext(WorkflowParametersStateContext);
if (value === undefined) {
throw new Error(
"useWorkflowParametersState must be used within a WorkflowParametersStateProvider",
);
}
return value;
}
export { useWorkflowParametersState };

View File

@@ -6,7 +6,7 @@ import {
getUpdatedParametersAfterLabelUpdateForSourceParameterKey,
} from "../editor/workflowEditorUtils";
import { useState } from "react";
import { useWorkflowParametersState } from "../editor/useWorkflowParametersState";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
type Props = {
id: string;
@@ -17,8 +17,10 @@ function useNodeLabelChangeHandler({ id, initialValue }: Props) {
const [label, setLabel] = useState(initialValue);
const nodes = useNodes<AppNode>();
const { setNodes } = useReactFlow();
const [workflowParameters, setWorkflowParameters] =
useWorkflowParametersState();
const {
parameters: workflowParameters,
setParameters: setWorkflowParameters,
} = useWorkflowParametersStore();
function handleLabelChange(value: string) {
const existingLabels = nodes

View File

@@ -0,0 +1,16 @@
import { create } from "zustand";
import { ParametersState } from "@/routes/workflows/editor/types";
interface WorkflowParametersStore {
parameters: ParametersState;
setParameters: (parameters: ParametersState) => void;
}
const useWorkflowParametersStore = create<WorkflowParametersStore>((set) => {
return {
parameters: [],
setParameters: (parameters: ParametersState) => set({ parameters }),
};
});
export { useWorkflowParametersStore };