merging all branches/feat
This commit is contained in:
parent
a7c1af8c4f
commit
02cca793eb
44 changed files with 309 additions and 192 deletions
3
.github/workflows/docker-build.yml
vendored
3
.github/workflows/docker-build.yml
vendored
|
|
@ -80,7 +80,10 @@ jobs:
|
|||
langflowai/langflow-frontend:1.0-alpha
|
||||
|
||||
restart-space:
|
||||
name: Restart HuggingFace Spaces
|
||||
if: ${{ inputs.release_type == 'main' }}
|
||||
runs-on: ubuntu-latest
|
||||
needs: docker_build
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
|
|
|
|||
4
.github/workflows/pre-release.yml
vendored
4
.github/workflows/pre-release.yml
vendored
|
|
@ -35,6 +35,10 @@ jobs:
|
|||
with:
|
||||
python-version: "3.10"
|
||||
cache: "poetry"
|
||||
- name: Set up Nodejs 20
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
- name: Check Version
|
||||
id: check-version
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -1,6 +1,14 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
# Keep this syntax directive! It's used to enable Docker BuildKit
|
||||
|
||||
FROM node:20-bookworm-slim as builder-node
|
||||
WORKDIR /app
|
||||
COPY src/frontend/package.json src/frontend/package-lock.json ./
|
||||
RUN npm install
|
||||
COPY src/frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
|
||||
################################
|
||||
# BUILDER-BASE
|
||||
# Used to build deps + create our virtual environment
|
||||
|
|
@ -48,10 +56,10 @@ COPY pyproject.toml poetry.lock README.md ./
|
|||
COPY src/ ./src
|
||||
COPY scripts/ ./scripts
|
||||
RUN python -m pip install requests --user && cd ./scripts && python update_dependencies.py
|
||||
COPY --from=builder-node /app/build ./src/backend/base/langflow/frontend
|
||||
RUN $POETRY_HOME/bin/poetry lock --no-update \
|
||||
&& $POETRY_HOME/bin/poetry install --no-interaction --no-ansi -E deploy \
|
||||
&& $POETRY_HOME/bin/poetry build -f wheel \
|
||||
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl
|
||||
&& $POETRY_HOME/bin/poetry run pip install dist/*.whl --force-reinstall
|
||||
|
||||
################################
|
||||
# RUNTIME
|
||||
|
|
|
|||
14
poetry.lock
generated
14
poetry.lock
generated
|
|
@ -698,13 +698,13 @@ graph = ["gremlinpython (==3.4.6)"]
|
|||
|
||||
[[package]]
|
||||
name = "cassio"
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
description = "A framework-agnostic Python library to seamlessly integrate Apache Cassandra(R) with ML/LLM/genAI workloads."
|
||||
optional = false
|
||||
python-versions = "<4.0,>=3.8"
|
||||
files = [
|
||||
{file = "cassio-0.1.7-py3-none-any.whl", hash = "sha256:08d1028a20d09bd207de0e17eaf7ae821b3c8e4788555e2d337aa440e0846d87"},
|
||||
{file = "cassio-0.1.7.tar.gz", hash = "sha256:44f705dff8a9a1c48527db2c9e968686358c960fa21ba940d9e66de00639ad78"},
|
||||
{file = "cassio-0.1.8-py3-none-any.whl", hash = "sha256:c09e7c884ba7227ff5277c86f3b0f31c523672ea407f56d093c7227e69c54d94"},
|
||||
{file = "cassio-0.1.8.tar.gz", hash = "sha256:4e09929506cb3dd6fad217e89846d0a1a59069afd24b82c72526ef6f2e9271af"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
@ -4327,7 +4327,7 @@ types-requests = ">=2.31.0.2,<3.0.0.0"
|
|||
|
||||
[[package]]
|
||||
name = "langflow-base"
|
||||
version = "0.0.58"
|
||||
version = "0.0.59"
|
||||
description = "A Python package with a built-in web application"
|
||||
optional = false
|
||||
python-versions = ">=3.10,<3.13"
|
||||
|
|
@ -5588,13 +5588,13 @@ sympy = "*"
|
|||
|
||||
[[package]]
|
||||
name = "openai"
|
||||
version = "1.31.2"
|
||||
version = "1.32.0"
|
||||
description = "The official Python library for the openai API"
|
||||
optional = false
|
||||
python-versions = ">=3.7.1"
|
||||
files = [
|
||||
{file = "openai-1.31.2-py3-none-any.whl", hash = "sha256:203cf21294f347c3d7b591e0ccbe18389d6f8967d4237214b926ea76b1e1781c"},
|
||||
{file = "openai-1.31.2.tar.gz", hash = "sha256:966ab3165b926cb5ec091d2434c90613e2ea8b73ffad984f7fec34bde971725a"},
|
||||
{file = "openai-1.32.0-py3-none-any.whl", hash = "sha256:953d57669f309002044fd2f678aba9f07a43256d74b3b00cd04afb5b185568ea"},
|
||||
{file = "openai-1.32.0.tar.gz", hash = "sha256:a6df15a7ab9344b1bc2bc8d83639f68b7a7e2453c0f5e50c1666547eee86f0bd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow"
|
||||
version = "1.0.0a46"
|
||||
version = "1.0.0a48"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Langflow <contact@langflow.org>"]
|
||||
maintainers = [
|
||||
|
|
|
|||
|
|
@ -1,28 +0,0 @@
|
|||
from .AstraDB import AstraDBVectorStoreComponent
|
||||
from .Chroma import ChromaComponent
|
||||
from .FAISS import FAISSComponent
|
||||
from .MongoDBAtlasVector import MongoDBAtlasComponent
|
||||
from .Pinecone import PineconeComponent
|
||||
from .Qdrant import QdrantComponent
|
||||
from .Redis import RedisComponent
|
||||
from .SupabaseVectorStore import SupabaseComponent
|
||||
from .Vectara import VectaraComponent
|
||||
from .Weaviate import WeaviateVectorStoreComponent
|
||||
from .pgvector import PGVectorComponent
|
||||
from .Couchbase import CouchbaseComponent
|
||||
|
||||
__all__ = [
|
||||
"AstraDBVectorStoreComponent",
|
||||
"ChromaComponent",
|
||||
"CouchbaseComponent",
|
||||
"FAISSComponent",
|
||||
"MongoDBAtlasComponent",
|
||||
"PineconeComponent",
|
||||
"QdrantComponent",
|
||||
"RedisComponent",
|
||||
"SupabaseComponent",
|
||||
"VectaraComponent",
|
||||
"WeaviateVectorStoreComponent",
|
||||
"base",
|
||||
"PGVectorComponent",
|
||||
]
|
||||
|
|
@ -297,7 +297,7 @@ class CodeParser:
|
|||
bases = self.execute_and_inspect_classes(self.code)
|
||||
except Exception as e:
|
||||
# If the code cannot be executed, return an empty list
|
||||
logger.exception(e)
|
||||
logger.debug(e)
|
||||
bases = []
|
||||
raise e
|
||||
return bases
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ class DirectoryReader:
|
|||
component_tuple = (*build_component(component), component)
|
||||
components.append(component_tuple)
|
||||
except Exception as e:
|
||||
logger.error(f"Error while loading component { component['name']}: {e}")
|
||||
logger.debug(f"Error while loading component { component['name']}")
|
||||
logger.debug(e)
|
||||
continue
|
||||
items.append({"name": menu["name"], "path": menu["path"], "components": components})
|
||||
filtered = [menu for menu in items if menu["components"]]
|
||||
|
|
@ -266,8 +267,7 @@ class DirectoryReader:
|
|||
if validation_result:
|
||||
try:
|
||||
output_types = self.get_output_types_from_code(result_content)
|
||||
except Exception as exc:
|
||||
logger.exception(f"Error while getting output types from code: {str(exc)}")
|
||||
except Exception:
|
||||
output_types = [component_name_camelcase]
|
||||
else:
|
||||
output_types = [component_name_camelcase]
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ class MonitorService(Service):
|
|||
order: Optional[str] = "DESC",
|
||||
limit: Optional[int] = None,
|
||||
):
|
||||
query = "SELECT index, flow_id, sender_name, sender, session_id, message, artifacts, timestamp FROM messages"
|
||||
query = "SELECT index, flow_id, sender_name, sender, session_id, message, timestamp FROM messages"
|
||||
conditions = []
|
||||
if sender:
|
||||
conditions.append(f"sender = '{sender}'")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "langflow-base"
|
||||
version = "0.0.58"
|
||||
version = "0.0.59"
|
||||
description = "A Python package with a built-in web application"
|
||||
authors = ["Langflow <contact@langflow.org>"]
|
||||
maintainers = [
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import {
|
|||
LANGFLOW_SUPPORTED_TYPES,
|
||||
TOOLTIP_EMPTY,
|
||||
} from "../../../../constants/constants";
|
||||
import OutputModal from "../../../../customNodes/genericNode/components/outputModal";
|
||||
import { Case } from "../../../../shared/components/caseComponent";
|
||||
import useFlowStore from "../../../../stores/flowStore";
|
||||
import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
|
||||
|
|
@ -44,7 +45,6 @@ import useFetchDataOnMount from "../../../hooks/use-fetch-data-on-mount";
|
|||
import useHandleOnNewValue from "../../../hooks/use-handle-new-value";
|
||||
import useHandleNodeClass from "../../../hooks/use-handle-node-class";
|
||||
import useHandleRefreshButtonPress from "../../../hooks/use-handle-refresh-buttons";
|
||||
import OutputModal from "../outputModal";
|
||||
import TooltipRenderComponent from "../tooltipRenderComponent";
|
||||
import { TEXT_FIELD_TYPES } from "./constants";
|
||||
|
||||
|
|
@ -284,7 +284,7 @@ export default function ParameterComponent({
|
|||
"h-5 w-5 rounded-md",
|
||||
displayOutputPreview && !unknownOutput
|
||||
? " hover:bg-secondary-foreground/5 hover:text-medium-indigo"
|
||||
: " cursor-not-allowed text-muted-foreground",
|
||||
: " cursor-not-allowed text-muted-foreground"
|
||||
)}
|
||||
name={"ScanEye"}
|
||||
/>
|
||||
|
|
@ -11,6 +11,8 @@ import {
|
|||
STATUS_BUILDING,
|
||||
} from "../../constants/constants";
|
||||
import { BuildStatus } from "../../constants/enums";
|
||||
import { countHandlesFn } from "../../customNodes/helpers/count-handles";
|
||||
import { getSpecificClassFromBuildStatus } from "../../customNodes/helpers/get-class-from-build-status";
|
||||
import NodeToolbarComponent from "../../pages/FlowPage/components/nodeToolbarComponent";
|
||||
import useAlertStore from "../../stores/alertStore";
|
||||
import { useDarkStore } from "../../stores/darkStore";
|
||||
|
|
@ -22,8 +24,6 @@ import { NodeDataType } from "../../types/flow";
|
|||
import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils";
|
||||
import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils";
|
||||
import { classNames, cn } from "../../utils/utils";
|
||||
import { countHandlesFn } from "../helpers/count-handles";
|
||||
import { getSpecificClassFromBuildStatus } from "../helpers/get-class-from-build-status";
|
||||
import useCheckCodeValidity from "../hooks/use-check-code-validity";
|
||||
import useIconNodeRender from "../hooks/use-icon-render";
|
||||
import useIconStatus from "../hooks/use-icons-status";
|
||||
|
|
@ -127,7 +127,7 @@ export default function GenericNode({
|
|||
const names = classNames(
|
||||
baseBorderClass,
|
||||
nodeSizeClass,
|
||||
"generic-node-div",
|
||||
"generic-node-div group/node",
|
||||
specificClassFromBuildStatus
|
||||
);
|
||||
return names;
|
||||
|
|
@ -51,13 +51,15 @@ export default function ErrorAlert({
|
|||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className="error-build-foreground">{title}</h3>
|
||||
<h3 className="error-build-foreground line-clamp-2">{title}</h3>
|
||||
{list?.length !== 0 &&
|
||||
list?.some((item) => item !== null && item !== undefined) ? (
|
||||
<div className="error-build-message-div">
|
||||
<ul className="error-build-message-list">
|
||||
{list.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
<li key={index} className="line-clamp-5">
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ export default function NoticeAlert({
|
|||
/>
|
||||
</div>
|
||||
<div className="ml-3 flex-1 md:flex md:justify-between">
|
||||
<p className="text-sm text-info-foreground word-break-break-word">
|
||||
<p className="line-clamp-2 text-sm text-info-foreground word-break-break-word">
|
||||
{title}
|
||||
</p>
|
||||
<p className="mt-3 text-sm md:ml-6 md:mt-0">
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ export default function SuccessAlert({
|
|||
/>
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<p className="success-alert-message">{title}</p>
|
||||
<p className="success-alert-message line-clamp-2">{title}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ export default function Header(): JSX.Element {
|
|||
const lastFlowVisitedIndex = routeHistory
|
||||
.reverse()
|
||||
.findIndex(
|
||||
(path) => path.includes("/flow/") && path !== location.pathname
|
||||
(path) => path.includes("/flow/") && path !== location.pathname,
|
||||
);
|
||||
|
||||
const lastFlowVisited = routeHistory[lastFlowVisitedIndex];
|
||||
|
|
@ -200,6 +200,28 @@ export default function Header(): JSX.Element {
|
|||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent>
|
||||
{!autoLogin && (
|
||||
<>
|
||||
<DropdownMenuLabel>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={
|
||||
"h-5 w-5 rounded-full focus-visible:outline-0 " +
|
||||
(userData?.profile_image ??
|
||||
(userData?.id
|
||||
? gradients[
|
||||
parseInt(userData?.id ?? "", 30) %
|
||||
gradients.length
|
||||
]
|
||||
: "bg-gray-500"))
|
||||
}
|
||||
/>
|
||||
{userData?.username ?? "User"}
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
<DropdownMenuLabel>General</DropdownMenuLabel>
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer"
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import IconComponent, {
|
|||
import { Button, buttonVariants } from "../../../ui/button";
|
||||
import { Input } from "../../../ui/input";
|
||||
import useFileDrop from "../../hooks/use-on-file-drop";
|
||||
import useAlertStore from "../../../../stores/alertStore";
|
||||
|
||||
type SideBarFoldersButtonsComponentProps = {
|
||||
folders: FolderType[];
|
||||
|
|
@ -51,6 +52,7 @@ const SideBarFoldersButtonsComponent = ({
|
|||
const location = useLocation();
|
||||
const folderId = location?.state?.folderId ?? myCollectionId;
|
||||
const getFolderById = useFolderStore((state) => state.getFolderById);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
|
||||
const handleFolderChange = (folderId: string) => {
|
||||
getFolderById(folderId);
|
||||
|
|
@ -62,7 +64,17 @@ const SideBarFoldersButtonsComponent = ({
|
|||
);
|
||||
|
||||
const handleUploadFlowsToFolder = () => {
|
||||
uploadFolder(folderId);
|
||||
uploadFolder(folderId)
|
||||
.then(() => {
|
||||
getFolderById(folderId);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
setErrorData({
|
||||
title: `Error on upload`,
|
||||
list: [err["response"]["data"]],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const handleDownloadFolder = (id: string) => {
|
||||
|
|
|
|||
|
|
@ -668,7 +668,7 @@ export const ZERO_NOTIFICATIONS = "No new notifications";
|
|||
export const SUCCESS_BUILD = "Built sucessfully ✨";
|
||||
|
||||
export const ALERT_SAVE_WITH_API =
|
||||
"Caution: Uncheck this box only removes API keys from fields specifically designated for API keys.";
|
||||
"Caution: Unchecking this box only removes API keys from fields specifically designated for API keys.";
|
||||
|
||||
export const SAVE_WITH_API_CHECKBOX = "Save with my API keys";
|
||||
export const EDIT_TEXT_MODAL_TITLE = "Edit Text";
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ const ExportModal = forwardRef(
|
|||
is_component: false,
|
||||
},
|
||||
name!,
|
||||
description
|
||||
description,
|
||||
);
|
||||
setNoticeData({
|
||||
title: API_WARNING_NOTICE_ALERT,
|
||||
|
|
@ -61,7 +61,7 @@ const ExportModal = forwardRef(
|
|||
is_component: false,
|
||||
}),
|
||||
name!,
|
||||
description
|
||||
description,
|
||||
);
|
||||
setOpen(false);
|
||||
}}
|
||||
|
|
@ -94,7 +94,7 @@ const ExportModal = forwardRef(
|
|||
{SAVE_WITH_API_CHECKBOX}
|
||||
</label>
|
||||
</div>
|
||||
<span className=" text-xs text-destructive ">
|
||||
<span className="mt-1 text-xs text-destructive ">
|
||||
{ALERT_SAVE_WITH_API}
|
||||
</span>
|
||||
</BaseModal.Content>
|
||||
|
|
@ -102,6 +102,6 @@ const ExportModal = forwardRef(
|
|||
<BaseModal.Footer submit={{ label: "Download Flow" }} />
|
||||
</BaseModal>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
export default ExportModal;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ import { FormProvider, useForm, useWatch } from "react-hook-form";
|
|||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import CollectionCardComponent from "../../../../components/cardComponent";
|
||||
import CardsWrapComponent from "../../../../components/cardsWrapComponent";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import IconComponent, {
|
||||
ForwardedIconComponent,
|
||||
} from "../../../../components/genericIconComponent";
|
||||
import PaginatorComponent from "../../../../components/paginatorComponent";
|
||||
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
|
|
@ -18,6 +20,9 @@ import { getNameByType } from "../../utils/get-name-by-type";
|
|||
import { sortFlows } from "../../utils/sort-flows";
|
||||
import EmptyComponent from "../emptyComponent";
|
||||
import HeaderComponent from "../headerComponent";
|
||||
import { downloadFlow, removeApiKeys } from "../../../../utils/reactflowUtils";
|
||||
import { useDarkStore } from "../../../../stores/darkStore";
|
||||
import { UPLOAD_ERROR_ALERT } from "../../../../constants/alerts_constants";
|
||||
|
||||
export default function ComponentsComponent({
|
||||
type = "all",
|
||||
|
|
@ -31,22 +36,22 @@ export default function ComponentsComponent({
|
|||
const allFlows = useFlowsManagerStore((state) => state.allFlows);
|
||||
|
||||
const flowsFromFolder = useFolderStore(
|
||||
(state) => state.selectedFolder?.flows
|
||||
(state) => state.selectedFolder?.flows,
|
||||
);
|
||||
|
||||
const setSuccessData = useAlertStore((state) => state.setSuccessData);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const [openDelete, setOpenDelete] = useState(false);
|
||||
const searchFlowsComponents = useFlowsManagerStore(
|
||||
(state) => state.searchFlowsComponents
|
||||
(state) => state.searchFlowsComponents,
|
||||
);
|
||||
|
||||
const setSelectedFlowsComponentsCards = useFlowsManagerStore(
|
||||
(state) => state.setSelectedFlowsComponentsCards
|
||||
(state) => state.setSelectedFlowsComponentsCards,
|
||||
);
|
||||
|
||||
const selectedFlowsComponentsCards = useFlowsManagerStore(
|
||||
(state) => state.selectedFlowsComponentsCards
|
||||
(state) => state.selectedFlowsComponentsCards,
|
||||
);
|
||||
|
||||
const [handleFileDrop] = useFileDrop(uploadFlow, type)!;
|
||||
|
|
@ -82,7 +87,7 @@ export default function ComponentsComponent({
|
|||
f.name.toLowerCase().includes(searchFlowsComponents.toLowerCase()) ||
|
||||
f.description
|
||||
.toLowerCase()
|
||||
.includes(searchFlowsComponents.toLowerCase())
|
||||
.includes(searchFlowsComponents.toLowerCase()),
|
||||
);
|
||||
|
||||
if (searchFlowsComponents === "") {
|
||||
|
|
@ -129,6 +134,8 @@ export default function ComponentsComponent({
|
|||
setOpenDelete(true);
|
||||
} else if (action === "duplicate") {
|
||||
handleDuplicate();
|
||||
} else if (action === "export") {
|
||||
handleExport();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -137,9 +144,9 @@ export default function ComponentsComponent({
|
|||
selectedFlowsComponentsCards.map((selectedFlow) =>
|
||||
addFlow(
|
||||
true,
|
||||
allFlows.find((flow) => flow.id === selectedFlow)
|
||||
)
|
||||
)
|
||||
allFlows.find((flow) => flow.id === selectedFlow),
|
||||
),
|
||||
),
|
||||
).then(() => {
|
||||
resetFilter();
|
||||
getFoldersApi(true);
|
||||
|
|
@ -152,6 +159,47 @@ export default function ComponentsComponent({
|
|||
});
|
||||
};
|
||||
|
||||
const handleImport = () => {
|
||||
uploadFlow({ newProject: true, isComponent: false })
|
||||
.then(() => {
|
||||
resetFilter();
|
||||
getFoldersApi(true);
|
||||
if (!folderId || folderId === myCollectionId) {
|
||||
getFolderById(folderId ? folderId : myCollectionId);
|
||||
}
|
||||
setSelectedFlowsComponentsCards([]);
|
||||
|
||||
setSuccessData({ title: "Flows imported successfully" });
|
||||
})
|
||||
.catch((error) => {
|
||||
setErrorData({
|
||||
title: UPLOAD_ERROR_ALERT,
|
||||
list: [error],
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const version = useDarkStore((state) => state.version);
|
||||
|
||||
const handleExport = () => {
|
||||
selectedFlowsComponentsCards.map((selectedFlowId) => {
|
||||
const selectedFlow = allFlows.find((flow) => flow.id === selectedFlowId);
|
||||
downloadFlow(
|
||||
removeApiKeys({
|
||||
id: selectedFlow!.id,
|
||||
data: selectedFlow!.data!,
|
||||
description: selectedFlow!.description,
|
||||
name: selectedFlow!.name,
|
||||
last_tested_version: version,
|
||||
is_component: false,
|
||||
}),
|
||||
selectedFlow!.name,
|
||||
selectedFlow!.description,
|
||||
);
|
||||
});
|
||||
setSuccessData({ title: "Flows exported successfully" });
|
||||
};
|
||||
|
||||
const handleDeleteMultiple = () => {
|
||||
removeFlow(selectedFlowsComponentsCards)
|
||||
.then(() => {
|
||||
|
|
@ -161,7 +209,7 @@ export default function ComponentsComponent({
|
|||
getFolderById(folderId ? folderId : myCollectionId);
|
||||
}
|
||||
setSuccessData({
|
||||
title: "Selected items deleted successfully!",
|
||||
title: "Selected items deleted successfully",
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
|
|
@ -180,7 +228,7 @@ export default function ComponentsComponent({
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
setSelectedFlowsComponentsCards(selectedFlows);
|
||||
|
|
@ -215,20 +263,23 @@ export default function ComponentsComponent({
|
|||
if (type === "all") return allFlows?.length;
|
||||
|
||||
return allFlows?.filter(
|
||||
(f) => (f.is_component ?? false) === (type === "component")
|
||||
(f) => (f.is_component ?? false) === (type === "component"),
|
||||
)?.length;
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{allFlows?.length > 0 && (
|
||||
<HeaderComponent
|
||||
handleDelete={() => handleSelectOptionsChange("delete")}
|
||||
handleSelectAll={handleSelectAll}
|
||||
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
|
||||
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
|
||||
/>
|
||||
)}
|
||||
<div className="flex w-full gap-4 pb-5">
|
||||
{allFlows?.length > 0 && (
|
||||
<HeaderComponent
|
||||
handleDelete={() => handleSelectOptionsChange("delete")}
|
||||
handleSelectAll={handleSelectAll}
|
||||
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
|
||||
handleExport={() => handleSelectOptionsChange("export")}
|
||||
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<CardsWrapComponent
|
||||
onFileDrop={handleFileDrop}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ const EmptyComponent = ({}: EmptyComponentProps) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="mt-6 flex w-full items-center justify-center text-center">
|
||||
<div className="mt-2 flex w-full items-center justify-center text-center">
|
||||
<div className="flex-max-width h-full flex-col">
|
||||
<div className="align-center flex w-full justify-center gap-1 ">
|
||||
<span className="text-muted-foreground">
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
import { useState } from "react";
|
||||
import IconComponent from "../../../../components/genericIconComponent";
|
||||
import IconComponent, {
|
||||
ForwardedIconComponent,
|
||||
} from "../../../../components/genericIconComponent";
|
||||
import ShadTooltip from "../../../../components/shadTooltipComponent";
|
||||
import { Checkbox } from "../../../../components/ui/checkbox";
|
||||
import { cn } from "../../../../utils/utils";
|
||||
import { Button } from "../../../../components/ui/button";
|
||||
|
||||
type HeaderComponentProps = {
|
||||
handleSelectAll: (select) => void;
|
||||
handleDelete: () => void;
|
||||
handleDuplicate: () => void;
|
||||
handleExport: () => void;
|
||||
disableFunctions: boolean;
|
||||
};
|
||||
|
||||
|
|
@ -15,6 +19,7 @@ const HeaderComponent = ({
|
|||
handleSelectAll,
|
||||
handleDelete,
|
||||
handleDuplicate,
|
||||
handleExport,
|
||||
disableFunctions,
|
||||
}: HeaderComponentProps) => {
|
||||
const [shouldSelectAll, setShouldSelectAll] = useState(true);
|
||||
|
|
@ -26,29 +31,46 @@ const HeaderComponent = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div className="grid grid-cols-3 pb-5">
|
||||
<div className="col-auto grid-cols-1 self-center justify-self-start">
|
||||
<a onClick={handleClick} className="text-sm">
|
||||
<div className="header-menu-bar-display ">
|
||||
<div
|
||||
className="header-menu-flow-name"
|
||||
data-testid="select_all_collection"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox checked={!shouldSelectAll} id="terms" />
|
||||
<label
|
||||
onClick={handleClick}
|
||||
htmlFor="terms"
|
||||
className="label cursor-pointer text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||
>
|
||||
{shouldSelectAll ? "Select All" : "Unselect All"}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between gap-4">
|
||||
<div className="flex items-center justify-self-start">
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
onClick={handleClick}
|
||||
className="text-sm"
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox checked={!shouldSelectAll} id="terms" />
|
||||
<span className="label text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
|
||||
{shouldSelectAll ? "Select All" : "Unselect All"}
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="col-span-2 flex grid-cols-1 gap-2 justify-self-end">
|
||||
<div className="flex items-center gap-2">
|
||||
<div>
|
||||
<ShadTooltip
|
||||
content={
|
||||
disableFunctions ? (
|
||||
<span>Select items to export</span>
|
||||
) : (
|
||||
<span>Export selected items</span>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
onClick={handleExport}
|
||||
disabled={disableFunctions}
|
||||
>
|
||||
<IconComponent
|
||||
name="FileDown"
|
||||
className={cn("h-5 w-5 text-primary transition-all")}
|
||||
/>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
<div>
|
||||
<ShadTooltip
|
||||
content={
|
||||
|
|
@ -59,12 +81,17 @@ const HeaderComponent = ({
|
|||
)
|
||||
}
|
||||
>
|
||||
<button onClick={handleDuplicate} disabled={disableFunctions}>
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
onClick={handleDuplicate}
|
||||
disabled={disableFunctions}
|
||||
>
|
||||
<IconComponent
|
||||
name="Copy"
|
||||
className={cn("h-5 w-5 text-primary transition-all")}
|
||||
/>
|
||||
</button>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -77,15 +104,20 @@ const HeaderComponent = ({
|
|||
)
|
||||
}
|
||||
>
|
||||
<button onClick={handleDelete} disabled={disableFunctions}>
|
||||
<Button
|
||||
variant="none"
|
||||
size="none"
|
||||
onClick={handleDelete}
|
||||
disabled={disableFunctions}
|
||||
>
|
||||
<IconComponent
|
||||
name="Trash2"
|
||||
className={cn(
|
||||
"h-5 w-5 text-primary transition-all",
|
||||
disableFunctions ? "" : "hover:text-destructive"
|
||||
disableFunctions ? "" : "hover:text-destructive",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
</Button>
|
||||
</ShadTooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import useFlowsManagerStore from "../../stores/flowsManagerStore";
|
|||
export default function SettingsPage(): JSX.Element {
|
||||
const pathname = location.pathname;
|
||||
const setCurrentFlowId = useFlowsManagerStore(
|
||||
(state) => state.setCurrentFlowId
|
||||
(state) => state.setCurrentFlowId,
|
||||
);
|
||||
useEffect(() => {
|
||||
setCurrentFlowId("");
|
||||
|
|
@ -59,7 +59,10 @@ export default function SettingsPage(): JSX.Element {
|
|||
title: "Messages",
|
||||
href: "/settings/messages",
|
||||
icon: (
|
||||
<ForwardedIconComponent name="Keyboard" className="w-5 stroke-[1.5]" />
|
||||
<ForwardedIconComponent
|
||||
name="MessagesSquare"
|
||||
className="w-4 flex-shrink-0 justify-start stroke-[1.5]"
|
||||
/>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
|
@ -72,8 +75,10 @@ export default function SettingsPage(): JSX.Element {
|
|||
<aside className="flex h-full shrink-0 flex-col space-y-6 lg:w-[20vw]">
|
||||
<SidebarNav items={sidebarNavItems} />
|
||||
</aside>
|
||||
<div className="h-full w-full flex-1 pb-8">
|
||||
<Outlet />
|
||||
<div className="flex h-full w-full flex-1 flex-col">
|
||||
<div className="flex-1 pb-8">
|
||||
<Outlet />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PageLayout>
|
||||
|
|
|
|||
|
|
@ -52,23 +52,17 @@ export default function ApiKeysPage() {
|
|||
/>
|
||||
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
||||
<CardContent className="h-full">
|
||||
<TableComponent
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(
|
||||
event.api.getSelectedRows().map((row) => row.id),
|
||||
);
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={columnDefs}
|
||||
rowData={keysList.current}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<TableComponent
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(event.api.getSelectedRows().map((row) => row.id));
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={columnDefs}
|
||||
rowData={keysList.current}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -16,13 +16,13 @@ import { cn } from "../../../../utils/utils";
|
|||
|
||||
export default function GlobalVariablesPage() {
|
||||
const globalVariablesEntries = useGlobalVariablesStore(
|
||||
(state) => state.globalVariablesEntries
|
||||
(state) => state.globalVariablesEntries,
|
||||
);
|
||||
const removeGlobalVariable = useGlobalVariablesStore(
|
||||
(state) => state.removeGlobalVariable
|
||||
(state) => state.removeGlobalVariable,
|
||||
);
|
||||
const globalVariables = useGlobalVariablesStore(
|
||||
(state) => state.globalVariables
|
||||
(state) => state.globalVariables,
|
||||
);
|
||||
const setErrorData = useAlertStore((state) => state.setErrorData);
|
||||
const getVariableId = useGlobalVariablesStore((state) => state.getVariableId);
|
||||
|
|
@ -154,7 +154,7 @@ export default function GlobalVariablesPage() {
|
|||
<IconComponent
|
||||
name="Trash2"
|
||||
className={cn(
|
||||
"h-5 w-5 text-destructive group-disabled:text-primary"
|
||||
"h-5 w-5 text-destructive group-disabled:text-primary",
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
|
|
@ -168,23 +168,17 @@ export default function GlobalVariablesPage() {
|
|||
</div>
|
||||
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
||||
<CardContent className="h-full">
|
||||
<TableComponent
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(
|
||||
event.api.getSelectedRows().map((row) => row.name)
|
||||
);
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={colDefs}
|
||||
rowData={rowData}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<TableComponent
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(event.api.getSelectedRows().map((row) => row.name));
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={colDefs}
|
||||
rowData={rowData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -115,15 +115,11 @@ export default function ShortcutsPage() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
||||
<CardContent className="h-full">
|
||||
<TableComponent
|
||||
pagination={false}
|
||||
columnDefs={colDefs}
|
||||
rowData={nodesRowData}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<TableComponent
|
||||
pagination={false}
|
||||
columnDefs={colDefs}
|
||||
rowData={nodesRowData}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export default function MessagesPage() {
|
|||
setSelectedRows,
|
||||
setSuccessData,
|
||||
setErrorData,
|
||||
selectedRows
|
||||
selectedRows,
|
||||
);
|
||||
|
||||
const { handleUpdate } = useUpdateMessage(setSuccessData, setErrorData);
|
||||
|
|
@ -52,29 +52,25 @@ export default function MessagesPage() {
|
|||
handleRemoveMessages={handleRemoveMessages}
|
||||
/>
|
||||
|
||||
<div className="flex h-full w-full flex-col justify-between pb-8">
|
||||
<Card x-chunk="dashboard-04-chunk-2" className="h-full pt-4">
|
||||
<CardContent className="h-full">
|
||||
<TableComponent
|
||||
readOnlyEdit
|
||||
onCellEditRequest={(event) => {
|
||||
handleUpdateMessage(event);
|
||||
}}
|
||||
editable={["Sender Name", "Message"]}
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(
|
||||
event.api.getSelectedRows().map((row) => row.index)
|
||||
);
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={columns}
|
||||
rowData={messages}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<div className="flex h-full w-full flex-col justify-between">
|
||||
<TableComponent
|
||||
readOnlyEdit
|
||||
onCellEditRequest={(event) => {
|
||||
handleUpdateMessage(event);
|
||||
}}
|
||||
editable={["Sender Name", "Message"]}
|
||||
overlayNoRowsTemplate="No data available"
|
||||
onSelectionChanged={(event: SelectionChangedEvent) => {
|
||||
setSelectedRows(
|
||||
event.api.getSelectedRows().map((row) => row.index),
|
||||
);
|
||||
}}
|
||||
rowSelection="multiple"
|
||||
suppressRowClickSelection={true}
|
||||
pagination={true}
|
||||
columnDefs={columns}
|
||||
rowData={messages}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from "../pages/MainPage/services";
|
||||
import { FoldersStoreType } from "../types/zustand/folders";
|
||||
import useFlowsManagerStore from "./flowsManagerStore";
|
||||
import { uploadFlowsToDatabase } from "../controllers/API";
|
||||
|
||||
export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
||||
folders: [],
|
||||
|
|
@ -17,18 +18,18 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
|||
getFolders().then(
|
||||
(res) => {
|
||||
const foldersWithoutStarterProjects = res.filter(
|
||||
(folder) => folder.name !== STARTER_FOLDER_NAME
|
||||
(folder) => folder.name !== STARTER_FOLDER_NAME,
|
||||
);
|
||||
|
||||
const starterProjects = res.find(
|
||||
(folder) => folder.name === STARTER_FOLDER_NAME
|
||||
(folder) => folder.name === STARTER_FOLDER_NAME,
|
||||
);
|
||||
|
||||
set({ starterProjectId: starterProjects!.id ?? "" });
|
||||
set({ folders: foldersWithoutStarterProjects });
|
||||
|
||||
const myCollectionId = res?.find(
|
||||
(f) => f.name === DEFAULT_FOLDER
|
||||
(f) => f.name === DEFAULT_FOLDER,
|
||||
)?.id;
|
||||
|
||||
set({ myCollectionId });
|
||||
|
|
@ -45,7 +46,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
|||
set({ folders: [] });
|
||||
get().setLoading(false);
|
||||
reject();
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
@ -65,7 +66,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
|||
},
|
||||
() => {
|
||||
get().setLoadingById(false);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
|
|
@ -100,7 +101,7 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
|||
folderIdDragging: "",
|
||||
setFolderIdDragging: (id) => set(() => ({ folderIdDragging: id })),
|
||||
uploadFolder: () => {
|
||||
return new Promise<void>(() => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.onchange = (event: Event) => {
|
||||
|
|
@ -111,8 +112,31 @@ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
|
|||
const file = (event.target as HTMLInputElement).files![0];
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
uploadFlowsFromFolders(formData).then(() => {
|
||||
get().getFoldersApi(true);
|
||||
file.text().then((text) => {
|
||||
const data = JSON.parse(text);
|
||||
if (data.data?.nodes) {
|
||||
useFlowsManagerStore
|
||||
.getState()
|
||||
.addFlow(true, data)
|
||||
.then(() => {
|
||||
resolve();
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
uploadFlowsFromFolders(formData)
|
||||
.then(() => {
|
||||
get()
|
||||
.getFoldersApi(true)
|
||||
.then(() => {
|
||||
resolve();
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export type FoldersStoreType = {
|
|||
setFolderUrl: (folderUrl: string) => void;
|
||||
folderDragging: boolean;
|
||||
setFolderDragging: (set: boolean) => void;
|
||||
uploadFolder: (folderId: string) => void;
|
||||
uploadFolder: (folderId: string) => Promise<void>;
|
||||
folderIdDragging: string;
|
||||
setFolderIdDragging: (id: string) => void;
|
||||
starterProjectId: string;
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ import {
|
|||
FolderIcon,
|
||||
FolderPlus,
|
||||
FolderPlusIcon,
|
||||
FolderUp,
|
||||
FormInput,
|
||||
Forward,
|
||||
Gift,
|
||||
|
|
@ -433,6 +434,7 @@ export const nodeIconsLucide: iconsType = {
|
|||
ChevronLeft,
|
||||
SlidersHorizontal,
|
||||
Palette,
|
||||
FolderUp,
|
||||
Blocks,
|
||||
ChevronDown,
|
||||
ArrowLeft,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue