"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 { FolderOpen, Loader2, ChevronRight, ChevronLeft, CheckCircle2 } from "lucide-react"; import { toast } from "sonner"; import { Progress } from "@/components/ui/progress"; import { useTranslation } from "@/i18n/client"; import { validateB4FileName, B4UploadValidationDialog, type FileValidationResult, } from "@/lib/dolce/dialogs/b4-upload-validation-dialog"; import { checkB4MappingStatus, type MappingCheckResult, type B4BulkUploadResult, } from "@/lib/dolce/actions"; import { bulkUploadB4FilesV3 } from "@/lib/dolce-v2/actions"; interface B4BulkUploadDialogV3SyncProps { open: boolean; onOpenChange: (open: boolean) => void; projectNo: string; userId: string; userName: string; userEmail: string; vendorCode: string; onUploadComplete?: () => void; lng: string; } type UploadStep = "files" | "validation" | "uploading" | "complete"; export function B4BulkUploadDialogV3Sync({ open, onOpenChange, projectNo, userId, userName, userEmail, vendorCode, onUploadComplete, lng, }: B4BulkUploadDialogV3SyncProps) { const { t } = useTranslation(lng, "dolce"); const [currentStep, setCurrentStep] = useState("files"); const [selectedFiles, setSelectedFiles] = useState([]); const [isUploading, setIsUploading] = useState(false); const [validationResults, setValidationResults] = useState([]); const [mappingResultsMap, setMappingResultsMap] = useState>(new Map()); const [showValidationDialog, setShowValidationDialog] = useState(false); const [isDragging, setIsDragging] = useState(false); // const [uploadProgress, setUploadProgress] = useState(0); // 로컬 저장은 순식간이라 프로그레스 불필요 const [uploadResult, setUploadResult] = useState<{ success: boolean, syncIds: string[], error?: string } | null>(null); // Reset on close React.useEffect(() => { if (!open) { setCurrentStep("files"); setSelectedFiles([]); setValidationResults([]); setMappingResultsMap(new Map()); setShowValidationDialog(false); setIsDragging(false); setUploadResult(null); } }, [open]); // File Selection Handler (동일) const handleFilesChange = (files: File[]) => { if (files.length === 0) return; const existingNames = new Set(selectedFiles.map((f) => f.name)); const newFiles = files.filter((f) => !existingNames.has(f.name)); if (newFiles.length === 0) { toast.error(t("bulkUpload.duplicateFileError")); return; } setSelectedFiles((prev) => [...prev, ...newFiles]); toast.success(t("bulkUpload.filesSelectedSuccess", { count: newFiles.length })); }; // Drag & Drop Handlers (생략 - 코드 길이 줄임) const handleDragEnter = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(true); }; const handleDragLeave = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); }; const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = "copy"; }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); const droppedFiles = Array.from(e.dataTransfer.files); if (droppedFiles.length > 0) handleFilesChange(droppedFiles); }; const handleRemoveFile = (index: number) => { setSelectedFiles((prev) => prev.filter((_, i) => i !== index)); }; // Step 1 Next: Validation (동일) const handleFilesNext = () => { if (selectedFiles.length === 0) { toast.error(t("bulkUpload.selectFilesError")); return; } setCurrentStep("validation"); handleValidate(); }; // Validation Process (V3 - 기존과 동일) const handleValidate = async () => { try { // 1. Parse Filenames const parseResults: FileValidationResult[] = selectedFiles.map((file) => { const validation = validateB4FileName(file.name); return { file, valid: validation.valid, parsed: validation.parsed, error: validation.error, }; }); const parsedFiles = parseResults.filter((r) => r.valid && r.parsed); if (parsedFiles.length === 0) { setValidationResults(parseResults); setShowValidationDialog(true); return; } // 2. Call MatchBatchFileDwg to check mapping status const mappingCheckItems = parsedFiles.map((r) => ({ DrawingNo: r.parsed!.drawingNo, RevNo: r.parsed!.revNo, FileNm: r.file.name, })); const mappingResults = await checkB4MappingStatus(projectNo, mappingCheckItems); const newMappingResultsMap = new Map(); mappingResults.forEach((result) => { newMappingResultsMap.set(result.FileNm, result); }); setMappingResultsMap(newMappingResultsMap); // 3. Merge results const finalResults: FileValidationResult[] = parseResults.map((parseResult) => { if (!parseResult.valid || !parseResult.parsed) return parseResult; const mappingResult = newMappingResultsMap.get(parseResult.file.name); if (!mappingResult) return { ...parseResult, mappingStatus: "not_found" as const, error: t("validation.notFound") }; if (mappingResult.MappingYN !== "Y") return { ...parseResult, mappingStatus: "not_found" as const, error: t("validation.notRegistered") }; if (mappingResult.DrawingMoveGbn !== "도면입수") return { ...parseResult, mappingStatus: "not_found" as const, error: t("validation.notGttDeliverables") }; return { ...parseResult, mappingStatus: "available" as const, drawingName: mappingResult.DrawingName || undefined, registerGroupId: mappingResult.RegisterGroupId, }; }); setValidationResults(finalResults); setShowValidationDialog(true); } catch (error) { console.error("Validation failed:", error); toast.error(error instanceof Error ? error.message : t("bulkUpload.validationError")); setCurrentStep("files"); } }; // Confirm Upload & Save (V3 Sync - 수정됨) const handleConfirmUpload = async (validFiles: FileValidationResult[]) => { setIsUploading(true); setCurrentStep("uploading"); setShowValidationDialog(false); try { // FormData 구성 const formData = new FormData(); formData.append("projectNo", projectNo); formData.append("userId", userId); formData.append("userNm", userName); formData.append("email", userEmail); formData.append("vendorCode", vendorCode); formData.append("registerKind", ""); // B4는 mappingData에 있음, 혹은 필요하다면 추가 formData.append("fileCount", String(validFiles.length)); validFiles.forEach((fileResult, index) => { formData.append(`file_${index}`, fileResult.file); const mappingData = mappingResultsMap.get(fileResult.file.name); if (mappingData) { // UploadId가 없으면 생성 if (!mappingData.UploadId) { mappingData.UploadId = uuidv4(); // 임시 ID 생성 (서버에서 그룹핑용) } formData.append(`mappingData_${index}`, JSON.stringify(mappingData)); } }); // Action 호출 const result = await bulkUploadB4FilesV3(formData); setUploadResult(result); setCurrentStep("complete"); if (result.success) { toast.success(t("bulkUpload.uploadSuccessToast", { successCount: validFiles.length, total: validFiles.length })); } else { toast.error(result.error || t("bulkUpload.uploadError")); } } catch (error) { console.error("Upload process failed:", error); toast.error(error instanceof Error ? error.message : t("bulkUpload.uploadError")); setCurrentStep("files"); } finally { setIsUploading(false); } }; return ( <> {t("bulkUpload.title")} (Offline) {currentStep === "files" && t("bulkUpload.stepFiles")} {currentStep === "validation" && t("bulkUpload.stepValidation")}
{/* Step 1: Files */} {currentStep === "files" && ( <>
handleFilesChange(Array.from(e.target.files || []))} className="hidden" id="b4-file-upload-v3-sync" />
{selectedFiles.length > 0 && (

{t("bulkUpload.selectedFiles", { count: selectedFiles.length })}

{selectedFiles.map((file, index) => (

{file.name}

))}
)} )} {/* Loading Indicator */} {currentStep === "validation" && (

{t("bulkUpload.validating")}

)} {/* Uploading (Saving locally) */} {currentStep === "uploading" && (

Saving to Local...

Please wait while we buffer your files.

)} {/* Completion Screen */} {currentStep === "complete" && uploadResult && (

Saved Locally

{uploadResult.syncIds.length} items are ready to sync.

)}
{/* Footer */} {currentStep !== "uploading" && currentStep !== "complete" && currentStep !== "validation" && ( {currentStep === "files" && ( <> )} )}
{/* Validation Dialog */} { setShowValidationDialog(open); if (!open && currentStep !== "uploading" && currentStep !== "complete") { setCurrentStep("files"); } }} validationResults={validationResults} onConfirmUpload={handleConfirmUpload} isUploading={isUploading} /> ); }