- {isUploaded ? (
+ {uploadStatus[index] === true ? (
- ) : isFailed ? (
+ ) : uploadStatus[index] === false ? (
void;
}) {
- const [isOpenUploadFile, setIsOpenUploadFile] = React.useState(false);
+ const [isOpenUpload, setIsOpenUpload] = React.useState(false);
const [fileCount, setFileCount] = React.useState(0);
- const [isOpenUploadPresigned, setIsOpenUploadPresigned] = React.useState(false);
- const [presignedCount, setPresignedCount] = React.useState(0);
- const [isOpenUploadFolderFile, setIsOpenUploadFolderFile] = React.useState(false);
- const [isOpenUploadFolder, setIsOpenUploadFolder] = React.useState(false);
- const [isOpenUploadZip, setIsOpenUploadZip] = React.useState(false);
- const [isOpenTestUpload, setIsOpenTestUpload] = React.useState(false);
+ const [uploadType, setUploadType] = React.useState<'file' | 'folder' | 'zip'>('file');
- const uploadImageZipMutation = useUploadImageZipQuery();
- const uploadImageFolderFileMutation = useUploadImageFolderFileQuery();
- const uploadImageFileMutation = useUploadImageFileQuery();
- const uploadImageFolderMutation = useUploadImageFolderQuery();
-
- const handleCloseUploadFile = () => setIsOpenUploadFile(false);
- const handleCloseUploadFolderFile = () => setIsOpenUploadFolderFile(false);
- const handleCloseUploadFolder = () => setIsOpenUploadFolder(false);
- const handleCloseUploadZip = () => setIsOpenUploadZip(false);
- const handleCloseTestUpload = () => setIsOpenTestUpload(false);
+ const handleCloseUpload = () => setIsOpenUpload(false);
const handleFileCount = (fileCount: number) => {
setFileCount(fileCount);
@@ -56,148 +36,59 @@ export default function WorkspaceDropdownMenu({
- console.log('프로젝트 이름 수정')}>프로젝트 이름 수정
+ {
+ setUploadType('file');
+ setIsOpenUpload(true);
+ }}
+ >
+ 파일 업로드
+
- setIsOpenUploadFile(true)}>파일 업로드
- setIsOpenUploadPresigned(true)}>
- 파일 업로드 (PresignedUrl 이용)
+ {
+ setUploadType('folder');
+ setIsOpenUpload(true);
+ }}
+ >
+ 폴더 업로드
- setIsOpenUploadFolderFile(true)}>
- 폴더 업로드 (파일 업로드 API 이용)
+
+ {
+ setUploadType('zip');
+ setIsOpenUpload(true);
+ }}
+ >
+ 압축 파일 업로드
- setIsOpenUploadFolder(true)}>
- 폴더 업로드 (백엔드 구현 필요)
-
- setIsOpenUploadZip(true)}>폴더 압축파일 업로드
- setIsOpenTestUpload(true)}>
- 테스트 업로드 (PresignedUrl 이용)
- {' '}
- {/* 새로운 메뉴 항목 추가 */}
- {/* 기존 Dialogs */}
-
-
-
-
-
-
-
-
-
- {/* 테스트 업로드 Dialog */}
-
diff --git a/frontend/src/components/WorkspaceSidebar/ProjectContextMenu.tsx b/frontend/src/components/WorkspaceSidebar/ProjectContextMenu.tsx
index 6293c03..403f75f 100644
--- a/frontend/src/components/WorkspaceSidebar/ProjectContextMenu.tsx
+++ b/frontend/src/components/WorkspaceSidebar/ProjectContextMenu.tsx
@@ -96,7 +96,6 @@ export default function ProjectContextMenu({ projectId, folderId, node, onRefetc
}
}
};
-
const handleDelete = () => {
if (node?.type === 'folder') {
deleteFolderMutation.mutate(
@@ -104,7 +103,7 @@ export default function ProjectContextMenu({ projectId, folderId, node, onRefetc
{
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['folder', projectId, folderId] });
- queryClient.invalidateQueries({ queryKey: ['project', projectId] });
+ queryClient.invalidateQueries({ queryKey: ['folder', projectId, node.id] });
onRefetch();
},
}
@@ -116,7 +115,6 @@ export default function ProjectContextMenu({ projectId, folderId, node, onRefetc
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['folder', projectId, folderId] });
queryClient.invalidateQueries({ queryKey: ['image', node.id] });
- queryClient.invalidateQueries({ queryKey: ['project', projectId] });
onRefetch();
},
}
diff --git a/frontend/src/components/WorkspaceSidebar/ProjectStructure.tsx b/frontend/src/components/WorkspaceSidebar/ProjectStructure.tsx
index a494fd4..c3c2a4c 100644
--- a/frontend/src/components/WorkspaceSidebar/ProjectStructure.tsx
+++ b/frontend/src/components/WorkspaceSidebar/ProjectStructure.tsx
@@ -11,8 +11,7 @@ import AutoLabelButton from './AutoLabelButton';
import { Folder, Image as ImageIcon } from 'lucide-react';
import { Spinner } from '../ui/spinner';
import { FixedSizeList as List, ListChildComponentProps } from 'react-window';
-import { DndProvider, useDrag, useDrop } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
+import { useDrag, useDrop } from 'react-dnd';
import useFolderQuery from '@/queries/folders/useFolderQuery';
import MemoFileStatusIcon from './FileStatusIcon';
import moveNodeInTree from '@/utils/moveNodeInTree';
@@ -36,7 +35,7 @@ const MENU_ID = 'project-menu';
export default function ProjectStructure({ project }: { project: Project }) {
const { setProject, setCategories, setFolderId } = useProjectStore();
const { image: selectedImage, setImage } = useCanvasStore();
- const { treeData, fetchNodeData, setTreeData } = useTreeData(project.id.toString());
+ const { treeData, fetchNodeData, fetchContextFolderData, setTreeData } = useTreeData(project.id.toString());
const { data: categories } = useProjectCategoriesQuery(project.id);
const { isLoading, refetch } = useFolderQuery(project.id.toString(), 0);
@@ -142,6 +141,8 @@ export default function ProjectStructure({ project }: { project: Project }) {
const handleContextMenu = (event: React.MouseEvent, node: FlatNode) => {
event.preventDefault();
setContextNode(node);
+ const parentId = node.parent ? Number(node.parent.id) : null;
+ fetchContextFolderData(parentId);
show({ event });
};
@@ -209,46 +210,44 @@ export default function ProjectStructure({ project }: { project: Project }) {
};
return (
-
-
-
-
-
-
{project.type}
-
-
+
+
+ {isLoading || !treeData ? (
+
+
-
- {isLoading || !treeData ? (
-
-
-
- ) : (
-
- {Row}
-
- )}
-
-
+
+ ) : (
+
+ {Row}
+
+ )}
+
+
-
+
);
}
diff --git a/frontend/src/hooks/useTreeData.ts b/frontend/src/hooks/useTreeData.ts
index 1196b2a..c3a7528 100644
--- a/frontend/src/hooks/useTreeData.ts
+++ b/frontend/src/hooks/useTreeData.ts
@@ -5,7 +5,7 @@ import { useQuery } from '@tanstack/react-query';
import { getFolder } from '@/api/folderApi';
import buildTreeNodes from '@/utils/buildTreeNodes';
-function useFolder(projectId: string, folderId: number, enabled: boolean = folderId === 0) {
+function useFolder(projectId: string, folderId: number, enabled: boolean) {
return useQuery({
queryKey: ['folder', projectId, folderId],
queryFn: () => getFolder(projectId, folderId),
@@ -16,13 +16,26 @@ function useFolder(projectId: string, folderId: number, enabled: boolean = folde
export default function useTreeData(projectId: string) {
const [treeData, setTreeData] = useState(null);
const [currentFolderId, setCurrentFolderId] = useState(null);
+ const [contextFolderId, setContextFolderId] = useState(null);
- const { data: rootFolder, isLoading: isRootLoading } = useFolder(projectId, 0);
+ // 루트 폴더 데이터
+ const { data: rootFolder, isLoading: isRootLoading } = useFolder(projectId, 0, true);
+
+ // 현재 선택된 폴더 데이터
const { data: childFolder, isFetching: isChildLoading } = useFolder(
projectId,
currentFolderId || 0,
currentFolderId !== null
);
+
+ // 컨텍스트 메뉴에서 선택된 폴더 데이터
+ const { data: contextFolder, isFetching: isContextLoading } = useFolder(
+ projectId,
+ contextFolderId || 0,
+ contextFolderId !== null
+ );
+
+ // 트리 데이터를 업데이트하는 함수
const updateTreeData = useCallback((folder: FolderResponse, isRoot: boolean = false) => {
if (!folder) return;
@@ -56,16 +69,25 @@ export default function useTreeData(projectId: string) {
});
}, []);
+ // 루트 폴더 데이터 로드
useEffect(() => {
if (!rootFolder) return;
updateTreeData(rootFolder, true);
}, [rootFolder, updateTreeData]);
+ // 현재 선택된 폴더 데이터 업데이트
useEffect(() => {
if (!childFolder || currentFolderId === null) return;
updateTreeData(childFolder);
}, [childFolder, currentFolderId, updateTreeData]);
+ // 컨텍스트 메뉴에서 선택된 폴더 데이터 업데이트
+ useEffect(() => {
+ if (!contextFolder || contextFolderId === null) return;
+ -updateTreeData(contextFolder);
+ }, [contextFolder, contextFolderId, updateTreeData]);
+
+ // 현재 폴더 선택 시 폴더 ID 설정 함수
const fetchNodeData = useCallback(
(node: TreeNode) => {
if (currentFolderId === Number(node.id)) return;
@@ -73,11 +95,20 @@ export default function useTreeData(projectId: string) {
},
[currentFolderId]
);
+ // 컨텍스트 메뉴 선택 시 폴더 ID 설정 함수
+ const fetchContextFolderData = useCallback(
+ (folderId: number | null) => {
+ if (contextFolderId === folderId) return;
+ setContextFolderId(folderId);
+ },
+ [contextFolderId]
+ );
return {
treeData,
fetchNodeData,
+ fetchContextFolderData,
setTreeData,
- isLoading: isRootLoading || isChildLoading,
+ isLoading: isRootLoading || isChildLoading || isContextLoading,
};
}