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 Fuse from "fuse.js";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { ChangeEvent, useEffect, useRef, useState } from "react"; import { ChangeEvent, useEffect, useRef, useState } from "react";
import { DropDownComponentType } from "../../types/components"; import { DropDownComponent } from "../../types/components";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import { default as ForwardedIconComponent } from "../genericIconComponent"; import { default as ForwardedIconComponent } from "../genericIconComponent";
import ShadTooltip from "../shadTooltipComponent"; import ShadTooltip from "../shadTooltipComponent";
@ -31,7 +31,7 @@ export default function Dropdown({
editNode = false, editNode = false,
id = "", id = "",
children, children,
}: DropDownComponentType): JSX.Element { }: DropDownComponent): JSX.Element {
const [open, setOpen] = useState(children ? true : false); const [open, setOpen] = useState(children ? true : false);
const refButton = useRef<HTMLButtonElement>(null); 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 { AgGridReact } from "ag-grid-react";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { useMemo, useRef, useState } from "react"; import { useMemo, useRef, useState } from "react";
import { ForwardedIconComponent } from "../../components/genericIconComponent"; import { ForwardedIconComponent } from "../../../genericIconComponent";
import { TableComponentType } from "../../types/components"; import { Button } from "../../../ui/button";
import { Button } from "../ui/button"; import { InputProps, TableComponentType } from "../../types";
export default function TableNodeComponent({ export default function TableNodeComponent({
tableTitle, tableTitle,
description, description,
value, value,
onChange,
editNode = false, editNode = false,
id = "", id = "",
columns, columns,
}: TableComponentType): JSX.Element { handleOnNewValue,
disabled = false,
}: InputProps<any[], TableComponentType>): JSX.Element {
const dataTypeDefinitions: { const dataTypeDefinitions: {
[cellDataType: string]: DataTypeDefinition<any>; [cellDataType: string]: DataTypeDefinition<any>;
} = useMemo(() => { } = useMemo(() => {
@ -68,7 +69,7 @@ export default function TableNodeComponent({
if (agGrid.current && !agGrid.current.api.isDestroyed()) { if (agGrid.current && !agGrid.current.api.isDestroyed()) {
const rows: any = []; const rows: any = [];
agGrid.current.api.forEachNode((node) => rows.push(node.data)); agGrid.current.api.forEachNode((node) => rows.push(node.data));
onChange(rows); handleOnNewValue({ value: rows });
} }
} }
function deleteRow() { function deleteRow() {
@ -85,7 +86,7 @@ export default function TableNodeComponent({
const toDuplicate = selectedNodes.map((node) => cloneDeep(node.data)); const toDuplicate = selectedNodes.map((node) => cloneDeep(node.data));
setSelectedNodes([]); setSelectedNodes([]);
const rows: any = []; const rows: any = [];
onChange([...value, ...toDuplicate]); handleOnNewValue({ value: [...value, ...toDuplicate] });
} }
} }
function addRow() { function addRow() {
@ -93,7 +94,7 @@ export default function TableNodeComponent({
componentColumns.forEach((column) => { componentColumns.forEach((column) => {
newRow[column.name] = null; newRow[column.name] = null;
}); });
onChange([...value, newRow]); handleOnNewValue({ value: [...value, newRow] });
} }
function updateComponent() { function updateComponent() {
@ -111,7 +112,11 @@ export default function TableNodeComponent({
}); });
return ( 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}> <div className="flex w-full items-center gap-3" data-testid={"div-" + id}>
<TableModal <TableModal
dataTypeDefinitions={dataTypeDefinitions} dataTypeDefinitions={dataTypeDefinitions}
@ -135,9 +140,13 @@ export default function TableNodeComponent({
rowData={value} rowData={value}
> >
<Button <Button
disabled={disabled}
variant="primary" variant="primary"
size={editNode ? "xs" : "default"} 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" /> <ForwardedIconComponent name="Table" className="mt-px h-4 w-4" />
<span className="font-normal">Open Table</span> <span className="font-normal">Open Table</span>

View file

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

View file

@ -1,30 +1,27 @@
import { useEffect } from "react"; import { useEffect } from "react";
import { DictComponentType } from "../../types/components";
import DictAreaModal from "../../modals/dictAreaModal"; import DictAreaModal from "../../../../modals/dictAreaModal";
import { classNames, cn } from "../../utils/utils"; import { classNames, cn } from "../../../../utils/utils";
import ForwardedIconComponent from "../genericIconComponent"; import ForwardedIconComponent from "../../../genericIconComponent";
import { Button } from "../ui/button"; import { Button } from "../../../ui/button";
import { InputProps } from "../../types";
export default function DictComponent({ export default function DictComponent({
value = [], value = [],
onChange, handleOnNewValue,
disabled, disabled,
editNode = false, editNode = false,
id = "", id = "",
}: DictComponentType): JSX.Element { }: InputProps<object | object[] | string>): JSX.Element {
// Create a reference to the value
useEffect(() => { useEffect(() => {
if (disabled) { if (disabled) {
onChange({}); handleOnNewValue({ value: {} }, { skipSnapshot: true });
} }
}, [disabled]); }, [disabled]);
return ( return (
<div <div
className={classNames( className={classNames(
value.length > 1 && editNode ? "my-1" : "",
"flex w-full flex-col gap-3", "flex w-full flex-col gap-3",
disabled ? "pointer-events-none" : "", disabled ? "pointer-events-none" : "",
)} )}
@ -32,9 +29,9 @@ export default function DictComponent({
{ {
<div className="flex w-full gap-3" data-testid={id}> <div className="flex w-full gap-3" data-testid={id}>
<DictAreaModal <DictAreaModal
value={value} value={(value || "").toString() === "{}" ? {} : value}
onChange={(obj) => { onChange={(obj) => {
onChange(obj); handleOnNewValue({ value: obj });
}} }}
disabled={disabled} 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 { useEffect } from "react";
import { FloatComponentType } from "../../types/components"; import { handleKeyDown } from "../../../../utils/reactflowUtils";
import { handleKeyDown } from "../../utils/reactflowUtils"; import { Input } from "../../../ui/input";
import { Input } from "../ui/input"; import { FloatComponentType, InputProps } from "../../types";
export default function FloatComponent({ export default function FloatComponent({
value, value,
onChange,
disabled, disabled,
rangeSpec, rangeSpec,
editNode = false, editNode = false,
}: FloatComponentType): JSX.Element { handleOnNewValue,
}: InputProps<string, FloatComponentType>): JSX.Element {
const step = rangeSpec?.step ?? 0.1; const step = rangeSpec?.step ?? 0.1;
const min = rangeSpec?.min ?? -2; const min = rangeSpec?.min ?? -2;
const max = rangeSpec?.max ?? 2; const max = rangeSpec?.max ?? 2;
// Clear component state // Clear component state
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange("", undefined, true); handleOnNewValue({ value: "" }, { skipSnapshot: true });
} }
}, [disabled]); }, [disabled]);
@ -30,7 +30,7 @@ export default function FloatComponent({
}; };
const handleChange = (event) => { const handleChange = (event) => {
onChange(Number(event.target.value)); handleOnNewValue({ value: Number(event.target.value) });
}; };
return ( return (

View file

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

View file

@ -3,34 +3,39 @@ import {
useGetGlobalVariables, useGetGlobalVariables,
} from "@/controllers/API/queries/variables"; } from "@/controllers/API/queries/variables";
import { useEffect } from "react"; import { useEffect } from "react";
import DeleteConfirmationModal from "../../modals/deleteConfirmationModal"; import DeleteConfirmationModal from "../../../../modals/deleteConfirmationModal";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import { InputGlobalComponentType } from "../../types/components"; import { cn } from "../../../../utils/utils";
import { cn } from "../../utils/utils"; import GlobalVariableModal from "../../../GlobalVariableModal/GlobalVariableModal";
import GlobalVariableModal from "../GlobalVariableModal/GlobalVariableModal"; import ForwardedIconComponent from "../../../genericIconComponent";
import ForwardedIconComponent from "../genericIconComponent"; import InputComponent from "../../../inputComponent";
import InputComponent from "../inputComponent"; import { CommandItem } from "../../../ui/command";
import { CommandItem } from "../ui/command"; import { InputGlobalComponentType, InputProps } from "../../types";
export default function InputGlobalComponent({ export default function InputGlobalComponent({
disabled, disabled,
onChange, handleOnNewValue,
name, value,
data, id,
load_from_db,
password,
editNode = false, editNode = false,
}: InputGlobalComponentType): JSX.Element { }: InputProps<string, InputGlobalComponentType>): JSX.Element {
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const { data: globalVariables } = useGetGlobalVariables(); const { data: globalVariables } = useGetGlobalVariables();
const { mutate: mutateDeleteGlobalVariable } = useDeleteGlobalVariables(); const { mutate: mutateDeleteGlobalVariable } = useDeleteGlobalVariables();
useEffect(() => { useEffect(() => {
if (data && globalVariables) if (globalVariables)
if ( if (
data.load_from_db && load_from_db &&
!globalVariables.find((variable) => variable.name === data.value) !globalVariables.find((variable) => variable.name === value)
) { ) {
onChange("", false, true); handleOnNewValue(
{ value: "", load_from_db: false },
{ skipSnapshot: true },
);
} }
}, [globalVariables]); }, [globalVariables]);
@ -42,8 +47,8 @@ export default function InputGlobalComponent({
{ id }, { id },
{ {
onSuccess: () => { onSuccess: () => {
if (data?.value === key && data?.load_from_db) { if (value === key && load_from_db) {
onChange("", false); handleOnNewValue({ value: "", load_from_db: false });
} }
}, },
onError: () => { onError: () => {
@ -63,11 +68,11 @@ export default function InputGlobalComponent({
} }
return ( return (
<InputComponent <InputComponent
id={"input-" + name} id={id}
editNode={editNode} editNode={editNode}
disabled={disabled} disabled={disabled}
password={data.password ?? false} password={password ?? false}
value={data.value ?? ""} value={value ?? ""}
options={globalVariables?.map((variable) => variable.name) ?? []} options={globalVariables?.map((variable) => variable.name) ?? []}
optionsPlaceholder={"Global Variables"} optionsPlaceholder={"Global Variables"}
optionsIcon="Globe" optionsIcon="Globe"
@ -110,19 +115,23 @@ export default function InputGlobalComponent({
</DeleteConfirmationModal> </DeleteConfirmationModal>
)} )}
selectedOption={ selectedOption={
data?.load_from_db && load_from_db &&
globalVariables && globalVariables &&
globalVariables globalVariables?.map((variable) => variable.name).includes(value ?? "")
?.map((variable) => variable.name) ? value
.includes(data?.value ?? "")
? data?.value
: "" : ""
} }
setSelectedOption={(value) => { setSelectedOption={(value) => {
onChange(value, value !== "" ? true : false); handleOnNewValue({
value: value,
load_from_db: value !== "" ? true : false,
});
}} }}
onChange={(value, skipSnapshot) => { 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 { useEffect } from "react";
import { InputListComponentType } from "../../types/components";
import _ from "lodash"; import _ from "lodash";
import { classNames, cn } from "../../utils/utils"; import { classNames, cn } from "../../../../utils/utils";
import IconComponent from "../genericIconComponent"; import IconComponent from "../../../genericIconComponent";
import { Button } from "../ui/button"; import { Button } from "../../../ui/button";
import { Input } from "../ui/input"; import { Input } from "../../../ui/input";
import { InputListComponentType, InputProps } from "../../types";
export default function InputListComponent({ export default function InputListComponent({
value, value = [""],
onChange, handleOnNewValue,
disabled, disabled,
editNode = false, editNode = false,
componentName, componentName,
playgroundDisabled,
id, id,
}: InputListComponentType): JSX.Element { }: InputProps<string[], InputListComponentType>): JSX.Element {
useEffect(() => { useEffect(() => {
if (disabled && value.length > 0 && value[0] !== "") { if (disabled && value.length > 0 && value[0] !== "") {
onChange([""], undefined, true); handleOnNewValue({ value: [""] }, { skipSnapshot: true });
} }
}, [disabled]); }, [disabled]);
@ -32,26 +31,26 @@ export default function InputListComponent({
const handleInputChange = (index, newValue) => { const handleInputChange = (index, newValue) => {
const newInputList = _.cloneDeep(value); const newInputList = _.cloneDeep(value);
newInputList[index] = newValue; newInputList[index] = newValue;
onChange(newInputList); handleOnNewValue({ value: newInputList });
}; };
const addNewInput = (e) => { const addNewInput = (e) => {
e.preventDefault(); e.preventDefault();
const newInputList = _.cloneDeep(value); const newInputList = _.cloneDeep(value);
newInputList.push(""); newInputList.push("");
onChange(newInputList); handleOnNewValue({ value: newInputList });
}; };
const removeInput = (index, e) => { const removeInput = (index, e) => {
e.preventDefault(); e.preventDefault();
const newInputList = _.cloneDeep(value); const newInputList = _.cloneDeep(value);
newInputList.splice(index, 1); newInputList.splice(index, 1);
onChange(newInputList); handleOnNewValue({ value: newInputList });
}; };
const getButtonClassName = () => const getButtonClassName = () =>
classNames( classNames(
disabled || playgroundDisabled disabled
? "cursor-not-allowed text-muted-foreground" ? "cursor-not-allowed text-muted-foreground"
: "text-primary hover:text-accent-foreground", : "text-primary hover:text-accent-foreground",
); );
@ -69,7 +68,7 @@ export default function InputListComponent({
{value.map((singleValue, index) => ( {value.map((singleValue, index) => (
<div key={index} className="flex w-full gap-3"> <div key={index} className="flex w-full gap-3">
<Input <Input
disabled={disabled || playgroundDisabled} disabled={disabled}
type="text" type="text"
value={singleValue} value={singleValue}
className={editNode ? "input-edit-node" : ""} className={editNode ? "input-edit-node" : ""}
@ -82,7 +81,7 @@ export default function InputListComponent({
className={getButtonClassName()} className={getButtonClassName()}
onClick={index === 0 ? addNewInput : (e) => removeInput(index, e)} onClick={index === 0 ? addNewInput : (e) => removeInput(index, e)}
data-testid={getTestId(index === 0 ? "plus" : "minus", index)} data-testid={getTestId(index === 0 ? "plus" : "minus", index)}
disabled={disabled || playgroundDisabled} disabled={disabled}
> >
<IconComponent <IconComponent
name={index === 0 ? "Plus" : "X"} name={index === 0 ? "Plus" : "X"}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,24 +1,24 @@
import PromptModal from "@/modals/promptModal"; import PromptModal from "@/modals/promptModal";
import { useEffect } from "react"; import { useEffect } from "react";
import { PromptAreaComponentType } from "../../types/components"; import { cn } from "../../../../utils/utils";
import { cn } from "../../utils/utils"; import IconComponent from "../../../genericIconComponent";
import IconComponent from "../genericIconComponent"; import { Button } from "../../../ui/button";
import { Button } from "../ui/button"; import { InputProps, PromptAreaComponentType } from "../../types";
export default function PromptAreaComponent({ export default function PromptAreaComponent({
field_name, field_name,
setNodeClass,
nodeClass, nodeClass,
handleOnNewValue,
handleNodeClass,
value, value,
onChange,
disabled, disabled,
editNode = false, editNode = false,
id = "", id = "",
readonly = false, readonly = false,
}: PromptAreaComponentType): JSX.Element { }: InputProps<string, PromptAreaComponentType>): JSX.Element {
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange("", undefined, true); handleOnNewValue({ value: "" }, { skipSnapshot: true });
} }
}, [disabled]); }, [disabled]);
@ -59,9 +59,9 @@ export default function PromptAreaComponent({
field_name={field_name} field_name={field_name}
readonly={readonly} readonly={readonly}
value={value} value={value}
setValue={onChange} setValue={() => handleOnNewValue({ value: "" })}
nodeClass={nodeClass} nodeClass={nodeClass}
setNodeClass={setNodeClass} setNodeClass={handleNodeClass}
> >
<Button unstyled className="w-full"> <Button unstyled className="w-full">
<div className="flex w-full items-center gap-3"> <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 { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value";
import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template"; import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template";
import useAlertStore from "@/stores/alertStore"; import useAlertStore from "@/stores/alertStore";
import { APIClassType, InputFieldType } from "@/types/api";
import { InputProps } from "../../types";
export function RefreshParameterComponent({ export function RefreshParameterComponent({
children, children,
@ -12,6 +14,15 @@ export function RefreshParameterComponent({
handleNodeClass, handleNodeClass,
nodeId, nodeId,
name, 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({ const postTemplateValue = usePostTemplateValue({
parameterId: name, 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 ComponentTextModal from "@/modals/textAreaModal";
import { classNames } from "@/utils/utils"; import { classNames } from "@/utils/utils";
import { useEffect } from "react"; import { useEffect } from "react";
import { TextAreaComponentType } from "../../types/components"; import IconComponent from "../../../genericIconComponent";
import IconComponent from "../genericIconComponent"; import { Button } from "../../../ui/button";
import { Button } from "../ui/button"; import { Textarea } from "../../../ui/textarea";
import { Textarea } from "../ui/textarea"; import { InputProps, TextAreaComponentType } from "../../types";
export default function TextAreaComponent({ export default function TextAreaComponent({
value, value,
onChange,
disabled, disabled,
handleOnNewValue,
editNode = false, editNode = false,
id = "", id = "",
password, password,
updateVisibility, updateVisibility,
}: TextAreaComponentType): JSX.Element { }: InputProps<string, TextAreaComponentType>): JSX.Element {
// Clear text area // Clear text area
useEffect(() => { useEffect(() => {
if (disabled && value !== "") { if (disabled && value !== "") {
onChange("", undefined, true); handleOnNewValue({ value: "" }, { skipSnapshot: true });
} }
}, [disabled]); }, [disabled]);
@ -38,7 +38,7 @@ export default function TextAreaComponent({
)} )}
rows={1} rows={1}
placeholder="Type something..." 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 <ComponentTextModal
changeVisibility={updateVisibility} changeVisibility={updateVisibility}
value={value} value={value}
setValue={(value) => onChange(value)} setValue={(value) => handleOnNewValue({ value: value })}
disabled={disabled} disabled={disabled}
password={password} password={password}
> >
<div className={classNames("flex items-center")}> <div className={classNames("flex items-center")}>
<Button unstyled> <Button unstyled disabled={disabled}>
<IconComponent <IconComponent
strokeWidth={1.5} strokeWidth={1.5}
id={id} id={id}
name="ExternalLink" name="ExternalLink"
className={classNames( className={classNames(
"icons-parameters-comp shrink-0", "icons-parameters-comp shrink-0",
disabled ? "text-ring" : "hover:text-accent-foreground", disabled
? "cursor-not-allowed text-ring"
: "hover:text-accent-foreground",
)} )}
/> />
</Button> </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({ export default function ToggleShadComponent({
enabled, value,
setEnabled, editNode,
handleOnNewValue,
disabled, disabled,
size, size,
id = "", showToogle,
}: ToggleComponentType): JSX.Element { id,
}: InputProps<boolean, ToggleComponentType>): JSX.Element {
let scaleX, scaleY; let scaleX, scaleY;
switch (size) { switch (size) {
case "small": case "small":
@ -23,8 +25,14 @@ export default function ToggleShadComponent({
scaleY = 1; scaleY = 1;
break; break;
default: default:
scaleX = 1; if (editNode) {
scaleY = 1; scaleX = 0.6;
scaleY = 0.6;
} else {
scaleX = 1;
scaleY = 1;
}
break;
} }
return ( return (
@ -36,9 +44,12 @@ export default function ToggleShadComponent({
}} }}
disabled={disabled} disabled={disabled}
className="" className=""
checked={enabled} checked={value}
onCheckedChange={(isEnabled: boolean) => { onCheckedChange={(isEnabled: boolean) => {
setEnabled(isEnabled); const data = showToogle
? { advanced: !isEnabled }
: { value: isEnabled };
handleOnNewValue(data);
}} }}
></Switch> ></Switch>
); );

View file

@ -2,18 +2,22 @@ import { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value";
import { TEXT_FIELD_TYPES } from "@/constants/constants"; import { TEXT_FIELD_TYPES } from "@/constants/constants";
import { APIClassType, InputFieldType } from "@/types/api"; import { APIClassType, InputFieldType } from "@/types/api";
import { useMemo } from "react"; import { useMemo } from "react";
import TableNodeComponent from "../TableNodeComponent"; import TableNodeComponent from "./components/TableNodeComponent";
import CodeAreaComponent from "../codeAreaComponent"; import CodeAreaComponent from "./components/codeAreaComponent";
import DictComponent from "../dictComponent"; import DictComponent from "./components/dictComponent";
import FloatComponent from "../floatComponent"; import { EmptyParameterComponent } from "./components/emptyParameterComponent";
import InputFileComponent from "../inputFileComponent"; import FloatComponent from "./components/floatComponent";
import IntComponent from "../intComponent"; import InputFileComponent from "./components/inputFileComponent";
import KeypairListComponent from "../keypairListComponent"; import InputListComponent from "./components/inputListComponent";
import LinkComponent from "../linkComponent"; import IntComponent from "./components/intComponent";
import PromptAreaComponent from "../promptComponent"; import KeypairListComponent from "./components/keypairListComponent";
import ToggleShadComponent from "../toggleShadComponent"; import LinkComponent from "./components/linkComponent";
import { RefreshParameterComponent } from "./component/refreshParameterComponent"; import MultiselectComponent from "./components/multiselectComponent";
import { StrRenderComponent } from "./component/strRenderComponent"; 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({ export function ParameterRenderComponent({
handleOnNewValue, handleOnNewValue,
@ -36,10 +40,6 @@ export function ParameterRenderComponent({
nodeClass: APIClassType; nodeClass: APIClassType;
disabled: boolean; disabled: boolean;
}) { }) {
const onChange = (value: any) => {
handleOnNewValue({ value });
};
const id = ( const id = (
templateData.type + templateData.type +
"_" + "_" +
@ -47,6 +47,123 @@ export function ParameterRenderComponent({
templateData.name templateData.name
).toLowerCase(); ).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( return useMemo(
() => ( () => (
<RefreshParameterComponent <RefreshParameterComponent
@ -58,113 +175,7 @@ export function ParameterRenderComponent({
handleNodeClass={handleNodeClass} handleNodeClass={handleNodeClass}
name={name} name={name}
> >
{TEXT_FIELD_TYPES.includes(templateData.type ?? "") ? ( {renderComponent()}
<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)
)}
</RefreshParameterComponent> </RefreshParameterComponent>
), ),
[templateData, disabled, nodeId, editNode, nodeClass, name, templateValue], [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 useFlowStore from "@/stores/flowStore";
import { isTargetHandleConnected } from "@/utils/reactflowUtils"; import { isTargetHandleConnected } from "@/utils/reactflowUtils";
import { CustomCellRendererProps } from "ag-grid-react"; import { CustomCellRendererProps } from "ag-grid-react";
import ToggleShadComponent from "../../../toggleShadComponent"; import ToggleShadComponent from "../../../parameterRenderComponent/components/toggleShadComponent";
export default function TableAdvancedToggleCellRender({ export default function TableAdvancedToggleCellRender({
value: { nodeId, parameterId }, value: { nodeId, parameterId },
@ -38,14 +38,13 @@ export default function TableAdvancedToggleCellRender({
<div> <div>
<div className="flex h-full items-center"> <div className="flex h-full items-center">
<ToggleShadComponent <ToggleShadComponent
id={"show" + parameterId}
disabled={disabled} disabled={disabled}
enabled={!parameter.advanced} value={!parameter.advanced}
setEnabled={(e) => { handleOnNewValue={handleOnNewValue}
handleOnNewValue({ advanced: !e });
}}
size="small"
editNode={true} editNode={true}
showToogle
size="small"
id={"show" + parameterId}
/> />
</div> </div>
</div> </div>

View file

@ -7,6 +7,8 @@ import { ElementRef, forwardRef, useRef, useState } from "react";
import { import {
DEFAULT_TABLE_ALERT_MSG, DEFAULT_TABLE_ALERT_MSG,
DEFAULT_TABLE_ALERT_TITLE, DEFAULT_TABLE_ALERT_TITLE,
NO_COLUMN_DEFINITION_ALERT_DESCRIPTION,
NO_COLUMN_DEFINITION_ALERT_TITLE,
} from "../../constants/constants"; } from "../../constants/constants";
import { useDarkStore } from "../../stores/darkStore"; import { useDarkStore } from "../../stores/darkStore";
import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
@ -149,6 +151,25 @@ const TableComponent = forwardRef<
</div> </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 ( return (
<div <div
className={cn( 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 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 LOCATIONS_TO_RETURN = ["/flow/", "/settings/"];
export const MAX_BATCH_SIZE = 50; export const MAX_BATCH_SIZE = 50;

View file

@ -1,14 +1,14 @@
import { IOJSONInputComponentType } from "@/types/components";
import { useEffect, useRef } from "react"; import { useEffect, useRef } from "react";
import JsonView from "react18-json-view"; import JsonView from "react18-json-view";
import { useDarkStore } from "../../../../../../stores/darkStore"; import { useDarkStore } from "../../../../../../stores/darkStore";
import { DictComponentType } from "../../../../../../types/components";
export default function IoJsonInput({ export default function IoJsonInput({
value = [], value = [],
onChange, onChange,
left, left,
output, output,
}: DictComponentType): JSX.Element { }: IOJSONInputComponentType): JSX.Element {
useEffect(() => { useEffect(() => {
if (value) onChange(value); if (value) onChange(value);
}, [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 { cloneDeep } from "lodash";
import { useState } from "react"; import { useState } from "react";
import ImageViewer from "../../../../components/ImageViewer"; import ImageViewer from "../../../../components/ImageViewer";
import CsvOutputComponent from "../../../../components/csvOutputComponent"; import CsvOutputComponent from "../../../../components/csvOutputComponent";
import DataOutputComponent from "../../../../components/dataOutputComponent"; import DataOutputComponent from "../../../../components/dataOutputComponent";
import InputListComponent from "../../../../components/inputListComponent"; import InputListComponent from "../../../../components/parameterRenderComponent/components/inputListComponent";
import PdfViewer from "../../../../components/pdfViewer"; import PdfViewer from "../../../../components/pdfViewer";
import { Textarea } from "../../../../components/ui/textarea"; import { Textarea } from "../../../../components/ui/textarea";
import { PDFViewConstant } from "../../../../constants/constants"; import { PDFViewConstant } from "../../../../constants/constants";
@ -33,14 +35,14 @@ export default function IOFieldView({
const nodes = useFlowStore((state) => state.nodes); const nodes = useFlowStore((state) => state.nodes);
const setNode = useFlowStore((state) => state.setNode); const setNode = useFlowStore((state) => state.setNode);
const flowPool = useFlowStore((state) => state.flowPool); 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] ?? [])[ const flowPoolNode = (flowPool[node!.id] ?? [])[
(flowPool[node!.id]?.length ?? 1) - 1 (flowPool[node!.id]?.length ?? 1) - 1
]; ];
const handleChangeSelect = (e) => { const handleChangeSelect = (e) => {
if (node) { if (node) {
let newNode = cloneDeep(node); let newNode = cloneDeep(node);
if (newNode.data.node.template.separator) { if (newNode.data.node?.template.separator) {
newNode.data.node.template.separator.value = e; newNode.data.node.template.separator.value = e;
setNode(newNode.id, newNode); setNode(newNode.id, newNode);
} }
@ -53,6 +55,14 @@ export default function IOFieldView({
(flowPool[node!.id] ?? [])[(flowPool[node!.id]?.length ?? 1) - 1]?.data (flowPool[node!.id] ?? [])[(flowPool[node!.id]?.length ?? 1) - 1]?.data
.results.text ?? ""; .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() { function handleOutputType() {
if (!node) return <>"No node found!"</>; if (!node) return <>"No node found!"</>;
switch (type) { switch (type) {
@ -129,14 +139,10 @@ export default function IOFieldView({
return ( return (
<> <>
<InputListComponent <InputListComponent
id={`playground_${node.id}_input_list`}
editNode={false}
value={node.data.node!.template["input_value"]?.value} value={node.data.node!.template["input_value"]?.value}
onChange={(e) => { handleOnNewValue={handleOnNewValue}
if (node) {
let newNode = cloneDeep(node);
newNode.data.node!.template["input_value"].value = e;
setNode(node.id, newNode);
}
}}
disabled={false} disabled={false}
/> />
</> </>
@ -236,16 +242,11 @@ export default function IOFieldView({
return ( return (
<> <>
<InputListComponent <InputListComponent
id={`playground_${node.id}_output_list`}
editNode={false}
value={node.data.node!.template["input_value"]?.value} value={node.data.node!.template["input_value"]?.value}
onChange={(e) => { handleOnNewValue={handleOnNewValue}
if (node) { disabled={true}
let newNode = cloneDeep(node);
newNode.data.node!.template["input_value"].value = e;
setNode(node.id, newNode);
}
}}
playgroundDisabled
disabled={false}
/> />
</> </>
); );

View file

@ -10,36 +10,42 @@ import BaseModal from "../baseModal";
interface TableModalProps extends TableComponentProps { interface TableModalProps extends TableComponentProps {
tableTitle: string; tableTitle: string;
description: string; description: string;
disabled?: boolean;
children: React.ReactNode; children: React.ReactNode;
} }
const TableModal = forwardRef< const TableModal = forwardRef<
ElementRef<typeof TableComponent>, ElementRef<typeof TableComponent>,
TableModalProps TableModalProps
>(({ tableTitle, description, children, ...props }: TableModalProps, ref) => { >(
return ( (
<BaseModal> { tableTitle, description, children, disabled, ...props }: TableModalProps,
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger> ref,
<BaseModal.Header description={description}> ) => {
<span className="pr-2">{tableTitle}</span> return (
<ForwardedIconComponent name="Table" className="mr-2 h-4 w-4" /> <BaseModal disable={disabled}>
</BaseModal.Header> <BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
<BaseModal.Content> <BaseModal.Header description={description}>
<TableComponent <span className="pr-2">{tableTitle}</span>
className="h-full w-full" <ForwardedIconComponent name="Table" className="mr-2 h-4 w-4" />
ref={ref} </BaseModal.Header>
{...props} <BaseModal.Content>
></TableComponent> <TableComponent
</BaseModal.Content> className="h-full w-full"
<BaseModal.Footer> ref={ref}
<DialogClose> {...props}
<div className="flex w-full justify-end gap-2 pt-2"> ></TableComponent>
<Button>Close</Button> </BaseModal.Content>
</div> <BaseModal.Footer>
</DialogClose> <DialogClose>
</BaseModal.Footer> <div className="flex w-full justify-end gap-2 pt-2">
</BaseModal> <Button>Close</Button>
); </div>
}); </DialogClose>
</BaseModal.Footer>
</BaseModal>
);
},
);
export default TableModal; 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 useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class";
import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex"; import { usePostRetrieveVertexOrder } from "@/controllers/API/queries/vertex";
import useAddFlow from "@/hooks/flows/use-add-flow"; import useAddFlow from "@/hooks/flows/use-add-flow";
import CodeAreaModal from "@/modals/codeAreaModal";
import { APIClassType } from "@/types/api"; import { APIClassType } from "@/types/api";
import _, { cloneDeep } from "lodash"; import _, { cloneDeep } from "lodash";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useUpdateNodeInternals } from "reactflow"; import { useUpdateNodeInternals } from "reactflow";
import CodeAreaComponent from "../../../../components/codeAreaComponent";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent"; import ShadTooltip from "../../../../components/shadTooltipComponent";
import { import {
@ -638,22 +638,17 @@ export default function NodeToolbarComponent({
{hasCode && ( {hasCode && (
<div className="hidden"> <div className="hidden">
{openModal && ( {openModal && (
<CodeAreaComponent <CodeAreaModal
setValue={handleOnNewValue}
open={openModal} open={openModal}
setOpen={setOpenModal} setOpen={setOpenModal}
readonly={ dynamic={true}
data.node?.flow && data.node.template[name].dynamic
? true
: false
}
dynamic={data.node?.template[name].dynamic ?? false}
setNodeClass={handleNodeClass} setNodeClass={handleNodeClass}
nodeClass={data.node} nodeClass={data.node}
disabled={false}
value={data.node?.template[name].value ?? ""} value={data.node?.template[name].value ?? ""}
onChange={handleOnNewValue} >
id={"code-input-node-toolbar-" + name} <></>
/> </CodeAreaModal>
)} )}
</div> </div>
)} )}

View file

@ -42,15 +42,7 @@ export type InputComponentType = {
isObjectOption?: boolean; isObjectOption?: boolean;
onChangeFolderName?: (e: any) => void; onChangeFolderName?: (e: any) => void;
}; };
export type ToggleComponentType = { export type DropDownComponent = {
enabled: boolean;
setEnabled: (state: boolean) => void;
disabled?: boolean | undefined;
size: "small" | "medium" | "large";
id?: string;
editNode?: boolean;
};
export type DropDownComponentType = {
disabled?: boolean; disabled?: boolean;
isLoading?: boolean; isLoading?: boolean;
value: string; value: string;
@ -61,17 +53,6 @@ export type DropDownComponentType = {
id?: string; id?: string;
children?: ReactNode; 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 = { export type ParameterComponentType = {
selected?: boolean; selected?: boolean;
data: NodeDataType; data: NodeDataType;
@ -124,73 +105,12 @@ export type NodeInputFieldComponentType = {
showNode: boolean; showNode: boolean;
}; };
export type InputListComponentType = { export type IOJSONInputComponentType = {
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 = {
value: any; value: any;
onChange: (value) => void; onChange: (value) => void;
disabled?: boolean;
editNode?: boolean;
id?: string;
left?: boolean; left?: boolean;
output?: 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 = { export type outputComponentType = {
types: string[]; types: string[];
selected: string; selected: string;
@ -201,51 +121,6 @@ export type outputComponentType = {
proxy?: OutputFieldProxyType; 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 = { export type DisclosureComponentType = {
children: ReactNode; children: ReactNode;
defaultOpen: boolean; defaultOpen: boolean;
@ -278,15 +153,6 @@ export type IntComponentType = {
id?: string; 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 = { export type FilePreviewType = {
loading: boolean; loading: boolean;
file: File; file: File;
@ -881,11 +747,3 @@ export type handleSelectPropsType = {
setLockChat: (lock: boolean) => void; setLockChat: (lock: boolean) => void;
setChatHistory: (chatHistory: ChatMessageType) => 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 { expect, test } from "@playwright/test";
import uaParser from "ua-parser-js";
test("user must be able to interact with table input component", async ({ test("user must be able to interact with table input component", async ({
page, 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 secondRandomText = Math.random().toString(36).substring(7);
const thirdRandomText = 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) { while (modalCount === 0) {
await page.getByText("New Project", { exact: true }).click(); await page.getByText("New Project", { exact: true }).click();
await page.waitForTimeout(3000); await page.waitForTimeout(3000);
@ -99,7 +108,7 @@ class CustomComponent(Component):
return data return data
`; `;
await page.locator("textarea").press("Control+a"); await page.locator("textarea").press(`${control}+a`);
await page.locator("textarea").fill(customCodeWithError); await page.locator("textarea").fill(customCodeWithError);
await page.getByText("Check & Save").last().click(); await page.getByText("Check & Save").last().click();