Feature/UI table (#1825)
This commit is contained in:
commit
bdca2e3a40
35 changed files with 1162 additions and 154 deletions
|
|
@ -0,0 +1,43 @@
|
|||
"""Add default_fields column
|
||||
|
||||
Revision ID: 1f4d6df60295
|
||||
Revises: 6e7b581b5648
|
||||
Create Date: 2024-04-29 09:49:46.864145
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "1f4d6df60295"
|
||||
down_revision: Union[str, None] = "6e7b581b5648"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
column_names = [column["name"] for column in inspector.get_columns("variable")]
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if "default_fields" not in column_names:
|
||||
batch_op.add_column(sa.Column("default_fields", sa.JSON(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
column_names = [column["name"] for column in inspector.get_columns("variable")]
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
if "default_fields" in column_names:
|
||||
batch_op.drop_column("default_fields")
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
"""Set name and value to not nullable
|
||||
|
||||
Revision ID: c153816fd85f
|
||||
Revises: 1f4d6df60295
|
||||
Create Date: 2024-04-30 14:31:23.898995
|
||||
|
||||
"""
|
||||
|
||||
from typing import Sequence, Union
|
||||
|
||||
import sqlalchemy as sa
|
||||
from alembic import op
|
||||
from sqlalchemy.engine.reflection import Inspector
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = "c153816fd85f"
|
||||
down_revision: Union[str, None] = "1f4d6df60295"
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
columns = inspector.get_columns("variable")
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
name_column = [column for column in columns if column["name"] == "name"][0]
|
||||
if name_column and name_column["nullable"]:
|
||||
batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=False)
|
||||
value_column = [column for column in columns if column["name"] == "value"][0]
|
||||
if value_column and value_column["nullable"]:
|
||||
batch_op.alter_column("value", existing_type=sa.VARCHAR(), nullable=False)
|
||||
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
conn = op.get_bind()
|
||||
inspector = Inspector.from_engine(conn) # type: ignore
|
||||
columns = inspector.get_columns("variable")
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table("variable", schema=None) as batch_op:
|
||||
name_column = [column for column in columns if column["name"] == "name"][0]
|
||||
if name_column and not name_column["nullable"]:
|
||||
batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=True)
|
||||
value_column = [column for column in columns if column["name"] == "value"][0]
|
||||
if value_column and not value_column["nullable"]:
|
||||
batch_op.alter_column("name", existing_type=sa.VARCHAR(), nullable=True)
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
|
|
@ -37,7 +37,11 @@ def create_variable(
|
|||
variable_dict["user_id"] = current_user.id
|
||||
|
||||
db_variable = Variable.model_validate(variable_dict)
|
||||
if not db_variable.value:
|
||||
if not db_variable.name and not db_variable.value:
|
||||
raise HTTPException(status_code=400, detail="Variable name and value cannot be empty")
|
||||
elif not db_variable.name:
|
||||
raise HTTPException(status_code=400, detail="Variable name cannot be empty")
|
||||
elif not db_variable.value:
|
||||
raise HTTPException(status_code=400, detail="Variable value cannot be empty")
|
||||
encrypted = auth_utils.encrypt_api_key(db_variable.value, settings_service=settings_service)
|
||||
db_variable.value = encrypted
|
||||
|
|
@ -85,7 +89,7 @@ def update_variable(
|
|||
variable_data = variable.model_dump(exclude_unset=True)
|
||||
for key, value in variable_data.items():
|
||||
setattr(db_variable, key, value)
|
||||
db_variable.updated_at = datetime.utcnow()
|
||||
db_variable.updated_at = datetime.now(timezone.utc)
|
||||
session.commit()
|
||||
session.refresh(db_variable)
|
||||
return db_variable
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from typing import Optional
|
||||
|
||||
from langchain.llms.base import BaseLanguageModel
|
||||
from langchain_openai import AzureChatOpenAI
|
||||
from pydantic.v1 import SecretStr
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ from langchain_community.chat_models.litellm import ChatLiteLLM, ChatLiteLLMExce
|
|||
|
||||
from langflow.base.constants import STREAM_INFO_TEXT
|
||||
from langflow.base.models.model import LCModelComponent
|
||||
from langflow.field_typing import BaseLanguageModel, Text
|
||||
from langflow.field_typing import Text
|
||||
|
||||
|
||||
class ChatLiteLLMModelComponent(LCModelComponent):
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from typing import Literal, Optional, cast
|
|||
from langchain_core.documents import Document
|
||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
||||
from pydantic import BaseModel, model_validator
|
||||
from langchain_core.messages import HumanMessage, AIMessage
|
||||
|
||||
|
||||
class Record(BaseModel):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlmodel import Column, DateTime, Field, Relationship, SQLModel, func
|
||||
from sqlmodel import JSON, Column, DateTime, Field, Relationship, SQLModel, func
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.database.models.user.model import User
|
||||
|
|
@ -13,8 +13,9 @@ def utc_now():
|
|||
|
||||
|
||||
class VariableBase(SQLModel):
|
||||
name: Optional[str] = Field(None, description="Name of the variable")
|
||||
value: Optional[str] = Field(None, description="Encrypted value of the variable")
|
||||
name: str = Field(description="Name of the variable")
|
||||
value: str = Field(description="Encrypted value of the variable")
|
||||
default_fields: Optional[List[str]] = Field(sa_column=Column(JSON))
|
||||
type: Optional[str] = Field(None, description="Type of the variable")
|
||||
|
||||
|
||||
|
|
@ -35,6 +36,7 @@ class Variable(VariableBase, table=True):
|
|||
sa_column=Column(DateTime(timezone=True), nullable=True),
|
||||
description="Last update time of the variable",
|
||||
)
|
||||
default_fields: Optional[List[str]] = Field(sa_column=Column(JSON))
|
||||
# foreign key to user table
|
||||
user_id: UUID = Field(description="User ID associated with this variable", foreign_key="user.id")
|
||||
user: "User" = Relationship(back_populates="variables")
|
||||
|
|
@ -50,9 +52,11 @@ class VariableRead(SQLModel):
|
|||
id: UUID
|
||||
name: Optional[str] = Field(None, description="Name of the variable")
|
||||
type: Optional[str] = Field(None, description="Type of the variable")
|
||||
default_fields: Optional[List[str]] = Field(None, description="Default fields for the variable")
|
||||
|
||||
|
||||
class VariableUpdate(SQLModel):
|
||||
id: UUID # Include the ID for updating
|
||||
name: Optional[str] = Field(None, description="Name of the variable")
|
||||
value: Optional[str] = Field(None, description="Encrypted value of the variable")
|
||||
default_fields: Optional[List[str]] = Field(None, description="Default fields for the variable")
|
||||
|
|
|
|||
|
|
@ -30,7 +30,14 @@ class VariableService(Service):
|
|||
if var in os.environ:
|
||||
logger.debug(f"Creating {var} variable from environment.")
|
||||
try:
|
||||
self.create_variable(user_id, var, os.environ[var], _type="Credential", session=session)
|
||||
self.create_variable(
|
||||
user_id=user_id,
|
||||
name=var,
|
||||
value=os.environ[var],
|
||||
default_fields=[],
|
||||
_type="Credential",
|
||||
session=session,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating {var} variable: {e}")
|
||||
|
||||
|
|
@ -91,6 +98,7 @@ class VariableService(Service):
|
|||
user_id: Union[UUID, str],
|
||||
name: str,
|
||||
value: str,
|
||||
default_fields: list[str] = [],
|
||||
_type: str = "Generic",
|
||||
session: Session = Depends(get_session),
|
||||
):
|
||||
|
|
@ -98,6 +106,7 @@ class VariableService(Service):
|
|||
name=name,
|
||||
type=_type,
|
||||
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
||||
default_fields=default_fields,
|
||||
)
|
||||
variable = Variable.model_validate(variable_base, from_attributes=True, update={"user_id": user_id})
|
||||
session.add(variable)
|
||||
|
|
|
|||
1
src/frontend/.prettierignore
Normal file
1
src/frontend/.prettierignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
build/*
|
||||
5
src/frontend/package-lock.json
generated
5
src/frontend/package-lock.json
generated
|
|
@ -31,6 +31,7 @@
|
|||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@types/axios": "^0.14.0",
|
||||
"ace-builds": "^1.24.1",
|
||||
"ag-grid-community": "^31.2.1",
|
||||
"ag-grid-react": "^31.2.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"axios": "^1.5.0",
|
||||
|
|
@ -49,10 +50,10 @@
|
|||
"moment": "^2.29.4",
|
||||
"openseadragon": "^4.1.1",
|
||||
"playwright": "^1.42.0",
|
||||
"react": "^18.2.0",
|
||||
"react": "^18.2.21",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^18.2.21",
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-laag": "^2.0.5",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
"@tailwindcss/line-clamp": "^0.4.4",
|
||||
"@types/axios": "^0.14.0",
|
||||
"ace-builds": "^1.24.1",
|
||||
"ag-grid-community": "^31.2.1",
|
||||
"ag-grid-react": "^31.2.1",
|
||||
"ansi-to-html": "^0.7.2",
|
||||
"axios": "^1.5.0",
|
||||
|
|
@ -44,10 +45,10 @@
|
|||
"moment": "^2.29.4",
|
||||
"openseadragon": "^4.1.1",
|
||||
"playwright": "^1.42.0",
|
||||
"react": "^18.2.0",
|
||||
"react": "^18.2.21",
|
||||
"react-ace": "^10.1.0",
|
||||
"react-cookie": "^4.1.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dom": "^18.2.21",
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react-icons": "^5.0.1",
|
||||
"react-laag": "^2.0.5",
|
||||
|
|
@ -74,7 +75,7 @@
|
|||
"start": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\"",
|
||||
"format": "npx prettier --write \"./**/*.{js,jsx,ts,tsx,json,md}\" --ignore-path .prettierignore",
|
||||
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
|
||||
},
|
||||
"eslintConfig": {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ import useFlowsManagerStore from "./stores/flowsManagerStore";
|
|||
import { useGlobalVariablesStore } from "./stores/globalVariables";
|
||||
import { useStoreStore } from "./stores/storeStore";
|
||||
import { useTypesStore } from "./stores/typesStore";
|
||||
|
||||
export default function App() {
|
||||
const removeFromTempNotificationList = useAlertStore(
|
||||
(state) => state.removeFromTempNotificationList
|
||||
|
|
@ -48,6 +47,9 @@ export default function App() {
|
|||
const setGlobalVariables = useGlobalVariablesStore(
|
||||
(state) => state.setGlobalVariables
|
||||
);
|
||||
const setUnavailableFields = useGlobalVariablesStore(
|
||||
(state) => state.setUnavaliableFields
|
||||
);
|
||||
const checkHasStore = useStoreStore((state) => state.checkHasStore);
|
||||
const navigate = useNavigate();
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { registerGlobalVariable } from "../../controllers/API";
|
|||
import BaseModal from "../../modals/baseModal";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||
import { useTypesStore } from "../../stores/typesStore";
|
||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||
import ForwardedIconComponent from "../genericIconComponent";
|
||||
import InputComponent from "../inputComponent";
|
||||
|
|
@ -16,25 +17,42 @@ import { Textarea } from "../ui/textarea";
|
|||
export default function AddNewVariableButton({ children }): JSX.Element {
|
||||
const [key, setKey] = useState("");
|
||||
const [value, setValue] = useState("");
|
||||
const [type, setType] = useState("");
|
||||
const [type, setType] = useState("Generic");
|
||||
const [fields, setFields] = useState<string[]>([]);
|
||||
const [open, setOpen] = useState(false);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const componentFields = useTypesStore((state) => state.ComponentFields);
|
||||
const unavaliableFields =new Set(Object.keys(useGlobalVariablesStore(
|
||||
(state) => state.unavaliableFields
|
||||
)));
|
||||
|
||||
const availableFields = Array.from(componentFields).filter(
|
||||
(field) => !unavaliableFields.has(field)
|
||||
);
|
||||
const addGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.addGlobalVariable
|
||||
);
|
||||
|
||||
function handleSaveVariable() {
|
||||
let data: { name: string; value: string; type?: string } = {
|
||||
let data: {
|
||||
name: string;
|
||||
value: string;
|
||||
type?: string;
|
||||
default_fields?: string[];
|
||||
} = {
|
||||
name: key,
|
||||
type,
|
||||
value,
|
||||
default_fields: fields,
|
||||
};
|
||||
registerGlobalVariable(data)
|
||||
.then((res) => {
|
||||
const { name, id, type } = res.data;
|
||||
addGlobalVariable(name, id, type);
|
||||
addGlobalVariable(name, id, type, fields);
|
||||
setKey("");
|
||||
setValue("");
|
||||
setType("");
|
||||
setFields([]);
|
||||
setOpen(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
@ -89,6 +107,14 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
|||
placeholder="Insert a value for the variable..."
|
||||
className="w-full resize-none custom-scroll"
|
||||
/>
|
||||
<Label>Apply To Fields (optional)</Label>
|
||||
<InputComponent
|
||||
setSelectedOptions={(value) => setFields(value)}
|
||||
selectedOptions={fields}
|
||||
password={false}
|
||||
options={availableFields}
|
||||
placeholder="Choose a field for the variable..."
|
||||
></InputComponent>
|
||||
</div>
|
||||
</BaseModal.Content>
|
||||
<BaseModal.Footer>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||
import { useRef, useState } from "react";
|
||||
import { DropDownComponentType } from "../../types/components";
|
||||
import { cn } from "../../utils/utils";
|
||||
|
|
@ -13,6 +14,7 @@ import {
|
|||
} from "../ui/command";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverContentWithoutPortal,
|
||||
PopoverTrigger,
|
||||
} from "../ui/popover";
|
||||
|
|
@ -25,50 +27,63 @@ export default function Dropdown({
|
|||
onSelect,
|
||||
editNode = false,
|
||||
id = "",
|
||||
children,
|
||||
}: DropDownComponentType): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [open, setOpen] = useState(children ? true : false);
|
||||
|
||||
const refButton = useRef<HTMLButtonElement>(null);
|
||||
|
||||
const PopoverContentDropdown = children
|
||||
? PopoverContent
|
||||
: PopoverContentWithoutPortal;
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(options)?.length > 0 ? (
|
||||
<>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
variant="primary"
|
||||
size="xs"
|
||||
role="combobox"
|
||||
ref={refButton}
|
||||
aria-expanded={open}
|
||||
data-testid={`${id ?? ""}`}
|
||||
className={cn(
|
||||
editNode
|
||||
? "dropdown-component-outline"
|
||||
: "dropdown-component-false-outline",
|
||||
"w-full justify-between font-normal",
|
||||
editNode ? "input-edit-node" : "py-2"
|
||||
)}
|
||||
>
|
||||
<span data-testid={`value-dropdown-` + id}>
|
||||
{value &&
|
||||
value !== "" &&
|
||||
options.find((option) => option === value)
|
||||
? options.find((option) => option === value)
|
||||
: "Choose an option..."}
|
||||
</span>
|
||||
<Popover open={open} onOpenChange={children ? () => {} : setOpen}>
|
||||
{children ? (
|
||||
<PopoverAnchor>{children}</PopoverAnchor>
|
||||
) : (
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
disabled={disabled}
|
||||
variant="primary"
|
||||
size="xs"
|
||||
role="combobox"
|
||||
ref={refButton}
|
||||
aria-expanded={open}
|
||||
data-testid={`${id ?? ""}`}
|
||||
className={cn(
|
||||
editNode
|
||||
? "dropdown-component-outline"
|
||||
: "dropdown-component-false-outline",
|
||||
"w-full justify-between font-normal",
|
||||
editNode ? "input-edit-node" : "py-2"
|
||||
)}
|
||||
>
|
||||
<span data-testid={`value-dropdown-` + id}>
|
||||
{value &&
|
||||
value !== "" &&
|
||||
options.find((option) => option === value)
|
||||
? options.find((option) => option === value)
|
||||
: "Choose an option..."}
|
||||
</span>
|
||||
|
||||
<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" }}
|
||||
<ForwardedIconComponent
|
||||
name="ChevronsUpDown"
|
||||
className="ml-2 h-4 w-4 shrink-0 opacity-50"
|
||||
/>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
)}
|
||||
<PopoverContentDropdown
|
||||
className="nocopy nowheel nopan nodelete nodrag noundo p-0"
|
||||
style={
|
||||
children
|
||||
? {}
|
||||
: { minWidth: refButton?.current?.clientWidth ?? "200px" }
|
||||
}
|
||||
>
|
||||
<Command>
|
||||
<CommandInput placeholder="Search options..." className="h-9" />
|
||||
|
|
@ -98,7 +113,7 @@ export default function Dropdown({
|
|||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContentWithoutPortal>
|
||||
</PopoverContentDropdown>
|
||||
</Popover>
|
||||
</>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { gradients } from "../../utils/styleUtils";
|
|||
|
||||
export default function GradientChooserComponent({ value, onChange }) {
|
||||
return (
|
||||
<div className="flex flex-wrap items-center justify-center gap-4">
|
||||
<div className="flex flex-wrap items-center justify-start gap-2">
|
||||
{gradients.map((gradient, idx) => (
|
||||
<div
|
||||
onClick={() => {
|
||||
|
|
|
|||
|
|
@ -168,50 +168,56 @@ export default function Header(): JSX.Element {
|
|||
/>
|
||||
</button>
|
||||
)}
|
||||
{!autoLogin && (
|
||||
<>
|
||||
<Separator orientation="vertical" />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className={
|
||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||
(userData?.profile_image ??
|
||||
gradients[
|
||||
parseInt(userData?.id ?? "", 30) % gradients.length
|
||||
])
|
||||
}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
{isAdmin && (
|
||||
|
||||
<>
|
||||
<Separator orientation="vertical" />
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<button
|
||||
className={
|
||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||
(userData?.profile_image ??
|
||||
(userData?.id
|
||||
? gradients[
|
||||
parseInt(userData?.id ?? "", 30) % gradients.length
|
||||
]
|
||||
: "bg-gray-500"))
|
||||
}
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuLabel>General</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => navigate("/settings")}
|
||||
>
|
||||
Settings
|
||||
</DropdownMenuItem>
|
||||
{!autoLogin && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||
{isAdmin && (
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => navigate("/admin")}
|
||||
>
|
||||
Admin Page
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => navigate("/admin")}
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
>
|
||||
Admin Page
|
||||
Sign Out
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => navigate("/account/settings")}
|
||||
>
|
||||
Profile Settings
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
onClick={() => {
|
||||
logout();
|
||||
}}
|
||||
>
|
||||
Sign Out
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ export default function InputComponent({
|
|||
optionsIcon = "ChevronsUpDown",
|
||||
selectedOption,
|
||||
setSelectedOption,
|
||||
selectedOptions = [],
|
||||
setSelectedOptions,
|
||||
options = [],
|
||||
optionsPlaceholder = "Search options...",
|
||||
optionsButton,
|
||||
|
|
@ -101,13 +103,18 @@ export default function InputComponent({
|
|||
value={
|
||||
(selectedOption !== "" || !onChange) && setSelectedOption
|
||||
? selectedOption
|
||||
: (selectedOptions?.length !== 0 || !onChange) &&
|
||||
setSelectedOptions
|
||||
? selectedOptions?.join(", ")
|
||||
: value
|
||||
}
|
||||
autoFocus={autoFocus}
|
||||
disabled={disabled}
|
||||
onClick={() => {
|
||||
(selectedOption !== "" || !onChange) &&
|
||||
setSelectedOption &&
|
||||
(((selectedOption !== "" || !onChange) &&
|
||||
setSelectedOption) ||
|
||||
((selectedOptions?.length !== 0 || !onChange) &&
|
||||
setSelectedOptions)) &&
|
||||
setShowOptions(true);
|
||||
}}
|
||||
required={required}
|
||||
|
|
@ -119,9 +126,11 @@ export default function InputComponent({
|
|||
? " text-clip password "
|
||||
: "",
|
||||
editNode ? " input-edit-node " : "",
|
||||
password && setSelectedOption ? "pr-[62.9px]" : "",
|
||||
(!password && setSelectedOption) ||
|
||||
(password && !setSelectedOption)
|
||||
password && (setSelectedOption || setSelectedOptions)
|
||||
? "pr-[62.9px]"
|
||||
: "",
|
||||
(!password && (setSelectedOption || setSelectedOptions)) ||
|
||||
(password && !(setSelectedOption || setSelectedOptions))
|
||||
? "pr-8"
|
||||
: "",
|
||||
|
||||
|
|
@ -164,7 +173,10 @@ export default function InputComponent({
|
|||
>
|
||||
<Command
|
||||
filter={(value, search) => {
|
||||
if (value.includes(search) || value.includes("doNotFilter-"))
|
||||
if (
|
||||
value.toLowerCase().includes(search.toLowerCase()) ||
|
||||
value.includes("doNotFilter-")
|
||||
)
|
||||
return 1; // ensures items arent filtered
|
||||
return 0;
|
||||
}}
|
||||
|
|
@ -184,7 +196,15 @@ export default function InputComponent({
|
|||
? ""
|
||||
: currentValue
|
||||
);
|
||||
setShowOptions(false);
|
||||
setSelectedOptions &&
|
||||
setSelectedOptions(
|
||||
selectedOptions?.includes(currentValue)
|
||||
? selectedOptions.filter(
|
||||
(item) => item !== currentValue
|
||||
)
|
||||
: [...selectedOptions, currentValue]
|
||||
);
|
||||
!setSelectedOptions && setShowOptions(false);
|
||||
}}
|
||||
>
|
||||
<div className="group flex w-full items-center justify-between">
|
||||
|
|
@ -192,7 +212,8 @@ export default function InputComponent({
|
|||
<div
|
||||
className={cn(
|
||||
"relative mr-2 h-4 w-4",
|
||||
selectedOption === option
|
||||
selectedOption === option ||
|
||||
selectedOptions?.includes(option)
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
)}
|
||||
|
|
@ -228,12 +249,16 @@ export default function InputComponent({
|
|||
<div
|
||||
className={cn(
|
||||
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
|
||||
(selectedOption !== "" || !onChange) && setSelectedOption
|
||||
((selectedOption !== "" || !onChange) && setSelectedOption) ||
|
||||
((selectedOptions?.length !== 0 || !onChange) &&
|
||||
setSelectedOptions)
|
||||
? ""
|
||||
: "hidden"
|
||||
)}
|
||||
onClick={
|
||||
(selectedOption !== "" || !onChange) && setSelectedOption
|
||||
((selectedOption !== "" || !onChange) && setSelectedOption) ||
|
||||
((selectedOptions?.length !== 0 || !onChange) &&
|
||||
setSelectedOptions)
|
||||
? (e) => {
|
||||
setShowOptions((old) => !old);
|
||||
e.preventDefault();
|
||||
|
|
@ -245,7 +270,7 @@ export default function InputComponent({
|
|||
</>
|
||||
)}
|
||||
|
||||
{setSelectedOption && (
|
||||
{(setSelectedOption || setSelectedOptions) && (
|
||||
<span
|
||||
className={cn(
|
||||
password && selectedOption === "" ? "right-8" : "right-0",
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ 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";
|
||||
|
|
@ -24,6 +23,9 @@ export default function InputGlobalComponent({
|
|||
);
|
||||
|
||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||
const unavaliableFields = useGlobalVariablesStore(
|
||||
(state) => state.unavaliableFields
|
||||
);
|
||||
const removeGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.removeGlobalVariable
|
||||
);
|
||||
|
|
@ -35,16 +37,32 @@ export default function InputGlobalComponent({
|
|||
!globalVariablesEntries.includes(data.node?.template[name].value) &&
|
||||
data.node?.template[name].load_from_db
|
||||
) {
|
||||
onChange("");
|
||||
setDb(false);
|
||||
setTimeout(() => {
|
||||
onChange("");
|
||||
setDb(false);
|
||||
}, 100);
|
||||
}
|
||||
}, [globalVariablesEntries]);
|
||||
|
||||
function handleDelete(key: string) {
|
||||
useEffect(() => {
|
||||
if (
|
||||
!data.node?.template[name].value &&
|
||||
data.node?.template[name].display_name
|
||||
) {
|
||||
if (unavaliableFields[data.node?.template[name].display_name!] && !disabled) {
|
||||
setTimeout(() => {
|
||||
setDb(true);
|
||||
onChange(unavaliableFields[data.node?.template[name].display_name!]);
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
}, [unavaliableFields]);
|
||||
|
||||
async function handleDelete(key: string) {
|
||||
const id = getVariableId(key);
|
||||
if (id !== undefined) {
|
||||
deleteGlobalVariable(id)
|
||||
.then((_) => {
|
||||
await deleteGlobalVariable(id)
|
||||
.then(() => {
|
||||
removeGlobalVariable(key);
|
||||
if (
|
||||
data?.node?.template[name].value === key &&
|
||||
|
|
@ -54,11 +72,10 @@ export default function InputGlobalComponent({
|
|||
setDb(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
let responseError = error as ResponseErrorDetailAPI;
|
||||
.catch(() => {
|
||||
setErrorData({
|
||||
title: "Error deleting variable",
|
||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
||||
list: [cn("ID not found for variable: ", key)],
|
||||
});
|
||||
});
|
||||
} else {
|
||||
|
|
@ -117,7 +134,8 @@ export default function InputGlobalComponent({
|
|||
</DeleteConfirmationModal>
|
||||
)}
|
||||
selectedOption={
|
||||
data?.node?.template[name].load_from_db ?? false
|
||||
data?.node?.template[name].load_from_db &&
|
||||
globalVariablesEntries.includes(data?.node?.template[name].value ?? "")
|
||||
? data?.node?.template[name].value
|
||||
: ""
|
||||
}
|
||||
|
|
|
|||
29
src/frontend/src/components/tableComponent/index.tsx
Normal file
29
src/frontend/src/components/tableComponent/index.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import "ag-grid-community/styles/ag-grid.css"; // Mandatory CSS required by the grid
|
||||
import "ag-grid-community/styles/ag-theme-quartz.css"; // Optional Theme applied to the grid
|
||||
import { AgGridReact } from "ag-grid-react";
|
||||
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
import "../../style/ag-theme-shadcn.css"; // Custom CSS applied to the grid
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
const TableComponent = forwardRef<
|
||||
ElementRef<typeof AgGridReact>,
|
||||
ComponentPropsWithoutRef<typeof AgGridReact>
|
||||
>(({ pagination = true, ...props }, ref) => {
|
||||
const dark = useDarkStore((state) => state.dark);
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col">
|
||||
<div
|
||||
className={cn(
|
||||
dark ? "ag-theme-quartz-dark" : "ag-theme-quartz",
|
||||
"ag-theme-shadcn flex h-full flex-col"
|
||||
)} // applying the grid theme
|
||||
>
|
||||
<AgGridReact ref={ref} {...props} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default TableComponent;
|
||||
|
|
@ -862,13 +862,14 @@ export async function requestLogout() {
|
|||
}
|
||||
|
||||
export async function getGlobalVariables(): Promise<{
|
||||
[key: string]: { id: string; type: string };
|
||||
[key: string]: { id: string; type: string; default_fields: string[] };
|
||||
}> {
|
||||
const globalVariables = {};
|
||||
(await api.get(`${BASE_URL_API}variables/`)).data.forEach((element) => {
|
||||
globalVariables[element.name] = {
|
||||
id: element.id,
|
||||
type: element.type,
|
||||
default_fields: element.default_fields,
|
||||
};
|
||||
});
|
||||
return globalVariables;
|
||||
|
|
@ -878,20 +879,33 @@ export async function registerGlobalVariable({
|
|||
name,
|
||||
value,
|
||||
type,
|
||||
default_fields = [],
|
||||
}: {
|
||||
name: string;
|
||||
value: string;
|
||||
type?: string;
|
||||
default_fields?: string[];
|
||||
}): Promise<AxiosResponse<{ name: string; id: string; type: string }>> {
|
||||
return await api.post(`${BASE_URL_API}variables/`, {
|
||||
name,
|
||||
value,
|
||||
type,
|
||||
});
|
||||
try {
|
||||
const response = await api.post(`${BASE_URL_API}variables/`, {
|
||||
name,
|
||||
value,
|
||||
type,
|
||||
default_fields: default_fields,
|
||||
});
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteGlobalVariable(id: string) {
|
||||
api.delete(`${BASE_URL_API}variables/${id}`);
|
||||
try {
|
||||
const response = await api.delete(`${BASE_URL_API}variables/${id}`);
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function updateGlobalVariable(
|
||||
|
|
@ -899,10 +913,16 @@ export async function updateGlobalVariable(
|
|||
value: string,
|
||||
id: string
|
||||
) {
|
||||
api.patch(`${BASE_URL_API}variables/${id}`, {
|
||||
name,
|
||||
value,
|
||||
});
|
||||
try {
|
||||
const response = api.patch(`${BASE_URL_API}variables/${id}`, {
|
||||
name,
|
||||
value,
|
||||
});
|
||||
|
||||
return response;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getVerticesOrder(
|
||||
|
|
|
|||
91
src/frontend/src/pages/SettingsPage/index.tsx
Normal file
91
src/frontend/src/pages/SettingsPage/index.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
import { useEffect } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import ForwardedIconComponent from "../../components/genericIconComponent";
|
||||
import PageLayout from "../../components/pageLayout";
|
||||
import SidebarNav from "../../components/sidebarComponent";
|
||||
import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
||||
|
||||
export default function SettingsPage(): JSX.Element {
|
||||
const pathname = location.pathname;
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
useEffect(() => {
|
||||
setCurrentFlowId("");
|
||||
}, [pathname]);
|
||||
|
||||
const sidebarNavItems = [
|
||||
{
|
||||
title: "General",
|
||||
href: "/settings/general",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="SlidersHorizontal"
|
||||
className="mx-[0.08rem] w-[1.1rem] stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
/* {
|
||||
title: "Theme",
|
||||
href: "/settings/theme",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Palette"
|
||||
className="mx-[0.08rem] w-[1.1rem] stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Bundles",
|
||||
href: "/settings/bundles",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Boxes"
|
||||
className="mx-[0.08rem] w-[1.1rem] stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Integrations",
|
||||
href: "/settings/integrations",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Blocks"
|
||||
className="mx-[0.08rem] w-[1.1rem] stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
}, */
|
||||
{
|
||||
title: "Global Variables",
|
||||
href: "/settings/global-variables",
|
||||
icon: (
|
||||
<ForwardedIconComponent
|
||||
name="Globe"
|
||||
className="mx-[0.08rem] w-[1.1rem] stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Shortcuts",
|
||||
href: "/settings/shortcuts",
|
||||
icon: (
|
||||
<ForwardedIconComponent name="Keyboard" className="w-5 stroke-[1.5]" />
|
||||
),
|
||||
},
|
||||
];
|
||||
return (
|
||||
<PageLayout
|
||||
title="Settings"
|
||||
description="Manage the general settings for Langflow."
|
||||
>
|
||||
<div className="flex h-full w-full space-y-8 lg:flex-row lg:space-x-8 lg:space-y-0">
|
||||
<aside className="flex h-full flex-col space-y-6 lg:w-1/5">
|
||||
<SidebarNav items={sidebarNavItems} />
|
||||
</aside>
|
||||
<div className="h-full w-full flex-1">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</PageLayout>
|
||||
);
|
||||
}
|
||||
226
src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx
Normal file
226
src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import * as Form from "@radix-ui/react-form";
|
||||
import { cloneDeep } from "lodash";
|
||||
import { useContext, useEffect, useState } from "react";
|
||||
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
||||
import GradientChooserComponent from "../../../../components/gradientChooserComponent";
|
||||
import InputComponent from "../../../../components/inputComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../../../../components/ui/card";
|
||||
import {
|
||||
EDIT_PASSWORD_ALERT_LIST,
|
||||
EDIT_PASSWORD_ERROR_ALERT,
|
||||
SAVE_ERROR_ALERT,
|
||||
SAVE_SUCCESS_ALERT,
|
||||
} from "../../../../constants/alerts_constants";
|
||||
import { CONTROL_PATCH_USER_STATE } from "../../../../constants/constants";
|
||||
import { AuthContext } from "../../../../contexts/authContext";
|
||||
import { resetPassword, updateUser } from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
import {
|
||||
inputHandlerEventType,
|
||||
patchUserInputStateType,
|
||||
} from "../../../../types/components";
|
||||
import { gradients } from "../../../../utils/styleUtils";
|
||||
|
||||
export default function GeneralPage() {
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
);
|
||||
|
||||
const [inputState, setInputState] = useState<patchUserInputStateType>(
|
||||
CONTROL_PATCH_USER_STATE
|
||||
);
|
||||
|
||||
const { autoLogin } = useContext(AuthContext);
|
||||
|
||||
// set null id
|
||||
useEffect(() => {
|
||||
setCurrentFlowId("");
|
||||
}, []);
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const { userData, setUserData } = useContext(AuthContext);
|
||||
const { password, cnfPassword, gradient } = inputState;
|
||||
|
||||
async function handlePatchPassword() {
|
||||
if (password !== cnfPassword) {
|
||||
setErrorData({
|
||||
title: EDIT_PASSWORD_ERROR_ALERT,
|
||||
list: [EDIT_PASSWORD_ALERT_LIST],
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (password !== "") await resetPassword(userData!.id, { password });
|
||||
handleInput({ target: { name: "password", value: "" } });
|
||||
handleInput({ target: { name: "cnfPassword", value: "" } });
|
||||
setSuccessData({ title: SAVE_SUCCESS_ALERT });
|
||||
} catch (error) {
|
||||
setErrorData({
|
||||
title: SAVE_ERROR_ALERT,
|
||||
list: [(error as any).response.data.detail],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePatchGradient() {
|
||||
try {
|
||||
if (gradient !== "")
|
||||
await updateUser(userData!.id, { profile_image: gradient });
|
||||
if (gradient !== "") {
|
||||
let newUserData = cloneDeep(userData);
|
||||
newUserData!.profile_image = gradient;
|
||||
|
||||
setUserData(newUserData);
|
||||
}
|
||||
setSuccessData({ title: SAVE_SUCCESS_ALERT });
|
||||
} catch (error) {
|
||||
setErrorData({
|
||||
title: SAVE_ERROR_ALERT,
|
||||
list: [(error as any).response.data.detail],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleInput({
|
||||
target: { name, value },
|
||||
}: inputHandlerEventType): void {
|
||||
setInputState((prev) => ({ ...prev, [name]: value }));
|
||||
}
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col gap-6">
|
||||
<div className="flex w-full items-center justify-between gap-4 space-y-0.5">
|
||||
<div className="flex w-full flex-col">
|
||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||
General
|
||||
<ForwardedIconComponent
|
||||
name="SlidersHorizontal"
|
||||
className="ml-2 h-5 w-5 text-primary"
|
||||
/>
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Manage settings related to Langflow and your account.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-6">
|
||||
{!autoLogin && (
|
||||
<>
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
handlePatchGradient();
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Card x-chunk="dashboard-04-chunk-1">
|
||||
<CardHeader>
|
||||
<CardTitle>Profile Gradient</CardTitle>
|
||||
<CardDescription>
|
||||
Choose the gradient that appears as your profile picture.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="py-2">
|
||||
<GradientChooserComponent
|
||||
value={
|
||||
gradient == ""
|
||||
? userData?.profile_image ??
|
||||
gradients[
|
||||
parseInt(userData?.id ?? "", 30) %
|
||||
gradients.length
|
||||
]
|
||||
: gradient
|
||||
}
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "gradient", value } });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="border-t px-6 py-4">
|
||||
<Form.Submit asChild>
|
||||
<Button type="submit">Save</Button>
|
||||
</Form.Submit>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Form.Root>
|
||||
<Form.Root
|
||||
onSubmit={(event) => {
|
||||
handlePatchPassword();
|
||||
event.preventDefault();
|
||||
}}
|
||||
>
|
||||
<Card x-chunk="dashboard-04-chunk-2">
|
||||
<CardHeader>
|
||||
<CardTitle>Password</CardTitle>
|
||||
<CardDescription>
|
||||
Type your new password and confirm it.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="flex w-full gap-4">
|
||||
<Form.Field name="password" className="w-full">
|
||||
<InputComponent
|
||||
id="pasword"
|
||||
onChange={(value) => {
|
||||
handleInput({ target: { name: "password", value } });
|
||||
}}
|
||||
value={password}
|
||||
isForm
|
||||
password={true}
|
||||
placeholder="Password"
|
||||
className="w-full"
|
||||
/>
|
||||
<Form.Message
|
||||
match="valueMissing"
|
||||
className="field-invalid"
|
||||
>
|
||||
Please enter your password
|
||||
</Form.Message>
|
||||
</Form.Field>
|
||||
<Form.Field name="cnfPassword" className="w-full">
|
||||
<InputComponent
|
||||
id="cnfPassword"
|
||||
onChange={(value) => {
|
||||
handleInput({
|
||||
target: { name: "cnfPassword", value },
|
||||
});
|
||||
}}
|
||||
value={cnfPassword}
|
||||
isForm
|
||||
password={true}
|
||||
placeholder="Confirm Password"
|
||||
className="w-full"
|
||||
/>
|
||||
|
||||
<Form.Message
|
||||
className="field-invalid"
|
||||
match="valueMissing"
|
||||
>
|
||||
Please confirm your password
|
||||
</Form.Message>
|
||||
</Form.Field>
|
||||
</div>
|
||||
</CardContent>
|
||||
<CardFooter className="border-t px-6 py-4">
|
||||
<Form.Submit asChild>
|
||||
<Button type="submit">Save</Button>
|
||||
</Form.Submit>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</Form.Root>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
|
||||
import { ColDef, ColGroupDef, SelectionChangedEvent } from "ag-grid-community";
|
||||
import { useEffect, useState } from "react";
|
||||
import AddNewVariableButton from "../../../../components/addNewVariableButtonComponent/addNewVariableButton";
|
||||
import Dropdown from "../../../../components/dropdownComponent";
|
||||
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
||||
import TableComponent from "../../../../components/tableComponent";
|
||||
import { Badge } from "../../../../components/ui/badge";
|
||||
import { deleteGlobalVariable } from "../../../../controllers/API";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
import { useGlobalVariablesStore } from "../../../../stores/globalVariables";
|
||||
import { cn } from "../../../../utils/utils";
|
||||
|
||||
export default function GlobalVariablesPage() {
|
||||
const globalVariablesEntries = useGlobalVariablesStore(
|
||||
(state) => state.globalVariablesEntries
|
||||
);
|
||||
const removeGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.removeGlobalVariable
|
||||
);
|
||||
const globalVariables = useGlobalVariablesStore(
|
||||
(state) => state.globalVariables
|
||||
);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||
|
||||
const BadgeRenderer = (props) => {
|
||||
return props.value !== "" ? (
|
||||
<div>
|
||||
<Badge variant="outline" size="md" className="font-normal">
|
||||
{props.value}
|
||||
</Badge>
|
||||
</div>
|
||||
) : (
|
||||
<div></div>
|
||||
);
|
||||
};
|
||||
|
||||
const [rowData, setRowData] = useState<
|
||||
{
|
||||
type: string | undefined;
|
||||
id: string;
|
||||
name: string;
|
||||
default_fields: string | undefined;
|
||||
}[]
|
||||
>();
|
||||
|
||||
useEffect(() => {
|
||||
const rows: Array<{
|
||||
type: string | undefined;
|
||||
id: string;
|
||||
name: string;
|
||||
default_fields: string | undefined;
|
||||
}> = [];
|
||||
globalVariablesEntries.forEach((entrie) => {
|
||||
const globalVariableObj = globalVariables[entrie];
|
||||
rows.push({
|
||||
type: globalVariableObj.type,
|
||||
id: globalVariableObj.id,
|
||||
default_fields: (globalVariableObj.default_fields ?? []).join(", "),
|
||||
name: entrie,
|
||||
});
|
||||
});
|
||||
setRowData(rows);
|
||||
}, [globalVariables]);
|
||||
|
||||
const DropdownEditor = ({ options, value, onValueChange }) => {
|
||||
return (
|
||||
<Dropdown options={options} value={value} onSelect={onValueChange}>
|
||||
<div className="-mt-1.5 w-full"></div>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
// Column Definitions: Defines the columns to be displayed.
|
||||
const [colDefs, setColDefs] = useState<(ColDef<any> | ColGroupDef<any>)[]>([
|
||||
{
|
||||
headerCheckboxSelection: true,
|
||||
checkboxSelection: true,
|
||||
showDisabledCheckboxes: true,
|
||||
headerName: "Variable Name",
|
||||
field: "name",
|
||||
flex: 1,
|
||||
}, //This column will be twice as wide as the others
|
||||
{
|
||||
field: "type",
|
||||
cellRenderer: BadgeRenderer,
|
||||
cellEditor: DropdownEditor,
|
||||
cellEditorParams: {
|
||||
options: ["Generic", "Credential"],
|
||||
},
|
||||
flex: 1,
|
||||
editable: false,
|
||||
},
|
||||
// {
|
||||
// field: "value",
|
||||
// cellEditor: "agLargeTextCellEditor",
|
||||
// flex: 2,
|
||||
// editable: false,
|
||||
// },
|
||||
{
|
||||
headerName: "Apply To Fields",
|
||||
field: "default_fields",
|
||||
flex: 1,
|
||||
editable: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const [selectedRows, setSelectedRows] = useState<string[]>([]);
|
||||
|
||||
async function removeVariables() {
|
||||
const deleteGlobalVariablesPromise = selectedRows.map(async (row) => {
|
||||
const id = getVariableId(row);
|
||||
const deleteGlobalVariables = deleteGlobalVariable(id!);
|
||||
await deleteGlobalVariables;
|
||||
});
|
||||
Promise.all(deleteGlobalVariablesPromise)
|
||||
.then(() => {
|
||||
selectedRows.forEach((row) => {
|
||||
removeGlobalVariable(row);
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
setErrorData({
|
||||
title: `Error deleting global variables.`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col justify-between gap-6">
|
||||
<div className="flex w-full items-center justify-between gap-4 space-y-0.5">
|
||||
<div className="flex w-full flex-col">
|
||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||
Global Variables
|
||||
<ForwardedIconComponent
|
||||
name="Globe"
|
||||
className="ml-2 h-5 w-5 text-primary"
|
||||
/>
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Manage global variables and assign them to fields.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 items-center gap-2">
|
||||
<Button
|
||||
data-testid="api-key-button-store"
|
||||
variant="primary"
|
||||
className="group px-2"
|
||||
disabled={selectedRows.length === 0}
|
||||
onClick={removeVariables}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className={cn(
|
||||
"h-5 w-5 text-destructive group-disabled:text-primary"
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
<AddNewVariableButton>
|
||||
<Button data-testid="api-key-button-store" variant="primary">
|
||||
<IconComponent name="Plus" className="mr-2 w-4" />
|
||||
Add New
|
||||
</Button>
|
||||
</AddNewVariableButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex h-full w-full flex-col justify-between pb-8">
|
||||
<TableComponent
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(event.api.getSelectedRows().map((row) => row.name));
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
columnDefs={colDefs}
|
||||
rowData={rowData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
import { ColDef, ColGroupDef } from "ag-grid-community";
|
||||
import { useState } from "react";
|
||||
import ForwardedIconComponent from "../../../../components/genericIconComponent";
|
||||
import TableComponent from "../../../../components/tableComponent";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "../../../../components/ui/card";
|
||||
|
||||
export default function ShortcutsPage() {
|
||||
const advancedShortcut = "Ctrl + Shift + A";
|
||||
const minizmizeShortcut = "Ctrl + Shift + Q";
|
||||
const codeShortcut = "Ctrl + Shift + C";
|
||||
const copyShortcut = "Ctrl + C";
|
||||
const duplicateShortcut = "Ctrl + D";
|
||||
const shareShortcut = "Ctrl + Shift + S";
|
||||
const docsShortcut = "Ctrl + Shift + D";
|
||||
const saveShortcut = "Ctrl + S";
|
||||
const deleteShortcut = "Backspace";
|
||||
const interactionShortcut = "Ctrl + K";
|
||||
const undoShortcut = "Ctrl + Z";
|
||||
const redoShortcut = "Ctrl + Y";
|
||||
|
||||
// Column Definitions: Defines the columns to be displayed.
|
||||
const [colDefs, setColDefs] = useState<(ColDef<any> | ColGroupDef<any>)[]>([
|
||||
{ headerName: "Functionality", field: "name", flex: 1, editable: false }, //This column will be twice as wide as the others
|
||||
{
|
||||
field: "shortcut",
|
||||
flex: 2,
|
||||
editable: false,
|
||||
},
|
||||
]);
|
||||
|
||||
const [nodesRowData, setNodesRowData] = useState([
|
||||
{
|
||||
name: "Advanced Settings Component",
|
||||
shortcut: advancedShortcut,
|
||||
},
|
||||
{
|
||||
name: "Minimize Component",
|
||||
shortcut: minizmizeShortcut,
|
||||
},
|
||||
{
|
||||
name: "Code Component",
|
||||
shortcut: codeShortcut,
|
||||
},
|
||||
{
|
||||
name: "Copy Component",
|
||||
shortcut: copyShortcut,
|
||||
},
|
||||
{
|
||||
name: "Duplicate Component",
|
||||
shortcut: duplicateShortcut,
|
||||
},
|
||||
{
|
||||
name: "Share Component",
|
||||
shortcut: shareShortcut,
|
||||
},
|
||||
{
|
||||
name: "Docs Component",
|
||||
shortcut: docsShortcut,
|
||||
},
|
||||
{
|
||||
name: "Save Component",
|
||||
shortcut: saveShortcut,
|
||||
},
|
||||
{
|
||||
name: "Delete Component",
|
||||
shortcut: deleteShortcut,
|
||||
},
|
||||
{
|
||||
name: "Open Playground",
|
||||
shortcut: interactionShortcut,
|
||||
},
|
||||
{
|
||||
name: "Undo",
|
||||
shortcut: undoShortcut,
|
||||
},
|
||||
{
|
||||
name: "Redo",
|
||||
shortcut: redoShortcut,
|
||||
},
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col gap-6">
|
||||
<div className="flex w-full items-center justify-between gap-4 space-y-0.5">
|
||||
<div className="flex w-full flex-col">
|
||||
<h2 className="flex items-center text-lg font-semibold tracking-tight">
|
||||
Shortcuts
|
||||
<ForwardedIconComponent
|
||||
name="Keyboard"
|
||||
className="ml-2 h-5 w-5 text-primary"
|
||||
/>
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Manage Shortcuts for quick access to
|
||||
frequently used actions.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-6 pb-8">
|
||||
<Card x-chunk="dashboard-04-chunk-2" className="pt-4">
|
||||
<CardContent>
|
||||
<TableComponent
|
||||
domLayout="autoHeight"
|
||||
pagination={false}
|
||||
columnDefs={colDefs}
|
||||
rowData={nodesRowData}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -10,7 +10,10 @@ import ApiKeysPage from "./pages/ApiKeysPage";
|
|||
import FlowPage from "./pages/FlowPage";
|
||||
import HomePage from "./pages/MainPage";
|
||||
import ComponentsComponent from "./pages/MainPage/components/components";
|
||||
import ProfileSettingsPage from "./pages/ProfileSettingsPage";
|
||||
import SettingsPage from "./pages/SettingsPage";
|
||||
import GeneralPage from "./pages/SettingsPage/pages/GeneralPage";
|
||||
import GlobalVariablesPage from "./pages/SettingsPage/pages/GlobalVariablesPage";
|
||||
import ShortcutsPage from "./pages/SettingsPage/pages/ShortcutsPage";
|
||||
import StorePage from "./pages/StorePage";
|
||||
import ViewPage from "./pages/ViewPage";
|
||||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||
|
|
@ -39,6 +42,19 @@ const Router = () => {
|
|||
element={<ComponentsComponent key="components" />}
|
||||
/>
|
||||
</Route>
|
||||
<Route
|
||||
path="/settings"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<SettingsPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
>
|
||||
<Route index element={<Navigate replace to={"general"} />} />
|
||||
<Route path="global-variables" element={<GlobalVariablesPage />} />
|
||||
<Route path="general" element={<GeneralPage />} />
|
||||
<Route path="shortcuts" element={<ShortcutsPage />} />
|
||||
</Route>
|
||||
<Route
|
||||
path="/store"
|
||||
element={
|
||||
|
|
@ -128,14 +144,6 @@ const Router = () => {
|
|||
/>
|
||||
|
||||
<Route path="/account">
|
||||
<Route
|
||||
path="settings"
|
||||
element={
|
||||
<ProtectedRoute>
|
||||
<ProfileSettingsPage />
|
||||
</ProtectedRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="delete"
|
||||
element={
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
addVersionToDuplicates,
|
||||
createFlowComponent,
|
||||
createNewFlow,
|
||||
extractFieldsFromComponenents,
|
||||
processDataFromFlow,
|
||||
processFlows,
|
||||
} from "../utils/reactflowUtils";
|
||||
|
|
@ -92,6 +93,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
);
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
ComponentFields: extractFieldsFromComponenents({
|
||||
...state.data,
|
||||
["saved_components"]: data,
|
||||
}),
|
||||
}));
|
||||
set({ isLoading: false });
|
||||
resolve();
|
||||
|
|
@ -207,6 +212,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
set({ isLoading: false });
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
ComponentFields: extractFieldsFromComponenents({
|
||||
...state.data,
|
||||
["saved_components"]: data,
|
||||
}),
|
||||
}));
|
||||
}, 200);
|
||||
// addFlowToLocalState(newFlow);
|
||||
|
|
@ -229,6 +238,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
set({ isLoading: false });
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
ComponentFields: extractFieldsFromComponenents({
|
||||
...state.data,
|
||||
["saved_components"]: data,
|
||||
}),
|
||||
}));
|
||||
|
||||
// Return the id
|
||||
|
|
@ -258,6 +271,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
|||
set({ isLoading: false });
|
||||
useTypesStore.setState((state) => ({
|
||||
data: { ...state.data, ["saved_components"]: data },
|
||||
ComponentFields: extractFieldsFromComponenents({
|
||||
...state.data,
|
||||
["saved_components"]: data,
|
||||
}),
|
||||
}));
|
||||
resolve();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,30 +1,45 @@
|
|||
import { create } from "zustand";
|
||||
import { GlobalVariablesStore } from "../types/zustand/globalVariables";
|
||||
import { getUnavailableFields } from "../utils/utils";
|
||||
|
||||
export const useGlobalVariablesStore = create<GlobalVariablesStore>(
|
||||
(set, get) => ({
|
||||
unavaliableFields: {},
|
||||
setUnavaliableFields: (fields) => {
|
||||
set({ unavaliableFields: fields });
|
||||
},
|
||||
removeUnavaliableField: (field) => {
|
||||
const newFields = get().unavaliableFields;
|
||||
delete newFields[field];
|
||||
set({ unavaliableFields: newFields });
|
||||
},
|
||||
globalVariablesEntries: [],
|
||||
globalVariables: {},
|
||||
setGlobalVariables: (variables) => {
|
||||
set({
|
||||
globalVariables: variables,
|
||||
globalVariablesEntries: Object.keys(variables),
|
||||
unavaliableFields: getUnavailableFields(variables),
|
||||
});
|
||||
},
|
||||
addGlobalVariable: (name, id, type) => {
|
||||
const data = { id, type };
|
||||
addGlobalVariable: (name, id, type, default_fields) => {
|
||||
const data = { id, type, default_fields };
|
||||
const newVariables = { ...get().globalVariables, [name]: data };
|
||||
set({
|
||||
globalVariables: newVariables,
|
||||
globalVariablesEntries: Object.keys(newVariables),
|
||||
unavaliableFields: getUnavailableFields(newVariables),
|
||||
});
|
||||
},
|
||||
removeGlobalVariable: (name) => {
|
||||
removeGlobalVariable: async (name) => {
|
||||
const id = get().globalVariables[name]?.id;
|
||||
if (id === undefined) return;
|
||||
const newVariables = { ...get().globalVariables };
|
||||
delete newVariables[name];
|
||||
set({
|
||||
globalVariables: newVariables,
|
||||
globalVariablesEntries: Object.keys(newVariables),
|
||||
unavaliableFields: getUnavailableFields(newVariables),
|
||||
});
|
||||
},
|
||||
getVariableId: (name) => {
|
||||
|
|
|
|||
|
|
@ -2,11 +2,22 @@ import { create } from "zustand";
|
|||
import { getAll } from "../controllers/API";
|
||||
import { APIDataType } from "../types/api";
|
||||
import { TypesStoreType } from "../types/zustand/types";
|
||||
import { templatesGenerator, typesGenerator } from "../utils/reactflowUtils";
|
||||
import {
|
||||
extractFieldsFromComponenents,
|
||||
templatesGenerator,
|
||||
typesGenerator,
|
||||
} from "../utils/reactflowUtils";
|
||||
import useAlertStore from "./alertStore";
|
||||
import useFlowsManagerStore from "./flowsManagerStore";
|
||||
|
||||
export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||
ComponentFields: new Set(),
|
||||
setComponentFields: (fields) => {
|
||||
set({ ComponentFields: fields });
|
||||
},
|
||||
addComponentField: (field) => {
|
||||
set({ ComponentFields: get().ComponentFields.add(field) });
|
||||
},
|
||||
types: {},
|
||||
templates: {},
|
||||
data: {},
|
||||
|
|
@ -21,6 +32,10 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
|||
set((old) => ({
|
||||
types: typesGenerator(data),
|
||||
data: { ...old.data, ...data },
|
||||
ComponentFields: extractFieldsFromComponenents({
|
||||
...old.data,
|
||||
...data,
|
||||
}),
|
||||
templates: templatesGenerator(data),
|
||||
}));
|
||||
setLoading(false);
|
||||
|
|
@ -43,5 +58,6 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
|||
setData: (change: APIDataType | ((old: APIDataType) => APIDataType)) => {
|
||||
let newChange = typeof change === "function" ? change(get().data) : change;
|
||||
set({ data: newChange });
|
||||
get().setComponentFields(extractFieldsFromComponenents(newChange));
|
||||
},
|
||||
}));
|
||||
|
|
|
|||
21
src/frontend/src/style/ag-theme-shadcn.css
Normal file
21
src/frontend/src/style/ag-theme-shadcn.css
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* set the background color of many elements across the grid */
|
||||
.ag-theme-shadcn {
|
||||
--ag-foreground-color: hsl(var(--foreground));
|
||||
--ag-background-color: hsl(var(--background));
|
||||
--ag-secondary-foreground-color: hsl(var(--secondary-foreground));
|
||||
--ag-data-color: hsl(var(--foreground));
|
||||
--ag-header-foreground-color: hsl(var(--muted-foreground));
|
||||
--ag-header-background-color: hsl(var(--background));
|
||||
--ag-tooltip-background-color: hsl(var(--muted));
|
||||
--ag-disabled-foreground-color: hsl(var(--muted-foreground));
|
||||
--ag-border-color: hsl(var(--border));
|
||||
--ag-selected-row-background-color: hsl(var(--accent));
|
||||
--ag-menu-background-color: hsl(var(--accent));
|
||||
--ag-panel-background-color: hsl(var(--accent));
|
||||
--ag-row-hover-color: hsl(var(--accent));
|
||||
--ag-header-height: 2.5rem;
|
||||
}
|
||||
|
||||
.ag-theme-shadcn .ag-paging-panel {
|
||||
height: 3rem;
|
||||
}
|
||||
|
|
@ -28,6 +28,8 @@ export type InputComponentType = {
|
|||
optionButton?: (option: string) => ReactElement;
|
||||
selectedOption?: string;
|
||||
setSelectedOption?: (value: string) => void;
|
||||
selectedOptions?: string[];
|
||||
setSelectedOptions?: (value: string[]) => void;
|
||||
};
|
||||
export type ToggleComponentType = {
|
||||
enabled: boolean;
|
||||
|
|
@ -45,6 +47,7 @@ export type DropDownComponentType = {
|
|||
onSelect: (value: string) => void;
|
||||
editNode?: boolean;
|
||||
id?: string;
|
||||
children?: ReactNode;
|
||||
};
|
||||
export type ParameterComponentType = {
|
||||
data: NodeDataType;
|
||||
|
|
@ -72,15 +75,6 @@ export type InputListComponentType = {
|
|||
playgroundDisabled?: boolean;
|
||||
};
|
||||
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,31 @@
|
|||
export type GlobalVariablesStore = {
|
||||
globalVariablesEntries: Array<string>;
|
||||
globalVariables: { [name: string]: { id: string; type?: string } };
|
||||
globalVariables: {
|
||||
[name: string]: {
|
||||
id: string;
|
||||
type?: string;
|
||||
default_fields?: string[];
|
||||
value?: string;
|
||||
};
|
||||
};
|
||||
setGlobalVariables: (variables: {
|
||||
[name: string]: { id: string; type?: string };
|
||||
[name: string]: {
|
||||
id: string;
|
||||
type?: string;
|
||||
default_fields?: string[];
|
||||
value?: string;
|
||||
};
|
||||
}) => void;
|
||||
addGlobalVariable: (name: string, id: string, type?: string) => void;
|
||||
removeGlobalVariable: (name: string) => void;
|
||||
addGlobalVariable: (
|
||||
name: string,
|
||||
id: string,
|
||||
type?: string,
|
||||
default_fields?: string[],
|
||||
value?: string
|
||||
) => void;
|
||||
removeGlobalVariable: (name: string) => Promise<void>;
|
||||
getVariableId: (name: string) => string | undefined;
|
||||
unavaliableFields: {[name: string]: string};
|
||||
setUnavaliableFields: (fields: {[name: string]: string}) => void;
|
||||
removeUnavaliableField: (field: string) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -8,4 +8,7 @@ export type TypesStoreType = {
|
|||
data: APIDataType;
|
||||
setData: (newState: {}) => void;
|
||||
getTypes: () => Promise<void>;
|
||||
ComponentFields: Set<string>;
|
||||
setComponentFields: (fields: Set<string>) => void;
|
||||
addComponentField: (field: string) => void;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1234,6 +1234,22 @@ export function templatesGenerator(data: APIObjectType) {
|
|||
}, {});
|
||||
}
|
||||
|
||||
export function extractFieldsFromComponenents(data: APIObjectType) {
|
||||
const fields = new Set<string>();
|
||||
Object.keys(data).forEach((key) => {
|
||||
Object.keys(data[key]).forEach((kind) => {
|
||||
Object.keys(data[key][kind].template).forEach((field) => {
|
||||
if (
|
||||
data[key][kind].template[field].display_name &&
|
||||
data[key][kind].template[field].show
|
||||
)
|
||||
fields.add(data[key][kind].template[field].display_name!);
|
||||
});
|
||||
});
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
|
||||
export function downloadFlow(
|
||||
flow: FlowType,
|
||||
flowName: string,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
ArrowUpToLine,
|
||||
Bell,
|
||||
Binary,
|
||||
Blocks,
|
||||
BookMarked,
|
||||
BookmarkPlus,
|
||||
Bot,
|
||||
|
|
@ -67,6 +68,7 @@ import {
|
|||
Home,
|
||||
Info,
|
||||
Key,
|
||||
Keyboard,
|
||||
Laptop2,
|
||||
Layers,
|
||||
Link,
|
||||
|
|
@ -86,6 +88,7 @@ import {
|
|||
MoreHorizontal,
|
||||
Network,
|
||||
Package2,
|
||||
Palette,
|
||||
Paperclip,
|
||||
Pencil,
|
||||
PencilLine,
|
||||
|
|
@ -108,6 +111,7 @@ import {
|
|||
Share2,
|
||||
Shield,
|
||||
Sliders,
|
||||
SlidersHorizontal,
|
||||
Snowflake,
|
||||
Sparkles,
|
||||
Square,
|
||||
|
|
@ -376,6 +380,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
tools: Hammer,
|
||||
advanced: Laptop2,
|
||||
chat: MessageCircle,
|
||||
Keyboard: Keyboard,
|
||||
embeddings: Binary,
|
||||
saved_components: GradientSave,
|
||||
documentloaders: Paperclip,
|
||||
|
|
@ -414,6 +419,9 @@ export const nodeIconsLucide: iconsType = {
|
|||
MoonIcon,
|
||||
Bell,
|
||||
ChevronLeft,
|
||||
SlidersHorizontal,
|
||||
Palette,
|
||||
Blocks,
|
||||
ChevronDown,
|
||||
ArrowLeft,
|
||||
Shield,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,20 @@ export function toTitleCase(
|
|||
.join(" ");
|
||||
}
|
||||
|
||||
export function getUnavailableFields(variables: {
|
||||
[key: string]: { default_fields?: string[] };
|
||||
}): {[name: string]: string} {
|
||||
const unVariables:{[name: string]: string} = {};
|
||||
Object.keys(variables).forEach((key) => {
|
||||
if (variables[key].default_fields) {
|
||||
variables[key].default_fields!.forEach((field) => {
|
||||
unVariables[field] = key;
|
||||
});
|
||||
}
|
||||
});
|
||||
return unVariables;
|
||||
}
|
||||
|
||||
export const upperCaseWords: string[] = ["llm", "uri"];
|
||||
export function checkUpperWords(str: string): string {
|
||||
const words = str.split(" ").map((word) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue