Merge branch 'dev' of https://github.com/logspace-ai/langflow into dev
This commit is contained in:
commit
1b317ee4c9
31 changed files with 574 additions and 403 deletions
0
.githooks/pre-commit
Normal file → Executable file
0
.githooks/pre-commit
Normal file → Executable file
13
poetry.lock
generated
13
poetry.lock
generated
|
|
@ -7041,6 +7041,17 @@ files = [
|
|||
{file = "types_pytz-2023.3.0.1-py3-none-any.whl", hash = "sha256:65152e872137926bb67a8fe6cc9cfd794365df86650c5d5fdc7b167b0f38892e"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-pywin32"
|
||||
version = "306.0.0.4"
|
||||
description = "Typing stubs for pywin32"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "types-pywin32-306.0.0.4.tar.gz", hash = "sha256:ae4bbec80d535053236d4bebedf55f58dee89cf5883d277f0fa89e857f3ff337"},
|
||||
{file = "types_pywin32-306.0.0.4-py3-none-any.whl", hash = "sha256:f76a343ed6933008af85e158063963f923e54f2f461e697b2929b4178c7b77a1"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.12.11"
|
||||
|
|
@ -7773,4 +7784,4 @@ local = ["ctransformers", "llama-cpp-python", "sentence-transformers"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.9,<3.11"
|
||||
content-hash = "c877b4d713eef71815d858d30976ab21c42e5eadcc2df8159e940e03323681ee"
|
||||
content-hash = "a3a506d483c2db7169a9790090095d1764aa5be223d135c6fc3fc2768dfef36c"
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ types-python-jose = "^3.3.4.8"
|
|||
types-passlib = "^1.7.7.13"
|
||||
pytest-mock = "^3.11.1"
|
||||
pytest-xdist = "^3.3.1"
|
||||
types-pywin32 = "^306.0.0.4"
|
||||
|
||||
|
||||
[tool.poetry.extras]
|
||||
|
|
|
|||
|
|
@ -356,7 +356,7 @@ def superuser(
|
|||
with session_getter(db_manager) as session:
|
||||
from langflow.services.auth.utils import create_super_user
|
||||
|
||||
if create_super_user(session, username, password):
|
||||
if create_super_user(db=session, username=username, password=password):
|
||||
# Verify that the superuser was created
|
||||
from langflow.services.database.models.user.user import User
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class FrontendNodeRequest(FrontendNode):
|
|||
class ValidatePromptRequest(BaseModel):
|
||||
name: str
|
||||
template: str
|
||||
#optional for tweak call
|
||||
# optional for tweak call
|
||||
frontend_node: Optional[FrontendNodeRequest]
|
||||
|
||||
|
||||
|
|
@ -41,7 +41,7 @@ class CodeValidationResponse(BaseModel):
|
|||
|
||||
class PromptValidationResponse(BaseModel):
|
||||
input_variables: list
|
||||
#object return for tweak call
|
||||
# object return for tweak call
|
||||
frontend_node: FrontendNodeRequest | object
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -61,14 +61,13 @@ async def chat(
|
|||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
||||
except Exception as exc:
|
||||
logger.error(f"Error in chat websocket: {exc}")
|
||||
if isinstance(exc, HTTPException):
|
||||
exc = exc.detail
|
||||
messsage = exc.detail if isinstance(exc, HTTPException) else str(exc)
|
||||
if "Could not validate credentials" in str(exc):
|
||||
await websocket.close(
|
||||
code=status.WS_1008_POLICY_VIOLATION, reason="Unauthorized"
|
||||
)
|
||||
else:
|
||||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=str(exc))
|
||||
await websocket.close(code=status.WS_1011_INTERNAL_ERROR, reason=messsage)
|
||||
|
||||
|
||||
@router.post("/build/init/{flow_id}", response_model=InitResponse, status_code=201)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ def add_user(
|
|||
db.refresh(new_user)
|
||||
except IntegrityError as e:
|
||||
db.rollback()
|
||||
raise HTTPException(status_code=400, detail="This username is unavailable.") from e
|
||||
raise HTTPException(
|
||||
status_code=400, detail="This username is unavailable."
|
||||
) from e
|
||||
|
||||
return new_user
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from langflow.api import router
|
|||
|
||||
from langflow.interface.utils import setup_llm_caching
|
||||
from langflow.services.database.utils import initialize_database
|
||||
from langflow.services.manager import initialize_services
|
||||
from langflow.services.manager import initialize_services, teardown_services
|
||||
from langflow.utils.logger import configure
|
||||
|
||||
|
||||
|
|
@ -40,6 +40,7 @@ def create_app():
|
|||
app.on_event("startup")(initialize_services)
|
||||
app.on_event("startup")(initialize_database)
|
||||
app.on_event("startup")(setup_llm_caching)
|
||||
app.on_event("shutdown")(teardown_services)
|
||||
return app
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,12 @@ async def api_key_security(
|
|||
result: Optional[Union[ApiKey, User]] = None
|
||||
if settings_manager.auth_settings.AUTO_LOGIN:
|
||||
# Get the first user
|
||||
settings_manager.auth_settings.FIRST_SUPERUSER
|
||||
if not settings_manager.auth_settings.FIRST_SUPERUSER:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Missing first superuser credentials",
|
||||
)
|
||||
|
||||
result = get_user_by_username(
|
||||
db, settings_manager.auth_settings.FIRST_SUPERUSER
|
||||
)
|
||||
|
|
@ -80,6 +85,9 @@ async def get_current_user(
|
|||
if isinstance(token, Coroutine):
|
||||
token = await token
|
||||
|
||||
if settings_manager.auth_settings.SECRET_KEY is None:
|
||||
raise credentials_exception
|
||||
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token,
|
||||
|
|
@ -150,22 +158,16 @@ def create_token(data: dict, expires_delta: timedelta):
|
|||
|
||||
|
||||
def create_super_user(
|
||||
username: str,
|
||||
password: str,
|
||||
db: Session = Depends(get_session),
|
||||
username: Optional[str] = None,
|
||||
password: Optional[str] = None,
|
||||
) -> User:
|
||||
settings_manager = get_settings_manager()
|
||||
|
||||
super_user = get_user_by_username(
|
||||
db, username or settings_manager.auth_settings.FIRST_SUPERUSER
|
||||
)
|
||||
super_user = get_user_by_username(db, username)
|
||||
|
||||
if not super_user:
|
||||
super_user = User(
|
||||
username=username or settings_manager.auth_settings.FIRST_SUPERUSER,
|
||||
password=get_password_hash(
|
||||
password or settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
|
||||
),
|
||||
username=username,
|
||||
password=get_password_hash(password),
|
||||
is_superuser=True,
|
||||
is_active=True,
|
||||
last_login_at=None,
|
||||
|
|
@ -179,7 +181,15 @@ def create_super_user(
|
|||
|
||||
|
||||
def create_user_longterm_token(db: Session = Depends(get_session)) -> dict:
|
||||
super_user = create_super_user(db)
|
||||
settings_manager = get_settings_manager()
|
||||
username = settings_manager.auth_settings.FIRST_SUPERUSER
|
||||
password = settings_manager.auth_settings.FIRST_SUPERUSER_PASSWORD
|
||||
if not username or not password:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="Missing first superuser credentials",
|
||||
)
|
||||
super_user = create_super_user(db=db, username=username, password=password)
|
||||
|
||||
access_token_expires_longterm = timedelta(days=365)
|
||||
access_token = create_token(
|
||||
|
|
|
|||
|
|
@ -1,2 +1,8 @@
|
|||
class Service:
|
||||
from abc import ABC
|
||||
|
||||
|
||||
class Service(ABC):
|
||||
name: str
|
||||
|
||||
def teardown(self):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
from langflow.services.base import Service
|
||||
from langflow.services.database.models.user.crud import get_user_by_username
|
||||
from langflow.services.database.utils import Result, TableResults
|
||||
from langflow.services.utils import get_settings_manager
|
||||
from sqlalchemy import inspect
|
||||
|
|
@ -159,3 +160,23 @@ class DatabaseManager(Service):
|
|||
)
|
||||
|
||||
logger.debug("Database and tables created successfully")
|
||||
|
||||
def teardown(self):
|
||||
logger.debug("Tearing down database")
|
||||
try:
|
||||
settings_manager = get_settings_manager()
|
||||
# remove the default superuser if auto_login is enabled
|
||||
# using the FIRST_SUPERUSER to get the user
|
||||
if settings_manager.auth_settings.AUTO_LOGIN:
|
||||
logger.debug("Removing default superuser")
|
||||
username = settings_manager.auth_settings.FIRST_SUPERUSER
|
||||
with Session(self.engine) as session:
|
||||
user = get_user_by_username(session, username)
|
||||
session.delete(user)
|
||||
session.commit()
|
||||
logger.debug("Default superuser removed")
|
||||
|
||||
except Exception as exc:
|
||||
logger.error(f"Error tearing down database: {exc}")
|
||||
|
||||
self.engine.dispose()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
from langflow.services.schema import ServiceType
|
||||
from typing import TYPE_CHECKING, List, Optional
|
||||
from langflow.utils.logger import logger
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from langflow.services.factory import ServiceFactory
|
||||
|
|
@ -42,6 +43,7 @@ class ServiceManager:
|
|||
"""
|
||||
Create a new service given its name, handling dependencies.
|
||||
"""
|
||||
logger.debug(f"Create service {service_name}")
|
||||
self._validate_service_creation(service_name)
|
||||
|
||||
# Create dependencies first
|
||||
|
|
@ -74,9 +76,21 @@ class ServiceManager:
|
|||
Update a service by its name.
|
||||
"""
|
||||
if service_name in self.services:
|
||||
logger.debug(f"Update service {service_name}")
|
||||
self.services.pop(service_name, None)
|
||||
self.get(service_name)
|
||||
|
||||
def teardown(self):
|
||||
"""
|
||||
Teardown all the services.
|
||||
"""
|
||||
for service in self.services.values():
|
||||
logger.debug(f"Teardown service {service.name}")
|
||||
service.teardown()
|
||||
self.services = {}
|
||||
self.factories = {}
|
||||
self.dependencies = {}
|
||||
|
||||
|
||||
service_manager = ServiceManager()
|
||||
|
||||
|
|
@ -121,7 +135,7 @@ def initialize_session_manager():
|
|||
"""
|
||||
Initialize the session manager.
|
||||
"""
|
||||
from langflow.services.session import factory as session_manager_factory
|
||||
from langflow.services.session import factory as session_manager_factory # type: ignore
|
||||
from langflow.services.cache import factory as cache_factory
|
||||
|
||||
initialize_settings_manager()
|
||||
|
|
@ -134,3 +148,10 @@ def initialize_session_manager():
|
|||
session_manager_factory.SessionManagerFactory(),
|
||||
dependencies=[ServiceType.CACHE_MANAGER],
|
||||
)
|
||||
|
||||
|
||||
def teardown_services():
|
||||
"""
|
||||
Teardown all the services.
|
||||
"""
|
||||
service_manager.teardown()
|
||||
|
|
|
|||
|
|
@ -11,10 +11,11 @@ from langflow.utils.logger import logger
|
|||
class AuthSettings(BaseSettings):
|
||||
# Login settings
|
||||
CONFIG_DIR: str
|
||||
SECRET_KEY: Optional[str] = Field(
|
||||
None,
|
||||
SECRET_KEY: str = Field(
|
||||
default="",
|
||||
description="Secret key for JWT. If not provided, a random one will be generated.",
|
||||
env="LANGFLOW_SECRET_KEY",
|
||||
allow_mutation=False,
|
||||
)
|
||||
ALGORITHM: str = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_MINUTES: int = 60
|
||||
|
|
|
|||
|
|
@ -35,5 +35,10 @@ class SettingsManager(Service):
|
|||
)
|
||||
|
||||
settings = Settings(**settings_dict)
|
||||
auth_settings = AuthSettings(CONFIG_DIR=settings.CONFIG_DIR)
|
||||
if not settings.CONFIG_DIR:
|
||||
raise ValueError("CONFIG_DIR must be set in settings")
|
||||
|
||||
auth_settings = AuthSettings(
|
||||
CONFIG_DIR=settings.CONFIG_DIR,
|
||||
)
|
||||
return cls(settings, auth_settings)
|
||||
|
|
|
|||
|
|
@ -43,5 +43,5 @@ def write_secret_to_file(path: Path, value: str) -> None:
|
|||
|
||||
|
||||
def read_secret_from_file(path: Path) -> str:
|
||||
with path.open("rb") as f:
|
||||
with path.open("r") as f:
|
||||
return f.read()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
import { useState } from "react";
|
||||
import { dropdownButtonPropsType } from "../../types/components";
|
||||
import IconComponent from "../genericIconComponent";
|
||||
import { Button } from "../ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "../ui/dropdown-menu";
|
||||
|
||||
export default function DropdownButton({
|
||||
firstButtonName,
|
||||
onFirstBtnClick,
|
||||
options,
|
||||
}: dropdownButtonPropsType): JSX.Element {
|
||||
const [showOptions, setShowOptions] = useState<boolean>(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DropdownMenu open={showOptions}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="relative pr-10"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
onFirstBtnClick();
|
||||
}}
|
||||
>
|
||||
{firstButtonName}
|
||||
<div
|
||||
className="absolute right-2 items-center text-muted-foreground"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
setShowOptions(!showOptions);
|
||||
}}
|
||||
>
|
||||
{!showOptions ? (
|
||||
<IconComponent
|
||||
name="ChevronDown"
|
||||
/>
|
||||
) : (
|
||||
<IconComponent name="ChevronUp" />
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
onPointerDownOutside={(event) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
setShowOptions(!showOptions);
|
||||
}}
|
||||
>
|
||||
{options.map(({ name, onBtnClick }, index) => (
|
||||
<DropdownMenuItem onClick={onBtnClick} key={index}>
|
||||
{name}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -28,7 +28,6 @@ import {
|
|||
TabsList,
|
||||
TabsTrigger,
|
||||
} from "../../components/ui/tabs";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import { codeTabsPropsType } from "../../types/components";
|
||||
|
|
@ -57,7 +56,7 @@ export default function CodeTabsComponent({
|
|||
}, [flow]);
|
||||
|
||||
useEffect(() => {
|
||||
if(tweaks){
|
||||
if (tweaks) {
|
||||
unselectAllNodes({
|
||||
data,
|
||||
updateNodes: (nodes) => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { cn } from "../../utils/utils"
|
||||
import { cn } from "../../utils/utils";
|
||||
|
||||
function Skeleton({
|
||||
className,
|
||||
|
|
@ -9,7 +9,7 @@ function Skeleton({
|
|||
className={cn("animate-pulse rounded-md bg-border", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Skeleton }
|
||||
export { Skeleton };
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const initialValue: alertContextType = {
|
|||
notificationList: [],
|
||||
pushNotificationList: () => {},
|
||||
clearNotificationList: () => {},
|
||||
removeFromNotificationList: () => {}
|
||||
removeFromNotificationList: () => {},
|
||||
};
|
||||
|
||||
export const alertContext = createContext<alertContextType>(initialValue);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const TabsContextInitialValue: TabsContextType = {
|
|||
downloadFlow: (flow: FlowType) => {},
|
||||
downloadFlows: () => {},
|
||||
uploadFlows: () => {},
|
||||
uploadFlow: () => {},
|
||||
uploadFlow: async () => "",
|
||||
isBuilt: false,
|
||||
setIsBuilt: (state: boolean) => {},
|
||||
hardReset: () => {},
|
||||
|
|
@ -298,39 +298,38 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
* If the file type is application/json, the file is read and parsed into a JSON object.
|
||||
* The resulting JSON object is passed to the addFlow function.
|
||||
*/
|
||||
function uploadFlow(newProject?: boolean, file?: File) {
|
||||
async function uploadFlow(
|
||||
newProject?: boolean,
|
||||
file?: File
|
||||
): Promise<String | undefined> {
|
||||
let id;
|
||||
if (file) {
|
||||
file.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
let text = await file.text();
|
||||
// parse the text into a JSON object
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
|
||||
addFlow(flow, newProject);
|
||||
});
|
||||
id = await addFlow(flow, newProject);
|
||||
} else {
|
||||
// create a file input
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".json";
|
||||
// add a change event listener to the file input
|
||||
input.onchange = (e: Event) => {
|
||||
// check if the file type is application/json
|
||||
if (
|
||||
(e.target as HTMLInputElement).files![0].type === "application/json"
|
||||
) {
|
||||
// get the file from the file input
|
||||
const currentfile = (e.target as HTMLInputElement).files![0];
|
||||
// read the file as text
|
||||
currentfile.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
id = await new Promise(resolve => {
|
||||
input.onchange = async (e: Event) => {
|
||||
if ((e.target as HTMLInputElement).files![0].type === "application/json") {
|
||||
const currentfile = (e.target as HTMLInputElement).files![0];
|
||||
let text = await currentfile.text();
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
|
||||
addFlow(flow, newProject);
|
||||
});
|
||||
}
|
||||
};
|
||||
// trigger the file input click event to open the file dialog
|
||||
input.click();
|
||||
const flowId = await addFlow(flow, newProject);
|
||||
resolve(flowId);
|
||||
}
|
||||
};
|
||||
// trigger the file input click event to open the file dialog
|
||||
input.click();
|
||||
});
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
function uploadFlows() {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { classNames } from "../../utils/utils";
|
|||
import ChatInput from "./chatInput";
|
||||
import ChatMessage from "./chatMessage";
|
||||
|
||||
import _, { set } from "lodash";
|
||||
import _ from "lodash";
|
||||
import AccordionComponent from "../../components/AccordionComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
|
|
@ -25,9 +25,9 @@ import { Textarea } from "../../components/ui/textarea";
|
|||
import { CHAT_FORM_DIALOG_SUBTITLE } from "../../constants/constants";
|
||||
import { AuthContext } from "../../contexts/authContext";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import { getBuildStatus } from "../../controllers/API";
|
||||
import { TabsState } from "../../types/tabs";
|
||||
import { validateNodes } from "../../utils/reactflowUtils";
|
||||
import { getBuildStatus } from "../../controllers/API";
|
||||
|
||||
export default function FormModal({
|
||||
flow,
|
||||
|
|
@ -156,19 +156,21 @@ export default function FormModal({
|
|||
|
||||
function handleOnClose(event: CloseEvent): void {
|
||||
if (isOpen.current) {
|
||||
getBuildStatus(flow.id).then((response) => {
|
||||
if (response.data.built) {
|
||||
connectWS();
|
||||
}
|
||||
else {
|
||||
getBuildStatus(flow.id)
|
||||
.then((response) => {
|
||||
if (response.data.built) {
|
||||
connectWS();
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Please build the flow again before using the chat.",
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: "Please build the flow again before using the chat."
|
||||
})
|
||||
}
|
||||
}).catch((error) => {
|
||||
setErrorData({title:error.data?.detail?error.data.detail:error.message})
|
||||
|
||||
});
|
||||
title: error.data?.detail ? error.data.detail : error.message,
|
||||
});
|
||||
});
|
||||
setErrorData({ title: event.reason });
|
||||
setTimeout(() => {
|
||||
setLockChat(false);
|
||||
|
|
@ -186,8 +188,9 @@ export default function FormModal({
|
|||
const host = isDevelopment ? "localhost:7860" : window.location.host;
|
||||
const chatEndpoint = `/api/v1/chat/${chatId}`;
|
||||
|
||||
return `${isDevelopment ? "ws" : webSocketProtocol
|
||||
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
|
||||
return `${
|
||||
isDevelopment ? "ws" : webSocketProtocol
|
||||
}://${host}${chatEndpoint}?token=${encodeURIComponent(accessToken!)}`;
|
||||
}
|
||||
|
||||
function handleWsMessage(data: any) {
|
||||
|
|
@ -209,20 +212,20 @@ export default function FormModal({
|
|||
newChatHistory.push(
|
||||
chatItem.files
|
||||
? {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.template,
|
||||
thought: chatItem.intermediate_steps,
|
||||
files: chatItem.files,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.template,
|
||||
thought: chatItem.intermediate_steps,
|
||||
files: chatItem.files,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
: {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.template,
|
||||
thought: chatItem.intermediate_steps,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.message,
|
||||
template: chatItem.template,
|
||||
thought: chatItem.intermediate_steps,
|
||||
chatKey: chatItem.chatKey,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -442,73 +445,73 @@ export default function FormModal({
|
|||
|
||||
{tabsState[id.current]?.formKeysData?.input_keys
|
||||
? Object.keys(
|
||||
tabsState[id.current].formKeysData.input_keys!
|
||||
).map((key, index) => (
|
||||
<div className="file-component-accordion-div" key={index}>
|
||||
<AccordionComponent
|
||||
trigger={
|
||||
<div className="file-component-badge-div">
|
||||
<Badge variant="gray" size="md">
|
||||
{key}
|
||||
</Badge>
|
||||
tabsState[id.current].formKeysData.input_keys!
|
||||
).map((key, index) => (
|
||||
<div className="file-component-accordion-div" key={index}>
|
||||
<AccordionComponent
|
||||
trigger={
|
||||
<div className="file-component-badge-div">
|
||||
<Badge variant="gray" size="md">
|
||||
{key}
|
||||
</Badge>
|
||||
|
||||
<div
|
||||
className="-mb-1"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === key}
|
||||
setEnabled={(value) =>
|
||||
handleOnCheckedChange(value, key)
|
||||
}
|
||||
size="small"
|
||||
disabled={tabsState[
|
||||
id.current
|
||||
].formKeysData.handle_keys!.some(
|
||||
(t) => t === key
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className="-mb-1"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === key}
|
||||
setEnabled={(value) =>
|
||||
handleOnCheckedChange(value, key)
|
||||
}
|
||||
size="small"
|
||||
disabled={tabsState[
|
||||
id.current
|
||||
].formKeysData.handle_keys!.some(
|
||||
(t) => t === key
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
key={index}
|
||||
keyValue={key}
|
||||
>
|
||||
<div className="file-component-tab-column">
|
||||
{tabsState[id.current].formKeysData.handle_keys!.some(
|
||||
(t) => t === key
|
||||
) && (
|
||||
}
|
||||
key={index}
|
||||
keyValue={key}
|
||||
>
|
||||
<div className="file-component-tab-column">
|
||||
{tabsState[id.current].formKeysData.handle_keys!.some(
|
||||
(t) => t === key
|
||||
) && (
|
||||
<div className="font-normal text-muted-foreground ">
|
||||
Source: Component
|
||||
</div>
|
||||
)}
|
||||
<Textarea
|
||||
className="custom-scroll"
|
||||
value={
|
||||
tabsState[id.current].formKeysData.input_keys![
|
||||
key
|
||||
]
|
||||
}
|
||||
onChange={(e) => {
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[
|
||||
id.current
|
||||
].formKeysData.input_keys![key] =
|
||||
e.target.value;
|
||||
return newTabsState;
|
||||
});
|
||||
}}
|
||||
disabled={chatKey === key}
|
||||
placeholder="Enter text..."
|
||||
></Textarea>
|
||||
</div>
|
||||
</AccordionComponent>
|
||||
</div>
|
||||
))
|
||||
<Textarea
|
||||
className="custom-scroll"
|
||||
value={
|
||||
tabsState[id.current].formKeysData.input_keys![
|
||||
key
|
||||
]
|
||||
}
|
||||
onChange={(e) => {
|
||||
//@ts-ignore
|
||||
setTabsState((old: TabsState) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[
|
||||
id.current
|
||||
].formKeysData.input_keys![key] =
|
||||
e.target.value;
|
||||
return newTabsState;
|
||||
});
|
||||
}}
|
||||
disabled={chatKey === key}
|
||||
placeholder="Enter text..."
|
||||
></Textarea>
|
||||
</div>
|
||||
</AccordionComponent>
|
||||
</div>
|
||||
))
|
||||
: null}
|
||||
{tabsState[id.current].formKeysData.memory_keys!.map(
|
||||
(key, index) => (
|
||||
|
|
@ -522,7 +525,7 @@ export default function FormModal({
|
|||
<div className="-mb-1">
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === key}
|
||||
setEnabled={() => { }}
|
||||
setEnabled={() => {}}
|
||||
size="small"
|
||||
disabled={true}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -136,7 +136,11 @@ export default function GenericModal({
|
|||
setSuccessData({
|
||||
title: "Prompt is ready",
|
||||
});
|
||||
if(JSON.stringify(apiReturn.data?.frontend_node)!==JSON.stringify({})) setNodeClass!(apiReturn.data?.frontend_node);
|
||||
if (
|
||||
JSON.stringify(apiReturn.data?.frontend_node) !==
|
||||
JSON.stringify({})
|
||||
)
|
||||
setNodeClass!(apiReturn.data?.frontend_node);
|
||||
setModalOpen(closeModal);
|
||||
setValue(inputValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useContext, useEffect, useRef, useState } from "react";
|
|||
import PaginatorComponent from "../../components/PaginatorComponent";
|
||||
import ShadTooltip from "../../components/ShadTooltipComponent";
|
||||
import IconComponent from "../../components/genericIconComponent";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { Button } from "../../components/ui/button";
|
||||
import { Checkbox } from "../../components/ui/checkbox";
|
||||
import { Input } from "../../components/ui/input";
|
||||
|
|
@ -25,9 +26,8 @@ import {
|
|||
} from "../../controllers/API";
|
||||
import ConfirmationModal from "../../modals/ConfirmationModal";
|
||||
import UserManagementModal from "../../modals/UserManagementModal";
|
||||
import { UserInputType } from "../../types/components";
|
||||
import Header from "../../components/headerComponent";
|
||||
import { Users } from "../../types/api";
|
||||
import { UserInputType } from "../../types/components";
|
||||
|
||||
export default function AdminPage() {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
|
|
@ -89,7 +89,7 @@ export default function AdminPage() {
|
|||
if (input === "") {
|
||||
setFilterUserList(userList.current);
|
||||
} else {
|
||||
const filteredList = userList.current.filter((user:Users) =>
|
||||
const filteredList = userList.current.filter((user: Users) =>
|
||||
user.username.toLowerCase().includes(input.toLowerCase())
|
||||
);
|
||||
setFilterUserList(filteredList);
|
||||
|
|
@ -183,249 +183,254 @@ export default function AdminPage() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col">
|
||||
<Header />
|
||||
{userData && (
|
||||
<div className="main-page-panel">
|
||||
<div className="m-auto flex h-full flex-row justify-center">
|
||||
<div className="basis-5/6">
|
||||
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
|
||||
<div className="flex items-center justify-between space-y-2">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold tracking-tight">
|
||||
Welcome back!
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Navigate through this section to efficiently oversee all
|
||||
application users. From here, you can seamlessly manage
|
||||
user accounts.
|
||||
</p>
|
||||
<div className="flex flex-col">
|
||||
<Header />
|
||||
{userData && (
|
||||
<div className="main-page-panel">
|
||||
<div className="m-auto flex h-full flex-row justify-center">
|
||||
<div className="basis-5/6">
|
||||
<div className="m-auto flex h-full flex-col space-y-8 p-8 ">
|
||||
<div className="flex items-center justify-between space-y-2">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold tracking-tight">
|
||||
Welcome back!
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Navigate through this section to efficiently oversee all
|
||||
application users. From here, you can seamlessly manage
|
||||
user accounts.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2"></div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2"></div>
|
||||
</div>
|
||||
|
||||
{userList.current.length === 0 && !loadingUsers && (
|
||||
{userList.current.length === 0 && !loadingUsers && (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2>There's no users registered :)</h2>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<h2>There's no users registered :)</h2>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<Input
|
||||
value={inputValue}
|
||||
placeholder="Filter users..."
|
||||
className="h-8 w-[150px] lg:w-[250px]"
|
||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||
/>
|
||||
{inputValue.length > 0 && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setInputValue("");
|
||||
setFilterUserList(userList.current);
|
||||
<div className="flex flex-1 items-center space-x-2">
|
||||
<Input
|
||||
value={inputValue}
|
||||
placeholder="Filter users..."
|
||||
className="h-8 w-[150px] lg:w-[250px]"
|
||||
onChange={(e) => handleFilterUsers(e.target.value)}
|
||||
/>
|
||||
{inputValue.length > 0 && (
|
||||
<Button
|
||||
onClick={() => {
|
||||
setInputValue("");
|
||||
setFilterUserList(userList.current);
|
||||
}}
|
||||
variant="ghost"
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Reset
|
||||
<X className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<UserManagementModal
|
||||
title="New User"
|
||||
titleHeader={"Add a new user"}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
onConfirm={(index, user) => {
|
||||
handleNewUser(user);
|
||||
}}
|
||||
variant="ghost"
|
||||
className="h-8 px-2 lg:px-3"
|
||||
>
|
||||
Reset
|
||||
<X className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button>New User</Button>
|
||||
</UserManagementModal>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<UserManagementModal
|
||||
title="New User"
|
||||
titleHeader={"Add a new user"}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
onConfirm={(index, user) => {
|
||||
handleNewUser(user);
|
||||
}}
|
||||
>
|
||||
<Button>New User</Button>
|
||||
</UserManagementModal>
|
||||
</div>
|
||||
</div>
|
||||
{loadingUsers && (
|
||||
<div>
|
||||
<strong>Loading...</strong>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||
(loadingUsers ? " border-0" : "")
|
||||
}
|
||||
>
|
||||
<Table className={"table-fixed bg-muted outline-1"}>
|
||||
<TableHeader
|
||||
className={
|
||||
loadingUsers
|
||||
? "hidden"
|
||||
: "table-fixed bg-muted outline-1"
|
||||
}
|
||||
>
|
||||
<TableRow>
|
||||
<TableHead className="h-10">Id</TableHead>
|
||||
<TableHead className="h-10">Username</TableHead>
|
||||
<TableHead className="h-10">Active</TableHead>
|
||||
<TableHead className="h-10">Superuser</TableHead>
|
||||
<TableHead className="h-10">Created At</TableHead>
|
||||
<TableHead className="h-10">Updated At</TableHead>
|
||||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{!loadingUsers && (
|
||||
<TableBody>
|
||||
{filterUserList.map((user:UserInputType, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2 font-medium">
|
||||
<ShadTooltip content={user.id}>
|
||||
<span className="cursor-default">
|
||||
{user.id}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDisableUser(
|
||||
user.is_active,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
checked={user.is_active}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleSuperUserEdit(
|
||||
user.is_superuser,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_superuser"
|
||||
checked={user.is_superuser}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2 ">
|
||||
{
|
||||
new Date(user.create_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
{
|
||||
new Date(user.updated_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
<UserManagementModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.id}`}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, editUser) => {
|
||||
handleEditUser(user.id, editUser);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
{loadingUsers && (
|
||||
<div>
|
||||
<strong>Loading...</strong>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={
|
||||
"max-h-[26rem] min-h-[26rem] overflow-scroll overflow-x-hidden rounded-md border-2 bg-muted custom-scroll" +
|
||||
(loadingUsers ? " border-0" : "")
|
||||
}
|
||||
>
|
||||
<Table className={"table-fixed bg-muted outline-1"}>
|
||||
<TableHeader
|
||||
className={
|
||||
loadingUsers
|
||||
? "hidden"
|
||||
: "table-fixed bg-muted outline-1"
|
||||
}
|
||||
>
|
||||
<TableRow>
|
||||
<TableHead className="h-10">Id</TableHead>
|
||||
<TableHead className="h-10">Username</TableHead>
|
||||
<TableHead className="h-10">Active</TableHead>
|
||||
<TableHead className="h-10">Superuser</TableHead>
|
||||
<TableHead className="h-10">Created At</TableHead>
|
||||
<TableHead className="h-10">Updated At</TableHead>
|
||||
<TableHead className="h-10 w-[100px] text-right"></TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
{!loadingUsers && (
|
||||
<TableBody>
|
||||
{filterUserList.map(
|
||||
(user: UserInputType, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="truncate py-2 font-medium">
|
||||
<ShadTooltip content={user.id}>
|
||||
<span className="cursor-default">
|
||||
{user.id}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</UserManagementModal>
|
||||
|
||||
<ConfirmationModal
|
||||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Delete" side="top">
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
<ShadTooltip content={user.username}>
|
||||
<span className="cursor-default">
|
||||
{user.username}
|
||||
</span>
|
||||
</ShadTooltip>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDisableUser(
|
||||
user.is_active,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_active"
|
||||
checked={user.is_active}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="relative left-5 truncate py-2 text-align-last-left">
|
||||
<ConfirmationModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.username}`}
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you completely confident about the changes you are making to this user?"
|
||||
cancelText="Cancel"
|
||||
confirmationText="Confirm"
|
||||
icon={"UserCog2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleSuperUserEdit(
|
||||
user.is_superuser,
|
||||
user.id,
|
||||
user
|
||||
);
|
||||
}}
|
||||
>
|
||||
<Checkbox
|
||||
id="is_superuser"
|
||||
checked={user.is_superuser}
|
||||
/>
|
||||
</ConfirmationModal>
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2 ">
|
||||
{
|
||||
new Date(user.create_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="truncate py-2">
|
||||
{
|
||||
new Date(user.updated_at!)
|
||||
.toISOString()
|
||||
.split("T")[0]
|
||||
}
|
||||
</TableCell>
|
||||
<TableCell className="flex w-[100px] py-2 text-right">
|
||||
<div className="flex">
|
||||
<UserManagementModal
|
||||
title="Edit"
|
||||
titleHeader={`${user.id}`}
|
||||
cancelText="Cancel"
|
||||
confirmationText="Save"
|
||||
icon={"UserPlus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, editUser) => {
|
||||
handleEditUser(user.id, editUser);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip content="Edit" side="top">
|
||||
<IconComponent
|
||||
name="Pencil"
|
||||
className="h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</UserManagementModal>
|
||||
|
||||
<PaginatorComponent
|
||||
pageIndex={index}
|
||||
pageSize={size}
|
||||
totalRowsCount={totalRowsCount}
|
||||
paginate={(pageIndex, pageSize) => {
|
||||
handleChangePagination(pageSize, pageIndex);
|
||||
}}
|
||||
></PaginatorComponent>
|
||||
</>
|
||||
<ConfirmationModal
|
||||
title="Delete"
|
||||
titleHeader="Delete User"
|
||||
modalContentTitle="Attention!"
|
||||
modalContent="Are you sure you want to delete this user? This action cannot be undone."
|
||||
cancelText="Cancel"
|
||||
confirmationText="Delete"
|
||||
icon={"UserMinus2"}
|
||||
data={user}
|
||||
index={index}
|
||||
onConfirm={(index, user) => {
|
||||
handleDeleteUser(user);
|
||||
}}
|
||||
>
|
||||
<ShadTooltip
|
||||
content="Delete"
|
||||
side="top"
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
/>
|
||||
</ShadTooltip>
|
||||
</ConfirmationModal>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
)}
|
||||
</TableBody>
|
||||
)}
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
<PaginatorComponent
|
||||
pageIndex={index}
|
||||
pageSize={size}
|
||||
totalRowsCount={totalRowsCount}
|
||||
paginate={(pageIndex, pageSize) => {
|
||||
handleChangePagination(pageSize, pageIndex);
|
||||
}}
|
||||
></PaginatorComponent>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -421,7 +421,7 @@ export default function Page({
|
|||
zoomOnScroll={!view}
|
||||
zoomOnPinch={!view}
|
||||
panOnDrag={!view}
|
||||
proOptions={{hideAttribution: true}}
|
||||
proOptions={{ hideAttribution: true }}
|
||||
>
|
||||
<Background className="" />
|
||||
{!view && (
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
const { data, templates } = useContext(typesContext);
|
||||
const { flows, tabId, uploadFlow, tabsState, saveFlow, isBuilt } =
|
||||
useContext(TabsContext);
|
||||
const { setSuccessData, setErrorData } =
|
||||
useContext(alertContext);
|
||||
const { setSuccessData, setErrorData } = useContext(alertContext);
|
||||
const [dataFilter, setFilterData] = useState(data);
|
||||
const [search, setSearch] = useState("");
|
||||
const isPending = tabsState[tabId]?.isPending;
|
||||
|
|
@ -101,9 +100,7 @@ export default function ExtraSidebar(): JSX.Element {
|
|||
<div className="side-bar-button">
|
||||
{flow && flow.data && (
|
||||
<ApiModal flow={flow} disable={!isBuilt}>
|
||||
<div
|
||||
className={classNames("extra-side-bar-buttons")}
|
||||
>
|
||||
<div className={classNames("extra-side-bar-buttons")}>
|
||||
<IconComponent
|
||||
name="Code2"
|
||||
className={
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { SkeletonCardComponent } from "../../components/skeletonCardComponent";
|
|||
import { Button } from "../../components/ui/button";
|
||||
import { USER_PROJECTS_HEADER } from "../../constants/constants";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import DropdownButton from "../../components/DropdownButtonComponent";
|
||||
export default function HomePage(): JSX.Element {
|
||||
const {
|
||||
flows,
|
||||
|
|
@ -14,9 +15,12 @@ export default function HomePage(): JSX.Element {
|
|||
downloadFlows,
|
||||
uploadFlows,
|
||||
addFlow,
|
||||
removeFlow,
|
||||
removeFlow, uploadFlow,
|
||||
isLoading,
|
||||
} = useContext(TabsContext);
|
||||
const dropdownOptions = [{name: "Import from JSON", onBtnClick: () => uploadFlow(true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
})}]
|
||||
|
||||
// Set a null id
|
||||
useEffect(() => {
|
||||
|
|
@ -57,21 +61,19 @@ export default function HomePage(): JSX.Element {
|
|||
<IconComponent name="Upload" className="main-page-nav-button" />
|
||||
Upload Collection
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={() => {
|
||||
<DropdownButton
|
||||
firstButtonName="New Project"
|
||||
onFirstBtnClick={() => {
|
||||
addFlow(null!, true).then((id) => {
|
||||
navigate("/flow/" + id);
|
||||
});
|
||||
}}
|
||||
>
|
||||
<IconComponent name="Plus" className="main-page-nav-button" />
|
||||
New Project
|
||||
</Button>
|
||||
options={dropdownOptions}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<span className="main-page-description-text">
|
||||
Manage your personal projects. Download or upload your collection.
|
||||
Manage your personal projects. Download or upload your collection.
|
||||
</span>
|
||||
<div className="main-page-flows-display">
|
||||
{isLoading && flows.length == 0 ? (
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export default function LoginPage(): JSX.Element {
|
|||
useState<loginInputStateType>(CONTROL_LOGIN_STATE);
|
||||
|
||||
const { password, username } = inputState;
|
||||
const { login, getAuthentication, setUserData, setIsAdmin } = useContext(AuthContext);
|
||||
const { login, getAuthentication, setUserData, setIsAdmin } =
|
||||
useContext(AuthContext);
|
||||
const navigate = useNavigate();
|
||||
const { setErrorData } = useContext(alertContext);
|
||||
|
||||
|
|
@ -130,7 +131,9 @@ export default function LoginPage(): JSX.Element {
|
|||
</div>
|
||||
<div className="w-full">
|
||||
<Form.Submit asChild>
|
||||
<Button className="mr-3 mt-6 w-full" type="submit">Sign in</Button>
|
||||
<Button className="mr-3 mt-6 w-full" type="submit">
|
||||
Sign in
|
||||
</Button>
|
||||
</Form.Submit>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ export type UserInputType = {
|
|||
is_superuser?: boolean;
|
||||
id?: string;
|
||||
create_at?: string;
|
||||
updated_at?:string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export type ApiKeyType = {
|
||||
|
|
@ -544,3 +544,9 @@ export type fetchErrorComponentType = {
|
|||
message: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
export type dropdownButtonPropsType = {
|
||||
firstButtonName: string;
|
||||
onFirstBtnClick: () => void;
|
||||
options: Array<{ name: string; onBtnClick: () => void; }>;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export type TabsContextType = {
|
|||
uploadFlows: () => void;
|
||||
isBuilt: boolean;
|
||||
setIsBuilt: (state: boolean) => void;
|
||||
uploadFlow: (newFlow?: boolean, file?: File) => void;
|
||||
uploadFlow: (newFlow?: boolean, file?: File) => Promise<String | undefined>;
|
||||
hardReset: () => void;
|
||||
getNodeId: (nodeType: string) => string;
|
||||
tabsState: TabsState;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {
|
|||
ChevronDown,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronUp,
|
||||
ChevronsLeft,
|
||||
ChevronsRight,
|
||||
ChevronsUpDown,
|
||||
|
|
@ -300,4 +301,5 @@ export const nodeIconsLucide: iconsType = {
|
|||
UserCog2,
|
||||
Key,
|
||||
Unplug,
|
||||
ChevronUp,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,13 @@ from langflow.services.database.models.user import UserUpdate
|
|||
|
||||
@pytest.fixture
|
||||
def super_user(client, session):
|
||||
return create_super_user(session)
|
||||
settings_manager = get_settings_manager()
|
||||
auth_settings = settings_manager.auth_settings
|
||||
return create_super_user(
|
||||
db=session,
|
||||
username=auth_settings.FIRST_SUPERUSER,
|
||||
password=auth_settings.FIRST_SUPERUSER_PASSWORD,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue