Merge branch 'feature/output_dropdown' into two_edges
This commit is contained in:
commit
c6b315bf60
21 changed files with 844 additions and 87 deletions
|
|
@ -201,7 +201,9 @@ class Vertex:
|
||||||
|
|
||||||
self.description = self.data["node"].get("description", "")
|
self.description = self.data["node"].get("description", "")
|
||||||
self.frozen = self.data["node"].get("frozen", False)
|
self.frozen = self.data["node"].get("frozen", False)
|
||||||
self.selected_output_type = self.data["node"].get("selected_output_type")
|
self.selected_output_type = (
|
||||||
|
str(self.data.get("selected_output_type")).strip() if self.data.get("selected_output_type") else None
|
||||||
|
)
|
||||||
self.is_input = self.data["node"].get("is_input") or self.is_input
|
self.is_input = self.data["node"].get("is_input") or self.is_input
|
||||||
self.is_output = self.data["node"].get("is_output") or self.is_output
|
self.is_output = self.data["node"].get("is_output") or self.is_output
|
||||||
template_dicts = {key: value for key, value in self.data["node"]["template"].items() if isinstance(value, dict)}
|
template_dicts = {key: value for key, value in self.data["node"]["template"].items() if isinstance(value, dict)}
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -253,6 +253,9 @@ def build_class_constructor(compiled_class, exec_globals, class_name):
|
||||||
globals()[module_name] = module
|
globals()[module_name] = module
|
||||||
|
|
||||||
instance = exec_globals[class_name](*args, **kwargs)
|
instance = exec_globals[class_name](*args, **kwargs)
|
||||||
|
# Get selected type from global scope
|
||||||
|
if instance.selected_output_type in exec_globals:
|
||||||
|
instance.selected_output_type = exec_globals[instance.selected_output_type]
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
build_custom_class.__globals__.update(exec_globals)
|
build_custom_class.__globals__.update(exec_globals)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { TOOLTIP_EMPTY } from "../../../../constants/constants";
|
||||||
|
import { groupByFamily } from "../../../../utils/utils";
|
||||||
|
import TooltipRenderComponent from "../tooltipRenderComponent";
|
||||||
|
import { useTypesStore } from "../../../../stores/typesStore";
|
||||||
|
import { NodeType } from "../../../../types/flow";
|
||||||
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
|
|
||||||
|
export default function HandleTooltips({
|
||||||
|
left,
|
||||||
|
tooltipTitle,
|
||||||
|
}: {
|
||||||
|
left: boolean;
|
||||||
|
nodes: NodeType[];
|
||||||
|
tooltipTitle: string;
|
||||||
|
}) {
|
||||||
|
const myData = useTypesStore((state) => state.data);
|
||||||
|
const nodes = useFlowStore((state) => state.nodes);
|
||||||
|
|
||||||
|
let groupedObj: any = groupByFamily(myData, tooltipTitle!, left, nodes!);
|
||||||
|
|
||||||
|
if (groupedObj && groupedObj.length > 0) {
|
||||||
|
//@ts-ignore
|
||||||
|
return groupedObj.map((item, index) => {
|
||||||
|
return <TooltipRenderComponent index={index} item={item} left={left} />;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
//@ts-ignore
|
||||||
|
return <span data-testid={`empty-tooltip-filter`}>{TOOLTIP_EMPTY}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
||||||
|
import { outputComponentType } from "../../../../types/components";
|
||||||
|
import { cn } from "../../../../utils/utils";
|
||||||
|
import useFlowStore from "../../../../stores/flowStore";
|
||||||
|
import { NodeDataType } from "../../../../types/flow";
|
||||||
|
import { cloneDeep } from "lodash";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../../../../components/ui/dropdown-menu";
|
||||||
|
import { useUpdateNodeInternals } from "reactflow";
|
||||||
|
|
||||||
|
export default function OutputComponent({
|
||||||
|
selected,
|
||||||
|
types,
|
||||||
|
frozen = false,
|
||||||
|
nodeId,
|
||||||
|
idx,
|
||||||
|
}: outputComponentType) {
|
||||||
|
const setNode = useFlowStore((state) => state.setNode);
|
||||||
|
const updateNodeInternals = useUpdateNodeInternals();
|
||||||
|
|
||||||
|
if (types.length < 2) {
|
||||||
|
return <span className={cn(frozen ? " text-ice" : "")}>{selected}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger>
|
||||||
|
<span
|
||||||
|
className={cn(frozen ? " text-ice" : "", "flex items-center gap-1")}
|
||||||
|
>
|
||||||
|
{selected}
|
||||||
|
<ForwardedIconComponent name="ChevronDown" className="h-4 w-4" />
|
||||||
|
</span>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent>
|
||||||
|
{types.map((type) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
onSelect={() => {
|
||||||
|
// TODO: UDPDATE SET NODE TO NEW NODE FORM
|
||||||
|
setNode(nodeId, (node) => {
|
||||||
|
const newNode = cloneDeep(node);
|
||||||
|
(newNode.data as NodeDataType).node!.outputs![idx].selected =
|
||||||
|
type;
|
||||||
|
return newNode;
|
||||||
|
});
|
||||||
|
updateNodeInternals(nodeId);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{type}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,591 @@
|
||||||
|
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 ForwardedIconComponent, {
|
||||||
|
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 {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "../../../../components/ui/dropdown-menu";
|
||||||
|
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, cn, 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 HandleTooltips from "../HandleTooltipComponent";
|
||||||
|
import OutputComponent from "../OutputComponent";
|
||||||
|
|
||||||
|
export default function ParameterComponent({
|
||||||
|
left,
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
tooltipTitle,
|
||||||
|
title,
|
||||||
|
color,
|
||||||
|
type,
|
||||||
|
name = "",
|
||||||
|
required = false,
|
||||||
|
optionalHandle = null,
|
||||||
|
info = "",
|
||||||
|
proxy,
|
||||||
|
showNode,
|
||||||
|
index,
|
||||||
|
}: ParameterComponentType): JSX.Element {
|
||||||
|
const infoHtml = useRef<HTMLDivElement & ReactNode>(null);
|
||||||
|
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 setFilterEdge = useFlowStore((state) => state.setFilterEdge);
|
||||||
|
|
||||||
|
const { handleOnNewValue: handleOnNewValueHook } = useHandleOnNewValue(
|
||||||
|
data,
|
||||||
|
name,
|
||||||
|
takeSnapshot,
|
||||||
|
handleUpdateValues,
|
||||||
|
debouncedHandleUpdateValues,
|
||||||
|
setNode,
|
||||||
|
isLoading,
|
||||||
|
setIsLoading,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { handleNodeClass: handleNodeClassHook } = useHandleNodeClass(
|
||||||
|
data,
|
||||||
|
name,
|
||||||
|
takeSnapshot,
|
||||||
|
setNode,
|
||||||
|
updateNodeInternals,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { handleRefreshButtonPress: handleRefreshButtonPressHook } =
|
||||||
|
useHandleRefreshButtonPress(setIsLoading, setNode);
|
||||||
|
|
||||||
|
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, 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 renderTitle() {
|
||||||
|
return !left ? (
|
||||||
|
<OutputComponent
|
||||||
|
idx={index}
|
||||||
|
types={type?.split("|") ?? []}
|
||||||
|
selected={title}
|
||||||
|
nodeId={data.id}
|
||||||
|
frozen={data.node?.frozen}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<span>{title}</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If optionalHandle is an empty list, then it is not an optional handle
|
||||||
|
if (optionalHandle && optionalHandle.length === 0) {
|
||||||
|
optionalHandle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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={
|
||||||
|
<HandleTooltips
|
||||||
|
left={left}
|
||||||
|
nodes={nodes}
|
||||||
|
tooltipTitle={tooltipTitle!}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
groupByFamily(myData, tooltipTitle!, left, nodes!),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
></Handle>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
<div
|
||||||
|
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>}>
|
||||||
|
{renderTitle()}
|
||||||
|
</ShadTooltip>
|
||||||
|
) : (
|
||||||
|
renderTitle()
|
||||||
|
)}
|
||||||
|
<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={
|
||||||
|
<HandleTooltips
|
||||||
|
left={left}
|
||||||
|
nodes={nodes}
|
||||||
|
tooltipTitle={tooltipTitle!}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
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(
|
||||||
|
groupByFamily(myData, tooltipTitle!, left, nodes!),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ShadTooltip>
|
||||||
|
</div>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Case
|
||||||
|
condition={
|
||||||
|
left === true &&
|
||||||
|
type === "str" &&
|
||||||
|
!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 ??
|
||||||
|
"Refresh"
|
||||||
|
}
|
||||||
|
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}
|
||||||
|
/>
|
||||||
|
</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 ??
|
||||||
|
"Refresh"
|
||||||
|
}
|
||||||
|
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">
|
||||||
|
<div className="w-5/6 flex-grow">
|
||||||
|
<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}
|
||||||
|
className="extra-side-bar-buttons ml-2 mt-1"
|
||||||
|
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() === "{}"
|
||||||
|
? {
|
||||||
|
// yourkey: "value",
|
||||||
|
}
|
||||||
|
: 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -553,7 +553,7 @@ export default function GenericNode({
|
||||||
data.node!.template[templateField].show &&
|
data.node!.template[templateField].show &&
|
||||||
!data.node!.template[templateField].advanced && (
|
!data.node!.template[templateField].advanced && (
|
||||||
<ParameterComponent
|
<ParameterComponent
|
||||||
index={idx.toString()}
|
index={idx}
|
||||||
key={scapedJSONStringfy({
|
key={scapedJSONStringfy({
|
||||||
inputTypes:
|
inputTypes:
|
||||||
data.node!.template[templateField].input_types,
|
data.node!.template[templateField].input_types,
|
||||||
|
|
@ -626,6 +626,7 @@ export default function GenericNode({
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
<ParameterComponent
|
<ParameterComponent
|
||||||
|
index={0}
|
||||||
key={scapedJSONStringfy({
|
key={scapedJSONStringfy({
|
||||||
baseClasses: data.node!.base_classes,
|
baseClasses: data.node!.base_classes,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
|
|
@ -636,7 +637,7 @@ export default function GenericNode({
|
||||||
title={
|
title={
|
||||||
data.node?.output_types &&
|
data.node?.output_types &&
|
||||||
data.node.output_types.length > 0
|
data.node.output_types.length > 0
|
||||||
? data.node.output_types.join(" | ")
|
? data.node.output_types.join("|")
|
||||||
: data.type
|
: data.type
|
||||||
}
|
}
|
||||||
tooltipTitle={data.node?.base_classes.join("\n")}
|
tooltipTitle={data.node?.base_classes.join("\n")}
|
||||||
|
|
@ -644,6 +645,7 @@ export default function GenericNode({
|
||||||
baseClasses: data.node!.base_classes,
|
baseClasses: data.node!.base_classes,
|
||||||
id: data.id,
|
id: data.id,
|
||||||
dataType: data.type,
|
dataType: data.type,
|
||||||
|
idx: 0,
|
||||||
}}
|
}}
|
||||||
type={data.node?.base_classes.join("|")}
|
type={data.node?.base_classes.join("|")}
|
||||||
left={false}
|
left={false}
|
||||||
|
|
@ -803,7 +805,7 @@ export default function GenericNode({
|
||||||
{data.node!.template[templateField].show &&
|
{data.node!.template[templateField].show &&
|
||||||
!data.node!.template[templateField].advanced ? (
|
!data.node!.template[templateField].advanced ? (
|
||||||
<ParameterComponent
|
<ParameterComponent
|
||||||
index={idx.toString()}
|
index={idx}
|
||||||
key={scapedJSONStringfy({
|
key={scapedJSONStringfy({
|
||||||
inputTypes:
|
inputTypes:
|
||||||
data.node!.template[templateField].input_types,
|
data.node!.template[templateField].input_types,
|
||||||
|
|
@ -880,29 +882,36 @@ export default function GenericNode({
|
||||||
>
|
>
|
||||||
{" "}
|
{" "}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
{data.node!.outputs &&
|
||||||
{data.node!.base_classes.length > 0 &&
|
data.node!.outputs.length > 0 &&
|
||||||
// if conditional_paths in data.node.conditional_paths
|
data.node!.outputs.map((output, idx) => (
|
||||||
// then buildParameterComponent for each conditionalPath
|
<ParameterComponent
|
||||||
// else buildParameterComponent for each data.node.base_classes
|
index={idx}
|
||||||
// first we check if there are any conditional paths
|
key={scapedJSONStringfy({
|
||||||
data.node!.conditional_paths &&
|
baseClasses: [output.selected ?? output.types[0]],
|
||||||
data.node!.conditional_paths.length > 0
|
id: data.id,
|
||||||
? data.node!.conditional_paths.map((conditionalPath) =>
|
dataType: data.type,
|
||||||
buildParameterComponent({
|
|
||||||
data,
|
|
||||||
conditionalPath,
|
|
||||||
showNode,
|
|
||||||
left: false,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
: buildParameterComponent({
|
|
||||||
data,
|
|
||||||
conditionalPath: null,
|
|
||||||
showNode,
|
|
||||||
left: false,
|
|
||||||
})}
|
})}
|
||||||
</div>
|
data={data}
|
||||||
|
color={
|
||||||
|
nodeColors[output.selected ?? output.types[0]] ??
|
||||||
|
nodeColors[types[output.selected ?? output.types[0]]] ??
|
||||||
|
nodeColors[types[data.type]] ??
|
||||||
|
nodeColors.unknown
|
||||||
|
}
|
||||||
|
title={output.selected ?? output.types[0]}
|
||||||
|
tooltipTitle={output.selected ?? output.types[0]}
|
||||||
|
id={{
|
||||||
|
baseClasses: [output.selected ?? output.types[0]],
|
||||||
|
id: data.id,
|
||||||
|
dataType: data.type,
|
||||||
|
idx: idx,
|
||||||
|
}}
|
||||||
|
type={output.types.join("|")}
|
||||||
|
left={false}
|
||||||
|
showNode={showNode}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</>
|
</>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ const useFetchDataOnMount = (
|
||||||
name,
|
name,
|
||||||
handleUpdateValues,
|
handleUpdateValues,
|
||||||
setNode,
|
setNode,
|
||||||
renderTooltips,
|
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
) => {
|
) => {
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
|
|
@ -44,7 +43,6 @@ const useFetchDataOnMount = (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
renderTooltips();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ const useHandleOnNewValue = (
|
||||||
handleUpdateValues,
|
handleUpdateValues,
|
||||||
debouncedHandleUpdateValues,
|
debouncedHandleUpdateValues,
|
||||||
setNode,
|
setNode,
|
||||||
renderTooltips,
|
|
||||||
isLoading,
|
isLoading,
|
||||||
setIsLoading,
|
setIsLoading,
|
||||||
) => {
|
) => {
|
||||||
|
|
@ -65,8 +64,6 @@ const useHandleOnNewValue = (
|
||||||
|
|
||||||
return newNode;
|
return newNode;
|
||||||
});
|
});
|
||||||
|
|
||||||
renderTooltips();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { handleOnNewValue };
|
return { handleOnNewValue };
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ const useHandleNodeClass = (
|
||||||
takeSnapshot,
|
takeSnapshot,
|
||||||
setNode,
|
setNode,
|
||||||
updateNodeInternals,
|
updateNodeInternals,
|
||||||
renderTooltips,
|
|
||||||
) => {
|
) => {
|
||||||
const handleNodeClass = (newNodeClass, code) => {
|
const handleNodeClass = (newNodeClass, code) => {
|
||||||
if (!data.node) return;
|
if (!data.node) return;
|
||||||
|
|
@ -30,8 +29,6 @@ const useHandleNodeClass = (
|
||||||
});
|
});
|
||||||
|
|
||||||
updateNodeInternals(data.id);
|
updateNodeInternals(data.id);
|
||||||
|
|
||||||
renderTooltips();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { handleNodeClass };
|
return { handleNodeClass };
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import useAlertStore from "../../stores/alertStore";
|
||||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||||
import { handleUpdateValues } from "../../utils/parameterUtils";
|
import { handleUpdateValues } from "../../utils/parameterUtils";
|
||||||
|
|
||||||
const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => {
|
const useHandleRefreshButtonPress = (setIsLoading, setNode) => {
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||||
|
|
||||||
const handleRefreshButtonPress = async (name, data) => {
|
const handleRefreshButtonPress = async (name, data) => {
|
||||||
|
|
@ -30,7 +30,6 @@ const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
renderTooltips();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return { handleRefreshButtonPress };
|
return { handleRefreshButtonPress };
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ export async function addFolder(data: AddFolderType): Promise<FolderType> {
|
||||||
|
|
||||||
export async function updateFolder(
|
export async function updateFolder(
|
||||||
body: FolderType,
|
body: FolderType,
|
||||||
folderId: string
|
folderId: string,
|
||||||
): Promise<FolderType> {
|
): Promise<FolderType> {
|
||||||
try {
|
try {
|
||||||
const response = await api.patch(
|
const response = await api.patch(
|
||||||
`${BASE_URL_API}folders/${folderId}`,
|
`${BASE_URL_API}folders/${folderId}`,
|
||||||
body
|
body,
|
||||||
);
|
);
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -68,7 +68,7 @@ export async function downloadFlowsFromFolders(folderId: string): Promise<{
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const response = await api.get(
|
const response = await api.get(
|
||||||
`${BASE_URL_API}folders/download/${folderId}`
|
`${BASE_URL_API}folders/download/${folderId}`,
|
||||||
);
|
);
|
||||||
if (response?.status !== 200) {
|
if (response?.status !== 200) {
|
||||||
throw new Error(`HTTP error! status: ${response?.status}`);
|
throw new Error(`HTTP error! status: ${response?.status}`);
|
||||||
|
|
@ -82,7 +82,7 @@ export async function downloadFlowsFromFolders(folderId: string): Promise<{
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function uploadFlowsFromFolders(
|
export async function uploadFlowsFromFolders(
|
||||||
flows: FormData
|
flows: FormData,
|
||||||
): Promise<FlowType[]> {
|
): Promise<FlowType[]> {
|
||||||
try {
|
try {
|
||||||
const response = await api.post(`${BASE_URL_API}folders/upload/`, flows);
|
const response = await api.post(`${BASE_URL_API}folders/upload/`, flows);
|
||||||
|
|
@ -99,11 +99,11 @@ export async function uploadFlowsFromFolders(
|
||||||
|
|
||||||
export async function moveFlowToFolder(
|
export async function moveFlowToFolder(
|
||||||
flowId: string,
|
flowId: string,
|
||||||
folderId: string
|
folderId: string,
|
||||||
): Promise<FlowType> {
|
): Promise<FlowType> {
|
||||||
try {
|
try {
|
||||||
const response = await api.patch(
|
const response = await api.patch(
|
||||||
`${BASE_URL_API}folders/move_to_folder/${flowId}/${folderId}`
|
`${BASE_URL_API}folders/move_to_folder/${flowId}/${folderId}`,
|
||||||
);
|
);
|
||||||
return response?.data;
|
return response?.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -199,11 +199,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
position?: XYPosition,
|
position?: XYPosition,
|
||||||
fromDragAndDrop?: boolean,
|
fromDragAndDrop?: boolean,
|
||||||
): Promise<string | undefined> => {
|
): Promise<string | undefined> => {
|
||||||
|
let flowData = flow
|
||||||
|
? processDataFromFlow(flow)
|
||||||
|
: { nodes: [], edges: [], viewport: { zoom: 1, x: 0, y: 0 } };
|
||||||
if (newProject) {
|
if (newProject) {
|
||||||
let flowData = flow
|
|
||||||
? processDataFromFlow(flow)
|
|
||||||
: { nodes: [], edges: [], viewport: { zoom: 1, x: 0, y: 0 } };
|
|
||||||
|
|
||||||
// Create a new flow with a default name if no flow is provided.
|
// Create a new flow with a default name if no flow is provided.
|
||||||
const folder_id = useFolderStore.getState().folderUrl;
|
const folder_id = useFolderStore.getState().folderUrl;
|
||||||
const my_collection_id = useFolderStore.getState().myCollectionId;
|
const my_collection_id = useFolderStore.getState().myCollectionId;
|
||||||
|
|
@ -272,7 +271,7 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
useFlowStore
|
useFlowStore
|
||||||
.getState()
|
.getState()
|
||||||
.paste(
|
.paste(
|
||||||
{ nodes: flow!.data!.nodes, edges: flow!.data!.edges },
|
{ nodes: flowData?.nodes, edges: flowData?.edges },
|
||||||
position ?? { x: 10, y: 10 },
|
position ?? { x: 10, y: 10 },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ export type APIClassType = {
|
||||||
documentation: string;
|
documentation: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
official?: boolean;
|
official?: boolean;
|
||||||
|
outputs?: Array<{ types: Array<string>; selected?: string }>;
|
||||||
frozen?: boolean;
|
frozen?: boolean;
|
||||||
flow?: FlowType;
|
flow?: FlowType;
|
||||||
field_order?: string[];
|
field_order?: string[];
|
||||||
|
|
@ -39,7 +40,8 @@ export type APIClassType = {
|
||||||
| FlowType
|
| FlowType
|
||||||
| CustomFieldsType
|
| CustomFieldsType
|
||||||
| boolean
|
| boolean
|
||||||
| undefined;
|
| undefined
|
||||||
|
| Array<{ types: Array<string>; selected?: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TemplateVariableType = {
|
export type TemplateVariableType = {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ export type ParameterComponentType = {
|
||||||
info?: string;
|
info?: string;
|
||||||
proxy?: { field: string; id: string };
|
proxy?: { field: string; id: string };
|
||||||
showNode?: boolean;
|
showNode?: boolean;
|
||||||
index?: string;
|
index: number;
|
||||||
onCloseModal?: (close: boolean) => void;
|
onCloseModal?: (close: boolean) => void;
|
||||||
};
|
};
|
||||||
export type InputListComponentType = {
|
export type InputListComponentType = {
|
||||||
|
|
@ -112,6 +112,14 @@ export type TextAreaComponentType = {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type outputComponentType = {
|
||||||
|
types: string[];
|
||||||
|
selected: string;
|
||||||
|
nodeId: string;
|
||||||
|
frozen?: boolean;
|
||||||
|
idx: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type PromptAreaComponentType = {
|
export type PromptAreaComponentType = {
|
||||||
field_name?: string;
|
field_name?: string;
|
||||||
nodeClass?: APIClassType;
|
nodeClass?: APIClassType;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export type NodeDataType = {
|
||||||
node?: APIClassType;
|
node?: APIClassType;
|
||||||
id: string;
|
id: string;
|
||||||
output_types?: string[];
|
output_types?: string[];
|
||||||
|
selected_output_type?: string;
|
||||||
buildStatus?: BuildStatus;
|
buildStatus?: BuildStatus;
|
||||||
};
|
};
|
||||||
// FlowStyleType is the type of the style object that is used to style the
|
// FlowStyleType is the type of the style object that is used to style the
|
||||||
|
|
@ -58,6 +59,7 @@ export type sourceHandleType = {
|
||||||
id: string;
|
id: string;
|
||||||
baseClasses: string[];
|
baseClasses: string[];
|
||||||
conditionalPath?: string | null;
|
conditionalPath?: string | null;
|
||||||
|
idx: number;
|
||||||
};
|
};
|
||||||
//left side
|
//left side
|
||||||
export type targetHandleType = {
|
export type targetHandleType = {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ type BuildVerticesParams = {
|
||||||
onBuildUpdate?: (
|
onBuildUpdate?: (
|
||||||
data: VertexBuildTypeAPI,
|
data: VertexBuildTypeAPI,
|
||||||
status: BuildStatus,
|
status: BuildStatus,
|
||||||
buildId: string
|
buildId: string,
|
||||||
) => void; // Replace any with the actual type if it's not any
|
) => void; // Replace any with the actual type if it's not any
|
||||||
onBuildComplete?: (allNodesValid: boolean) => void;
|
onBuildComplete?: (allNodesValid: boolean) => void;
|
||||||
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
|
onBuildError?: (title, list, idList: VertexLayerElementType[]) => void;
|
||||||
|
|
@ -53,7 +53,7 @@ export async function updateVerticesOrder(
|
||||||
startNodeId?: string | null,
|
startNodeId?: string | null,
|
||||||
stopNodeId?: string | null,
|
stopNodeId?: string | null,
|
||||||
nodes?: Node[],
|
nodes?: Node[],
|
||||||
edges?: Edge[]
|
edges?: Edge[],
|
||||||
): Promise<{
|
): Promise<{
|
||||||
verticesLayers: VertexLayerElementType[][];
|
verticesLayers: VertexLayerElementType[][];
|
||||||
verticesIds: string[];
|
verticesIds: string[];
|
||||||
|
|
@ -69,7 +69,7 @@ export async function updateVerticesOrder(
|
||||||
startNodeId,
|
startNodeId,
|
||||||
stopNodeId,
|
stopNodeId,
|
||||||
nodes,
|
nodes,
|
||||||
edges
|
edges,
|
||||||
);
|
);
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
setErrorData({
|
setErrorData({
|
||||||
|
|
@ -125,7 +125,7 @@ export async function buildVertices({
|
||||||
startNodeId,
|
startNodeId,
|
||||||
stopNodeId,
|
stopNodeId,
|
||||||
nodes,
|
nodes,
|
||||||
edges
|
edges,
|
||||||
);
|
);
|
||||||
if (onValidateNodes) {
|
if (onValidateNodes) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -187,14 +187,14 @@ export async function buildVertices({
|
||||||
onBuildUpdate(
|
onBuildUpdate(
|
||||||
getInactiveVertexData(element.id),
|
getInactiveVertexData(element.id),
|
||||||
BuildStatus.INACTIVE,
|
BuildStatus.INACTIVE,
|
||||||
runId
|
runId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (element.reference) {
|
if (element.reference) {
|
||||||
onBuildUpdate(
|
onBuildUpdate(
|
||||||
getInactiveVertexData(element.reference),
|
getInactiveVertexData(element.reference),
|
||||||
BuildStatus.INACTIVE,
|
BuildStatus.INACTIVE,
|
||||||
runId
|
runId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
buildResults.push(false);
|
buildResults.push(false);
|
||||||
|
|
@ -219,7 +219,7 @@ export async function buildVertices({
|
||||||
if (stop) {
|
if (stop) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
// Once the current layer is built, move to the next layer
|
// Once the current layer is built, move to the next layer
|
||||||
currentLayerIndex += 1;
|
currentLayerIndex += 1;
|
||||||
|
|
@ -262,7 +262,7 @@ async function buildVertex({
|
||||||
onBuildError!(
|
onBuildError!(
|
||||||
"Error Building Component",
|
"Error Building Component",
|
||||||
[buildData.params],
|
[buildData.params],
|
||||||
verticesIds.map((id) => ({ id }))
|
verticesIds.map((id) => ({ id })),
|
||||||
);
|
);
|
||||||
stopBuild();
|
stopBuild();
|
||||||
}
|
}
|
||||||
|
|
@ -273,7 +273,7 @@ async function buildVertex({
|
||||||
onBuildError!(
|
onBuildError!(
|
||||||
"Error Building Component",
|
"Error Building Component",
|
||||||
[(error as AxiosError<any>).response?.data?.detail ?? "Unknown Error"],
|
[(error as AxiosError<any>).response?.data?.detail ?? "Unknown Error"],
|
||||||
verticesIds.map((id) => ({ id }))
|
verticesIds.map((id) => ({ id })),
|
||||||
);
|
);
|
||||||
stopBuild();
|
stopBuild();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ import {
|
||||||
specialCharsRegex,
|
specialCharsRegex,
|
||||||
} from "../constants/constants";
|
} from "../constants/constants";
|
||||||
import { downloadFlowsFromDatabase } from "../controllers/API";
|
import { downloadFlowsFromDatabase } from "../controllers/API";
|
||||||
|
import getFieldTitle from "../customNodes/utils/get-field-title";
|
||||||
|
import { DESCRIPTIONS } from "../flow_constants";
|
||||||
import {
|
import {
|
||||||
APIClassType,
|
APIClassType,
|
||||||
APIKindType,
|
APIKindType,
|
||||||
|
|
@ -37,8 +39,6 @@ import {
|
||||||
updateEdgesHandleIdsType,
|
updateEdgesHandleIdsType,
|
||||||
} from "../types/utils/reactflowUtils";
|
} from "../types/utils/reactflowUtils";
|
||||||
import { createRandomKey, toTitleCase } from "./utils";
|
import { createRandomKey, toTitleCase } from "./utils";
|
||||||
import { DESCRIPTIONS } from "../flow_constants";
|
|
||||||
import getFieldTitle from "../customNodes/utils/get-field-title";
|
|
||||||
const uid = new ShortUniqueId({ length: 5 });
|
const uid = new ShortUniqueId({ length: 5 });
|
||||||
|
|
||||||
export function checkChatInput(nodes: Node[]) {
|
export function checkChatInput(nodes: Node[]) {
|
||||||
|
|
@ -46,6 +46,7 @@ export function checkChatInput(nodes: Node[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cleanEdges(nodes: Node[], edges: Edge[]) {
|
export function cleanEdges(nodes: Node[], edges: Edge[]) {
|
||||||
|
console.log("cleanEdges");
|
||||||
let newEdges = cloneDeep(edges);
|
let newEdges = cloneDeep(edges);
|
||||||
edges.forEach((edge) => {
|
edges.forEach((edge) => {
|
||||||
// check if the source and target node still exists
|
// check if the source and target node still exists
|
||||||
|
|
@ -75,10 +76,12 @@ export function cleanEdges(nodes: Node[], edges: Edge[]) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sourceHandle) {
|
if (sourceHandle) {
|
||||||
|
const index = scapeJSONParse(sourceHandle).idx ?? 0;
|
||||||
const id: sourceHandleType = {
|
const id: sourceHandleType = {
|
||||||
id: sourceNode.data.id,
|
id: sourceNode.data.id,
|
||||||
baseClasses: sourceNode.data.node!.base_classes,
|
baseClasses: [sourceNode.data.node.outputs[index].selected],
|
||||||
dataType: sourceNode.data.type,
|
dataType: sourceNode.data.type,
|
||||||
|
idx: index,
|
||||||
};
|
};
|
||||||
if (scapedJSONStringfy(id) !== sourceHandle) {
|
if (scapedJSONStringfy(id) !== sourceHandle) {
|
||||||
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
newEdges = newEdges.filter((e) => e.id !== edge.id);
|
||||||
|
|
@ -191,8 +194,8 @@ export const processDataFromFlow = (flow: FlowType, refreshIds = true) => {
|
||||||
let data = flow?.data ? flow.data : null;
|
let data = flow?.data ? flow.data : null;
|
||||||
if (data) {
|
if (data) {
|
||||||
processFlowEdges(flow);
|
processFlowEdges(flow);
|
||||||
//prevent node update for now
|
//add dropdown option to nodeOutputs
|
||||||
// processFlowNodes(flow);
|
processFlowNodes(flow);
|
||||||
//add animation to text type edges
|
//add animation to text type edges
|
||||||
updateEdges(data.edges);
|
updateEdges(data.edges);
|
||||||
// updateNodes(data.nodes, data.edges);
|
// updateNodes(data.nodes, data.edges);
|
||||||
|
|
@ -397,6 +400,7 @@ export function updateEdgesHandleIds({
|
||||||
id: sourceNode.data.id,
|
id: sourceNode.data.id,
|
||||||
baseClasses: sourceNode.data.node!.base_classes,
|
baseClasses: sourceNode.data.node!.base_classes,
|
||||||
dataType: sourceNode.data.type,
|
dataType: sourceNode.data.type,
|
||||||
|
idx: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
edge.sourceHandle = scapedJSONStringfy(newSource!);
|
edge.sourceHandle = scapedJSONStringfy(newSource!);
|
||||||
|
|
@ -410,6 +414,49 @@ export function updateEdgesHandleIds({
|
||||||
return newEdges;
|
return newEdges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function updateNewOutput({ nodes, edges }: updateEdgesHandleIdsType) {
|
||||||
|
console.log("updateNewOutput");
|
||||||
|
let newEdges = cloneDeep(edges);
|
||||||
|
let newNodes = cloneDeep(nodes);
|
||||||
|
newEdges.forEach((edge) => {
|
||||||
|
if (edge.sourceHandle && edge.targetHandle) {
|
||||||
|
let newSourceHandle: sourceHandleType = scapeJSONParse(edge.sourceHandle);
|
||||||
|
let newTargetHandle: targetHandleType = scapeJSONParse(edge.targetHandle);
|
||||||
|
let intersection;
|
||||||
|
if (newTargetHandle.inputTypes && newTargetHandle.inputTypes.length > 0) {
|
||||||
|
//conjuction subtraction
|
||||||
|
intersection = newSourceHandle.baseClasses.filter((type) =>
|
||||||
|
newTargetHandle.inputTypes!.includes(type),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
intersection = newSourceHandle.baseClasses.filter(
|
||||||
|
(type) => type === newTargetHandle.type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const selected = intersection[0];
|
||||||
|
newSourceHandle.baseClasses = [selected];
|
||||||
|
const id = newSourceHandle.id;
|
||||||
|
newSourceHandle.idx = 0;
|
||||||
|
const sourceNodeIndex = newNodes.findIndex((node) => node.id === id);
|
||||||
|
if (sourceNodeIndex > -1) {
|
||||||
|
const sourceNode = newNodes[sourceNodeIndex];
|
||||||
|
if (
|
||||||
|
!sourceNode.data.node?.outputs ||
|
||||||
|
sourceNode.data.node!.outputs!.length === 0
|
||||||
|
) {
|
||||||
|
sourceNode.data.node!.outputs = [
|
||||||
|
{ types: sourceNode.data.node!.base_classes, selected: selected },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
edge.sourceHandle = scapedJSONStringfy(newSourceHandle);
|
||||||
|
edge.data.sourceHandle = newSourceHandle;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { nodes: newNodes, edges: newEdges };
|
||||||
|
}
|
||||||
|
|
||||||
export function handleKeyDown(
|
export function handleKeyDown(
|
||||||
e:
|
e:
|
||||||
| React.KeyboardEvent<HTMLInputElement>
|
| React.KeyboardEvent<HTMLInputElement>
|
||||||
|
|
@ -561,6 +608,10 @@ export function checkOldEdgesHandles(edges: Edge[]): boolean {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function checkOldNodesOutput(nodes: NodeType[]): boolean {
|
||||||
|
return nodes.some((node) => !node.data.node?.outputs);
|
||||||
|
}
|
||||||
|
|
||||||
export function customStringify(obj: any): string {
|
export function customStringify(obj: any): string {
|
||||||
if (typeof obj === "undefined") {
|
if (typeof obj === "undefined") {
|
||||||
return "null";
|
return "null";
|
||||||
|
|
@ -1017,6 +1068,15 @@ export function processFlowEdges(flow: FlowType) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function processFlowNodes(flow: FlowType) {
|
||||||
|
if (!flow.data || !flow.data.nodes) return;
|
||||||
|
if (checkOldNodesOutput(flow.data.nodes)) {
|
||||||
|
const { nodes, edges } = updateNewOutput(flow.data);
|
||||||
|
flow.data.nodes = nodes;
|
||||||
|
flow.data.edges = edges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function expandGroupNode(
|
export function expandGroupNode(
|
||||||
id: string,
|
id: string,
|
||||||
flow: FlowType,
|
flow: FlowType,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ test("chat_io_teste", async ({ page }) => {
|
||||||
|
|
||||||
const jsonContent = readFileSync(
|
const jsonContent = readFileSync(
|
||||||
"tests/end-to-end/assets/ChatTest.json",
|
"tests/end-to-end/assets/ChatTest.json",
|
||||||
"utf-8"
|
"utf-8",
|
||||||
);
|
);
|
||||||
|
|
||||||
await page.getByTestId("blank-flow").click();
|
await page.getByTestId("blank-flow").click();
|
||||||
|
|
@ -47,7 +47,7 @@ test("chat_io_teste", async ({ page }) => {
|
||||||
"drop",
|
"drop",
|
||||||
{
|
{
|
||||||
dataTransfer,
|
dataTransfer,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
await page.getByLabel("fit view").click();
|
await page.getByLabel("fit view").click();
|
||||||
await page.getByText("Playground", { exact: true }).click();
|
await page.getByText("Playground", { exact: true }).click();
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => {
|
||||||
.getByTestId("textarea-input_value")
|
.getByTestId("textarea-input_value")
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.fill(
|
.fill(
|
||||||
"testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!"
|
"testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!",
|
||||||
);
|
);
|
||||||
await page
|
await page
|
||||||
.getByTestId("popover-anchor-input-sender_name")
|
.getByTestId("popover-anchor-input-sender_name")
|
||||||
|
|
@ -85,8 +85,8 @@ test("user must interact with chat with Input/Output", async ({ page }) => {
|
||||||
await page
|
await page
|
||||||
.getByText(
|
.getByText(
|
||||||
"testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!",
|
"testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!",
|
||||||
{ exact: true }
|
{ exact: true },
|
||||||
)
|
)
|
||||||
.isVisible()
|
.isVisible(),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -41,19 +41,19 @@ test("InputListComponent", async ({ page }) => {
|
||||||
await page.getByTestId("edit-button-modal").click();
|
await page.getByTestId("edit-button-modal").click();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.getByTestId("showmetadata_indexing_exclude").isChecked()
|
await page.getByTestId("showmetadata_indexing_exclude").isChecked(),
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
await page.getByTestId("showmetadata_indexing_exclude").click();
|
await page.getByTestId("showmetadata_indexing_exclude").click();
|
||||||
expect(
|
expect(
|
||||||
await page.getByTestId("showmetadata_indexing_exclude").isChecked()
|
await page.getByTestId("showmetadata_indexing_exclude").isChecked(),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
await page.getByTestId("showmetadata_indexing_include").isChecked()
|
await page.getByTestId("showmetadata_indexing_include").isChecked(),
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
await page.getByTestId("showmetadata_indexing_include").click();
|
await page.getByTestId("showmetadata_indexing_include").click();
|
||||||
expect(
|
expect(
|
||||||
await page.getByTestId("showmetadata_indexing_include").isChecked()
|
await page.getByTestId("showmetadata_indexing_include").isChecked(),
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
await page
|
await page
|
||||||
|
|
@ -93,7 +93,7 @@ test("InputListComponent", async ({ page }) => {
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
const plusButtonLocator = page.getByTestId(
|
const plusButtonLocator = page.getByTestId(
|
||||||
"input-list-plus-btn_metadata_indexing_include-1"
|
"input-list-plus-btn_metadata_indexing_include-1",
|
||||||
);
|
);
|
||||||
const elementCount = await plusButtonLocator?.count();
|
const elementCount = await plusButtonLocator?.count();
|
||||||
|
|
||||||
|
|
@ -164,12 +164,12 @@ test("InputListComponent", async ({ page }) => {
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
const plusButtonLocatorEdit0 = await page.getByTestId(
|
const plusButtonLocatorEdit0 = await page.getByTestId(
|
||||||
"input-list-plus-btn-edit_metadata_indexing_include-0"
|
"input-list-plus-btn-edit_metadata_indexing_include-0",
|
||||||
);
|
);
|
||||||
const elementCountEdit0 = await plusButtonLocatorEdit0?.count();
|
const elementCountEdit0 = await plusButtonLocatorEdit0?.count();
|
||||||
|
|
||||||
const plusButtonLocatorEdit2 = await page.getByTestId(
|
const plusButtonLocatorEdit2 = await page.getByTestId(
|
||||||
"input-list-plus-btn-edit_metadata_indexing_include-2"
|
"input-list-plus-btn-edit_metadata_indexing_include-2",
|
||||||
);
|
);
|
||||||
const elementCountEdit2 = await plusButtonLocatorEdit2?.count();
|
const elementCountEdit2 = await plusButtonLocatorEdit2?.count();
|
||||||
|
|
||||||
|
|
@ -178,13 +178,13 @@ test("InputListComponent", async ({ page }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const minusButtonLocatorEdit1 = await page.getByTestId(
|
const minusButtonLocatorEdit1 = await page.getByTestId(
|
||||||
"input-list-minus-btn-edit_metadata_indexing_include-1"
|
"input-list-minus-btn-edit_metadata_indexing_include-1",
|
||||||
);
|
);
|
||||||
|
|
||||||
const elementCountMinusEdit1 = await minusButtonLocatorEdit1?.count();
|
const elementCountMinusEdit1 = await minusButtonLocatorEdit1?.count();
|
||||||
|
|
||||||
const minusButtonLocatorEdit2 = await page.getByTestId(
|
const minusButtonLocatorEdit2 = await page.getByTestId(
|
||||||
"input-list-minus-btn-edit_metadata_indexing_include-2"
|
"input-list-minus-btn-edit_metadata_indexing_include-2",
|
||||||
);
|
);
|
||||||
|
|
||||||
const elementCountMinusEdit2 = await minusButtonLocatorEdit2?.count();
|
const elementCountMinusEdit2 = await minusButtonLocatorEdit2?.count();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue