Remove folder
This commit is contained in:
parent
e994b1b649
commit
0f82fdc77b
10 changed files with 0 additions and 1784 deletions
|
|
@ -1 +0,0 @@
|
||||||
export const TEXT_FIELD_TYPES: string[] = ["str", "SecretStr"];
|
|
||||||
|
|
@ -1,584 +0,0 @@
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
|
||||||
import { Handle, Position, useUpdateNodeInternals } from "reactflow";
|
|
||||||
import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
|
||||||
import DictComponent from "../../../../components/dictComponent";
|
|
||||||
import Dropdown from "../../../../components/dropdownComponent";
|
|
||||||
import FloatComponent from "../../../../components/floatComponent";
|
|
||||||
import { default as IconComponent } from "../../../../components/genericIconComponent";
|
|
||||||
import InputFileComponent from "../../../../components/inputFileComponent";
|
|
||||||
import InputGlobalComponent from "../../../../components/inputGlobalComponent";
|
|
||||||
import InputListComponent from "../../../../components/inputListComponent";
|
|
||||||
import IntComponent from "../../../../components/intComponent";
|
|
||||||
import KeypairListComponent from "../../../../components/keypairListComponent";
|
|
||||||
import PromptAreaComponent from "../../../../components/promptComponent";
|
|
||||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
|
||||||
import TextAreaComponent from "../../../../components/textAreaComponent";
|
|
||||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
|
||||||
import { Button } from "../../../../components/ui/button";
|
|
||||||
import { RefreshButton } from "../../../../components/ui/refreshButton";
|
|
||||||
import {
|
|
||||||
LANGFLOW_SUPPORTED_TYPES,
|
|
||||||
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 } from "../../../../types/api";
|
|
||||||
import { ParameterComponentType } from "../../../../types/components";
|
|
||||||
import {
|
|
||||||
debouncedHandleUpdateValues,
|
|
||||||
handleUpdateValues,
|
|
||||||
} from "../../../../utils/parameterUtils";
|
|
||||||
import {
|
|
||||||
convertObjToArray,
|
|
||||||
convertValuesToNumbers,
|
|
||||||
hasDuplicateKeys,
|
|
||||||
isValidConnection,
|
|
||||||
scapedJSONStringfy,
|
|
||||||
} from "../../../../utils/reactflowUtils";
|
|
||||||
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";
|
|
||||||
import { TEXT_FIELD_TYPES } from "./constants";
|
|
||||||
|
|
||||||
export default function ParameterComponent({
|
|
||||||
left,
|
|
||||||
id,
|
|
||||||
data,
|
|
||||||
tooltipTitle,
|
|
||||||
title,
|
|
||||||
color,
|
|
||||||
type,
|
|
||||||
name = "",
|
|
||||||
required = false,
|
|
||||||
optionalHandle = null,
|
|
||||||
info = "",
|
|
||||||
proxy,
|
|
||||||
showNode,
|
|
||||||
index = "",
|
|
||||||
}: ParameterComponentType): JSX.Element {
|
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
|
||||||
const refHtml = useRef<HTMLDivElement & ReactNode>(null);
|
|
||||||
const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
|
|
||||||
const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
|
|
||||||
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,
|
|
||||||
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 handleRefreshButtonPress = async (name, data) => {
|
|
||||||
handleRefreshButtonPressHook(name, data);
|
|
||||||
};
|
|
||||||
|
|
||||||
useFetchDataOnMount(
|
|
||||||
data,
|
|
||||||
name,
|
|
||||||
handleUpdateValues,
|
|
||||||
setNode,
|
|
||||||
renderTooltips,
|
|
||||||
setIsLoading
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleOnNewValue = async (
|
|
||||||
newValue: string | string[] | boolean | Object[],
|
|
||||||
skipSnapshot: boolean | undefined = false
|
|
||||||
): Promise<void> => {
|
|
||||||
handleOnNewValueHook(newValue, skipSnapshot);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNodeClass = (newNodeClass: APIClassType, code?: string): void => {
|
|
||||||
handleNodeClassHook(newNodeClass, code);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// @ts-ignore
|
|
||||||
infoHtml.current = (
|
|
||||||
<div className="h-full w-full break-words">
|
|
||||||
{info.split("\n").map((line, index) => (
|
|
||||||
<p key={index} className="block">
|
|
||||||
{line}
|
|
||||||
</p>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}, [info]);
|
|
||||||
|
|
||||||
function renderTooltips() {
|
|
||||||
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, flow!);
|
|
||||||
groupedEdge.current = groupedObj;
|
|
||||||
|
|
||||||
if (groupedObj && groupedObj.length > 0) {
|
|
||||||
//@ts-ignore
|
|
||||||
refHtml.current = groupedObj.map((item, index) => {
|
|
||||||
return <TooltipRenderComponent index={index} item={item} left={left} />;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
//@ts-ignore
|
|
||||||
refHtml.current = (
|
|
||||||
<span data-testid={`empty-tooltip-filter`}>{TOOLTIP_EMPTY}</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If optionalHandle is an empty list, then it is not an optional handle
|
|
||||||
if (optionalHandle && optionalHandle.length === 0) {
|
|
||||||
optionalHandle = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
renderTooltips();
|
|
||||||
}, [tooltipTitle, flow]);
|
|
||||||
|
|
||||||
return !showNode ? (
|
|
||||||
left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
|
|
||||||
<></>
|
|
||||||
) : (
|
|
||||||
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
|
|
||||||
<div className="flex">
|
|
||||||
<ShadTooltip
|
|
||||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
|
||||||
delayDuration={1000}
|
|
||||||
content={refHtml.current}
|
|
||||||
side={left ? "left" : "right"}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
data-test-id={`handle-${title.toLowerCase()}-${
|
|
||||||
left ? "target" : "source"
|
|
||||||
}`}
|
|
||||||
type={left ? "target" : "source"}
|
|
||||||
position={left ? Position.Left : Position.Right}
|
|
||||||
key={
|
|
||||||
proxy
|
|
||||||
? scapedJSONStringfy({ ...id, proxy })
|
|
||||||
: scapedJSONStringfy(id)
|
|
||||||
}
|
|
||||||
id={
|
|
||||||
proxy
|
|
||||||
? scapedJSONStringfy({ ...id, proxy })
|
|
||||||
: scapedJSONStringfy(id)
|
|
||||||
}
|
|
||||||
isValidConnection={(connection) =>
|
|
||||||
isValidConnection(connection, nodes, edges)
|
|
||||||
}
|
|
||||||
className={classNames(
|
|
||||||
left ? "my-12 -ml-0.5 " : " my-12 -mr-0.5 ",
|
|
||||||
"h-3 w-3 rounded-full border-2 bg-background",
|
|
||||||
!showNode ? "mt-0" : ""
|
|
||||||
)}
|
|
||||||
style={{
|
|
||||||
borderColor: color ?? nodeColors.unknown,
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setFilterEdge(groupedEdge.current);
|
|
||||||
}}
|
|
||||||
></Handle>
|
|
||||||
</ShadTooltip>
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
className={
|
|
||||||
"relative mt-1 flex w-full flex-wrap items-center justify-between bg-muted px-5 py-2" +
|
|
||||||
((name === "code" && type === "code") ||
|
|
||||||
(name.includes("code") && proxy)
|
|
||||||
? " hidden "
|
|
||||||
: "")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
"flex w-full items-center truncate text-sm" +
|
|
||||||
(left ? "" : " justify-end")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Case condition={!left && data.node?.frozen}>
|
|
||||||
<div className="pr-1">
|
|
||||||
<IconComponent className="h-5 w-5 text-ice" name={"Snowflake"} />
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
{proxy ? (
|
|
||||||
<ShadTooltip content={<span>{proxy.id}</span>}>
|
|
||||||
<span className={!left && data.node?.frozen ? " text-ice" : ""}>
|
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
</ShadTooltip>
|
|
||||||
) : (
|
|
||||||
<span className={!left && data.node?.frozen ? " text-ice" : ""}>
|
|
||||||
{title}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className={(required ? "ml-2 " : "") + "text-status-red"}>
|
|
||||||
{required ? "*" : ""}
|
|
||||||
</span>
|
|
||||||
<div className="">
|
|
||||||
{info !== "" && (
|
|
||||||
<ShadTooltip content={infoHtml.current}>
|
|
||||||
{/* put div to avoid bug that does not display tooltip */}
|
|
||||||
<div>
|
|
||||||
<IconComponent
|
|
||||||
name="Info"
|
|
||||||
className="relative bottom-px ml-1.5 h-3 w-4"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ShadTooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{left && LANGFLOW_SUPPORTED_TYPES.has(type ?? "") && !optionalHandle ? (
|
|
||||||
<></>
|
|
||||||
) : (
|
|
||||||
<Button className="h-7 truncate bg-muted p-0 text-sm font-normal text-black hover:bg-muted">
|
|
||||||
<div className="flex">
|
|
||||||
<ShadTooltip
|
|
||||||
styleClasses={"tooltip-fixed-width custom-scroll nowheel"}
|
|
||||||
delayDuration={1000}
|
|
||||||
content={refHtml.current}
|
|
||||||
side={left ? "left" : "right"}
|
|
||||||
>
|
|
||||||
<Handle
|
|
||||||
data-test-id={`handle-${title.toLowerCase()}-${
|
|
||||||
left ? "left" : "right"
|
|
||||||
}`}
|
|
||||||
type={left ? "target" : "source"}
|
|
||||||
position={left ? Position.Left : Position.Right}
|
|
||||||
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",
|
|
||||||
"h-3 w-3 rounded-full border-2 bg-background"
|
|
||||||
)}
|
|
||||||
style={{ borderColor: color ?? nodeColors.unknown }}
|
|
||||||
onClick={() => setFilterEdge(groupedEdge.current)}
|
|
||||||
/>
|
|
||||||
</ShadTooltip>
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Case
|
|
||||||
condition={
|
|
||||||
left === true &&
|
|
||||||
TEXT_FIELD_TYPES.includes(type ?? "") &&
|
|
||||||
!data.node?.template[name]?.options
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="w-full">
|
|
||||||
<Case condition={data.node?.template[name]?.list}>
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
// Commenting this out until we have a better
|
|
||||||
// way to display
|
|
||||||
// (data.node?.template[name]?.refresh ? "w-5/6 " : "") +
|
|
||||||
"flex-grow"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<InputListComponent
|
|
||||||
componentName={name}
|
|
||||||
disabled={disabled}
|
|
||||||
value={
|
|
||||||
!data.node!.template[name]?.value ||
|
|
||||||
data.node!.template[name]?.value === ""
|
|
||||||
? [""]
|
|
||||||
: data.node!.template[name]?.value
|
|
||||||
}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
<Case condition={data.node?.template[name]?.multiline}>
|
|
||||||
<div className="mt-2 flex w-full flex-col ">
|
|
||||||
<div className="flex-grow">
|
|
||||||
<TextAreaComponent
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node!.template[name]?.value ?? ""}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
id={"textarea-" + data.node!.template[name]?.name}
|
|
||||||
data-testid={"textarea-" + data.node!.template[name]?.name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{data.node?.template[name]?.refresh_button && (
|
|
||||||
<div className="flex-grow">
|
|
||||||
<RefreshButton
|
|
||||||
isLoading={isLoading}
|
|
||||||
disabled={disabled}
|
|
||||||
name={name}
|
|
||||||
data={data}
|
|
||||||
button_text={
|
|
||||||
data.node?.template[name].refresh_button_text
|
|
||||||
}
|
|
||||||
className="extra-side-bar-buttons mt-1"
|
|
||||||
handleUpdateValues={handleRefreshButtonPress}
|
|
||||||
id={"refresh-button-" + name}
|
|
||||||
/>
|
|
||||||
</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={
|
|
||||||
"flex-grow " +
|
|
||||||
(data.node?.template[name]?.refresh_button ? "w-5/6" : "")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<InputGlobalComponent
|
|
||||||
disabled={disabled}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
setDb={(value) => {
|
|
||||||
setNode(data.id, (oldNode) => {
|
|
||||||
let newNode = cloneDeep(oldNode);
|
|
||||||
newNode.data = {
|
|
||||||
...newNode.data,
|
|
||||||
};
|
|
||||||
newNode.data.node.template[name].load_from_db = value;
|
|
||||||
return newNode;
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
name={name}
|
|
||||||
data={data.node?.template[name]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{data.node?.template[name]?.refresh_button && (
|
|
||||||
<div className="w-1/6">
|
|
||||||
<RefreshButton
|
|
||||||
isLoading={isLoading}
|
|
||||||
disabled={disabled}
|
|
||||||
name={name}
|
|
||||||
data={data}
|
|
||||||
button_text={
|
|
||||||
data.node?.template[name].refresh_button_text
|
|
||||||
}
|
|
||||||
className="extra-side-bar-buttons ml-2 mt-1"
|
|
||||||
handleUpdateValues={handleRefreshButtonPress}
|
|
||||||
id={"refresh-button-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "bool"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<ToggleShadComponent
|
|
||||||
id={"toggle-" + name}
|
|
||||||
disabled={disabled}
|
|
||||||
enabled={data.node?.template[name]?.value ?? false}
|
|
||||||
setEnabled={handleOnNewValue}
|
|
||||||
size="large"
|
|
||||||
editNode={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "float"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<FloatComponent
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node?.template[name]?.value ?? ""}
|
|
||||||
rangeSpec={data.node?.template[name]?.rangeSpec}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case
|
|
||||||
condition={
|
|
||||||
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 gap-2">
|
|
||||||
<div className="flex-1">
|
|
||||||
<Dropdown
|
|
||||||
disabled={disabled}
|
|
||||||
isLoading={isLoading}
|
|
||||||
options={data.node!.template[name]?.options}
|
|
||||||
onSelect={handleOnNewValue}
|
|
||||||
value={data.node!.template[name]?.value}
|
|
||||||
id={"dropdown-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{data.node?.template[name]?.refresh_button && (
|
|
||||||
<div className="w-1/6">
|
|
||||||
<RefreshButton
|
|
||||||
isLoading={isLoading}
|
|
||||||
disabled={disabled}
|
|
||||||
name={name}
|
|
||||||
data={data}
|
|
||||||
button_text={data.node?.template[name]?.refresh_button_text}
|
|
||||||
handleUpdateValues={handleRefreshButtonPress}
|
|
||||||
id={"refresh-button-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "code"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<CodeAreaComponent
|
|
||||||
readonly={
|
|
||||||
data.node?.flow && data.node.template[name]?.dynamic
|
|
||||||
? true
|
|
||||||
: false
|
|
||||||
}
|
|
||||||
dynamic={data.node?.template[name]?.dynamic ?? false}
|
|
||||||
setNodeClass={handleNodeClass}
|
|
||||||
nodeClass={data.node}
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node?.template[name]?.value ?? ""}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
id={"code-input-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "file"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<InputFileComponent
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node?.template[name]?.value ?? ""}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
fileTypes={data.node?.template[name]?.fileTypes}
|
|
||||||
onFileChange={(filePath: string) => {
|
|
||||||
data.node!.template[name].file_path = filePath;
|
|
||||||
}}
|
|
||||||
></InputFileComponent>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "int"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<IntComponent
|
|
||||||
rangeSpec={data.node?.template[name]?.rangeSpec}
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node?.template[name]?.value ?? ""}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
id={"int-input-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "prompt"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<PromptAreaComponent
|
|
||||||
readonly={data.node?.flow ? true : false}
|
|
||||||
field_name={name}
|
|
||||||
setNodeClass={handleNodeClass}
|
|
||||||
nodeClass={data.node}
|
|
||||||
disabled={disabled}
|
|
||||||
value={data.node?.template[name]?.value ?? ""}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
id={"prompt-input-" + name}
|
|
||||||
data-testid={"prompt-input-" + name}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "NestedDict"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<DictComponent
|
|
||||||
disabled={disabled}
|
|
||||||
editNode={false}
|
|
||||||
value={
|
|
||||||
!data.node!.template[name]?.value ||
|
|
||||||
data.node!.template[name]?.value?.toString() === "{}"
|
|
||||||
? {}
|
|
||||||
: data.node!.template[name]?.value
|
|
||||||
}
|
|
||||||
onChange={handleOnNewValue}
|
|
||||||
id="div-dict-input"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
|
|
||||||
<Case condition={left === true && type === "dict"}>
|
|
||||||
<div className="mt-2 w-full">
|
|
||||||
<KeypairListComponent
|
|
||||||
disabled={disabled}
|
|
||||||
editNode={false}
|
|
||||||
value={
|
|
||||||
data.node!.template[name]?.value?.length === 0 ||
|
|
||||||
!data.node!.template[name]?.value
|
|
||||||
? [{ "": "" }]
|
|
||||||
: convertObjToArray(data.node!.template[name]?.value, type!)
|
|
||||||
}
|
|
||||||
duplicateKey={errorDuplicateKey}
|
|
||||||
onChange={(newValue) => {
|
|
||||||
const valueToNumbers = convertValuesToNumbers(newValue);
|
|
||||||
setErrorDuplicateKey(hasDuplicateKeys(valueToNumbers));
|
|
||||||
// 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) {
|
|
||||||
handleOnNewValue(valueToNumbers);
|
|
||||||
} else handleOnNewValue(valueToNumbers[0]);
|
|
||||||
}}
|
|
||||||
isList={data.node?.template[name]?.list ?? false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Case>
|
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,849 +0,0 @@
|
||||||
import emojiRegex from "emoji-regex";
|
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
||||||
import { NodeToolbar, useUpdateNodeInternals } from "reactflow";
|
|
||||||
import IconComponent from "../../components/genericIconComponent";
|
|
||||||
import InputComponent from "../../components/inputComponent";
|
|
||||||
import ShadTooltip from "../../components/shadTooltipComponent";
|
|
||||||
import { Button } from "../../components/ui/button";
|
|
||||||
import Checkmark from "../../components/ui/checkmark";
|
|
||||||
import Loading from "../../components/ui/loading";
|
|
||||||
import { Textarea } from "../../components/ui/textarea";
|
|
||||||
import Xmark from "../../components/ui/xmark";
|
|
||||||
import {
|
|
||||||
RUN_TIMESTAMP_PREFIX,
|
|
||||||
STATUS_BUILD,
|
|
||||||
STATUS_BUILDING,
|
|
||||||
} from "../../constants/constants";
|
|
||||||
import { BuildStatus } from "../../constants/enums";
|
|
||||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
|
||||||
import useAlertStore from "../../stores/alertStore";
|
|
||||||
import { useDarkStore } from "../../stores/darkStore";
|
|
||||||
import useFlowStore from "../../stores/flowStore";
|
|
||||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
|
||||||
import { useTypesStore } from "../../stores/typesStore";
|
|
||||||
import { APIClassType } from "../../types/api";
|
|
||||||
import { validationStatusType } from "../../types/components";
|
|
||||||
import { NodeDataType } from "../../types/flow";
|
|
||||||
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
|
||||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
|
||||||
import { classNames, cn } from "../../utils/utils";
|
|
||||||
import getFieldTitle from "../utils/get-field-title";
|
|
||||||
import sortFields from "../utils/sort-fields";
|
|
||||||
import ParameterComponent from "./components/parameterComponent";
|
|
||||||
|
|
||||||
export default function GenericNode({
|
|
||||||
data,
|
|
||||||
xPos,
|
|
||||||
yPos,
|
|
||||||
selected,
|
|
||||||
}: {
|
|
||||||
data: NodeDataType;
|
|
||||||
selected: boolean;
|
|
||||||
xPos: number;
|
|
||||||
yPos: number;
|
|
||||||
}): JSX.Element {
|
|
||||||
const types = useTypesStore((state) => state.types);
|
|
||||||
const templates = useTypesStore((state) => state.templates);
|
|
||||||
const deleteNode = useFlowStore((state) => state.deleteNode);
|
|
||||||
const flowPool = useFlowStore((state) => state.flowPool);
|
|
||||||
const buildFlow = useFlowStore((state) => state.buildFlow);
|
|
||||||
const setNode = useFlowStore((state) => state.setNode);
|
|
||||||
const updateNodeInternals = useUpdateNodeInternals();
|
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
|
||||||
const name = nodeIconsLucide[data.type] ? data.type : types[data.type];
|
|
||||||
const [inputName, setInputName] = useState(false);
|
|
||||||
const [nodeName, setNodeName] = useState(data.node!.display_name);
|
|
||||||
const [inputDescription, setInputDescription] = useState(false);
|
|
||||||
const [nodeDescription, setNodeDescription] = useState(
|
|
||||||
data.node?.description!
|
|
||||||
);
|
|
||||||
const [isOutdated, setIsOutdated] = useState(false);
|
|
||||||
const buildStatus = useFlowStore(
|
|
||||||
(state) => state.flowBuildStatus[data.id]?.status
|
|
||||||
);
|
|
||||||
const lastRunTime = useFlowStore(
|
|
||||||
(state) => state.flowBuildStatus[data.id]?.timestamp
|
|
||||||
);
|
|
||||||
const [validationStatus, setValidationStatus] =
|
|
||||||
useState<validationStatusType | null>(null);
|
|
||||||
const [handles, setHandles] = useState<number>(0);
|
|
||||||
|
|
||||||
const [validationString, setValidationString] = useState<string>("");
|
|
||||||
|
|
||||||
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// This one should run only once
|
|
||||||
// first check if data.type in NATIVE_CATEGORIES
|
|
||||||
// if not return
|
|
||||||
if (!data.node?.template?.code?.value) return;
|
|
||||||
const thisNodeTemplate = templates[data.type]?.template;
|
|
||||||
// if the template does not have a code key
|
|
||||||
// return
|
|
||||||
if (!thisNodeTemplate?.code) return;
|
|
||||||
const currentCode = thisNodeTemplate.code?.value;
|
|
||||||
const thisNodesCode = data.node!.template?.code?.value;
|
|
||||||
const componentsToIgnore = ["Custom Component"];
|
|
||||||
if (
|
|
||||||
currentCode !== thisNodesCode &&
|
|
||||||
!componentsToIgnore.includes(data.node!.display_name)
|
|
||||||
) {
|
|
||||||
setIsOutdated(true);
|
|
||||||
} else {
|
|
||||||
setIsOutdated(false);
|
|
||||||
}
|
|
||||||
// template.code can be undefined
|
|
||||||
}, [data.node?.template?.code?.value]);
|
|
||||||
|
|
||||||
const updateNodeCode = useCallback(
|
|
||||||
(newNodeClass: APIClassType, code: string, name: string) => {
|
|
||||||
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;
|
|
||||||
setIsOutdated(false);
|
|
||||||
|
|
||||||
return newNode;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateNodeInternals(data.id);
|
|
||||||
},
|
|
||||||
[data.id, data.node, setNode, setIsOutdated]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!data.node!.template) {
|
|
||||||
setErrorData({
|
|
||||||
title: `Error in component ${data.node!.display_name}`,
|
|
||||||
list: [
|
|
||||||
`The component ${data.node!.display_name} has no template.`,
|
|
||||||
`Please contact the developer of the component to fix this issue.`,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
takeSnapshot();
|
|
||||||
deleteNode(data.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function countHandles(): void {
|
|
||||||
let count = Object.keys(data.node!.template)
|
|
||||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
|
||||||
.map((templateCamp) => {
|
|
||||||
const { template } = data.node!;
|
|
||||||
if (template[templateCamp].input_types) return true;
|
|
||||||
if (!template[templateCamp].show) return false;
|
|
||||||
switch (template[templateCamp].type) {
|
|
||||||
case "str":
|
|
||||||
case "bool":
|
|
||||||
case "float":
|
|
||||||
case "code":
|
|
||||||
case "prompt":
|
|
||||||
case "file":
|
|
||||||
case "int":
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.reduce((total, value) => total + (value ? 1 : 0), 0);
|
|
||||||
|
|
||||||
setHandles(count);
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
|
||||||
countHandles();
|
|
||||||
}, [data, data.node]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!selected) {
|
|
||||||
setInputName(false);
|
|
||||||
setInputDescription(false);
|
|
||||||
}
|
|
||||||
}, [selected]);
|
|
||||||
|
|
||||||
// State for outline color
|
|
||||||
const isBuilding = useFlowStore((state) => state.isBuilding);
|
|
||||||
|
|
||||||
// should be empty string if no duration
|
|
||||||
// else should be `Duration: ${duration}`
|
|
||||||
const getDurationString = (duration: number | undefined): string => {
|
|
||||||
if (duration === undefined) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return `${duration}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const durationString = getDurationString(validationStatus?.data.duration);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setNodeDescription(data.node!.description);
|
|
||||||
}, [data.node!.description]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setNodeName(data.node!.display_name);
|
|
||||||
}, [data.node!.display_name]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const relevantData =
|
|
||||||
flowPool[data.id] && flowPool[data.id]?.length > 0
|
|
||||||
? flowPool[data.id][flowPool[data.id].length - 1]
|
|
||||||
: null;
|
|
||||||
if (relevantData) {
|
|
||||||
// Extract validation information from relevantData and update the validationStatus state
|
|
||||||
setValidationStatus(relevantData);
|
|
||||||
} else {
|
|
||||||
setValidationStatus(null);
|
|
||||||
}
|
|
||||||
}, [flowPool[data.id], data.id]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (validationStatus?.params) {
|
|
||||||
// if it is not a string turn it into a string
|
|
||||||
let newValidationString = validationStatus.params;
|
|
||||||
if (typeof newValidationString !== "string") {
|
|
||||||
newValidationString = JSON.stringify(validationStatus.params);
|
|
||||||
}
|
|
||||||
|
|
||||||
setValidationString(newValidationString);
|
|
||||||
}
|
|
||||||
}, [validationStatus, validationStatus?.params]);
|
|
||||||
|
|
||||||
const [showNode, setShowNode] = useState(data.showNode ?? true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setShowNode(data.showNode ?? true);
|
|
||||||
}, [data.showNode]);
|
|
||||||
|
|
||||||
const nameEditable = true;
|
|
||||||
|
|
||||||
const isEmoji = emojiRegex().test(data?.node?.icon!);
|
|
||||||
|
|
||||||
const iconNodeRender = useCallback(() => {
|
|
||||||
const iconElement = data?.node?.icon;
|
|
||||||
const iconColor = nodeColors[types[data.type]];
|
|
||||||
const iconName =
|
|
||||||
iconElement || (data.node?.flow ? "group_components" : name);
|
|
||||||
const iconClassName = `generic-node-icon ${
|
|
||||||
!showNode ? " absolute inset-x-6 h-12 w-12 " : ""
|
|
||||||
}`;
|
|
||||||
if (iconElement && isEmoji) {
|
|
||||||
return nodeIconFragment(iconElement);
|
|
||||||
} else {
|
|
||||||
return checkNodeIconFragment(iconColor, iconName, iconClassName);
|
|
||||||
}
|
|
||||||
}, [data, isEmoji, name, showNode]);
|
|
||||||
|
|
||||||
const nodeIconFragment = (icon) => {
|
|
||||||
return <span className="text-lg">{icon}</span>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkNodeIconFragment = (iconColor, iconName, iconClassName) => {
|
|
||||||
return (
|
|
||||||
<IconComponent
|
|
||||||
name={iconName}
|
|
||||||
className={iconClassName}
|
|
||||||
iconColor={iconColor}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isDark = useDarkStore((state) => state.dark);
|
|
||||||
const renderIconStatus = (
|
|
||||||
buildStatus: BuildStatus | undefined,
|
|
||||||
validationStatus: validationStatusType | null
|
|
||||||
) => {
|
|
||||||
if (buildStatus === BuildStatus.BUILDING) {
|
|
||||||
return <Loading className="text-medium-indigo" />;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<IconComponent
|
|
||||||
name="Play"
|
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-medium-indigo opacity-0 transition-all group-hover:opacity-100"
|
|
||||||
/>
|
|
||||||
{validationStatus && validationStatus.valid ? (
|
|
||||||
<Checkmark
|
|
||||||
className="absolute ml-0.5 h-5 stroke-2 text-status-green opacity-100 transition-all group-hover:opacity-0"
|
|
||||||
isVisible={true}
|
|
||||||
/>
|
|
||||||
) : validationStatus &&
|
|
||||||
!validationStatus.valid &&
|
|
||||||
buildStatus === BuildStatus.INACTIVE ? (
|
|
||||||
<IconComponent
|
|
||||||
name="Play"
|
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-green opacity-30 transition-all group-hover:opacity-0"
|
|
||||||
/>
|
|
||||||
) : buildStatus === BuildStatus.ERROR ||
|
|
||||||
(validationStatus && !validationStatus.valid) ? (
|
|
||||||
<Xmark
|
|
||||||
isVisible={true}
|
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-status-red opacity-100 transition-all group-hover:opacity-0"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<IconComponent
|
|
||||||
name="Play"
|
|
||||||
className="absolute ml-0.5 h-5 fill-current stroke-2 text-muted-foreground opacity-100 transition-all group-hover:opacity-0"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const getSpecificClassFromBuildStatus = (
|
|
||||||
buildStatus: BuildStatus | undefined,
|
|
||||||
validationStatus: validationStatusType | null
|
|
||||||
) => {
|
|
||||||
let isInvalid = validationStatus && !validationStatus.valid;
|
|
||||||
|
|
||||||
if (buildStatus === BuildStatus.INACTIVE) {
|
|
||||||
// INACTIVE should have its own class
|
|
||||||
return "inactive-status";
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(buildStatus === BuildStatus.BUILT && isInvalid) ||
|
|
||||||
buildStatus === BuildStatus.ERROR
|
|
||||||
) {
|
|
||||||
return isDark ? "built-invalid-status-dark" : "built-invalid-status";
|
|
||||||
} else if (buildStatus === BuildStatus.BUILDING) {
|
|
||||||
return "building-status";
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNodeBorderClassName = (
|
|
||||||
selected: boolean,
|
|
||||||
showNode: boolean,
|
|
||||||
buildStatus: BuildStatus | undefined,
|
|
||||||
validationStatus: validationStatusType | null
|
|
||||||
) => {
|
|
||||||
const specificClassFromBuildStatus = getSpecificClassFromBuildStatus(
|
|
||||||
buildStatus,
|
|
||||||
validationStatus
|
|
||||||
);
|
|
||||||
|
|
||||||
const baseBorderClass = getBaseBorderClass(selected);
|
|
||||||
const nodeSizeClass = getNodeSizeClass(showNode);
|
|
||||||
const names = classNames(
|
|
||||||
baseBorderClass,
|
|
||||||
nodeSizeClass,
|
|
||||||
"generic-node-div",
|
|
||||||
specificClassFromBuildStatus
|
|
||||||
);
|
|
||||||
return names;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getBaseBorderClass = (selected) => {
|
|
||||||
let className = selected ? "border border-ring" : "border";
|
|
||||||
let frozenClass = selected ? "border-ring-frozen" : "border-frozen";
|
|
||||||
return data.node?.frozen ? frozenClass : className;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getNodeSizeClass = (showNode) =>
|
|
||||||
showNode ? "w-96 rounded-lg" : "w-26 h-26 rounded-full";
|
|
||||||
|
|
||||||
const memoizedNodeToolbarComponent = useMemo(() => {
|
|
||||||
return (
|
|
||||||
<NodeToolbar>
|
|
||||||
<NodeToolbarComponent
|
|
||||||
data={data}
|
|
||||||
deleteNode={(id) => {
|
|
||||||
takeSnapshot();
|
|
||||||
deleteNode(id);
|
|
||||||
}}
|
|
||||||
setShowNode={(show) => {
|
|
||||||
setNode(data.id, (old) => ({
|
|
||||||
...old,
|
|
||||||
data: { ...old.data, showNode: show },
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
setShowState={setShowNode}
|
|
||||||
numberOfHandles={handles}
|
|
||||||
showNode={showNode}
|
|
||||||
openAdvancedModal={false}
|
|
||||||
onCloseAdvancedModal={() => {}}
|
|
||||||
updateNodeCode={updateNodeCode}
|
|
||||||
isOutdated={isOutdated}
|
|
||||||
selected={selected}
|
|
||||||
/>
|
|
||||||
</NodeToolbar>
|
|
||||||
);
|
|
||||||
}, [
|
|
||||||
data,
|
|
||||||
deleteNode,
|
|
||||||
takeSnapshot,
|
|
||||||
setNode,
|
|
||||||
setShowNode,
|
|
||||||
handles,
|
|
||||||
showNode,
|
|
||||||
updateNodeCode,
|
|
||||||
isOutdated,
|
|
||||||
selected,
|
|
||||||
]);
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{memoizedNodeToolbarComponent}
|
|
||||||
<div
|
|
||||||
className={getNodeBorderClassName(
|
|
||||||
selected,
|
|
||||||
showNode,
|
|
||||||
buildStatus,
|
|
||||||
validationStatus
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{data.node?.beta && showNode && (
|
|
||||||
<div className="beta-badge-wrapper">
|
|
||||||
<div className="beta-badge-content">BETA</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
data-testid={"div-generic-node"}
|
|
||||||
className={
|
|
||||||
"generic-node-div-title " +
|
|
||||||
(!showNode
|
|
||||||
? " relative h-24 w-24 rounded-full "
|
|
||||||
: " justify-between rounded-t-lg ")
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
"generic-node-title-arrangement rounded-full" +
|
|
||||||
(!showNode && " justify-center ")
|
|
||||||
}
|
|
||||||
data-testid="generic-node-title-arrangement"
|
|
||||||
>
|
|
||||||
{iconNodeRender()}
|
|
||||||
{showNode && (
|
|
||||||
<div className="generic-node-tooltip-div">
|
|
||||||
{nameEditable && inputName ? (
|
|
||||||
<div>
|
|
||||||
<InputComponent
|
|
||||||
onBlur={() => {
|
|
||||||
setInputName(false);
|
|
||||||
if (nodeName.trim() !== "") {
|
|
||||||
setNodeName(nodeName);
|
|
||||||
setNode(data.id, (old) => ({
|
|
||||||
...old,
|
|
||||||
data: {
|
|
||||||
...old.data,
|
|
||||||
node: {
|
|
||||||
...old.data.node,
|
|
||||||
display_name: nodeName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
setNodeName(data.node!.display_name);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
value={nodeName}
|
|
||||||
onChange={setNodeName}
|
|
||||||
password={false}
|
|
||||||
blurOnEnter={true}
|
|
||||||
id={`input-title-${data.node?.display_name}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="group flex items-start gap-1.5">
|
|
||||||
<ShadTooltip content={data.node?.display_name}>
|
|
||||||
<div
|
|
||||||
onClick={(event) => {
|
|
||||||
if (nameEditable) {
|
|
||||||
setInputName(true);
|
|
||||||
}
|
|
||||||
takeSnapshot();
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
}}
|
|
||||||
data-testid={"title-" + data.node?.display_name}
|
|
||||||
className="generic-node-tooltip-div cursor-text text-primary"
|
|
||||||
>
|
|
||||||
{data.node?.display_name}
|
|
||||||
</div>
|
|
||||||
</ShadTooltip>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
{!showNode && (
|
|
||||||
<>
|
|
||||||
{Object.keys(data.node!.template)
|
|
||||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
|
||||||
.map(
|
|
||||||
(templateField: string, idx) =>
|
|
||||||
data.node!.template[templateField].show &&
|
|
||||||
!data.node!.template[templateField].advanced && (
|
|
||||||
<ParameterComponent
|
|
||||||
index={idx.toString()}
|
|
||||||
key={scapedJSONStringfy({
|
|
||||||
inputTypes:
|
|
||||||
data.node!.template[templateField].input_types,
|
|
||||||
type: data.node!.template[templateField].type,
|
|
||||||
id: data.id,
|
|
||||||
fieldName: templateField,
|
|
||||||
proxy: data.node!.template[templateField].proxy,
|
|
||||||
})}
|
|
||||||
data={data}
|
|
||||||
color={
|
|
||||||
data.node?.template[templateField].input_types &&
|
|
||||||
data.node?.template[templateField].input_types!
|
|
||||||
.length > 0
|
|
||||||
? nodeColors[
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types![
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types!.length - 1
|
|
||||||
]
|
|
||||||
] ??
|
|
||||||
nodeColors[
|
|
||||||
types[
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types![
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types!.length - 1
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
: nodeColors[
|
|
||||||
data.node?.template[templateField].type!
|
|
||||||
] ??
|
|
||||||
nodeColors[
|
|
||||||
types[
|
|
||||||
data.node?.template[templateField].type!
|
|
||||||
]
|
|
||||||
] ??
|
|
||||||
nodeColors.unknown
|
|
||||||
}
|
|
||||||
title={getFieldTitle(
|
|
||||||
data.node?.template!,
|
|
||||||
templateField
|
|
||||||
)}
|
|
||||||
info={data.node?.template[templateField].info}
|
|
||||||
name={templateField}
|
|
||||||
tooltipTitle={
|
|
||||||
data.node?.template[
|
|
||||||
templateField
|
|
||||||
].input_types?.join("\n") ??
|
|
||||||
data.node?.template[templateField].type
|
|
||||||
}
|
|
||||||
required={
|
|
||||||
data.node!.template[templateField].required
|
|
||||||
}
|
|
||||||
id={{
|
|
||||||
inputTypes:
|
|
||||||
data.node!.template[templateField].input_types,
|
|
||||||
type: data.node!.template[templateField].type,
|
|
||||||
id: data.id,
|
|
||||||
fieldName: templateField,
|
|
||||||
}}
|
|
||||||
left={true}
|
|
||||||
type={data.node?.template[templateField].type}
|
|
||||||
optionalHandle={
|
|
||||||
data.node?.template[templateField].input_types
|
|
||||||
}
|
|
||||||
proxy={data.node?.template[templateField].proxy}
|
|
||||||
showNode={showNode}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
<ParameterComponent
|
|
||||||
key={scapedJSONStringfy({
|
|
||||||
baseClasses: data.node!.base_classes,
|
|
||||||
id: data.id,
|
|
||||||
dataType: data.type,
|
|
||||||
})}
|
|
||||||
data={data}
|
|
||||||
color={nodeColors[types[data.type]] ?? nodeColors.unknown}
|
|
||||||
title={
|
|
||||||
data.node?.output_types &&
|
|
||||||
data.node.output_types.length > 0
|
|
||||||
? data.node.output_types.join(" | ")
|
|
||||||
: data.type
|
|
||||||
}
|
|
||||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
|
||||||
id={{
|
|
||||||
baseClasses: data.node!.base_classes,
|
|
||||||
id: data.id,
|
|
||||||
dataType: data.type,
|
|
||||||
}}
|
|
||||||
type={data.node?.base_classes.join("|")}
|
|
||||||
left={false}
|
|
||||||
showNode={showNode}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{showNode && (
|
|
||||||
<ShadTooltip
|
|
||||||
content={
|
|
||||||
buildStatus === BuildStatus.BUILDING ? (
|
|
||||||
<span> {STATUS_BUILDING} </span>
|
|
||||||
) : !validationStatus ? (
|
|
||||||
<span className="flex">{STATUS_BUILD}</span>
|
|
||||||
) : (
|
|
||||||
<div className="max-h-100 p-2">
|
|
||||||
<div>
|
|
||||||
{lastRunTime && (
|
|
||||||
<div className="justify-left flex font-normal text-muted-foreground">
|
|
||||||
<div>{RUN_TIMESTAMP_PREFIX}</div>
|
|
||||||
<div className="ml-1 text-status-blue">
|
|
||||||
{lastRunTime}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="justify-left flex font-normal text-muted-foreground">
|
|
||||||
<div>Duration:</div>
|
|
||||||
<div className="mb-3 ml-1 text-status-blue">
|
|
||||||
{validationStatus?.data.duration}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<span className="mb-2 mt-2 flex justify-center font-semibold text-muted-foreground">
|
|
||||||
Output
|
|
||||||
</span>
|
|
||||||
<div className="max-h-96 overflow-auto font-normal custom-scroll">
|
|
||||||
{validationString.split("\n").map((line, index) => (
|
|
||||||
<div className="font-normal" key={index}>
|
|
||||||
{line}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
side="bottom"
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (buildStatus === BuildStatus.BUILDING || isBuilding)
|
|
||||||
return;
|
|
||||||
setValidationStatus(null);
|
|
||||||
buildFlow({ stopNodeId: data.id });
|
|
||||||
}}
|
|
||||||
variant="secondary"
|
|
||||||
className={"group h-9 px-1.5"}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
data-testid={
|
|
||||||
`button_run_` + data?.node?.display_name.toLowerCase()
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="generic-node-status-position flex items-center justify-center">
|
|
||||||
{renderIconStatus(buildStatus, validationStatus)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Button>
|
|
||||||
</ShadTooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showNode && (
|
|
||||||
<div
|
|
||||||
className={
|
|
||||||
showNode
|
|
||||||
? data.node?.description === "" && !nameEditable
|
|
||||||
? "pb-5"
|
|
||||||
: "py-5"
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div className="generic-node-desc">
|
|
||||||
{showNode && nameEditable && inputDescription ? (
|
|
||||||
<Textarea
|
|
||||||
autoFocus
|
|
||||||
onBlur={() => {
|
|
||||||
setInputDescription(false);
|
|
||||||
setInputName(false);
|
|
||||||
setNodeDescription(nodeDescription);
|
|
||||||
setNode(data.id, (old) => ({
|
|
||||||
...old,
|
|
||||||
data: {
|
|
||||||
...old.data,
|
|
||||||
node: {
|
|
||||||
...old.data.node,
|
|
||||||
description: nodeDescription,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
value={nodeDescription}
|
|
||||||
onChange={(e) => setNodeDescription(e.target.value)}
|
|
||||||
onKeyDown={(e) => {
|
|
||||||
handleKeyDown(e, nodeDescription, "");
|
|
||||||
if (
|
|
||||||
e.key === "Enter" &&
|
|
||||||
e.shiftKey === false &&
|
|
||||||
e.ctrlKey === false &&
|
|
||||||
e.altKey === false
|
|
||||||
) {
|
|
||||||
setInputDescription(false);
|
|
||||||
setNodeDescription(nodeDescription);
|
|
||||||
setNode(data.id, (old) => ({
|
|
||||||
...old,
|
|
||||||
data: {
|
|
||||||
...old.data,
|
|
||||||
node: {
|
|
||||||
...old.data.node,
|
|
||||||
description: nodeDescription,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"generic-node-desc-text cursor-text truncate-multiline word-break-break-word",
|
|
||||||
(data.node?.description === "" ||
|
|
||||||
!data.node?.description) &&
|
|
||||||
nameEditable
|
|
||||||
? "font-light italic"
|
|
||||||
: ""
|
|
||||||
)}
|
|
||||||
onClick={(e) => {
|
|
||||||
setInputDescription(true);
|
|
||||||
takeSnapshot();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{(data.node?.description === "" || !data.node?.description) &&
|
|
||||||
nameEditable
|
|
||||||
? "Double Click to Edit Description"
|
|
||||||
: data.node?.description}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<>
|
|
||||||
{Object.keys(data.node!.template)
|
|
||||||
.filter((templateField) => templateField.charAt(0) !== "_")
|
|
||||||
.sort((a, b) => sortFields(a, b, data.node?.field_order ?? []))
|
|
||||||
.map((templateField: string, idx) => (
|
|
||||||
<div key={idx}>
|
|
||||||
{data.node!.template[templateField].show &&
|
|
||||||
!data.node!.template[templateField].advanced ? (
|
|
||||||
<ParameterComponent
|
|
||||||
index={idx.toString()}
|
|
||||||
key={scapedJSONStringfy({
|
|
||||||
inputTypes:
|
|
||||||
data.node!.template[templateField].input_types,
|
|
||||||
type: data.node!.template[templateField].type,
|
|
||||||
id: data.id,
|
|
||||||
fieldName: templateField,
|
|
||||||
proxy: data.node!.template[templateField].proxy,
|
|
||||||
})}
|
|
||||||
data={data}
|
|
||||||
color={
|
|
||||||
data.node?.template[templateField].input_types &&
|
|
||||||
data.node?.template[templateField].input_types!
|
|
||||||
.length > 0
|
|
||||||
? nodeColors[
|
|
||||||
data.node?.template[templateField].input_types![
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types!.length - 1
|
|
||||||
]
|
|
||||||
] ??
|
|
||||||
nodeColors[
|
|
||||||
types[
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types![
|
|
||||||
data.node?.template[templateField]
|
|
||||||
.input_types!.length - 1
|
|
||||||
]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
: nodeColors[
|
|
||||||
data.node?.template[templateField].type!
|
|
||||||
] ??
|
|
||||||
nodeColors[
|
|
||||||
types[data.node?.template[templateField].type!]
|
|
||||||
] ??
|
|
||||||
nodeColors.unknown
|
|
||||||
}
|
|
||||||
title={getFieldTitle(
|
|
||||||
data.node?.template!,
|
|
||||||
templateField
|
|
||||||
)}
|
|
||||||
info={data.node?.template[templateField].info}
|
|
||||||
name={templateField}
|
|
||||||
tooltipTitle={
|
|
||||||
data.node?.template[templateField].input_types?.join(
|
|
||||||
"\n"
|
|
||||||
) ?? data.node?.template[templateField].type
|
|
||||||
}
|
|
||||||
required={data.node!.template[templateField].required}
|
|
||||||
id={{
|
|
||||||
inputTypes:
|
|
||||||
data.node!.template[templateField].input_types,
|
|
||||||
type: data.node!.template[templateField].type,
|
|
||||||
id: data.id,
|
|
||||||
fieldName: templateField,
|
|
||||||
}}
|
|
||||||
left={true}
|
|
||||||
type={data.node?.template[templateField].type}
|
|
||||||
optionalHandle={
|
|
||||||
data.node?.template[templateField].input_types
|
|
||||||
}
|
|
||||||
proxy={data.node?.template[templateField].proxy}
|
|
||||||
showNode={showNode}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
Object.keys(data.node!.template).length < 1 ? "hidden" : "",
|
|
||||||
"flex-max-width justify-center"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{" "}
|
|
||||||
</div>
|
|
||||||
{data.node!.base_classes.length > 0 && (
|
|
||||||
<ParameterComponent
|
|
||||||
key={scapedJSONStringfy({
|
|
||||||
baseClasses: data.node!.base_classes,
|
|
||||||
id: data.id,
|
|
||||||
dataType: data.type,
|
|
||||||
})}
|
|
||||||
data={data}
|
|
||||||
color={
|
|
||||||
(data.node?.output_types &&
|
|
||||||
data.node.output_types.length > 0
|
|
||||||
? nodeColors[data.node.output_types[0]] ??
|
|
||||||
nodeColors[types[data.node.output_types[0]]]
|
|
||||||
: nodeColors[types[data.type]]) ?? nodeColors.unknown
|
|
||||||
}
|
|
||||||
title={
|
|
||||||
data.node?.output_types && data.node.output_types.length > 0
|
|
||||||
? data.node.output_types.join(" | ")
|
|
||||||
: data.type
|
|
||||||
}
|
|
||||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
|
||||||
id={{
|
|
||||||
baseClasses: data.node!.base_classes,
|
|
||||||
id: data.id,
|
|
||||||
dataType: data.type,
|
|
||||||
}}
|
|
||||||
type={data.node?.base_classes.join("|")}
|
|
||||||
left={false}
|
|
||||||
showNode={showNode}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
import { cloneDeep } from "lodash";
|
|
||||||
import useAlertStore from "../../stores/alertStore";
|
|
||||||
import { ResponseErrorTypeAPI } from "../../types/api";
|
|
||||||
|
|
||||||
const useHandleOnNewValue = (
|
|
||||||
data,
|
|
||||||
name,
|
|
||||||
takeSnapshot,
|
|
||||||
handleUpdateValues,
|
|
||||||
debouncedHandleUpdateValues,
|
|
||||||
setNode,
|
|
||||||
renderTooltips,
|
|
||||||
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;
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
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;
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
import { APITemplateType } from "../../types/api";
|
|
||||||
|
|
||||||
export default function getFieldTitle(
|
|
||||||
template: APITemplateType,
|
|
||||||
templateField: string
|
|
||||||
): string {
|
|
||||||
return template[templateField].display_name
|
|
||||||
? template[templateField].display_name!
|
|
||||||
: template[templateField].name ?? templateField;
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import { priorityFields } from "../../constants/constants";
|
|
||||||
|
|
||||||
export default function sortFields(a, b, fieldOrder) {
|
|
||||||
// Early return for empty fields
|
|
||||||
if (!a && !b) return 0;
|
|
||||||
if (!a) return 1;
|
|
||||||
if (!b) return -1;
|
|
||||||
|
|
||||||
// Normalize the case to ensure case-insensitive comparison
|
|
||||||
const normalizedFieldA = a.toLowerCase();
|
|
||||||
const normalizedFieldB = b.toLowerCase();
|
|
||||||
|
|
||||||
const aIsPriority = priorityFields.has(normalizedFieldA);
|
|
||||||
const bIsPriority = priorityFields.has(normalizedFieldB);
|
|
||||||
|
|
||||||
// Sort by priority
|
|
||||||
if (aIsPriority && !bIsPriority) return -1;
|
|
||||||
if (!aIsPriority && bIsPriority) return 1;
|
|
||||||
|
|
||||||
// Check if either field is in the fieldOrder array
|
|
||||||
const indexOfA = fieldOrder.indexOf(normalizedFieldA);
|
|
||||||
const indexOfB = fieldOrder.indexOf(normalizedFieldB);
|
|
||||||
|
|
||||||
// If both fields are in fieldOrder, sort by their order in the array
|
|
||||||
if (indexOfA !== -1 && indexOfB !== -1) {
|
|
||||||
return indexOfA - indexOfB;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If only one of the fields is in fieldOrder, that field comes first
|
|
||||||
if (indexOfA !== -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (indexOfB !== -1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default case for fields not in priorityFields and not found in fieldOrder
|
|
||||||
// You might want to sort them alphabetically or in another specific manner
|
|
||||||
return a.localeCompare(b);
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue