fix zustand
This commit is contained in:
commit
f13d7ecab4
119 changed files with 2517 additions and 2077 deletions
787
src/frontend/package-lock.json
generated
787
src/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -37,6 +37,7 @@
|
|||
"base64-js": "^1.5.1",
|
||||
"class-variance-authority": "^0.6.1",
|
||||
"clsx": "^1.2.1",
|
||||
"cmdk": "^1.0.0",
|
||||
"dompurify": "^3.0.5",
|
||||
"esbuild": "^0.17.19",
|
||||
"framer-motion": "^11.0.6",
|
||||
|
|
|
|||
|
|
@ -15,11 +15,12 @@ import {
|
|||
FETCH_ERROR_MESSAGE,
|
||||
} from "./constants/constants";
|
||||
import { AuthContext } from "./contexts/authContext";
|
||||
import { getHealth } from "./controllers/API";
|
||||
import { getGlobalVariables, getHealth } from "./controllers/API";
|
||||
import Router from "./routes";
|
||||
import useAlertStore from "./stores/alertStore";
|
||||
import { useDarkStore } from "./stores/darkStore";
|
||||
import useFlowsManagerStore from "./stores/flowsManagerStore";
|
||||
import { useGlobalVariablesStore } from "./stores/globalVariables";
|
||||
import { useStoreStore } from "./stores/storeStore";
|
||||
import { useTypesStore } from "./stores/typesStore";
|
||||
|
||||
|
|
@ -43,6 +44,9 @@ export default function App() {
|
|||
const getTypes = useTypesStore((state) => state.getTypes);
|
||||
const refreshVersion = useDarkStore((state) => state.refreshVersion);
|
||||
const refreshStars = useDarkStore((state) => state.refreshStars);
|
||||
const setGlobalVariables = useGlobalVariablesStore(
|
||||
(state) => state.setGlobalVariables
|
||||
);
|
||||
const checkHasStore = useStoreStore((state) => state.checkHasStore);
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
|
@ -58,6 +62,9 @@ export default function App() {
|
|||
getTypes().then(() => {
|
||||
refreshFlows();
|
||||
});
|
||||
getGlobalVariables().then((res) => {
|
||||
setGlobalVariables(res);
|
||||
});
|
||||
checkHasStore();
|
||||
fetchApiData();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import CodeAreaComponent from "../../../../components/codeAreaComponent";
|
|||
import DictComponent from "../../../../components/dictComponent";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
import FloatComponent from "../../../../components/floatComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import InputComponent from "../../../../components/inputComponent";
|
||||
import { default as IconComponent } from "../../../../components/genericIconComponent";
|
||||
import InputFileComponent from "../../../../components/inputFileComponent";
|
||||
import InputGlobalComponent from "../../../../components/inputGlobalComponent";
|
||||
import InputListComponent from "../../../../components/inputListComponent";
|
||||
import IntComponent from "../../../../components/intComponent";
|
||||
import KeypairListComponent from "../../../../components/keypairListComponent";
|
||||
|
|
@ -71,6 +71,7 @@ export default function ParameterComponent({
|
|||
const nodes = useFlowStore((state) => state.nodes);
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const flow = currentFlow?.data?.nodes ?? null;
|
||||
|
||||
|
|
@ -400,9 +401,8 @@ export default function ParameterComponent({
|
|||
<>
|
||||
<div
|
||||
className={
|
||||
"w-full truncate text-sm" +
|
||||
(left ? "" : " flex items-center justify-end gap-2") +
|
||||
(info !== "" ? " flex items-center" : "")
|
||||
"flex w-full items-center truncate text-sm" +
|
||||
(left ? "" : " justify-end")
|
||||
}
|
||||
>
|
||||
{!left && data.node?.frozen && (
|
||||
|
|
@ -559,12 +559,21 @@ export default function ParameterComponent({
|
|||
(data.node?.template[name].refresh_button ? "w-5/6" : "")
|
||||
}
|
||||
>
|
||||
<InputComponent
|
||||
id={"input-" + name}
|
||||
<InputGlobalComponent
|
||||
disabled={disabled}
|
||||
password={data.node?.template[name].password ?? false}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
onChange={handleOnNewValue}
|
||||
setDb={(value) => {
|
||||
setNode(data.id, (oldNode) => {
|
||||
let newNode = cloneDeep(oldNode);
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
newNode.data.node.template[name].load_from_db = value;
|
||||
return newNode;
|
||||
});
|
||||
}}
|
||||
name={name}
|
||||
data={data}
|
||||
/>
|
||||
</div>
|
||||
{data.node?.template[name].refresh_button && (
|
||||
|
|
|
|||
|
|
@ -182,9 +182,16 @@ export default function IOView({
|
|||
<AccordionComponent
|
||||
trigger={
|
||||
<div className="file-component-badge-div">
|
||||
<Badge variant="gray" size="md">
|
||||
{node.data.node.display_name}
|
||||
</Badge>
|
||||
<ShadTooltip
|
||||
content={input.id}
|
||||
styleClasses="z-50"
|
||||
>
|
||||
<div>
|
||||
<Badge variant="gray" size="md">
|
||||
{node.data.node.display_name}
|
||||
</Badge>
|
||||
</div>
|
||||
</ShadTooltip>
|
||||
<div
|
||||
className="-mb-1 pr-4"
|
||||
onClick={(event) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
import { useState } from "react";
|
||||
import { registerGlobalVariable } from "../../controllers/API";
|
||||
import BaseModal from "../../modals/baseModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import InputComponent from "../inputComponent";
|
||||
import { Button } from "../ui/button";
|
||||
import { Input } from "../ui/input";
|
||||
import { Label } from "../ui/label";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
||||
//TODO IMPLEMENT FORM LOGIC
|
||||
|
||||
export default function AddNewVariableButton({ children }): JSX.Element {
|
||||
const [key, setKey] = useState("");
|
||||
const [value, setValue] = useState("");
|
||||
const [type, setType] = useState("");
|
||||
const [open, setOpen] = useState(false);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const addGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.addGlobalVariable
|
||||
);
|
||||
function handleSaveVariable() {
|
||||
let data: { name: string; value: string; type?: string } = {
|
||||
name: key,
|
||||
type,
|
||||
value,
|
||||
};
|
||||
registerGlobalVariable(data)
|
||||
.then((res) => {
|
||||
const { name, id, type } = res.data;
|
||||
addGlobalVariable(name, id, type);
|
||||
setKey("");
|
||||
setValue("");
|
||||
setType("");
|
||||
setOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
let responseError = error as ResponseErrorDetailAPI;
|
||||
setErrorData({
|
||||
title: "Error creating variable",
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
});
|
||||
});
|
||||
}
|
||||
return (
|
||||
<BaseModal open={open} setOpen={setOpen} size="x-small">
|
||||
<BaseModal.Header
|
||||
description={
|
||||
"This variable will be encrypted and will be available for you to use in any of your projects."
|
||||
}
|
||||
>
|
||||
<span className="pr-2"> Create Variable </span>
|
||||
<ForwardedIconComponent
|
||||
name="Globe"
|
||||
className="h-6 w-6 pl-1 text-primary "
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</BaseModal.Header>
|
||||
<BaseModal.Trigger>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Content>
|
||||
<div className="flex h-full w-full flex-col gap-4 align-middle">
|
||||
<Label>Variable Name</Label>
|
||||
<Input
|
||||
value={key}
|
||||
onChange={(e) => {
|
||||
setKey(e.target.value);
|
||||
}}
|
||||
placeholder="Insert a name for the variable..."
|
||||
></Input>
|
||||
<Label>Type (optional)</Label>
|
||||
<InputComponent
|
||||
setSelectedOption={(e) => {
|
||||
setType(e);
|
||||
}}
|
||||
selectedOption={type}
|
||||
password={false}
|
||||
options={["Variable", "Credential"]}
|
||||
placeholder="Choose a type for the variable..."
|
||||
></InputComponent>
|
||||
<Label>Value</Label>
|
||||
<Textarea
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
placeholder="Insert a value for the variable..."
|
||||
className="w-full resize-none custom-scroll"
|
||||
/>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
<BaseModal.Footer>
|
||||
<Button onClick={handleSaveVariable}>Save variable</Button>
|
||||
</BaseModal.Footer>
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
|
|
@ -250,7 +250,7 @@ export default function CollectionCardComponent({
|
|||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
|
|
@ -275,7 +275,7 @@ export default function CollectionCardComponent({
|
|||
<Button
|
||||
disabled={loadingLike}
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "")
|
||||
|
|
@ -312,7 +312,7 @@ export default function CollectionCardComponent({
|
|||
<Button
|
||||
disabled={loading}
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
size="icon"
|
||||
className={
|
||||
"whitespace-nowrap" +
|
||||
(!authorized ? " cursor-not-allowed" : "") +
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import AccordionComponent from "../../components/AccordionComponent";
|
|||
import CodeAreaComponent from "../../components/codeAreaComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
import FloatComponent from "../../components/floatComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import InputFileComponent from "../../components/inputFileComponent";
|
||||
import InputListComponent from "../../components/inputListComponent";
|
||||
import IntComponent from "../../components/intComponent";
|
||||
|
|
@ -37,8 +36,10 @@ import {
|
|||
hasDuplicateKeys,
|
||||
} from "../../utils/reactflowUtils";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import ShadTooltip from "../ShadTooltipComponent";
|
||||
import DictComponent from "../dictComponent";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import InputGlobalComponent from "../inputGlobalComponent";
|
||||
import KeypairListComponent from "../keypairListComponent";
|
||||
|
||||
export default function CodeTabsComponent({
|
||||
|
|
@ -54,6 +55,7 @@ export default function CodeTabsComponent({
|
|||
const [openAccordion, setOpenAccordion] = useState<string[]>([]);
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
const unselectAll = useFlowStore((state) => state.unselectAll);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
|
||||
const [errorDuplicateKey, setErrorDuplicateKey] = useState(false);
|
||||
|
||||
|
|
@ -215,14 +217,22 @@ export default function CodeTabsComponent({
|
|||
node["data"]["id"]
|
||||
) && (
|
||||
<AccordionComponent
|
||||
trigger={node["data"]["id"]}
|
||||
trigger={
|
||||
<ShadTooltip
|
||||
side="top"
|
||||
styleClasses="z-50"
|
||||
content={node["data"]["id"]}
|
||||
>
|
||||
<div>{node["data"]["node"]["display_name"]}</div>
|
||||
</ShadTooltip>
|
||||
}
|
||||
open={openAccordion}
|
||||
keyValue={node["data"]["id"]}
|
||||
>
|
||||
<div className="api-modal-table-arrangement">
|
||||
<Table className="table-fixed bg-muted outline-1">
|
||||
<TableHeader className="h-10 border-input text-xs font-medium text-ring">
|
||||
<TableRow className="dark:border-b-muted">
|
||||
<TableRow className="">
|
||||
<TableHead className="h-7 text-center">
|
||||
PARAM
|
||||
</TableHead>
|
||||
|
|
@ -245,10 +255,7 @@ export default function CodeTabsComponent({
|
|||
)
|
||||
.map((templateField, indx) => {
|
||||
return (
|
||||
<TableRow
|
||||
key={indx}
|
||||
className="h-10 dark:border-b-muted"
|
||||
>
|
||||
<TableRow key={indx} className="h-10">
|
||||
<TableCell className="p-0 text-center text-sm text-foreground">
|
||||
{templateField}
|
||||
</TableCell>
|
||||
|
|
@ -347,38 +354,31 @@ export default function CodeTabsComponent({
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<InputComponent
|
||||
<InputGlobalComponent
|
||||
editNode={true}
|
||||
disabled={false}
|
||||
password={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].password ?? false
|
||||
}
|
||||
value={
|
||||
!node.data.node.template[
|
||||
templateField
|
||||
].value ||
|
||||
node.data.node.template[
|
||||
templateField
|
||||
].value === ""
|
||||
? ""
|
||||
: node.data.node
|
||||
.template[
|
||||
templateField
|
||||
].value
|
||||
}
|
||||
onChange={(target) => {
|
||||
setData((old) => {
|
||||
let newInputList =
|
||||
cloneDeep(old);
|
||||
newInputList![
|
||||
i
|
||||
].data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
return newInputList;
|
||||
});
|
||||
if (node.data) {
|
||||
setNode(
|
||||
node.data.id,
|
||||
(oldNode) => {
|
||||
let newNode =
|
||||
cloneDeep(
|
||||
oldNode
|
||||
);
|
||||
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
|
||||
newNode.data.node.template[
|
||||
templateField
|
||||
].value = target;
|
||||
|
||||
return newNode;
|
||||
}
|
||||
);
|
||||
}
|
||||
tweaks.buildTweakObject!(
|
||||
node["data"]["id"],
|
||||
target,
|
||||
|
|
@ -387,6 +387,25 @@ export default function CodeTabsComponent({
|
|||
]
|
||||
);
|
||||
}}
|
||||
setDb={(value) => {
|
||||
setNode(
|
||||
node.data.id,
|
||||
(oldNode) => {
|
||||
let newNode =
|
||||
cloneDeep(oldNode);
|
||||
newNode.data = {
|
||||
...newNode.data,
|
||||
};
|
||||
newNode.data.node.template[
|
||||
templateField
|
||||
].load_from_db =
|
||||
value;
|
||||
return newNode;
|
||||
}
|
||||
);
|
||||
}}
|
||||
name={templateField}
|
||||
data={node.data}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -505,7 +524,6 @@ export default function CodeTabsComponent({
|
|||
<div className="mx-auto">
|
||||
<Dropdown
|
||||
editNode={true}
|
||||
apiModal={true}
|
||||
options={
|
||||
node.data.node.template[
|
||||
templateField
|
||||
|
|
|
|||
|
|
@ -1,8 +1,21 @@
|
|||
import { Listbox, Transition } from "@headlessui/react";
|
||||
import { Fragment, useEffect, useState } from "react";
|
||||
import { useRef, useState } from "react";
|
||||
import { DropDownComponentType } from "../../types/components";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { cn } from "../../utils/utils";
|
||||
import { default as ForwardedIconComponent } from "../genericIconComponent";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "../ui/command";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContentWithoutPortal,
|
||||
PopoverTrigger,
|
||||
} from "../ui/popover";
|
||||
|
||||
export default function Dropdown({
|
||||
disabled,
|
||||
|
|
@ -11,124 +24,77 @@ export default function Dropdown({
|
|||
options,
|
||||
onSelect,
|
||||
editNode = false,
|
||||
numberOfOptions = 0,
|
||||
apiModal = false,
|
||||
id = "",
|
||||
}: DropDownComponentType): JSX.Element {
|
||||
let [internalValue, setInternalValue] = useState(
|
||||
value === "" || !value ? "Choose an option" : value
|
||||
);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalValue(value === "" || !value ? "Choose an option" : value);
|
||||
}, [value]);
|
||||
const refButton = useRef<HTMLButtonElement>(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(options)?.length > 0 ? (
|
||||
<>
|
||||
<Listbox
|
||||
value={internalValue}
|
||||
disabled={disabled}
|
||||
onChange={(value) => {
|
||||
setInternalValue(value);
|
||||
onSelect(value);
|
||||
}}
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className={"relative mt-1"}>
|
||||
<Listbox.Button
|
||||
data-test={`${id ?? ""}`}
|
||||
className={
|
||||
editNode
|
||||
? "dropdown-component-outline"
|
||||
: "dropdown-component-false-outline"
|
||||
}
|
||||
>
|
||||
<span
|
||||
className="dropdown-component-display"
|
||||
data-testid={`${id ?? ""}-display`}
|
||||
>
|
||||
{internalValue}
|
||||
</span>
|
||||
<span className={"dropdown-component-arrow"}>
|
||||
<IconComponent
|
||||
name="ChevronsUpDown"
|
||||
className="dropdown-component-arrow-color"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
</Listbox.Button>
|
||||
|
||||
<Transition
|
||||
show={open}
|
||||
as={Fragment}
|
||||
leave="transition ease-in duration-100"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<Listbox.Options
|
||||
className={classNames(
|
||||
editNode
|
||||
? "dropdown-component-true-options nowheel custom-scroll"
|
||||
: "dropdown-component-false-options nowheel custom-scroll",
|
||||
apiModal ? "mb-2 w-[250px]" : "absolute w-full"
|
||||
)}
|
||||
>
|
||||
{options?.map((option, id) => (
|
||||
<Listbox.Option
|
||||
key={id}
|
||||
className={({ active }) =>
|
||||
classNames(
|
||||
active ? " bg-accent" : "",
|
||||
editNode
|
||||
? "dropdown-component-false-option"
|
||||
: "dropdown-component-true-option"
|
||||
)
|
||||
}
|
||||
value={option}
|
||||
>
|
||||
{({ selected, active }) => (
|
||||
<>
|
||||
<span
|
||||
className={classNames(
|
||||
selected ? "font-semibold" : "font-normal",
|
||||
"block truncate "
|
||||
)}
|
||||
data-testid={`${option}-${id ?? ""}-option`}
|
||||
>
|
||||
{option}
|
||||
</span>
|
||||
|
||||
{selected ? (
|
||||
<span
|
||||
className={classNames(
|
||||
active ? "text-background " : "",
|
||||
"dropdown-component-choosal"
|
||||
)}
|
||||
>
|
||||
<IconComponent
|
||||
name="Check"
|
||||
className={
|
||||
active
|
||||
? "dropdown-component-check-icon"
|
||||
: "dropdown-component-check-icon"
|
||||
}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
variant="primary"
|
||||
size="xs"
|
||||
role="combobox"
|
||||
ref={refButton}
|
||||
aria-expanded={open}
|
||||
data-test={`${id ?? ""}`}
|
||||
className={cn(
|
||||
editNode
|
||||
? "dropdown-component-outline"
|
||||
: "dropdown-component-false-outline",
|
||||
"w-full justify-between font-normal",
|
||||
editNode ? "input-edit-node" : "py-2"
|
||||
)}
|
||||
>
|
||||
{value
|
||||
? options.find((option) => option === value)
|
||||
: "Choose an option..."}
|
||||
<ForwardedIconComponent
|
||||
name="ChevronsUpDown"
|
||||
className="ml-2 h-4 w-4 shrink-0 opacity-50"
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContentWithoutPortal
|
||||
className="nocopy nowheel nopan nodelete nodrag noundo w-full p-0"
|
||||
style={{ minWidth: refButton?.current?.clientWidth ?? "200px" }}
|
||||
>
|
||||
<Command>
|
||||
<CommandInput placeholder="Search options..." className="h-9" />
|
||||
<CommandList>
|
||||
<CommandEmpty>No values found.</CommandEmpty>
|
||||
<CommandGroup defaultChecked={false}>
|
||||
{options?.map((option, id) => (
|
||||
<CommandItem
|
||||
key={id}
|
||||
value={option}
|
||||
onSelect={(currentValue) => {
|
||||
onSelect(currentValue);
|
||||
setOpen(false);
|
||||
}}
|
||||
data-testid={`${option}-${id ?? ""}-option`}
|
||||
>
|
||||
{option}
|
||||
<ForwardedIconComponent
|
||||
name="Check"
|
||||
className={cn(
|
||||
"ml-auto h-4 w-4 text-primary",
|
||||
value === option ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
</Listbox.Option>
|
||||
))}
|
||||
</Listbox.Options>
|
||||
</Transition>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Listbox>
|
||||
/>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContentWithoutPortal>
|
||||
</Popover>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,25 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { InputComponentType } from "../../types/components";
|
||||
import { handleKeyDown } from "../../utils/reactflowUtils";
|
||||
import { classNames } from "../../utils/utils";
|
||||
import { classNames, cn } from "../../utils/utils";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import {
|
||||
Command,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "../ui/command";
|
||||
import { Input } from "../ui/input";
|
||||
import { Popover, PopoverContentWithoutPortal } from "../ui/popover";
|
||||
|
||||
export default function InputComponent({
|
||||
autoFocus = false,
|
||||
onBlur,
|
||||
value,
|
||||
value = "",
|
||||
onChange,
|
||||
disabled,
|
||||
required = false,
|
||||
|
|
@ -20,17 +30,30 @@ export default function InputComponent({
|
|||
className,
|
||||
id = "",
|
||||
blurOnEnter = false,
|
||||
optionsIcon = "ChevronsUpDown",
|
||||
selectedOption,
|
||||
setSelectedOption,
|
||||
options = [],
|
||||
optionsPlaceholder = "Search options...",
|
||||
optionsButton,
|
||||
optionButton,
|
||||
}: InputComponentType): JSX.Element {
|
||||
const setErrorData = useAlertStore.getState().setErrorData;
|
||||
const [pwdVisible, setPwdVisible] = useState(false);
|
||||
const refInput = useRef<HTMLInputElement>(null);
|
||||
const [showOptions, setShowOptions] = useState<boolean>(false);
|
||||
|
||||
// Clear component state
|
||||
useEffect(() => {
|
||||
if (disabled && value !== "") {
|
||||
if (disabled && value && onChange && value !== "") {
|
||||
onChange("");
|
||||
}
|
||||
}, [disabled]);
|
||||
|
||||
function onInputLostFocus(event): void {
|
||||
if (onBlur) onBlur(event);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative w-full">
|
||||
{isForm ? (
|
||||
|
|
@ -38,7 +61,7 @@ export default function InputComponent({
|
|||
<Input
|
||||
id={"form-" + id}
|
||||
ref={refInput}
|
||||
onBlur={onBlur}
|
||||
onBlur={onInputLostFocus}
|
||||
autoFocus={autoFocus}
|
||||
type={password && !pwdVisible ? "password" : "text"}
|
||||
value={value}
|
||||
|
|
@ -55,7 +78,7 @@ export default function InputComponent({
|
|||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
onChange && onChange(e.target.value);
|
||||
}}
|
||||
onCopy={(e) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -67,54 +90,168 @@ export default function InputComponent({
|
|||
/>
|
||||
</Form.Control>
|
||||
) : (
|
||||
<Input
|
||||
id={id}
|
||||
ref={refInput}
|
||||
type="text"
|
||||
onBlur={onBlur}
|
||||
value={value}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
required={required}
|
||||
className={classNames(
|
||||
password && !pwdVisible && value !== ""
|
||||
? " text-clip password "
|
||||
: "",
|
||||
editNode ? " input-edit-node " : "",
|
||||
password && editNode ? "pr-8" : "",
|
||||
password && !editNode ? "pr-10" : "",
|
||||
className!
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={(e) => {
|
||||
// if the user copies a password from another input
|
||||
// it might come as ••••••••••• it causes errors
|
||||
// in ascii encoding, so we need to handle it
|
||||
if (password && e.target.value.length > 0) {
|
||||
// check if all chars are •
|
||||
if (e.target.value.split("").every((char) => char === "•")) {
|
||||
setErrorData({
|
||||
title: `Invalid characters: ${e.target.value}`,
|
||||
list: [
|
||||
"It seems you are trying to paste a password. Make sure the value is visible before copying from another field.",
|
||||
],
|
||||
});
|
||||
}
|
||||
<>
|
||||
<Popover modal open={showOptions} onOpenChange={setShowOptions}>
|
||||
<PopoverAnchor>
|
||||
<Input
|
||||
id={id}
|
||||
ref={refInput}
|
||||
type="text"
|
||||
onBlur={onInputLostFocus}
|
||||
value={
|
||||
selectedOption !== "" || !onChange ? selectedOption : value
|
||||
}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
(selectedOption !== "" || !onChange) && setShowOptions(true);
|
||||
}}
|
||||
required={required}
|
||||
className={classNames(
|
||||
password &&
|
||||
selectedOption === "" &&
|
||||
!pwdVisible &&
|
||||
value !== ""
|
||||
? " text-clip password "
|
||||
: "",
|
||||
editNode ? " input-edit-node " : "",
|
||||
password && selectedOption === "" && editNode ? "pr-8" : "",
|
||||
password && selectedOption === "" && !editNode ? "pr-10" : "",
|
||||
className!
|
||||
)}
|
||||
placeholder={password && editNode ? "Key" : placeholder}
|
||||
onChange={(e) => {
|
||||
// if the user copies a password from another input
|
||||
// it might come as ••••••••••• it causes errors
|
||||
// in ascii encoding, so we need to handle it
|
||||
if (password) {
|
||||
// check if all chars are •
|
||||
if (
|
||||
e.target.value.split("").every((char) => char === "•") &&
|
||||
e.target.value !== ""
|
||||
) {
|
||||
setErrorData({
|
||||
title: `Invalid characters: ${e.target.value}`,
|
||||
list: [
|
||||
"It seems you are trying to paste a password. Make sure the value is visible before copying from another field.",
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
onChange && onChange(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "");
|
||||
if (blurOnEnter && e.key === "Enter")
|
||||
refInput.current?.blur();
|
||||
}}
|
||||
data-testid={editNode ? id + "-edit" : id}
|
||||
/>
|
||||
</PopoverAnchor>
|
||||
<PopoverContentWithoutPortal
|
||||
className="nocopy nopan nodelete nodrag noundo p-0"
|
||||
style={{ minWidth: refInput?.current?.clientWidth ?? "200px" }}
|
||||
side="bottom"
|
||||
align="center"
|
||||
>
|
||||
<Command
|
||||
filter={(value, search) => {
|
||||
if (value.includes(search) || value.includes("doNotFilter-"))
|
||||
return 1; // ensures items arent filtered
|
||||
return 0;
|
||||
}}
|
||||
>
|
||||
<CommandInput placeholder={optionsPlaceholder} />
|
||||
<CommandList>
|
||||
<CommandGroup defaultChecked={false}>
|
||||
{options.map((option, id) => (
|
||||
<CommandItem
|
||||
className="group"
|
||||
key={option + id}
|
||||
value={option}
|
||||
onSelect={(currentValue) => {
|
||||
setSelectedOption &&
|
||||
setSelectedOption(
|
||||
currentValue === selectedOption
|
||||
? ""
|
||||
: currentValue
|
||||
);
|
||||
setShowOptions(false);
|
||||
}}
|
||||
>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<div className="flex items-center">
|
||||
<ForwardedIconComponent
|
||||
name="Check"
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4 text-primary",
|
||||
selectedOption === option
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{option}
|
||||
</div>
|
||||
{optionButton && optionButton(option)}
|
||||
</div>
|
||||
</CommandItem>
|
||||
))}
|
||||
{optionsButton && optionsButton}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContentWithoutPortal>
|
||||
</Popover>
|
||||
<div
|
||||
className={cn(
|
||||
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
|
||||
selectedOption !== "" || !onChange ? "" : "hidden"
|
||||
)}
|
||||
onClick={
|
||||
selectedOption !== "" || !onChange
|
||||
? (e) => {
|
||||
setShowOptions((old) => !old);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
: () => {}
|
||||
}
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
handleKeyDown(e, value, "");
|
||||
if (blurOnEnter && e.key === "Enter") refInput.current?.blur();
|
||||
}}
|
||||
data-testid={editNode ? id + "-edit" : id}
|
||||
/>
|
||||
></div>
|
||||
</>
|
||||
)}
|
||||
{password && (
|
||||
|
||||
<span
|
||||
className={cn(
|
||||
password && selectedOption === "" ? "right-8" : "right-0",
|
||||
"absolute inset-y-0 flex items-center pr-2.5"
|
||||
)}
|
||||
>
|
||||
<button
|
||||
onClick={() => {
|
||||
setShowOptions(!showOptions);
|
||||
}}
|
||||
className={cn(
|
||||
selectedOption !== ""
|
||||
? "text-medium-indigo"
|
||||
: "text-muted-foreground",
|
||||
"hover:text-accent-foreground"
|
||||
)}
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name={optionsIcon}
|
||||
className={"h-4 w-4"}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</span>
|
||||
|
||||
{password && selectedOption === "" && (
|
||||
<button
|
||||
type="button"
|
||||
tabIndex={-1}
|
||||
className={classNames(
|
||||
"mb-px",
|
||||
editNode
|
||||
? "input-component-true-button"
|
||||
: "input-component-false-button"
|
||||
|
|
@ -125,6 +262,7 @@ export default function InputComponent({
|
|||
}}
|
||||
>
|
||||
{password &&
|
||||
selectedOption === "" &&
|
||||
(pwdVisible ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
|
|||
134
src/frontend/src/components/inputGlobalComponent/index.tsx
Normal file
134
src/frontend/src/components/inputGlobalComponent/index.tsx
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
import { useEffect } from "react";
|
||||
import { deleteGlobalVariable } from "../../controllers/API";
|
||||
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||
import { InputGlobalComponentType } from "../../types/components";
|
||||
import { cn } from "../../utils/utils";
|
||||
import AddNewVariableButton from "../addNewVariableButtonComponent/addNewVariableButton";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import InputComponent from "../inputComponent";
|
||||
import { CommandItem } from "../ui/command";
|
||||
|
||||
export default function InputGlobalComponent({
|
||||
disabled,
|
||||
onChange,
|
||||
setDb,
|
||||
name,
|
||||
data,
|
||||
editNode = false,
|
||||
}: InputGlobalComponentType): JSX.Element {
|
||||
const globalVariablesEntries = useGlobalVariablesStore(
|
||||
(state) => state.globalVariablesEntries
|
||||
);
|
||||
|
||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||
const removeGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.removeGlobalVariable
|
||||
);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
useEffect(() => {
|
||||
if (data.node?.template[name])
|
||||
if (
|
||||
!globalVariablesEntries.includes(data.node?.template[name].value) &&
|
||||
data.node?.template[name].load_from_db
|
||||
) {
|
||||
onChange("");
|
||||
setDb(false);
|
||||
}
|
||||
}, [globalVariablesEntries]);
|
||||
|
||||
function handleDelete(key: string) {
|
||||
const id = getVariableId(key);
|
||||
if (id !== undefined) {
|
||||
deleteGlobalVariable(id)
|
||||
.then((_) => {
|
||||
removeGlobalVariable(key);
|
||||
if (
|
||||
data?.node?.template[name].value === key &&
|
||||
data?.node?.template[name].load_from_db
|
||||
) {
|
||||
onChange("");
|
||||
setDb(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
let responseError = error as ResponseErrorDetailAPI;
|
||||
setErrorData({
|
||||
title: "Error deleting variable",
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
});
|
||||
});
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Error deleting variable",
|
||||
list: [cn("ID not found for variable: ", key)],
|
||||
});
|
||||
}
|
||||
}
|
||||
return (
|
||||
<InputComponent
|
||||
id={"input-" + name}
|
||||
editNode={editNode}
|
||||
disabled={disabled}
|
||||
password={data.node?.template[name].password ?? false}
|
||||
value={data.node?.template[name].value ?? ""}
|
||||
options={globalVariablesEntries}
|
||||
optionsPlaceholder={"Global Variables"}
|
||||
optionsIcon="Globe"
|
||||
optionsButton={
|
||||
<AddNewVariableButton>
|
||||
<CommandItem value="doNotFilter-addNewVariable">
|
||||
<ForwardedIconComponent
|
||||
name="Plus"
|
||||
className={cn("mr-2 h-4 w-4 text-primary")}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>Add New Variable</span>
|
||||
</CommandItem>
|
||||
</AddNewVariableButton>
|
||||
}
|
||||
optionButton={(option) => (
|
||||
<DeleteConfirmationModal
|
||||
onConfirm={(e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
handleDelete(option);
|
||||
}}
|
||||
description={'variable "' + option + '"'}
|
||||
asChild
|
||||
>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
className="pr-1"
|
||||
>
|
||||
<ForwardedIconComponent
|
||||
name="Trash2"
|
||||
className={cn(
|
||||
"h-4 w-4 text-primary opacity-0 hover:text-status-red group-hover:opacity-100"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</DeleteConfirmationModal>
|
||||
)}
|
||||
selectedOption={
|
||||
data?.node?.template[name].load_from_db ?? false
|
||||
? data?.node?.template[name].value
|
||||
: ""
|
||||
}
|
||||
setSelectedOption={(value) => {
|
||||
onChange(value);
|
||||
setDb(value !== "" ? true : false);
|
||||
}}
|
||||
onChange={(value) => {
|
||||
onChange(value);
|
||||
setDb(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
@ -23,8 +23,9 @@ const buttonVariants = cva(
|
|||
size: {
|
||||
default: "h-10 py-2 px-4",
|
||||
sm: "h-9 px-3 rounded-md",
|
||||
xs: "py-1 px-1 rounded-md",
|
||||
xs: "py-0.5 px-3 rounded-md",
|
||||
lg: "h-11 px-8 rounded-md",
|
||||
icon: "py-1 px-1 rounded-md",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ const CommandItem = React.forwardRef<
|
|||
<CommandPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,21 @@ const PopoverContent = React.forwardRef<
|
|||
));
|
||||
PopoverContent.displayName = PopoverPrimitive.Content.displayName;
|
||||
|
||||
export { Popover, PopoverContent, PopoverTrigger };
|
||||
const PopoverContentWithoutPortal = React.forwardRef<
|
||||
React.ElementRef<typeof PopoverPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
||||
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
||||
<PopoverPrimitive.Content
|
||||
ref={ref}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
PopoverContentWithoutPortal.displayName = PopoverPrimitive.Content.displayName;
|
||||
|
||||
export { Popover, PopoverContent, PopoverContentWithoutPortal, PopoverTrigger };
|
||||
|
|
|
|||
|
|
@ -859,6 +859,50 @@ export async function requestLogout() {
|
|||
}
|
||||
}
|
||||
|
||||
export async function getGlobalVariables(): Promise<{
|
||||
[key: string]: { id: string; type: string };
|
||||
}> {
|
||||
const globalVariables = {};
|
||||
(await api.get(`${BASE_URL_API}variables/`)).data.forEach((element) => {
|
||||
globalVariables[element.name] = {
|
||||
id: element.id,
|
||||
type: element.type,
|
||||
};
|
||||
});
|
||||
return globalVariables;
|
||||
}
|
||||
|
||||
export async function registerGlobalVariable({
|
||||
name,
|
||||
value,
|
||||
type,
|
||||
}: {
|
||||
name: string;
|
||||
value: string;
|
||||
type?: string;
|
||||
}): Promise<AxiosResponse<{ name: string; id: string; type: string }>> {
|
||||
return await api.post(`${BASE_URL_API}variables/`, {
|
||||
name,
|
||||
value,
|
||||
type,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteGlobalVariable(id: string) {
|
||||
api.delete(`${BASE_URL_API}variables/${id}`);
|
||||
}
|
||||
|
||||
export async function updateGlobalVariable(
|
||||
name: string,
|
||||
value: string,
|
||||
id: string
|
||||
) {
|
||||
api.patch(`${BASE_URL_API}variables/${id}`, {
|
||||
name,
|
||||
value,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getVerticesOrder(
|
||||
flowId: string,
|
||||
startNodeId?: string | null,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,28 @@
|
|||
const AstraSVG = (props) => (
|
||||
<svg width="96" height="96" viewBox="12 33 72 29" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
|
||||
<g clip-path="url(#clip0_702_1449)">
|
||||
{/* <rect width="96" height="96" rx="6" fill="white"/> */}
|
||||
<path d="M38.0469 33H12V62.1892H38.0469L44.5902 57.1406V38.0485L38.0469 33ZM17.0478 38.0485H39.5424V57.1459H17.0478V38.0485Z" fill="black"/>
|
||||
<path d="M82.0705 38.2605V33.3243H58.2546L51.788 38.2605V45.038L58.2546 49.9742H79.0107V56.9286H53.076V61.8648H77.5334L84 56.9286V49.9742L77.5334 45.038H56.7772V38.2605H82.0705Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_702_1449">
|
||||
<rect width="96" height="96" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
width="96"
|
||||
height="96"
|
||||
viewBox="12 33 72 29"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<g clip-path="url(#clip0_702_1449)">
|
||||
{/* <rect width="96" height="96" rx="6" fill="white"/> */}
|
||||
<path
|
||||
d="M38.0469 33H12V62.1892H38.0469L44.5902 57.1406V38.0485L38.0469 33ZM17.0478 38.0485H39.5424V57.1459H17.0478V38.0485Z"
|
||||
fill="black"
|
||||
/>
|
||||
<path
|
||||
d="M82.0705 38.2605V33.3243H58.2546L51.788 38.2605V45.038L58.2546 49.9742H79.0107V56.9286H53.076V61.8648H77.5334L84 56.9286V49.9742L77.5334 45.038H56.7772V38.2605H82.0705Z"
|
||||
fill="black"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_702_1449">
|
||||
<rect width="96" height="96" fill="white" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
export default AstraSVG;
|
||||
|
|
|
|||
|
|
@ -225,7 +225,7 @@ const ApiModal = forwardRef(
|
|||
<BaseModal open={open} setOpen={setOpen}>
|
||||
<BaseModal.Trigger asChild>{children}</BaseModal.Trigger>
|
||||
<BaseModal.Header description={EXPORT_CODE_DIALOG}>
|
||||
<span className="pr-2">Code</span>
|
||||
<span className="pr-2">API</span>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className="h-6 w-6 pl-1 text-gray-800 dark:text-white"
|
||||
|
|
|
|||
|
|
@ -14,14 +14,18 @@ export default function DeleteConfirmationModal({
|
|||
children,
|
||||
onConfirm,
|
||||
description,
|
||||
asChild,
|
||||
}: {
|
||||
children: JSX.Element;
|
||||
onConfirm: () => void;
|
||||
onConfirm: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
|
||||
description?: string;
|
||||
asChild?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger tabIndex={-1}>{children}</DialogTrigger>
|
||||
<DialogTrigger asChild={asChild} tabIndex={-1}>
|
||||
{children}
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
|
|
@ -39,16 +43,21 @@ export default function DeleteConfirmationModal({
|
|||
Note: This action is irreversible.
|
||||
</span>
|
||||
<DialogFooter>
|
||||
<DialogClose>
|
||||
<Button className="mr-3" variant="outline">
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="mr-3"
|
||||
variant="outline"
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
</DialogClose>
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="destructive"
|
||||
onClick={() => {
|
||||
onConfirm();
|
||||
onClick={(e) => {
|
||||
onConfirm(e);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import DictComponent from "../../components/dictComponent";
|
|||
import Dropdown from "../../components/dropdownComponent";
|
||||
import FloatComponent from "../../components/floatComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import InputComponent from "../../components/inputComponent";
|
||||
import InputFileComponent from "../../components/inputFileComponent";
|
||||
import InputGlobalComponent from "../../components/inputGlobalComponent";
|
||||
import InputListComponent from "../../components/inputListComponent";
|
||||
import IntComponent from "../../components/intComponent";
|
||||
import KeypairListComponent from "../../components/keypairListComponent";
|
||||
|
|
@ -28,7 +28,9 @@ import {
|
|||
LANGFLOW_SUPPORTED_TYPES,
|
||||
limitScrollFieldsModal,
|
||||
} from "../../constants/constants";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import useFlowStore from "../../stores/flowStore";
|
||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||
import { NodeDataType } from "../../types/flow";
|
||||
import {
|
||||
convertObjToArray,
|
||||
|
|
@ -58,6 +60,10 @@ const EditNodeModal = forwardRef(
|
|||
|
||||
const edges = useFlowStore((state) => state.edges);
|
||||
const setNode = useFlowStore((state) => state.setNode);
|
||||
const setNoticeData = useAlertStore((state) => state.setNoticeData);
|
||||
const globalVariablesEntries = useGlobalVariablesStore(
|
||||
(state) => state.globalVariablesEntries
|
||||
);
|
||||
|
||||
function changeAdvanced(n) {
|
||||
setMyData((old) => {
|
||||
|
|
@ -254,28 +260,23 @@ const EditNodeModal = forwardRef(
|
|||
}}
|
||||
/>
|
||||
) : (
|
||||
<InputComponent
|
||||
id={
|
||||
"input-" +
|
||||
myData.node.template[templateParam]
|
||||
.name
|
||||
}
|
||||
editNode={true}
|
||||
<InputGlobalComponent
|
||||
disabled={disabled}
|
||||
password={
|
||||
myData.node.template[templateParam]
|
||||
.password ?? false
|
||||
editNode={true}
|
||||
onChange={(value) =>
|
||||
handleOnNewValue(value, templateParam)
|
||||
}
|
||||
value={
|
||||
myData.node.template[templateParam]
|
||||
.value ?? ""
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleOnNewValue(
|
||||
value,
|
||||
templateParam
|
||||
);
|
||||
setDb={(value) => {
|
||||
setMyData((oldData) => {
|
||||
let newData = cloneDeep(oldData);
|
||||
newData.node!.template[
|
||||
templateParam
|
||||
].load_from_db = value;
|
||||
return newData;
|
||||
});
|
||||
}}
|
||||
name={templateParam}
|
||||
data={myData}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -403,7 +404,6 @@ const EditNodeModal = forwardRef(
|
|||
.options ? (
|
||||
<div className="mx-auto">
|
||||
<Dropdown
|
||||
numberOfOptions={nodeLength}
|
||||
editNode={true}
|
||||
options={
|
||||
myData.node.template[templateParam]
|
||||
|
|
|
|||
|
|
@ -617,12 +617,14 @@ export default function NodeToolbarComponent({
|
|||
open={showModalAdvanced}
|
||||
setOpen={setShowModalAdvanced}
|
||||
/>
|
||||
{showconfirmShare&&<ShareModal
|
||||
open={showconfirmShare}
|
||||
setOpen={setShowconfirmShare}
|
||||
is_component={true}
|
||||
component={flowComponent!}
|
||||
/>}
|
||||
{showconfirmShare && (
|
||||
<ShareModal
|
||||
open={showconfirmShare}
|
||||
setOpen={setShowconfirmShare}
|
||||
is_component={true}
|
||||
component={flowComponent!}
|
||||
/>
|
||||
)}
|
||||
{hasCode && (
|
||||
<div className="hidden">
|
||||
<CodeAreaComponent
|
||||
|
|
|
|||
|
|
@ -505,6 +505,17 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
);
|
||||
|
||||
useFlowStore.getState().updateBuildStatus([vertexBuildData.id], status);
|
||||
|
||||
const verticesIds = get().verticesBuild?.verticesIds;
|
||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||
// filter out the vertices that are not status
|
||||
const verticesToUpdate = verticesIds?.filter(
|
||||
(id) => newFlowBuildStatus[id]?.status !== BuildStatus.BUILT
|
||||
);
|
||||
|
||||
if (verticesToUpdate) {
|
||||
useFlowStore.getState().updateBuildStatus(verticesToUpdate, status);
|
||||
}
|
||||
}
|
||||
await buildVertices({
|
||||
input_value,
|
||||
|
|
@ -591,7 +602,6 @@ const useFlowStore = create<FlowStoreType>((set, get) => ({
|
|||
},
|
||||
updateBuildStatus: (nodeIdList: string[], status: BuildStatus) => {
|
||||
const newFlowBuildStatus = { ...get().flowBuildStatus };
|
||||
|
||||
nodeIdList.forEach((id) => {
|
||||
newFlowBuildStatus[id] = {
|
||||
status,
|
||||
|
|
|
|||
34
src/frontend/src/stores/globalVariables.ts
Normal file
34
src/frontend/src/stores/globalVariables.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { create } from "zustand";
|
||||
import { GlobalVariablesStore } from "../types/zustand/globalVariables";
|
||||
|
||||
export const useGlobalVariablesStore = create<GlobalVariablesStore>(
|
||||
(set, get) => ({
|
||||
globalVariablesEntries: [],
|
||||
globalVariables: {},
|
||||
setGlobalVariables: (variables) => {
|
||||
set({
|
||||
globalVariables: variables,
|
||||
globalVariablesEntries: Object.keys(variables),
|
||||
});
|
||||
},
|
||||
addGlobalVariable: (name, id, type) => {
|
||||
const data = { id, type };
|
||||
const newVariables = { ...get().globalVariables, [name]: data };
|
||||
set({
|
||||
globalVariables: newVariables,
|
||||
globalVariablesEntries: Object.keys(newVariables),
|
||||
});
|
||||
},
|
||||
removeGlobalVariable: (name) => {
|
||||
const newVariables = { ...get().globalVariables };
|
||||
delete newVariables[name];
|
||||
set({
|
||||
globalVariables: newVariables,
|
||||
globalVariablesEntries: Object.keys(newVariables),
|
||||
});
|
||||
},
|
||||
getVariableId: (name) => {
|
||||
return get().globalVariables[name]?.id;
|
||||
},
|
||||
})
|
||||
);
|
||||
|
|
@ -450,7 +450,7 @@
|
|||
@apply text-muted-foreground;
|
||||
}
|
||||
.dropdown-component-arrow {
|
||||
@apply pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2;
|
||||
@apply pointer-events-none absolute inset-y-0 flex items-center pr-2;
|
||||
}
|
||||
.dropdown-component-arrow-color {
|
||||
@apply h-5 w-5 text-accent-foreground;
|
||||
|
|
@ -480,7 +480,7 @@
|
|||
@apply absolute inset-y-0 right-0 flex items-center pr-4;
|
||||
}
|
||||
.dropdown-component-check-icon {
|
||||
@apply h-5 w-5 text-black;
|
||||
@apply h-5 w-5 text-primary;
|
||||
}
|
||||
|
||||
.edit-flow-arrangement {
|
||||
|
|
|
|||
|
|
@ -177,3 +177,6 @@ export type CodeErrorDataTypeAPI = {
|
|||
export type ResponseErrorTypeAPI = {
|
||||
response: { data: { detail: CodeErrorDataTypeAPI } };
|
||||
};
|
||||
export type ResponseErrorDetailAPI = {
|
||||
response: { data: { detail: string } };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import { sourceHandleType, targetHandleType } from "./../flow/index";
|
|||
export type InputComponentType = {
|
||||
autoFocus?: boolean;
|
||||
onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
|
||||
value: string;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
onChange: (value: string) => void;
|
||||
onChange?: (value: string) => void;
|
||||
password: boolean;
|
||||
required?: boolean;
|
||||
isForm?: boolean;
|
||||
|
|
@ -20,6 +20,13 @@ export type InputComponentType = {
|
|||
className?: string;
|
||||
id?: string;
|
||||
blurOnEnter?: boolean;
|
||||
optionsIcon?: string;
|
||||
optionsPlaceholder?: string;
|
||||
options?: string[];
|
||||
optionsButton?: ReactElement;
|
||||
optionButton?: (option: string) => ReactElement;
|
||||
selectedOption?: string;
|
||||
setSelectedOption?: (value: string) => void;
|
||||
};
|
||||
export type ToggleComponentType = {
|
||||
enabled: boolean;
|
||||
|
|
@ -36,8 +43,6 @@ export type DropDownComponentType = {
|
|||
options: string[];
|
||||
onSelect: (value: string) => void;
|
||||
editNode?: boolean;
|
||||
apiModal?: boolean;
|
||||
numberOfOptions?: number;
|
||||
id?: string;
|
||||
};
|
||||
export type ParameterComponentType = {
|
||||
|
|
@ -65,6 +70,15 @@ export type InputListComponentType = {
|
|||
componentName?: string;
|
||||
};
|
||||
|
||||
export type InputGlobalComponentType = {
|
||||
disabled: boolean;
|
||||
onChange: (value: string) => void;
|
||||
setDb: (value: boolean) => void;
|
||||
name: string;
|
||||
data: NodeDataType;
|
||||
editNode?: boolean;
|
||||
};
|
||||
|
||||
export type KeyPairListComponentType = {
|
||||
value: any;
|
||||
onChange: (value: Object[]) => void;
|
||||
|
|
|
|||
10
src/frontend/src/types/zustand/globalVariables/index.ts
Normal file
10
src/frontend/src/types/zustand/globalVariables/index.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export type GlobalVariablesStore = {
|
||||
globalVariablesEntries: Array<string>;
|
||||
globalVariables: { [name: string]: { id: string; type?: string } };
|
||||
setGlobalVariables: (variables: {
|
||||
[name: string]: { id: string; type?: string };
|
||||
}) => void;
|
||||
addGlobalVariable: (name: string, id: string, type?: string) => void;
|
||||
removeGlobalVariable: (name: string) => void;
|
||||
getVariableId: (name: string) => string | undefined;
|
||||
};
|
||||
|
|
@ -58,6 +58,7 @@ import {
|
|||
GitBranchPlus,
|
||||
GitFork,
|
||||
GithubIcon,
|
||||
Globe,
|
||||
Group,
|
||||
Hammer,
|
||||
Heart,
|
||||
|
|
@ -418,6 +419,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
PlusCircle,
|
||||
PlusSquare,
|
||||
Code2,
|
||||
Globe,
|
||||
Variable,
|
||||
Snowflake,
|
||||
Store,
|
||||
|
|
|
|||
|
|
@ -305,10 +305,7 @@
|
|||
"_type": "initialize_agent"
|
||||
},
|
||||
"description": "Construct a zero shot agent from an LLM and tools.",
|
||||
"base_classes": [
|
||||
"AgentExecutor",
|
||||
"function"
|
||||
],
|
||||
"base_classes": ["AgentExecutor", "function"],
|
||||
"display_name": "AgentInitializer"
|
||||
},
|
||||
"id": "AgentInitializer-QiQ4x",
|
||||
|
|
@ -372,9 +369,7 @@
|
|||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": [
|
||||
"Tool"
|
||||
],
|
||||
"base_classes": ["Tool"],
|
||||
"display_name": "PythonFunctionTool"
|
||||
},
|
||||
"id": "PythonFunctionTool-kX99N",
|
||||
|
|
@ -434,9 +429,7 @@
|
|||
"selected": false,
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Tool"
|
||||
],
|
||||
"baseClasses": ["Tool"],
|
||||
"dataType": "PythonFunctionTool",
|
||||
"id": "PythonFunctionTool-kX99N"
|
||||
},
|
||||
|
|
@ -822,10 +815,7 @@
|
|||
"_type": "initialize_agent"
|
||||
},
|
||||
"description": "Construct a zero shot agent from an LLM and tools.",
|
||||
"base_classes": [
|
||||
"AgentExecutor",
|
||||
"function"
|
||||
],
|
||||
"base_classes": ["AgentExecutor", "function"],
|
||||
"display_name": "AgentInitializer"
|
||||
},
|
||||
"id": "AgentInitializer-YJgqs",
|
||||
|
|
@ -889,9 +879,7 @@
|
|||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": [
|
||||
"Tool"
|
||||
],
|
||||
"base_classes": ["Tool"],
|
||||
"display_name": "PythonFunctionTool"
|
||||
},
|
||||
"id": "PythonFunctionTool-gqQDg",
|
||||
|
|
@ -951,9 +939,7 @@
|
|||
"selected": false,
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Tool"
|
||||
],
|
||||
"baseClasses": ["Tool"],
|
||||
"dataType": "PythonFunctionTool",
|
||||
"id": "PythonFunctionTool-gqQDg"
|
||||
},
|
||||
|
|
@ -1339,10 +1325,7 @@
|
|||
"_type": "initialize_agent"
|
||||
},
|
||||
"description": "Construct a zero shot agent from an LLM and tools.",
|
||||
"base_classes": [
|
||||
"AgentExecutor",
|
||||
"function"
|
||||
],
|
||||
"base_classes": ["AgentExecutor", "function"],
|
||||
"display_name": "AgentInitializer"
|
||||
},
|
||||
"id": "AgentInitializer-grV0u",
|
||||
|
|
@ -1406,9 +1389,7 @@
|
|||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": [
|
||||
"Tool"
|
||||
],
|
||||
"base_classes": ["Tool"],
|
||||
"display_name": "PythonFunctionTool"
|
||||
},
|
||||
"id": "PythonFunctionTool-SctM2",
|
||||
|
|
@ -1468,9 +1449,7 @@
|
|||
"selected": false,
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Tool"
|
||||
],
|
||||
"baseClasses": ["Tool"],
|
||||
"dataType": "PythonFunctionTool",
|
||||
"id": "PythonFunctionTool-SctM2"
|
||||
},
|
||||
|
|
@ -2210,10 +2189,7 @@
|
|||
"_type": "initialize_agent"
|
||||
},
|
||||
"description": "Construct a zero shot agent from an LLM and tools.",
|
||||
"base_classes": [
|
||||
"AgentExecutor",
|
||||
"function"
|
||||
],
|
||||
"base_classes": ["AgentExecutor", "function"],
|
||||
"display_name": "AgentInitializer"
|
||||
},
|
||||
"id": "AgentInitializer-EE8R4",
|
||||
|
|
@ -2277,9 +2253,7 @@
|
|||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": [
|
||||
"Tool"
|
||||
],
|
||||
"base_classes": ["Tool"],
|
||||
"display_name": "PythonFunctionTool"
|
||||
},
|
||||
"id": "PythonFunctionTool-YKkDL",
|
||||
|
|
@ -2339,9 +2313,7 @@
|
|||
"selected": false,
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Tool"
|
||||
],
|
||||
"baseClasses": ["Tool"],
|
||||
"dataType": "PythonFunctionTool",
|
||||
"id": "PythonFunctionTool-YKkDL"
|
||||
},
|
||||
|
|
@ -2727,10 +2699,7 @@
|
|||
"_type": "initialize_agent"
|
||||
},
|
||||
"description": "Construct a zero shot agent from an LLM and tools.",
|
||||
"base_classes": [
|
||||
"AgentExecutor",
|
||||
"function"
|
||||
],
|
||||
"base_classes": ["AgentExecutor", "function"],
|
||||
"display_name": "AgentInitializer"
|
||||
},
|
||||
"id": "AgentInitializer-goPm2",
|
||||
|
|
@ -2794,9 +2763,7 @@
|
|||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": [
|
||||
"Tool"
|
||||
],
|
||||
"base_classes": ["Tool"],
|
||||
"display_name": "PythonFunctionTool"
|
||||
},
|
||||
"id": "PythonFunctionTool-SQikY",
|
||||
|
|
@ -2856,9 +2823,7 @@
|
|||
"selected": false,
|
||||
"data": {
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"Tool"
|
||||
],
|
||||
"baseClasses": ["Tool"],
|
||||
"dataType": "PythonFunctionTool",
|
||||
"id": "PythonFunctionTool-SQikY"
|
||||
},
|
||||
|
|
@ -2881,4 +2846,4 @@
|
|||
"user_id": "6f6ebc2c-9e7a-4c35-84fc-51760db10d9b"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@
|
|||
"dynamic": false,
|
||||
"info": "",
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"name": {
|
||||
"type": "str",
|
||||
|
|
@ -66,9 +64,7 @@
|
|||
"dynamic": false,
|
||||
"info": "",
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"return_direct": {
|
||||
"type": "bool",
|
||||
|
|
@ -90,10 +86,7 @@
|
|||
"_type": "PythonFunctionTool"
|
||||
},
|
||||
"description": "Python function to be executed.",
|
||||
"base_classes": [
|
||||
"BaseTool",
|
||||
"Tool"
|
||||
],
|
||||
"base_classes": ["BaseTool", "Tool"],
|
||||
"display_name": "PythonFunctionTool",
|
||||
"documentation": "",
|
||||
"custom_fields": {},
|
||||
|
|
@ -208,9 +201,7 @@
|
|||
"dynamic": false,
|
||||
"info": "",
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"code": {
|
||||
"type": "code",
|
||||
|
|
@ -269,10 +260,7 @@
|
|||
"max_iterations": null,
|
||||
"memory": null
|
||||
},
|
||||
"output_types": [
|
||||
"AgentExecutor",
|
||||
"Callable"
|
||||
],
|
||||
"output_types": ["AgentExecutor", "Callable"],
|
||||
"field_formatters": {},
|
||||
"pinned": false,
|
||||
"beta": true
|
||||
|
|
@ -372,9 +360,7 @@
|
|||
"dynamic": false,
|
||||
"info": "",
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_base": {
|
||||
"type": "str",
|
||||
|
|
@ -392,9 +378,7 @@
|
|||
"dynamic": false,
|
||||
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\n\nYou can change this to use other APIs like JinaChat, LocalAI and Prem.",
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"openai_api_key": {
|
||||
"type": "str",
|
||||
|
|
@ -412,9 +396,7 @@
|
|||
"dynamic": false,
|
||||
"info": "",
|
||||
"title_case": false,
|
||||
"input_types": [
|
||||
"Text"
|
||||
]
|
||||
"input_types": ["Text"]
|
||||
},
|
||||
"temperature": {
|
||||
"type": "float",
|
||||
|
|
@ -462,10 +444,7 @@
|
|||
"openai_api_key": null,
|
||||
"temperature": null
|
||||
},
|
||||
"output_types": [
|
||||
"BaseLanguageModel",
|
||||
"BaseLLM"
|
||||
],
|
||||
"output_types": ["BaseLanguageModel", "BaseLLM"],
|
||||
"field_formatters": {},
|
||||
"pinned": false,
|
||||
"beta": true
|
||||
|
|
@ -528,10 +507,7 @@
|
|||
"type": "Tool"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"BaseTool",
|
||||
"Tool"
|
||||
],
|
||||
"baseClasses": ["BaseTool", "Tool"],
|
||||
"dataType": "PythonFunctionTool",
|
||||
"id": "PythonFunctionTool-RfJui"
|
||||
}
|
||||
|
|
@ -553,4 +529,4 @@
|
|||
"name": "Untitled document (20)",
|
||||
"last_tested_version": "0.7.0a0",
|
||||
"is_component": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,27 +62,21 @@
|
|||
"name": "text_input",
|
||||
"display_name": "Text Input",
|
||||
"advanced": false,
|
||||
"input_types": [
|
||||
"str"
|
||||
],
|
||||
"input_types": ["str"],
|
||||
"dynamic": false,
|
||||
"info": "",
|
||||
"value": ""
|
||||
},
|
||||
"_type": "CustomComponent"
|
||||
},
|
||||
"base_classes": [
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["str"],
|
||||
"display_name": "text checkpoint",
|
||||
"documentation": "http://docs.langflow.org/components/custom",
|
||||
"custom_fields": {
|
||||
"save_path": null,
|
||||
"text_input": null
|
||||
},
|
||||
"output_types": [
|
||||
"str"
|
||||
],
|
||||
"output_types": ["str"],
|
||||
"field_formatters": {},
|
||||
"beta": true
|
||||
},
|
||||
|
|
@ -161,18 +155,14 @@
|
|||
"_type": "CustomComponent"
|
||||
},
|
||||
"description": "Converts audio to text using OpenAI's Whisper.",
|
||||
"base_classes": [
|
||||
"str"
|
||||
],
|
||||
"base_classes": ["str"],
|
||||
"display_name": "Whisper Transcriber",
|
||||
"documentation": "",
|
||||
"custom_fields": {
|
||||
"OpenAIKey": null,
|
||||
"audio": null
|
||||
},
|
||||
"output_types": [
|
||||
"str"
|
||||
],
|
||||
"output_types": ["str"],
|
||||
"field_formatters": {},
|
||||
"beta": true
|
||||
},
|
||||
|
|
@ -196,15 +186,11 @@
|
|||
"targetHandle": {
|
||||
"fieldName": "text_input",
|
||||
"id": "CustomComponent-MtJjl",
|
||||
"inputTypes": [
|
||||
"str"
|
||||
],
|
||||
"inputTypes": ["str"],
|
||||
"type": "str"
|
||||
},
|
||||
"sourceHandle": {
|
||||
"baseClasses": [
|
||||
"str"
|
||||
],
|
||||
"baseClasses": ["str"],
|
||||
"dataType": "CustomComponent",
|
||||
"id": "CustomComponent-7NQoq"
|
||||
}
|
||||
|
|
@ -228,4 +214,4 @@
|
|||
"folder": null,
|
||||
"id": "1b0814b7-2964-4e09-9b4b-f7413c4fb50b",
|
||||
"user_id": "8b5cf798-f1b8-4108-88fd-d7274d08d471"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { readFileSync } from "fs";
|
||||
|
||||
test.describe("group node test", () => {
|
||||
/// <reference lib="dom"/>
|
||||
|
|
@ -7,71 +6,24 @@ test.describe("group node test", () => {
|
|||
await page.goto("http:localhost:3000/");
|
||||
await page.locator('//*[@id="new-project-btn"]').click();
|
||||
|
||||
await page.getByTestId("blank-flow").click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Read your file into a buffer.
|
||||
const jsonContent = readFileSync(
|
||||
"tests/end-to-end/assets/flow_group_test.json",
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
// Create the DataTransfer and File
|
||||
const dataTransfer = await page.evaluateHandle((data) => {
|
||||
const dt = new DataTransfer();
|
||||
// Convert the buffer to a hex array
|
||||
const file = new File([data], "flow_group_test.json", {
|
||||
type: "application/json",
|
||||
});
|
||||
dt.items.add(file);
|
||||
return dt;
|
||||
}, jsonContent);
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// Now dispatch
|
||||
await page.dispatchEvent(
|
||||
"//*[@id='react-flow-id']/div[1]/div[1]/div",
|
||||
"drop",
|
||||
{
|
||||
dataTransfer,
|
||||
}
|
||||
);
|
||||
|
||||
const genericNoda = page.getByTestId("div-generic-node");
|
||||
const elementCount = await genericNoda.count();
|
||||
if (elementCount > 0) {
|
||||
expect(true).toBeTruthy();
|
||||
}
|
||||
await page.getByRole("heading", { name: "Data Ingestion" }).click();
|
||||
await page.waitForTimeout(2000);
|
||||
await page.getByLabel("fit view").click();
|
||||
await page.keyboard.down("Control");
|
||||
await page
|
||||
.locator('//*[@id="react-flow-id"]/div[1]/div[2]/button[3]')
|
||||
.click();
|
||||
|
||||
await page.getByTestId("title-Agent Initializer").click({
|
||||
modifiers: ["Control"],
|
||||
});
|
||||
|
||||
await page.getByTestId("title-PythonFunctionTool").click({
|
||||
modifiers: ["Control"],
|
||||
});
|
||||
await page.getByTestId("title-ChatOpenAI").click({
|
||||
modifiers: ["Control"],
|
||||
});
|
||||
|
||||
.getByTestId("title-OpenAIEmbeddings")
|
||||
.click({ modifiers: ["Control"] });
|
||||
await page.getByTestId("title-URL").click({ modifiers: ["Control"] });
|
||||
await page
|
||||
.getByTestId("title-Recursive Character Text Splitter")
|
||||
.click({ modifiers: ["Control"] });
|
||||
await page.keyboard.up("Control");
|
||||
await page.getByRole("button", { name: "Group" }).click();
|
||||
|
||||
const textArea = page.getByTestId("div-textarea-description");
|
||||
const elementCountText = await textArea.count();
|
||||
if (elementCountText > 0) {
|
||||
expect(true).toBeTruthy();
|
||||
}
|
||||
|
||||
const groupNode = page.getByTestId("title-Group");
|
||||
const elementGroup = await groupNode.count();
|
||||
if (elementGroup > 0) {
|
||||
expect(true).toBeTruthy();
|
||||
}
|
||||
|
||||
// Now dispatch
|
||||
await page.getByTestId(/input-collection_name_Chroma-.*/).click();
|
||||
await page.getByTestId(/input-collection_name_Chroma-.*/).fill("test");
|
||||
await page.getByTestId("title-Group").click();
|
||||
await page.keyboard.press("Control+g");
|
||||
const value = await page.getByTestId("input-collection_name").inputValue();
|
||||
expect(value).toBe("test");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue