refactor: folder fetching, folder sidebar and fix various issues (#3344)

* Removed unused code

* Separated folder sidebar from common sidebar

* Removed useOnFileDrop from common sidebar

* Added folderSidebarComponent that fetches the folders

* Added useOnFileDrop and SIdebarFoldersButton

* Make auth context not get folders

* Make delete folder refetch get folders

* Make folder mutations refetch the getFolder

* Create getFolder query

* Removed unused functions from useFolderStore

* Make isLoading from application depend only on flows

* Make main page not refetch folders

* Change types of folders store

* removed getFolders refetch on duplicateFolders

* Added loading from query into ComponentsComponent

* Made the flow page get the flows and types if they're not available

* Made the loading be the isLoading only

* Removed unused function

* Make cards appear even if it didnt load

* Removed setLoading of various pages

* Fixed loading happening every time the flow changes

* Added skeleton instead of loading on folders

* Added routing to contain folderId on both flows and initial page

* remove redirect of useFileDrop

* remove folderid getting from state

* removed setAllFlows

* chore: Remove unused variables and functions in useDuplicateFlows hook

* Added refetch of folders when the flow is deleted or added

* Changed redirectToLastLocation to redirect to last folder also

* Added upload flow to folder tanstack and refetched folders on upload

* Added loading of current folder on display of empty component

* Removed refetching folder on file drop

* Removed unused code

* Fixed add new flow from header not redirecting correctly

* Fixed unused code

* Added undefined on setting current flow as empty

* Added disable top bar and make it be displayed on the top of an empty folder

* Fixed select all checked when deleting all flows
This commit is contained in:
Lucas Oliveira 2024-08-14 14:40:18 -03:00 • committed by GitHub
commit 765895432e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 539 additions and 656 deletions

View file

@ -8,7 +8,7 @@ import { createFileUpload } from "@/helpers/create-file-upload";
import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist"; import { getObjectsFromFilelist } from "@/helpers/get-objects-from-filelist";
import useUploadFlow from "@/hooks/flows/use-upload-flow"; import useUploadFlow from "@/hooks/flows/use-upload-flow";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useLocation } from "react-router-dom"; import { useParams } from "react-router-dom";
import { FolderType } from "../../../../pages/MainPage/entities"; import { FolderType } from "../../../../pages/MainPage/entities";
import useAlertStore from "../../../../stores/alertStore"; import useAlertStore from "../../../../stores/alertStore";
import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore";
@ -21,30 +21,32 @@ import IconComponent, {
import { Button, buttonVariants } from "../../../ui/button"; import { Button, buttonVariants } from "../../../ui/button";
import { Input } from "../../../ui/input"; import { Input } from "../../../ui/input";
import useFileDrop from "../../hooks/use-on-file-drop"; import useFileDrop from "../../hooks/use-on-file-drop";
import { SidebarFolderSkeleton } from "../sidebarFolderSkeleton";
type SideBarFoldersButtonsComponentProps = { type SideBarFoldersButtonsComponentProps = {
pathname: string; pathname: string;
handleChangeFolder?: (id: string) => void; handleChangeFolder?: (id: string) => void;
handleDeleteFolder?: (item: FolderType) => void; handleDeleteFolder?: (item: FolderType) => void;
folders: FolderType[] | undefined;
loading?: boolean;
}; };
const SideBarFoldersButtonsComponent = ({ const SideBarFoldersButtonsComponent = ({
pathname, pathname,
handleChangeFolder, handleChangeFolder,
handleDeleteFolder, handleDeleteFolder,
folders = [],
loading,
}: SideBarFoldersButtonsComponentProps) => { }: SideBarFoldersButtonsComponentProps) => {
const refInput = useRef<HTMLInputElement>(null); const refInput = useRef<HTMLInputElement>(null);
const setFolders = useFolderStore((state) => state.setFolders);
const folders = useFolderStore((state) => state.folders);
const [foldersNames, setFoldersNames] = useState({}); const [foldersNames, setFoldersNames] = useState({});
const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot);
const [editFolders, setEditFolderName] = useState( const [editFolders, setEditFolderName] = useState(
folders.map((obj) => ({ name: obj.name, edit: false })), folders.map((obj) => ({ name: obj.name, edit: false })) ?? [],
); );
const currentFolder = pathname.split("/"); const currentFolder = pathname.split("/");
const urlWithoutPath = pathname.split("/").length < 4; const urlWithoutPath = pathname.split("/").length < 4;
const myCollectionId = useFolderStore((state) => state.myCollectionId); const myCollectionId = useFolderStore((state) => state.myCollectionId);
const folderIdDragging = useFolderStore((state) => state.folderIdDragging); const folderIdDragging = useFolderStore((state) => state.folderIdDragging);
const refreshFolders = useFolderStore((state) => state.refreshFolders);
const checkPathName = (itemId: string) => { const checkPathName = (itemId: string) => {
if (urlWithoutPath && itemId === myCollectionId) { if (urlWithoutPath && itemId === myCollectionId) {
@ -52,22 +54,12 @@ const SideBarFoldersButtonsComponent = ({
} }
return currentFolder.includes(itemId); return currentFolder.includes(itemId);
}; };
const location = useLocation(); const folderId = useParams().folderId ?? myCollectionId ?? "";
const folderId = location?.state?.folderId ?? myCollectionId;
const getFolderById = useFolderStore((state) => state.getFolderById);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const uploadFlow = useUploadFlow(); const uploadFlow = useUploadFlow();
const handleFolderChange = () => { const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(folderId);
getFolderById(folderId);
};
const { dragOver, dragEnter, dragLeave, onDrop } = useFileDrop(
folderId,
handleFolderChange,
);
const { mutate } = usePostUploadFolders(); const { mutate } = usePostUploadFolders();
@ -76,7 +68,6 @@ const SideBarFoldersButtonsComponent = ({
getObjectsFromFilelist<any>(files).then((objects) => { getObjectsFromFilelist<any>(files).then((objects) => {
if (objects.every((flow) => flow.data?.nodes)) { if (objects.every((flow) => flow.data?.nodes)) {
uploadFlow({ files }).then(() => { uploadFlow({ files }).then(() => {
getFolderById(folderId);
setSuccessData({ setSuccessData({
title: "Uploaded successfully", title: "Uploaded successfully",
}); });
@ -89,7 +80,6 @@ const SideBarFoldersButtonsComponent = ({
{ formData }, { formData },
{ {
onSuccess: () => { onSuccess: () => {
getFoldersApi(true);
setSuccessData({ setSuccessData({
title: "Folder uploaded successfully.", title: "Folder uploaded successfully.",
}); });
@ -146,20 +136,13 @@ const SideBarFoldersButtonsComponent = ({
const { mutate: mutateUpdateFolder } = usePatchFolders(); const { mutate: mutateUpdateFolder } = usePatchFolders();
function addNewFolder() { function addNewFolder() {
mutateAddFolder( mutateAddFolder({
{ data: {
data: { name: "New Folder",
name: "New Folder", parent_id: null,
parent_id: null, description: "",
description: "",
},
}, },
{ });
onSuccess: () => {
refreshFolders();
},
},
);
} }
function handleEditFolderName(e, name): void { function handleEditFolderName(e, name): void {
@ -173,7 +156,7 @@ const SideBarFoldersButtonsComponent = ({
} }
useEffect(() => { useEffect(() => {
folders.map((obj) => ({ name: obj.name, edit: false })); setEditFolderName(folders.map((obj) => ({ name: obj.name, edit: false })));
}, [folders]); }, [folders]);
const handleEditNameFolder = async (item) => { const handleEditNameFolder = async (item) => {
@ -210,7 +193,6 @@ const SideBarFoldersButtonsComponent = ({
const updateFolders = [...folders]; const updateFolders = [...folders];
updateFolders[updatedFolderIndex] = updatedFolder; updateFolders[updatedFolderIndex] = updatedFolder;
setFolders(updateFolders);
setFoldersNames({}); setFoldersNames({});
setEditFolderName( setEditFolderName(
folders.map((obj) => ({ folders.map((obj) => ({
@ -255,156 +237,163 @@ const SideBarFoldersButtonsComponent = ({
<div className="flex gap-2 overflow-auto lg:h-[70vh] lg:flex-col"> <div className="flex gap-2 overflow-auto lg:h-[70vh] lg:flex-col">
<> <>
{folders.map((item, index) => { {!loading ? (
const editFolderName = editFolders?.filter( folders.map((item, index) => {
(folder) => folder.name === item.name, const editFolderName = editFolders?.filter(
)[0]; (folder) => folder.name === item.name,
return ( )[0];
<div return (
onDragOver={(e) => dragOver(e, item.id!)}
onDragEnter={(e) => dragEnter(e, item.id!)}
onDragLeave={dragLeave}
onDrop={(e) => onDrop(e, item.id!)}
key={item.id}
data-testid={`sidebar-nav-${item.name}`}
className={cn(
buttonVariants({ variant: "ghost" }),
checkPathName(item.id!)
? "border border-border bg-muted hover:bg-muted"
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
folderIdDragging === item.id! ? "bg-border" : "",
)}
onClick={() => handleChangeFolder!(item.id!)}
>
<div <div
onDoubleClick={(event) => { onDragOver={(e) => dragOver(e, item.id!)}
if (item.name === "My Projects") { onDragEnter={(e) => dragEnter(e, item.id!)}
return; onDragLeave={dragLeave}
} onDrop={(e) => onDrop(e, item.id!)}
key={item.id}
data-testid={`sidebar-nav-${item.name}`}
className={cn(
buttonVariants({ variant: "ghost" }),
checkPathName(item.id!)
? "border border-border bg-muted hover:bg-muted"
: "border hover:bg-transparent lg:border-transparent lg:hover:border-border",
"group flex w-full shrink-0 cursor-pointer gap-2 opacity-100 lg:min-w-full",
folderIdDragging === item.id! ? "bg-border" : "",
)}
onClick={() => handleChangeFolder!(item.id!)}
>
<div
onDoubleClick={(event) => {
if (item.name === "My Projects") {
return;
}
if (!foldersNames[item.name]) { if (!foldersNames[item.name]) {
setFoldersNames({ [item.name]: item.name }); setFoldersNames({ [item.name]: item.name });
} }
if ( if (
editFolders.find((obj) => obj.name === item.name)?.name editFolders.find((obj) => obj.name === item.name)?.name
) { ) {
const newEditFolders = editFolders.map((obj) => { const newEditFolders = editFolders.map((obj) => {
if (obj.name === item.name) { if (obj.name === item.name) {
return { name: item.name, edit: true }; return { name: item.name, edit: true };
} }
return { name: obj.name, edit: false }; return { name: obj.name, edit: false };
}); });
setEditFolderName(newEditFolders); setEditFolderName(newEditFolders);
takeSnapshot();
event.stopPropagation();
event.preventDefault();
return;
}
setEditFolderName((old) => [
...old,
{ name: item.name, edit: true },
]);
setFoldersNames((oldFolder) => ({
...oldFolder,
[item.name]: item.name,
}));
takeSnapshot(); takeSnapshot();
event.stopPropagation(); event.stopPropagation();
event.preventDefault(); event.preventDefault();
return; }}
} className="flex w-full items-center gap-2"
>
setEditFolderName((old) => [ <IconComponent
...old, name={"folder"}
{ name: item.name, edit: true }, className="mr-2 w-4 flex-shrink-0 justify-start stroke-[1.5] opacity-100"
]); />
setFoldersNames((oldFolder) => ({ {editFolderName?.edit ? (
...oldFolder, <div>
[item.name]: item.name, <Input
})); className="w-36"
takeSnapshot(); onChange={(e) => {
event.stopPropagation(); handleEditFolderName(e, item.name);
event.preventDefault(); }}
}} ref={refInput}
className="flex w-full items-center gap-2" onKeyDown={(e) => {
> if (e.key === "Escape") {
<IconComponent const newEditFolders = editFolders.map((obj) => {
name={"folder"} if (obj.name === item.name) {
className="mr-2 w-4 flex-shrink-0 justify-start stroke-[1.5] opacity-100" return { name: item.name, edit: false };
/> }
{editFolderName?.edit ? ( return { name: obj.name, edit: false };
<div> });
<Input setEditFolderName(newEditFolders);
className="w-36" setFoldersNames({});
onChange={(e) => { setEditFolderName(
handleEditFolderName(e, item.name); folders.map((obj) => ({
name: obj.name,
edit: false,
})),
);
}
if (e.key === "Enter") {
refInput.current?.blur();
}
handleKeyDown(e, e.key, "");
}}
autoFocus={true}
onBlur={async () => {
if (refInput.current?.value !== item.name) {
handleEditNameFolder(item);
} else {
editFolderName.edit = false;
}
}}
value={foldersNames[item.name]}
id={`input-folder-${item.name}`}
data-testid={`input-folder`}
/>
</div>
) : (
<span className="block w-full truncate opacity-100">
{item.name}
</span>
)}
{index > 0 && (
<Button
data-testid="btn-delete-folder"
className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]"
onClick={(e) => {
handleDeleteFolder!(item);
e.stopPropagation();
e.preventDefault();
}} }}
ref={refInput} variant={"ghost"}
onKeyDown={(e) => { size={"icon"}
if (e.key === "Escape") { >
const newEditFolders = editFolders.map((obj) => { <IconComponent
if (obj.name === item.name) { name={"trash"}
return { name: item.name, edit: false }; className="w-4 stroke-[1.5] p-0"
} />
return { name: obj.name, edit: false }; </Button>
}); )}
setEditFolderName(newEditFolders);
setFoldersNames({});
setEditFolderName(
folders.map((obj) => ({
name: obj.name,
edit: false,
})),
);
}
if (e.key === "Enter") {
refInput.current?.blur();
}
handleKeyDown(e, e.key, "");
}}
autoFocus={true}
onBlur={async () => {
if (refInput.current?.value !== item.name) {
handleEditNameFolder(item);
} else {
editFolderName.edit = false;
}
}}
value={foldersNames[item.name]}
id={`input-folder-${item.name}`}
data-testid={`input-folder`}
/>
</div>
) : (
<span className="block w-full truncate opacity-100">
{item.name}
</span>
)}
{index > 0 && (
<Button <Button
data-testid="btn-delete-folder"
className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]" className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]"
onClick={(e) => { onClick={(e) => {
handleDeleteFolder!(item); handleDownloadFolder(item.id!);
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}} }}
variant={"ghost"} unstyled
size={"icon"}
> >
<IconComponent <IconComponent
name={"trash"} name={"Download"}
className="w-4 stroke-[1.5] p-0" className="w-4 stroke-[1.5] text-white"
/> />
</Button> </Button>
)} </div>
<Button
className="hidden p-0 hover:bg-white group-hover:block hover:dark:bg-[#0c101a00]"
onClick={(e) => {
handleDownloadFolder(item.id!);
e.stopPropagation();
e.preventDefault();
}}
unstyled
>
<IconComponent
name={"Download"}
className="w-4 stroke-[1.5] text-white"
/>
</Button>
</div> </div>
</div> );
); })
})} ) : (
<>
<SidebarFolderSkeleton />
<SidebarFolderSkeleton />
</>
)}
</> </>
</div> </div>
</> </>

View file

@ -0,0 +1,10 @@
import { Skeleton } from "@/components/ui/skeleton";
export function SidebarFolderSkeleton() {
return (
<div className="flex h-10 w-full shrink-0 animate-pulse cursor-pointer items-center gap-4 rounded-md border bg-background px-4 opacity-100 lg:min-w-full">
<Skeleton className="h-3 w-4 rounded-full" />
<Skeleton className="h-3 w-[40%]" />
</div>
);
}

View file

@ -1,33 +1,24 @@
import { usePostUploadFlowToFolder } from "@/controllers/API/queries/folders/use-post-upload-to-folder";
import useSaveFlow from "@/hooks/flows/use-save-flow"; import useSaveFlow from "@/hooks/flows/use-save-flow";
import { import {
UPLOAD_ALERT_LIST, UPLOAD_ALERT_LIST,
WRONG_FILE_ERROR_ALERT, WRONG_FILE_ERROR_ALERT,
} from "../../../constants/alerts_constants"; } from "../../../constants/alerts_constants";
import { uploadFlowToFolder } from "../../../pages/MainPage/services";
import useAlertStore from "../../../stores/alertStore"; import useAlertStore from "../../../stores/alertStore";
import useFlowsManagerStore from "../../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../../stores/flowsManagerStore";
import { useFolderStore } from "../../../stores/foldersStore"; import { useFolderStore } from "../../../stores/foldersStore";
import { addVersionToDuplicates } from "../../../utils/reactflowUtils"; import { addVersionToDuplicates } from "../../../utils/reactflowUtils";
const useFileDrop = ( const useFileDrop = (folderId: string) => {
folderId: string,
folderChangeCallback: (folderId: string) => void,
) => {
const setFolderDragging = useFolderStore((state) => state.setFolderDragging); const setFolderDragging = useFolderStore((state) => state.setFolderDragging);
const setFolderIdDragging = useFolderStore( const setFolderIdDragging = useFolderStore(
(state) => state.setFolderIdDragging, (state) => state.setFolderIdDragging,
); );
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const refreshFolders = useFolderStore((state) => state.refreshFolders);
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const saveFlow = useSaveFlow(); const saveFlow = useSaveFlow();
const { mutate: uploadFlowToFolder } = usePostUploadFlowToFolder();
const triggerFolderChange = (folderId) => {
if (folderChangeCallback) {
folderChangeCallback(folderId);
}
};
const handleFileDrop = async (e) => { const handleFileDrop = async (e) => {
if (e.dataTransfer.types.some((type) => type === "Files")) { if (e.dataTransfer.types.some((type) => type === "Files")) {
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) { if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
@ -121,10 +112,7 @@ const useFileDrop = (
setFolderDragging(false); setFolderDragging(false);
setFolderIdDragging(""); setFolderIdDragging("");
saveFlow(updatedFlow).then(() => { saveFlow(updatedFlow);
refreshFolders();
triggerFolderChange(folderId);
});
}; };
const uploadFormData = (data) => { const uploadFormData = (data) => {
@ -133,10 +121,7 @@ const useFileDrop = (
setFolderDragging(false); setFolderDragging(false);
setFolderIdDragging(""); setFolderIdDragging("");
uploadFlowToFolder(formData, folderId).then(() => { uploadFlowToFolder({ flows: formData, folderId });
refreshFolders();
triggerFolderChange(folderId);
});
}; };
return { return {

View file

@ -0,0 +1,38 @@
import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
import { useLocation } from "react-router-dom";
import { FolderType } from "../../pages/MainPage/entities";
import { cn } from "../../utils/utils";
import HorizontalScrollFadeComponent from "../horizontalScrollFadeComponent";
import SideBarFoldersButtonsComponent from "./components/sideBarFolderButtons";
type SidebarNavProps = {
handleChangeFolder?: (id: string) => void;
handleDeleteFolder?: (item: FolderType) => void;
className?: string;
};
export default function FolderSidebarNav({
className,
handleChangeFolder,
handleDeleteFolder,
...props
}: SidebarNavProps) {
const location = useLocation();
const pathname = location.pathname;
const { data: folders, isPending } = useGetFoldersQuery();
return (
<nav className={cn(className)} {...props}>
<HorizontalScrollFadeComponent>
<SideBarFoldersButtonsComponent
loading={isPending || !folders}
pathname={pathname}
handleChangeFolder={handleChangeFolder}
handleDeleteFolder={handleDeleteFolder}
folders={folders}
/>
</HorizontalScrollFadeComponent>
</nav>
);
}

View file

@ -54,6 +54,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow); const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
const updatedAt = currentSavedFlow?.updated_at; const updatedAt = currentSavedFlow?.updated_at;
const onFlowPage = useFlowStore((state) => state.onFlowPage); const onFlowPage = useFlowStore((state) => state.onFlowPage);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const changesNotSaved = const changesNotSaved =
customStringify(currentFlow) !== customStringify(currentSavedFlow) && customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
@ -71,6 +72,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
function handleAddFlow() { function handleAddFlow() {
try { try {
addFlow().then((id) => { addFlow().then((id) => {
setCurrentFlow(undefined); // Reset current flow for useEffect of flowPage to update the current flow
navigate("/flow/" + id); navigate("/flow/" + id);
}); });
} catch (err) { } catch (err) {

View file

@ -55,16 +55,12 @@ export default function Header(): JSX.Element {
}`; }`;
const redirectToLastLocation = () => { const redirectToLastLocation = () => {
const lastFlowVisitedIndex = routeHistory const lastVisitedIndex = routeHistory
.reverse() .reverse()
.findIndex( .findIndex((path) => path !== location.pathname);
(path) => path.includes("/flow/") && path !== location.pathname,
);
const lastFlowVisited = routeHistory[lastFlowVisitedIndex]; const lastFlowVisited = routeHistory[lastVisitedIndex];
lastFlowVisited && !location.pathname.includes("/flow") lastFlowVisited ? navigate(lastFlowVisited) : navigate("/all");
? navigate(lastFlowVisited)
: navigate("/all");
}; };
const visitedFlowPathBefore = () => { const visitedFlowPathBefore = () => {
@ -108,7 +104,7 @@ export default function Header(): JSX.Element {
</div> </div>
<div className="round-button-div"> <div className="round-button-div">
<Link to="/"> <Link to="/all">
<Button <Button
className="gap-2" className="gap-2"
variant={ variant={

View file

@ -1,10 +1,7 @@
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import { FolderType } from "../../pages/MainPage/entities";
import { useFolderStore } from "../../stores/foldersStore";
import { cn } from "../../utils/utils"; import { cn } from "../../utils/utils";
import HorizontalScrollFadeComponent from "../horizontalScrollFadeComponent"; import HorizontalScrollFadeComponent from "../horizontalScrollFadeComponent";
import SideBarButtonsComponent from "./components/sideBarButtons"; import SideBarButtonsComponent from "./components/sideBarButtons";
import SideBarFoldersButtonsComponent from "./components/sideBarFolderButtons";
type SidebarNavProps = { type SidebarNavProps = {
items: { items: {
@ -12,44 +9,21 @@ type SidebarNavProps = {
title: string; title: string;
icon: React.ReactNode; icon: React.ReactNode;
}[]; }[];
handleChangeFolder?: (id: string) => void;
handleEditFolder?: (item: FolderType) => void;
handleDeleteFolder?: (item: FolderType) => void;
className?: string; className?: string;
}; };
export default function SidebarNav({ export default function SidebarNav({
className, className,
items, items,
handleChangeFolder,
handleEditFolder,
handleDeleteFolder,
...props ...props
}: SidebarNavProps) { }: SidebarNavProps) {
const location = useLocation(); const location = useLocation();
const pathname = location.pathname; const pathname = location.pathname;
const loadingFolders = useFolderStore((state) => state.isLoadingFolders);
const folders = useFolderStore((state) => state.folders);
const pathValues = ["folder", "components", "flows", "all"];
const isFolderPath = pathValues.some((value) => pathname.includes(value));
// Ensure all conditions are covered and provide a default case if necessary
const sidebarContent =
items.length > 0 ? (
<SideBarButtonsComponent items={items} pathname={pathname} />
) : !loadingFolders && folders?.length > 0 && isFolderPath ? (
<SideBarFoldersButtonsComponent
pathname={pathname}
handleChangeFolder={handleChangeFolder}
handleDeleteFolder={handleDeleteFolder}
/>
) : null;
return ( return (
<nav className={cn(className)} {...props}> <nav className={cn(className)} {...props}>
<HorizontalScrollFadeComponent> <HorizontalScrollFadeComponent>
{sidebarContent!} <SideBarButtonsComponent items={items} pathname={pathname} />
</HorizontalScrollFadeComponent> </HorizontalScrollFadeComponent>
</nav> </nav>
); );

View file

@ -8,7 +8,6 @@ import useAuthStore from "@/stores/authStore";
import { createContext, useEffect, useState } from "react"; import { createContext, useEffect, useState } from "react";
import Cookies from "universal-cookie"; import Cookies from "universal-cookie";
import useAlertStore from "../stores/alertStore"; import useAlertStore from "../stores/alertStore";
import { useFolderStore } from "../stores/foldersStore";
import { useStoreStore } from "../stores/storeStore"; import { useStoreStore } from "../stores/storeStore";
import { Users } from "../types/api"; import { Users } from "../types/api";
import { AuthContextType } from "../types/contexts/auth"; import { AuthContextType } from "../types/contexts/auth";
@ -38,8 +37,6 @@ export function AuthProvider({ children }): React.ReactElement {
cookies.get(LANGFLOW_API_TOKEN), cookies.get(LANGFLOW_API_TOKEN),
); );
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const checkHasStore = useStoreStore((state) => state.checkHasStore); const checkHasStore = useStoreStore((state) => state.checkHasStore);
const fetchApiData = useStoreStore((state) => state.fetchApiData); const fetchApiData = useStoreStore((state) => state.fetchApiData);
const setIsAuthenticated = useAuthStore((state) => state.setIsAuthenticated); const setIsAuthenticated = useAuthStore((state) => state.setIsAuthenticated);
@ -68,7 +65,6 @@ export function AuthProvider({ children }): React.ReactElement {
setUserData(user); setUserData(user);
const isSuperUser = user!.is_superuser; const isSuperUser = user!.is_superuser;
useAuthStore.getState().setIsAdmin(isSuperUser); useAuthStore.getState().setIsAdmin(isSuperUser);
getFoldersApi(true, true);
checkHasStore(); checkHasStore();
fetchApiData(); fetchApiData();
}, },

View file

@ -12,7 +12,7 @@ export const useDeleteDeleteFlows: useMutationFunctionType<
undefined, undefined,
IDeleteFlows IDeleteFlows
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const deleteFlowsFn = async (payload: IDeleteFlows): Promise<any> => { const deleteFlowsFn = async (payload: IDeleteFlows): Promise<any> => {
const response = await api.delete<any>(`${getURL("FLOWS")}/`, { const response = await api.delete<any>(`${getURL("FLOWS")}/`, {
@ -25,7 +25,12 @@ export const useDeleteDeleteFlows: useMutationFunctionType<
const mutation: UseMutationResult<IDeleteFlows, any, IDeleteFlows> = mutate( const mutation: UseMutationResult<IDeleteFlows, any, IDeleteFlows> = mutate(
["useLoginUser"], ["useLoginUser"],
deleteFlowsFn, deleteFlowsFn,
options, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolder"] });
},
},
); );
return mutation; return mutation;

View file

@ -18,7 +18,7 @@ export const usePatchUpdateFlow: useMutationFunctionType<
undefined, undefined,
IPatchUpdateFlow IPatchUpdateFlow
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const PatchUpdateFlowFn = async (payload: IPatchUpdateFlow): Promise<any> => { const PatchUpdateFlowFn = async (payload: IPatchUpdateFlow): Promise<any> => {
const response = await api.patch(`${getURL("FLOWS")}/${payload.id}`, { const response = await api.patch(`${getURL("FLOWS")}/${payload.id}`, {
@ -33,7 +33,14 @@ export const usePatchUpdateFlow: useMutationFunctionType<
}; };
const mutation: UseMutationResult<IPatchUpdateFlow, any, IPatchUpdateFlow> = const mutation: UseMutationResult<IPatchUpdateFlow, any, IPatchUpdateFlow> =
mutate(["usePatchUpdateFlow"], PatchUpdateFlowFn, options); mutate(["usePatchUpdateFlow"], PatchUpdateFlowFn, {
...options,
onSettled: () => {
queryClient.refetchQueries({
queryKey: ["useGetFolder"],
});
},
});
return mutation; return mutation;
}; };

View file

@ -18,7 +18,7 @@ export const usePostAddFlow: useMutationFunctionType<
undefined, undefined,
IPostAddFlow IPostAddFlow
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const postAddFlowFn = async (payload: IPostAddFlow): Promise<any> => { const postAddFlowFn = async (payload: IPostAddFlow): Promise<any> => {
const response = await api.post(`${getURL("FLOWS")}/`, { const response = await api.post(`${getURL("FLOWS")}/`, {
@ -36,7 +36,12 @@ export const usePostAddFlow: useMutationFunctionType<
const mutation: UseMutationResult<IPostAddFlow, any, IPostAddFlow> = mutate( const mutation: UseMutationResult<IPostAddFlow, any, IPostAddFlow> = mutate(
["usePostAddFlow"], ["usePostAddFlow"],
postAddFlowFn, postAddFlowFn,
options, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolder"] });
},
},
); );
return mutation; return mutation;

View file

@ -12,7 +12,7 @@ export const useDeleteFolders: useMutationFunctionType<
undefined, undefined,
DeleteFoldersParams DeleteFoldersParams
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const deleteFolder = async ({ const deleteFolder = async ({
folder_id, folder_id,
@ -25,7 +25,12 @@ export const useDeleteFolders: useMutationFunctionType<
DeleteFoldersParams, DeleteFoldersParams,
any, any,
DeleteFoldersParams DeleteFoldersParams
> = mutate(["useDeleteFolders"], deleteFolder, options); > = mutate(["useDeleteFolders"], deleteFolder, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
},
});
return mutation; return mutation;
}; };

View file

@ -0,0 +1,30 @@
import { FolderType } from "@/pages/MainPage/entities";
import { useQueryFunctionType } from "@/types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
interface IGetFolder {
id: string;
}
export const useGetFolderQuery: useQueryFunctionType<IGetFolder, FolderType> = (
params,
options,
) => {
const { query } = UseRequestProcessor();
const getFolderFn = async (): Promise<FolderType> => {
const res = await api.get(`${getURL("FOLDERS")}/${params.id}`);
const data = res.data;
return data;
};
const queryResult = query(
["useGetFolder", { id: params.id }],
getFolderFn,
options,
);
return queryResult;
};

View file

@ -0,0 +1,50 @@
import { DEFAULT_FOLDER, STARTER_FOLDER_NAME } from "@/constants/constants";
import { FolderType } from "@/pages/MainPage/entities";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useFolderStore } from "@/stores/foldersStore";
import { useTypesStore } from "@/stores/typesStore";
import { useQueryFunctionType } from "@/types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
export const useGetFoldersQuery: useQueryFunctionType<
undefined,
FolderType[]
> = (options) => {
const { query } = UseRequestProcessor();
const setStarterProjectId = useFolderStore(
(state) => state.setStarterProjectId,
);
const setMyCollectionId = useFolderStore((state) => state.setMyCollectionId);
const getFoldersFn = async (): Promise<FolderType[]> => {
const res = await api.get(`${getURL("FOLDERS")}/`);
const data = res.data;
const foldersWithoutStarterProjects = data?.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME,
);
const starterProjects = data?.find(
(folder) => folder.name === STARTER_FOLDER_NAME,
);
setStarterProjectId(starterProjects?.id ?? "");
const myCollectionId = data?.find((f) => f.name === DEFAULT_FOLDER)?.id;
setMyCollectionId(myCollectionId);
const { refreshFlows } = useFlowsManagerStore.getState();
const { getTypes } = useTypesStore.getState();
await refreshFlows();
await getTypes();
return foldersWithoutStarterProjects;
};
const queryResult = query(["useGetFolders"], getFoldersFn, options);
return queryResult;
};

View file

@ -1,5 +1,4 @@
import { AddFolderType } from "@/pages/MainPage/entities"; import { AddFolderType } from "@/pages/MainPage/entities";
import { useFolderStore } from "@/stores/foldersStore";
import { useMutationFunctionType } from "@/types/api"; import { useMutationFunctionType } from "@/types/api";
import { api } from "../../api"; import { api } from "../../api";
import { getURL } from "../../helpers/constants"; import { getURL } from "../../helpers/constants";
@ -14,7 +13,7 @@ export const usePatchFolders: useMutationFunctionType<
undefined, undefined,
IPatchPatchFolders IPatchPatchFolders
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const patchFoldersFn = async ( const patchFoldersFn = async (
newFolder: IPatchPatchFolders, newFolder: IPatchPatchFolders,
@ -33,7 +32,12 @@ export const usePatchFolders: useMutationFunctionType<
return res.data; return res.data;
}; };
const mutation = mutate(["usePatchFolders"], patchFoldersFn, options); const mutation = mutate(["usePatchFolders"], patchFoldersFn, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
},
});
return mutation; return mutation;
}; };

View file

@ -1,7 +1,5 @@
import { AddFolderType } from "@/pages/MainPage/entities"; import { AddFolderType } from "@/pages/MainPage/entities";
import useFlowsManagerStore from "@/stores/flowsManagerStore"; import { useMutationFunctionType } from "@/types/api";
import { useFolderStore } from "@/stores/foldersStore";
import { Users, useMutationFunctionType } from "@/types/api";
import { api } from "../../api"; import { api } from "../../api";
import { getURL } from "../../helpers/constants"; import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor"; import { UseRequestProcessor } from "../../services/request-processor";
@ -14,7 +12,7 @@ export const usePostFolders: useMutationFunctionType<
undefined, undefined,
IPostAddFolders IPostAddFolders
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const addFoldersFn = async (newFolder: IPostAddFolders): Promise<void> => { const addFoldersFn = async (newFolder: IPostAddFolders): Promise<void> => {
const payload = { const payload = {
@ -28,7 +26,12 @@ export const usePostFolders: useMutationFunctionType<
return res.data; return res.data;
}; };
const mutation = mutate(["usePostFolders"], addFoldersFn, options); const mutation = mutate(["usePostFolders"], addFoldersFn, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
},
});
return mutation; return mutation;
}; };

View file

@ -11,7 +11,7 @@ export const usePostUploadFolders: useMutationFunctionType<
undefined, undefined,
IPostAddUploadFolders IPostAddUploadFolders
> = (options?) => { > = (options?) => {
const { mutate } = UseRequestProcessor(); const { mutate, queryClient } = UseRequestProcessor();
const uploadFoldersFn = async ( const uploadFoldersFn = async (
payload: IPostAddUploadFolders, payload: IPostAddUploadFolders,
@ -23,7 +23,12 @@ export const usePostUploadFolders: useMutationFunctionType<
return res.data; return res.data;
}; };
const mutation = mutate(["usePostUploadFolders"], uploadFoldersFn, options); const mutation = mutate(["usePostUploadFolders"], uploadFoldersFn, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
},
});
return mutation; return mutation;
}; };

View file

@ -0,0 +1,35 @@
import { useMutationFunctionType } from "@/types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
import { UseRequestProcessor } from "../../services/request-processor";
interface IPostAddUploadFlowToFolder {
flows: FormData;
folderId: string;
}
export const usePostUploadFlowToFolder: useMutationFunctionType<
undefined,
IPostAddUploadFlowToFolder
> = (options?) => {
const { mutate, queryClient } = UseRequestProcessor();
const uploadFlowToFolderFn = async (
payload: IPostAddUploadFlowToFolder,
): Promise<void> => {
const res = await api.post(
`${getURL("FLOWS")}/upload/?folder_id=${encodeURIComponent(payload.folderId)}`,
payload.flows,
);
return res.data;
};
const mutation = mutate(["usePostUploadFlowToFolder"], uploadFlowToFolderFn, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolder"] });
},
});
return mutation;
};

View file

@ -14,6 +14,7 @@ import {
updateGroupRecursion, updateGroupRecursion,
} from "@/utils/reactflowUtils"; } from "@/utils/reactflowUtils";
import { cloneDeep } from "lodash"; import { cloneDeep } from "lodash";
import { useParams } from "react-router-dom";
import useDeleteFlow from "./use-delete-flow"; import useDeleteFlow from "./use-delete-flow";
const useAddFlow = () => { const useAddFlow = () => {
@ -26,7 +27,10 @@ const useAddFlow = () => {
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const setFlows = useFlowsManagerStore((state) => state.setFlows); const setFlows = useFlowsManagerStore((state) => state.setFlows);
const deleteFlow = useDeleteFlow(); const deleteFlow = useDeleteFlow();
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const { folderId } = useParams();
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const { mutate: postAddFlow } = usePostAddFlow(); const { mutate: postAddFlow } = usePostAddFlow();
@ -45,8 +49,7 @@ const useAddFlow = () => {
); );
}); });
// Create a new flow with a default name if no flow is provided. // Create a new flow with a default name if no flow is provided.
const folder_id = useFolderStore.getState().folderUrl; const folder_id = folderId ?? myCollectionId ?? "";
const my_collection_id = useFolderStore.getState().myCollectionId;
if (params?.override && flow) { if (params?.override && flow) {
const flowId = flows?.find((f) => f.name === flow.name); const flowId = flows?.find((f) => f.name === flow.name);
@ -54,15 +57,11 @@ const useAddFlow = () => {
await deleteFlow({ id: flowId.id }); await deleteFlow({ id: flowId.id });
} }
} }
const newFlow = createNewFlow( const newFlow = createNewFlow(flowData!, folder_id, flow);
flowData!,
folder_id || my_collection_id!,
flow,
);
const newName = addVersionToDuplicates(newFlow, flows ?? []); const newName = addVersionToDuplicates(newFlow, flows ?? []);
newFlow.name = newName; newFlow.name = newName;
newFlow.folder_id = useFolderStore.getState().folderUrl; newFlow.folder_id = folder_id;
postAddFlow(newFlow, { postAddFlow(newFlow, {
onSuccess: ({ id }) => { onSuccess: ({ id }) => {
@ -80,7 +79,6 @@ const useAddFlow = () => {
["saved_components"]: data, ["saved_components"]: data,
}), }),
})); }));
setIsLoading(false);
resolve(id); resolve(id);
}, },
onError: (error) => { onError: (error) => {
@ -98,7 +96,6 @@ const useAddFlow = () => {
], ],
}); });
} }
setIsLoading(false);
reject(error); // Re-throw the error so the caller can handle it if needed}, reject(error); // Re-throw the error so the caller can handle it if needed},
}, },
}); });

View file

@ -9,7 +9,6 @@ import {
const useDeleteFlow = () => { const useDeleteFlow = () => {
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const setFlows = useFlowsManagerStore((state) => state.setFlows); const setFlows = useFlowsManagerStore((state) => state.setFlows);
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const { mutate } = useDeleteDeleteFlows(); const { mutate } = useDeleteDeleteFlows();
@ -30,7 +29,6 @@ const useDeleteFlow = () => {
(flows ?? []).filter((flow) => !id.includes(flow.id)), (flows ?? []).filter((flow) => !id.includes(flow.id)),
); );
setFlows(myFlows); setFlows(myFlows);
setIsLoading(false);
useTypesStore.setState((state) => ({ useTypesStore.setState((state) => ({
data: { ...state.data, ["saved_components"]: data }, data: { ...state.data, ["saved_components"]: data },
ComponentFields: extractFieldsFromComponenents({ ComponentFields: extractFieldsFromComponenents({

View file

@ -1,25 +1,21 @@
import useAddFlow from "@/hooks/flows/use-add-flow"; import useAddFlow from "@/hooks/flows/use-add-flow";
import { useLocation, useNavigate } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import { import {
Card, Card,
CardContent, CardContent,
CardDescription, CardDescription,
CardTitle, CardTitle,
} from "../../../../components/ui/card"; } from "../../../../components/ui/card";
import { useFolderStore } from "../../../../stores/foldersStore";
export default function NewFlowCardComponent() { export default function NewFlowCardComponent() {
const addFlow = useAddFlow(); const addFlow = useAddFlow();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const { folderId } = useParams();
const folderId = location?.state?.folderId;
const setFolderUrl = useFolderStore((state) => state.setFolderUrl);
return ( return (
<Card <Card
onClick={() => { onClick={() => {
addFlow().then((id) => { addFlow().then((id) => {
setFolderUrl(folderId ?? "");
navigate(`/flow/${id}${folderId ? `/folder/${folderId}` : ""}`); navigate(`/flow/${id}${folderId ? `/folder/${folderId}` : ""}`);
}); });
}} }}

View file

@ -1,5 +1,5 @@
/// <reference types="vite-plugin-svgr/client" /> /// <reference types="vite-plugin-svgr/client" />
import { useLocation, useNavigate } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import BlogPost from "../../../../assets/undraw_blog_post_re_fy5x.svg?react"; import BlogPost from "../../../../assets/undraw_blog_post_re_fy5x.svg?react";
import ChatBot from "../../../../assets/undraw_chat_bot_re_e2gj.svg?react"; import ChatBot from "../../../../assets/undraw_chat_bot_re_e2gj.svg?react";
import PromptChaining from "../../../../assets/undraw_cloud_docs_re_xjht.svg?react"; import PromptChaining from "../../../../assets/undraw_cloud_docs_re_xjht.svg?react";
@ -26,12 +26,10 @@ export default function UndrawCardComponent({
}: UndrawCardComponentProps): JSX.Element { }: UndrawCardComponentProps): JSX.Element {
const addFlow = useAddFlow(); const addFlow = useAddFlow();
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const { folderId } = useParams();
const folderId = location?.state?.folderId;
const setFolderUrl = useFolderStore((state) => state.setFolderUrl);
const myCollectionId = useFolderStore((state) => state.myCollectionId); const myCollectionId = useFolderStore((state) => state.myCollectionId);
const folderIdUrl = folderId || myCollectionId || ""; const folderIdUrl = folderId ?? myCollectionId;
function selectImage() { function selectImage() {
switch (flow.name) { switch (flow.name) {
@ -143,7 +141,6 @@ export default function UndrawCardComponent({
onClick={() => { onClick={() => {
updateIds(flow.data!); updateIds(flow.data!);
addFlow({ flow }).then((id) => { addFlow({ flow }).then((id) => {
setFolderUrl(folderId ?? "");
navigate(`/flow/${id}/folder/${folderIdUrl}`); navigate(`/flow/${id}/folder/${folderIdUrl}`);
}); });
}} }}

View file

@ -1,8 +1,6 @@
import { useLoginUser } from "@/controllers/API/queries/auth"; import { useLoginUser } from "@/controllers/API/queries/auth";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useFolderStore } from "@/stores/foldersStore"; import { useFolderStore } from "@/stores/foldersStore";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import { Button } from "../../../components/ui/button"; import { Button } from "../../../components/ui/button";
import { Input } from "../../../components/ui/input"; import { Input } from "../../../components/ui/input";
import { SIGNIN_ERROR_ALERT } from "../../../constants/alerts_constants"; import { SIGNIN_ERROR_ALERT } from "../../../constants/alerts_constants";
@ -16,14 +14,10 @@ import {
} from "../../../types/components"; } from "../../../types/components";
export default function LoginAdminPage() { export default function LoginAdminPage() {
const navigate = useNavigate();
const [inputState, setInputState] = const [inputState, setInputState] =
useState<loginInputStateType>(CONTROL_LOGIN_STATE); useState<loginInputStateType>(CONTROL_LOGIN_STATE);
const { login } = useContext(AuthContext); const { login } = useContext(AuthContext);
const setLoading = useAlertStore((state) => state.setLoading); const setLoading = useAlertStore((state) => state.setLoading);
const setAllFlows = useFlowsManagerStore((state) => state.setAllFlows);
const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder); const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
const { password, username } = inputState; const { password, username } = inputState;
@ -44,7 +38,6 @@ export default function LoginAdminPage() {
mutate(user, { mutate(user, {
onSuccess: (res) => { onSuccess: (res) => {
setAllFlows([]);
setSelectedFolder(null); setSelectedFolder(null);
setLoading(true); setLoading(true);

View file

@ -9,7 +9,6 @@ import {
import { useGetHealthQuery } from "@/controllers/API/queries/health"; import { useGetHealthQuery } from "@/controllers/API/queries/health";
import useTrackLastVisitedPath from "@/hooks/use-track-last-visited-path"; import useTrackLastVisitedPath from "@/hooks/use-track-last-visited-path";
import useFlowsManagerStore from "@/stores/flowsManagerStore"; import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { useFolderStore } from "@/stores/foldersStore";
import { cn } from "@/utils/utils"; import { cn } from "@/utils/utils";
import { ErrorBoundary } from "react-error-boundary"; import { ErrorBoundary } from "react-error-boundary";
import { Outlet } from "react-router-dom"; import { Outlet } from "react-router-dom";
@ -18,14 +17,12 @@ export function AppWrapperPage() {
useTrackLastVisitedPath(); useTrackLastVisitedPath();
const isLoading = useFlowsManagerStore((state) => state.isLoading); const isLoading = useFlowsManagerStore((state) => state.isLoading);
const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
const { const {
data: healthData, data: healthData,
isFetching: fetchingHealth, isFetching: fetchingHealth,
isError: isErrorHealth, isError: isErrorHealth,
refetch, refetch,
} = useGetHealthQuery(); } = useGetHealthQuery();
const isLoadingApplication = isLoading || isLoadingFolders;
return ( return (
<div className="flex h-full flex-col"> <div className="flex h-full flex-col">
<ErrorBoundary <ErrorBoundary
@ -54,7 +51,7 @@ export function AppWrapperPage() {
<div <div
className={cn( className={cn(
"loading-page-panel absolute left-0 top-0 z-[999]", "loading-page-panel absolute left-0 top-0 z-[999]",
isLoadingApplication ? "" : "hidden", isLoading ? "" : "hidden",
)} )}
> >
<LoadingComponent remSize={50} /> <LoadingComponent remSize={50} />

View file

@ -87,7 +87,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
(state) => state.setLastCopiedSelection, (state) => state.setLastCopiedSelection,
); );
const onConnect = useFlowStore((state) => state.onConnect); const onConnect = useFlowStore((state) => state.onConnect);
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setNoticeData = useAlertStore((state) => state.setNoticeData); const setNoticeData = useAlertStore((state) => state.setNoticeData);
const updateCurrentFlow = useFlowStore((state) => state.updateCurrentFlow); const updateCurrentFlow = useFlowStore((state) => state.updateCurrentFlow);
@ -423,23 +422,10 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
[], [],
); );
const onPaneClick = useCallback((flow) => { const onPaneClick = useCallback(() => {
setFilterEdge([]); setFilterEdge([]);
}, []); }, []);
function onMouseAction(edge: Edge, color: string): void {
const edges = useFlowStore.getState().edges;
const newEdges = _.cloneDeep(edges);
const style = { stroke: color, transition: "stroke 0.25s" };
const updatedEdges = newEdges.map((obj) => {
if (obj.id === edge.id) {
return { ...obj, style };
}
return obj;
});
setEdges(updatedEdges);
}
return ( return (
<div className="h-full w-full" ref={reactFlowWrapper}> <div className="h-full w-full" ref={reactFlowWrapper}>
{showCanvas ? ( {showCanvas ? (

View file

@ -2,6 +2,7 @@ import FeatureFlags from "@/../feature-config.json";
import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import useSaveFlow from "@/hooks/flows/use-save-flow"; import useSaveFlow from "@/hooks/flows/use-save-flow";
import { SaveChangesModal } from "@/modals/saveChangesModal"; import { SaveChangesModal } from "@/modals/saveChangesModal";
import { useTypesStore } from "@/stores/typesStore";
import { customStringify } from "@/utils/reactflowUtils"; import { customStringify } from "@/utils/reactflowUtils";
import { useEffect } from "react"; import { useEffect } from "react";
import { useBlocker, useNavigate, useParams } from "react-router-dom"; import { useBlocker, useNavigate, useParams } from "react-router-dom";
@ -31,6 +32,9 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows);
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const getTypes = useTypesStore((state) => state.getTypes);
const handleSave = () => { const handleSave = () => {
saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null)); saveFlow().then(() => (blocker.proceed ? blocker.proceed() : null));
@ -53,16 +57,24 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
// Set flow tab id // Set flow tab id
useEffect(() => { useEffect(() => {
if (flows && currentFlowId === "") { const awaitgetTypes = async () => {
const isAnExistingFlow = flows.find((flow) => flow.id === id); if (flows && currentFlowId === "") {
const isAnExistingFlow = flows.find((flow) => flow.id === id);
if (!isAnExistingFlow) { if (!isAnExistingFlow) {
navigate("/all"); navigate("/all");
return; return;
}
setCurrentFlow(isAnExistingFlow);
} else if (!flows) {
setIsLoading(true);
await refreshFlows();
await getTypes();
setIsLoading(false);
} }
};
setCurrentFlow(isAnExistingFlow); awaitgetTypes();
}
}, [id, flows]); }, [id, flows]);
useEffect(() => { useEffect(() => {
@ -70,7 +82,7 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
return () => { return () => {
setOnFlowPage(false); setOnFlowPage(false);
setCurrentFlow(); setCurrentFlow(undefined);
}; };
}, [id]); }, [id]);

View file

@ -2,7 +2,7 @@ import { useLoginUser } from "@/controllers/API/queries/auth";
import { useFolderStore } from "@/stores/foldersStore"; import { useFolderStore } from "@/stores/foldersStore";
import * as Form from "@radix-ui/react-form"; import * as Form from "@radix-ui/react-form";
import { useContext, useState } from "react"; import { useContext, useState } from "react";
import { Link, useNavigate } from "react-router-dom"; import { Link } from "react-router-dom";
import InputComponent from "../../components/inputComponent"; import InputComponent from "../../components/inputComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input"; import { Input } from "../../components/ui/input";
@ -10,7 +10,6 @@ import { SIGNIN_ERROR_ALERT } from "../../constants/alerts_constants";
import { CONTROL_LOGIN_STATE } from "../../constants/constants"; import { CONTROL_LOGIN_STATE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext"; import { AuthContext } from "../../contexts/authContext";
import useAlertStore from "../../stores/alertStore"; import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { LoginType } from "../../types/api"; import { LoginType } from "../../types/api";
import { import {
inputHandlerEventType, inputHandlerEventType,
@ -23,10 +22,7 @@ export default function LoginPage(): JSX.Element {
const { password, username } = inputState; const { password, username } = inputState;
const { login } = useContext(AuthContext); const { login } = useContext(AuthContext);
const navigate = useNavigate();
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const setLoading = useFlowsManagerStore((state) => state.setIsLoading);
const setAllFlows = useFlowsManagerStore((state) => state.setAllFlows);
const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder); const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
function handleInput({ function handleInput({
@ -45,10 +41,8 @@ export default function LoginPage(): JSX.Element {
mutate(user, { mutate(user, {
onSuccess: (data) => { onSuccess: (data) => {
setAllFlows([]);
setSelectedFolder(null); setSelectedFolder(null);
setLoading(true);
login(data.access_token, "login"); login(data.access_token, "login");
}, },
onError: (error) => { onError: (error) => {

View file

@ -1,24 +1,19 @@
import { useFolderStore } from "@/stores/foldersStore"; import { Link, useNavigate, useParams } from "react-router-dom";
import React from "react";
import { Link, useNavigate } from "react-router-dom";
import CollectionCardComponent from "../../../../../../components/cardComponent"; import CollectionCardComponent from "../../../../../../components/cardComponent";
import IconComponent from "../../../../../../components/genericIconComponent"; import IconComponent from "../../../../../../components/genericIconComponent";
import { Button } from "../../../../../../components/ui/button"; import { Button } from "../../../../../../components/ui/button";
const CollectionCard = ({ item, type, isLoading, control }) => { const CollectionCard = ({ item, type, isLoading, control }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const isComponent = item.is_component ?? false; const isComponent = item.is_component ?? false;
const editFlowLink = `/flow/${item.id}`;
const editFlowButtonTestId = `edit-flow-button-${item.id}`; const editFlowButtonTestId = `edit-flow-button-${item.id}`;
const folderUrl = useFolderStore((state) => state.folderUrl); const { folderId } = useParams();
const myCollectionIdFolder = useFolderStore((state) => state.myCollectionId);
const hasFolderUrl = folderUrl != null && folderUrl !== ""; const editFlowLink = `/flow/${item.id}${folderId ? `/folder/${folderId}` : ""}`;
const currentFolderUrl = hasFolderUrl ? folderUrl : myCollectionIdFolder;
const handleClick = () => { const handleClick = () => {
if (!isComponent) { if (!isComponent) {
navigate(editFlowLink, { state: { folderId: currentFolderUrl } }); navigate(editFlowLink);
} }
}; };

View file

@ -5,13 +5,6 @@ const useDuplicateFlows = (
selectedFlowsComponentsCards: string[], selectedFlowsComponentsCards: string[],
allFlows: any[], allFlows: any[],
resetFilter: () => void, resetFilter: () => void,
getFoldersApi: (
refetch?: boolean,
startupApplication?: boolean,
) => Promise<void>,
folderId: string,
myCollectionId: string,
getFolderById: (id: string) => void,
setSuccessData: (data: { title: string }) => void, setSuccessData: (data: { title: string }) => void,
setSelectedFlowsComponentsCards: ( setSelectedFlowsComponentsCards: (
selectedFlowsComponentsCards: string[], selectedFlowsComponentsCards: string[],
@ -27,10 +20,6 @@ const useDuplicateFlows = (
), ),
).then(() => { ).then(() => {
resetFilter(); resetFilter();
getFoldersApi(true);
if (!folderId || folderId === myCollectionId) {
getFolderById(folderId ? folderId : myCollectionId);
}
setSuccessData({ title: `${cardTypes} duplicated successfully` }); setSuccessData({ title: `${cardTypes} duplicated successfully` });
setSelectedFlowsComponentsCards([]); setSelectedFlowsComponentsCards([]);
handleSelectAll(false); handleSelectAll(false);
@ -40,10 +29,6 @@ const useDuplicateFlows = (
addFlow, addFlow,
allFlows, allFlows,
resetFilter, resetFilter,
getFoldersApi,
folderId,
myCollectionId,
getFolderById,
setSuccessData, setSuccessData,
setSelectedFlowsComponentsCards, setSelectedFlowsComponentsCards,
handleSelectAll, handleSelectAll,

View file

@ -1,8 +1,10 @@
import { usePostDownloadMultipleFlows } from "@/controllers/API/queries/flows"; import { usePostDownloadMultipleFlows } from "@/controllers/API/queries/flows";
import { useGetFolderQuery } from "@/controllers/API/queries/folders/use-get-folder";
import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders";
import useDeleteFlow from "@/hooks/flows/use-delete-flow"; import useDeleteFlow from "@/hooks/flows/use-delete-flow";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { FormProvider, useForm, useWatch } from "react-hook-form"; import { FormProvider, useForm, useWatch } from "react-hook-form";
import { useLocation } from "react-router-dom"; import { useLocation, useParams } from "react-router-dom";
import CardsWrapComponent from "../../../../components/cardsWrapComponent"; import CardsWrapComponent from "../../../../components/cardsWrapComponent";
import PaginatorComponent from "../../../../components/paginatorComponent"; import PaginatorComponent from "../../../../components/paginatorComponent";
import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent"; import { SkeletonCardComponent } from "../../../../components/skeletonCardComponent";
@ -30,12 +32,8 @@ export default function ComponentsComponent({
type?: string; type?: string;
}) { }) {
const isLoading = useFlowsManagerStore((state) => state.isLoading); const isLoading = useFlowsManagerStore((state) => state.isLoading);
const setAllFlows = useFlowsManagerStore((state) => state.setAllFlows);
const allFlows = useFlowsManagerStore((state) => state.allFlows);
const flowsFromFolder = useFolderStore( const { folderId } = useParams();
(state) => state.selectedFolder?.flows,
);
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
@ -51,47 +49,51 @@ export default function ComponentsComponent({
const selectedFlowsComponentsCards = useFlowsManagerStore( const selectedFlowsComponentsCards = useFlowsManagerStore(
(state) => state.selectedFlowsComponentsCards, (state) => state.selectedFlowsComponentsCards,
); );
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const { data: currentFolder, isLoading: isLoadingCurrentFolder } =
useGetFolderQuery({
id: folderId ?? myCollectionId ?? "",
});
const flowsFromFolder = currentFolder?.flows ?? [];
const [filteredFlows, setFilteredFlows] =
useState<FlowType[]>(flowsFromFolder);
const handleFileDrop = useFileDrop(type); const handleFileDrop = useFileDrop(type);
const [pageSize, setPageSize] = useState(20); const [pageSize, setPageSize] = useState(20);
const [pageIndex, setPageIndex] = useState(1); const [pageIndex, setPageIndex] = useState(1);
const location = useLocation(); const all: FlowType[] = sortFlows(filteredFlows, type);
const all: FlowType[] = sortFlows(allFlows, type);
const start = (pageIndex - 1) * pageSize; const start = (pageIndex - 1) * pageSize;
const end = start + pageSize; const end = start + pageSize;
const data: FlowType[] = all?.slice(start, end); const data: FlowType[] = all?.slice(start, end);
const location = useLocation();
const name = getNameByType(type); const name = getNameByType(type);
const folderId = location?.state?.folderId;
const getFolderById = useFolderStore((state) => state.getFolderById);
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const setFolderUrl = useFolderStore((state) => state.setFolderUrl);
const isLoadingFolders = useFolderStore((state) => state.isLoadingFolders);
const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder); const setSelectedFolder = useFolderStore((state) => state.setSelectedFolder);
const { isLoading: isLoadingFolders } = useGetFoldersQuery();
const [shouldSelectAll, setShouldSelectAll] = useState(true); const [shouldSelectAll, setShouldSelectAll] = useState(true);
const cardTypes = useMemo(() => { const cardTypes = useMemo(() => {
if (window.location.pathname.includes("components")) { if (location.pathname.includes("components")) {
return "Components"; return "Components";
} }
if (window.location.pathname.includes("flows")) { if (location.pathname.includes("flows")) {
return "Flows"; return "Flows";
} }
return "Items"; return "Items";
}, [window.location]); }, [location]);
useEffect(() => { useEffect(() => {
setFolderUrl(folderId ?? "");
setSelectedFlowsComponentsCards([]); setSelectedFlowsComponentsCards([]);
handleSelectAll(false); handleSelectAll(false);
setShouldSelectAll(true); setShouldSelectAll(true);
getFolderById(folderId ? folderId : myCollectionId); }, [folderId, location, myCollectionId]);
}, [location, folderId, myCollectionId]);
useFilteredFlows(flowsFromFolder!, searchFlowsComponents, setAllFlows); useFilteredFlows(flowsFromFolder, searchFlowsComponents, setFilteredFlows);
const resetFilter = () => { const resetFilter = () => {
setPageIndex(1); setPageIndex(1);
@ -111,12 +113,8 @@ export default function ComponentsComponent({
const { handleDuplicate } = useDuplicateFlows( const { handleDuplicate } = useDuplicateFlows(
selectedFlowsComponentsCards, selectedFlowsComponentsCards,
allFlows, flowsFromFolder,
resetFilter, resetFilter,
getFoldersApi,
folderId,
myCollectionId!,
getFolderById,
setSuccessData, setSuccessData,
setSelectedFlowsComponentsCards, setSelectedFlowsComponentsCards,
handleSelectAll, handleSelectAll,
@ -133,7 +131,7 @@ export default function ComponentsComponent({
}, },
{ {
onSuccess: (data) => { onSuccess: (data) => {
const selectedFlow = allFlows.find( const selectedFlow = flowsFromFolder.find(
(flow) => flow.id === selectedFlowsComponentsCards[0], (flow) => flow.id === selectedFlowsComponentsCards[0],
); );
@ -190,18 +188,16 @@ export default function ComponentsComponent({
const handleDeleteMultiple = () => { const handleDeleteMultiple = () => {
deleteFlow({ id: selectedFlowsComponentsCards }) deleteFlow({ id: selectedFlowsComponentsCards })
.then(() => { .then(() => {
setAllFlows([]);
setSelectedFolder(null); setSelectedFolder(null);
resetFilter(); resetFilter();
getFoldersApi(true); setSelectedFlowsComponentsCards([]);
if (!folderId || folderId === myCollectionId) { handleSelectAll(false);
getFolderById(folderId ? folderId : myCollectionId); setShouldSelectAll(true);
}
setSuccessData({ setSuccessData({
title: "Selected items deleted successfully", title: "Selected items deleted successfully",
}); });
}) })
.catch((e) => { .catch(() => {
setErrorData({ setErrorData({
title: "Error deleting items", title: "Error deleting items",
list: ["Please try again"], list: ["Please try again"],
@ -216,28 +212,26 @@ export default function ComponentsComponent({
type, type,
); );
const getTotalRowsCount = () => { const totalRowsCount = filteredFlows?.length;
if (type === "all") return allFlows?.length;
return allFlows?.filter(
(f) => (f.is_component ?? false) === (type === "component"),
)?.length;
};
return ( return (
<> <>
<div className="flex w-full gap-4 pb-5"> <div className="flex w-full gap-4 pb-5">
{allFlows?.length > 0 && ( <HeaderComponent
<HeaderComponent disabled={
shouldSelectAll={shouldSelectAll} isLoading ||
setShouldSelectAll={setShouldSelectAll} isLoadingFolders ||
handleDelete={() => handleSelectOptionsChange("delete")} isLoadingCurrentFolder ||
handleSelectAll={handleSelectAll} data?.length === 0
handleDuplicate={() => handleSelectOptionsChange("duplicate")} }
handleExport={() => handleSelectOptionsChange("export")} shouldSelectAll={shouldSelectAll}
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)} setShouldSelectAll={setShouldSelectAll}
/> handleDelete={() => handleSelectOptionsChange("delete")}
)} handleSelectAll={handleSelectAll}
handleDuplicate={() => handleSelectOptionsChange("duplicate")}
handleExport={() => handleSelectOptionsChange("export")}
disableFunctions={!(selectedFlowsComponentsCards?.length > 0)}
/>
</div> </div>
<CardsWrapComponent <CardsWrapComponent
@ -249,13 +243,16 @@ export default function ComponentsComponent({
data-testid="cards-wrapper" data-testid="cards-wrapper"
> >
<div className="flex w-full flex-col gap-4"> <div className="flex w-full flex-col gap-4">
{!isLoading && !isLoadingFolders && data?.length === 0 ? ( {!isLoading &&
!isLoadingFolders &&
!isLoadingCurrentFolder &&
data?.length === 0 ? (
<EmptyComponent /> <EmptyComponent />
) : ( ) : (
<div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-2"> <div className="grid w-full gap-4 md:grid-cols-2 lg:grid-cols-2">
{isLoading === false && {data?.length > 0 &&
data?.length > 0 && isLoadingFolders === false &&
isLoadingFolders === false ? ( isLoadingCurrentFolder === false ? (
<> <>
{data?.map((item) => ( {data?.map((item) => (
<FormProvider {...methods} key={item.id}> <FormProvider {...methods} key={item.id}>
@ -286,7 +283,7 @@ export default function ComponentsComponent({
pageIndex={pageIndex} pageIndex={pageIndex}
pageSize={pageSize} pageSize={pageSize}
rowsCount={[10, 20, 50, 100]} rowsCount={[10, 20, 50, 100]}
totalRowsCount={getTotalRowsCount()} totalRowsCount={totalRowsCount}
paginate={(pageSize, pageIndex) => { paginate={(pageSize, pageIndex) => {
setPageIndex(pageIndex); setPageIndex(pageIndex);
setPageSize(pageSize); setPageSize(pageSize);

View file

@ -1,4 +1,3 @@
import { useState } from "react";
import IconComponent from "../../../../components/genericIconComponent"; import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent"; import ShadTooltip from "../../../../components/shadTooltipComponent";
import { Button } from "../../../../components/ui/button"; import { Button } from "../../../../components/ui/button";
@ -13,6 +12,7 @@ type HeaderComponentProps = {
disableFunctions: boolean; disableFunctions: boolean;
setShouldSelectAll: (select) => void; setShouldSelectAll: (select) => void;
shouldSelectAll: boolean; shouldSelectAll: boolean;
disabled: boolean;
}; };
const HeaderComponent = ({ const HeaderComponent = ({
@ -23,6 +23,7 @@ const HeaderComponent = ({
disableFunctions, disableFunctions,
setShouldSelectAll, setShouldSelectAll,
shouldSelectAll, shouldSelectAll,
disabled,
}: HeaderComponentProps) => { }: HeaderComponentProps) => {
const handleClick = () => { const handleClick = () => {
handleSelectAll(shouldSelectAll); handleSelectAll(shouldSelectAll);
@ -31,7 +32,12 @@ const HeaderComponent = ({
return ( return (
<> <>
<div className="flex w-full items-center justify-between gap-4"> <div
className={cn(
"flex w-full items-center justify-between gap-4",
disabled ? "pointer-events-none opacity-50" : "",
)}
>
<div className="flex items-center justify-self-start"> <div className="flex items-center justify-self-start">
<a onClick={handleClick} className="cursor-pointer text-sm"> <a onClick={handleClick} className="cursor-pointer text-sm">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">

View file

@ -1,7 +1,6 @@
import { ChangeEvent, KeyboardEvent } from "react"; import { ChangeEvent, KeyboardEvent } from "react";
import ForwardedIconComponent from "../../../../../../components/genericIconComponent"; import ForwardedIconComponent from "../../../../../../components/genericIconComponent";
import { Input } from "../../../../../../components/ui/input"; import { Input } from "../../../../../../components/ui/input";
import useFlowsManagerStore from "../../../../../../stores/flowsManagerStore";
type InputSearchComponentProps = { type InputSearchComponentProps = {
loading: boolean; loading: boolean;
@ -21,9 +20,6 @@ const InputSearchComponent = ({
onKeyDown, onKeyDown,
}: InputSearchComponentProps) => { }: InputSearchComponentProps) => {
const pagePath = window.location.pathname; const pagePath = window.location.pathname;
const allFlows = useFlowsManagerStore((state) => state.allFlows);
const disableInputSearch = loading || !allFlows;
const getSearchPlaceholder = () => { const getSearchPlaceholder = () => {
if (pagePath.includes("flows")) { if (pagePath.includes("flows")) {
@ -40,7 +36,7 @@ const InputSearchComponent = ({
<div className={`${divClasses ? divClasses : "relative h-12 w-[60%]"}`}> <div className={`${divClasses ? divClasses : "relative h-12 w-[60%]"}`}>
<Input <Input
data-testid="search-store-input" data-testid="search-store-input"
disabled={disableInputSearch} disabled={loading}
placeholder={getSearchPlaceholder()} placeholder={getSearchPlaceholder()}
className="absolute h-12 pl-5 pr-12" className="absolute h-12 pl-5 pr-12"
onChange={onChange} onChange={onChange}

View file

@ -1,6 +1,5 @@
import { useEffect, useState } from "react"; import { useEffect } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { useFolderStore } from "../../../../../../stores/foldersStore";
type TabsSearchComponentProps = { type TabsSearchComponentProps = {
tabsOptions: string[]; tabsOptions: string[];
@ -16,11 +15,6 @@ const TabsSearchComponent = ({
tabActive, tabActive,
}: TabsSearchComponentProps) => { }: TabsSearchComponentProps) => {
const navigate = useNavigate(); const navigate = useNavigate();
const folderUrl = useFolderStore((state) => state.folderUrl);
const myCollectionIdFolder = useFolderStore((state) => state.myCollectionId);
const hasFolderUrl = folderUrl != null && folderUrl !== "";
const currentFolderUrl = hasFolderUrl ? folderUrl : myCollectionIdFolder;
const changeLocation = (tabOption) => { const changeLocation = (tabOption) => {
const location = window.location.pathname; const location = window.location.pathname;
@ -37,7 +31,7 @@ const TabsSearchComponent = ({
break; break;
} }
navigate(newLocation, { state: { folderId: currentFolderUrl } }); navigate(newLocation);
}; };
useEffect(() => { useEffect(() => {

View file

@ -1,16 +1,10 @@
import useUploadFlow from "@/hooks/flows/use-upload-flow"; import useUploadFlow from "@/hooks/flows/use-upload-flow";
import { useLocation } from "react-router-dom";
import { CONSOLE_ERROR_MSG } from "../../../constants/alerts_constants"; import { CONSOLE_ERROR_MSG } from "../../../constants/alerts_constants";
import useAlertStore from "../../../stores/alertStore"; import useAlertStore from "../../../stores/alertStore";
import { useFolderStore } from "../../../stores/foldersStore";
const useFileDrop = (type?: string) => { const useFileDrop = (type?: string) => {
const location = useLocation();
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const getFolderById = useFolderStore((state) => state.getFolderById);
const folderId = location?.state?.folderId;
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const uploadFlow = useUploadFlow(); const uploadFlow = useUploadFlow();
const handleFileDrop = (e) => { const handleFileDrop = (e) => {
@ -26,7 +20,6 @@ const useFileDrop = (type?: string) => {
setSuccessData({ setSuccessData({
title: `All files uploaded successfully`, title: `All files uploaded successfully`,
}); });
getFolderById(folderId ? folderId : myCollectionId);
}) })
.catch((error) => { .catch((error) => {
console.log(error); console.log(error);

View file

@ -1,10 +1,10 @@
import FolderSidebarNav from "@/components/folderSidebarComponent";
import { useDeleteFolders } from "@/controllers/API/queries/folders"; import { useDeleteFolders } from "@/controllers/API/queries/folders";
import useAlertStore from "@/stores/alertStore"; import useAlertStore from "@/stores/alertStore";
import { useState } from "react"; import { useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { Outlet, useLocation, useNavigate } from "react-router-dom";
import DropdownButton from "../../../../components/dropdownButtonComponent"; import DropdownButton from "../../../../components/dropdownButtonComponent";
import PageLayout from "../../../../components/pageLayout"; import PageLayout from "../../../../components/pageLayout";
import SidebarNav from "../../../../components/sidebarComponent";
import { import {
MY_COLLECTION_DESC, MY_COLLECTION_DESC,
USER_PROJECTS_HEADER, USER_PROJECTS_HEADER,
@ -12,13 +12,11 @@ import {
import { useFolderStore } from "../../../../stores/foldersStore"; import { useFolderStore } from "../../../../stores/foldersStore";
import ModalsComponent from "../../components/modalsComponent"; import ModalsComponent from "../../components/modalsComponent";
import useDropdownOptions from "../../hooks/use-dropdown-options"; import useDropdownOptions from "../../hooks/use-dropdown-options";
import { getFolderById } from "../../services";
export default function HomePage(): JSX.Element { export default function HomePage(): JSX.Element {
const location = useLocation(); const location = useLocation();
const pathname = location.pathname; const pathname = location.pathname;
const [openModal, setOpenModal] = useState(false); const [openModal, setOpenModal] = useState(false);
const [openFolderModal, setOpenFolderModal] = useState(false);
const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false); const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false);
const is_component = pathname === "/components"; const is_component = pathname === "/components";
const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit); const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit);
@ -27,8 +25,6 @@ export default function HomePage(): JSX.Element {
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData); const setErrorData = useAlertStore((state) => state.setErrorData);
const folderToEdit = useFolderStore((state) => state.folderToEdit); const folderToEdit = useFolderStore((state) => state.folderToEdit);
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const dropdownOptions = useDropdownOptions({ const dropdownOptions = useDropdownOptions({
navigate, navigate,
@ -47,8 +43,6 @@ export default function HomePage(): JSX.Element {
setSuccessData({ setSuccessData({
title: "Folder deleted successfully.", title: "Folder deleted successfully.",
}); });
getFolderById(myCollectionId!);
getFoldersApi(true);
navigate("/all"); navigate("/all");
}, },
onError: (err) => { onError: (err) => {
@ -80,14 +74,9 @@ export default function HomePage(): JSX.Element {
> >
<div className="flex h-full w-full space-y-8 md:flex-col lg:flex-row lg:space-x-8 lg:space-y-0"> <div className="flex h-full w-full space-y-8 md:flex-col lg:flex-row lg:space-x-8 lg:space-y-0">
<aside className="flex h-fit w-fit flex-col space-y-6"> <aside className="flex h-fit w-fit flex-col space-y-6">
<SidebarNav <FolderSidebarNav
items={[]}
handleChangeFolder={(id: string) => { handleChangeFolder={(id: string) => {
navigate(`all/folder/${id}`, { state: { folderId: id } }); navigate(`all/folder/${id}`);
}}
handleEditFolder={(item) => {
setFolderToEdit(item);
setOpenFolderModal(true);
}} }}
handleDeleteFolder={(item) => { handleDeleteFolder={(item) => {
setFolderToEdit(item); setFolderToEdit(item);

View file

@ -1,41 +0,0 @@
import { BASE_URL_API } from "../../../constants/constants";
import { api } from "../../../controllers/API/api";
import { FlowType } from "../../../types/flow";
import { AddFolderType, FolderType } from "../entities";
export async function getFolders(): Promise<FolderType[]> {
try {
const response = await api.get(`${BASE_URL_API}folders/`);
return response?.data;
} catch (error) {
throw error;
}
}
export async function getFolderById(folderId: string): Promise<FolderType> {
try {
const response = await api.get(`${BASE_URL_API}folders/${folderId}`);
return response?.data;
} catch (error) {
throw error;
}
}
export async function uploadFlowToFolder(
flows: FormData,
folderId: string,
): Promise<FlowType[]> {
try {
const url = `${BASE_URL_API}flows/upload/?folder_id=${encodeURIComponent(folderId)}`;
const response = await api.post(url, flows);
if (response?.status !== 201) {
throw new Error(`HTTP error! status: ${response?.status}`);
}
return response.data;
} catch (error) {
console.error(error);
throw error;
}
}

View file

@ -1,5 +1,6 @@
import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables";
import useFlowStore from "@/stores/flowStore"; import useFlowStore from "@/stores/flowStore";
import { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react"; import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom"; import { useNavigate, useParams } from "react-router-dom";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -15,19 +16,30 @@ export default function ViewPage() {
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId);
const refreshFlows = useFlowsManagerStore((state) => state.refreshFlows);
const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading);
const getTypes = useTypesStore((state) => state.getTypes);
// Set flow tab id // Set flow tab id
useEffect(() => { useEffect(() => {
if (flows && currentFlowId === "") { const awaitgetTypes = async () => {
const isAnExistingFlow = flows.find((flow) => flow.id === id); if (flows && currentFlowId === "") {
const isAnExistingFlow = flows.find((flow) => flow.id === id);
if (!isAnExistingFlow) { if (!isAnExistingFlow) {
navigate("/all"); navigate("/all");
return; return;
}
setCurrentFlow(isAnExistingFlow);
} else if (!flows) {
setIsLoading(true);
await refreshFlows();
await getTypes();
setIsLoading(false);
} }
};
setCurrentFlow(isAnExistingFlow); awaitgetTypes();
}
}, [id, flows]); }, [id, flows]);
useEffect(() => { useEffect(() => {
@ -35,7 +47,7 @@ export default function ViewPage() {
return () => { return () => {
setOnFlowPage(false); setOnFlowPage(false);
setCurrentFlow(); setCurrentFlow(undefined);
}; };
}, [id]); }, [id]);

View file

@ -5,7 +5,6 @@ import {
Navigate, Navigate,
Route, Route,
} from "react-router-dom"; } from "react-router-dom";
import App from "./App";
import { ProtectedAdminRoute } from "./components/authAdminGuard"; import { ProtectedAdminRoute } from "./components/authAdminGuard";
import { ProtectedRoute } from "./components/authGuard"; import { ProtectedRoute } from "./components/authGuard";
import { ProtectedLoginRoute } from "./components/authLoginGuard"; import { ProtectedLoginRoute } from "./components/authLoginGuard";
@ -56,17 +55,34 @@ const router = createBrowserRouter(
> >
<Route index element={<Navigate replace to={"all"} />} /> <Route index element={<Navigate replace to={"all"} />} />
<Route <Route
path="flows/*" path="flows/"
element={<MyCollectionComponent key="flows" type="flow" />} element={<MyCollectionComponent key="flows" type="flow" />}
/> >
<Route
path="folder/:folderId"
element={<MyCollectionComponent key="flows" type="flow" />}
/>
</Route>
<Route <Route
path="components/*" path="components/"
element={<MyCollectionComponent key="components" type="component" />} element={<MyCollectionComponent key="components" type="component" />}
/> >
<Route
path="folder/:folderId"
element={
<MyCollectionComponent key="components" type="component" />
}
/>
</Route>
<Route <Route
path="all/*" path="all/"
element={<MyCollectionComponent key="all" type="all" />} element={<MyCollectionComponent key="all" type="all" />}
/> >
<Route
path="folder/:folderId"
element={<MyCollectionComponent key="all" type="all" />}
/>
</Route>
</Route> </Route>
<Route <Route
path="/settings" path="/settings"
@ -122,7 +138,7 @@ const router = createBrowserRouter(
</Route> </Route>
<Route path="/flow/:id/"> <Route path="/flow/:id/">
<Route <Route
path="*" path="folder/:folderId/"
element={ element={
<ProtectedRoute> <ProtectedRoute>
<FlowPage /> <FlowPage />

View file

@ -1,10 +1,8 @@
// authStore.js // authStore.js
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants"; import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { AuthStoreType } from "@/types/zustand/auth"; import { AuthStoreType } from "@/types/zustand/auth";
import Cookies from "universal-cookie"; import Cookies from "universal-cookie";
import { create } from "zustand"; import { create } from "zustand";
import { useFolderStore } from "../stores/foldersStore";
const cookies = new Cookies(); const cookies = new Cookies();
const useAuthStore = create<AuthStoreType>((set, get) => ({ const useAuthStore = create<AuthStoreType>((set, get) => ({
@ -40,33 +38,6 @@ const useAuthStore = create<AuthStoreType>((set, get) => ({
window.location.href = "/login"; window.location.href = "/login";
}, },
// getUser: () => {
// const setLoading = useAlertStore.getState().setLoading;
// const getFoldersApi = useFolderStore.getState().getFoldersApi;
// const checkHasStore = useStoreStore.getState().checkHasStore;
// const fetchApiData = useStoreStore.getState().fetchApiData;
// getLoggedUser()
// .then(async (user) => {
// set({ userData: user, isAdmin: user.is_superuser });
// getFoldersApi(true, true);
// checkHasStore();
// fetchApiData();
// })
// .catch((error) => {
// setLoading(false);
// });
// },
// login: (newAccessToken) => {
// set({ accessToken: newAccessToken, isAuthenticated: true });
// get().getUser();
// },
// storeApiKey: (apikey) => {
// cookies.set('apikey_tkn_lflw', apikey, { path: '/' });
// set({ apiKey: apikey });
// },
})); }));
export default useAuthStore; export default useAuthStore;

View file

@ -40,10 +40,6 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
return get().flows?.find((flow) => flow.id === id); return get().flows?.find((flow) => flow.id === id);
}, },
flows: undefined, flows: undefined,
allFlows: [],
setAllFlows: (allFlows: FlowType[]) => {
set({ allFlows });
},
setFlows: (flows: FlowType[]) => { setFlows: (flows: FlowType[]) => {
set({ set({
flows, flows,
@ -57,8 +53,6 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
setIsLoading: (isLoading: boolean) => set({ isLoading }), setIsLoading: (isLoading: boolean) => set({ isLoading }),
refreshFlows: () => { refreshFlows: () => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
set({ isLoading: true });
const starterFolderId = useFolderStore.getState().starterProjectId; const starterFolderId = useFolderStore.getState().starterProjectId;
readFlowsFromDatabase() readFlowsFromDatabase()
@ -82,7 +76,6 @@ const useFlowsManagerStore = create<FlowsManagerStoreType>((set, get) => ({
["saved_components"]: data, ["saved_components"]: data,
}), }),
})); }));
set({ isLoading: false });
resolve(); resolve();
} }
}) })

View file

@ -1,130 +1,16 @@
import { create } from "zustand"; import { create } from "zustand";
import { DEFAULT_FOLDER, STARTER_FOLDER_NAME } from "../constants/constants";
import { getFolderById, getFolders } from "../pages/MainPage/services";
import { FoldersStoreType } from "../types/zustand/folders"; import { FoldersStoreType } from "../types/zustand/folders";
import useFlowsManagerStore from "./flowsManagerStore";
import { useTypesStore } from "./typesStore";
export const useFolderStore = create<FoldersStoreType>((set, get) => ({ export const useFolderStore = create<FoldersStoreType>((set, get) => ({
folders: [],
getFoldersApi: (refetch = false, startupApplication: boolean = false) => {
return new Promise<void>((resolve, reject) => {
get().setIsLoadingFolders(true);
if (get()?.folders.length === 0 || refetch === true) {
getFolders().then(
async (res) => {
const foldersWithoutStarterProjects = res?.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME,
);
const starterProjects = res?.find(
(folder) => folder.name === STARTER_FOLDER_NAME,
);
set({ starterProjectId: starterProjects?.id ?? "" });
set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find(
(f) => f.name === DEFAULT_FOLDER,
)?.id;
set({ myCollectionId });
const { refreshFlows } = useFlowsManagerStore.getState();
const { getTypes } = useTypesStore.getState();
if (refetch) {
if (startupApplication) {
await refreshFlows();
await getTypes();
get().setIsLoadingFolders(false);
} else {
refreshFlows();
getTypes();
get().setIsLoadingFolders(false);
}
}
resolve();
},
(error) => {
set({ folders: [] });
get().setIsLoadingFolders(false);
reject(error);
},
);
}
});
},
refreshFolders: () => {
return new Promise<void>((resolve, reject) => {
getFolders().then(
async (res) => {
const foldersWithoutStarterProjects = res?.filter(
(folder) => folder.name !== STARTER_FOLDER_NAME,
);
const starterProjects = res?.find(
(folder) => folder.name === STARTER_FOLDER_NAME,
);
set({ starterProjectId: starterProjects?.id ?? "" });
set({ folders: foldersWithoutStarterProjects });
const myCollectionId = res?.find(
(f) => f.name === DEFAULT_FOLDER,
)?.id;
set({ myCollectionId });
resolve();
},
(error) => {
set({ folders: [] });
get().setIsLoadingFolders(false);
reject(error);
},
);
});
},
setFolders: (folders) => set(() => ({ folders: folders })),
isLoadingFolders: false,
setIsLoadingFolders: (isLoadingFolders) => set(() => ({ isLoadingFolders })),
getFolderById: (id) => {
if (id) {
getFolderById(id).then((res) => {
const setAllFlows = useFlowsManagerStore.getState().setAllFlows;
setAllFlows(res?.flows);
set({ selectedFolder: res });
});
}
},
selectedFolder: null, selectedFolder: null,
setSelectedFolder: (folder) => set(() => ({ selectedFolder: folder })), setSelectedFolder: (folder) => set(() => ({ selectedFolder: folder })),
loadingById: false, loadingById: false,
getMyCollectionFolder: () => { setMyCollectionId: (myCollectionId) => {
const folders = get().folders; set({ myCollectionId });
const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id;
if (myCollectionId) {
getFolderById(myCollectionId).then((res) => {
set({ myCollectionFlows: res });
});
}
},
setMyCollectionFlow: (folder) => set(() => ({ myCollectionFlows: folder })),
myCollectionFlows: null,
setMyCollectionId: () => {
const folders = get().folders;
const myCollectionId = folders?.find((f) => f.name === DEFAULT_FOLDER)?.id;
if (myCollectionId) {
set({ myCollectionId });
}
}, },
myCollectionId: "", myCollectionId: "",
folderToEdit: null, folderToEdit: null,
setFolderToEdit: (folder) => set(() => ({ folderToEdit: folder })), setFolderToEdit: (folder) => set(() => ({ folderToEdit: folder })),
folderUrl: "",
setFolderUrl: (url) => set(() => ({ folderUrl: url })),
folderDragging: false, folderDragging: false,
setFolderDragging: (folder) => set(() => ({ folderDragging: folder })), setFolderDragging: (folder) => set(() => ({ folderDragging: folder })),
folderIdDragging: "", folderIdDragging: "",

View file

@ -24,7 +24,6 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
getTypes: (force_refresh: boolean = false) => { getTypes: (force_refresh: boolean = false) => {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
const setLoading = useFlowsManagerStore.getState().setIsLoading; const setLoading = useFlowsManagerStore.getState().setIsLoading;
setLoading(true);
getAll(force_refresh) getAll(force_refresh)
.then((response) => { .then((response) => {
const data = response?.data; const data = response?.data;
@ -38,7 +37,6 @@ export const useTypesStore = create<TypesStoreType>((set, get) => ({
}), }),
templates: templatesGenerator(data), templates: templatesGenerator(data),
})); }));
setLoading(false);
resolve(); resolve();
}) })
.catch((error) => { .catch((error) => {

View file

@ -3,8 +3,6 @@ import { FlowType } from "../../flow";
export type FlowsManagerStoreType = { export type FlowsManagerStoreType = {
getFlowById: (id: string) => FlowType | undefined; getFlowById: (id: string) => FlowType | undefined;
flows: Array<FlowType> | undefined; flows: Array<FlowType> | undefined;
allFlows: Array<FlowType>;
setAllFlows: (flows: FlowType[]) => void;
setFlows: (flows: FlowType[]) => void; setFlows: (flows: FlowType[]) => void;
currentFlow: FlowType | undefined; currentFlow: FlowType | undefined;
currentFlowId: string; currentFlowId: string;

View file

@ -1,30 +1,16 @@
import { FolderType } from "../../../pages/MainPage/entities"; import { FolderType } from "../../../pages/MainPage/entities";
export type FoldersStoreType = { export type FoldersStoreType = {
folders: FolderType[];
getFoldersApi: (
refetch?: boolean,
startupApplication?: boolean,
) => Promise<void>;
setFolders: (folders: FolderType[]) => void;
isLoadingFolders: boolean;
setIsLoadingFolders: (isLoadingFolders: boolean) => void;
selectedFolder: FolderType | null; selectedFolder: FolderType | null;
setSelectedFolder: (folder: FolderType | null) => void; setSelectedFolder: (folder: FolderType | null) => void;
getFolderById: (id: string) => void;
getMyCollectionFolder: () => void;
myCollectionFlows: FolderType | null;
myCollectionId: string | null; myCollectionId: string | null;
setMyCollectionId: () => void; setMyCollectionId: (value: string) => void;
folderToEdit: FolderType | null; folderToEdit: FolderType | null;
setFolderToEdit: (folder: FolderType | null) => void; setFolderToEdit: (folder: FolderType | null) => void;
folderUrl: string;
setFolderUrl: (folderUrl: string) => void;
folderDragging: boolean; folderDragging: boolean;
setFolderDragging: (set: boolean) => void; setFolderDragging: (set: boolean) => void;
folderIdDragging: string; folderIdDragging: string;
setFolderIdDragging: (id: string) => void; setFolderIdDragging: (id: string) => void;
starterProjectId: string; starterProjectId: string;
setStarterProjectId: (id: string) => void; setStarterProjectId: (id: string) => void;
refreshFolders: () => void;
}; };