diff options
Diffstat (limited to 'lib/dolce-v2/dialogs/upload-files-to-detail-dialog-v2.tsx')
| -rw-r--r-- | lib/dolce-v2/dialogs/upload-files-to-detail-dialog-v2.tsx | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/lib/dolce-v2/dialogs/upload-files-to-detail-dialog-v2.tsx b/lib/dolce-v2/dialogs/upload-files-to-detail-dialog-v2.tsx new file mode 100644 index 00000000..c59f6d78 --- /dev/null +++ b/lib/dolce-v2/dialogs/upload-files-to-detail-dialog-v2.tsx @@ -0,0 +1,247 @@ +"use client"; + +import * as React from "react"; +import { useState } from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Upload, FolderOpen, Loader2, X, FileText, AlertCircle } from "lucide-react"; +import { toast } from "sonner"; +import { useTranslation } from "@/i18n/client"; +import { useFileUploadWithProgress } from "@/lib/dolce/hooks/use-file-upload-with-progress"; +import { uploadFilesToDetailDrawingV2 } from "@/lib/dolce-v2/actions"; + +interface UploadFilesToDetailDialogV2Props { + open: boolean; + onOpenChange: (open: boolean) => void; + uploadId: string; + drawingNo: string; + revNo: string; + // [추가] 메타데이터 저장을 위한 추가 정보 + drawingName?: string; + discipline?: string; + registerKind?: string; + + userId: string; + projectNo?: string; // V2에서는 projectNo 필요 (Sync List 조회 인덱스용) + vendorCode?: string; // V2: 동기화 필터링용 + onUploadComplete?: () => void; + lng: string; +} + +export function UploadFilesToDetailDialogV2({ + open, + onOpenChange, + uploadId, + drawingNo, + revNo, + drawingName, + discipline, + registerKind, + userId, + projectNo, + vendorCode, + onUploadComplete, + lng, +}: UploadFilesToDetailDialogV2Props) { + const { t } = useTranslation(lng, "dolce"); + const [isUploading, setIsUploading] = useState(false); + + // 파일 업로드 훅 사용 (UI용) + const { + files: selectedFiles, + removeFile, + clearFiles, + getRootProps, + getInputProps, + isDragActive, + } = useFileUploadWithProgress(); + + // 다이얼로그 닫을 때 초기화 + React.useEffect(() => { + if (!open) { + clearFiles(); + } + }, [open, clearFiles]); + + // 업로드 처리 + const handleUpload = async () => { + if (selectedFiles.length === 0) { + toast.error(t("uploadFilesDialog.selectFilesError")); + return; + } + + setIsUploading(true); + + try { + const formData = new FormData(); + formData.append("uploadId", uploadId); + formData.append("userId", userId); + formData.append("fileCount", String(selectedFiles.length)); + if (projectNo) formData.append("projectNo", projectNo); + if (vendorCode) formData.append("vendorCode", vendorCode); + + // 메타데이터 추가 + formData.append("drawingNo", drawingNo); + formData.append("revNo", revNo); + if (drawingName) formData.append("drawingName", drawingName); + if (discipline) formData.append("discipline", discipline); + if (registerKind) formData.append("registerKind", registerKind); + + selectedFiles.forEach((file, index) => { + formData.append(`file_${index}`, file); + }); + + const result = await uploadFilesToDetailDrawingV2(formData); + + if (result.success) { + toast.success(t("uploadFilesDialog.uploadSuccess", { count: selectedFiles.length })); + onOpenChange(false); + onUploadComplete?.(); + } else { + toast.error(result.error || t("uploadFilesDialog.uploadError")); + } + } catch (error) { + console.error("업로드 실패:", error); + toast.error( + error instanceof Error ? error.message : t("uploadFilesDialog.uploadErrorMessage") + ); + } finally { + setIsUploading(false); + } + }; + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="max-w-2xl"> + <DialogHeader> + <DialogTitle>{t("uploadFilesDialog.title")}</DialogTitle> + <DialogDescription> + {t("uploadFilesDialog.description", { drawingNo, revNo })} + </DialogDescription> + </DialogHeader> + + <div className="space-y-4"> + {/* 안내 메시지 */} + <Alert> + <AlertCircle className="h-4 w-4" /> + <AlertDescription> + {t("uploadFilesDialog.alertMessage")} + </AlertDescription> + </Alert> + + {/* 파일 선택 영역 */} + <div + {...getRootProps()} + className={`border-2 border-dashed rounded-lg p-8 transition-all duration-200 cursor-pointer ${ + isDragActive + ? "border-primary bg-primary/5 scale-[1.02]" + : "border-muted-foreground/30 hover:border-muted-foreground/50" + }`} + > + <input {...getInputProps()} /> + <div className="flex flex-col items-center justify-center"> + <FolderOpen + className={`h-12 w-12 mb-3 transition-colors ${ + isDragActive ? "text-primary" : "text-muted-foreground" + }`} + /> + <p + className={`text-sm transition-colors ${ + isDragActive + ? "text-primary font-medium" + : "text-muted-foreground" + }`} + > + {isDragActive + ? t("uploadFilesDialog.dropHereText") + : t("uploadFilesDialog.dragDropText")} + </p> + <p className="text-xs text-muted-foreground mt-1"> + {t("uploadFilesDialog.fileInfo")} + </p> + </div> + </div> + + {/* 선택된 파일 목록 */} + {selectedFiles.length > 0 && ( + <div className="border rounded-lg p-4"> + <> + <div className="flex items-center justify-between mb-3"> + <h4 className="text-sm font-medium"> + {t("uploadFilesDialog.selectedFiles", { count: selectedFiles.length })} + </h4> + <Button + variant="ghost" + size="sm" + onClick={clearFiles} + > + {t("uploadFilesDialog.removeAll")} + </Button> + </div> + <div className="max-h-60 overflow-y-auto space-y-2"> + {selectedFiles.map((file, index) => ( + <div + key={index} + className="flex items-center justify-between p-2 rounded bg-muted/50" + > + <div className="flex items-center gap-2 flex-1 min-w-0"> + <FileText 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> + </div> + <Button + variant="ghost" + size="sm" + onClick={() => removeFile(index)} + > + <X className="h-4 w-4" /> + </Button> + </div> + ))} + </div> + </> + </div> + )} + </div> + + <DialogFooter> + <Button + variant="outline" + onClick={() => onOpenChange(false)} + disabled={isUploading} + > + {t("uploadFilesDialog.cancelButton")} + </Button> + <Button + onClick={handleUpload} + disabled={selectedFiles.length === 0 || isUploading} + > + {isUploading ? ( + <> + <Loader2 className="mr-2 h-4 w-4 animate-spin" /> + {t("uploadFilesDialog.uploadingButton")} + </> + ) : ( + <> + <Upload className="mr-2 h-4 w-4" /> + {t("uploadFilesDialog.uploadButton", { count: selectedFiles.length })} + </> + )} + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ); +} |
