summaryrefslogtreecommitdiff
path: root/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dolce/dialogs/b4-bulk-upload-dialog.tsx')
-rw-r--r--lib/dolce/dialogs/b4-bulk-upload-dialog.tsx108
1 files changed, 81 insertions, 27 deletions
diff --git a/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx b/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
index 21647e63..b7b25fca 100644
--- a/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
+++ b/lib/dolce/dialogs/b4-bulk-upload-dialog.tsx
@@ -36,6 +36,9 @@ import {
type B4MappingSaveItem,
} from "../actions";
import { v4 as uuidv4 } from "uuid";
+import { uploadFilesWithProgress } from "../utils/upload-with-progress";
+import { FileUploadProgressList } from "../components/file-upload-progress-list";
+import type { FileUploadProgress } from "../hooks/use-file-upload-with-progress";
interface B4BulkUploadDialogProps {
open: boolean;
@@ -73,6 +76,7 @@ export function B4BulkUploadDialog({
const [isDragging, setIsDragging] = useState(false);
const [uploadProgress, setUploadProgress] = useState(0);
const [uploadResult, setUploadResult] = useState<B4BulkUploadResult | null>(null);
+ const [fileProgresses, setFileProgresses] = useState<FileUploadProgress[]>([]);
// B4 GTT 옵션 (코드 번역 유틸리티 사용)
const drawingUsageOptions = [
@@ -98,6 +102,7 @@ export function B4BulkUploadDialog({
setIsDragging(false);
setUploadProgress(0);
setUploadResult(null);
+ setFileProgresses([]);
}
}, [open]);
@@ -275,6 +280,14 @@ export function B4BulkUploadDialog({
try {
console.log(`[B4 일괄 업로드] 시작: ${validFiles.length}개 파일`);
+ // 0단계: 모든 파일에 대한 진행도 상태 초기화
+ const initialProgresses: FileUploadProgress[] = validFiles.map((fileResult) => ({
+ file: fileResult.file,
+ progress: 0,
+ status: "pending" as const,
+ }));
+ setFileProgresses(initialProgresses);
+
// 파일을 DrawingNo + RevNo로 그룹화
const uploadGroups = new Map<
string,
@@ -284,10 +297,11 @@ export function B4BulkUploadDialog({
revNo: string;
fileName: string;
registerGroupId: number;
+ fileIndex: number; // 전체 배열에서의 인덱스
}>
>();
- validFiles.forEach((fileResult) => {
+ validFiles.forEach((fileResult, index) => {
const groupKey = `${fileResult.parsed!.drawingNo}_${fileResult.parsed!.revNo}`;
if (!uploadGroups.has(groupKey)) {
uploadGroups.set(groupKey, []);
@@ -298,6 +312,7 @@ export function B4BulkUploadDialog({
revNo: fileResult.parsed!.revNo,
fileName: fileResult.file.name,
registerGroupId: fileResult.registerGroupId || 0,
+ fileIndex: index,
});
});
@@ -317,27 +332,63 @@ export function B4BulkUploadDialog({
// 1. UploadId 생성
const uploadId = uuidv4();
- // 2. 파일 업로드 (공통 API 사용)
- const formData = new FormData();
- formData.append("uploadId", uploadId);
- formData.append("userId", userId);
- formData.append("fileCount", String(files.length));
-
- files.forEach((fileInfo, index) => {
- formData.append(`file_${index}`, fileInfo.file);
- });
-
- const uploadResponse = await fetch("/api/dolce/upload-files", {
- method: "POST",
- body: formData,
+ // 그룹 내 모든 파일 상태를 uploading으로 변경
+ setFileProgresses((prev) =>
+ prev.map((fp, index) =>
+ files.some((f) => f.fileIndex === index)
+ ? { ...fp, status: "uploading" as const }
+ : fp
+ )
+ );
+
+ // 2. 파일 업로드 (uploadFilesWithProgress 사용)
+ const uploadResult = await uploadFilesWithProgress({
+ uploadId,
+ userId,
+ files: files.map((f) => f.file),
+ callbacks: {
+ onProgress: (fileIndexInGroup, progress) => {
+ // 그룹 내 파일 인덱스를 전체 인덱스로 변환
+ const globalFileIndex = files[fileIndexInGroup].fileIndex;
+
+ // 개별 파일 진행도 업데이트
+ setFileProgresses((prev) =>
+ prev.map((fp, index) =>
+ index === globalFileIndex
+ ? { ...fp, progress, status: "uploading" as const }
+ : fp
+ )
+ );
+
+ // 전체 진행도 계산
+ const groupProgress = (completedGroups / uploadGroups.size) * 100;
+ const currentGroupProgress = (progress / 100) * (100 / uploadGroups.size);
+ setUploadProgress(Math.round(groupProgress + currentGroupProgress));
+ },
+ onFileComplete: (fileIndexInGroup) => {
+ const globalFileIndex = files[fileIndexInGroup].fileIndex;
+ setFileProgresses((prev) =>
+ prev.map((fp, index) =>
+ index === globalFileIndex
+ ? { ...fp, progress: 100, status: "completed" as const }
+ : fp
+ )
+ );
+ },
+ onFileError: (fileIndexInGroup, error) => {
+ const globalFileIndex = files[fileIndexInGroup].fileIndex;
+ console.error(`[B4 업로드] 파일 ${globalFileIndex} 업로드 실패:`, error);
+ setFileProgresses((prev) =>
+ prev.map((fp, index) =>
+ index === globalFileIndex
+ ? { ...fp, status: "error" as const, error }
+ : fp
+ )
+ );
+ },
+ },
});
- if (!uploadResponse.ok) {
- throw new Error(`파일 업로드 실패: ${uploadResponse.status}`);
- }
-
- const uploadResult = await uploadResponse.json();
-
if (!uploadResult.success) {
throw new Error(uploadResult.error || "파일 업로드 실패");
}
@@ -599,7 +650,7 @@ export function B4BulkUploadDialog({
{/* 4단계: 업로드 진행 중 */}
{currentStep === "uploading" && (
- <div className="space-y-6 py-8">
+ <div className="space-y-6 py-4">
<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>
@@ -607,19 +658,22 @@ export function B4BulkUploadDialog({
{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" />
- {/* 90% 이상일 때 추가 안내 메시지 */}
- {uploadProgress >= 90 && uploadProgress < 100 && (
- <p className="text-xs text-muted-foreground text-center pt-2">
- {t("bulkUpload.uploadingToServer")}
- </p>
- )}
</div>
+
+ {/* 개별 파일 진행도 리스트 */}
+ {fileProgresses.length > 0 && (
+ <div className="border rounded-lg p-4 max-h-96 overflow-y-auto">
+ <FileUploadProgressList fileProgresses={fileProgresses} />
+ </div>
+ )}
</div>
)}