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 uuid import UUID
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException
|
from fastapi import APIRouter, Depends, HTTPException
|
||||||
|
|
@ -37,7 +37,11 @@ def create_variable(
|
||||||
variable_dict["user_id"] = current_user.id
|
variable_dict["user_id"] = current_user.id
|
||||||
|
|
||||||
db_variable = Variable.model_validate(variable_dict)
|
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")
|
raise HTTPException(status_code=400, detail="Variable value cannot be empty")
|
||||||
encrypted = auth_utils.encrypt_api_key(db_variable.value, settings_service=settings_service)
|
encrypted = auth_utils.encrypt_api_key(db_variable.value, settings_service=settings_service)
|
||||||
db_variable.value = encrypted
|
db_variable.value = encrypted
|
||||||
|
|
@ -85,7 +89,7 @@ def update_variable(
|
||||||
variable_data = variable.model_dump(exclude_unset=True)
|
variable_data = variable.model_dump(exclude_unset=True)
|
||||||
for key, value in variable_data.items():
|
for key, value in variable_data.items():
|
||||||
setattr(db_variable, key, value)
|
setattr(db_variable, key, value)
|
||||||
db_variable.updated_at = datetime.utcnow()
|
db_variable.updated_at = datetime.now(timezone.utc)
|
||||||
session.commit()
|
session.commit()
|
||||||
session.refresh(db_variable)
|
session.refresh(db_variable)
|
||||||
return db_variable
|
return db_variable
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from langchain.llms.base import BaseLanguageModel
|
|
||||||
from langchain_openai import AzureChatOpenAI
|
from langchain_openai import AzureChatOpenAI
|
||||||
from pydantic.v1 import SecretStr
|
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.constants import STREAM_INFO_TEXT
|
||||||
from langflow.base.models.model import LCModelComponent
|
from langflow.base.models.model import LCModelComponent
|
||||||
from langflow.field_typing import BaseLanguageModel, Text
|
from langflow.field_typing import Text
|
||||||
|
|
||||||
|
|
||||||
class ChatLiteLLMModelComponent(LCModelComponent):
|
class ChatLiteLLMModelComponent(LCModelComponent):
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ from typing import Literal, Optional, cast
|
||||||
from langchain_core.documents import Document
|
from langchain_core.documents import Document
|
||||||
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
from langchain_core.messages import AIMessage, BaseMessage, HumanMessage
|
||||||
from pydantic import BaseModel, model_validator
|
from pydantic import BaseModel, model_validator
|
||||||
|
from langchain_core.messages import HumanMessage, AIMessage
|
||||||
|
|
||||||
|
|
||||||
class Record(BaseModel):
|
class Record(BaseModel):
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, List, Optional
|
||||||
from uuid import UUID, uuid4
|
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:
|
if TYPE_CHECKING:
|
||||||
from langflow.services.database.models.user.model import User
|
from langflow.services.database.models.user.model import User
|
||||||
|
|
@ -13,8 +13,9 @@ def utc_now():
|
||||||
|
|
||||||
|
|
||||||
class VariableBase(SQLModel):
|
class VariableBase(SQLModel):
|
||||||
name: Optional[str] = Field(None, description="Name of the variable")
|
name: str = Field(description="Name of the variable")
|
||||||
value: Optional[str] = Field(None, description="Encrypted value 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")
|
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),
|
sa_column=Column(DateTime(timezone=True), nullable=True),
|
||||||
description="Last update time of the variable",
|
description="Last update time of the variable",
|
||||||
)
|
)
|
||||||
|
default_fields: Optional[List[str]] = Field(sa_column=Column(JSON))
|
||||||
# foreign key to user table
|
# foreign key to user table
|
||||||
user_id: UUID = Field(description="User ID associated with this variable", foreign_key="user.id")
|
user_id: UUID = Field(description="User ID associated with this variable", foreign_key="user.id")
|
||||||
user: "User" = Relationship(back_populates="variables")
|
user: "User" = Relationship(back_populates="variables")
|
||||||
|
|
@ -50,9 +52,11 @@ class VariableRead(SQLModel):
|
||||||
id: UUID
|
id: UUID
|
||||||
name: Optional[str] = Field(None, description="Name of the variable")
|
name: Optional[str] = Field(None, description="Name of the variable")
|
||||||
type: Optional[str] = Field(None, description="Type 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):
|
class VariableUpdate(SQLModel):
|
||||||
id: UUID # Include the ID for updating
|
id: UUID # Include the ID for updating
|
||||||
name: Optional[str] = Field(None, description="Name of the variable")
|
name: Optional[str] = Field(None, description="Name of the variable")
|
||||||
value: Optional[str] = Field(None, description="Encrypted value 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:
|
if var in os.environ:
|
||||||
logger.debug(f"Creating {var} variable from environment.")
|
logger.debug(f"Creating {var} variable from environment.")
|
||||||
try:
|
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:
|
except Exception as e:
|
||||||
logger.error(f"Error creating {var} variable: {e}")
|
logger.error(f"Error creating {var} variable: {e}")
|
||||||
|
|
||||||
|
|
@ -91,6 +98,7 @@ class VariableService(Service):
|
||||||
user_id: Union[UUID, str],
|
user_id: Union[UUID, str],
|
||||||
name: str,
|
name: str,
|
||||||
value: str,
|
value: str,
|
||||||
|
default_fields: list[str] = [],
|
||||||
_type: str = "Generic",
|
_type: str = "Generic",
|
||||||
session: Session = Depends(get_session),
|
session: Session = Depends(get_session),
|
||||||
):
|
):
|
||||||
|
|
@ -98,6 +106,7 @@ class VariableService(Service):
|
||||||
name=name,
|
name=name,
|
||||||
type=_type,
|
type=_type,
|
||||||
value=auth_utils.encrypt_api_key(value, settings_service=self.settings_service),
|
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})
|
variable = Variable.model_validate(variable_base, from_attributes=True, update={"user_id": user_id})
|
||||||
session.add(variable)
|
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",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"ace-builds": "^1.24.1",
|
"ace-builds": "^1.24.1",
|
||||||
|
"ag-grid-community": "^31.2.1",
|
||||||
"ag-grid-react": "^31.2.1",
|
"ag-grid-react": "^31.2.1",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
|
|
@ -49,10 +50,10 @@
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"openseadragon": "^4.1.1",
|
"openseadragon": "^4.1.1",
|
||||||
"playwright": "^1.42.0",
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.21",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
"react-cookie": "^4.1.1",
|
"react-cookie": "^4.1.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.21",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-laag": "^2.0.5",
|
"react-laag": "^2.0.5",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
"@tailwindcss/line-clamp": "^0.4.4",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"ace-builds": "^1.24.1",
|
"ace-builds": "^1.24.1",
|
||||||
|
"ag-grid-community": "^31.2.1",
|
||||||
"ag-grid-react": "^31.2.1",
|
"ag-grid-react": "^31.2.1",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
|
|
@ -44,10 +45,10 @@
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"openseadragon": "^4.1.1",
|
"openseadragon": "^4.1.1",
|
||||||
"playwright": "^1.42.0",
|
"playwright": "^1.42.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.21",
|
||||||
"react-ace": "^10.1.0",
|
"react-ace": "^10.1.0",
|
||||||
"react-cookie": "^4.1.1",
|
"react-cookie": "^4.1.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.21",
|
||||||
"react-error-boundary": "^4.0.11",
|
"react-error-boundary": "^4.0.11",
|
||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-laag": "^2.0.5",
|
"react-laag": "^2.0.5",
|
||||||
|
|
@ -74,7 +75,7 @@
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"serve": "vite preview",
|
"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"
|
"type-check": "tsc --noEmit --pretty --project tsconfig.json && vite"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import useFlowsManagerStore from "./stores/flowsManagerStore";
|
||||||
import { useGlobalVariablesStore } from "./stores/globalVariables";
|
import { useGlobalVariablesStore } from "./stores/globalVariables";
|
||||||
import { useStoreStore } from "./stores/storeStore";
|
import { useStoreStore } from "./stores/storeStore";
|
||||||
import { useTypesStore } from "./stores/typesStore";
|
import { useTypesStore } from "./stores/typesStore";
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const removeFromTempNotificationList = useAlertStore(
|
const removeFromTempNotificationList = useAlertStore(
|
||||||
(state) => state.removeFromTempNotificationList
|
(state) => state.removeFromTempNotificationList
|
||||||
|
|
@ -48,6 +47,9 @@ export default function App() {
|
||||||
const setGlobalVariables = useGlobalVariablesStore(
|
const setGlobalVariables = useGlobalVariablesStore(
|
||||||
(state) => state.setGlobalVariables
|
(state) => state.setGlobalVariables
|
||||||
);
|
);
|
||||||
|
const setUnavailableFields = useGlobalVariablesStore(
|
||||||
|
(state) => state.setUnavaliableFields
|
||||||
|
);
|
||||||
const checkHasStore = useStoreStore((state) => state.checkHasStore);
|
const checkHasStore = useStoreStore((state) => state.checkHasStore);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const dark = useDarkStore((state) => state.dark);
|
const dark = useDarkStore((state) => state.dark);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { registerGlobalVariable } from "../../controllers/API";
|
||||||
import BaseModal from "../../modals/baseModal";
|
import BaseModal from "../../modals/baseModal";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||||
|
import { useTypesStore } from "../../stores/typesStore";
|
||||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
import { ResponseErrorDetailAPI } from "../../types/api";
|
||||||
import ForwardedIconComponent from "../genericIconComponent";
|
import ForwardedIconComponent from "../genericIconComponent";
|
||||||
import InputComponent from "../inputComponent";
|
import InputComponent from "../inputComponent";
|
||||||
|
|
@ -16,25 +17,42 @@ import { Textarea } from "../ui/textarea";
|
||||||
export default function AddNewVariableButton({ children }): JSX.Element {
|
export default function AddNewVariableButton({ children }): JSX.Element {
|
||||||
const [key, setKey] = useState("");
|
const [key, setKey] = useState("");
|
||||||
const [value, setValue] = 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 [open, setOpen] = useState(false);
|
||||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
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(
|
const addGlobalVariable = useGlobalVariablesStore(
|
||||||
(state) => state.addGlobalVariable
|
(state) => state.addGlobalVariable
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSaveVariable() {
|
function handleSaveVariable() {
|
||||||
let data: { name: string; value: string; type?: string } = {
|
let data: {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
type?: string;
|
||||||
|
default_fields?: string[];
|
||||||
|
} = {
|
||||||
name: key,
|
name: key,
|
||||||
type,
|
type,
|
||||||
value,
|
value,
|
||||||
|
default_fields: fields,
|
||||||
};
|
};
|
||||||
registerGlobalVariable(data)
|
registerGlobalVariable(data)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const { name, id, type } = res.data;
|
const { name, id, type } = res.data;
|
||||||
addGlobalVariable(name, id, type);
|
addGlobalVariable(name, id, type, fields);
|
||||||
setKey("");
|
setKey("");
|
||||||
setValue("");
|
setValue("");
|
||||||
setType("");
|
setType("");
|
||||||
|
setFields([]);
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
|
@ -89,6 +107,14 @@ export default function AddNewVariableButton({ children }): JSX.Element {
|
||||||
placeholder="Insert a value for the variable..."
|
placeholder="Insert a value for the variable..."
|
||||||
className="w-full resize-none custom-scroll"
|
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>
|
</div>
|
||||||
</BaseModal.Content>
|
</BaseModal.Content>
|
||||||
<BaseModal.Footer>
|
<BaseModal.Footer>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { PopoverAnchor } from "@radix-ui/react-popover";
|
||||||
import { useRef, useState } from "react";
|
import { useRef, useState } from "react";
|
||||||
import { DropDownComponentType } from "../../types/components";
|
import { DropDownComponentType } from "../../types/components";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
|
|
@ -13,6 +14,7 @@ import {
|
||||||
} from "../ui/command";
|
} from "../ui/command";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
|
PopoverContent,
|
||||||
PopoverContentWithoutPortal,
|
PopoverContentWithoutPortal,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "../ui/popover";
|
} from "../ui/popover";
|
||||||
|
|
@ -25,50 +27,63 @@ export default function Dropdown({
|
||||||
onSelect,
|
onSelect,
|
||||||
editNode = false,
|
editNode = false,
|
||||||
id = "",
|
id = "",
|
||||||
|
children,
|
||||||
}: DropDownComponentType): JSX.Element {
|
}: DropDownComponentType): JSX.Element {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(children ? true : false);
|
||||||
|
|
||||||
const refButton = useRef<HTMLButtonElement>(null);
|
const refButton = useRef<HTMLButtonElement>(null);
|
||||||
|
|
||||||
|
const PopoverContentDropdown = children
|
||||||
|
? PopoverContent
|
||||||
|
: PopoverContentWithoutPortal;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{Object.keys(options)?.length > 0 ? (
|
{Object.keys(options)?.length > 0 ? (
|
||||||
<>
|
<>
|
||||||
<Popover open={open} onOpenChange={setOpen}>
|
<Popover open={open} onOpenChange={children ? () => {} : setOpen}>
|
||||||
<PopoverTrigger asChild>
|
{children ? (
|
||||||
<Button
|
<PopoverAnchor>{children}</PopoverAnchor>
|
||||||
disabled={disabled}
|
) : (
|
||||||
variant="primary"
|
<PopoverTrigger asChild>
|
||||||
size="xs"
|
<Button
|
||||||
role="combobox"
|
disabled={disabled}
|
||||||
ref={refButton}
|
variant="primary"
|
||||||
aria-expanded={open}
|
size="xs"
|
||||||
data-testid={`${id ?? ""}`}
|
role="combobox"
|
||||||
className={cn(
|
ref={refButton}
|
||||||
editNode
|
aria-expanded={open}
|
||||||
? "dropdown-component-outline"
|
data-testid={`${id ?? ""}`}
|
||||||
: "dropdown-component-false-outline",
|
className={cn(
|
||||||
"w-full justify-between font-normal",
|
editNode
|
||||||
editNode ? "input-edit-node" : "py-2"
|
? "dropdown-component-outline"
|
||||||
)}
|
: "dropdown-component-false-outline",
|
||||||
>
|
"w-full justify-between font-normal",
|
||||||
<span data-testid={`value-dropdown-` + id}>
|
editNode ? "input-edit-node" : "py-2"
|
||||||
{value &&
|
)}
|
||||||
value !== "" &&
|
>
|
||||||
options.find((option) => option === value)
|
<span data-testid={`value-dropdown-` + id}>
|
||||||
? options.find((option) => option === value)
|
{value &&
|
||||||
: "Choose an option..."}
|
value !== "" &&
|
||||||
</span>
|
options.find((option) => option === value)
|
||||||
|
? options.find((option) => option === value)
|
||||||
|
: "Choose an option..."}
|
||||||
|
</span>
|
||||||
|
|
||||||
<ForwardedIconComponent
|
<ForwardedIconComponent
|
||||||
name="ChevronsUpDown"
|
name="ChevronsUpDown"
|
||||||
className="ml-2 h-4 w-4 shrink-0 opacity-50"
|
className="ml-2 h-4 w-4 shrink-0 opacity-50"
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
<PopoverContentWithoutPortal
|
)}
|
||||||
className="nocopy nowheel nopan nodelete nodrag noundo w-full p-0"
|
<PopoverContentDropdown
|
||||||
style={{ minWidth: refButton?.current?.clientWidth ?? "200px" }}
|
className="nocopy nowheel nopan nodelete nodrag noundo p-0"
|
||||||
|
style={
|
||||||
|
children
|
||||||
|
? {}
|
||||||
|
: { minWidth: refButton?.current?.clientWidth ?? "200px" }
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Command>
|
<Command>
|
||||||
<CommandInput placeholder="Search options..." className="h-9" />
|
<CommandInput placeholder="Search options..." className="h-9" />
|
||||||
|
|
@ -98,7 +113,7 @@ export default function Dropdown({
|
||||||
</CommandGroup>
|
</CommandGroup>
|
||||||
</CommandList>
|
</CommandList>
|
||||||
</Command>
|
</Command>
|
||||||
</PopoverContentWithoutPortal>
|
</PopoverContentDropdown>
|
||||||
</Popover>
|
</Popover>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { gradients } from "../../utils/styleUtils";
|
||||||
|
|
||||||
export default function GradientChooserComponent({ value, onChange }) {
|
export default function GradientChooserComponent({ value, onChange }) {
|
||||||
return (
|
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) => (
|
{gradients.map((gradient, idx) => (
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
||||||
|
|
@ -168,50 +168,56 @@ export default function Header(): JSX.Element {
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{!autoLogin && (
|
|
||||||
<>
|
<>
|
||||||
<Separator orientation="vertical" />
|
<Separator orientation="vertical" />
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<button
|
<button
|
||||||
className={
|
className={
|
||||||
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
"h-7 w-7 rounded-full focus-visible:outline-0 " +
|
||||||
(userData?.profile_image ??
|
(userData?.profile_image ??
|
||||||
gradients[
|
(userData?.id
|
||||||
parseInt(userData?.id ?? "", 30) % gradients.length
|
? gradients[
|
||||||
])
|
parseInt(userData?.id ?? "", 30) % gradients.length
|
||||||
}
|
]
|
||||||
/>
|
: "bg-gray-500"))
|
||||||
</DropdownMenuTrigger>
|
}
|
||||||
<DropdownMenuContent>
|
/>
|
||||||
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuContent>
|
||||||
{isAdmin && (
|
<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
|
<DropdownMenuItem
|
||||||
className="cursor-pointer"
|
className="cursor-pointer"
|
||||||
onClick={() => navigate("/admin")}
|
onClick={() => {
|
||||||
|
logout();
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
Admin Page
|
Sign Out
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
)}
|
</>
|
||||||
<DropdownMenuItem
|
)}
|
||||||
className="cursor-pointer"
|
</DropdownMenuContent>
|
||||||
onClick={() => navigate("/account/settings")}
|
</DropdownMenu>
|
||||||
>
|
</>
|
||||||
Profile Settings
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
className="cursor-pointer"
|
|
||||||
onClick={() => {
|
|
||||||
logout();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Sign Out
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,8 @@ export default function InputComponent({
|
||||||
optionsIcon = "ChevronsUpDown",
|
optionsIcon = "ChevronsUpDown",
|
||||||
selectedOption,
|
selectedOption,
|
||||||
setSelectedOption,
|
setSelectedOption,
|
||||||
|
selectedOptions = [],
|
||||||
|
setSelectedOptions,
|
||||||
options = [],
|
options = [],
|
||||||
optionsPlaceholder = "Search options...",
|
optionsPlaceholder = "Search options...",
|
||||||
optionsButton,
|
optionsButton,
|
||||||
|
|
@ -101,13 +103,18 @@ export default function InputComponent({
|
||||||
value={
|
value={
|
||||||
(selectedOption !== "" || !onChange) && setSelectedOption
|
(selectedOption !== "" || !onChange) && setSelectedOption
|
||||||
? selectedOption
|
? selectedOption
|
||||||
|
: (selectedOptions?.length !== 0 || !onChange) &&
|
||||||
|
setSelectedOptions
|
||||||
|
? selectedOptions?.join(", ")
|
||||||
: value
|
: value
|
||||||
}
|
}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
(selectedOption !== "" || !onChange) &&
|
(((selectedOption !== "" || !onChange) &&
|
||||||
setSelectedOption &&
|
setSelectedOption) ||
|
||||||
|
((selectedOptions?.length !== 0 || !onChange) &&
|
||||||
|
setSelectedOptions)) &&
|
||||||
setShowOptions(true);
|
setShowOptions(true);
|
||||||
}}
|
}}
|
||||||
required={required}
|
required={required}
|
||||||
|
|
@ -119,9 +126,11 @@ export default function InputComponent({
|
||||||
? " text-clip password "
|
? " text-clip password "
|
||||||
: "",
|
: "",
|
||||||
editNode ? " input-edit-node " : "",
|
editNode ? " input-edit-node " : "",
|
||||||
password && setSelectedOption ? "pr-[62.9px]" : "",
|
password && (setSelectedOption || setSelectedOptions)
|
||||||
(!password && setSelectedOption) ||
|
? "pr-[62.9px]"
|
||||||
(password && !setSelectedOption)
|
: "",
|
||||||
|
(!password && (setSelectedOption || setSelectedOptions)) ||
|
||||||
|
(password && !(setSelectedOption || setSelectedOptions))
|
||||||
? "pr-8"
|
? "pr-8"
|
||||||
: "",
|
: "",
|
||||||
|
|
||||||
|
|
@ -164,7 +173,10 @@ export default function InputComponent({
|
||||||
>
|
>
|
||||||
<Command
|
<Command
|
||||||
filter={(value, search) => {
|
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 1; // ensures items arent filtered
|
||||||
return 0;
|
return 0;
|
||||||
}}
|
}}
|
||||||
|
|
@ -184,7 +196,15 @@ export default function InputComponent({
|
||||||
? ""
|
? ""
|
||||||
: currentValue
|
: 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">
|
<div className="group flex w-full items-center justify-between">
|
||||||
|
|
@ -192,7 +212,8 @@ export default function InputComponent({
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative mr-2 h-4 w-4",
|
"relative mr-2 h-4 w-4",
|
||||||
selectedOption === option
|
selectedOption === option ||
|
||||||
|
selectedOptions?.includes(option)
|
||||||
? "opacity-100"
|
? "opacity-100"
|
||||||
: "opacity-0"
|
: "opacity-0"
|
||||||
)}
|
)}
|
||||||
|
|
@ -228,12 +249,16 @@ export default function InputComponent({
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
|
"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"
|
: "hidden"
|
||||||
)}
|
)}
|
||||||
onClick={
|
onClick={
|
||||||
(selectedOption !== "" || !onChange) && setSelectedOption
|
((selectedOption !== "" || !onChange) && setSelectedOption) ||
|
||||||
|
((selectedOptions?.length !== 0 || !onChange) &&
|
||||||
|
setSelectedOptions)
|
||||||
? (e) => {
|
? (e) => {
|
||||||
setShowOptions((old) => !old);
|
setShowOptions((old) => !old);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -245,7 +270,7 @@ export default function InputComponent({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{setSelectedOption && (
|
{(setSelectedOption || setSelectedOptions) && (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
password && selectedOption === "" ? "right-8" : "right-0",
|
password && selectedOption === "" ? "right-8" : "right-0",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import { deleteGlobalVariable } from "../../controllers/API";
|
||||||
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
|
import DeleteConfirmationModal from "../../modals/DeleteConfirmationModal";
|
||||||
import useAlertStore from "../../stores/alertStore";
|
import useAlertStore from "../../stores/alertStore";
|
||||||
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
import { useGlobalVariablesStore } from "../../stores/globalVariables";
|
||||||
import { ResponseErrorDetailAPI } from "../../types/api";
|
|
||||||
import { InputGlobalComponentType } from "../../types/components";
|
import { InputGlobalComponentType } from "../../types/components";
|
||||||
import { cn } from "../../utils/utils";
|
import { cn } from "../../utils/utils";
|
||||||
import AddNewVariableButton from "../addNewVariableButtonComponent/addNewVariableButton";
|
import AddNewVariableButton from "../addNewVariableButtonComponent/addNewVariableButton";
|
||||||
|
|
@ -24,6 +23,9 @@ export default function InputGlobalComponent({
|
||||||
);
|
);
|
||||||
|
|
||||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||||
|
const unavaliableFields = useGlobalVariablesStore(
|
||||||
|
(state) => state.unavaliableFields
|
||||||
|
);
|
||||||
const removeGlobalVariable = useGlobalVariablesStore(
|
const removeGlobalVariable = useGlobalVariablesStore(
|
||||||
(state) => state.removeGlobalVariable
|
(state) => state.removeGlobalVariable
|
||||||
);
|
);
|
||||||
|
|
@ -35,16 +37,32 @@ export default function InputGlobalComponent({
|
||||||
!globalVariablesEntries.includes(data.node?.template[name].value) &&
|
!globalVariablesEntries.includes(data.node?.template[name].value) &&
|
||||||
data.node?.template[name].load_from_db
|
data.node?.template[name].load_from_db
|
||||||
) {
|
) {
|
||||||
onChange("");
|
setTimeout(() => {
|
||||||
setDb(false);
|
onChange("");
|
||||||
|
setDb(false);
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
}, [globalVariablesEntries]);
|
}, [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);
|
const id = getVariableId(key);
|
||||||
if (id !== undefined) {
|
if (id !== undefined) {
|
||||||
deleteGlobalVariable(id)
|
await deleteGlobalVariable(id)
|
||||||
.then((_) => {
|
.then(() => {
|
||||||
removeGlobalVariable(key);
|
removeGlobalVariable(key);
|
||||||
if (
|
if (
|
||||||
data?.node?.template[name].value === key &&
|
data?.node?.template[name].value === key &&
|
||||||
|
|
@ -54,11 +72,10 @@ export default function InputGlobalComponent({
|
||||||
setDb(false);
|
setDb(false);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch(() => {
|
||||||
let responseError = error as ResponseErrorDetailAPI;
|
|
||||||
setErrorData({
|
setErrorData({
|
||||||
title: "Error deleting variable",
|
title: "Error deleting variable",
|
||||||
list: [responseError.response.data.detail ?? "Unknown error"],
|
list: [cn("ID not found for variable: ", key)],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -117,7 +134,8 @@ export default function InputGlobalComponent({
|
||||||
</DeleteConfirmationModal>
|
</DeleteConfirmationModal>
|
||||||
)}
|
)}
|
||||||
selectedOption={
|
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
|
? 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<{
|
export async function getGlobalVariables(): Promise<{
|
||||||
[key: string]: { id: string; type: string };
|
[key: string]: { id: string; type: string; default_fields: string[] };
|
||||||
}> {
|
}> {
|
||||||
const globalVariables = {};
|
const globalVariables = {};
|
||||||
(await api.get(`${BASE_URL_API}variables/`)).data.forEach((element) => {
|
(await api.get(`${BASE_URL_API}variables/`)).data.forEach((element) => {
|
||||||
globalVariables[element.name] = {
|
globalVariables[element.name] = {
|
||||||
id: element.id,
|
id: element.id,
|
||||||
type: element.type,
|
type: element.type,
|
||||||
|
default_fields: element.default_fields,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return globalVariables;
|
return globalVariables;
|
||||||
|
|
@ -878,20 +879,33 @@ export async function registerGlobalVariable({
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
type,
|
type,
|
||||||
|
default_fields = [],
|
||||||
}: {
|
}: {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
type?: string;
|
type?: string;
|
||||||
|
default_fields?: string[];
|
||||||
}): Promise<AxiosResponse<{ name: string; id: string; type: string }>> {
|
}): Promise<AxiosResponse<{ name: string; id: string; type: string }>> {
|
||||||
return await api.post(`${BASE_URL_API}variables/`, {
|
try {
|
||||||
name,
|
const response = await api.post(`${BASE_URL_API}variables/`, {
|
||||||
value,
|
name,
|
||||||
type,
|
value,
|
||||||
});
|
type,
|
||||||
|
default_fields: default_fields,
|
||||||
|
});
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteGlobalVariable(id: string) {
|
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(
|
export async function updateGlobalVariable(
|
||||||
|
|
@ -899,10 +913,16 @@ export async function updateGlobalVariable(
|
||||||
value: string,
|
value: string,
|
||||||
id: string
|
id: string
|
||||||
) {
|
) {
|
||||||
api.patch(`${BASE_URL_API}variables/${id}`, {
|
try {
|
||||||
name,
|
const response = api.patch(`${BASE_URL_API}variables/${id}`, {
|
||||||
value,
|
name,
|
||||||
});
|
value,
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getVerticesOrder(
|
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 FlowPage from "./pages/FlowPage";
|
||||||
import HomePage from "./pages/MainPage";
|
import HomePage from "./pages/MainPage";
|
||||||
import ComponentsComponent from "./pages/MainPage/components/components";
|
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 StorePage from "./pages/StorePage";
|
||||||
import ViewPage from "./pages/ViewPage";
|
import ViewPage from "./pages/ViewPage";
|
||||||
import DeleteAccountPage from "./pages/deleteAccountPage";
|
import DeleteAccountPage from "./pages/deleteAccountPage";
|
||||||
|
|
@ -39,6 +42,19 @@ const Router = () => {
|
||||||
element={<ComponentsComponent key="components" />}
|
element={<ComponentsComponent key="components" />}
|
||||||
/>
|
/>
|
||||||
</Route>
|
</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
|
<Route
|
||||||
path="/store"
|
path="/store"
|
||||||
element={
|
element={
|
||||||
|
|
@ -128,14 +144,6 @@ const Router = () => {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route path="/account">
|
<Route path="/account">
|
||||||
<Route
|
|
||||||
path="settings"
|
|
||||||
element={
|
|
||||||
<ProtectedRoute>
|
|
||||||
<ProfileSettingsPage />
|
|
||||||
</ProtectedRoute>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
path="delete"
|
path="delete"
|
||||||
element={
|
element={
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import {
|
||||||
addVersionToDuplicates,
|
addVersionToDuplicates,
|
||||||
createFlowComponent,
|
createFlowComponent,
|
||||||
createNewFlow,
|
createNewFlow,
|
||||||
|
extractFieldsFromComponenents,
|
||||||
processDataFromFlow,
|
processDataFromFlow,
|
||||||
processFlows,
|
processFlows,
|
||||||
} from "../utils/reactflowUtils";
|
} from "../utils/reactflowUtils";
|
||||||
|
|
@ -92,6 +93,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
);
|
);
|
||||||
useTypesStore.setState((state) => ({
|
useTypesStore.setState((state) => ({
|
||||||
data: { ...state.data, ["saved_components"]: data },
|
data: { ...state.data, ["saved_components"]: data },
|
||||||
|
ComponentFields: extractFieldsFromComponenents({
|
||||||
|
...state.data,
|
||||||
|
["saved_components"]: data,
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
set({ isLoading: false });
|
set({ isLoading: false });
|
||||||
resolve();
|
resolve();
|
||||||
|
|
@ -207,6 +212,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
set({ isLoading: false });
|
set({ isLoading: false });
|
||||||
useTypesStore.setState((state) => ({
|
useTypesStore.setState((state) => ({
|
||||||
data: { ...state.data, ["saved_components"]: data },
|
data: { ...state.data, ["saved_components"]: data },
|
||||||
|
ComponentFields: extractFieldsFromComponenents({
|
||||||
|
...state.data,
|
||||||
|
["saved_components"]: data,
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
}, 200);
|
}, 200);
|
||||||
// addFlowToLocalState(newFlow);
|
// addFlowToLocalState(newFlow);
|
||||||
|
|
@ -229,6 +238,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
set({ isLoading: false });
|
set({ isLoading: false });
|
||||||
useTypesStore.setState((state) => ({
|
useTypesStore.setState((state) => ({
|
||||||
data: { ...state.data, ["saved_components"]: data },
|
data: { ...state.data, ["saved_components"]: data },
|
||||||
|
ComponentFields: extractFieldsFromComponenents({
|
||||||
|
...state.data,
|
||||||
|
["saved_components"]: data,
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Return the id
|
// Return the id
|
||||||
|
|
@ -258,6 +271,10 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
|
||||||
set({ isLoading: false });
|
set({ isLoading: false });
|
||||||
useTypesStore.setState((state) => ({
|
useTypesStore.setState((state) => ({
|
||||||
data: { ...state.data, ["saved_components"]: data },
|
data: { ...state.data, ["saved_components"]: data },
|
||||||
|
ComponentFields: extractFieldsFromComponenents({
|
||||||
|
...state.data,
|
||||||
|
["saved_components"]: data,
|
||||||
|
}),
|
||||||
}));
|
}));
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,45 @@
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { GlobalVariablesStore } from "../types/zustand/globalVariables";
|
import { GlobalVariablesStore } from "../types/zustand/globalVariables";
|
||||||
|
import { getUnavailableFields } from "../utils/utils";
|
||||||
|
|
||||||
export const useGlobalVariablesStore = create<GlobalVariablesStore>(
|
export const useGlobalVariablesStore = create<GlobalVariablesStore>(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
|
unavaliableFields: {},
|
||||||
|
setUnavaliableFields: (fields) => {
|
||||||
|
set({ unavaliableFields: fields });
|
||||||
|
},
|
||||||
|
removeUnavaliableField: (field) => {
|
||||||
|
const newFields = get().unavaliableFields;
|
||||||
|
delete newFields[field];
|
||||||
|
set({ unavaliableFields: newFields });
|
||||||
|
},
|
||||||
globalVariablesEntries: [],
|
globalVariablesEntries: [],
|
||||||
globalVariables: {},
|
globalVariables: {},
|
||||||
setGlobalVariables: (variables) => {
|
setGlobalVariables: (variables) => {
|
||||||
set({
|
set({
|
||||||
globalVariables: variables,
|
globalVariables: variables,
|
||||||
globalVariablesEntries: Object.keys(variables),
|
globalVariablesEntries: Object.keys(variables),
|
||||||
|
unavaliableFields: getUnavailableFields(variables),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
addGlobalVariable: (name, id, type) => {
|
addGlobalVariable: (name, id, type, default_fields) => {
|
||||||
const data = { id, type };
|
const data = { id, type, default_fields };
|
||||||
const newVariables = { ...get().globalVariables, [name]: data };
|
const newVariables = { ...get().globalVariables, [name]: data };
|
||||||
set({
|
set({
|
||||||
globalVariables: newVariables,
|
globalVariables: newVariables,
|
||||||
globalVariablesEntries: Object.keys(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 };
|
const newVariables = { ...get().globalVariables };
|
||||||
delete newVariables[name];
|
delete newVariables[name];
|
||||||
set({
|
set({
|
||||||
globalVariables: newVariables,
|
globalVariables: newVariables,
|
||||||
globalVariablesEntries: Object.keys(newVariables),
|
globalVariablesEntries: Object.keys(newVariables),
|
||||||
|
unavaliableFields: getUnavailableFields(newVariables),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
getVariableId: (name) => {
|
getVariableId: (name) => {
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,22 @@ import { create } from "zustand";
|
||||||
import { getAll } from "../controllers/API";
|
import { getAll } from "../controllers/API";
|
||||||
import { APIDataType } from "../types/api";
|
import { APIDataType } from "../types/api";
|
||||||
import { TypesStoreType } from "../types/zustand/types";
|
import { TypesStoreType } from "../types/zustand/types";
|
||||||
import { templatesGenerator, typesGenerator } from "../utils/reactflowUtils";
|
import {
|
||||||
|
extractFieldsFromComponenents,
|
||||||
|
templatesGenerator,
|
||||||
|
typesGenerator,
|
||||||
|
} from "../utils/reactflowUtils";
|
||||||
import useAlertStore from "./alertStore";
|
import useAlertStore from "./alertStore";
|
||||||
import useFlowsManagerStore from "./flowsManagerStore";
|
import useFlowsManagerStore from "./flowsManagerStore";
|
||||||
|
|
||||||
export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
|
ComponentFields: new Set(),
|
||||||
|
setComponentFields: (fields) => {
|
||||||
|
set({ ComponentFields: fields });
|
||||||
|
},
|
||||||
|
addComponentField: (field) => {
|
||||||
|
set({ ComponentFields: get().ComponentFields.add(field) });
|
||||||
|
},
|
||||||
types: {},
|
types: {},
|
||||||
templates: {},
|
templates: {},
|
||||||
data: {},
|
data: {},
|
||||||
|
|
@ -21,6 +32,10 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
set((old) => ({
|
set((old) => ({
|
||||||
types: typesGenerator(data),
|
types: typesGenerator(data),
|
||||||
data: { ...old.data, ...data },
|
data: { ...old.data, ...data },
|
||||||
|
ComponentFields: extractFieldsFromComponenents({
|
||||||
|
...old.data,
|
||||||
|
...data,
|
||||||
|
}),
|
||||||
templates: templatesGenerator(data),
|
templates: templatesGenerator(data),
|
||||||
}));
|
}));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
@ -43,5 +58,6 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
|
||||||
setData: (change: APIDataType | ((old: APIDataType) => APIDataType)) => {
|
setData: (change: APIDataType | ((old: APIDataType) => APIDataType)) => {
|
||||||
let newChange = typeof change === "function" ? change(get().data) : change;
|
let newChange = typeof change === "function" ? change(get().data) : change;
|
||||||
set({ data: newChange });
|
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;
|
optionButton?: (option: string) => ReactElement;
|
||||||
selectedOption?: string;
|
selectedOption?: string;
|
||||||
setSelectedOption?: (value: string) => void;
|
setSelectedOption?: (value: string) => void;
|
||||||
|
selectedOptions?: string[];
|
||||||
|
setSelectedOptions?: (value: string[]) => void;
|
||||||
};
|
};
|
||||||
export type ToggleComponentType = {
|
export type ToggleComponentType = {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
|
@ -45,6 +47,7 @@ export type DropDownComponentType = {
|
||||||
onSelect: (value: string) => void;
|
onSelect: (value: string) => void;
|
||||||
editNode?: boolean;
|
editNode?: boolean;
|
||||||
id?: string;
|
id?: string;
|
||||||
|
children?: ReactNode;
|
||||||
};
|
};
|
||||||
export type ParameterComponentType = {
|
export type ParameterComponentType = {
|
||||||
data: NodeDataType;
|
data: NodeDataType;
|
||||||
|
|
@ -72,15 +75,6 @@ export type InputListComponentType = {
|
||||||
playgroundDisabled?: boolean;
|
playgroundDisabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type InputGlobalComponentType = {
|
|
||||||
disabled: boolean;
|
|
||||||
onChange: (value: string) => void;
|
|
||||||
setDb: (value: boolean) => void;
|
|
||||||
name: string;
|
|
||||||
data: NodeDataType;
|
|
||||||
editNode?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type KeyPairListComponentType = {
|
export type KeyPairListComponentType = {
|
||||||
value: any;
|
value: any;
|
||||||
onChange: (value: Object[]) => void;
|
onChange: (value: Object[]) => void;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,31 @@
|
||||||
export type GlobalVariablesStore = {
|
export type GlobalVariablesStore = {
|
||||||
globalVariablesEntries: Array<string>;
|
globalVariablesEntries: Array<string>;
|
||||||
globalVariables: { [name: string]: { id: string; type?: string } };
|
globalVariables: {
|
||||||
|
[name: string]: {
|
||||||
|
id: string;
|
||||||
|
type?: string;
|
||||||
|
default_fields?: string[];
|
||||||
|
value?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
setGlobalVariables: (variables: {
|
setGlobalVariables: (variables: {
|
||||||
[name: string]: { id: string; type?: string };
|
[name: string]: {
|
||||||
|
id: string;
|
||||||
|
type?: string;
|
||||||
|
default_fields?: string[];
|
||||||
|
value?: string;
|
||||||
|
};
|
||||||
}) => void;
|
}) => void;
|
||||||
addGlobalVariable: (name: string, id: string, type?: string) => void;
|
addGlobalVariable: (
|
||||||
removeGlobalVariable: (name: string) => void;
|
name: string,
|
||||||
|
id: string,
|
||||||
|
type?: string,
|
||||||
|
default_fields?: string[],
|
||||||
|
value?: string
|
||||||
|
) => void;
|
||||||
|
removeGlobalVariable: (name: string) => Promise<void>;
|
||||||
getVariableId: (name: string) => string | undefined;
|
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;
|
data: APIDataType;
|
||||||
setData: (newState: {}) => void;
|
setData: (newState: {}) => void;
|
||||||
getTypes: () => Promise<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(
|
export function downloadFlow(
|
||||||
flow: FlowType,
|
flow: FlowType,
|
||||||
flowName: string,
|
flowName: string,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
ArrowUpToLine,
|
ArrowUpToLine,
|
||||||
Bell,
|
Bell,
|
||||||
Binary,
|
Binary,
|
||||||
|
Blocks,
|
||||||
BookMarked,
|
BookMarked,
|
||||||
BookmarkPlus,
|
BookmarkPlus,
|
||||||
Bot,
|
Bot,
|
||||||
|
|
@ -67,6 +68,7 @@ import {
|
||||||
Home,
|
Home,
|
||||||
Info,
|
Info,
|
||||||
Key,
|
Key,
|
||||||
|
Keyboard,
|
||||||
Laptop2,
|
Laptop2,
|
||||||
Layers,
|
Layers,
|
||||||
Link,
|
Link,
|
||||||
|
|
@ -86,6 +88,7 @@ import {
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
Network,
|
Network,
|
||||||
Package2,
|
Package2,
|
||||||
|
Palette,
|
||||||
Paperclip,
|
Paperclip,
|
||||||
Pencil,
|
Pencil,
|
||||||
PencilLine,
|
PencilLine,
|
||||||
|
|
@ -108,6 +111,7 @@ import {
|
||||||
Share2,
|
Share2,
|
||||||
Shield,
|
Shield,
|
||||||
Sliders,
|
Sliders,
|
||||||
|
SlidersHorizontal,
|
||||||
Snowflake,
|
Snowflake,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Square,
|
Square,
|
||||||
|
|
@ -376,6 +380,7 @@ export const nodeIconsLucide: iconsType = {
|
||||||
tools: Hammer,
|
tools: Hammer,
|
||||||
advanced: Laptop2,
|
advanced: Laptop2,
|
||||||
chat: MessageCircle,
|
chat: MessageCircle,
|
||||||
|
Keyboard: Keyboard,
|
||||||
embeddings: Binary,
|
embeddings: Binary,
|
||||||
saved_components: GradientSave,
|
saved_components: GradientSave,
|
||||||
documentloaders: Paperclip,
|
documentloaders: Paperclip,
|
||||||
|
|
@ -414,6 +419,9 @@ export const nodeIconsLucide: iconsType = {
|
||||||
MoonIcon,
|
MoonIcon,
|
||||||
Bell,
|
Bell,
|
||||||
ChevronLeft,
|
ChevronLeft,
|
||||||
|
SlidersHorizontal,
|
||||||
|
Palette,
|
||||||
|
Blocks,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
Shield,
|
Shield,
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,20 @@ export function toTitleCase(
|
||||||
.join(" ");
|
.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 const upperCaseWords: string[] = ["llm", "uri"];
|
||||||
export function checkUpperWords(str: string): string {
|
export function checkUpperWords(str: string): string {
|
||||||
const words = str.split(" ").map((word) => {
|
const words = str.split(" ").map((word) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue