✨ (codeTabsComponent/index.tsx): add support for exporting code as a file

♻️ (codeTabsComponent/index.tsx): refactor code to use a switch statement instead of multiple if conditions for different template field types
🔧 (codeTabsComponent/index.tsx): fix import statements and remove unused imports
📝 (codeTabsComponent/index.tsx): add comments to improve code readability

📝 (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
This commit is contained in:
cristhianzl 2024-05-16 19:01:15 -03:00
commit 8a4a346736
11 changed files with 791 additions and 541 deletions

View file

@ -26,6 +26,8 @@ import {
TabsTrigger, TabsTrigger,
} from "../../components/ui/tabs"; } from "../../components/ui/tabs";
import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants"; import { LANGFLOW_SUPPORTED_TYPES } from "../../constants/constants";
import ExportModal from "../../modals/exportModal";
import { Case } from "../../shared/components/caseComponent";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import { codeTabsPropsType } from "../../types/components"; import { codeTabsPropsType } from "../../types/components";
@ -43,7 +45,6 @@ import KeypairListComponent from "../keypairListComponent";
import ShadTooltip from "../shadTooltipComponent"; import ShadTooltip from "../shadTooltipComponent";
import { Label } from "../ui/label"; import { Label } from "../ui/label";
import { Switch } from "../ui/switch"; import { Switch } from "../ui/switch";
import ExportModal from "../../modals/exportModal";
export default function CodeTabsComponent({ export default function CodeTabsComponent({
flow, flow,
@ -87,6 +88,10 @@ export default function CodeTabsComponent({
}); });
}; };
const type = (node, templateParam) => {
return node.data.node.template[templateParam].type;
};
const downloadAsFile = () => { const downloadAsFile = () => {
const fileExtension = tabs[activeTab].language || ".txt"; const fileExtension = tabs[activeTab].language || ".txt";
const suggestedFileName = `${"generated-code."}${fileExtension}`; const suggestedFileName = `${"generated-code."}${fileExtension}`;
@ -276,107 +281,71 @@ export default function CodeTabsComponent({
</TableCell> </TableCell>
<TableCell className="p-0 text-xs text-foreground"> <TableCell className="p-0 text-xs text-foreground">
<div className="m-auto w-[250px]"> <div className="m-auto w-[250px]">
{node.data.node.template[ <Case
templateField condition={
].type === "str" && type(node, templateField) ===
!node.data.node.template[ "str" &&
templateField !node.data.node.template[
].options ? (
<div className="mx-auto">
{node.data.node.template[
templateField templateField
]?.list ? ( ].options
<InputListComponent }
componentName={ >
templateField <Case
} condition={
editNode={true} node.data.node.template[
disabled={false}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? [""]
: node.data.node
.template[
templateField
].value
}
onChange={(target) => {
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 templateField
].multiline ? ( ]?.list
<div> }
<TextAreaComponent >
disabled={false} <InputListComponent
editNode={true} componentName={templateField}
value={ editNode={true}
!node.data.node disabled={false}
.template[ value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? [""]
: node.data.node.template[
templateField templateField
].value || ].value
node.data.node.template[ }
templateField onChange={(target) => {
].value === "" setData((old) => {
? "" let newInputList =
: node.data.node cloneDeep(old);
.template[ newInputList![
templateField i
].value ].data.node.template[
} templateField
onChange={(target) => { ].value = target;
setData((old) => { return newInputList;
let newInputList = });
cloneDeep(old); tweaks?.buildTweakObject!(
newInputList![ node["data"]["id"],
i target,
].data.node.template[
templateField
].value = target;
return newInputList;
});
tweaks?.buildTweakObject!(
node["data"]["id"],
target,
node.data.node
.template[
templateField
],
);
}}
/>
</div>
) : (
<InputComponent
editNode={true}
disabled={false}
password={
node.data.node.template[ node.data.node.template[
templateField templateField
].password ?? false ],
} );
}}
/>
</Case>
<Case
condition={
node.data.node.template[
templateField
].multiline
}
>
<div>
<TextAreaComponent
disabled={false}
editNode={true}
value={ value={
!node.data.node.template[ !node.data.node.template[
templateField templateField
@ -410,11 +379,68 @@ export default function CodeTabsComponent({
); );
}} }}
/> />
)} </div>
</div> </Case>
) : node.data.node.template[
templateField <Case
].type === "bool" ? ( condition={
!node.data.node.template[
templateField
].multiline &&
!node.data.node.template[
templateField
].list
}
>
<InputComponent
editNode={true}
disabled={false}
password={
node.data.node.template[
templateField
].password ?? false
}
value={
!node.data.node.template[
templateField
].value ||
node.data.node.template[
templateField
].value === ""
? ""
: node.data.node.template[
templateField
].value
}
onChange={(target) => {
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
],
);
}}
/>
</Case>
</Case>
<Case
condition={
type(node, templateField) ===
"bool"
}
>
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
<ToggleShadComponent <ToggleShadComponent
@ -446,9 +472,14 @@ export default function CodeTabsComponent({
disabled={false} disabled={false}
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "file" ? ( <Case
condition={
type(node, templateField) ===
"file"
}
>
<div className="mx-auto"> <div className="mx-auto">
<InputFileComponent <InputFileComponent
editNode={true} editNode={true}
@ -473,9 +504,14 @@ export default function CodeTabsComponent({
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "float" ? ( <Case
condition={
type(node, templateField) ===
"float"
}
>
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
disabled={false} disabled={false}
@ -518,12 +554,17 @@ export default function CodeTabsComponent({
}} }}
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "str" && <Case
node.data.node.template[ condition={
templateField type(node, templateField) ===
].options ? ( "str" &&
node.data.node.template[
templateField
].options
}
>
<div className="mx-auto"> <div className="mx-auto">
<Dropdown <Dropdown
editNode={true} editNode={true}
@ -565,9 +606,14 @@ export default function CodeTabsComponent({
} }
></Dropdown> ></Dropdown>
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "int" ? ( <Case
condition={
type(node, templateField) ===
"int"
}
>
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
disabled={false} disabled={false}
@ -610,9 +656,14 @@ export default function CodeTabsComponent({
}} }}
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "prompt" ? ( <Case
condition={
type(node, templateField) ===
"prompt"
}
>
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
readonly={true} readonly={true}
@ -651,9 +702,14 @@ export default function CodeTabsComponent({
}} }}
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "code" ? ( <Case
condition={
type(node, templateField) ===
"code"
}
>
<div className="mx-auto"> <div className="mx-auto">
<CodeAreaComponent <CodeAreaComponent
disabled={false} disabled={false}
@ -692,9 +748,14 @@ export default function CodeTabsComponent({
}} }}
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "dict" ? ( <Case
condition={
type(node, templateField) ===
"dict"
}
>
<div className="mx-auto overflow-auto custom-scroll"> <div className="mx-auto overflow-auto custom-scroll">
<KeypairListComponent <KeypairListComponent
disabled={false} disabled={false}
@ -712,6 +773,10 @@ export default function CodeTabsComponent({
.template[ .template[
templateField templateField
].value, ].value,
type(
node,
templateField,
),
) )
} }
duplicateKey={ duplicateKey={
@ -755,9 +820,14 @@ export default function CodeTabsComponent({
} }
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "NestedDict" ? ( <Case
condition={
type(node, templateField) ===
"NestedDict"
}
>
<div className="mx-auto"> <div className="mx-auto">
<DictComponent <DictComponent
disabled={false} disabled={false}
@ -765,7 +835,7 @@ export default function CodeTabsComponent({
value={ value={
node.data.node!.template[ node.data.node!.template[
templateField templateField
].value.toString() === "{}" ].value?.toString() === "{}"
? { ? {
// yourkey: "value", // yourkey: "value",
} }
@ -795,13 +865,16 @@ export default function CodeTabsComponent({
}} }}
/> />
</div> </div>
) : node.data.node.template[ </Case>
templateField
].type === "Any" ? ( <Case
"-" condition={
) : ( type(node, templateField) ===
<div className="hidden"></div> "Any"
)} }
>
<>-</>
</Case>
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>

View file

@ -1,5 +1,5 @@
import { cloneDeep } from "lodash"; 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 { Handle, Position, useUpdateNodeInternals } from "reactflow";
import CodeAreaComponent from "../../../../components/codeAreaComponent"; import CodeAreaComponent from "../../../../components/codeAreaComponent";
import DictComponent from "../../../../components/dictComponent"; import DictComponent from "../../../../components/dictComponent";
@ -18,20 +18,15 @@ import ToggleShadComponent from "../../../../components/toggleShadComponent";
import { Button } from "../../../../components/ui/button"; import { Button } from "../../../../components/ui/button";
import { RefreshButton } from "../../../../components/ui/refreshButton"; import { RefreshButton } from "../../../../components/ui/refreshButton";
import { import {
INPUT_HANDLER_HOVER,
LANGFLOW_SUPPORTED_TYPES, LANGFLOW_SUPPORTED_TYPES,
OUTPUT_HANDLER_HOVER,
TOOLTIP_EMPTY, TOOLTIP_EMPTY,
} from "../../../../constants/constants"; } from "../../../../constants/constants";
import { Case } from "../../../../shared/components/caseComponent";
import useAlertStore from "../../../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import useFlowStore from "../../../../stores/flowStore"; import useFlowStore from "../../../../stores/flowStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import { useTypesStore } from "../../../../stores/typesStore"; import { useTypesStore } from "../../../../stores/typesStore";
import { import { APIClassType } from "../../../../types/api";
APIClassType,
ResponseErrorDetailAPI,
ResponseErrorTypeAPI,
} from "../../../../types/api";
import { ParameterComponentType } from "../../../../types/components"; import { ParameterComponentType } from "../../../../types/components";
import { import {
debouncedHandleUpdateValues, debouncedHandleUpdateValues,
@ -44,12 +39,13 @@ import {
isValidConnection, isValidConnection,
scapedJSONStringfy, scapedJSONStringfy,
} from "../../../../utils/reactflowUtils"; } from "../../../../utils/reactflowUtils";
import { import { nodeColors } from "../../../../utils/styleUtils";
nodeColors,
nodeIconsLucide,
nodeNames,
} from "../../../../utils/styleUtils";
import { classNames, groupByFamily } from "../../../../utils/utils"; 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({ export default function ParameterComponent({
left, left,
@ -75,175 +71,69 @@ export default function ParameterComponent({
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const edges = useFlowStore((state) => state.edges); const edges = useFlowStore((state) => state.edges);
const setNode = useFlowStore((state) => state.setNode); const setNode = useFlowStore((state) => state.setNode);
const myData = useTypesStore((state) => state.data);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const updateNodeInternals = useUpdateNodeInternals();
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
const flow = currentFlow?.data?.nodes ?? null; const flow = currentFlow?.data?.nodes ?? null;
const groupedEdge = useRef(null); const groupedEdge = useRef(null);
const setFilterEdge = useFlowStore((state) => state.setFilterEdge); 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 = let disabled =
edges.some( edges.some(
(edge) => (edge) =>
edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id), edge.targetHandle === scapedJSONStringfy(proxy ? { ...id, proxy } : id),
) ?? false; ) ?? false;
const myData = useTypesStore((state) => state.data);
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const handleRefreshButtonPress = async (name, data) => { const handleRefreshButtonPress = async (name, data) => {
setIsLoading(true); handleRefreshButtonPressHook(name, data);
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();
}; };
useEffect(() => { useFetchDataOnMount(
async function fetchData() { data,
if ( name,
(data.node?.template[name]?.real_time_refresh || handleUpdateValues,
data.node?.template[name]?.refresh_button) && setNode,
// options can be undefined but not an empty array renderTooltips,
(data.node?.template[name]?.options?.length ?? 0) === 0 setIsLoading,
) { );
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();
}, []);
const handleOnNewValue = async ( const handleOnNewValue = async (
newValue: string | string[] | boolean | Object[], newValue: string | string[] | boolean | Object[],
skipSnapshot: boolean | undefined = false, skipSnapshot: boolean | undefined = false,
): Promise<void> => { ): Promise<void> => {
const nodeTemplate = data.node!.template[name]; handleOnNewValueHook(newValue, skipSnapshot);
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();
}; };
const updateNodeInternals = useUpdateNodeInternals();
const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => { const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => {
if (!data.node) return; handleNodeClassHook(newNodeClass, code);
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();
}; };
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
useEffect(() => { useEffect(() => {
// @ts-ignore // @ts-ignore
infoHtml.current = ( infoHtml.current = (
@ -264,88 +154,7 @@ export default function ParameterComponent({
if (groupedObj && groupedObj.length > 0) { if (groupedObj && groupedObj.length > 0) {
//@ts-ignore //@ts-ignore
refHtml.current = groupedObj.map((item, index) => { refHtml.current = groupedObj.map((item, index) => {
const Icon: any = return <TooltipRenderComponent index={index} item={item} left={left} />;
nodeIconsLucide[item.family] ?? nodeIconsLucide["unknown"];
return (
<div
key={index}
data-testid={`available-${left ? "input" : "output"}-${
item.family
}`}
>
{index === 0 && (
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
)}
<span
key={index}
className={classNames(
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
)}
>
<div
className="h-5 w-5"
style={{
color: nodeColors[item.family],
}}
>
<Icon
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
}}
/>
</div>
<span
className="ps-2 text-xs text-foreground"
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
>
{nodeNames[item.family] ?? "Other"}{" "}
{item?.display_name && item?.display_name?.length > 0 ? (
<span
className="text-xs"
data-testid={`tooltip-${item?.display_name}`}
>
{" "}
{item.display_name === "" ? "" : " - "}
{item.display_name.split(", ").length > 2
? item.display_name.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index ===
item.display_name.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.display_name}
</span>
) : (
<span
className="text-xs"
data-testid={`tooltip-${item?.type}`}
>
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
</span>
)}
</span>
</span>
</div>
);
}); });
} else { } else {
//@ts-ignore //@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 is an empty list, then it is not an optional handle
if (optionalHandle && optionalHandle.length === 0) { if (optionalHandle && optionalHandle.length === 0) {
optionalHandle = null; optionalHandle = null;
@ -362,6 +172,9 @@ export default function ParameterComponent({
useEffect(() => { useEffect(() => {
renderTooltips(); renderTooltips();
}, [tooltipTitle, flow]); }, [tooltipTitle, flow]);
console.log(left === true && type === "dict");
return !showNode ? ( return !showNode ? (
left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? ( left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
<></> <></>
@ -427,11 +240,12 @@ export default function ParameterComponent({
(left ? "" : " justify-end") (left ? "" : " justify-end")
} }
> >
{!left && data.node?.frozen && ( <Case condition={left && data.node?.frozen}>
<div className="pr-1"> <div className="pr-1">
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} /> <IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
</div> </div>
)} </Case>
{proxy ? ( {proxy ? (
<ShadTooltip content={<span>{proxy.id}</span>}> <ShadTooltip content={<span>{proxy.id}</span>}>
<span className={!left && data.node?.frozen ? " text-ice" : ""}> <span className={!left && data.node?.frozen ? " text-ice" : ""}>
@ -477,45 +291,37 @@ export default function ParameterComponent({
}`} }`}
type={left ? "target" : "source"} type={left ? "target" : "source"}
position={left ? Position.Left : Position.Right} position={left ? Position.Left : Position.Right}
key={ key={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
proxy id={scapedJSONStringfy(proxy ? { ...id, proxy } : id)}
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)
}
id={
proxy
? scapedJSONStringfy({ ...id, proxy })
: scapedJSONStringfy(id)
}
isValidConnection={(connection) => isValidConnection={(connection) =>
isValidConnection(connection, nodes, edges) isValidConnection(connection, nodes, edges)
} }
className={classNames( 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", "h-3 w-3 rounded-full border-2 bg-background",
)} )}
style={{ style={{ borderColor: color ?? nodeColors.unknown }}
borderColor: color ?? nodeColors.unknown, onClick={() => setFilterEdge(groupedEdge.current)}
}} />
onClick={() => {
setFilterEdge(groupedEdge.current);
}}
></Handle>
</ShadTooltip> </ShadTooltip>
</div> </div>
</Button> </Button>
)} )}
{left === true && <Case
type === "str" && condition={
!data.node?.template[name].options ? ( left === true &&
type === "str" &&
!data.node?.template[name]?.options
}
>
<div className="w-full"> <div className="w-full">
{data.node?.template[name].list ? ( <Case condition={data.node?.template[name]?.list}>
<div <div
className={ className={
// Commenting this out until we have a better // Commenting this out until we have a better
// way to display // way to display
// (data.node?.template[name].refresh ? "w-5/6 " : "") + // (data.node?.template[name]?.refresh ? "w-5/6 " : "") +
"flex-grow" "flex-grow"
} }
> >
@ -523,39 +329,27 @@ export default function ParameterComponent({
componentName={name} componentName={name}
disabled={disabled} disabled={disabled}
value={ 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} onChange={handleOnNewValue}
/> />
{/* {data.node?.template[name].refresh_button && (
<div className="w-1/6">
<RefreshButton
isLoading={isLoading}
disabled={disabled}
name={name}
data={data}
className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name}
/>
</div>
)} */}
</div> </div>
) : data.node?.template[name].multiline ? ( </Case>
<Case condition={data.node?.template[name]?.multiline}>
<div className="mt-2 flex w-full flex-col "> <div className="mt-2 flex w-full flex-col ">
<div className="flex-grow"> <div className="flex-grow">
<TextAreaComponent <TextAreaComponent
disabled={disabled} disabled={disabled}
value={data.node.template[name].value ?? ""} value={data.node!.template[name]?.value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
id={"textarea-" + data.node.template[name].name} id={"textarea-" + data.node!.template[name]?.name}
data-testid={"textarea-" + data.node.template[name].name} data-testid={"textarea-" + data.node!.template[name]?.name}
/> />
</div> </div>
{data.node?.template[name].refresh_button && ( {data.node?.template[name]?.refresh_button && (
<div className="flex-grow"> <div className="flex-grow">
<RefreshButton <RefreshButton
isLoading={isLoading} isLoading={isLoading}
@ -563,7 +357,7 @@ export default function ParameterComponent({
name={name} name={name}
data={data} data={data}
button_text={ button_text={
data.node?.template[name].refresh_button_text ?? data.node?.template[name]?.refresh_button_text ??
"Refresh" "Refresh"
} }
className="extra-side-bar-buttons mt-1" className="extra-side-bar-buttons mt-1"
@ -573,12 +367,18 @@ export default function ParameterComponent({
</div> </div>
)} )}
</div> </div>
) : ( </Case>
<Case
condition={
!data.node?.template[name]?.multiline &&
!data.node?.template[name]?.list
}
>
<div className="mt-2 flex w-full items-center"> <div className="mt-2 flex w-full items-center">
<div <div
className={ className={
"flex-grow " + "flex-grow " +
(data.node?.template[name].refresh_button ? "w-5/6" : "") (data.node?.template[name]?.refresh_button ? "w-5/6" : "")
} }
> >
<InputGlobalComponent <InputGlobalComponent
@ -598,7 +398,7 @@ export default function ParameterComponent({
data={data} data={data}
/> />
</div> </div>
{data.node?.template[name].refresh_button && ( {data.node?.template[name]?.refresh_button && (
<div className="w-1/6"> <div className="w-1/6">
<RefreshButton <RefreshButton
isLoading={isLoading} isLoading={isLoading}
@ -606,7 +406,7 @@ export default function ParameterComponent({
name={name} name={name}
data={data} data={data}
button_text={ button_text={
data.node?.template[name].refresh_button_text ?? data.node?.template[name]?.refresh_button_text ??
"Refresh" "Refresh"
} }
className="extra-side-bar-buttons ml-2 mt-1" className="extra-side-bar-buttons ml-2 mt-1"
@ -616,52 +416,61 @@ export default function ParameterComponent({
</div> </div>
)} )}
</div> </div>
)} </Case>
</div> </div>
) : left === true && type === "bool" ? ( </Case>
<Case condition={left === true && type === "bool"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<ToggleShadComponent <ToggleShadComponent
id={"toggle-" + name} id={"toggle-" + name}
disabled={disabled} disabled={disabled}
enabled={data.node?.template[name].value ?? false} enabled={data.node?.template[name]?.value ?? false}
setEnabled={handleOnNewValue} setEnabled={handleOnNewValue}
size="large" size="large"
editNode={false} editNode={false}
/> />
</div> </div>
) : left === true && type === "float" ? ( </Case>
<Case condition={left === true && type === "float"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<FloatComponent <FloatComponent
disabled={disabled} disabled={disabled}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name]?.value ?? ""}
rangeSpec={data.node?.template[name]?.rangeSpec} rangeSpec={data.node?.template[name]?.rangeSpec}
onChange={handleOnNewValue} onChange={handleOnNewValue}
/> />
</div> </div>
) : left === true && </Case>
type === "str" &&
(data.node?.template[name].options || <Case
data.node?.template[name]?.real_time_refresh) ? ( condition={
// TODO: Improve CSS left === true &&
type === "str" &&
(data.node?.template[name]?.options ||
data.node?.template[name]?.real_time_refresh)
}
>
<div className="mt-2 flex w-full items-center"> <div className="mt-2 flex w-full items-center">
<div className="w-5/6 flex-grow"> <div className="w-5/6 flex-grow">
<Dropdown <Dropdown
disabled={disabled} disabled={disabled}
isLoading={isLoading} isLoading={isLoading}
options={data.node.template[name].options} options={data.node!.template[name]?.options}
onSelect={handleOnNewValue} onSelect={handleOnNewValue}
value={data.node.template[name].value} value={data.node!.template[name]?.value}
id={"dropdown-" + name} id={"dropdown-" + name}
/> />
</div> </div>
{data.node?.template[name].refresh_button && ( {data.node?.template[name]?.refresh_button && (
<div className="w-1/6"> <div className="w-1/6">
<RefreshButton <RefreshButton
isLoading={isLoading} isLoading={isLoading}
disabled={disabled} disabled={disabled}
name={name} name={name}
data={data} data={data}
button_text={data.node?.template[name].refresh_button_text} button_text={data.node?.template[name]?.refresh_button_text}
className="extra-side-bar-buttons ml-2 mt-1" className="extra-side-bar-buttons ml-2 mt-1"
handleUpdateValues={handleRefreshButtonPress} handleUpdateValues={handleRefreshButtonPress}
id={"refresh-button-" + name} id={"refresh-button-" + name}
@ -669,46 +478,54 @@ export default function ParameterComponent({
</div> </div>
)} )}
</div> </div>
) : left === true && type === "code" ? ( </Case>
<Case condition={left === true && type === "code"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<CodeAreaComponent <CodeAreaComponent
readonly={ readonly={
data.node?.flow && data.node.template[name].dynamic data.node?.flow && data.node.template[name]?.dynamic
? true ? true
: false : false
} }
dynamic={data.node?.template[name].dynamic ?? false} dynamic={data.node?.template[name]?.dynamic ?? false}
setNodeClass={handleNodeClass} setNodeClass={handleNodeClass}
nodeClass={data.node} nodeClass={data.node}
disabled={disabled} disabled={disabled}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
id={"code-input-" + name} id={"code-input-" + name}
/> />
</div> </div>
) : left === true && type === "file" ? ( </Case>
<Case condition={left === true && type === "file"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<InputFileComponent <InputFileComponent
disabled={disabled} disabled={disabled}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
fileTypes={data.node?.template[name].fileTypes} fileTypes={data.node?.template[name]?.fileTypes}
onFileChange={(filePath: string) => { onFileChange={(filePath: string) => {
data.node!.template[name].file_path = filePath; data.node!.template[name].file_path = filePath;
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : left === true && type === "int" ? ( </Case>
<Case condition={left === true && type === "int"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<IntComponent <IntComponent
rangeSpec={data.node?.template[name].rangeSpec} rangeSpec={data.node?.template[name]?.rangeSpec}
disabled={disabled} disabled={disabled}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
id={"int-input-" + name} id={"int-input-" + name}
/> />
</div> </div>
) : left === true && type === "prompt" ? ( </Case>
<Case condition={left === true && type === "prompt"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<PromptAreaComponent <PromptAreaComponent
readonly={data.node?.flow ? true : false} readonly={data.node?.flow ? true : false}
@ -716,57 +533,59 @@ export default function ParameterComponent({
setNodeClass={handleNodeClass} setNodeClass={handleNodeClass}
nodeClass={data.node} nodeClass={data.node}
disabled={disabled} disabled={disabled}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name]?.value ?? ""}
onChange={handleOnNewValue} onChange={handleOnNewValue}
id={"prompt-input-" + name} id={"prompt-input-" + name}
data-testid={"prompt-input-" + name} data-testid={"prompt-input-" + name}
/> />
</div> </div>
) : left === true && type === "NestedDict" ? ( </Case>
<Case condition={left === true && type === "NestedDict"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<DictComponent <DictComponent
disabled={disabled} disabled={disabled}
editNode={false} editNode={false}
value={ value={
!data.node!.template[name].value || !data.node!.template[name]?.value ||
data.node!.template[name].value?.toString() === "{}" data.node!.template[name]?.value?.toString() === "{}"
? { ? {
// yourkey: "value", // yourkey: "value",
} }
: data.node!.template[name].value : data.node!.template[name]?.value
} }
onChange={handleOnNewValue} onChange={handleOnNewValue}
id="div-dict-input" id="div-dict-input"
/> />
</div> </div>
) : left === true && type === "dict" ? ( </Case>
<Case condition={left === true && type === "dict"}>
<div className="mt-2 w-full"> <div className="mt-2 w-full">
<KeypairListComponent <KeypairListComponent
disabled={disabled} disabled={disabled}
editNode={false} editNode={false}
value={ value={
data.node!.template[name].value?.length === 0 || data.node!.template[name]?.value?.length === 0 ||
!data.node!.template[name].value !data.node!.template[name]?.value
? [{ "": "" }] ? [{ "": "" }]
: convertObjToArray(data.node!.template[name].value) : convertObjToArray(data.node!.template[name]?.value, type!)
} }
duplicateKey={errorDuplicateKey} duplicateKey={errorDuplicateKey}
onChange={(newValue) => { onChange={(newValue) => {
const valueToNumbers = convertValuesToNumbers(newValue); const valueToNumbers = convertValuesToNumbers(newValue);
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers)); 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 // 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); handleOnNewValue(valueToNumbers);
} else handleOnNewValue(valueToNumbers[0]); } else handleOnNewValue(valueToNumbers[0]);
}} }}
isList={data.node?.template[name].list ?? false} isList={data.node?.template[name]?.list ?? false}
/> />
</div> </div>
) : ( </Case>
<></>
)}
</> </>
</div> </div>
); );

View file

@ -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 (
<div
key={index}
data-testid={`available-${left ? "input" : "output"}-${item.family}`}
>
{index === 0 && (
<span>{left ? INPUT_HANDLER_HOVER : OUTPUT_HANDLER_HOVER}</span>
)}
<span
key={index}
className={classNames(
index > 0 ? "mt-2 flex items-center" : "mt-3 flex items-center",
)}
>
<div
className="h-5 w-5"
style={{
color: nodeColors[item.family],
}}
>
<Icon
className="h-5 w-5"
strokeWidth={1.5}
style={{
color: nodeColors[item.family] ?? nodeColors.unknown,
}}
/>
</div>
<span
className="ps-2 text-xs text-foreground"
data-testid={`tooltip-${nodeNames[item.family] ?? "Other"}`}
>
{nodeNames[item.family] ?? "Other"}{" "}
{item?.display_name && item?.display_name?.length > 0 ? (
<span
className="text-xs"
data-testid={`tooltip-${item?.display_name}`}
>
{" "}
{item.display_name === "" ? "" : " - "}
{item.display_name.split(", ").length > 2
? item.display_name.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.display_name.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.display_name}
</span>
) : (
<span className="text-xs" data-testid={`tooltip-${item?.type}`}>
{" "}
{item.type === "" ? "" : " - "}
{item.type.split(", ").length > 2
? item.type.split(", ").map((el, index) => (
<React.Fragment key={el + name}>
<span>
{index === item.type.split(", ").length - 1
? el
: (el += `, `)}
</span>
</React.Fragment>
))
: item.type}
</span>
)}
</span>
</span>
</div>
);
};
export default TooltipRenderComponent;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -28,6 +28,7 @@ import {
LANGFLOW_SUPPORTED_TYPES, LANGFLOW_SUPPORTED_TYPES,
limitScrollFieldsModal, limitScrollFieldsModal,
} from "../../constants/constants"; } from "../../constants/constants";
import { Case } from "../../shared/components/caseComponent";
import useFlowStore from "../../stores/flowStore"; import useFlowStore from "../../stores/flowStore";
import { NodeDataType } from "../../types/flow"; import { NodeDataType } from "../../types/flow";
import { import {
@ -52,7 +53,7 @@ const EditNodeModal = forwardRef(
open: boolean; open: boolean;
setOpen: (open: boolean) => void; setOpen: (open: boolean) => void;
}, },
ref ref,
) => { ) => {
const [myData, setMyData] = useState(data); const [myData, setMyData] = useState(data);
@ -84,6 +85,10 @@ const EditNodeModal = forwardRef(
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false); const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
const type = (templateParam) => {
return myData.node?.template[templateParam].type;
};
return ( return (
<BaseModal <BaseModal
key={data.id} key={data.id}
@ -116,7 +121,7 @@ const EditNodeModal = forwardRef(
"edit-node-modal-box", "edit-node-modal-box",
nodeLength > limitScrollFieldsModal nodeLength > limitScrollFieldsModal
? "overflow-scroll overflow-x-hidden custom-scroll" ? "overflow-scroll overflow-x-hidden custom-scroll"
: "" : "",
)} )}
> >
{nodeLength > 0 && ( {nodeLength > 0 && (
@ -138,8 +143,8 @@ const EditNodeModal = forwardRef(
templateParam.charAt(0) !== "_" && templateParam.charAt(0) !== "_" &&
myData.node?.template[templateParam].show && myData.node?.template[templateParam].show &&
LANGFLOW_SUPPORTED_TYPES.has( LANGFLOW_SUPPORTED_TYPES.has(
myData.node.template[templateParam].type myData.node!.template[templateParam].type,
) ),
) )
.map((templateParam, index) => { .map((templateParam, index) => {
let id = { let id = {
@ -161,8 +166,8 @@ const EditNodeModal = forwardRef(
myData.node?.template[templateParam] myData.node?.template[templateParam]
.proxy, .proxy,
} }
: id : id,
) ),
) ?? false; ) ?? false;
return ( return (
<TableRow <TableRow
@ -170,8 +175,7 @@ const EditNodeModal = forwardRef(
className={ className={
"h-10 " + "h-10 " +
((templateParam === "code" && ((templateParam === "code" &&
myData.node?.template[templateParam].type === type(templateParam) === "code") ||
"code") ||
(templateParam.includes("code") && (templateParam.includes("code") &&
myData.node?.template[templateParam].proxy) myData.node?.template[templateParam].proxy)
? " hidden " ? " hidden "
@ -190,7 +194,7 @@ const EditNodeModal = forwardRef(
<span> <span>
{myData.node?.template[templateParam] {myData.node?.template[templateParam]
.display_name .display_name
? myData.node.template[templateParam] ? myData.node!.template[templateParam]
.display_name .display_name
: myData.node?.template[templateParam] : myData.node?.template[templateParam]
.name} .name}
@ -198,58 +202,62 @@ const EditNodeModal = forwardRef(
</ShadTooltip> </ShadTooltip>
</TableCell> </TableCell>
<TableCell className="w-[300px] p-0 text-center text-xs text-foreground "> <TableCell className="w-[300px] p-0 text-center text-xs text-foreground ">
{myData.node?.template[templateParam].type === <Case
"str" && condition={
!myData.node.template[templateParam].options ? ( type(templateParam) === "str" &&
!myData.node!.template[templateParam]
.options
}
>
<div className="mx-auto"> <div className="mx-auto">
{myData.node.template[templateParam] {myData.node!.template[templateParam]
?.list ? ( ?.list ? (
<InputListComponent <InputListComponent
componentName={templateParam} componentName={templateParam}
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
value={ value={
!myData.node.template[templateParam] !myData.node!.template[templateParam]
.value || .value ||
myData.node.template[templateParam] myData.node!.template[templateParam]
.value === "" .value === ""
? [""] ? [""]
: myData.node.template[ : myData.node!.template[
templateParam templateParam
].value ].value
} }
onChange={(value: string[]) => { onChange={(value: string[]) => {
handleOnNewValue( handleOnNewValue(
value, value,
templateParam templateParam,
); );
}} }}
/> />
) : myData.node.template[templateParam] ) : myData.node!.template[templateParam]
.multiline ? ( .multiline ? (
<TextAreaComponent <TextAreaComponent
id={ id={
"textarea-edit-" + "textarea-edit-" +
myData.node.template[templateParam] myData.node!.template[templateParam]
.name .name
} }
data-testid={ data-testid={
"textarea-edit-" + "textarea-edit-" +
myData.node.template[templateParam] myData.node!.template[templateParam]
.name .name
} }
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={( onChange={(
value: string | string[] value: string | string[],
) => { ) => {
handleOnNewValue( handleOnNewValue(
value, value,
templateParam templateParam,
); );
}} }}
/> />
@ -274,8 +282,13 @@ const EditNodeModal = forwardRef(
/> />
)} )}
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "NestedDict" ? (
<Case
condition={
type(templateParam) === "NestedDict"
}
>
<div className=" w-full"> <div className=" w-full">
<DictComponent <DictComponent
disabled={disabled} disabled={disabled}
@ -296,21 +309,24 @@ const EditNodeModal = forwardRef(
].value = newValue; ].value = newValue;
handleOnNewValue( handleOnNewValue(
newValue, newValue,
templateParam templateParam,
); );
}} }}
id="editnode-div-dict-input" id="editnode-div-dict-input"
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "dict" ? (
<Case
condition={type(templateParam) === "dict"}
>
<div <div
className={classNames( className={classNames(
"max-h-48 w-full overflow-auto custom-scroll", "max-h-48 w-full overflow-auto custom-scroll",
myData.node!.template[templateParam].value myData.node!.template[templateParam].value
?.length > 1 ?.length > 1
? "my-3" ? "my-3"
: "" : "",
)} )}
> >
<KeypairListComponent <KeypairListComponent
@ -325,7 +341,8 @@ const EditNodeModal = forwardRef(
: convertObjToArray( : convertObjToArray(
myData.node!.template[ myData.node!.template[
templateParam templateParam
].value ].value,
type(templateParam)!,
) )
} }
duplicateKey={errorDuplicateKey} duplicateKey={errorDuplicateKey}
@ -336,11 +353,11 @@ const EditNodeModal = forwardRef(
templateParam templateParam
].value = valueToNumbers; ].value = valueToNumbers;
setErrorDuplicateKey( setErrorDuplicateKey(
hasDuplicateKeys(valueToNumbers) hasDuplicateKeys(valueToNumbers),
); );
handleOnNewValue( handleOnNewValue(
valueToNumbers, valueToNumbers,
templateParam templateParam,
); );
}} }}
isList={ isList={
@ -349,32 +366,39 @@ const EditNodeModal = forwardRef(
} }
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "bool" ? (
<Case
condition={type(templateParam) === "bool"}
>
<div className="ml-auto"> <div className="ml-auto">
{" "} {" "}
<ToggleShadComponent <ToggleShadComponent
id={ id={
"toggle-edit-" + "toggle-edit-" +
myData.node.template[templateParam].name myData.node!.template[templateParam]
.name
} }
disabled={disabled} disabled={disabled}
enabled={ enabled={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value .value
} }
setEnabled={(isEnabled) => { setEnabled={(isEnabled) => {
handleOnNewValue( handleOnNewValue(
isEnabled, isEnabled,
templateParam templateParam,
); );
}} }}
size="small" size="small"
editNode={true} editNode={true}
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "float" ? (
<Case
condition={type(templateParam) === "float"}
>
<div className="mx-auto"> <div className="mx-auto">
<FloatComponent <FloatComponent
disabled={disabled} disabled={disabled}
@ -384,7 +408,7 @@ const EditNodeModal = forwardRef(
.rangeSpec .rangeSpec
} }
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value) => { onChange={(value) => {
@ -392,32 +416,38 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "str" &&
myData.node.template[templateParam] <Case
.options ? ( condition={
type(templateParam) === "str" &&
myData.node!.template[templateParam].options
}
>
<div className="mx-auto"> <div className="mx-auto">
<Dropdown <Dropdown
editNode={true} editNode={true}
options={ options={
myData.node.template[templateParam] myData.node!.template[templateParam]
.options .options
} }
onSelect={(value) => onSelect={(value) =>
handleOnNewValue(value, templateParam) handleOnNewValue(value, templateParam)
} }
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "Choose an option" .value ?? "Choose an option"
} }
id={ id={
"dropdown-edit-" + "dropdown-edit-" +
myData.node.template[templateParam].name myData.node!.template[templateParam]
.name
} }
></Dropdown> ></Dropdown>
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "int" ? (
<Case condition={type(templateParam) === "int"}>
<div className="mx-auto"> <div className="mx-auto">
<IntComponent <IntComponent
rangeSpec={ rangeSpec={
@ -426,12 +456,13 @@ const EditNodeModal = forwardRef(
} }
id={ id={
"edit-int-input-" + "edit-int-input-" +
myData.node.template[templateParam].name myData.node!.template[templateParam]
.name
} }
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value) => { onChange={(value) => {
@ -439,21 +470,24 @@ const EditNodeModal = forwardRef(
}} }}
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "file" ? (
<Case
condition={type(templateParam) === "file"}
>
<div className="mx-auto"> <div className="mx-auto">
<InputFileComponent <InputFileComponent
editNode={true} editNode={true}
disabled={disabled} disabled={disabled}
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string | string[]) => { onChange={(value: string | string[]) => {
handleOnNewValue(value, templateParam); handleOnNewValue(value, templateParam);
}} }}
fileTypes={ fileTypes={
myData.node.template[templateParam] myData.node!.template[templateParam]
.fileTypes .fileTypes
} }
onFileChange={(filePath: string) => { onFileChange={(filePath: string) => {
@ -463,8 +497,11 @@ const EditNodeModal = forwardRef(
}} }}
></InputFileComponent> ></InputFileComponent>
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "prompt" ? (
<Case
condition={type(templateParam) === "prompt"}
>
<div className="mx-auto"> <div className="mx-auto">
<PromptAreaComponent <PromptAreaComponent
readonly={ readonly={
@ -478,7 +515,7 @@ const EditNodeModal = forwardRef(
myData.node = nodeClass; myData.node = nodeClass;
}} }}
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string | string[]) => { onChange={(value: string | string[]) => {
@ -486,21 +523,26 @@ const EditNodeModal = forwardRef(
}} }}
id={ id={
"prompt-area-edit-" + "prompt-area-edit-" +
myData.node.template[templateParam].name myData.node!.template[templateParam]
.name
} }
data-testid={ data-testid={
"modal-prompt-input-" + "modal-prompt-input-" +
myData.node.template[templateParam].name myData.node!.template[templateParam]
.name
} }
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "code" ? (
<Case
condition={type(templateParam) === "code"}
>
<div className="mx-auto"> <div className="mx-auto">
<CodeAreaComponent <CodeAreaComponent
readonly={ readonly={
myData.node?.flow && myData.node?.flow &&
myData.node.template[templateParam] myData.node!.template[templateParam]
.dynamic .dynamic
? true ? true
: false : false
@ -516,7 +558,7 @@ const EditNodeModal = forwardRef(
disabled={disabled} disabled={disabled}
editNode={true} editNode={true}
value={ value={
myData.node.template[templateParam] myData.node!.template[templateParam]
.value ?? "" .value ?? ""
} }
onChange={(value: string | string[]) => { onChange={(value: string | string[]) => {
@ -524,16 +566,16 @@ const EditNodeModal = forwardRef(
}} }}
id={ id={
"code-area-edit" + "code-area-edit" +
myData.node.template[templateParam].name myData.node!.template[templateParam]
.name
} }
/> />
</div> </div>
) : myData.node?.template[templateParam] </Case>
.type === "Any" ? (
"-" <Case condition={type(templateParam) === "Any"}>
) : ( <>-</>
<div className="hidden"></div> </Case>
)}
</TableCell> </TableCell>
<TableCell className="p-0 text-right"> <TableCell className="p-0 text-right">
<div className="items-center text-center"> <div className="items-center text-center">
@ -588,7 +630,7 @@ const EditNodeModal = forwardRef(
</BaseModal.Footer> </BaseModal.Footer>
</BaseModal> </BaseModal>
); );
} },
); );
export default EditNodeModal; export default EditNodeModal;

View file

@ -0,0 +1,15 @@
import { memo } from "react";
type BooleanLike = boolean | string | number | null | undefined;
type Props = {
condition: (() => BooleanLike) | BooleanLike;
children: React.ReactNode | any;
};
export const Case = memo(({ condition, children }: Props) => {
const conditionResult =
typeof condition === "function" ? condition() : condition;
return conditionResult ? children : null;
});

View file

@ -400,6 +400,7 @@ export type StoreApiKeyType = {
export type groupedObjType = { export type groupedObjType = {
family: string; family: string;
type: string; type: string;
display_name?: string;
}; };
export type nodeGroupedObjType = { export type nodeGroupedObjType = {

View file

@ -461,7 +461,8 @@ export function getConnectedNodes(
return nodes.filter((node) => node.id === targetId || node.id === sourceId); return nodes.filter((node) => node.id === targetId || node.id === sourceId);
} }
export function convertObjToArray(singleObject: object | string) { export function convertObjToArray(singleObject: object | string, type: string) {
if (type !== "dict") return [{ "": "" }];
if (typeof singleObject === "string") { if (typeof singleObject === "string") {
singleObject = JSON.parse(singleObject); singleObject = JSON.parse(singleObject);
} }