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

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 {
UPLOAD_ALERT_LIST,
WRONG_FILE_ERROR_ALERT,
} from "../../../constants/alerts_constants";
import { uploadFlowToFolder } from "../../../pages/MainPage/services";
import useAlertStore from "../../../stores/alertStore";
import useFlowsManagerStore from "../../../stores/flowsManagerStore";
import { useFolderStore } from "../../../stores/foldersStore";
import { addVersionToDuplicates } from "../../../utils/reactflowUtils";
const useFileDrop = (
folderId: string,
folderChangeCallback: (folderId: string) => void,
) => {
const useFileDrop = (folderId: string) => {
const setFolderDragging = useFolderStore((state) => state.setFolderDragging);
const setFolderIdDragging = useFolderStore(
(state) => state.setFolderIdDragging,
);
const setErrorData = useAlertStore((state) => state.setErrorData);
const refreshFolders = useFolderStore((state) => state.refreshFolders);
const flows = useFlowsManagerStore((state) => state.flows);
const saveFlow = useSaveFlow();
const triggerFolderChange = (folderId) => {
if (folderChangeCallback) {
folderChangeCallback(folderId);
}
};
const { mutate: uploadFlowToFolder } = usePostUploadFlowToFolder();
const handleFileDrop = async (e) => {
if (e.dataTransfer.types.some((type) => type === "Files")) {
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
@ -121,10 +112,7 @@ const useFileDrop = (
setFolderDragging(false);
setFolderIdDragging("");
saveFlow(updatedFlow).then(() => {
refreshFolders();
triggerFolderChange(folderId);
});
saveFlow(updatedFlow);
};
const uploadFormData = (data) => {
@ -133,10 +121,7 @@ const useFileDrop = (
setFolderDragging(false);
setFolderIdDragging("");
uploadFlowToFolder(formData, folderId).then(() => {
refreshFolders();
triggerFolderChange(folderId);
});
uploadFlowToFolder({ flows: formData, folderId });
};
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 updatedAt = currentSavedFlow?.updated_at;
const onFlowPage = useFlowStore((state) => state.onFlowPage);
const setCurrentFlow = useFlowsManagerStore((state) => state.setCurrentFlow);
const changesNotSaved =
customStringify(currentFlow) !== customStringify(currentSavedFlow) &&
@ -71,6 +72,7 @@ export const MenuBar = ({}: {}): JSX.Element => {
function handleAddFlow() {
try {
addFlow().then((id) => {
setCurrentFlow(undefined); // Reset current flow for useEffect of flowPage to update the current flow
navigate("/flow/" + id);
});
} catch (err) {

View file

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

View file

@ -1,10 +1,7 @@
import { useLocation } from "react-router-dom";
import { FolderType } from "../../pages/MainPage/entities";
import { useFolderStore } from "../../stores/foldersStore";
import { cn } from "../../utils/utils";
import HorizontalScrollFadeComponent from "../horizontalScrollFadeComponent";
import SideBarButtonsComponent from "./components/sideBarButtons";
import SideBarFoldersButtonsComponent from "./components/sideBarFolderButtons";
type SidebarNavProps = {
items: {
@ -12,44 +9,21 @@ type SidebarNavProps = {
title: string;
icon: React.ReactNode;
}[];
handleChangeFolder?: (id: string) => void;
handleEditFolder?: (item: FolderType) => void;
handleDeleteFolder?: (item: FolderType) => void;
className?: string;
};
export default function SidebarNav({
className,
items,
handleChangeFolder,
handleEditFolder,
handleDeleteFolder,
...props
}: SidebarNavProps) {
const location = useLocation();
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 (
<nav className={cn(className)} {...props}>
<HorizontalScrollFadeComponent>
{sidebarContent!}
<SideBarButtonsComponent items={items} pathname={pathname} />
</HorizontalScrollFadeComponent>
</nav>
);

View file

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

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@ export const useDeleteFolders: useMutationFunctionType<
undefined,
DeleteFoldersParams
> = (options?) => {
const { mutate } = UseRequestProcessor();
const { mutate, queryClient } = UseRequestProcessor();
const deleteFolder = async ({
folder_id,
@ -25,7 +25,12 @@ export const useDeleteFolders: useMutationFunctionType<
DeleteFoldersParams,
any,
DeleteFoldersParams
> = mutate(["useDeleteFolders"], deleteFolder, options);
> = mutate(["useDeleteFolders"], deleteFolder, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
},
});
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 { useFolderStore } from "@/stores/foldersStore";
import { useMutationFunctionType } from "@/types/api";
import { api } from "../../api";
import { getURL } from "../../helpers/constants";
@ -14,7 +13,7 @@ export const usePatchFolders: useMutationFunctionType<
undefined,
IPatchPatchFolders
> = (options?) => {
const { mutate } = UseRequestProcessor();
const { mutate, queryClient } = UseRequestProcessor();
const patchFoldersFn = async (
newFolder: IPatchPatchFolders,
@ -33,7 +32,12 @@ export const usePatchFolders: useMutationFunctionType<
return res.data;
};
const mutation = mutate(["usePatchFolders"], patchFoldersFn, options);
const mutation = mutate(["usePatchFolders"], patchFoldersFn, {
...options,
onSettled: () => {
queryClient.refetchQueries({ queryKey: ["useGetFolders"] });
},
});
return mutation;
};

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -87,7 +87,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element {
(state) => state.setLastCopiedSelection,
);
const onConnect = useFlowStore((state) => state.onConnect);
const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow);
const setErrorData = useAlertStore((state) => state.setErrorData);
const setNoticeData = useAlertStore((state) => state.setNoticeData);
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([]);
}, []);
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 (
<div className="h-full w-full" ref={reactFlowWrapper}>
{showCanvas ? (

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,3 @@
import { useState } from "react";
import IconComponent from "../../../../components/genericIconComponent";
import ShadTooltip from "../../../../components/shadTooltipComponent";
import { Button } from "../../../../components/ui/button";
@ -13,6 +12,7 @@ type HeaderComponentProps = {
disableFunctions: boolean;
setShouldSelectAll: (select) => void;
shouldSelectAll: boolean;
disabled: boolean;
};
const HeaderComponent = ({
@ -23,6 +23,7 @@ const HeaderComponent = ({
disableFunctions,
setShouldSelectAll,
shouldSelectAll,
disabled,
}: HeaderComponentProps) => {
const handleClick = () => {
handleSelectAll(shouldSelectAll);
@ -31,7 +32,12 @@ const HeaderComponent = ({
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">
<a onClick={handleClick} className="cursor-pointer text-sm">
<div className="flex items-center space-x-2">

View file

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

View file

@ -1,6 +1,5 @@
import { useEffect, useState } from "react";
import { useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useFolderStore } from "../../../../../../stores/foldersStore";
type TabsSearchComponentProps = {
tabsOptions: string[];
@ -16,11 +15,6 @@ const TabsSearchComponent = ({
tabActive,
}: TabsSearchComponentProps) => {
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 location = window.location.pathname;
@ -37,7 +31,7 @@ const TabsSearchComponent = ({
break;
}
navigate(newLocation, { state: { folderId: currentFolderUrl } });
navigate(newLocation);
};
useEffect(() => {

View file

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

View file

@ -1,10 +1,10 @@
import FolderSidebarNav from "@/components/folderSidebarComponent";
import { useDeleteFolders } from "@/controllers/API/queries/folders";
import useAlertStore from "@/stores/alertStore";
import { useState } from "react";
import { Outlet, useLocation, useNavigate } from "react-router-dom";
import DropdownButton from "../../../../components/dropdownButtonComponent";
import PageLayout from "../../../../components/pageLayout";
import SidebarNav from "../../../../components/sidebarComponent";
import {
MY_COLLECTION_DESC,
USER_PROJECTS_HEADER,
@ -12,13 +12,11 @@ import {
import { useFolderStore } from "../../../../stores/foldersStore";
import ModalsComponent from "../../components/modalsComponent";
import useDropdownOptions from "../../hooks/use-dropdown-options";
import { getFolderById } from "../../services";
export default function HomePage(): JSX.Element {
const location = useLocation();
const pathname = location.pathname;
const [openModal, setOpenModal] = useState(false);
const [openFolderModal, setOpenFolderModal] = useState(false);
const [openDeleteFolderModal, setOpenDeleteFolderModal] = useState(false);
const is_component = pathname === "/components";
const setFolderToEdit = useFolderStore((state) => state.setFolderToEdit);
@ -27,8 +25,6 @@ export default function HomePage(): JSX.Element {
const setSuccessData = useAlertStore((state) => state.setSuccessData);
const setErrorData = useAlertStore((state) => state.setErrorData);
const folderToEdit = useFolderStore((state) => state.folderToEdit);
const myCollectionId = useFolderStore((state) => state.myCollectionId);
const getFoldersApi = useFolderStore((state) => state.getFoldersApi);
const dropdownOptions = useDropdownOptions({
navigate,
@ -47,8 +43,6 @@ export default function HomePage(): JSX.Element {
setSuccessData({
title: "Folder deleted successfully.",
});
getFolderById(myCollectionId!);
getFoldersApi(true);
navigate("/all");
},
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">
<aside className="flex h-fit w-fit flex-col space-y-6">
<SidebarNav
items={[]}
<FolderSidebarNav
handleChangeFolder={(id: string) => {
navigate(`all/folder/${id}`, { state: { folderId: id } });
}}
handleEditFolder={(item) => {
setFolderToEdit(item);
setOpenFolderModal(true);
navigate(`all/folder/${id}`);
}}
handleDeleteFolder={(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 useFlowStore from "@/stores/flowStore";
import { useTypesStore } from "@/stores/typesStore";
import { useEffect } from "react";
import { useNavigate, useParams } from "react-router-dom";
import useFlowsManagerStore from "../../stores/flowsManagerStore";
@ -15,19 +16,30 @@ export default function ViewPage() {
const flows = useFlowsManagerStore((state) => state.flows);
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
useEffect(() => {
if (flows && currentFlowId === "") {
const isAnExistingFlow = flows.find((flow) => flow.id === id);
const awaitgetTypes = async () => {
if (flows && currentFlowId === "") {
const isAnExistingFlow = flows.find((flow) => flow.id === id);
if (!isAnExistingFlow) {
navigate("/all");
return;
if (!isAnExistingFlow) {
navigate("/all");
return;
}
setCurrentFlow(isAnExistingFlow);
} else if (!flows) {
setIsLoading(true);
await refreshFlows();
await getTypes();
setIsLoading(false);
}
setCurrentFlow(isAnExistingFlow);
}
};
awaitgetTypes();
}, [id, flows]);
useEffect(() => {
@ -35,7 +47,7 @@ export default function ViewPage() {
return () => {
setOnFlowPage(false);
setCurrentFlow();
setCurrentFlow(undefined);
};
}, [id]);

View file

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

View file

@ -1,10 +1,8 @@
// authStore.js
import { LANGFLOW_ACCESS_TOKEN } from "@/constants/constants";
import useFlowsManagerStore from "@/stores/flowsManagerStore";
import { AuthStoreType } from "@/types/zustand/auth";
import Cookies from "universal-cookie";
import { create } from "zustand";
import { useFolderStore } from "../stores/foldersStore";
const cookies = new Cookies();
const useAuthStore = create<AuthStoreType>((set, get) => ({
@ -40,33 +38,6 @@ const useAuthStore = create<AuthStoreType>((set, get) => ({
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;

View file

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

View file

@ -1,130 +1,16 @@
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 useFlowsManagerStore from "./flowsManagerStore";
import { useTypesStore } from "./typesStore";
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,
setSelectedFolder: (folder) => set(() => ({ selectedFolder: folder })),
loadingById: false,
getMyCollectionFolder: () => {
const folders = get().folders;
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 });
}
setMyCollectionId: (myCollectionId) => {
set({ myCollectionId });
},
myCollectionId: "",
folderToEdit: null,
setFolderToEdit: (folder) => set(() => ({ folderToEdit: folder })),
folderUrl: "",
setFolderUrl: (url) => set(() => ({ folderUrl: url })),
folderDragging: false,
setFolderDragging: (folder) => set(() => ({ folderDragging: folder })),
folderIdDragging: "",

View file

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

View file

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

View file

@ -1,30 +1,16 @@
import { FolderType } from "../../../pages/MainPage/entities";
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;
setSelectedFolder: (folder: FolderType | null) => void;
getFolderById: (id: string) => void;
getMyCollectionFolder: () => void;
myCollectionFlows: FolderType | null;
myCollectionId: string | null;
setMyCollectionId: () => void;
setMyCollectionId: (value: string) => void;
folderToEdit: FolderType | null;
setFolderToEdit: (folder: FolderType | null) => void;
folderUrl: string;
setFolderUrl: (folderUrl: string) => void;
folderDragging: boolean;
setFolderDragging: (set: boolean) => void;
folderIdDragging: string;
setFolderIdDragging: (id: string) => void;
starterProjectId: string;
setStarterProjectId: (id: string) => void;
refreshFolders: () => void;
};