diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx similarity index 54% rename from src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx rename to src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx index d48601c8b..5c4780828 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx @@ -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(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(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 = ({
<> - {folders.map((item, index) => { - const editFolderName = editFolders?.filter( - (folder) => folder.name === item.name, - )[0]; - return ( -
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 (
{ - 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!)} + > +
{ + 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" - > - - {editFolderName?.edit ? ( -
- { - handleEditFolderName(e, item.name); + }} + className="flex w-full items-center gap-2" + > + + {editFolderName?.edit ? ( +
+ { + 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`} + /> +
+ ) : ( + + {item.name} + + )} + {index > 0 && ( +
- ) : ( - - {item.name} - - )} - {index > 0 && ( + variant={"ghost"} + size={"icon"} + > + + + )} - )} - +
-
- ); - })} + ); + }) + ) : ( + <> + + + + )}
diff --git a/src/frontend/src/components/folderSidebarComponent/components/sidebarFolderSkeleton/index.tsx b/src/frontend/src/components/folderSidebarComponent/components/sidebarFolderSkeleton/index.tsx new file mode 100644 index 000000000..7f9ea28a6 --- /dev/null +++ b/src/frontend/src/components/folderSidebarComponent/components/sidebarFolderSkeleton/index.tsx @@ -0,0 +1,10 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export function SidebarFolderSkeleton() { + return ( +
+ + +
+ ); +} diff --git a/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx b/src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx similarity index 84% rename from src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx rename to src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx index 9e353624c..40b0d8e76 100644 --- a/src/frontend/src/components/sidebarComponent/hooks/use-on-file-drop.tsx +++ b/src/frontend/src/components/folderSidebarComponent/hooks/use-on-file-drop.tsx @@ -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 { diff --git a/src/frontend/src/components/folderSidebarComponent/index.tsx b/src/frontend/src/components/folderSidebarComponent/index.tsx new file mode 100644 index 000000000..25df8aa26 --- /dev/null +++ b/src/frontend/src/components/folderSidebarComponent/index.tsx @@ -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 ( + + ); +} diff --git a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx index f343511d1..578c8a85a 100644 --- a/src/frontend/src/components/headerComponent/components/menuBar/index.tsx +++ b/src/frontend/src/components/headerComponent/components/menuBar/index.tsx @@ -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) { diff --git a/src/frontend/src/components/headerComponent/index.tsx b/src/frontend/src/components/headerComponent/index.tsx index f7236a716..ded90967b 100644 --- a/src/frontend/src/components/headerComponent/index.tsx +++ b/src/frontend/src/components/headerComponent/index.tsx @@ -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 {
- +