diff --git a/frontend/src/api/imageApi.ts b/frontend/src/api/imageApi.ts index f3a21cc..d7c1ecf 100644 --- a/frontend/src/api/imageApi.ts +++ b/frontend/src/api/imageApi.ts @@ -56,7 +56,7 @@ export async function uploadImageFile( .then(({ data }) => data); } -export async function uploadImageFolder( +export async function uploadImageFolderFile( memberId: number, projectId: number, folderId: number, @@ -81,6 +81,31 @@ export async function uploadImageFolder( .then(({ data }) => data); } +export async function uploadImageFolder( + memberId: number, + projectId: number, + folderId: number, + files: File[], + processCallback: (progress: number) => void +) { + const formData = new FormData(); + files.forEach((file) => { + formData.append('imageList', file); + }); + + return api + .post(`/projects/${projectId}/folders/${folderId}/images/folder`, formData, { + params: { memberId }, + onUploadProgress: (progressEvent) => { + if (progressEvent.total) { + const progress = Math.round((progressEvent.loaded * 100) / progressEvent.total); + processCallback(progress); + } + }, + }) + .then(({ data }) => data); +} + export async function uploadImageZip( memberId: number, projectId: number, diff --git a/frontend/src/components/ImageUploadFolderFileModal/ImageUploadFolderFileForm.tsx b/frontend/src/components/ImageUploadFolderFileModal/ImageUploadFolderFileForm.tsx new file mode 100644 index 0000000..04bd592 --- /dev/null +++ b/frontend/src/components/ImageUploadFolderFileModal/ImageUploadFolderFileForm.tsx @@ -0,0 +1,190 @@ +import { useState } from 'react'; +import { Button } from '../ui/button'; +import { cn } from '@/lib/utils'; +import useAuthStore from '@/stores/useAuthStore'; +import { CircleCheckBig, CircleDashed, CircleX, X } from 'lucide-react'; +import useUploadImageFolderFileQuery from '@/queries/projects/useUploadImageFolderFileQuery'; + +export default function ImageUploadFolderFileForm({ + onClose, + onRefetch, + projectId, + folderId, +}: { + onClose: () => void; + onRefetch?: () => void; + projectId: number; + folderId: number; +}) { + const profile = useAuthStore((state) => state.profile); + const memberId = profile?.id || 0; + + const [files, setFiles] = useState([]); + const [inputKey, setInputKey] = useState(0); + const [isDragging, setIsDragging] = useState(false); + const [isUploading, setIsUploading] = useState(false); + const [isUploaded, setIsUploaded] = useState(false); + const [isFailed, setIsFailed] = useState(false); + const [progress, setProgress] = useState(0); + + const uploadImageFolderFile = useUploadImageFolderFileQuery(); + + const handleClose = () => { + onClose(); + }; + + const handleRefetch = () => { + if (onRefetch) { + onRefetch(); + } + }; + + 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 handleRemoveFile = () => { + setFiles([]); + setInputKey((prevKey) => prevKey + 1); + }; + + const handleUpload = async () => { + setIsUploading(true); + + uploadImageFolderFile.mutate( + { + memberId, + projectId, + folderId, + files, + progressCallback: (progress: number) => { + setProgress(progress); + }, + }, + { + onSuccess: () => { + handleRefetch(); + setIsUploaded(true); + }, + onError: () => { + setIsFailed(true); + }, + } + ); + }; + + return ( +
+ {!isUploading && ( +
+ + {isDragging ? ( +

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

+ ) : ( +

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

+ )} +
+ )} + {files.length > 0 && ( +
+ + {files[0].webkitRelativePath.substring(0, files[0].webkitRelativePath.indexOf('/'))} {} + + {isUploading ? ( +
+ {isUploaded ? ( + + ) : isFailed ? ( + + ) : ( + + )} +
+ ) : ( + + )} +
+ )} + {isUploading ? ( + + ) : ( + + )} +
+ ); +} diff --git a/frontend/src/components/ImageUploadFolderFileModal/index.tsx b/frontend/src/components/ImageUploadFolderFileModal/index.tsx new file mode 100644 index 0000000..73992b9 --- /dev/null +++ b/frontend/src/components/ImageUploadFolderFileModal/index.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { Dialog, DialogContent, DialogHeader, DialogTrigger } from '../ui/dialogCustom'; +import { Plus } from 'lucide-react'; +import ImageUploadFolderFileForm from './ImageUploadFolderFileForm'; + +export default function ImageUploadFolderFileModal({ projectId, folderId }: { projectId: number; folderId: number }) { + const [isOpen, setIsOpen] = React.useState(false); + + const handleOpen = () => setIsOpen(true); + const handleClose = () => setIsOpen(false); + + return ( + + + + + + + + + + ); +} diff --git a/frontend/src/components/ImageUploadFolderModal/ImageUploadFolderForm.tsx b/frontend/src/components/ImageUploadFolderModal/ImageUploadFolderForm.tsx index aaaae3a..be704f9 100644 --- a/frontend/src/components/ImageUploadFolderModal/ImageUploadFolderForm.tsx +++ b/frontend/src/components/ImageUploadFolderModal/ImageUploadFolderForm.tsx @@ -20,6 +20,7 @@ export default function ImageUploadFolderForm({ const memberId = profile?.id || 0; const [files, setFiles] = useState([]); + const [inputKey, setInputKey] = useState(0); const [isDragging, setIsDragging] = useState(false); const [isUploading, setIsUploading] = useState(false); const [isUploaded, setIsUploaded] = useState(false); @@ -44,8 +45,6 @@ export default function ImageUploadFolderForm({ if (newFiles) { setFiles((prevFiles) => [...prevFiles, ...Array.from(newFiles)]); } - - event.target.value = ''; }; const handleDragOver = (event: React.DragEvent) => { @@ -62,8 +61,9 @@ export default function ImageUploadFolderForm({ setIsDragging(false); }; - const handleRemoveFile = (index: number) => { - setFiles(files.filter((_, i) => i != index)); + const handleRemoveFile = () => { + setFiles([]); + setInputKey((prevKey) => prevKey + 1); }; const handleUpload = async () => { @@ -101,6 +101,7 @@ export default function ImageUploadFolderForm({ )} > )} {files.length > 0 && ( -
    - {files.map((file, index) => ( -
  • - {file.webkitRelativePath || file.name} - {isUploading ? ( -
    - {isUploaded ? ( - - ) : isFailed ? ( - - ) : ( - - )} -
    +
    + + {files[0].webkitRelativePath.substring(0, files[0].webkitRelativePath.indexOf('/'))} {} + + {isUploading ? ( +
    + {isUploaded ? ( + + ) : isFailed ? ( + ) : ( - + )} -
  • - ))} -
+ + ) : ( + + )} + )} {isUploading ? (