summaryrefslogtreecommitdiff
path: root/lib/dolce/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/dialogs')
-rw-r--r--lib/dolce/dialogs/b4-bulk-upload-dialog-v2.tsx625
-rw-r--r--lib/dolce/dialogs/b4-bulk-upload-dialog.tsx87
2 files changed, 680 insertions, 32 deletions
diff --git a/lib/dolce/dialogs/b4-bulk-upload-dialog-v2.tsx b/lib/dolce/dialogs/b4-bulk-upload-dialog-v2.tsx
new file mode 100644
index 00000000..3207c00b
--- /dev/null
+++ b/lib/dolce/dialogs/b4-bulk-upload-dialog-v2.tsx
@@ -0,0 +1,625 @@
+"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 { Label } from "@/components/ui/label";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+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 "./b4-upload-validation-dialog";
+import {
+ fetchDwgReceiptList,
+ bulkUploadB4FilesV2,
+ type B4BulkUploadResult,
+ type GttDwgReceiptItem,
+} from "../actions";
+
+interface B4BulkUploadDialogV2Props {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+ projectNo: string;
+ userId: string;
+ userName: string;
+ userEmail: string;
+ vendorCode: string;
+ onUploadComplete?: () => void;
+ lng: string;
+}
+
+type UploadStep = "settings" | "files" | "validation" | "uploading" | "complete";
+
+export function B4BulkUploadDialogV2({
+ open,
+ onOpenChange,
+ projectNo,
+ userId,
+ userName,
+ userEmail,
+ vendorCode,
+ onUploadComplete,
+ lng,
+}: B4BulkUploadDialogV2Props) {
+ const { t } = useTranslation(lng, "dolce");
+ const [currentStep, setCurrentStep] = useState<UploadStep>("settings");
+ const [drawingUsage, setDrawingUsage] = useState<string>("REC");
+ const [registerKind, setRegisterKind] = useState<string>("");
+ const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
+ const [isUploading, setIsUploading] = useState(false);
+ const [validationResults, setValidationResults] = useState<FileValidationResult[]>([]);
+ const [showValidationDialog, setShowValidationDialog] = useState(false);
+ const [isDragging, setIsDragging] = useState(false);
+ const [uploadProgress, setUploadProgress] = useState(0);
+ const [uploadResult, setUploadResult] = useState<B4BulkUploadResult | null>(null);
+
+ // B4 GTT 옵션
+ const drawingUsageOptions = [
+ { value: "REC", label: t("bulkUpload.drawingUsageReceive") },
+ ];
+
+ const registerKindOptionsMap: Record<string, Array<{ value: string; label: string }>> = {
+ REC: [
+ { value: "RECP", label: t("bulkUpload.registerKindRecP") },
+ { value: "RECW", label: t("bulkUpload.registerKindRecW") },
+ ],
+ };
+
+ // 다이얼로그 닫을 때 초기화
+ React.useEffect(() => {
+ if (!open) {
+ setCurrentStep("settings");
+ setDrawingUsage("REC");
+ setRegisterKind("");
+ setSelectedFiles([]);
+ setValidationResults([]);
+ setShowValidationDialog(false);
+ setIsDragging(false);
+ setUploadProgress(0);
+ setUploadResult(null);
+ }
+ }, [open]);
+
+ // 파일 선택 핸들러
+ 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 핸들러
+ const handleDragEnter = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ setIsDragging(true);
+ };
+
+ const handleDragLeave = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ if (e.currentTarget === e.target) {
+ 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));
+ };
+
+ // 1단계 완료 (설정)
+ const handleSettingsNext = () => {
+ if (!registerKind) {
+ toast.error(t("bulkUpload.selectRegisterKindError"));
+ return;
+ }
+ setCurrentStep("files");
+ };
+
+ // 2단계 완료 (파일 선택)
+ const handleFilesNext = () => {
+ if (selectedFiles.length === 0) {
+ toast.error(t("bulkUpload.selectFilesError"));
+ return;
+ }
+ setCurrentStep("validation");
+ handleValidate();
+ };
+
+ // 검증 시작 (V2: fetchDwgReceiptList 사용)
+ const handleValidate = async () => {
+ try {
+ console.log("[V2 Dialog] 검증 시작");
+
+ // 1단계: 파일명 파싱
+ 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단계: DrawingNo별로 도면 정보 조회
+ const drawingNoSet = new Set(parsedFiles.map((r) => r.parsed!.drawingNo));
+ const drawingInfoMap = new Map<string, GttDwgReceiptItem>();
+
+ console.log(`[V2 Dialog] ${drawingNoSet.size}개 도면번호 조회`);
+
+ for (const drawingNo of drawingNoSet) {
+ try {
+ const dwgList = await fetchDwgReceiptList({
+ project: projectNo,
+ drawingKind: "B4",
+ drawingMoveGbn: "도면입수",
+ drawingNo: drawingNo,
+ });
+
+ // 해당 DrawingNo 찾기
+ const dwgInfo = dwgList.find(
+ (d) => (d as GttDwgReceiptItem).DrawingNo === drawingNo
+ ) as GttDwgReceiptItem | undefined;
+
+ if (dwgInfo) {
+ drawingInfoMap.set(drawingNo, dwgInfo);
+ console.log(`[V2 Dialog] 도면 정보 조회 완료: ${drawingNo}`);
+ } else {
+ console.log(`[V2 Dialog] 도면 정보 없음: ${drawingNo}`);
+ }
+ } catch (error) {
+ console.error(`[V2 Dialog] 도면 정보 조회 실패: ${drawingNo}`, error);
+ }
+ }
+
+ // 3단계: 검증 결과 병합
+ const finalResults: FileValidationResult[] = parseResults.map((parseResult) => {
+ if (!parseResult.valid || !parseResult.parsed) {
+ return parseResult;
+ }
+
+ const drawingInfo = drawingInfoMap.get(parseResult.parsed.drawingNo);
+
+ if (!drawingInfo) {
+ return {
+ ...parseResult,
+ mappingStatus: "not_found" as const,
+ error: t("validation.notRegistered"),
+ };
+ }
+
+ // DrawingMoveGbn이 "도면입수"가 아니면 업로드 불가
+ if (drawingInfo.DrawingMoveGbn !== "도면입수") {
+ return {
+ ...parseResult,
+ mappingStatus: "not_found" as const,
+ error: t("validation.notGttDeliverables"),
+ };
+ }
+
+ // 업로드 가능
+ return {
+ ...parseResult,
+ mappingStatus: "available" as const,
+ drawingName: drawingInfo.DrawingName || undefined,
+ registerGroupId: drawingInfo.RegisterGroupId,
+ };
+ });
+
+ console.log("[V2 Dialog] 검증 완료");
+ setValidationResults(finalResults);
+ setShowValidationDialog(true);
+ } catch (error) {
+ console.error("[V2 Dialog] 검증 실패:", error);
+ toast.error(
+ error instanceof Error ? error.message : t("bulkUpload.validationError")
+ );
+ }
+ };
+
+ // 업로드 확인 (V2: bulkUploadB4FilesV2 사용)
+ const handleConfirmUpload = async (validFiles: FileValidationResult[]) => {
+ setIsUploading(true);
+ setCurrentStep("uploading");
+ setShowValidationDialog(false);
+
+ try {
+ console.log(`[V2 Dialog] 업로드 시작: ${validFiles.length}개 파일`);
+
+ // 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", registerKind);
+ formData.append("fileCount", String(validFiles.length));
+
+ validFiles.forEach((fileResult, index) => {
+ formData.append(`file_${index}`, fileResult.file);
+ });
+
+ // 업로드 프로그레스 시뮬레이션
+ const progressInterval = setInterval(() => {
+ setUploadProgress((prev) => {
+ if (prev >= 90) return 90;
+ return prev + 10;
+ });
+ }, 500);
+
+ // V2 함수 호출
+ const result = await bulkUploadB4FilesV2(formData);
+
+ clearInterval(progressInterval);
+ setUploadProgress(100);
+
+ console.log("[V2 Dialog] 업로드 완료:", result);
+
+ setUploadResult(result);
+ setCurrentStep("complete");
+
+ if (result.success) {
+ toast.success(
+ t("bulkUpload.uploadSuccessToast", {
+ successCount: result.successCount,
+ total: validFiles.length,
+ })
+ );
+ } else {
+ toast.error(result.error || t("bulkUpload.uploadError"));
+ }
+ } catch (error) {
+ console.error("[V2 Dialog] 업로드 실패:", error);
+ toast.error(
+ error instanceof Error ? error.message : t("bulkUpload.uploadError")
+ );
+ setCurrentStep("files");
+ } finally {
+ setIsUploading(false);
+ }
+ };
+
+ const registerKindOptions = drawingUsage
+ ? registerKindOptionsMap[drawingUsage] || []
+ : [];
+
+ const handleDrawingUsageChange = (value: string) => {
+ setDrawingUsage(value);
+ setRegisterKind("");
+ };
+
+ return (
+ <>
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="max-w-2xl">
+ <DialogHeader>
+ <DialogTitle>{t("bulkUpload.title")} (V2)</DialogTitle>
+ <DialogDescription>
+ {currentStep === "settings" && t("bulkUpload.stepSettings")}
+ {currentStep === "files" && t("bulkUpload.stepFiles")}
+ {currentStep === "validation" && t("bulkUpload.stepValidation")}
+ </DialogDescription>
+ </DialogHeader>
+
+ <div className="space-y-4">
+ {/* 1단계: 설정 입력 */}
+ {currentStep === "settings" && (
+ <>
+ {/* 도면용도 선택 */}
+ <div className="space-y-2">
+ <Label>{t("bulkUpload.drawingUsage")} *</Label>
+ <Select value={drawingUsage} onValueChange={handleDrawingUsageChange}>
+ <SelectTrigger>
+ <SelectValue placeholder={t("bulkUpload.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("bulkUpload.registerKind")} *</Label>
+ <Select
+ value={registerKind}
+ onValueChange={setRegisterKind}
+ disabled={!drawingUsage}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder={t("bulkUpload.registerKindPlaceholder")} />
+ </SelectTrigger>
+ <SelectContent>
+ {registerKindOptions.map((option) => (
+ <SelectItem key={option.value} value={option.value}>
+ {option.label}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ <p className="text-sm text-muted-foreground">
+ {t("bulkUpload.registerKindNote")}
+ </p>
+ </div>
+ </>
+ )}
+
+ {/* 2단계: 파일 선택 */}
+ {currentStep === "files" && (
+ <>
+ {/* 파일 선택 영역 */}
+ <div
+ className={`border-2 border-dashed rounded-lg p-8 transition-all duration-200 ${
+ isDragging
+ ? "border-primary bg-primary/5 scale-[1.02]"
+ : "border-muted-foreground/30 hover:border-muted-foreground/50"
+ }`}
+ onDragEnter={handleDragEnter}
+ onDragLeave={handleDragLeave}
+ onDragOver={handleDragOver}
+ onDrop={handleDrop}
+ >
+ <input
+ type="file"
+ multiple
+ accept=".pdf,.doc,.docx,.xls,.xlsx,.dwg,.dxf,.zip"
+ onChange={(e) => handleFilesChange(Array.from(e.target.files || []))}
+ className="hidden"
+ id="b4-file-upload-v2"
+ />
+ <label
+ htmlFor="b4-file-upload-v2"
+ className="flex flex-col items-center justify-center cursor-pointer"
+ >
+ <FolderOpen
+ className={`h-12 w-12 mb-3 transition-colors ${
+ isDragging ? "text-primary" : "text-muted-foreground"
+ }`}
+ />
+ <p
+ className={`text-sm transition-colors ${
+ isDragging
+ ? "text-primary font-medium"
+ : "text-muted-foreground"
+ }`}
+ >
+ {isDragging
+ ? t("bulkUpload.fileDropHere")
+ : t("bulkUpload.fileSelectArea")}
+ </p>
+ <p className="text-xs text-muted-foreground mt-1">
+ {t("bulkUpload.fileTypes")}
+ </p>
+ </label>
+ </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("bulkUpload.selectedFiles", { count: selectedFiles.length })}
+ </h4>
+ <Button
+ variant="ghost"
+ size="sm"
+ onClick={() => setSelectedFiles([])}
+ >
+ {t("bulkUpload.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-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={() => handleRemoveFile(index)}
+ >
+ {t("bulkUpload.removeFile")}
+ </Button>
+ </div>
+ ))}
+ </div>
+ </div>
+ )}
+ </>
+ )}
+
+ {/* 3단계: 검증 중 표시 */}
+ {currentStep === "validation" && (
+ <div className="flex flex-col items-center justify-center py-12">
+ <Loader2 className="h-12 w-12 animate-spin text-primary mb-4" />
+ <p className="text-sm text-muted-foreground">
+ {t("bulkUpload.validating")}
+ </p>
+ </div>
+ )}
+
+ {/* 4단계: 업로드 진행 중 */}
+ {currentStep === "uploading" && (
+ <div className="space-y-6 py-8">
+ <div className="flex flex-col items-center">
+ <Loader2 className="h-12 w-12 animate-spin text-primary mb-4" />
+ <h3 className="text-lg font-semibold mb-2">{t("bulkUpload.uploading")}</h3>
+ <p className="text-sm text-muted-foreground">
+ {t("bulkUpload.uploadingWait")}
+ </p>
+ </div>
+ <div className="space-y-2">
+ <div className="flex justify-between text-sm">
+ <span>{t("bulkUpload.uploadProgress")}</span>
+ <span>{uploadProgress}%</span>
+ </div>
+ <Progress value={uploadProgress} className="h-2" />
+ {uploadProgress >= 90 && uploadProgress < 100 && (
+ <p className="text-xs text-muted-foreground text-center pt-2">
+ {t("bulkUpload.uploadingToServer")}
+ </p>
+ )}
+ </div>
+ </div>
+ )}
+
+ {/* 5단계: 업로드 완료 */}
+ {currentStep === "complete" && uploadResult && (
+ <div className="space-y-6 py-8">
+ <div className="flex flex-col items-center">
+ <CheckCircle2 className="h-16 w-16 text-green-500 mb-4" />
+ <h3 className="text-lg font-semibold mb-2">{t("bulkUpload.uploadComplete")}</h3>
+ <p className="text-sm text-muted-foreground">
+ {t("bulkUpload.uploadSuccessMessage", { count: uploadResult.successCount })}
+ </p>
+ </div>
+
+ {uploadResult.failCount && uploadResult.failCount > 0 && (
+ <div className="bg-yellow-50 dark:bg-yellow-950/30 border border-yellow-200 dark:border-yellow-800 rounded-lg p-4">
+ <p className="text-sm text-yellow-800 dark:text-yellow-200">
+ {t("bulkUpload.uploadFailMessage", { count: uploadResult.failCount })}
+ </p>
+ </div>
+ )}
+
+ <div className="flex justify-center">
+ <Button
+ onClick={() => {
+ onOpenChange(false);
+ onUploadComplete?.();
+ }}
+ >
+ {t("bulkUpload.confirmButton")}
+ </Button>
+ </div>
+ </div>
+ )}
+ </div>
+
+ {/* 푸터 버튼 */}
+ {currentStep !== "uploading" && currentStep !== "complete" && currentStep !== "validation" && (
+ <DialogFooter>
+ {currentStep === "settings" && (
+ <>
+ <Button
+ variant="outline"
+ onClick={() => onOpenChange(false)}
+ >
+ {t("bulkUpload.cancelButton")}
+ </Button>
+ <Button
+ onClick={handleSettingsNext}
+ disabled={!registerKind}
+ >
+ {t("bulkUpload.nextButton")}
+ <ChevronRight className="ml-2 h-4 w-4" />
+ </Button>
+ </>
+ )}
+
+ {currentStep === "files" && (
+ <>
+ <Button
+ variant="outline"
+ onClick={() => setCurrentStep("settings")}
+ >
+ <ChevronLeft className="mr-2 h-4 w-4" />
+ {t("bulkUpload.previousButton")}
+ </Button>
+ <Button
+ onClick={handleFilesNext}
+ disabled={selectedFiles.length === 0}
+ >
+ {t("bulkUpload.validateButton")}
+ <ChevronRight className="ml-2 h-4 w-4" />
+ </Button>
+ </>
+ )}
+ </DialogFooter>
+ )}
+ </DialogContent>
+ </Dialog>
+
+ {/* 검증 다이얼로그 */}
+ <B4UploadValidationDialog
+ open={showValidationDialog}
+ onOpenChange={(open) => {
+ setShowValidationDialog(open);
+ if (!open) {
+ onOpenChange(false);
+ }
+ }}
+ validationResults={validationResults}
+ onConfirmUpload={handleConfirmUpload}
+ isUploading={isUploading}
+ />
+ </>
+ );
+}
+
diff --git a/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx b/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
index 1be7f226..21647e63 100644
--- a/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
+++ b/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
@@ -30,9 +30,10 @@ import {
} from "./b4-upload-validation-dialog";
import {
checkB4MappingStatus,
- editDetailDwgReceipt,
+ saveB4MappingBatch,
type MappingCheckItem,
type B4BulkUploadResult,
+ type B4MappingSaveItem,
} from "../actions";
import { v4 as uuidv4 } from "uuid";
@@ -343,37 +344,54 @@ export function B4BulkUploadDialog({
console.log(`[B4 업로드] 그룹 ${groupKey} 파일 업로드 완료`);
- // 3. 상세도면 등록
- await editDetailDwgReceipt({
- dwgList: [
- {
- Mode: "ADD",
- Status: "Draft",
- RegisterId: 0,
- ProjectNo: projectNo,
- Discipline: "",
- DrawingKind: "B4",
- DrawingNo: drawingNo,
- DrawingName: "",
- RegisterGroupId: registerGroupId,
- RegisterSerialNo: 0,
- RegisterKind: registerKind,
- DrawingRevNo: revNo,
- Category: "TS",
- Receiver: null,
- Manager: "",
- RegisterDesc: "",
- UploadId: uploadId,
- RegCompanyCode: vendorCode,
- },
- ],
- userId,
- userNm: userName,
- vendorCode,
- email: userEmail,
- });
+ // 3. 매핑 현황 재조회 (MatchBatchFileDwg)
+ const mappingCheckResults = await checkB4MappingStatus(projectNo, [
+ {
+ DrawingNo: drawingNo,
+ RevNo: revNo,
+ FileNm: files[0].fileName,
+ },
+ ]);
+
+ const mappingData = mappingCheckResults[0];
+ if (!mappingData || mappingData.RegisterGroupId === 0) {
+ throw new Error(`매핑 정보를 찾을 수 없습니다: ${groupKey}`);
+ }
+
+ console.log(`[B4 업로드] 그룹 ${groupKey} 매핑 정보 조회 완료`);
+
+ // 4. 매핑 정보 저장 (MatchBatchFileDwgEdit)
+ const mappingSaveItem: B4MappingSaveItem = {
+ CGbn: mappingData.CGbn,
+ Category: mappingData.Category,
+ CheckBox: "0",
+ DGbn: mappingData.DGbn,
+ DegreeGbn: mappingData.DegreeGbn,
+ DeptGbn: mappingData.DeptGbn,
+ Discipline: mappingData.Discipline,
+ DrawingKind: "B4",
+ DrawingMoveGbn: "도면입수",
+ DrawingName: mappingData.DrawingName,
+ DrawingNo: drawingNo,
+ DrawingUsage: "입수용",
+ FileNm: files[0].fileName,
+ JGbn: mappingData.JGbn,
+ Manager: mappingData.Manager || "970043",
+ MappingYN: "Y",
+ NewOrNot: "N",
+ ProjectNo: projectNo,
+ RegisterGroup: 0,
+ RegisterGroupId: registerGroupId,
+ RegisterKindCode: registerKind,
+ RegisterSerialNo: mappingData.RegisterSerialNo,
+ RevNo: revNo,
+ SGbn: mappingData.SGbn,
+ UploadId: uploadId,
+ };
+
+ await saveB4MappingBatch([mappingSaveItem], userId);
- console.log(`[B4 업로드] 그룹 ${groupKey} 상세도면 등록 완료`);
+ console.log(`[B4 업로드] 그룹 ${groupKey} 매핑 정보 저장 완료`);
successCount += files.length;
} catch (error) {
@@ -685,7 +703,12 @@ export function B4BulkUploadDialog({
{/* 검증 다이얼로그 */}
<B4UploadValidationDialog
open={showValidationDialog}
- onOpenChange={setShowValidationDialog}
+ onOpenChange={(open) => {
+ setShowValidationDialog(open);
+ if (!open) {
+ onOpenChange(false); // 검증 다이얼로그가 닫히면 메인 다이얼로그도 닫기
+ }
+ }}
validationResults={validationResults}
onConfirmUpload={handleConfirmUpload}
isUploading={isUploading}