From 8a4a346736e5e583f267a818164972f17d1f77f0 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Thu, 16 May 2024 19:01:15 -0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20(codeTabsComponent/index.tsx):=20ad?= =?UTF-8?q?d=20support=20for=20exporting=20code=20as=20a=20file=20?= =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(codeTabsComponent/index.tsx):=20refactor?= =?UTF-8?q?=20code=20to=20use=20a=20switch=20statement=20instead=20of=20mu?= =?UTF-8?q?ltiple=20if=20conditions=20for=20different=20template=20field?= =?UTF-8?q?=20types=20=F0=9F=94=A7=20(codeTabsComponent/index.tsx):=20fix?= =?UTF-8?q?=20import=20statements=20and=20remove=20unused=20imports=20?= =?UTF-8?q?=F0=9F=93=9D=20(codeTabsComponent/index.tsx):=20add=20comments?= =?UTF-8?q?=20to=20improve=20code=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📝 (codeTabsComponent/index.tsx): refactor code to use a switch statement instead of multiple if-else conditions for better readability and maintainability ♻️ (codeTabsComponent/index.tsx): refactor code to use a Case component for each condition in the switch statement to improve code organization and readability ✨ (index.tsx): remove unused import of React from "react" to improve code cleanliness ♻️ (index.tsx): refactor code to remove unused imports and variables, improve code readability and organization 📝 (index.tsx): add missing import of Case component from "../../../../shared/components/caseComponent" 🔧 (index.tsx): add missing import of TooltipRenderComponent from "../tooltipRenderComponent" ✅ (index.tsx): add missing hooks and functions to improve code functionality and maintainability 📝 (index.tsx): Refactor code to improve readability and maintainability 📝 (index.tsx): refactor the rendering logic in the ParameterComponent to improve readability and maintainability 🔧 (index.tsx): fix access to nested properties in the data object by using optional chaining operator (?.) to prevent potential errors when accessing undefined properties ✨ (tooltipRenderComponent): add a new component for rendering tooltips in the genericNode component 📝 (use-fetch-data-on-mount): add a new hook for fetching data on component mount 📝 (use-handle-new-value): add a new hook for handling new values in the component 📝 (use-handle-node-class): add a new hook for handling node class in the component 📝 (use-handle-refresh-buttons): add a new hook for handling refresh button press in the component ✨ (editNodeModal/index.tsx): import Case component to handle conditional rendering of different input components based on template type 📝 (editNodeModal/index.tsx): add comments to explain the purpose of the code and provide context for future developers ♻️ (editNodeModal/index.tsx): refactor code to use the Case component for conditional rendering instead of multiple if statements 📝 (file.js): update code formatting and indentation for better readability 📝 (editNodeModal/index.tsx): update code to fix a bug related to accessing nested properties in myData.node object ♻️ (editNodeModal/index.tsx): refactor code to use conditional rendering with Case component for better readability and maintainability ✨ (caseComponent/index.tsx): add a new component called Case that conditionally renders its children based on a given condition 📝 (components/index.ts): add an optional display_name property to the groupedObjType interface ♻️ (components/index.ts): remove unnecessary commas and fix indentation in the codeTabsPropsType interface 🐛 (reactflowUtils.ts): remove unused parameter 'edges' in isValidConnection function ♻️ (reactflowUtils.ts): refactor scapeJSONParse and scapeJSONStringfy functions to remove unnecessary exclamation marks 🐛 (reactflowUtils.ts): fix bug in updateIds function where it doesn't update the sourceHandle and targetHandle correctly 🐛 (reactflowUtils.ts): fix bug in validateNode function where it doesn't handle empty keys correctly 🐛 (reactflowUtils.ts): fix bug in validateNode function where it doesn't handle duplicate keys correctly 🐛 (reactflowUtils.ts): fix bug in validateNode function where it doesn't handle dict type correctly 🐛 (reactflowUtils.ts): fix bug in validateNodes function where it doesn't handle empty nodes correctly 🐛 (reactflowUtils.ts): fix bug in updateEdges function where it doesn't update the className correctly 🐛 (reactflowUtils.ts): fix bug in handleKeyDown function where it doesn't handle control+backspace on Windows/Linux correctly 🐛 (reactflowUtils.ts): fix bug in handleOnlyIntegerInput function where it doesn't handle decimal point correctly 🐛 (reactflowUtils.ts): fix bug in getConnectedNodes function where it doesn't return the correct connected nodes 🐛 (reactflowUtils.ts): fix bug in convertObjToArray function where it doesn't handle non-dict type correctly 🐛 (reactflowUtils.ts): fix bug in generateFlow function where it doesn't filter out edges correctly 🐛 (reactflowUtils.ts): fix bug in reconnectEdges function where it doesn't update the sourceHandle correctly 🐛 (reactflowUtils.ts): fix bug in filterFlow function where it doesn't filter out nodes and edges correctly 🐛 (reactflowUtils.ts): fix bug in validateSelection function where it doesn't handle selection mode correctly 📝 (reactflowUtils.ts): remove trailing commas and unnecessary whitespace to improve code readability and consistency ♻️ (reactflowUtils.ts): refactor validateSelection function to remove duplicate code and improve readability ♻️ (reactflowUtils.ts): refactor generateNodeTemplate function to simplify code and improve readability ♻️ (reactflowUtils.ts): refactor isHandleConnected function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor generateNodeFromFlow function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor connectedInputNodesOnHandle function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor updateProxyIdsOnTemplate function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor updateEdgesIds function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor expandGroupNode function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor getGroupStatus function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor createFlowComponent function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor updateComponentNameAndType function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor removeFileNameFromComponents function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor extractFieldsFromComponenents function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor downloadFlow function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor downloadFlows function to remove unnecessary parentheses and improve readability ♻️ (reactflowUtils.ts): refactor createNewFlow function to remove unnecessary parentheses and improve readability --- .../components/codeTabsComponent/index.tsx | 347 ++++++++----- .../components/parameterComponent/index.tsx | 481 ++++++------------ .../tooltipRenderComponent/index.tsx | 91 ++++ .../hooks/use-fetch-data-on-mount.tsx | 54 ++ .../hooks/use-handle-new-value.tsx | 75 +++ .../hooks/use-handle-node-class.tsx | 40 ++ .../hooks/use-handle-refresh-buttons.tsx | 39 ++ .../src/modals/editNodeModal/index.tsx | 186 ++++--- .../shared/components/caseComponent/index.tsx | 15 + src/frontend/src/types/components/index.ts | 1 + src/frontend/src/utils/reactflowUtils.ts | 3 +- 11 files changed, 791 insertions(+), 541 deletions(-) create mode 100644 src/frontend/src/customNodes/genericNode/components/tooltipRenderComponent/index.tsx create mode 100644 src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx create mode 100644 src/frontend/src/customNodes/hooks/use-handle-new-value.tsx create mode 100644 src/frontend/src/customNodes/hooks/use-handle-node-class.tsx create mode 100644 src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx create mode 100644 src/frontend/src/shared/components/caseComponent/index.tsx diff --git a/src/frontend/src/components/codeTabsComponent/index.tsx b/src/frontend/src/components/codeTabsComponent/index.tsx index 0b08aaccd..eac1c784e 100644 --- a/src/frontend/src/components/codeTabsComponent/index.tsx +++ b/src/frontend/src/components/codeTabsComponent/index.tsx @@ -26,6 +26,8 @@ import { TabsTrigger, } from "../../components/ui/tabs"; import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants"; +import ExportModal from "../../modals/exportModal"; +import { Case } from "../../shared/components/caseComponent"; import { useDarkStore } from "../../stores/darkStore"; import useFlowStore from "../../stores/flowStore"; import { codeTabsPropsType } from "../../types/components"; @@ -43,7 +45,6 @@ import KeypairListComponent from "../keypairListComponent"; import ShadTooltip from "../shadTooltipComponent"; import { Label } from "../ui/label"; import { Switch } from "../ui/switch"; -import ExportModal from "../../modals/exportModal"; export default function CodeTabsComponent({ flow, @@ -87,6 +88,10 @@ export default function CodeTabsComponent({ }); }; + const type = (node, templateParam) => { + return node.data.node.template[templateParam].type; + }; + const downloadAsFile = () => { const fileExtension = tabs[activeTab].language || ".txt"; const suggestedFileName = `${"generated-code."}${fileExtension}`; @@ -276,107 +281,71 @@ export default function CodeTabsComponent({
- {node.data.node.template[ - templateField - ].type === "str" && - !node.data.node.template[ - templateField - ].options ? ( -
- {node.data.node.template[ + { - setData((old) => { - let newInputList = - cloneDeep(old); - newInputList![ - i - ].data.node.template[ - templateField - ].value = target; - return newInputList; - }); - tweaks?.buildTweakObject!( - node["data"]["id"], - target, - node.data.node.template[ - templateField - ], - ); - }} - /> - ) : node.data.node.template[ + ].options + } + > + - + { - setData((old) => { - let newInputList = - cloneDeep(old); - newInputList![ - i - ].data.node.template[ - templateField - ].value = target; - return newInputList; - }); - tweaks?.buildTweakObject!( - node["data"]["id"], - target, - node.data.node - .template[ - templateField - ], - ); - }} - /> -
- ) : ( - { + setData((old) => { + let newInputList = + cloneDeep(old); + newInputList![ + i + ].data.node.template[ + templateField + ].value = target; + return newInputList; + }); + tweaks?.buildTweakObject!( + node["data"]["id"], + target, node.data.node.template[ templateField - ].password ?? false - } + ], + ); + }} + /> + + + +
+ - )} -
- ) : node.data.node.template[ - templateField - ].type === "bool" ? ( +
+ + + + { + setData((old) => { + let newInputList = + cloneDeep(old); + newInputList![ + i + ].data.node.template[ + templateField + ].value = target; + return newInputList; + }); + tweaks?.buildTweakObject!( + node["data"]["id"], + target, + node.data.node.template[ + templateField + ], + ); + }} + /> + + + +
{" "}
- ) : node.data.node.template[ - templateField - ].type === "file" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "float" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "str" && - node.data.node.template[ - templateField - ].options ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "int" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "prompt" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "code" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "dict" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "NestedDict" ? ( +
+ +
- ) : node.data.node.template[ - templateField - ].type === "Any" ? ( - "-" - ) : ( -
- )} +
+ + + <>- +
diff --git a/src/frontend/src/customNodes/genericNode/components/parameterComponent/index.tsx b/src/frontend/src/customNodes/genericNode/components/parameterComponent/index.tsx index dbcb1e3dc..c87c97741 100644 --- a/src/frontend/src/customNodes/genericNode/components/parameterComponent/index.tsx +++ b/src/frontend/src/customNodes/genericNode/components/parameterComponent/index.tsx @@ -1,5 +1,5 @@ import { cloneDeep } from "lodash"; -import React, { ReactNode, useEffect, useRef, useState } from "react"; +import { ReactNode, useEffect, useRef, useState } from "react"; import { Handle, Position, useUpdateNodeInternals } from "reactflow"; import CodeAreaComponent from "../../../../components/codeAreaComponent"; import DictComponent from "../../../../components/dictComponent"; @@ -18,20 +18,15 @@ import ToggleShadComponent from "../../../../components/toggleShadComponent"; import { Button } from "../../../../components/ui/button"; import { RefreshButton } from "../../../../components/ui/refreshButton"; import { - INPUT_HANDLER_HOVER, LANGFLOW_SUPPORTED_TYPES, - OUTPUT_HANDLER_HOVER, TOOLTIP_EMPTY, } from "../../../../constants/constants"; +import { Case } from "../../../../shared/components/caseComponent"; import useAlertStore from "../../../../stores/alertStore"; import useFlowStore from "../../../../stores/flowStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { useTypesStore } from "../../../../stores/typesStore"; -import { - APIClassType, - ResponseErrorDetailAPI, - ResponseErrorTypeAPI, -} from "../../../../types/api"; +import { APIClassType } from "../../../../types/api"; import { ParameterComponentType } from "../../../../types/components"; import { debouncedHandleUpdateValues, @@ -44,12 +39,13 @@ import { isValidConnection, scapedJSONStringfy, } from "../../../../utils/reactflowUtils"; -import { - nodeColors, - nodeIconsLucide, - nodeNames, -} from "../../../../utils/styleUtils"; +import { nodeColors } from "../../../../utils/styleUtils"; import { classNames, groupByFamily } from "../../../../utils/utils"; +import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount"; +import useHandleOnNewValue from "../../../hooks/use-handle-new-value"; +import useHandleNodeClass from "../../../hooks/use-handle-node-class"; +import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons"; +import TooltipRenderComponent from "../tooltipRenderComponent"; export default function ParameterComponent({ left, @@ -75,175 +71,69 @@ export default function ParameterComponent({ const nodes = useFlowStore((state) => state.nodes); const edges = useFlowStore((state) => state.edges); const setNode = useFlowStore((state) => state.setNode); - + const myData = useTypesStore((state) => state.data); + const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const [isLoading, setIsLoading] = useState(false); + const updateNodeInternals = useUpdateNodeInternals(); + const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const flow = currentFlow?.data?.nodes ?? null; - const groupedEdge = useRef(null); - const setFilterEdge = useFlowStore((state) => state.setFilterEdge); + const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue( + data, + name, + takeSnapshot, + handleUpdateValues, + debouncedHandleUpdateValues, + setNode, + renderTooltips, + isLoading, + setIsLoading, + ); + + const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass( + data, + name, + takeSnapshot, + setNode, + updateNodeInternals, + renderTooltips, + ); + + const { handleRefreshButtonPress: handleRefreshButtonPressHook } = + useHandleRefreshButtonPress(setIsLoading, setNode, renderTooltips); + let disabled = edges.some( (edge) => edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), ) ?? false; - const myData = useTypesStore((state) => state.data); - - const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); - const handleRefreshButtonPress = async (name, data) => { - setIsLoading(true); - try { - let newTemplate = await handleUpdateValues(name, data); - - if (newTemplate) { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template = newTemplate; - return newNode; - }); - } - } catch (error) { - let responseError = error as ResponseErrorDetailAPI; - - setErrorData({ - title: "Error while updating the Component", - list: [responseError.response.data.detail ?? "Unknown error"], - }); - } - setIsLoading(false); - renderTooltips(); + handleRefreshButtonPressHook(name, data); }; - useEffect(() => { - async function fetchData() { - if ( - (data.node?.template[name]?.real_time_refresh || - data.node?.template[name]?.refresh_button) && - // options can be undefined but not an empty array - (data.node?.template[name]?.options?.length ?? 0) === 0 - ) { - setIsLoading(true); - try { - let newTemplate = await handleUpdateValues(name, data); - - if (newTemplate) { - setNode(data.id, (oldNode) => { - let newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - newNode.data.node.template = newTemplate; - return newNode; - }); - } - } catch (error) { - let responseError = error as ResponseErrorDetailAPI; - - setErrorData({ - title: "Error while updating the Component", - list: [responseError.response.data.detail ?? "Unknown error"], - }); - } - setIsLoading(false); - renderTooltips(); - } - } - fetchData(); - }, []); + useFetchDataOnMount( + data, + name, + handleUpdateValues, + setNode, + renderTooltips, + setIsLoading, + ); const handleOnNewValue = async ( newValue: string | string[] | boolean | Object[], skipSnapshot: boolean | undefined = false, ): Promise => { - const nodeTemplate = data.node!.template[name]; - const currentValue = nodeTemplate.value; - - if (currentValue !== newValue && !skipSnapshot) { - takeSnapshot(); - } - - const shouldUpdate = - data.node?.template[name].real_time_refresh && - !data.node?.template[name].refresh_button && - currentValue !== newValue; - - const typeToDebounce = nodeTemplate.type; - - nodeTemplate.value = newValue; - - let newTemplate; - if (shouldUpdate) { - setIsLoading(true); - try { - if (["int"].includes(typeToDebounce)) { - newTemplate = await handleUpdateValues(name, data); - } else { - newTemplate = await debouncedHandleUpdateValues(name, data); - } - } catch (error) { - let responseError = error as ResponseErrorTypeAPI; - setErrorData({ - title: "Error while updating the Component", - list: [responseError.response.data.detail.error ?? "Unknown error"], - }); - } - setIsLoading(false); - } - - setNode(data.id, (oldNode) => { - const newNode = cloneDeep(oldNode); - newNode.data = { - ...newNode.data, - }; - - if (data.node?.template[name].real_time_refresh && newTemplate) { - newNode.data.node.template = newTemplate; - } else { - newNode.data.node.template[name].value = newValue; - } - - return newNode; - }); - - renderTooltips(); + handleOnNewValueHook(newValue, skipSnapshot); }; - 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(); + handleNodeClassHook(newNodeClass, code); }; - const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); - useEffect(() => { // @ts-ignore infoHtml.current = ( @@ -264,88 +154,7 @@ export default function ParameterComponent({ if (groupedObj && groupedObj.length > 0) { //@ts-ignore refHtml.current = groupedObj.map((item, index) => { - const Icon: any = - nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"]; - - return ( -
- {index === 0 && ( - {left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER} - )} - 0 ? "mt-2 flex items-center" : "mt-3 flex items-center", - )} - > -
- -
- - {nodeNames[item.family] ?? "Other"}{" "} - {item?.display_name && item?.display_name?.length > 0 ? ( - - {" "} - {item.display_name === "" ? "" : " - "} - {item.display_name.split(", ").length > 2 - ? item.display_name.split(", ").map((el, index) => ( - - - {index === - item.display_name.split(", ").length - 1 - ? el - : (el += `, `)} - - - )) - : item.display_name} - - ) : ( - - {" "} - {item.type === "" ? "" : " - "} - {item.type.split(", ").length > 2 - ? item.type.split(", ").map((el, index) => ( - - - {index === item.type.split(", ").length - 1 - ? el - : (el += `, `)} - - - )) - : item.type} - - )} - -
-
- ); + return ; }); } else { //@ts-ignore @@ -354,6 +163,7 @@ export default function ParameterComponent({ ); } } + // If optionalHandle is an empty list, then it is not an optional handle if (optionalHandle && optionalHandle.length === 0) { optionalHandle = null; @@ -362,6 +172,9 @@ export default function ParameterComponent({ useEffect(() => { renderTooltips(); }, [tooltipTitle, flow]); + + console.log(left === true && type === "dict"); + return !showNode ? ( left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? ( <> @@ -427,11 +240,12 @@ export default function ParameterComponent({ (left ? "" : " justify-end") } > - {!left && data.node?.frozen && ( +
- )} +
+ {proxy ? ( {proxy.id}}> @@ -477,45 +291,37 @@ export default function ParameterComponent({ }`} type={left ? "target" : "source"} position={left ? Position.Left : Position.Right} - key={ - proxy - ? scapedJSONStringfy({ ...id, proxy }) - : scapedJSONStringfy(id) - } - id={ - proxy - ? scapedJSONStringfy({ ...id, proxy }) - : scapedJSONStringfy(id) - } + key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)} + id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)} isValidConnection={(connection) => isValidConnection(connection, nodes, edges) } className={classNames( - left ? "-ml-0.5 " : "-mr-0.5 ", + left ? "-ml-0.5" : "-mr-0.5", "h-3 w-3 rounded-full border-2 bg-background", )} - style={{ - borderColor: color ?? nodeColors.unknown, - }} - onClick={() => { - setFilterEdge(groupedEdge.current); - }} - > + style={{ borderColor: color ?? nodeColors.unknown }} + onClick={() => setFilterEdge(groupedEdge.current)} + /> )} - {left === true && - type === "str" && - !data.node?.template[name].options ? ( +
- {data.node?.template[name].list ? ( +
@@ -523,39 +329,27 @@ export default function ParameterComponent({ componentName={name} disabled={disabled} value={ - !data.node.template[name].value || - data.node.template[name].value === "" + !data.node!.template[name]?.value || + data.node!.template[name]?.value === "" ? [""] - : data.node.template[name].value + : data.node!.template[name]?.value } onChange={handleOnNewValue} /> - {/* {data.node?.template[name].refresh_button && ( -
- -
- )} */}
- ) : data.node?.template[name].multiline ? ( +
+
- {data.node?.template[name].refresh_button && ( + {data.node?.template[name]?.refresh_button && (
)}
- ) : ( + +
- {data.node?.template[name].refresh_button && ( + {data.node?.template[name]?.refresh_button && (
)}
- )} +
- ) : left === true && type === "bool" ? ( +
+ +
- ) : left === true && type === "float" ? ( +
+ +
- ) : left === true && - type === "str" && - (data.node?.template[name].options || - data.node?.template[name]?.real_time_refresh) ? ( - // TODO: Improve CSS +
+ +
- {data.node?.template[name].refresh_button && ( + {data.node?.template[name]?.refresh_button && (
)}
- ) : left === true && type === "code" ? ( + + +
- ) : left === true && type === "file" ? ( +
+ +
{ data.node!.template[name].file_path = filePath; }} >
- ) : left === true && type === "int" ? ( +
+ +
- ) : left === true && type === "prompt" ? ( +
+ +
- ) : left === true && type === "NestedDict" ? ( +
+ +
- ) : left === true && type === "dict" ? ( +
+ +
{ const valueToNumbers = convertValuesToNumbers(newValue); setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers)); - // if data.node?.template[name].list is true, then the value is an array of objects + // if data.node?.template[name]?.list is true, then the value is an array of objects // else we need to get the first object of the array - if (data.node?.template[name].list) { + if (data.node?.template[name]?.list) { handleOnNewValue(valueToNumbers); } else handleOnNewValue(valueToNumbers[0]); }} - isList={data.node?.template[name].list ?? false} + isList={data.node?.template[name]?.list ?? false} />
- ) : ( - <> - )} +
); diff --git a/src/frontend/src/customNodes/genericNode/components/tooltipRenderComponent/index.tsx b/src/frontend/src/customNodes/genericNode/components/tooltipRenderComponent/index.tsx new file mode 100644 index 000000000..ed2760161 --- /dev/null +++ b/src/frontend/src/customNodes/genericNode/components/tooltipRenderComponent/index.tsx @@ -0,0 +1,91 @@ +import React from "react"; +import { + INPUT_HANDLER_HOVER, + OUTPUT_HANDLER_HOVER, +} from "../../../../constants/constants"; +import { + nodeColors, + nodeIconsLucide, + nodeNames, +} from "../../../../utils/styleUtils"; +import { classNames } from "../../../../utils/utils"; + +const TooltipRenderComponent = ({ item, index, left }) => { + const Icon = nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"]; + + return ( +
+ {index === 0 && ( + {left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER} + )} + 0 ? "mt-2 flex items-center" : "mt-3 flex items-center", + )} + > +
+ +
+ + {nodeNames[item.family] ?? "Other"}{" "} + {item?.display_name && item?.display_name?.length > 0 ? ( + + {" "} + {item.display_name === "" ? "" : " - "} + {item.display_name.split(", ").length > 2 + ? item.display_name.split(", ").map((el, index) => ( + + + {index === item.display_name.split(", ").length - 1 + ? el + : (el += `, `)} + + + )) + : item.display_name} + + ) : ( + + {" "} + {item.type === "" ? "" : " - "} + {item.type.split(", ").length > 2 + ? item.type.split(", ").map((el, index) => ( + + + {index === item.type.split(", ").length - 1 + ? el + : (el += `, `)} + + + )) + : item.type} + + )} + +
+
+ ); +}; + +export default TooltipRenderComponent; diff --git a/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx b/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx new file mode 100644 index 000000000..3fc3fbe72 --- /dev/null +++ b/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx @@ -0,0 +1,54 @@ +import { cloneDeep } from "lodash"; +import { useEffect } from "react"; +import useAlertStore from "../../stores/alertStore"; +import { ResponseErrorDetailAPI } from "../../types/api"; + +const useFetchDataOnMount = ( + data, + name, + handleUpdateValues, + setNode, + renderTooltips, + setIsLoading, +) => { + const setErrorData = useAlertStore((state) => state.setErrorData); + + useEffect(() => { + async function fetchData() { + if ( + (data.node?.template[name]?.real_time_refresh || + data.node?.template[name]?.refresh_button) && + // options can be undefined but not an empty array + (data.node?.template[name]?.options?.length ?? 0) === 0 + ) { + setIsLoading(true); + try { + let newTemplate = await handleUpdateValues(name, data); + + if (newTemplate) { + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + newNode.data = { + ...newNode.data, + }; + newNode.data.node.template = newTemplate; + return newNode; + }); + } + } catch (error) { + let responseError = error as ResponseErrorDetailAPI; + + setErrorData({ + title: "Error while updating the Component", + list: [responseError.response.data.detail ?? "Unknown error"], + }); + } + setIsLoading(false); + renderTooltips(); + } + } + fetchData(); + }, []); // Empty dependency array ensures that this effect runs only once, on mount +}; + +export default useFetchDataOnMount; diff --git a/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx b/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx new file mode 100644 index 000000000..7de830eda --- /dev/null +++ b/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx @@ -0,0 +1,75 @@ +import { cloneDeep } from "lodash"; +import useAlertStore from "../../stores/alertStore"; +import { ResponseErrorTypeAPI } from "../../types/api"; + +const useHandleOnNewValue = ( + data, + name, + takeSnapshot, + handleUpdateValues, + debouncedHandleUpdateValues, + setNode, + renderTooltips, + isLoading, + setIsLoading, +) => { + const setErrorData = useAlertStore((state) => state.setErrorData); + + const handleOnNewValue = async (newValue, skipSnapshot = false) => { + const nodeTemplate = data.node!.template[name]; + const currentValue = nodeTemplate.value; + + if (currentValue !== newValue && !skipSnapshot) { + takeSnapshot(); + } + + const shouldUpdate = + data.node?.template[name].real_time_refresh && + !data.node?.template[name].refresh_button && + currentValue !== newValue; + + const typeToDebounce = nodeTemplate.type; + + nodeTemplate.value = newValue; + + let newTemplate; + if (shouldUpdate) { + setIsLoading(true); + try { + if (["int"].includes(typeToDebounce)) { + newTemplate = await handleUpdateValues(name, data); + } else { + newTemplate = await debouncedHandleUpdateValues(name, data); + } + } catch (error) { + let responseError = error as ResponseErrorTypeAPI; + setErrorData({ + title: "Error while updating the Component", + list: [responseError.response.data.detail.error ?? "Unknown error"], + }); + } + setIsLoading(false); + } + + setNode(data.id, (oldNode) => { + const newNode = cloneDeep(oldNode); + newNode.data = { + ...newNode.data, + }; + + if (data.node?.template[name].real_time_refresh && newTemplate) { + newNode.data.node.template = newTemplate; + } else { + newNode.data.node.template[name].value = newValue; + } + + return newNode; + }); + + renderTooltips(); + }; + + return { handleOnNewValue }; +}; + +export default useHandleOnNewValue; diff --git a/src/frontend/src/customNodes/hooks/use-handle-node-class.tsx b/src/frontend/src/customNodes/hooks/use-handle-node-class.tsx new file mode 100644 index 000000000..412658d77 --- /dev/null +++ b/src/frontend/src/customNodes/hooks/use-handle-node-class.tsx @@ -0,0 +1,40 @@ +import { cloneDeep } from "lodash"; + +const useHandleNodeClass = ( + data, + name, + takeSnapshot, + setNode, + updateNodeInternals, + renderTooltips, +) => { + const handleNodeClass = (newNodeClass, code) => { + 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(); + }; + + return { handleNodeClass }; +}; + +export default useHandleNodeClass; diff --git a/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx b/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx new file mode 100644 index 000000000..19f2a3c29 --- /dev/null +++ b/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx @@ -0,0 +1,39 @@ +import { cloneDeep } from "lodash"; +import useAlertStore from "../../stores/alertStore"; +import { ResponseErrorDetailAPI } from "../../types/api"; +import { handleUpdateValues } from "../../utils/parameterUtils"; + +const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => { + const setErrorData = useAlertStore((state) => state.setErrorData); + + const handleRefreshButtonPress = async (name, data) => { + setIsLoading(true); + try { + let newTemplate = await handleUpdateValues(name, data); + + if (newTemplate) { + setNode(data.id, (oldNode) => { + let newNode = cloneDeep(oldNode); + newNode.data = { + ...newNode.data, + }; + newNode.data.node.template = newTemplate; + return newNode; + }); + } + } catch (error) { + let responseError = error as ResponseErrorDetailAPI; + + setErrorData({ + title: "Error while updating the Component", + list: [responseError.response.data.detail ?? "Unknown error"], + }); + } + setIsLoading(false); + renderTooltips(); + }; + + return { handleRefreshButtonPress }; +}; + +export default useHandleRefreshButtonPress; diff --git a/src/frontend/src/modals/editNodeModal/index.tsx b/src/frontend/src/modals/editNodeModal/index.tsx index b853ecfab..0db4bdcf5 100644 --- a/src/frontend/src/modals/editNodeModal/index.tsx +++ b/src/frontend/src/modals/editNodeModal/index.tsx @@ -28,6 +28,7 @@ import { LANGFLOW_SUPPORTED_TYPES, limitScrollFieldsModal, } from "../../constants/constants"; +import { Case } from "../../shared/components/caseComponent"; import useFlowStore from "../../stores/flowStore"; import { NodeDataType } from "../../types/flow"; import { @@ -52,7 +53,7 @@ const EditNodeModal = forwardRef( open: boolean; setOpen: (open: boolean) => void; }, - ref + ref, ) => { const [myData, setMyData] = useState(data); @@ -84,6 +85,10 @@ const EditNodeModal = forwardRef( const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); + const type = (templateParam) => { + return myData.node?.template[templateParam].type; + }; + return ( limitScrollFieldsModal ? "overflow-scroll overflow-x-hidden custom-scroll" - : "" + : "", )} > {nodeLength > 0 && ( @@ -138,8 +143,8 @@ const EditNodeModal = forwardRef( templateParam.charAt(0) !== "_" && myData.node?.template[templateParam].show && LANGFLOW_SUPPORTED_TYPES.has( - myData.node.template[templateParam].type - ) + myData.node!.template[templateParam].type, + ), ) .map((templateParam, index) => { let id = { @@ -161,8 +166,8 @@ const EditNodeModal = forwardRef( myData.node?.template[templateParam] .proxy, } - : id - ) + : id, + ), ) ?? false; return (