refactor: improve parameter render component (#3995)

* refactor: update dynamic parameter in ParameterRenderComponent to false

* refactor: update CodeAreaComponent to use CodeAreaModal for node toolbar

* feat: Add EmptyParameterComponent to render empty parameters

* refactor: Add types for parameter render component

Add typescript types for the parameter render component to improve code readability and maintainability.

* feat: Add EmptyParameterComponent to render empty parameters

* refactor: Update RefreshParameterComponent to use InputProps

Update the RefreshParameterComponent to use the InputProps interface for better code readability and maintainability.

* refactor: Update InputProps in parameterRenderComponent/types.ts

Update the InputProps interface in parameterRenderComponent/types.ts to remove the Partial<OptionalInputProps> and simplify the composition of input props. This improves code readability and maintainability.

* refactor: Update InputProps in parameterRenderComponent/types.ts

* refactor: Update parameterRenderComponent/types.ts to use handleOnNewValue instead of onChange

Update the parameterRenderComponent/types.ts file to import the handleOnNewValueType from "@/CustomNodes/hooks/use-handle-new-value" and replace the onChange function with handleOnNewValue in the BaseInputProps interface. This change improves code readability and maintainability.

* update folder structure

* refactor: Update TableModal to support disabled state

Update the TableModal component to include a disabled prop, allowing the modal to be disabled when necessary. This improves the flexibility and usability of the component.

* refactor: Update parameterRenderComponent/types.ts to include TableComponentType

Update the parameterRenderComponent/types.ts file to include the TableComponentType interface, which defines the structure for a table component. This change improves code organization and maintainability.

* refactor: Update parameterRenderComponent to use TableNodeComponent from the correct folder

* refactor: Update codeAreaComponent imports and props in parameterRenderComponent

Update the imports and props in the parameterRenderComponent file to reflect the changes made in the codeAreaComponent file. This refactor improves code organization and maintainability.

* refactor: Update parameterRenderComponent/types.ts to include CodeAreaComponentType

Update the parameterRenderComponent/types.ts file to include the CodeAreaComponentType interface, which defines the structure for a code area component. This change improves code organization and maintainability.

* refactor: Remove unused import of CodeAreaComponent in nodeToolbarComponent

Remove the unused import of CodeAreaComponent in the nodeToolbarComponent file to improve code cleanliness and maintainability.

* refactor: Remove unused import of CodeAreaComponent in nodeToolbarComponent

* refactor: Remove unused import of CodeAreaComponent in nodeToolbarComponent

* refactor: Remove unused import of CodeAreaComponent in codeAreaComponent/index.tsx

* refactor: Remove unused import of CodeAreaComponent in parameterRenderComponent/index.tsx

* refactor: Update floatComponent imports and props in parameterRenderComponent

Update the imports and props in the parameterRenderComponent file to reflect the changes made in the floatComponent file. This refactor improves code organization and maintainability.

* refactor: Update floatComponent imports and props in parameterRenderComponent

* update int component path

* update int component type

* update toogle location

* refactor: Update ToggleComponentType in components/index.ts

Remove the ToggleComponentType interface from the components/index.ts file. This change is part of a refactoring effort to remove unused code and improve code organization.

* refactor: Update ToggleComponentType in components/index.ts

Remove unused ToggleComponentType interface from components/index.ts

* refactor: Update ToggleShadComponent to handle new value changes

Refactor the ToggleShadComponent to handle new value changes by passing the updated value to the handleOnNewValue function. This improves the functionality and maintainability of the component.

* refactor: Move InputFileComponent to parameterRenderComponent/components folder

Move the InputFileComponent file from the src/frontend/src/components folder to the src/frontend/src/components/parameterRenderComponent/components folder. This change improves code organization and maintainability.

* refactor: Add FileComponentType interface to parameterRenderComponent/types.ts

Add the FileComponentType interface to the parameterRenderComponent/types.ts file. This interface defines the fileTypes property for the FileComponentType, allowing for better type checking and documentation. This change improves code organization and maintainability.

* refactor: Update InputFileComponent props in parameterRenderComponent

Update the props of the InputFileComponent in the parameterRenderComponent file to match the changes made in the index.tsx file. This refactor improves code organization and maintainability.

* refactor: Move PromptAreaComponent to parameterRenderComponent/components folder

* update types prompt field

* refactor: Update PromptAreaComponent to handle new value changes

Refactor the PromptAreaComponent to handle new value changes by passing the updated value to the handleOnNewValue function. This improves the functionality and maintainability of the component.

* refactor: Move LinkComponent to parameterRenderComponent/components folder

* refactor: Add LinkComponentType interface to parameterRenderComponent/types.ts

Add the LinkComponentType interface to the parameterRenderComponent/types.ts file. This interface defines the icon and text properties for the LinkComponentType, allowing for better type checking and documentation. This change improves code organization and maintainability.

* refactor: Update LinkComponent to use InputProps in parameterRenderComponent

Update the LinkComponent in parameterRenderComponent to use the InputProps interface for better type checking and consistency. This refactor improves code organization and maintainability.

* refactor: Move KeypairListComponent to parameterRenderComponent/components folder

* refactor: Add KeyPairListComponentType interface to parameterRenderComponent/types.ts

* refactor: Update KeypairListComponent to use InputProps in parameterRenderComponent

Refactor the KeypairListComponent in parameterRenderComponent to use the InputProps interface for better type checking and consistency. This refactor improves code organization and maintainability.

* refactor: Move DictComponent to parameterRenderComponent/components folder

* refactor: Update KeyPairListComponentType interface in parameterRenderComponent/types.ts

* refactor: Update KeypairListComponent to use InputProps in parameterRenderComponent

* refactor: Move InputListComponent to parameterRenderComponent/components folder

Move the InputListComponent to the parameterRenderComponent/components folder for better code organization and maintainability.

* refactor: Add StrRenderComponentType interface to parameterRenderComponent/types.ts

* refactor: Move InputListComponent to parameterRenderComponent/components folder

* refactor: Move InputListComponent to parameterRenderComponent/components folder

* refactor: Move InputListComponent to parameterRenderComponent/components folder

Move the InputListComponent to the parameterRenderComponent/components folder for better code organization and maintainability.

* refactor: Update InputListComponent to use InputProps in parameterRenderComponent

Refactor the InputListComponent in parameterRenderComponent to use the InputProps interface for better type checking and consistency. This refactor improves code organization and maintainability.

* refactor: Move IOFieldView to IOModal/components folder

Move the IOFieldView component to the IOModal/components folder for better code organization and maintainability.

* refactor: Move DropdownComponent to parameterRenderComponent/components folder

* refactor: Add DropDownComponentType interface to parameterRenderComponent/types.ts

* refactor: Update DropdownComponent import in StrRenderComponent

Update the import statement for DropdownComponent in StrRenderComponent to reflect its new location in the parameterRenderComponent/components folder. This refactor improves code organization and maintainability.

* refactor: Update StrRenderComponent to use baseInputProps

Refactor the StrRenderComponent in parameterRenderComponent to use the baseInputProps object instead of individual props for better code organization and maintainability.

* refactor: Update StrRenderComponent to use TextAreaComponent from correct location

Update the import statement for TextAreaComponent in StrRenderComponent to reflect its new location in the parameterRenderComponent/components folder. This refactor improves code organization and maintainability.

* remove unused imports

* refactor: Add TextAreaComponentType interface to parameterRenderComponent/types.ts

* refactor: Update TextAreaComponent to use handleOnNewValue instead of onChange

Refactor the TextAreaComponent in parameterRenderComponent to use the handleOnNewValue function instead of the onChange function for better code consistency. This change aligns with the recent updates to the component's props and improves maintainability.

* refactor: Update StrRenderComponent to use baseInputProps

Refactor the StrRenderComponent in parameterRenderComponent to use the baseInputProps object instead of individual props for better code organization and maintainability.

* refactor: Move inputGlobalComponent to parameterRenderComponent/components folder

Move the inputGlobalComponent to the parameterRenderComponent/components folder to improve code organization and maintainability.

* refactor: Move inputGlobalComponent to parameterRenderComponent/components folder

* refactor: Update InputGlobalComponent to use handleOnNewValue instead of onChange

Refactor the InputGlobalComponent in parameterRenderComponent to use the handleOnNewValue function instead of the onChange function for better code consistency. This change aligns with the recent updates to the component's props and improves maintainability.

* refactor: Update StrRenderComponent to use baseInputProps

Refactor the StrRenderComponent in parameterRenderComponent to use the baseInputProps object instead of individual props for better code organization and maintainability.

* refactor: Move multiselectComponent to parameterRenderComponent/components folder

Move the multiselectComponent to the parameterRenderComponent/components folder to improve code organization and maintainability.

* refactor: Move MultiselectComponent to parameterRenderComponent/components folder

Move the MultiselectComponent to the parameterRenderComponent/components folder to improve code organization and maintainability.

* refactor: Move MultiselectComponent to parameterRenderComponent/components folder

* refactor: Move MultiselectComponent to parameterRenderComponent/components folder

* refactor: Remove unused code in StrRenderComponent

* refactor: Remove unused code in StrRenderComponent

* refactor: Remove unused code in StrRenderComponent

* [autofix.ci] apply automated fixes

*  (tableInputComponent.spec.ts): add ua-parser-js library to parse user agent information for better control handling based on OS
🔧 (tableInputComponent.spec.ts): update key press event to use the correct control key based on the user's operating system for textarea selection.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Cristhian Zanforlin Lousa <cristhian.lousa@gmail.com>
This commit is contained in:
anovazzi1 2024-10-08 15:29:23 -03:00 committed by GitHub
commit 12a381a3fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
31 changed files with 642 additions and 651 deletions

View file

@ -2,7 +2,7 @@ import { PopoverAnchor } from "@radix-ui/react-popover";
import Fuse from "fuse.js";
import { cloneDeep } from "lodash";
import { ChangeEvent, useEffect, useRef, useState } from "react";
import { DropDownComponentType } from "../../types/components";
import { DropDownComponent } from "../../types/components";
import { cn } from "../../utils/utils";
import { default as ForwardedIconComponent } from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent";
@ -31,7 +31,7 @@ export default function Dropdown({
editNode = false,
id = "",
children,
}: DropDownComponentType): JSX.Element {
}: DropDownComponent): JSX.Element {
const [open, setOpen] = useState(children ? true : false);
const refButton = useRef<HTMLButtonElement>(null);

View file

@ -1,102 +0,0 @@
import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value";
import { InputFieldType } from "@/types/api";
import Dropdown from "../../../dropdownComponent";
import InputGlobalComponent from "../../../inputGlobalComponent";
import InputListComponent from "../../../inputListComponent";
import MultiselectComponent from "../../../multiselectComponent";
import TextAreaComponent from "../../../textAreaComponent";
export function StrRenderComponent({
templateData,
value,
name,
disabled,
handleOnNewValue,
editNode,
id,
}: {
templateData: Partial<InputFieldType>;
value: any;
name: string;
disabled: boolean;
handleOnNewValue: handleOnNewValueType;
editNode: boolean;
id: string;
}) {
const onChange = (value: any, dbValue?: boolean, skipSnapshot?: boolean) => {
handleOnNewValue({ value, load_from_db: dbValue }, { skipSnapshot });
};
if (!templateData.options) {
return templateData?.list ? (
<InputListComponent
componentName={name ?? undefined}
editNode={editNode}
disabled={disabled}
value={value || [""]}
onChange={onChange}
id={`inputlist_${id}`}
/>
) : templateData.multiline ? (
<TextAreaComponent
password={templateData.password}
updateVisibility={() => {
if (templateData.password !== undefined) {
handleOnNewValue(
{ password: !templateData.password },
{ skipSnapshot: true },
);
}
}}
id={`textarea_${id}`}
disabled={disabled}
editNode={editNode}
value={value ?? ""}
onChange={onChange}
/>
) : (
<InputGlobalComponent
disabled={disabled}
editNode={editNode}
onChange={onChange}
name={name}
data={templateData}
/>
);
}
if (!!templateData.options && !!templateData?.list) {
return (
<MultiselectComponent
editNode={editNode}
disabled={disabled}
options={
(Array.isArray(templateData.options)
? templateData.options
: [templateData.options]) || []
}
combobox={templateData.combobox}
value={
(Array.isArray(templateData.value)
? templateData.value
: [templateData.value]) || []
}
id={`multiselect_${id}`}
onSelect={onChange}
/>
);
}
if (!!templateData.options) {
return (
<Dropdown
editNode={editNode}
options={templateData.options}
onSelect={onChange}
combobox={templateData.combobox}
value={value || ""}
id={`dropdown_${id}`}
/>
);
}
}

View file

@ -4,19 +4,20 @@ import { DataTypeDefinition, SelectionChangedEvent } from "ag-grid-community";
import { AgGridReact } from "ag-grid-react";
import { cloneDeep } from "lodash";
import { useMemo, useRef, useState } from "react";
import { ForwardedIconComponent } from "../../components/genericIconComponent";
import { TableComponentType } from "../../types/components";
import { Button } from "../ui/button";
import { ForwardedIconComponent } from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { InputProps, TableComponentType } from "../../types";
export default function TableNodeComponent({
tableTitle,
description,
value,
onChange,
editNode = false,
id = "",
columns,
}: TableComponentType): JSX.Element {
handleOnNewValue,
disabled = false,
}: InputProps<any[], TableComponentType>): JSX.Element {
const dataTypeDefinitions: {
[cellDataType: string]: DataTypeDefinition<any>;
} = useMemo(() => {
@ -68,7 +69,7 @@ export default function TableNodeComponent({
if (agGrid.current && !agGrid.current.api.isDestroyed()) {
const rows: any = [];
agGrid.current.api.forEachNode((node) => rows.push(node.data));
onChange(rows);
handleOnNewValue({ value: rows });
}
}
function deleteRow() {
@ -85,7 +86,7 @@ export default function TableNodeComponent({
const toDuplicate = selectedNodes.map((node) => cloneDeep(node.data));
setSelectedNodes([]);
const rows: any = [];
onChange([...value, ...toDuplicate]);
handleOnNewValue({ value: [...value, ...toDuplicate] });
}
}
function addRow() {
@ -93,7 +94,7 @@ export default function TableNodeComponent({
componentColumns.forEach((column) => {
newRow[column.name] = null;
});
onChange([...value, newRow]);
handleOnNewValue({ value: [...value, newRow] });
}
function updateComponent() {
@ -111,7 +112,11 @@ export default function TableNodeComponent({
});
return (
<div className={"flex w-full items-center"}>
<div
className={
"flex w-full items-center" + (disabled ? " cursor-not-allowed" : "")
}
>
<div className="flex w-full items-center gap-3" data-testid={"div-" + id}>
<TableModal
dataTypeDefinitions={dataTypeDefinitions}
@ -135,9 +140,13 @@ export default function TableNodeComponent({
rowData={value}
>
<Button
disabled={disabled}
variant="primary"
size={editNode ? "xs" : "default"}
className="w-full"
className={
"w-full " +
(disabled ? "pointer-events-none cursor-not-allowed" : "")
}
>
<ForwardedIconComponent name="Table" className="mt-px h-4 w-4" />
<span className="font-normal">Open Table</span>

View file

@ -1,30 +1,26 @@
import { useEffect, useState } from "react";
import CodeAreaModal from "../../modals/codeAreaModal";
import { CodeAreaComponentType } from "../../types/components";
import { cn } from "../../utils/utils";
import CodeAreaModal from "../../../../modals/codeAreaModal";
import { cn } from "../../../../utils/utils";
import IconComponent from "../genericIconComponent";
import IconComponent from "../../../genericIconComponent";
import { InputProps } from "../../types";
export default function CodeAreaComponent({
value,
onChange,
handleOnNewValue,
disabled,
editNode = false,
nodeClass,
dynamic,
setNodeClass,
handleNodeClass,
id = "",
readonly = false,
open,
setOpen,
}: CodeAreaComponentType) {
}: InputProps<string>) {
const [componentValue, setComponentValue] = useState(
typeof value == "string" ? value : JSON.stringify(value),
);
useEffect(() => {
if (disabled && componentValue !== "") {
setComponentValue("");
onChange("", undefined, true);
handleOnNewValue({ value: "" }, { skipSnapshot: true });
}
}, [disabled]);
@ -33,7 +29,7 @@ export default function CodeAreaComponent({
}, [value]);
const handleValueChange = (newValue) => {
onChange(newValue);
handleOnNewValue({ value: newValue });
};
const renderInputText = () => (
@ -68,13 +64,10 @@ export default function CodeAreaComponent({
return (
<div className={cn("w-full", disabled && "pointer-events-none")}>
<CodeAreaModal
open={open}
setOpen={setOpen}
readonly={readonly}
dynamic={dynamic}
dynamic={false}
value={value}
nodeClass={nodeClass}
setNodeClass={setNodeClass!}
setNodeClass={handleNodeClass!}
setValue={handleValueChange}
>
<div className="flex w-full items-center gap-3">

View file

@ -1,30 +1,27 @@
import { useEffect } from "react";
import { DictComponentType } from "../../types/components";
import DictAreaModal from "../../modals/dictAreaModal";
import { classNames, cn } from "../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import DictAreaModal from "../../../../modals/dictAreaModal";
import { classNames, cn } from "../../../../utils/utils";
import ForwardedIconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { InputProps } from "../../types";
export default function DictComponent({
value = [],
onChange,
handleOnNewValue,
disabled,
editNode = false,
id = "",
}: DictComponentType): JSX.Element {
// Create a reference to the value
}: InputProps<object | object[] | string>): JSX.Element {
useEffect(() => {
if (disabled) {
onChange({});
handleOnNewValue({ value: {} }, { skipSnapshot: true });
}
}, [disabled]);
return (
<div
className={classNames(
value.length > 1 && editNode ? "my-1" : "",
"flex w-full flex-col gap-3",
disabled ? "pointer-events-none" : "",
)}
@ -32,9 +29,9 @@ export default function DictComponent({
{
<div className="flex w-full gap-3" data-testid={id}>
<DictAreaModal
value={value}
value={(value || "").toString() === "{}" ? {} : value}
onChange={(obj) => {
onChange(obj);
handleOnNewValue({ value: obj });
}}
disabled={disabled}
>

View file

@ -0,0 +1,27 @@
import Dropdown from "../../../dropdownComponent";
import { DropDownComponentType, InputProps } from "../../types";
export default function DropdownComponent({
id,
value,
editNode,
handleOnNewValue,
disabled,
combobox,
options,
}: InputProps<string, DropDownComponentType>) {
const onChange = (value: any, dbValue?: boolean, skipSnapshot?: boolean) => {
handleOnNewValue({ value, load_from_db: dbValue }, { skipSnapshot });
};
return (
<Dropdown
disabled={disabled}
editNode={editNode}
options={options}
onSelect={onChange}
combobox={combobox}
value={value || ""}
id={`dropdown_${id}`}
/>
);
}

View file

@ -0,0 +1,11 @@
import { InputProps } from "../../types";
export function EmptyParameterComponent({
id,
value,
editNode,
handleOnNewValue,
disabled,
}: InputProps) {
return <div id={id}>{String(value)}</div>;
}

View file

@ -1,22 +1,22 @@
import { useEffect } from "react";
import { FloatComponentType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { Input } from "../ui/input";
import { handleKeyDown } from "../../../../utils/reactflowUtils";
import { Input } from "../../../ui/input";
import { FloatComponentType, InputProps } from "../../types";
export default function FloatComponent({
value,
onChange,
disabled,
rangeSpec,
editNode = false,
}: FloatComponentType): JSX.Element {
handleOnNewValue,
}: InputProps<string, FloatComponentType>): JSX.Element {
const step = rangeSpec?.step ?? 0.1;
const min = rangeSpec?.min ?? -2;
const max = rangeSpec?.max ?? 2;
// Clear component state
useEffect(() => {
if (disabled && value !== "") {
onChange("", undefined, true);
handleOnNewValue({ value: "" }, { skipSnapshot: true });
}
}, [disabled]);
@ -30,7 +30,7 @@ export default function FloatComponent({
};
const handleChange = (event) => {
onChange(Number(event.target.value));
handleOnNewValue({ value: Number(event.target.value) });
};
return (

View file

@ -7,12 +7,12 @@ import {
CONSOLE_ERROR_MSG,
INVALID_FILE_ALERT,
INVALID_FILE_SIZE_ALERT,
} from "../../constants/alerts_constants";
import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { FileComponentType } from "../../types/components";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
} from "../../../../constants/alerts_constants";
import useAlertStore from "../../../../stores/alertStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { FileComponentType, InputProps } from "../../types";
export default function InputFileComponent({
value,
@ -21,7 +21,7 @@ export default function InputFileComponent({
fileTypes,
editNode = false,
id,
}: FileComponentType): JSX.Element {
}: InputProps<string, FileComponentType>): JSX.Element {
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
const setErrorData = useAlertStore((state) => state.setErrorData);
const { validateFileSize } = useFileSizeValidator(setErrorData);

View file

@ -3,34 +3,39 @@ import {
useGetGlobalVariables,
} from "@/controllers/API/queries/variables";
import { useEffect } from "react";
import DeleteConfirmationModal from "../../modals/deleteConfirmationModal";
import useAlertStore from "../../stores/alertStore";
import { InputGlobalComponentType } from "../../types/components";
import { cn } from "../../utils/utils";
import GlobalVariableModal from "../GlobalVariableModal/GlobalVariableModal";
import ForwardedIconComponent from "../genericIconComponent";
import InputComponent from "../inputComponent";
import { CommandItem } from "../ui/command";
import DeleteConfirmationModal from "../../../../modals/deleteConfirmationModal";
import useAlertStore from "../../../../stores/alertStore";
import { cn } from "../../../../utils/utils";
import GlobalVariableModal from "../../../GlobalVariableModal/GlobalVariableModal";
import ForwardedIconComponent from "../../../genericIconComponent";
import InputComponent from "../../../inputComponent";
import { CommandItem } from "../../../ui/command";
import { InputGlobalComponentType, InputProps } from "../../types";
export default function InputGlobalComponent({
disabled,
onChange,
name,
data,
handleOnNewValue,
value,
id,
load_from_db,
password,
editNode = false,
}: InputGlobalComponentType): JSX.Element {
}: InputProps<string, InputGlobalComponentType>): JSX.Element {
const setErrorData = useAlertStore((state) => state.setErrorData);
const { data: globalVariables } = useGetGlobalVariables();
const { mutate: mutateDeleteGlobalVariable } = useDeleteGlobalVariables();
useEffect(() => {
if (data && globalVariables)
if (globalVariables)
if (
data.load_from_db &&
!globalVariables.find((variable) => variable.name === data.value)
load_from_db &&
!globalVariables.find((variable) => variable.name === value)
) {
onChange("", false, true);
handleOnNewValue(
{ value: "", load_from_db: false },
{ skipSnapshot: true },
);
}
}, [globalVariables]);
@ -42,8 +47,8 @@ export default function InputGlobalComponent({
{ id },
{
onSuccess: () => {
if (data?.value === key && data?.load_from_db) {
onChange("", false);
if (value === key && load_from_db) {
handleOnNewValue({ value: "", load_from_db: false });
}
},
onError: () => {
@ -63,11 +68,11 @@ export default function InputGlobalComponent({
}
return (
<InputComponent
id={"input-" + name}
id={id}
editNode={editNode}
disabled={disabled}
password={data.password ?? false}
value={data.value ?? ""}
password={password ?? false}
value={value ?? ""}
options={globalVariables?.map((variable) => variable.name) ?? []}
optionsPlaceholder={"Global Variables"}
optionsIcon="Globe"
@ -110,19 +115,23 @@ export default function InputGlobalComponent({
</DeleteConfirmationModal>
)}
selectedOption={
data?.load_from_db &&
load_from_db &&
globalVariables &&
globalVariables
?.map((variable) => variable.name)
.includes(data?.value ?? "")
? data?.value
globalVariables?.map((variable) => variable.name).includes(value ?? "")
? value
: ""
}
setSelectedOption={(value) => {
onChange(value, value !== "" ? true : false);
handleOnNewValue({
value: value,
load_from_db: value !== "" ? true : false,
});
}}
onChange={(value, skipSnapshot) => {
onChange(value, false, skipSnapshot);
handleOnNewValue(
{ value: value, load_from_db: false },
{ skipSnapshot },
);
}}
/>
);

View file

@ -1,24 +1,23 @@
import { useEffect } from "react";
import { InputListComponentType } from "../../types/components";
import _ from "lodash";
import { classNames, cn } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import { Input } from "../ui/input";
import { classNames, cn } from "../../../../utils/utils";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { Input } from "../../../ui/input";
import { InputListComponentType, InputProps } from "../../types";
export default function InputListComponent({
value,
onChange,
value = [""],
handleOnNewValue,
disabled,
editNode = false,
componentName,
playgroundDisabled,
id,
}: InputListComponentType): JSX.Element {
}: InputProps<string[], InputListComponentType>): JSX.Element {
useEffect(() => {
if (disabled && value.length > 0 && value[0] !== "") {
onChange([""], undefined, true);
handleOnNewValue({ value: [""] }, { skipSnapshot: true });
}
}, [disabled]);
@ -32,26 +31,26 @@ export default function InputListComponent({
const handleInputChange = (index, newValue) => {
const newInputList = _.cloneDeep(value);
newInputList[index] = newValue;
onChange(newInputList);
handleOnNewValue({ value: newInputList });
};
const addNewInput = (e) => {
e.preventDefault();
const newInputList = _.cloneDeep(value);
newInputList.push("");
onChange(newInputList);
handleOnNewValue({ value: newInputList });
};
const removeInput = (index, e) => {
e.preventDefault();
const newInputList = _.cloneDeep(value);
newInputList.splice(index, 1);
onChange(newInputList);
handleOnNewValue({ value: newInputList });
};
const getButtonClassName = () =>
classNames(
disabled || playgroundDisabled
disabled
? "cursor-not-allowed text-muted-foreground"
: "text-primary hover:text-accent-foreground",
);
@ -69,7 +68,7 @@ export default function InputListComponent({
{value.map((singleValue, index) => (
<div key={index} className="flex w-full gap-3">
<Input
disabled={disabled || playgroundDisabled}
disabled={disabled}
type="text"
value={singleValue}
className={editNode ? "input-edit-node" : ""}
@ -82,7 +81,7 @@ export default function InputListComponent({
className={getButtonClassName()}
onClick={index === 0 ? addNewInput : (e) => removeInput(index, e)}
data-testid={getTestId(index === 0 ? "plus" : "minus", index)}
disabled={disabled || playgroundDisabled}
disabled={disabled}
>
<IconComponent
name={index === 0 ? "Plus" : "X"}

View file

@ -7,24 +7,24 @@ import {
NumberInputStepper,
} from "@chakra-ui/number-input";
import { useEffect, useRef, useState } from "react";
import { IntComponentType } from "../../types/components";
import { handleKeyDown } from "../../utils/reactflowUtils";
import { handleKeyDown } from "../../../../utils/reactflowUtils";
import { InputProps, IntComponentType } from "../../types";
export default function IntComponent({
value,
onChange,
handleOnNewValue,
rangeSpec,
disabled,
editNode = false,
id = "",
}: IntComponentType): JSX.Element {
}: InputProps<number, IntComponentType>): JSX.Element {
const min = -Infinity;
// Clear component state
useEffect(() => {
if (disabled && value !== 0) {
onChange(0, undefined, true);
handleOnNewValue({ value: 0 }, { skipSnapshot: true });
}
}, [disabled, onChange]);
}, [disabled, handleOnNewValue]);
const [cursor, setCursor] = useState<number | null>(null);
const ref = useRef<HTMLInputElement>(null);
@ -35,7 +35,7 @@ export default function IntComponent({
const handleChangeInput = (e: React.ChangeEvent<HTMLInputElement>) => {
setCursor(e.target.selectionStart);
onChange(Number(e.target.value));
handleOnNewValue({ value: Number(e.target.value) });
};
const getStepValue = () => {
@ -58,7 +58,7 @@ export default function IntComponent({
};
const handleNumberChange = (newValue) => {
onChange(Number(newValue));
handleOnNewValue({ value: Number(newValue) });
};
const handleInputChange = (event) => {

View file

@ -1,5 +1,4 @@
import { useEffect, useState } from "react";
import { KeyPairListComponentType } from "../../types/components";
import {
convertObjToArray,
@ -7,20 +6,24 @@ import {
hasDuplicateKeys,
} from "@/utils/reactflowUtils";
import { cloneDeep } from "lodash";
import IconComponent from "../genericIconComponent";
import { Input } from "../ui/input";
import IconComponent from "../../../genericIconComponent";
import { Input } from "../../../ui/input";
import { InputProps, KeyPairListComponentType } from "../../types";
export default function KeypairListComponent({
value,
onChange,
handleOnNewValue,
disabled,
editNode = false,
isList = true,
id,
}: KeyPairListComponentType): JSX.Element {
}: InputProps<
object[] | object | string,
KeyPairListComponentType
>): JSX.Element {
useEffect(() => {
if (disabled && value.length > 0 && value[0] !== "") {
onChange([{ "": "" }]);
handleOnNewValue({ value: [{ "": "" }] }, { skipSnapshot: true });
}
}, [disabled]);
@ -37,8 +40,8 @@ export default function KeypairListComponent({
const valueToNumbers = convertValuesToNumbers(newValue);
setDuplicateKey(hasDuplicateKeys(valueToNumbers));
if (isList) {
onChange(valueToNumbers);
} else onChange(valueToNumbers[0]);
handleOnNewValue({ value: valueToNumbers });
} else handleOnNewValue({ value: valueToNumbers[0] });
};
const handleChangeKey = (event, idx) => {
@ -64,13 +67,13 @@ export default function KeypairListComponent({
const addNewKeyValuePair = () => {
const newValues = cloneDeep(values);
newValues.push({ "": "" });
onChange(newValues);
handleOnNewValue({ value: newValues });
};
const removeKeyValuePair = (index) => {
const newValues = cloneDeep(values);
newValues.splice(index, 1);
onChange(newValues);
handleOnNewValue({ value: newValues });
};
const getInputClassName = (isEditNode, isDuplicateKey) => {

View file

@ -1,8 +1,7 @@
import { LinkComponentType } from "@/types/components";
import { useCallback, useEffect, useState } from "react";
import { classNames } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import { classNames } from "../../../../utils/utils";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { InputProps, LinkComponentType } from "../../types";
const DEFAULT_ICON = "ExternalLink";
@ -10,21 +9,17 @@ export default function LinkComponent({
value,
disabled = false,
id = "",
}: LinkComponentType): JSX.Element {
const [componentValue, setComponentValue] = useState(value);
useEffect(() => {
setComponentValue(value);
}, [value]);
const handleOpenLink = useCallback(() => {
if (componentValue?.value) {
const url = !/^https?:\/\//i.test(componentValue.value)
? `https://${componentValue.value}`
: componentValue.value;
text,
icon,
editNode = false,
handleOnNewValue,
}: InputProps<string, LinkComponentType>): JSX.Element {
function handleOpenLink() {
if (value) {
const url = !/^https?:\/\//i.test(value) ? `https://${value}` : value;
window.open(url, "_blank", "noopener,noreferrer");
}
}, [componentValue]);
}
const buttonClassName = classNames(
"nopan w-full shrink-0",
@ -49,16 +44,13 @@ export default function LinkComponent({
<Button
data-testid={id}
onClick={handleOpenLink}
disabled={disabled || !componentValue}
disabled={disabled || !value}
type="button"
variant="primary"
size="sm"
className={buttonClassName}
>
<ButtonContent
icon={componentValue?.icon ?? DEFAULT_ICON}
text={componentValue?.text ?? ""}
/>
<ButtonContent icon={icon ?? DEFAULT_ICON} text={text ?? ""} />
</Button>
</div>
);

View file

@ -1,42 +1,42 @@
import { PopoverAnchor } from "@radix-ui/react-popover";
import Fuse from "fuse.js";
import { useEffect, useRef, useState } from "react";
import { MultiselectComponentType } from "../../types/components";
import { cn } from "../../utils/utils";
import { default as ForwardedIconComponent } from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent";
import { Button } from "../ui/button";
import { cn } from "../../../../utils/utils";
import { default as ForwardedIconComponent } from "../../../genericIconComponent";
import ShadTooltip from "../../../shadTooltipComponent";
import { Button } from "../../../ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandItem,
CommandList,
} from "../ui/command";
} from "../../../ui/command";
import {
Popover,
PopoverContent,
PopoverContentWithoutPortal,
PopoverTrigger,
} from "../ui/popover";
} from "../../../ui/popover";
import { InputProps, MultiselectComponentType } from "../../types";
export default function MultiselectComponent({
disabled,
isLoading,
value,
options: defaultOptions,
handleOnNewValue,
combobox,
onSelect,
editNode = false,
id = "",
children,
}: MultiselectComponentType): JSX.Element {
const [open, setOpen] = useState(children ? true : false);
}: InputProps<string[], MultiselectComponentType>): JSX.Element {
const [open, setOpen] = useState(false);
const treatedValue = typeof value === "string" ? [value] : value;
const refButton = useRef<HTMLButtonElement>(null);
const PopoverContentDropdown =
children || editNode ? PopoverContent : PopoverContentWithoutPortal;
const PopoverContentDropdown = editNode
? PopoverContent
: PopoverContentWithoutPortal;
const [customValues, setCustomValues] = useState<string[]>([]);
const [searchValue, setSearchValue] = useState("");
@ -46,7 +46,7 @@ export default function MultiselectComponent({
const [options, setOptions] = useState<string[]>(defaultOptions);
const fuseOptions = new Fuse(options, { keys: ["name", "value"] });
const fuseValues = new Fuse(value, { keys: ["name", "value"] });
const fuseValues = new Fuse(treatedValue, { keys: ["name", "value"] });
const searchRoleByTerm = async (v: string) => {
const fuse = onlySelected ? fuseValues : fuseOptions;
@ -57,14 +57,14 @@ export default function MultiselectComponent({
v
? filtered
: onlySelected
? options.filter((x) => value.includes(x))
? options.filter((x) => treatedValue.includes(x))
: options,
);
};
useEffect(() => {
if (disabled && value.length > 0 && value[0] !== "") {
onSelect([], undefined, true);
if (disabled && treatedValue.length > 0 && treatedValue[0] !== "") {
handleOnNewValue({ value: [] }, { skipSnapshot: true });
}
}, [disabled]);
@ -77,9 +77,11 @@ export default function MultiselectComponent({
}, [options]);
useEffect(() => {
setCustomValues(value.filter((v) => !defaultOptions.includes(v)) ?? []);
setCustomValues(
treatedValue.filter((v) => !defaultOptions.includes(v)) ?? [],
);
setOptions([
...value.filter((v) => !defaultOptions.includes(v) && v),
...treatedValue.filter((v) => !defaultOptions.includes(v) && v),
...defaultOptions,
]);
}, [value]);
@ -93,10 +95,12 @@ export default function MultiselectComponent({
}, [open]);
const handleOptionSelect = (currentValue) => {
if (value.includes(currentValue)) {
onSelect(value.filter((v) => v !== currentValue));
if (treatedValue.includes(currentValue)) {
handleOnNewValue({
value: treatedValue.filter((v) => v !== currentValue),
});
} else {
onSelect([...value, currentValue]);
handleOnNewValue({ value: [...treatedValue, currentValue] });
}
};
@ -118,8 +122,9 @@ export default function MultiselectComponent({
)}
>
<span className="truncate" data-testid={`value-dropdown-${id}`}>
{value.length > 0 && options.find((option) => value.includes(option))
? value.join(", ")
{treatedValue.length > 0 &&
options.find((option) => treatedValue.includes(option))
? treatedValue.join(", ")
: "Choose an option..."}
</span>
<ForwardedIconComponent
@ -177,7 +182,7 @@ export default function MultiselectComponent({
name="Check"
className={cn(
"ml-auto h-4 w-4 shrink-0 text-primary",
value.includes(option) ? "opacity-100" : "opacity-0",
treatedValue.includes(option) ? "opacity-100" : "opacity-0",
)}
/>
</CommandItem>
@ -189,11 +194,7 @@ export default function MultiselectComponent({
);
if (Object.keys(options).length === 0 && !combobox) {
return isLoading ? (
<div>
<span className="text-sm italic">Loading...</span>
</div>
) : (
return (
<div>
<span className="text-sm italic">
No parameters are available for display.
@ -203,22 +204,14 @@ export default function MultiselectComponent({
}
return (
<Popover open={open} onOpenChange={children ? () => {} : setOpen}>
{children ? (
<PopoverAnchor>{children}</PopoverAnchor>
) : (
renderDropdownTrigger()
)}
<Popover open={open} onOpenChange={setOpen}>
{renderDropdownTrigger()}
<PopoverContentDropdown
onOpenAutoFocus={(event) => event.preventDefault()}
side="bottom"
avoidCollisions={!!children}
avoidCollisions={false}
className="noflow nowheel nopan nodelete nodrag p-0"
style={
children
? {}
: { minWidth: refButton?.current?.clientWidth ?? "200px" }
}
style={{ minWidth: refButton?.current?.clientWidth ?? "200px" }}
>
<Command>
{renderSearchInput()}

View file

@ -1,24 +1,24 @@
import PromptModal from "@/modals/promptModal";
import { useEffect } from "react";
import { PromptAreaComponentType } from "../../types/components";
import { cn } from "../../utils/utils";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import { cn } from "../../../../utils/utils";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { InputProps, PromptAreaComponentType } from "../../types";
export default function PromptAreaComponent({
field_name,
setNodeClass,
nodeClass,
handleOnNewValue,
handleNodeClass,
value,
onChange,
disabled,
editNode = false,
id = "",
readonly = false,
}: PromptAreaComponentType): JSX.Element {
}: InputProps<string, PromptAreaComponentType>): JSX.Element {
useEffect(() => {
if (disabled && value !== "") {
onChange("", undefined, true);
handleOnNewValue({ value: "" }, { skipSnapshot: true });
}
}, [disabled]);
@ -59,9 +59,9 @@ export default function PromptAreaComponent({
field_name={field_name}
readonly={readonly}
value={value}
setValue={onChange}
setValue={() => handleOnNewValue({ value: "" })}
nodeClass={nodeClass}
setNodeClass={setNodeClass}
setNodeClass={handleNodeClass}
>
<Button unstyled className="w-full">
<div className="flex w-full items-center gap-3">

View file

@ -2,6 +2,8 @@ import { RefreshButton } from "@/components/ui/refreshButton";
import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value";
import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template";
import useAlertStore from "@/stores/alertStore";
import { APIClassType, InputFieldType } from "@/types/api";
import { InputProps } from "../../types";
export function RefreshParameterComponent({
children,
@ -12,6 +14,15 @@ export function RefreshParameterComponent({
handleNodeClass,
nodeId,
name,
}: {
children: React.ReactElement<InputProps>;
templateData: Partial<InputFieldType>;
disabled: boolean;
nodeClass: APIClassType;
editNode: boolean;
handleNodeClass: (value: any, code?: string, type?: string) => void;
nodeId: string;
name: string;
}) {
const postTemplateValue = usePostTemplateValue({
parameterId: name,

View file

@ -0,0 +1,47 @@
import { InputProps, StrRenderComponentType } from "../../types";
import DropdownComponent from "../dropdownComponent";
import InputGlobalComponent from "../inputGlobalComponent";
import TextAreaComponent from "../textAreaComponent";
export function StrRenderComponent({
templateData,
name,
...baseInputProps
}: InputProps<string, StrRenderComponentType>) {
const { handleOnNewValue, id, disabled, editNode, value } = baseInputProps;
if (!templateData.options) {
return templateData.multiline ? (
<TextAreaComponent
{...baseInputProps}
password={templateData.password}
updateVisibility={() => {
if (templateData.password !== undefined) {
handleOnNewValue(
{ password: !templateData.password },
{ skipSnapshot: true },
);
}
}}
id={`textarea_${id}`}
/>
) : (
<InputGlobalComponent
{...baseInputProps}
password={templateData.password}
load_from_db={templateData.load_from_db}
id={"input-" + name}
/>
);
}
if (!!templateData.options) {
return (
<DropdownComponent
{...baseInputProps}
options={templateData.options}
combobox={templateData.combobox}
/>
);
}
}

View file

@ -1,24 +1,24 @@
import ComponentTextModal from "@/modals/textAreaModal";
import { classNames } from "@/utils/utils";
import { useEffect } from "react";
import { TextAreaComponentType } from "../../types/components";
import IconComponent from "../genericIconComponent";
import { Button } from "../ui/button";
import { Textarea } from "../ui/textarea";
import IconComponent from "../../../genericIconComponent";
import { Button } from "../../../ui/button";
import { Textarea } from "../../../ui/textarea";
import { InputProps, TextAreaComponentType } from "../../types";
export default function TextAreaComponent({
value,
onChange,
disabled,
handleOnNewValue,
editNode = false,
id = "",
password,
updateVisibility,
}: TextAreaComponentType): JSX.Element {
}: InputProps<string, TextAreaComponentType>): JSX.Element {
// Clear text area
useEffect(() => {
if (disabled && value !== "") {
onChange("", undefined, true);
handleOnNewValue({ value: "" }, { skipSnapshot: true });
}
}, [disabled]);
@ -38,7 +38,7 @@ export default function TextAreaComponent({
)}
rows={1}
placeholder="Type something..."
onChange={(event) => onChange(event.target.value)}
onChange={(event) => handleOnNewValue({ value: event.target.value })}
/>
);
@ -46,19 +46,21 @@ export default function TextAreaComponent({
<ComponentTextModal
changeVisibility={updateVisibility}
value={value}
setValue={(value) => onChange(value)}
setValue={(value) => handleOnNewValue({ value: value })}
disabled={disabled}
password={password}
>
<div className={classNames("flex items-center")}>
<Button unstyled>
<Button unstyled disabled={disabled}>
<IconComponent
strokeWidth={1.5}
id={id}
name="ExternalLink"
className={classNames(
"icons-parameters-comp shrink-0",
disabled ? "text-ring" : "hover:text-accent-foreground",
disabled
? "cursor-not-allowed text-ring"
: "hover:text-accent-foreground",
)}
/>
</Button>

View file

@ -1,13 +1,15 @@
import { ToggleComponentType } from "../../types/components";
import { Switch } from "../ui/switch";
import { Switch } from "../../../ui/switch";
import { InputProps, ToggleComponentType } from "../../types";
export default function ToggleShadComponent({
enabled,
setEnabled,
value,
editNode,
handleOnNewValue,
disabled,
size,
id = "",
}: ToggleComponentType): JSX.Element {
showToogle,
id,
}: InputProps<boolean, ToggleComponentType>): JSX.Element {
let scaleX, scaleY;
switch (size) {
case "small":
@ -23,8 +25,14 @@ export default function ToggleShadComponent({
scaleY = 1;
break;
default:
scaleX = 1;
scaleY = 1;
if (editNode) {
scaleX = 0.6;
scaleY = 0.6;
} else {
scaleX = 1;
scaleY = 1;
}
break;
}
return (
@ -36,9 +44,12 @@ export default function ToggleShadComponent({
}}
disabled={disabled}
className=""
checked={enabled}
checked={value}
onCheckedChange={(isEnabled: boolean) => {
setEnabled(isEnabled);
const data = showToogle
? { advanced: !isEnabled }
: { value: isEnabled };
handleOnNewValue(data);
}}
></Switch>
);

View file

@ -2,18 +2,22 @@ import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value";
import { TEXT_FIELD_TYPES } from "@/constants/constants";
import { APIClassType, InputFieldType } from "@/types/api";
import { useMemo } from "react";
import TableNodeComponent from "../TableNodeComponent";
import CodeAreaComponent from "../codeAreaComponent";
import DictComponent from "../dictComponent";
import FloatComponent from "../floatComponent";
import InputFileComponent from "../inputFileComponent";
import IntComponent from "../intComponent";
import KeypairListComponent from "../keypairListComponent";
import LinkComponent from "../linkComponent";
import PromptAreaComponent from "../promptComponent";
import ToggleShadComponent from "../toggleShadComponent";
import { RefreshParameterComponent } from "./component/refreshParameterComponent";
import { StrRenderComponent } from "./component/strRenderComponent";
import TableNodeComponent from "./components/TableNodeComponent";
import CodeAreaComponent from "./components/codeAreaComponent";
import DictComponent from "./components/dictComponent";
import { EmptyParameterComponent } from "./components/emptyParameterComponent";
import FloatComponent from "./components/floatComponent";
import InputFileComponent from "./components/inputFileComponent";
import InputListComponent from "./components/inputListComponent";
import IntComponent from "./components/intComponent";
import KeypairListComponent from "./components/keypairListComponent";
import LinkComponent from "./components/linkComponent";
import MultiselectComponent from "./components/multiselectComponent";
import PromptAreaComponent from "./components/promptComponent";
import { RefreshParameterComponent } from "./components/refreshParameterComponent";
import { StrRenderComponent } from "./components/strRenderComponent";
import ToggleShadComponent from "./components/toggleShadComponent";
import { InputProps } from "./types";
export function ParameterRenderComponent({
handleOnNewValue,
@ -36,10 +40,6 @@ export function ParameterRenderComponent({
nodeClass: APIClassType;
disabled: boolean;
}) {
const onChange = (value: any) => {
handleOnNewValue({ value });
};
const id = (
templateData.type +
"_" +
@ -47,6 +47,123 @@ export function ParameterRenderComponent({
templateData.name
).toLowerCase();
const renderComponent = (): React.ReactElement<InputProps> => {
const baseInputProps: InputProps = {
id,
value: templateValue,
editNode,
handleOnNewValue,
disabled,
nodeClass,
handleNodeClass,
readonly: templateData.readonly,
};
if (TEXT_FIELD_TYPES.includes(templateData.type ?? "")) {
if (templateData.list) {
if (!templateData.options) {
return (
<InputListComponent
{...baseInputProps}
componentName={name}
id={`inputlist_${id}`}
/>
);
}
if (!!templateData.options) {
return (
<MultiselectComponent
{...baseInputProps}
combobox={templateData.combobox}
options={
(Array.isArray(templateData.options)
? templateData.options
: [templateData.options]) || []
}
id={`multiselect_${id}`}
/>
);
}
}
return (
<StrRenderComponent
{...baseInputProps}
templateData={templateData}
name={name}
editNode={editNode}
/>
);
}
switch (templateData.type) {
case "NestedDict":
return <DictComponent {...baseInputProps} id={`dict_${id}`} />;
case "dict":
return (
<KeypairListComponent
{...baseInputProps}
isList={templateData.list ?? false}
id={`keypair_${id}`}
/>
);
case "bool":
return <ToggleShadComponent {...baseInputProps} id={`toggle_${id}`} />;
case "link":
return (
<LinkComponent
{...baseInputProps}
icon={templateData.icon}
text={templateData.text}
id={`link_${id}`}
/>
);
case "float":
return (
<FloatComponent
{...baseInputProps}
id={`float_${id}`}
rangeSpec={templateData.range_spec}
/>
);
case "int":
return (
<IntComponent
{...baseInputProps}
rangeSpec={templateData.range_spec}
id={`int_${id}`}
/>
);
case "file":
return (
<InputFileComponent
{...baseInputProps}
fileTypes={templateData.fileTypes}
id={`inputfile_${id}`}
/>
);
case "prompt":
return (
<PromptAreaComponent
{...baseInputProps}
readonly={!!nodeClass.flow}
field_name={name}
id={`promptarea_${id}`}
/>
);
case "code":
return <CodeAreaComponent {...baseInputProps} id={`codearea_${id}`} />;
case "table":
return (
<TableNodeComponent
{...baseInputProps}
description={templateData.info || "Add or edit data"}
columns={templateData?.table_schema?.columns}
tableTitle={templateData?.display_name ?? "Table"}
/>
);
default:
return <EmptyParameterComponent {...baseInputProps} />;
}
};
return useMemo(
() => (
<RefreshParameterComponent
@ -58,113 +175,7 @@ export function ParameterRenderComponent({
handleNodeClass={handleNodeClass}
name={name}
>
{TEXT_FIELD_TYPES.includes(templateData.type ?? "") ? (
<StrRenderComponent
templateData={templateData}
value={templateValue}
name={name}
disabled={disabled}
handleOnNewValue={handleOnNewValue}
id={id}
editNode={editNode}
/>
) : templateData.type === "NestedDict" ? (
<DictComponent
disabled={disabled}
editNode={editNode}
value={
(templateValue || "").toString() === "{}" ? {} : templateValue
}
onChange={onChange}
id={`dict_${id}`}
/>
) : templateData.type === "dict" ? (
<KeypairListComponent
disabled={disabled}
editNode={editNode}
value={templateValue}
onChange={onChange}
isList={templateData.list ?? false}
id={`keypair_${id}`}
/>
) : templateData.type === "bool" ? (
<ToggleShadComponent
id={`toggle_${id}`}
disabled={disabled}
enabled={templateValue}
setEnabled={onChange}
size={editNode ? "small" : "large"}
/>
) : templateData.type === "link" ? (
<LinkComponent
value={templateData}
onChange={onChange}
id={`link_${id}`}
/>
) : templateData.type === "float" ? (
<FloatComponent
disabled={disabled}
editNode={editNode}
rangeSpec={templateData.range_spec}
value={templateValue ?? ""}
onChange={onChange}
id={`float_${id}`}
/>
) : templateData.type === "int" ? (
<IntComponent
rangeSpec={templateData.range_spec}
id={`int_${id}`}
disabled={disabled}
editNode={editNode}
value={templateValue ?? 0}
onChange={onChange}
/>
) : templateData.type === "file" ? (
<InputFileComponent
editNode={editNode}
disabled={disabled}
value={templateValue ?? ""}
handleOnNewValue={handleOnNewValue}
fileTypes={templateData.fileTypes}
id={`inputfile_${id}`}
/>
) : templateData.type === "prompt" ? (
<PromptAreaComponent
readonly={nodeClass.flow ? true : false}
field_name={name}
editNode={editNode}
disabled={disabled}
nodeClass={nodeClass}
setNodeClass={handleNodeClass}
value={templateValue ?? ""}
onChange={onChange}
id={`promptarea_${id}`}
/>
) : templateData.type === "code" ? (
<CodeAreaComponent
readonly={nodeClass.flow && templateData.dynamic ? true : false}
dynamic={templateData.dynamic ?? false}
setNodeClass={handleNodeClass}
nodeClass={nodeClass}
disabled={disabled}
editNode={editNode}
value={templateValue ?? ""}
onChange={onChange}
id={`codearea_${id}`}
/>
) : templateData.type === "Any" ? (
<>-</>
) : templateData.type === "table" ? (
<TableNodeComponent
description={templateData.info || "Add or edit data"}
columns={templateData?.table_schema?.columns}
onChange={onChange}
tableTitle={templateData?.display_name ?? "Table"}
value={templateValue}
/>
) : (
String(templateValue)
)}
{renderComponent()}
</RefreshParameterComponent>
),
[templateData, disabled, nodeId, editNode, nodeClass, name, templateValue],

View file

@ -0,0 +1,84 @@
import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value";
import { APIClassType, InputFieldType } from "@/types/api";
import { RangeSpecType } from "@/types/components";
import { ColumnField } from "@/types/utils/functions";
// Base type for RefreshParameterComponent children
export type BaseInputProps<valueType = any> = {
id: string;
value: valueType;
editNode: boolean;
handleOnNewValue: handleOnNewValueType;
disabled: boolean;
nodeClass?: APIClassType;
handleNodeClass?: (value: any, code?: string, type?: string) => void;
readonly?: boolean;
};
// Generic type for composing input props
export type InputProps<valueType = any, T = {}> = BaseInputProps<valueType> & T;
export type TableComponentType = {
description: string;
tableTitle: string;
columns?: ColumnField[];
};
export type FloatComponentType = {
rangeSpec: RangeSpecType;
};
export type IntComponentType = {
rangeSpec: RangeSpecType;
};
export type ToggleComponentType = {
size?: "small" | "medium" | "large";
showToogle?: boolean;
};
export type FileComponentType = {
fileTypes: Array<string>;
};
export type PromptAreaComponentType = {
field_name?: string;
};
export type LinkComponentType = {
icon?: string;
text?: string;
};
export type KeyPairListComponentType = {
value: any;
isList?: boolean;
};
export type StrRenderComponentType = {
templateData: Partial<InputFieldType>;
name: string;
};
export type InputListComponentType = {
componentName?: string;
id?: string;
};
export type DropDownComponentType = {
combobox?: boolean;
options: string[];
};
export type TextAreaComponentType = {
password?: boolean;
updateVisibility?: () => void;
};
export type InputGlobalComponentType = {
load_from_db: boolean | undefined;
password: boolean | undefined;
};
export type MultiselectComponentType = {
options: string[];
combobox?: boolean;
};

View file

@ -3,7 +3,7 @@ import ShadTooltip from "@/components/shadTooltipComponent";
import useFlowStore from "@/stores/flowStore";
import { isTargetHandleConnected } from "@/utils/reactflowUtils";
import { CustomCellRendererProps } from "ag-grid-react";
import ToggleShadComponent from "../../../toggleShadComponent";
import ToggleShadComponent from "../../../parameterRenderComponent/components/toggleShadComponent";
export default function TableAdvancedToggleCellRender({
value: { nodeId, parameterId },
@ -38,14 +38,13 @@ export default function TableAdvancedToggleCellRender({
<div>
<div className="flex h-full items-center">
<ToggleShadComponent
id={"show" + parameterId}
disabled={disabled}
enabled={!parameter.advanced}
setEnabled={(e) => {
handleOnNewValue({ advanced: !e });
}}
size="small"
value={!parameter.advanced}
handleOnNewValue={handleOnNewValue}
editNode={true}
showToogle
size="small"
id={"show" + parameterId}
/>
</div>
</div>

View file

@ -7,6 +7,8 @@ import { ElementRef, forwardRef, useRef, useState } from "react";
import {
DEFAULT_TABLE_ALERT_MSG,
DEFAULT_TABLE_ALERT_TITLE,
NO_COLUMN_DEFINITION_ALERT_DESCRIPTION,
NO_COLUMN_DEFINITION_ALERT_TITLE,
} from "../../constants/constants";
import { useDarkStore } from "../../stores/darkStore";
import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
@ -149,6 +151,25 @@ const TableComponent = forwardRef<
</div>
);
}
if (colDef.length === 0) {
{
return (
<div className="flex h-full w-full items-center justify-center rounded-md border">
<Alert variant={"default"} className="w-fit">
<ForwardedIconComponent
name="AlertCircle"
className="h-5 w-5 text-primary"
/>
<AlertTitle>{NO_COLUMN_DEFINITION_ALERT_TITLE}</AlertTitle>
<AlertDescription>
{NO_COLUMN_DEFINITION_ALERT_DESCRIPTION}
</AlertDescription>
</Alert>
</div>
);
}
}
return (
<div
className={cn(

View file

@ -855,6 +855,11 @@ export const DEFAULT_TABLE_ALERT_MSG = `Oops! It seems there's no data to displa
export const DEFAULT_TABLE_ALERT_TITLE = "No Data Available";
export const NO_COLUMN_DEFINITION_ALERT_TITLE = "No Column Definitions";
export const NO_COLUMN_DEFINITION_ALERT_DESCRIPTION =
"There are no column definitions available for this table.";
export const LOCATIONS_TO_RETURN = ["/flow/", "/settings/"];
export const MAX_BATCH_SIZE = 50;

View file

@ -1,14 +1,14 @@
import { IOJSONInputComponentType } from "@/types/components";
import { useEffect, useRef } from "react";
import JsonView from "react18-json-view";
import { useDarkStore } from "../../../../../../stores/darkStore";
import { DictComponentType } from "../../../../../../types/components";
export default function IoJsonInput({
value = [],
onChange,
left,
output,
}: DictComponentType): JSX.Element {
}: IOJSONInputComponentType): JSX.Element {
useEffect(() => {
if (value) onChange(value);
}, [value]);

View file

@ -1,9 +1,11 @@
import useHandleNewValue from "@/CustomNodes/hooks/use-handle-new-value";
import { NodeType } from "@/types/flow";
import { cloneDeep } from "lodash";
import { useState } from "react";
import ImageViewer from "../../../../components/ImageViewer";
import CsvOutputComponent from "../../../../components/csvOutputComponent";
import DataOutputComponent from "../../../../components/dataOutputComponent";
import InputListComponent from "../../../../components/inputListComponent";
import InputListComponent from "../../../../components/parameterRenderComponent/components/inputListComponent";
import PdfViewer from "../../../../components/pdfViewer";
import { Textarea } from "../../../../components/ui/textarea";
import { PDFViewConstant } from "../../../../constants/constants";
@ -33,14 +35,14 @@ export default function IOFieldView({
const nodes = useFlowStore((state) => state.nodes);
const setNode = useFlowStore((state) => state.setNode);
const flowPool = useFlowStore((state) => state.flowPool);
const node = nodes.find((node) => node.id === fieldId);
const node: NodeType | undefined = nodes.find((node) => node.id === fieldId);
const flowPoolNode = (flowPool[node!.id] ?? [])[
(flowPool[node!.id]?.length ?? 1) - 1
];
const handleChangeSelect = (e) => {
if (node) {
let newNode = cloneDeep(node);
if (newNode.data.node.template.separator) {
if (newNode.data.node?.template.separator) {
newNode.data.node.template.separator.value = e;
setNode(newNode.id, newNode);
}
@ -53,6 +55,14 @@ export default function IOFieldView({
(flowPool[node!.id] ?? [])[(flowPool[node!.id]?.length ?? 1) - 1]?.data
.results.text ?? "";
const { handleOnNewValue } = node?.data.node
? useHandleNewValue({
node: node.data.node,
nodeId: node.id,
name: "input_value",
})
: { handleOnNewValue: (value: any, options?: any) => {} };
function handleOutputType() {
if (!node) return <>"No node found!"</>;
switch (type) {
@ -129,14 +139,10 @@ export default function IOFieldView({
return (
<>
<InputListComponent
id={`playground_${node.id}_input_list`}
editNode={false}
value={node.data.node!.template["input_value"]?.value}
onChange={(e) => {
if (node) {
let newNode = cloneDeep(node);
newNode.data.node!.template["input_value"].value = e;
setNode(node.id, newNode);
}
}}
handleOnNewValue={handleOnNewValue}
disabled={false}
/>
</>
@ -236,16 +242,11 @@ export default function IOFieldView({
return (
<>
<InputListComponent
id={`playground_${node.id}_output_list`}
editNode={false}
value={node.data.node!.template["input_value"]?.value}
onChange={(e) => {
if (node) {
let newNode = cloneDeep(node);
newNode.data.node!.template["input_value"].value = e;
setNode(node.id, newNode);
}
}}
playgroundDisabled
disabled={false}
handleOnNewValue={handleOnNewValue}
disabled={true}
/>
</>
);

View file

@ -10,36 +10,42 @@ import BaseModal from "../baseModal";
interface TableModalProps extends TableComponentProps {
tableTitle: string;
description: string;
disabled?: boolean;
children: React.ReactNode;
}
const TableModal = forwardRef<
ElementRef<typeof TableComponent>,
TableModalProps
>(({ tableTitle, description, children, ...props }: TableModalProps, ref) => {
return (
<BaseModal>
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
<BaseModal.Header description={description}>
<span className="pr-2">{tableTitle}</span>
<ForwardedIconComponent name="Table" className="mr-2 h-4 w-4" />
</BaseModal.Header>
<BaseModal.Content>
<TableComponent
className="h-full w-full"
ref={ref}
{...props}
></TableComponent>
</BaseModal.Content>
<BaseModal.Footer>
<DialogClose>
<div className="flex w-full justify-end gap-2 pt-2">
<Button>Close</Button>
</div>
</DialogClose>
</BaseModal.Footer>
</BaseModal>
);
});
>(
(
{ tableTitle, description, children, disabled, ...props }: TableModalProps,
ref,
) => {
return (
<BaseModal disable={disabled}>
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
<BaseModal.Header description={description}>
<span className="pr-2">{tableTitle}</span>
<ForwardedIconComponent name="Table" className="mr-2 h-4 w-4" />
</BaseModal.Header>
<BaseModal.Content>
<TableComponent
className="h-full w-full"
ref={ref}
{...props}
></TableComponent>
</BaseModal.Content>
<BaseModal.Footer>
<DialogClose>
<div className="flex w-full justify-end gap-2 pt-2">
<Button>Close</Button>
</div>
</DialogClose>
</BaseModal.Footer>
</BaseModal>
);
},
);
export default TableModal;

View file

@ -3,11 +3,11 @@ import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value";
import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class";
import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex";
import useAddFlow from "@/hooks/flows/use-add-flow";
import CodeAreaModal from "@/modals/codeAreaModal";
import { APIClassType } from "@/types/api";
import _, { cloneDeep } from "lodash";
import { useEffect, useState } from "react";
import { useUpdateNodeInternals } from "reactflow";
import CodeAreaComponent from "../../../../components/codeAreaComponent";
import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import {
@ -638,22 +638,17 @@ export default function NodeToolbarComponent({
{hasCode && (
<div className="hidden">
{openModal && (
<CodeAreaComponent
<CodeAreaModal
setValue={handleOnNewValue}
open={openModal}
setOpen={setOpenModal}
readonly={
data.node?.flow && data.node.template[name].dynamic
? true
: false
}
dynamic={data.node?.template[name].dynamic ?? false}
dynamic={true}
setNodeClass={handleNodeClass}
nodeClass={data.node}
disabled={false}
value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue}
id={"code-input-node-toolbar-" + name}
/>
>
<></>
</CodeAreaModal>
)}
</div>
)}

View file

@ -42,15 +42,7 @@ export type InputComponentType = {
isObjectOption?: boolean;
onChangeFolderName?: (e: any) => void;
};
export type ToggleComponentType = {
enabled: boolean;
setEnabled: (state: boolean) => void;
disabled?: boolean | undefined;
size: "small" | "medium" | "large";
id?: string;
editNode?: boolean;
};
export type DropDownComponentType = {
export type DropDownComponent = {
disabled?: boolean;
isLoading?: boolean;
value: string;
@ -61,17 +53,6 @@ export type DropDownComponentType = {
id?: string;
children?: ReactNode;
};
export type MultiselectComponentType = {
disabled?: boolean;
isLoading?: boolean;
value: string[];
combobox?: boolean;
options: string[];
onSelect: (value: string[], dbValue?: boolean, snapshot?: boolean) => void;
editNode?: boolean;
id?: string;
children?: ReactNode;
};
export type ParameterComponentType = {
selected?: boolean;
data: NodeDataType;
@ -124,73 +105,12 @@ export type NodeInputFieldComponentType = {
showNode: boolean;
};
export type InputListComponentType = {
value: string[];
onChange: (value: string[], dbValue?: boolean, snapshot?: boolean) => void;
disabled: boolean;
editNode?: boolean;
componentName?: string;
playgroundDisabled?: boolean;
id?: string;
};
export type InputGlobalComponentType = {
disabled: boolean;
onChange: (value: string, dbValue: boolean, snapshot?: boolean) => void;
name: string;
data: Partial<InputFieldType>;
editNode?: boolean;
playgroundDisabled?: boolean;
};
export type KeyPairListComponentType = {
value: any;
onChange: (value: Object[]) => void;
disabled: boolean;
editNode?: boolean;
editNodeModal?: boolean;
isList?: boolean;
id?: string;
};
export type DictComponentType = {
export type IOJSONInputComponentType = {
value: any;
onChange: (value) => void;
disabled?: boolean;
editNode?: boolean;
id?: string;
left?: boolean;
output?: boolean;
};
export type TextAreaComponentType = {
field_name?: string;
nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType) => void;
disabled: boolean;
onChange: (
value: string[] | string,
dbValue?: boolean,
skipSnapshot?: boolean,
) => void;
value: string;
editNode?: boolean;
id?: string;
readonly?: boolean;
password?: boolean;
updateVisibility?: () => void;
};
export type TableComponentType = {
description: string;
tableTitle: string;
onChange: (value: any[]) => void;
value: any[];
editNode?: boolean;
id?: string;
columns?: ColumnField[];
};
export type outputComponentType = {
types: string[];
selected: string;
@ -201,51 +121,6 @@ export type outputComponentType = {
proxy?: OutputFieldProxyType;
};
export type PromptAreaComponentType = {
field_name?: string;
nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType) => void;
disabled: boolean;
onChange: (
value: string[] | string,
dbValue?: boolean,
skipSnapshot?: boolean,
) => void;
value: string;
readonly?: boolean;
editNode?: boolean;
id?: string;
};
export type CodeAreaComponentType = {
setOpenModal?: (bool: boolean) => void;
disabled: boolean;
onChange: (
value: string[] | string,
dbValue?: boolean,
skipSnapshot?: boolean,
) => void;
value: string;
editNode?: boolean;
nodeClass?: APIClassType;
setNodeClass?: (value: APIClassType, type: string) => void;
dynamic?: boolean;
id?: string;
readonly?: boolean;
open?: boolean;
setOpen?: (open: boolean) => void;
};
export type FileComponentType = {
IOInputProps?;
disabled: boolean;
handleOnNewValue: handleOnNewValueType;
value: string;
fileTypes: Array<string>;
editNode?: boolean;
id?: string;
};
export type DisclosureComponentType = {
children: ReactNode;
defaultOpen: boolean;
@ -278,15 +153,6 @@ export type IntComponentType = {
id?: string;
};
export type FloatComponentType = {
value: string;
disabled?: boolean;
onChange: (value: string, dbValue?: boolean, skipSnapshot?: boolean) => void;
rangeSpec: RangeSpecType;
editNode?: boolean;
id?: string;
};
export type FilePreviewType = {
loading: boolean;
file: File;
@ -881,11 +747,3 @@ export type handleSelectPropsType = {
setLockChat: (lock: boolean) => void;
setChatHistory: (chatHistory: ChatMessageType) => void;
};
export type LinkComponentType = {
value: Partial<InputFieldType>;
onChange: (value: string) => void;
disabled?: boolean;
editNode?: boolean;
id?: string;
};

View file

@ -1,4 +1,5 @@
import { expect, test } from "@playwright/test";
import uaParser from "ua-parser-js";
test("user must be able to interact with table input component", async ({
page,
@ -26,6 +27,14 @@ test("user must be able to interact with table input component", async ({
const secondRandomText = Math.random().toString(36).substring(7);
const thirdRandomText = Math.random().toString(36).substring(7);
const getUA = await page.evaluate(() => navigator.userAgent);
const userAgentInfo = uaParser(getUA);
let control = "Control";
if (userAgentInfo.os.name.includes("Mac")) {
control = "Meta";
}
while (modalCount === 0) {
await page.getByText("New Project", { exact: true }).click();
await page.waitForTimeout(3000);
@ -99,7 +108,7 @@ class CustomComponent(Component):
return data
`;
await page.locator("textarea").press("Control+a");
await page.locator("textarea").press(`${control}+a`);
await page.locator("textarea").fill(customCodeWithError);
await page.getByText("Check & Save").last().click();