diff options
Diffstat (limited to 'lib/dolce/dialogs/add-detail-drawing-dialog.tsx')
| -rw-r--r-- | lib/dolce/dialogs/add-detail-drawing-dialog.tsx | 129 |
1 files changed, 82 insertions, 47 deletions
diff --git a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx index 290a226b..34d06368 100644 --- a/lib/dolce/dialogs/add-detail-drawing-dialog.tsx +++ b/lib/dolce/dialogs/add-detail-drawing-dialog.tsx @@ -1,7 +1,6 @@ "use client"; -import { useState, useCallback } from "react"; -import { useDropzone } from "react-dropzone"; +import { useState } from "react"; import { Dialog, DialogContent, @@ -22,8 +21,11 @@ import { import { Alert, AlertDescription } from "@/components/ui/alert"; import { Upload, X, FileIcon, Info } from "lucide-react"; import { toast } from "sonner"; -import { UnifiedDwgReceiptItem, editDetailDwgReceipt, uploadFilesToDetailDrawing } from "../actions"; +import { UnifiedDwgReceiptItem, editDetailDwgReceipt } from "../actions"; import { v4 as uuidv4 } from "uuid"; +import { useFileUploadWithProgress } from "../hooks/use-file-upload-with-progress"; +import { uploadFilesWithProgress } from "../utils/upload-with-progress"; +import { FileUploadProgressList } from "../components/file-upload-progress-list"; interface AddDetailDrawingDialogProps { open: boolean; @@ -80,30 +82,26 @@ export function AddDetailDrawingDialog({ const [drawingUsage, setDrawingUsage] = useState<string>(""); const [registerKind, setRegisterKind] = useState<string>(""); const [revision, setRevision] = useState<string>(""); - const [files, setFiles] = useState<File[]>([]); const [isSubmitting, setIsSubmitting] = useState(false); - // 파일 드롭 핸들러 - const onDrop = useCallback((acceptedFiles: File[]) => { - setFiles((prev) => [...prev, ...acceptedFiles]); - }, []); - - const { getRootProps, getInputProps, isDragActive } = useDropzone({ - onDrop, - multiple: true, - }); - - // 파일 제거 - const removeFile = (index: number) => { - setFiles((prev) => prev.filter((_, i) => i !== index)); - }; + // 파일 업로드 훅 사용 (진행도 추적) + const { + fileProgresses, + files, + removeFile, + clearFiles, + updateFileProgress, + getRootProps, + getInputProps, + isDragActive, + } = useFileUploadWithProgress(); // 폼 초기화 const resetForm = () => { setDrawingUsage(""); setRegisterKind(""); setRevision(""); - setFiles([]); + clearFiles(); }; // 제출 @@ -169,16 +167,27 @@ export function AddDetailDrawingDialog({ if (files.length > 0) { toast.info(`${files.length}개 파일 업로드를 진행합니다...`); - const formData = new FormData(); - formData.append("uploadId", uploadId); - formData.append("userId", userId); - formData.append("fileCount", String(files.length)); - - files.forEach((file, index) => { - formData.append(`file_${index}`, file); + // 모든 파일 상태를 uploading으로 변경 + files.forEach((_, index) => { + updateFileProgress(index, 0, "uploading"); }); - const uploadResult = await uploadFilesToDetailDrawing(formData); + const uploadResult = await uploadFilesWithProgress({ + uploadId, + userId, + files, + callbacks: { + onProgress: (fileIndex, progress) => { + updateFileProgress(fileIndex, progress, "uploading"); + }, + onFileComplete: (fileIndex) => { + updateFileProgress(fileIndex, 100, "completed"); + }, + onFileError: (fileIndex, error) => { + updateFileProgress(fileIndex, 0, "error", error); + }, + }, + }); if (uploadResult.success) { toast.success(`상세도면 추가 및 ${uploadResult.uploadedCount}개 파일 업로드 완료`); @@ -189,8 +198,10 @@ export function AddDetailDrawingDialog({ toast.success("상세도면이 추가되었습니다"); } + // API 호출 성공 시 무조건 다이얼로그 닫기 (파일 업로드 성공 여부와 무관) resetForm(); onComplete(); + onOpenChange(false); } else { toast.error("상세도면 추가에 실패했습니다"); } @@ -318,7 +329,7 @@ export function AddDetailDrawingDialog({ 파일을 드래그하거나 클릭하여 선택 </p> <p className="text-xs text-muted-foreground"> - 여러 파일을 한 번에 업로드할 수 있습니다 + 여러 파일을 한 번에 업로드할 수 있습니다 (최대 1GB/파일) </p> </div> </div> @@ -337,25 +348,49 @@ export function AddDetailDrawingDialog({ {/* 선택된 파일 목록 */} {files.length > 0 && ( <div className="space-y-2 mt-4"> - {files.map((file, index) => ( - <div - key={index} - className="flex items-center gap-2 p-2 border rounded-lg" - > - <FileIcon className="h-4 w-4 text-muted-foreground" /> - <span className="flex-1 text-sm truncate">{file.name}</span> - <span className="text-xs text-muted-foreground"> - {(file.size / 1024).toFixed(2)} KB - </span> - <Button - variant="ghost" - size="icon" - onClick={() => removeFile(index)} - > - <X className="h-4 w-4" /> - </Button> - </div> - ))} + {isSubmitting ? ( + // 업로드 중: 진행도 표시 + <FileUploadProgressList fileProgresses={fileProgresses} /> + ) : ( + // 대기 중: 삭제 버튼 표시 + <> + <div className="flex items-center justify-between mb-2"> + <h4 className="text-sm font-medium"> + 선택된 파일 ({files.length}개) + </h4> + <Button + variant="ghost" + size="sm" + onClick={clearFiles} + > + 전체 제거 + </Button> + </div> + <div className="max-h-48 overflow-auto space-y-2"> + {files.map((file, index) => ( + <div + key={index} + className="flex items-center gap-2 p-2 border rounded-lg bg-muted/50" + > + <FileIcon className="h-4 w-4 text-muted-foreground shrink-0" /> + <div className="flex-1 min-w-0"> + <p className="text-sm truncate">{file.name}</p> + <p className="text-xs text-muted-foreground"> + {(file.size / 1024 / 1024).toFixed(2)} MB + </p> + </div> + <Button + variant="ghost" + size="sm" + onClick={() => removeFile(index)} + > + <X className="h-4 w-4" /> + </Button> + </div> + ))} + </div> + </> + )} </div> )} </div> |
