diff options
| author | joonhoekim <26rote@gmail.com> | 2025-11-27 13:48:44 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-11-27 13:48:44 +0900 |
| commit | b43b1d92ef3d7e57b5df5cd72f75dc3a1c3f1c7a (patch) | |
| tree | 943e2a52c9e56bdce2fa3a35ef61b795370f47e0 /lib/dolce/dialogs | |
| parent | 79cfa7ea8f21ae227dbb2843ae536fe876ba7c55 (diff) | |
(김준회) swp 파일 개수 컬럼 삭제 (API에서 주지 않는 데이터), dolce rebuild 에서 상태값 수정, bulk upload MatchBatchFileDwg API 사용해 Edit 으로 보내도록 수정 (Category, status 하드코딩 값 넣어주도록 처리), 상세도면, 파일 추가시 확인 다이얼로그 추가
Diffstat (limited to 'lib/dolce/dialogs')
| -rw-r--r-- | lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx | 415 | ||||
| -rw-r--r-- | lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx | 25 | ||||
| -rw-r--r-- | lib/dolce/dialogs/upload-files-to-detail-dialog.tsx | 252 |
3 files changed, 416 insertions, 276 deletions
diff --git a/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx b/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx index 673d48d6..0253228b 100644 --- a/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx +++ b/lib/dolce/dialogs/add-and-modify-detail-drawing-dialog.tsx @@ -72,6 +72,8 @@ export function AddAndModifyDetailDrawingDialog({ const [comment, setComment] = useState<string>(""); const [isSubmitting, setIsSubmitting] = useState(false); + const [showConfirmation, setShowConfirmation] = useState(false); + // Edit 모드일 때 초기값 설정 useEffect(() => { if (mode === "edit" && detailDrawing && open) { @@ -155,9 +157,10 @@ export function AddAndModifyDetailDrawingDialog({ setRevisionError(""); setComment(""); clearFiles(); + setShowConfirmation(false); }; - // 제출 + // 제출 (확인 단계 포함) const handleSubmit = async () => { // 유효성 검사 if (!registerKind) { @@ -200,6 +203,12 @@ export function AddAndModifyDetailDrawingDialog({ return; } + // 확인 단계가 아니면 확인 단계로 이동 + if (!showConfirmation) { + setShowConfirmation(true); + return; + } + try { setIsSubmitting(true); @@ -212,7 +221,7 @@ export function AddAndModifyDetailDrawingDialog({ dwgList: [ { Mode: "ADD", - Status: "Submitted", + Status: "Standby", RegisterId: 0, ProjectNo: drawing.ProjectNo, Discipline: drawing.Discipline, @@ -329,8 +338,12 @@ export function AddAndModifyDetailDrawingDialog({ }; const handleCancel = () => { - resetForm(); - onOpenChange(false); + if (showConfirmation) { + setShowConfirmation(false); + } else { + resetForm(); + onOpenChange(false); + } }; // DrawingUsage가 변경되면 RegisterKind 초기화 @@ -355,219 +368,279 @@ export function AddAndModifyDetailDrawingDialog({ return ( <Dialog open={open} onOpenChange={onOpenChange}> - <DialogContent className="max-w-2xl"> + <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto"> <DialogHeader> <DialogTitle> - {mode === "edit" ? t("editDetailDialog.title") : t("addDetailDialog.title")} + {showConfirmation + ? t("addDetailDialog.confirmTitle", "확인") + : (mode === "edit" ? t("editDetailDialog.title") : t("addDetailDialog.title")) + } </DialogTitle> </DialogHeader> - <div className="space-y-6"> - {/* 도면 정보 표시 */} - {mode === "add" && drawing && ( + {showConfirmation ? ( + <div className="space-y-6"> <Alert> <Info className="h-4 w-4" /> <AlertDescription> - <div className="font-medium">{drawing.DrawingNo}</div> - <div className="text-sm text-muted-foreground">{drawing.DrawingName}</div> + {t("addDetailDialog.confirmMessage", "아래 내용으로 제출하시겠습니까?")} </AlertDescription> </Alert> - )} - {mode === "edit" && detailDrawing && ( - <Alert> - <Info className="h-4 w-4" /> - <AlertDescription> - <div className="font-medium">{detailDrawing.DrawingNo} - Rev. {detailDrawing.DrawingRevNo}</div> - <div className="text-sm text-muted-foreground">{detailDrawing.DrawingName}</div> - </AlertDescription> - </Alert> - )} + <div className="grid grid-cols-2 gap-4 border rounded-lg p-4 bg-muted/20"> + <div className="space-y-1"> + <Label className="text-xs text-muted-foreground">{t("addDetailDialog.drawingUsageLabel")}</Label> + <p className="text-sm font-medium"> + {drawingUsageOptions.find(opt => opt.value === drawingUsage)?.label || drawingUsage} + </p> + </div> + <div className="space-y-1"> + <Label className="text-xs text-muted-foreground">{t("addDetailDialog.registerKindLabel")}</Label> + <p className="text-sm font-medium"> + {registerKindOptions.find(opt => opt.value === registerKind)?.label || registerKind} + </p> + </div> + {drawingUsage !== "CMT" && ( + <div className="space-y-1"> + <Label className="text-xs text-muted-foreground">{t("addDetailDialog.revisionLabel")}</Label> + <p className="text-sm font-medium">{revision}</p> + </div> + )} + <div className="space-y-1 col-span-2"> + <Label className="text-xs text-muted-foreground">{t("addDetailDialog.commentLabel")}</Label> + <p className="text-sm">{comment || "-"}</p> + </div> + </div> + + {files.length > 0 && ( + <div className="space-y-2"> + <Label>{t("addDetailDialog.selectedFiles", { count: files.length })}</Label> + <div className="max-h-60 overflow-y-auto space-y-2 border rounded-lg p-2"> + {isSubmitting ? ( + <FileUploadProgressList fileProgresses={fileProgresses} /> + ) : ( + files.map((file, index) => ( + <div key={index} className="flex items-center gap-2 p-2 rounded bg-muted/50 text-sm"> + <FileIcon className="h-4 w-4 text-muted-foreground shrink-0" /> + <span className="truncate flex-1">{file.name}</span> + <span className="text-xs text-muted-foreground whitespace-nowrap"> + {(file.size / 1024 / 1024).toFixed(2)} MB + </span> + </div> + )) + )} + </div> + </div> + )} + </div> + ) : ( + <div className="space-y-6"> + {/* 도면 정보 표시 */} + {mode === "add" && drawing && ( + <Alert> + <Info className="h-4 w-4" /> + <AlertDescription> + <div className="font-medium">{drawing.DrawingNo}</div> + <div className="text-sm text-muted-foreground">{drawing.DrawingName}</div> + </AlertDescription> + </Alert> + )} + + {mode === "edit" && detailDrawing && ( + <Alert> + <Info className="h-4 w-4" /> + <AlertDescription> + <div className="font-medium">{detailDrawing.DrawingNo} - Rev. {detailDrawing.DrawingRevNo}</div> + <div className="text-sm text-muted-foreground">{detailDrawing.DrawingName}</div> + </AlertDescription> + </Alert> + )} - {/* 도면용도 선택 (Add 모드에서만 표시) */} - {mode === "add" && ( + {/* 도면용도 선택 (Add 모드에서만 표시) */} + {mode === "add" && ( + <div className="space-y-2"> + <Label>{t("addDetailDialog.drawingUsageLabel")}</Label> + <Select value={drawingUsage} onValueChange={handleDrawingUsageChange}> + <SelectTrigger> + <SelectValue placeholder={t("addDetailDialog.drawingUsagePlaceholder")} /> + </SelectTrigger> + <SelectContent> + {drawingUsageOptions.map((option) => ( + <SelectItem key={option.value} value={option.value}> + {option.label} + </SelectItem> + ))} + </SelectContent> + </Select> + </div> + )} + + {/* 등록종류 선택 */} <div className="space-y-2"> - <Label>{t("addDetailDialog.drawingUsageLabel")}</Label> - <Select value={drawingUsage} onValueChange={handleDrawingUsageChange}> + <Label>{t("addDetailDialog.registerKindLabel")}</Label> + <Select + value={registerKind} + onValueChange={setRegisterKind} + disabled={mode === "add" && !drawingUsage} + > <SelectTrigger> - <SelectValue placeholder={t("addDetailDialog.drawingUsagePlaceholder")} /> + <SelectValue placeholder={t("addDetailDialog.registerKindPlaceholder")} /> </SelectTrigger> <SelectContent> - {drawingUsageOptions.map((option) => ( + {registerKindOptions.map((option) => ( <SelectItem key={option.value} value={option.value}> {option.label} </SelectItem> ))} </SelectContent> </Select> + {revisionRule && ( + <p className="text-sm text-muted-foreground"> + {t("addDetailDialog.revisionFormatPrefix")}{revisionRule} + </p> + )} </div> - )} - - {/* 등록종류 선택 */} - <div className="space-y-2"> - <Label>{t("addDetailDialog.registerKindLabel")}</Label> - <Select - value={registerKind} - onValueChange={setRegisterKind} - disabled={mode === "add" && !drawingUsage} - > - <SelectTrigger> - <SelectValue placeholder={t("addDetailDialog.registerKindPlaceholder")} /> - </SelectTrigger> - <SelectContent> - {registerKindOptions.map((option) => ( - <SelectItem key={option.value} value={option.value}> - {option.label} - </SelectItem> - ))} - </SelectContent> - </Select> - {revisionRule && ( - <p className="text-sm text-muted-foreground"> - {t("addDetailDialog.revisionFormatPrefix")}{revisionRule} - </p> + + {/* Revision 입력 */} + {drawingUsage !== "CMT" && ( + <div className="space-y-2"> + <Label>{t("addDetailDialog.revisionLabel")}</Label> + <Input + value={revision} + onChange={(e) => handleRevisionChange(e.target.value)} + placeholder={t("addDetailDialog.revisionPlaceholder")} + disabled={!registerKind} + className={revisionError ? "border-red-500 focus-visible:ring-red-500" : ""} + /> + {revisionError && ( + <p className="text-sm text-red-500 flex items-center gap-1"> + {revisionError} + </p> + )} + {!revisionError && revision && ( + <p className="text-sm text-green-600 flex items-center gap-1"> + {t("addDetailDialog.revisionValid")} + </p> + )} + </div> )} - </div> - {/* Revision 입력 */} - {drawingUsage !== "CMT" && ( + {/* Comment 입력 */} <div className="space-y-2"> - <Label>{t("addDetailDialog.revisionLabel")}</Label> - <Input - value={revision} - onChange={(e) => handleRevisionChange(e.target.value)} - placeholder={t("addDetailDialog.revisionPlaceholder")} - disabled={!registerKind} - className={revisionError ? "border-red-500 focus-visible:ring-red-500" : ""} + <Label>{t("addDetailDialog.commentLabel")}</Label> + <Textarea + value={comment} + onChange={(e) => setComment(e.target.value)} + placeholder={t("addDetailDialog.commentPlaceholder")} + rows={3} + className="resize-none" /> - {revisionError && ( - <p className="text-sm text-red-500 flex items-center gap-1"> - {revisionError} - </p> - )} - {!revisionError && revision && ( - <p className="text-sm text-green-600 flex items-center gap-1"> - {t("addDetailDialog.revisionValid")} - </p> - )} + <p className="text-xs text-muted-foreground"> + {t("addDetailDialog.commentMaxLength")} + </p> </div> - )} - - {/* Comment 입력 */} - <div className="space-y-2"> - <Label>{t("addDetailDialog.commentLabel")}</Label> - <Textarea - value={comment} - onChange={(e) => setComment(e.target.value)} - placeholder={t("addDetailDialog.commentPlaceholder")} - rows={3} - className="resize-none" - /> - <p className="text-xs text-muted-foreground"> - {t("addDetailDialog.commentMaxLength")} - </p> - </div> - {/* 파일 업로드 (Add 모드에서만 표시) */} - {mode === "add" && ( - <div className="space-y-2"> - <Label>{t("addDetailDialog.attachmentLabel")}</Label> - <div - {...getRootProps()} - className={` - border-2 border-dashed rounded-lg p-8 text-center cursor-pointer - transition-colors - ${isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25"} - ${files.length > 0 ? "py-4" : ""} - `} - > - <input {...getInputProps()} /> - {files.length === 0 ? ( - <div className="space-y-2"> - <Upload className="h-8 w-8 mx-auto text-muted-foreground" /> - <div> + {/* 파일 업로드 (Add 모드에서만 표시) */} + {mode === "add" && ( + <div className="space-y-2"> + <Label>{t("addDetailDialog.attachmentLabel")}</Label> + <div + {...getRootProps()} + className={` + border-2 border-dashed rounded-lg p-8 text-center cursor-pointer + transition-colors + ${isDragActive ? "border-primary bg-primary/5" : "border-muted-foreground/25"} + ${files.length > 0 ? "py-4" : ""} + `} + > + <input {...getInputProps()} /> + {files.length === 0 ? ( + <div className="space-y-2"> + <Upload className="h-8 w-8 mx-auto text-muted-foreground" /> + <div> + <p className="text-sm font-medium"> + {t("addDetailDialog.dragDropText")} + </p> + <p className="text-xs text-muted-foreground"> + {t("addDetailDialog.fileInfo")} + </p> + </div> + </div> + ) : ( + <div className="space-y-2"> <p className="text-sm font-medium"> - {t("addDetailDialog.dragDropText")} + {t("addDetailDialog.filesSelected", { count: files.length })} </p> <p className="text-xs text-muted-foreground"> - {t("addDetailDialog.fileInfo")} + {t("addDetailDialog.addMoreFiles")} </p> </div> - </div> - ) : ( - <div className="space-y-2"> - <p className="text-sm font-medium"> - {t("addDetailDialog.filesSelected", { count: files.length })} - </p> - <p className="text-xs text-muted-foreground"> - {t("addDetailDialog.addMoreFiles")} - </p> - </div> - )} - </div> + )} + </div> - {/* 선택된 파일 목록 */} - {files.length > 0 && ( - <div className="space-y-2 mt-4"> - {isSubmitting ? ( - // 업로드 중: 진행도 표시 - <FileUploadProgressList fileProgresses={fileProgresses} /> - ) : ( - // 대기 중: 삭제 버튼 표시 - <> - <div className="flex items-center justify-between mb-2"> - <h4 className="text-sm font-medium"> - {t("addDetailDialog.selectedFiles", { count: files.length })} - </h4> - <Button - variant="ghost" - size="sm" - onClick={clearFiles} - > - {t("addDetailDialog.removeAll")} - </Button> - </div> - <div className="max-h-60 overflow-y-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" + {/* 선택된 파일 목록 */} + {files.length > 0 && ( + <div className="space-y-2 mt-4"> + {isSubmitting ? ( + // 업로드 중: 진행도 표시 + <FileUploadProgressList fileProgresses={fileProgresses} /> + ) : ( + // 대기 중: 삭제 버튼 표시 + <> + <div className="flex items-center justify-between mb-2"> + <h4 className="text-sm font-medium"> + {t("addDetailDialog.selectedFiles", { count: files.length })} + </h4> + <Button + variant="ghost" + size="sm" + onClick={clearFiles} > - <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)} + {t("addDetailDialog.removeAll")} + </Button> + </div> + <div className="max-h-60 overflow-y-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" > - <X className="h-4 w-4" /> - </Button> - </div> - ))} - </div> - </> - )} + <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> )} - </div> - )} - </div> + </div> + )} <DialogFooter> <Button variant="outline" onClick={handleCancel} disabled={isSubmitting}> - {t("addDetailDialog.cancelButton")} + {showConfirmation ? t("addDetailDialog.backButton", "뒤로") : t("addDetailDialog.cancelButton")} </Button> <Button onClick={handleSubmit} disabled={isSubmitting || !isFormValid}> {isSubmitting ? t("addDetailDialog.processingButton") - : mode === "edit" - ? t("editDetailDialog.updateButton") - : t("addDetailDialog.addButton") + : showConfirmation + ? t("addDetailDialog.confirmSubmit", "제출") + : t("addDetailDialog.nextButton", "다음") } </Button> </DialogFooter> diff --git a/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx b/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx index ea955420..8bb5dd42 100644 --- a/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx +++ b/lib/dolce/dialogs/b4-bulk-upload-dialog-v3.tsx @@ -301,7 +301,7 @@ export function B4BulkUploadDialogV3({ // Reuse UploadId from the first item's mapping data if available, else generate new const firstItemMapping = groupItems[0].mappingData; // Reuse existing UploadId if present in API response, otherwise generate new one - // The prompt says: "UploadId는 있으면 재활용하고, 없으면 UUID로 만들어줌" + // UploadId는 있으면 재활용하고, 없으면 UUID로 만들어서 사용 const uploadId = firstItemMapping.UploadId || uuidv4(); console.log(`[V3 Dialog] Processing group ${groupKey}, UploadId: ${uploadId}`); @@ -371,30 +371,31 @@ export function B4BulkUploadDialogV3({ const m = item.mappingData; return { CGbn: m.CGbn, - Category: "TS", // Hardcoded as per prompt - CheckBox: "0", + Category: "TS", // Hardcoded fixed value is required! + CheckBox: m.CheckBox, DGbn: m.DGbn, DegreeGbn: m.DegreeGbn, DeptGbn: m.DeptGbn, Discipline: m.Discipline, - DrawingKind: "B4", - DrawingMoveGbn: "도면입수", + DrawingKind: m.DrawingKind, + DrawingMoveGbn: m.DrawingMoveGbn, DrawingName: m.DrawingName, DrawingNo: m.DrawingNo, - DrawingUsage: "입수용", + DrawingUsage: m.DrawingUsage, FileNm: item.file.name, JGbn: m.JGbn, - Manager: m.Manager || "970043", // Fallback/Default - MappingYN: "Y", - NewOrNot: "N", + Manager: m.Manager, + MappingYN: m.MappingYN, + NewOrNot: m.NewOrNot, ProjectNo: projectNo, - RegisterGroup: 0, + RegisterGroup: m.RegisterGroup, RegisterGroupId: m.RegisterGroupId, RegisterKindCode: m.RegisterKindCode, RegisterSerialNo: m.RegisterSerialNo, RevNo: m.RevNo, SGbn: m.SGbn, - UploadId: uploadId // Used for all files in this group + UploadId: uploadId, // Used for all files in this group + status: "Standby", // Hardcoded fixed value is required! }; }); @@ -473,7 +474,7 @@ export function B4BulkUploadDialogV3({ return ( <> <Dialog open={open} onOpenChange={onOpenChange}> - <DialogContent className="max-w-2xl"> + <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto"> <DialogHeader> <DialogTitle>{t("bulkUpload.title")} (V3)</DialogTitle> <DialogDescription> diff --git a/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx b/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx index e8d82129..f21ccc70 100644 --- a/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx +++ b/lib/dolce/dialogs/upload-files-to-detail-dialog.tsx @@ -55,20 +55,29 @@ export function UploadFilesToDetailDialog({ isDragActive, } = useFileUploadWithProgress(); + const [showConfirmation, setShowConfirmation] = useState(false); + // 다이얼로그 닫을 때 초기화 React.useEffect(() => { if (!open) { clearFiles(); + setShowConfirmation(false); } }, [open, clearFiles]); - // 업로드 처리 + // 업로드 처리 (확인 단계 포함) const handleUpload = async () => { if (selectedFiles.length === 0) { toast.error(t("uploadFilesDialog.selectFilesError")); return; } + // 확인 단계가 아니면 확인 단계로 이동 + if (!showConfirmation) { + setShowConfirmation(true); + return; + } + setIsUploading(true); try { @@ -112,117 +121,169 @@ export function UploadFilesToDetailDialog({ } }; + const handleCancel = () => { + if (showConfirmation) { + setShowConfirmation(false); + } else { + onOpenChange(false); + } + }; + return ( <Dialog open={open} onOpenChange={onOpenChange}> - <DialogContent className="max-w-2xl"> + <DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto"> <DialogHeader> - <DialogTitle>{t("uploadFilesDialog.title")}</DialogTitle> + <DialogTitle> + {showConfirmation + ? t("uploadFilesDialog.confirmTitle", "파일 업로드 확인") + : 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> + {showConfirmation ? ( + <div className="space-y-4"> + <Alert> + <AlertCircle className="h-4 w-4" /> + <AlertDescription> + {t("uploadFilesDialog.confirmMessage", "선택한 파일을 업로드하시겠습니까?")} + </AlertDescription> + </Alert> - {/* 선택된 파일 목록 */} - {selectedFiles.length > 0 && ( <div className="border rounded-lg p-4"> - {isUploading ? ( - // 업로드 중: 진행도 표시 - <FileUploadProgressList fileProgresses={fileProgresses} /> - ) : ( - // 대기 중: 삭제 버튼 표시 - <> - <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} + <h4 className="text-sm font-medium mb-3"> + {t("uploadFilesDialog.selectedFiles", { count: selectedFiles.length })} + </h4> + <div className="max-h-60 overflow-y-auto space-y-2"> + {isUploading ? ( + <FileUploadProgressList fileProgresses={fileProgresses} /> + ) : ( + selectedFiles.map((file, index) => ( + <div + key={index} + className="flex items-center justify-between p-2 rounded bg-muted/50" > - {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 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> - <Button - variant="ghost" - size="sm" - onClick={() => removeFile(index)} - > - <X className="h-4 w-4" /> - </Button> </div> - ))} - </div> - </> - )} + </div> + )) + )} + </div> + </div> + </div> + ) : ( + <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> - )} - </div> + + {/* 선택된 파일 목록 */} + {selectedFiles.length > 0 && ( + <div className="border rounded-lg p-4"> + {isUploading ? ( + // 업로드 중: 진행도 표시 + <FileUploadProgressList fileProgresses={fileProgresses} /> + ) : ( + // 대기 중: 삭제 버튼 표시 + <> + <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)} + onClick={handleCancel} disabled={isUploading} > - {t("uploadFilesDialog.cancelButton")} + {showConfirmation ? t("uploadFilesDialog.backButton", "뒤로") : t("uploadFilesDialog.cancelButton")} </Button> <Button onClick={handleUpload} @@ -233,10 +294,15 @@ export function UploadFilesToDetailDialog({ <Loader2 className="mr-2 h-4 w-4 animate-spin" /> {t("uploadFilesDialog.uploadingButton")} </> + ) : showConfirmation ? ( + <> + <Upload className="mr-2 h-4 w-4" /> + {t("uploadFilesDialog.confirmUpload", "업로드")} + </> ) : ( <> <Upload className="mr-2 h-4 w-4" /> - {t("uploadFilesDialog.uploadButton", { count: selectedFiles.length })} + {t("uploadFilesDialog.nextButton", "다음")} </> )} </Button> |
