diff --git a/src/frontend/playwright-report/index.html b/src/frontend/playwright-report/index.html index d65e584e4..e634446de 100644 --- a/src/frontend/playwright-report/index.html +++ b/src/frontend/playwright-report/index.html @@ -1,18 +1,18 @@ - - - + + + Document - - + + - - \ No newline at end of file + + diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 3b88ee14c..2bbab91f7 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -149,7 +149,7 @@ export default function App() { .catch(() => { setFetchError(true); }); - }, 5000); + }, 20000); // Clean up the timer on component unmount return () => { diff --git a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx index a76a15d17..09af96c6f 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/parameterComponent/index.tsx @@ -1,6 +1,6 @@ import { cloneDeep } from "lodash"; import React, { ReactNode, useEffect, useRef, useState } from "react"; -import { Handle, Position } from "reactflow"; +import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import ShadTooltip from "../../../../components/ShadTooltipComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent"; import DictComponent from "../../../../components/dictComponent"; @@ -71,7 +71,7 @@ export default function ParameterComponent({ const groupedEdge = useRef(null); - const setFilterEdge = useTypesStore((state) => state.setFilterEdge); + const setFilterEdge = useFlowStore((state) => state.setFilterEdge); let disabled = edges.some( @@ -124,27 +124,32 @@ export default function ParameterComponent({ renderTooltips(); }; + const updateNodeInternals = useUpdateNodeInternals(); + const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => { if (!data.node) return; if (data.node!.template[name].value !== code) { takeSnapshot(); } - + + setNode(data.id, (oldNode) => { let newNode = cloneDeep(oldNode); - + newNode.data = { ...newNode.data, node: newNodeClass, description: newNodeClass.description ?? data.node!.description, display_name: newNodeClass.display_name ?? data.node!.display_name, }; - + newNode.data.node.template[name].value = code; - + return newNode; }); - + + updateNodeInternals(data.id); + renderTooltips(); }; @@ -268,6 +273,9 @@ export default function ParameterComponent({ state.types); const templates = useTypesStore((state) => state.templates); - const setFilterEdge = useTypesStore((state) => state.setFilterEdge); + const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const reactFlowWrapper = useRef(null); const reactFlowInstance = useFlowStore((state) => state.reactFlowInstance); @@ -64,6 +64,7 @@ export default function Page({ const onEdgesChange = useFlowStore((state) => state.onEdgesChange); const setNodes = useFlowStore((state) => state.setNodes); const setEdges = useFlowStore((state) => state.setEdges); + const cleanFlow = useFlowStore((state) => state.cleanFlow); const deleteNode = useFlowStore((state) => state.deleteNode); const deleteEdge = useFlowStore((state) => state.deleteEdge); const undo = useFlowsManagerStore((state) => state.undo); @@ -77,6 +78,7 @@ export default function Page({ const setLastCopiedSelection = useFlowStore( (state) => state.setLastCopiedSelection ); + const onConnect = useFlowStore((state) => state.onConnect); const position = useRef({ x: 0, y: 0 }); const [lastSelection, setLastSelection] = @@ -170,12 +172,18 @@ export default function Page({ } }, [currentFlowId, reactFlowInstance]); + useEffect(() => { + return () => { + cleanFlow(); + } + }, []) + const onConnectMod = useCallback( (params: Connection) => { takeSnapshot(); onConnect(params); }, - [setEdges, takeSnapshot, addEdge] + [takeSnapshot, onConnect] ); const onNodeDragStart: NodeDragHandler = useCallback(() => { @@ -208,39 +216,6 @@ export default function Page({ } }, []); - const onConnect = useCallback( - (connection: Connection) => { - const newEdges = addEdge( - { - ...connection, - data: { - targetHandle: scapeJSONParse(connection.targetHandle!), - sourceHandle: scapeJSONParse(connection.sourceHandle!), - }, - style: { stroke: "#555" }, - className: - ((scapeJSONParse(connection.targetHandle!) as targetHandleType) - .type === "Text" - ? "stroke-foreground " - : "stroke-foreground ") + " stroke-connection", - animated: - (scapeJSONParse(connection.targetHandle!) as targetHandleType) - .type === "Text", - }, - edges - ); - setEdges(newEdges); - useFlowsManagerStore - .getState() - .autoSaveCurrentFlow( - nodes, - newEdges, - reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } - ); - }, - [nodes, edges, setEdges, reactFlowInstance, addEdge] - ); - const onDrop = useCallback( (event: React.DragEvent) => { event.preventDefault(); @@ -357,7 +332,7 @@ export default function Page({
{Object.keys(templates).length > 0 && - Object.keys(types).length > 0 ? ( + Object.keys(types).length > 0 ? (
state.data); const templates = useTypesStore((state) => state.templates); - const getFilterEdge = useTypesStore((state) => state.getFilterEdge); - const setFilterEdge = useTypesStore((state) => state.setFilterEdge); + const getFilterEdge = useFlowStore((state) => state.getFilterEdge); + const setFilterEdge = useFlowStore((state) => state.setFilterEdge); const uploadFlow = useFlowsManagerStore((state) => state.uploadFlow); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const hasStore = useStoreStore((state) => state.hasStore); diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index 910ec81b6..d8338428b 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -250,6 +250,53 @@ const useFlowStore = create((set, get) => ({ setLastCopiedSelection: (newSelection) => { set({ lastCopiedSelection: newSelection }); }, + cleanFlow: () => { + set({ + nodes: [], + edges: [], + flowState: undefined, + sseData: {}, + isBuilt: false, + getFilterEdge: [], + }); + }, + setFilterEdge: (newState) => { + set({ getFilterEdge: newState }); + }, + getFilterEdge: [], + onConnect: (connection) => { + let newEdges: Edge[] = [] + get().setEdges((oldEdges) => { + newEdges = addEdge( + { + ...connection, + data: { + targetHandle: scapeJSONParse(connection.targetHandle!), + sourceHandle: scapeJSONParse(connection.sourceHandle!), + }, + style: { stroke: "#555" }, + className: + ((scapeJSONParse(connection.targetHandle!) as targetHandleType) + .type === "Text" + ? "stroke-foreground " + : "stroke-foreground ") + " stroke-connection", + animated: + (scapeJSONParse(connection.targetHandle!) as targetHandleType) + .type === "Text", + }, + oldEdges + ); + return newEdges; + + }) + useFlowsManagerStore + .getState() + .autoSaveCurrentFlow( + get().nodes, + newEdges, + get().reactFlowInstance?.getViewport() ?? { x: 0, y: 0, zoom: 1 } + ); + }, })); export default useFlowStore; diff --git a/src/frontend/src/stores/typesStore.ts b/src/frontend/src/stores/typesStore.ts index e2ab3dfc4..90f2c5b9b 100644 --- a/src/frontend/src/stores/typesStore.ts +++ b/src/frontend/src/stores/typesStore.ts @@ -9,7 +9,6 @@ export const useTypesStore = create((set, get) => ({ types: {}, templates: {}, data: {}, - getFilterEdge: [], getTypes: () => { return new Promise(async (resolve, reject) => { getAll() @@ -44,7 +43,5 @@ export const useTypesStore = create((set, get) => ({ let newChange = typeof change === "function" ? change(get().data) : change; set({ data: newChange }); }, - setFilterEdge: (newState) => { - set({ getFilterEdge: newState }); - }, + })); diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index 9c331892f..273a1958f 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -1,4 +1,5 @@ import { + Connection, Edge, Node, OnEdgesChange, @@ -49,4 +50,8 @@ export type FlowStoreType = { ) => void; isBuilt: boolean; setIsBuilt: (isBuilt: boolean) => void; + cleanFlow: () => void; + setFilterEdge: (newState) => void; + getFilterEdge: any[]; + onConnect: (connection: Connection) => void; }; diff --git a/src/frontend/src/types/zustand/types/index.ts b/src/frontend/src/types/zustand/types/index.ts index f18d8b22e..133afbda1 100644 --- a/src/frontend/src/types/zustand/types/index.ts +++ b/src/frontend/src/types/zustand/types/index.ts @@ -8,6 +8,4 @@ export type TypesStoreType = { data: APIDataType; setData: (newState: {}) => void; getTypes: () => Promise; - setFilterEdge: (newState) => void; - getFilterEdge: any[]; }; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index 7c8a0b7c0..8d222349a 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -474,6 +474,8 @@ export function convertArrayToObj(arrayOfObjects) { export function hasDuplicateKeys(array) { const keys = {}; + // Transforms an empty object into an object array without opening the 'editNode' modal to prevent the flow build from breaking. + if (!Array.isArray(array)) array = [{"": ""}]; for (const obj of array) { for (const key in obj) { if (keys[key]) { @@ -486,6 +488,8 @@ export function hasDuplicateKeys(array) { } export function hasEmptyKey(objArray) { + // Transforms an empty object into an array without opening the 'editNode' modal to prevent the flow build from breaking. + if (!Array.isArray(objArray)) objArray = []; for (const obj of objArray) { for (const key in obj) { if (obj.hasOwnProperty(key) && key === "") {