/** * 특정 컬럼들 복합키로 묶어 UPDATE 처리해야 함. */ "use client" import React, { useRef } from 'react' import ExcelJS from 'exceljs' import { toast } from 'sonner' import { Button } from '@/components/ui/button' import { Upload, Loader } from 'lucide-react' import { processBulkImport } from '../service' import { Input } from '@/components/ui/input' import { useSession } from "next-auth/react" import { getCellValueAsString, getAccessorKeyByHeader, } from '../excel-utils' import { decryptWithServerAction } from '@/components/drm/drmUtils' import { debugLog, debugError, debugWarn, debugSuccess, debugProcess } from '@/lib/debug-utils' import { ImportResult } from './import-result-dialog' interface ImportExcelProps { onImportComplete: (result: ImportResult) => void } export function ImportVendorPoolButton({ onImportComplete }: ImportExcelProps) { const fileInputRef = useRef(null) const [isImporting, setIsImporting] = React.useState(false) const { data: session } = useSession() const handleImport = async (event: React.ChangeEvent) => { const file = event.target.files?.[0] if (!file) { debugWarn('[Import] 파일이 선택되지 않았습니다.') return } debugLog('[Import] 파일 임포트 시작:', { fileName: file.name, fileSize: file.size, fileType: file.type }) setIsImporting(true) try { // DRM 복호화 처리 debugProcess('[Import] DRM 복호화 시작') toast.info("파일을 복호화하고 있습니다...") let decryptedData: ArrayBuffer try { decryptedData = await decryptWithServerAction(file) debugSuccess('[Import] DRM 복호화 성공') toast.success("파일 복호화가 완료되었습니다.") } catch (drmError) { debugWarn('[Import] DRM 복호화 실패, 원본 파일로 진행:', drmError) console.warn("DRM 복호화 실패, 원본 파일로 진행합니다:", drmError) toast.warning("DRM 복호화에 실패했습니다. 원본 파일로 진행합니다.") decryptedData = await file.arrayBuffer() debugLog('[Import] 원본 파일 ArrayBuffer 로드 완료, size:', decryptedData.byteLength) } // 복호화된 데이터로 ExcelJS 워크북 로드 debugProcess('[Import] ExcelJS 워크북 로드 시작') toast.info("엑셀 파일을 분석하고 있습니다...") const workbook = new ExcelJS.Workbook() await workbook.xlsx.load(decryptedData) debugSuccess('[Import] ExcelJS 워크북 로드 완료') // Get the first worksheet const worksheet = workbook.getWorksheet(1) if (!worksheet) { debugError('[Import] 워크시트를 찾을 수 없습니다.') toast.error("No worksheet found in the spreadsheet") setIsImporting(false) return } debugLog('[Import] 워크시트 확인 완료:', { name: worksheet.name, rowCount: worksheet.rowCount, columnCount: worksheet.columnCount }) // Check if there's an instruction row (템플릿 안내 텍스트가 있는지 확인) const firstRowText = getCellValueAsString(worksheet.getRow(1).getCell(1)); const hasInstructionRow = firstRowText.includes('벤더풀 데이터 입력 템플릿') || firstRowText.includes('입력 가이드 시트') || firstRowText.includes('입력 가이드') || (worksheet.getRow(1).getCell(1).value !== null && worksheet.getRow(1).getCell(2).value === null); debugLog('[Import] 첫 번째 행 확인:', { firstRowText, hasInstructionRow }) // Get header row index (row 2 if there's an instruction row, otherwise row 1) const headerRowIndex = hasInstructionRow ? 2 : 1; debugLog('[Import] 헤더 행 인덱스:', headerRowIndex) // Get column headers and their indices debugProcess('[Import] 컬럼 헤더 매핑 시작') const headerRow = worksheet.getRow(headerRowIndex); const columnIndices: Record = {}; headerRow.eachCell((cell, colNumber) => { const header = getCellValueAsString(cell); // Excel 헤더를 통해 accessorKey 찾기 const accessorKey = getAccessorKeyByHeader(header); if (accessorKey) { columnIndices[accessorKey] = colNumber; } }); debugLog('[Import] 컬럼 매핑 완료:', { mappedColumns: Object.keys(columnIndices).length, columnIndices }) // Process data rows debugProcess('[Import] 데이터 행 처리 시작') const rows: Record[] = []; const startRow = headerRowIndex + 1; let skippedRows = 0; for (let i = startRow; i <= worksheet.rowCount; i++) { const row = worksheet.getRow(i); // Skip empty rows if (row.cellCount === 0) { skippedRows++; continue; } // Check if this is likely an empty template row (빈 템플릿 행 건너뛰기) let hasAnyData = false; for (let col = 1; col <= row.cellCount; col++) { if (getCellValueAsString(row.getCell(col)).trim()) { hasAnyData = true; break; } } if (!hasAnyData) { skippedRows++; continue; } const rowData: Record = {}; let hasData = false; // Map the data using accessorKey indices Object.entries(columnIndices).forEach(([accessorKey, colIndex]) => { const value = getCellValueAsString(row.getCell(colIndex)); if (value) { rowData[accessorKey] = value; hasData = true; } }); if (hasData) { rows.push(rowData); } else { skippedRows++; } } debugLog('[Import] 데이터 행 처리 완료:', { totalRows: worksheet.rowCount - headerRowIndex, validRows: rows.length, skippedRows }) if (rows.length === 0) { debugWarn('[Import] 유효한 데이터가 없습니다.') toast.error("No data found in the spreadsheet") setIsImporting(false) return } // 서버로 Bulk Import 요청 debugProcess('[Import] 서버 Bulk Import 요청 시작') toast.info(`${rows.length}개의 데이터를 처리하고 있습니다...`) const registrant = session?.user?.name || 'system'; const result = await processBulkImport(rows, registrant); debugSuccess('[Import] 서버 처리 완료', { success: result.successCount, error: result.errorCount }) // 결과 처리 및 콜백 호출 onImportComplete(result) } catch (error) { debugError('[Import] 전체 임포트 프로세스 실패:', error); console.error("Import error:", error); toast.error("Error importing data. Please check file format."); } finally { debugLog('[Import] 임포트 프로세스 종료'); setIsImporting(false); // Reset the file input if (fileInputRef.current) { fileInputRef.current.value = ''; } } } return ( <> ) }