From 3ba4f71240fb31c51d9e0a389beaf517c638a6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Fri, 20 Sep 2024 14:42:41 +0900 Subject: [PATCH 1/8] =?UTF-8?q?Feat:=20Input=EC=97=90=20webkitdirectory=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/index.d.ts | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 frontend/src/index.d.ts diff --git a/frontend/src/index.d.ts b/frontend/src/index.d.ts new file mode 100644 index 0000000..6df8161 --- /dev/null +++ b/frontend/src/index.d.ts @@ -0,0 +1,7 @@ +import 'react'; + +declare module 'react' { + interface InputHTMLAttributes extends AriaAttributes, DOMAttributes { + webkitdirectory?: string; + } +} From 0557def88e502b8b748c60677ce08b1aca1d28cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Fri, 20 Sep 2024 16:45:54 +0900 Subject: [PATCH 2/8] =?UTF-8?q?Feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B5=AC=ED=98=84=20=EC=A4=91,?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/imageApi.ts | 14 ++++- .../src/components/FolderUploadTest/index.tsx | 59 +++++++++++++++++++ frontend/src/router/index.tsx | 6 ++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/FolderUploadTest/index.tsx diff --git a/frontend/src/api/imageApi.ts b/frontend/src/api/imageApi.ts index 57b87f1..65c6aaa 100644 --- a/frontend/src/api/imageApi.ts +++ b/frontend/src/api/imageApi.ts @@ -39,7 +39,7 @@ export async function changeImageStatus( .then(({ data }) => data); } -export async function uploadImageList(projectId: number, folderId: number, memberId: number, imageList: string[]) { +export async function uploadImageList(projectId: number, folderId: number, memberId: number, imageList: File[]) { return api .post( `/projects/${projectId}/folders/${folderId}/images`, @@ -50,3 +50,15 @@ export async function uploadImageList(projectId: number, folderId: number, membe ) .then(({ data }) => data); } + +export async function uploadImageFolder(projectId: number, folderZip: File[], parentId: number, memberId: number) { + return api + .post( + `/projects/${projectId}/folders/0/images/upload`, + { folderZip, parentId }, + { + params: { memberId }, + } + ) + .then(({ data }) => data); +} diff --git a/frontend/src/components/FolderUploadTest/index.tsx b/frontend/src/components/FolderUploadTest/index.tsx new file mode 100644 index 0000000..d37f250 --- /dev/null +++ b/frontend/src/components/FolderUploadTest/index.tsx @@ -0,0 +1,59 @@ +import { uploadImageFolder, uploadImageList } from '@/api/imageApi'; +import useAuthStore from '@/stores/useAuthStore'; +import { useState } from 'react'; +import { useParams } from 'react-router-dom'; + +export default function FolderUploadTest() { + const params = useParams<{ workspaceId: string; projectId: string }>(); + const projectId = Number(params.projectId); + const profile = useAuthStore((state) => state.profile); + const memberId = profile?.id || 0; + const [files, setFiles] = useState>([]); + const [isUploading, setIsUploading] = useState(false); + + const handleFileChange = (e: React.ChangeEvent) => { + const selectedFiles = e.target.files; + if (selectedFiles) { + setFiles(Array.from(selectedFiles)); + } + }; + + const handleUpload = async () => { + if (!files || files.length === 0) { + console.log('No selected files'); + return; + } + + setIsUploading(true); + // uploadImageFolder(projectId, files, 0, memberId); + uploadImageList(projectId, 0, memberId, files); + }; + + return ( +
+

hello infikei

+ + +
+ {files.length > 0 && ( +
    + {files.map((file, index) => ( +
  • {file.webkitRelativePath}
  • + ))} +
+ )} +
+
+ ); +} diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 7b33116..8c10489 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -14,6 +14,7 @@ import WorkspaceBrowseIndex from '@/pages/WorkspaceBrowseIndex'; import AdminIndex from '@/pages/AdminIndex'; import LabelCanvas from '@/pages/LabelCanvas'; import ReviewDetail from '@/components/ReviewDetail'; +import FolderUploadTest from '@/components/FolderUploadTest'; export const webPath = { home: () => '/', @@ -21,6 +22,7 @@ export const webPath = { workspace: () => '/workspace', admin: () => `/admin`, oauthCallback: () => '/redirect/oauth2', + folderUploadTest: () => '/folderuploadtest', }; const router = createBrowserRouter([ @@ -106,6 +108,10 @@ const router = createBrowserRouter([ ), }, + { + path: `${webPath.folderUploadTest()}/:projectId`, + element: , + }, ]); export default router; From 5fde35a619300c09a976964d84ae8e19dcb0d371 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Sun, 22 Sep 2024 03:02:26 +0900 Subject: [PATCH 3/8] =?UTF-8?q?Feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/imageApi.ts | 4 +- .../src/components/FolderUploadTest/index.tsx | 53 ++--------- .../ImageFolderUploadForm.tsx | 88 +++++++++++++++++++ .../ImageFolderUploadModal/index.tsx | 34 +++++++ frontend/src/types/index.ts | 7 ++ 5 files changed, 136 insertions(+), 50 deletions(-) create mode 100644 frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx create mode 100644 frontend/src/components/ImageFolderUploadModal/index.tsx diff --git a/frontend/src/api/imageApi.ts b/frontend/src/api/imageApi.ts index 65c6aaa..3354d49 100644 --- a/frontend/src/api/imageApi.ts +++ b/frontend/src/api/imageApi.ts @@ -51,11 +51,11 @@ export async function uploadImageList(projectId: number, folderId: number, membe .then(({ data }) => data); } -export async function uploadImageFolder(projectId: number, folderZip: File[], parentId: number, memberId: number) { +export async function uploadImageFolder(memberId: number, projectId: number, files: File[], parentId: number = 0) { return api .post( `/projects/${projectId}/folders/0/images/upload`, - { folderZip, parentId }, + { files, parentId }, { params: { memberId }, } diff --git a/frontend/src/components/FolderUploadTest/index.tsx b/frontend/src/components/FolderUploadTest/index.tsx index d37f250..01bf4ff 100644 --- a/frontend/src/components/FolderUploadTest/index.tsx +++ b/frontend/src/components/FolderUploadTest/index.tsx @@ -1,59 +1,16 @@ -import { uploadImageFolder, uploadImageList } from '@/api/imageApi'; -import useAuthStore from '@/stores/useAuthStore'; -import { useState } from 'react'; import { useParams } from 'react-router-dom'; +import ImageFolderUploadModal from '../ImageFolderUploadModal'; export default function FolderUploadTest() { const params = useParams<{ workspaceId: string; projectId: string }>(); const projectId = Number(params.projectId); - const profile = useAuthStore((state) => state.profile); - const memberId = profile?.id || 0; - const [files, setFiles] = useState>([]); - const [isUploading, setIsUploading] = useState(false); - - const handleFileChange = (e: React.ChangeEvent) => { - const selectedFiles = e.target.files; - if (selectedFiles) { - setFiles(Array.from(selectedFiles)); - } - }; - - const handleUpload = async () => { - if (!files || files.length === 0) { - console.log('No selected files'); - return; - } - - setIsUploading(true); - // uploadImageFolder(projectId, files, 0, memberId); - uploadImageList(projectId, 0, memberId, files); - }; return ( -
-

hello infikei

- + - -
- {files.length > 0 && ( -
    - {files.map((file, index) => ( -
  • {file.webkitRelativePath}
  • - ))} -
- )} -
); } diff --git a/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx b/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx new file mode 100644 index 0000000..22f400d --- /dev/null +++ b/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx @@ -0,0 +1,88 @@ +import { useState } from 'react'; +import { Button } from '../ui/button'; +import { cn } from '@/lib/utils'; +import { uploadImageFolder } from '@/api/imageApi'; +import useAuthStore from '@/stores/useAuthStore'; + +export default function ImageFolderUploadForm({ projectId, parentId }: { projectId: number; parentId: number }) { + const profile = useAuthStore((state) => state.profile); + const memberId = profile?.id || 0; + + const [files, setFiles] = useState([]); + const [isDragging, setIsDragging] = useState(false); + const [isUploading, setIsUploading] = useState(false); + + const handleChange = (event: React.ChangeEvent) => { + const newFiles = event.target.files; + + if (newFiles) { + setFiles((prevFiles) => [...prevFiles, ...Array.from(newFiles)]); + } + }; + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragging(false); + }; + + const handleDrop = () => { + setIsDragging(false); + }; + + const handleClick = async () => { + setIsUploading(true); + await uploadImageFolder(memberId, projectId, files, parentId); + setFiles([]); + setIsUploading(false); + }; + + return ( +
+
+ + {isDragging ? ( +

드래그한 파일을 여기에 놓으세요

+ ) : ( +

+ 파일을 업로드하려면 여기를 클릭하거나 +
+ 파일을 드래그하여 여기에 놓으세요 +

+ )} +
+ {files.length > 0 && ( +
    + {files.map((file, index) => ( +
  • {file.webkitRelativePath}
  • + ))} +
+ )} + +
+ ); +} diff --git a/frontend/src/components/ImageFolderUploadModal/index.tsx b/frontend/src/components/ImageFolderUploadModal/index.tsx new file mode 100644 index 0000000..3cfd456 --- /dev/null +++ b/frontend/src/components/ImageFolderUploadModal/index.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom'; +import { Plus } from 'lucide-react'; +import ImageFolderUploadForm from './ImageFolderUploadForm'; + +export default function WorkSpaceCreateModal({ projectId, parentId = 0 }: { projectId: number; parentId: number }) { + const [isOpen, setIsOpen] = React.useState(false); + + const handleOpen = () => setIsOpen(true); + // const handleClose = () => setIsOpen(false); + + return ( + + + + + + + + + + ); +} diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts index 5c6bca4..3a8940e 100644 --- a/frontend/src/types/index.ts +++ b/frontend/src/types/index.ts @@ -261,3 +261,10 @@ export interface ErrorResponse { message: string; isSuccess: boolean; } + +export interface ImageFolderRequest { + memberId: number; + projectId: number; + parentId: number; + files: File[]; +} From 1236c080f56cad15b6e8a46fb6953cb7d0cc3d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Sun, 22 Sep 2024 04:15:01 +0900 Subject: [PATCH 4/8] =?UTF-8?q?Feat:=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EB=98=90=EB=8A=94=20=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=8B=9C=20=EB=8B=AB=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/imageApi.ts | 5 +- .../ImageFolderUploadForm.tsx | 116 ++++++++++++------ .../ImageFolderUploadModal/index.tsx | 3 +- 3 files changed, 83 insertions(+), 41 deletions(-) diff --git a/frontend/src/api/imageApi.ts b/frontend/src/api/imageApi.ts index 3354d49..460b4de 100644 --- a/frontend/src/api/imageApi.ts +++ b/frontend/src/api/imageApi.ts @@ -60,5 +60,8 @@ export async function uploadImageFolder(memberId: number, projectId: number, fil params: { memberId }, } ) - .then(({ data }) => data); + .then(({ data }) => data) + .catch((error) => { + return Promise.reject(error); + }); } diff --git a/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx b/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx index 22f400d..ef355d7 100644 --- a/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx +++ b/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx @@ -4,13 +4,27 @@ import { cn } from '@/lib/utils'; import { uploadImageFolder } from '@/api/imageApi'; import useAuthStore from '@/stores/useAuthStore'; -export default function ImageFolderUploadForm({ projectId, parentId }: { projectId: number; parentId: number }) { +export default function ImageFolderUploadForm({ + onClose, + projectId, + parentId, +}: { + onClose: () => void; + projectId: number; + parentId: number; +}) { const profile = useAuthStore((state) => state.profile); const memberId = profile?.id || 0; const [files, setFiles] = useState([]); const [isDragging, setIsDragging] = useState(false); const [isUploading, setIsUploading] = useState(false); + const [progress, setProgress] = useState(0); + const [isFailed, setIsFailed] = useState(false); + + const handleClose = () => { + onClose(); + }; const handleChange = (event: React.ChangeEvent) => { const newFiles = event.target.files; @@ -34,55 +48,79 @@ export default function ImageFolderUploadForm({ projectId, parentId }: { project setIsDragging(false); }; - const handleClick = async () => { + const handleUpload = async () => { setIsUploading(true); - await uploadImageFolder(memberId, projectId, files, parentId); - setFiles([]); - setIsUploading(false); + setProgress(0); + + await uploadImageFolder(memberId, projectId, files, parentId) + .then(() => { + setProgress(100); + }) + .catch(() => { + setProgress(100); + setIsFailed(true); + }); }; return (
-
- - {isDragging ? ( -

드래그한 파일을 여기에 놓으세요

- ) : ( -

- 파일을 업로드하려면 여기를 클릭하거나 -
- 파일을 드래그하여 여기에 놓으세요 -

- )} -
+ {!isUploading && ( +
+ + {isDragging ? ( +

드래그한 폴더를 여기에 놓으세요

+ ) : ( +

+ 폴더를 업로드하려면 여기를 클릭하거나 +
+ 폴더를 드래그하여 여기에 놓으세요 +

+ )} +
+ )} {files.length > 0 && (
    {files.map((file, index) => ( -
  • {file.webkitRelativePath}
  • +
  • {file.webkitRelativePath || file.name}
  • ))}
)} - + {isUploading ? ( + + ) : ( + + )}
); } diff --git a/frontend/src/components/ImageFolderUploadModal/index.tsx b/frontend/src/components/ImageFolderUploadModal/index.tsx index 3cfd456..96f737c 100644 --- a/frontend/src/components/ImageFolderUploadModal/index.tsx +++ b/frontend/src/components/ImageFolderUploadModal/index.tsx @@ -7,7 +7,7 @@ export default function WorkSpaceCreateModal({ projectId, parentId = 0 }: { proj const [isOpen, setIsOpen] = React.useState(false); const handleOpen = () => setIsOpen(true); - // const handleClose = () => setIsOpen(false); + const handleClose = () => setIsOpen(false); return ( From 6e38e4e79db858befdf4072c416461fd7ff8dc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Sun, 22 Sep 2024 04:34:41 +0900 Subject: [PATCH 5/8] =?UTF-8?q?Feat:=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EC=A0=9C=EA=B1=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImageFolderUploadForm.tsx | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx b/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx index ef355d7..f73f227 100644 --- a/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx +++ b/frontend/src/components/ImageFolderUploadModal/ImageFolderUploadForm.tsx @@ -3,6 +3,7 @@ import { Button } from '../ui/button'; import { cn } from '@/lib/utils'; import { uploadImageFolder } from '@/api/imageApi'; import useAuthStore from '@/stores/useAuthStore'; +import { X } from 'lucide-react'; export default function ImageFolderUploadForm({ onClose, @@ -48,6 +49,10 @@ export default function ImageFolderUploadForm({ setIsDragging(false); }; + const handleRemoveFile = (index: number) => { + setFiles(files.filter((_, i) => i != index)); + }; + const handleUpload = async () => { setIsUploading(true); setProgress(0); @@ -93,9 +98,24 @@ export default function ImageFolderUploadForm({ )} {files.length > 0 && ( -
    +
      {files.map((file, index) => ( -
    • {file.webkitRelativePath || file.name}
    • +
    • + {file.webkitRelativePath || file.name} + +
    • ))}
    )} From 57e83781b8ae67f8f53ed751d0c6a6318ae3b7ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Sun, 22 Sep 2024 04:39:50 +0900 Subject: [PATCH 6/8] =?UTF-8?q?Fix:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EC=97=85=EB=A1=9C=EB=93=9C=20api=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/imageApi.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/frontend/src/api/imageApi.ts b/frontend/src/api/imageApi.ts index 460b4de..577c440 100644 --- a/frontend/src/api/imageApi.ts +++ b/frontend/src/api/imageApi.ts @@ -55,7 +55,22 @@ export async function uploadImageFolder(memberId: number, projectId: number, fil return api .post( `/projects/${projectId}/folders/0/images/upload`, - { files, parentId }, + { folderZip: files, parentId }, + { + params: { memberId }, + } + ) + .then(({ data }) => data) + .catch((error) => { + return Promise.reject(error); + }); +} + +export async function uploadImageFolderZip(memberId: number, projectId: number, file: File, parentId: number = 0) { + return api + .post( + `/projects/${projectId}/folders/0/images/upload`, + { folderZip: file, parentId }, { params: { memberId }, } From 20a95747941e7816091e310bc9011021e4a94147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Sun, 22 Sep 2024 12:58:44 +0900 Subject: [PATCH 7/8] =?UTF-8?q?Refactor:=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/components/ImageFolderUploadModal/index.tsx | 2 +- .../index.tsx => pages/ImageFolderUploadTest.tsx} | 4 ++-- frontend/src/router/index.tsx | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) rename frontend/src/{components/FolderUploadTest/index.tsx => pages/ImageFolderUploadTest.tsx} (72%) diff --git a/frontend/src/components/ImageFolderUploadModal/index.tsx b/frontend/src/components/ImageFolderUploadModal/index.tsx index 96f737c..2a98405 100644 --- a/frontend/src/components/ImageFolderUploadModal/index.tsx +++ b/frontend/src/components/ImageFolderUploadModal/index.tsx @@ -3,7 +3,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialog import { Plus } from 'lucide-react'; import ImageFolderUploadForm from './ImageFolderUploadForm'; -export default function WorkSpaceCreateModal({ projectId, parentId = 0 }: { projectId: number; parentId: number }) { +export default function ImageFolderUploadModal({ projectId, parentId = 0 }: { projectId: number; parentId: number }) { const [isOpen, setIsOpen] = React.useState(false); const handleOpen = () => setIsOpen(true); diff --git a/frontend/src/components/FolderUploadTest/index.tsx b/frontend/src/pages/ImageFolderUploadTest.tsx similarity index 72% rename from frontend/src/components/FolderUploadTest/index.tsx rename to frontend/src/pages/ImageFolderUploadTest.tsx index 01bf4ff..21f4c6d 100644 --- a/frontend/src/components/FolderUploadTest/index.tsx +++ b/frontend/src/pages/ImageFolderUploadTest.tsx @@ -1,7 +1,7 @@ +import ImageFolderUploadModal from '@/components/ImageFolderUploadModal'; import { useParams } from 'react-router-dom'; -import ImageFolderUploadModal from '../ImageFolderUploadModal'; -export default function FolderUploadTest() { +export default function ImageFolderUploadTest() { const params = useParams<{ workspaceId: string; projectId: string }>(); const projectId = Number(params.projectId); diff --git a/frontend/src/router/index.tsx b/frontend/src/router/index.tsx index 8c10489..dbf88a0 100644 --- a/frontend/src/router/index.tsx +++ b/frontend/src/router/index.tsx @@ -14,7 +14,7 @@ import WorkspaceBrowseIndex from '@/pages/WorkspaceBrowseIndex'; import AdminIndex from '@/pages/AdminIndex'; import LabelCanvas from '@/pages/LabelCanvas'; import ReviewDetail from '@/components/ReviewDetail'; -import FolderUploadTest from '@/components/FolderUploadTest'; +import ImageFolderUploadTest from '@/pages/ImageFolderUploadTest'; export const webPath = { home: () => '/', @@ -22,7 +22,7 @@ export const webPath = { workspace: () => '/workspace', admin: () => `/admin`, oauthCallback: () => '/redirect/oauth2', - folderUploadTest: () => '/folderuploadtest', + imageFolderUploadTest: () => '/imagefolderuploadtest', }; const router = createBrowserRouter([ @@ -109,8 +109,8 @@ const router = createBrowserRouter([ ), }, { - path: `${webPath.folderUploadTest()}/:projectId`, - element: , + path: `${webPath.imageFolderUploadTest()}/:projectId`, + element: , }, ]); From c4414a1a9f20a37e9385e3bdae863b965e4345f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=ED=99=8D=EC=B0=BD=EA=B8=B0?= Date: Sun, 22 Sep 2024 13:00:34 +0900 Subject: [PATCH 8/8] =?UTF-8?q?Feat:=20=ED=8F=B4=EB=8D=94=20=EC=95=95?= =?UTF-8?q?=EC=B6=95=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/api/imageApi.ts | 35 +++-- .../ImageFolderZipUploadForm.tsx | 142 ++++++++++++++++++ .../ImageFolderZipUploadModal/index.tsx | 41 +++++ frontend/src/pages/ImageFolderUploadTest.tsx | 5 + 4 files changed, 213 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/ImageFolderZipUploadModal/ImageFolderZipUploadForm.tsx create mode 100644 frontend/src/components/ImageFolderZipUploadModal/index.tsx diff --git a/frontend/src/api/imageApi.ts b/frontend/src/api/imageApi.ts index 577c440..7c315ac 100644 --- a/frontend/src/api/imageApi.ts +++ b/frontend/src/api/imageApi.ts @@ -52,13 +52,18 @@ export async function uploadImageList(projectId: number, folderId: number, membe } export async function uploadImageFolder(memberId: number, projectId: number, files: File[], parentId: number = 0) { + const formData = new FormData(); + files.forEach((file) => { + formData.append('files', file); + }); + return api .post( - `/projects/${projectId}/folders/0/images/upload`, - { folderZip: files, parentId }, - { - params: { memberId }, - } + `/projects/${projectId}/folders/${0}/images/upload`, + { folderZip: files, parentId } + // { + // params: { memberId }, + // } ) .then(({ data }) => data) .catch((error) => { @@ -67,13 +72,23 @@ export async function uploadImageFolder(memberId: number, projectId: number, fil } export async function uploadImageFolderZip(memberId: number, projectId: number, file: File, parentId: number = 0) { + const formData = new FormData(); + formData.append('folderZip', file); + formData.append('parentId', parentId.toString()); + + // const jsonData = { + // parentId, + // }; + // const blob = new Blob([JSON.stringify(jsonData)], { type: 'application/json' }); + // formData.append('parentId', blob); + return api .post( - `/projects/${projectId}/folders/0/images/upload`, - { folderZip: file, parentId }, - { - params: { memberId }, - } + `/projects/${projectId}/folders/${0}/images/upload`, + formData + // { + // params: { memberId }, + // } ) .then(({ data }) => data) .catch((error) => { diff --git a/frontend/src/components/ImageFolderZipUploadModal/ImageFolderZipUploadForm.tsx b/frontend/src/components/ImageFolderZipUploadModal/ImageFolderZipUploadForm.tsx new file mode 100644 index 0000000..39ac97c --- /dev/null +++ b/frontend/src/components/ImageFolderZipUploadModal/ImageFolderZipUploadForm.tsx @@ -0,0 +1,142 @@ +import { useState } from 'react'; +import { Button } from '../ui/button'; +import { cn } from '@/lib/utils'; +import { uploadImageFolderZip } from '@/api/imageApi'; +import useAuthStore from '@/stores/useAuthStore'; +import { X } from 'lucide-react'; + +export default function ImageFolderZipUploadForm({ + onClose, + projectId, + parentId, +}: { + onClose: () => void; + projectId: number; + parentId: number; +}) { + const profile = useAuthStore((state) => state.profile); + const memberId = profile?.id || 0; + + const [file, setFile] = useState(); + const [isDragging, setIsDragging] = useState(false); + const [isUploading, setIsUploading] = useState(false); + const [progress, setProgress] = useState(0); + const [isFailed, setIsFailed] = useState(false); + + const handleClose = () => { + onClose(); + }; + + const handleChange = (event: React.ChangeEvent) => { + const newFiles = event.target.files; + + if (newFiles) { + setFile(newFiles[0]); + } + }; + + const handleDragOver = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragging(true); + }; + + const handleDragLeave = (event: React.DragEvent) => { + event.preventDefault(); + setIsDragging(false); + }; + + const handleDrop = () => { + setIsDragging(false); + }; + + const handleRemoveFile = () => { + setFile(undefined); + }; + + const handleUpload = async () => { + if (file) { + setIsUploading(true); + setProgress(0); + + await uploadImageFolderZip(memberId, projectId, file, parentId) + .then(() => { + setProgress(100); + }) + .catch(() => { + setProgress(100); + setIsFailed(true); + }); + } + }; + + return ( +
    + {!isUploading && ( +
    + + {isDragging ? ( +

    드래그한 파일을 여기에 놓으세요

    + ) : ( +

    + 파일을 업로드하려면 여기를 클릭하거나 +
    + 파일을 드래그하여 여기에 놓으세요 +

    + )} +
    + )} + {file && ( +
    + {file.webkitRelativePath || file.name} + +
    + )} + {isUploading ? ( + + ) : ( + + )} +
    + ); +} diff --git a/frontend/src/components/ImageFolderZipUploadModal/index.tsx b/frontend/src/components/ImageFolderZipUploadModal/index.tsx new file mode 100644 index 0000000..d143280 --- /dev/null +++ b/frontend/src/components/ImageFolderZipUploadModal/index.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom'; +import { Plus } from 'lucide-react'; +import ImageFolderZipUploadForm from './ImageFolderZipUploadForm'; + +export default function ImageFolderZipUploadModal({ + projectId, + parentId = 0, +}: { + projectId: number; + parentId: number; +}) { + const [isOpen, setIsOpen] = React.useState(false); + + const handleOpen = () => setIsOpen(true); + const handleClose = () => setIsOpen(false); + + return ( + + + + + + + + + + ); +} diff --git a/frontend/src/pages/ImageFolderUploadTest.tsx b/frontend/src/pages/ImageFolderUploadTest.tsx index 21f4c6d..266ccf6 100644 --- a/frontend/src/pages/ImageFolderUploadTest.tsx +++ b/frontend/src/pages/ImageFolderUploadTest.tsx @@ -1,4 +1,5 @@ import ImageFolderUploadModal from '@/components/ImageFolderUploadModal'; +import ImageFolderZipUploadModal from '@/components/ImageFolderZipUploadModal'; import { useParams } from 'react-router-dom'; export default function ImageFolderUploadTest() { @@ -11,6 +12,10 @@ export default function ImageFolderUploadTest() { projectId={projectId} parentId={0} /> + ); }