summaryrefslogtreecommitdiff
path: root/lib/dolce-v2/dialogs/upload-files-to-detail-dialog-v2.tsx
diff options
context:
space:
mode:
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.tsx247
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>
+ );
+}