From defda07c0bb4b0bd444ca8dc4fd3f89322bda0ce Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 3 Oct 2025 04:48:47 +0000
Subject: (대표님) edp, tbe, dolce 등
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data/import-excel-form.tsx | 312 ++++++++++++++---------------
 1 file changed, 155 insertions(+), 157 deletions(-)

(limited to 'components/form-data/import-excel-form.tsx')

diff --git a/components/form-data/import-excel-form.tsx b/components/form-data/import-excel-form.tsx
index df6ab6c1..0e757891 100644
--- a/components/form-data/import-excel-form.tsx
+++ b/components/form-data/import-excel-form.tsx
@@ -56,22 +56,22 @@ interface GenericData {
  * Check if a field is editable for a specific TAG_NO
  */
 function isFieldEditable(
-  column: DataTableColumnJSON, 
-  tagNo: string, 
+  column: DataTableColumnJSON,
+  tagNo: string,
   editableFieldsMap: Map<string, string[]>
 ): boolean {
   // SHI-only fields (shi === "OUT" or shi === null) are never editable
   if (column.shi === "OUT" || column.shi === null) return false;
-  
+
   // System fields are never editable
   if (column.key === "TAG_NO" || column.key === "TAG_DESC" || column.key === "status") return false;
-  
+
   // If no editableFieldsMap provided, assume all non-SHI fields are editable
   if (!editableFieldsMap || editableFieldsMap.size === 0) return true;
-  
+
   // If TAG_NO not in map, no fields are editable
   if (!editableFieldsMap.has(tagNo)) return false;
-  
+
   // Check if this field is in the editable fields list for this TAG_NO
   const editableFields = editableFieldsMap.get(tagNo) || [];
   return editableFields.includes(column.key);
@@ -86,7 +86,7 @@ function createImportErrorSheet(workbook: ExcelJS.Workbook, errors: ImportError[
   if (existingErrorSheet) {
     workbook.removeWorksheet("Import_Errors");
   }
-  
+
   const errorSheet = workbook.addWorksheet("Import_Errors");
 
   // Add header error section if exists
@@ -114,7 +114,7 @@ function createImportErrorSheet(workbook: ExcelJS.Workbook, errors: ImportError[
 
   // Data validation errors section
   const startRow = errorSheet.rowCount + 1;
-  
+
   // Summary row
   errorSheet.addRow([`DATA VALIDATION ERRORS: ${errors.length} errors found`]);
   const summaryRow = errorSheet.getRow(startRow);
@@ -143,7 +143,7 @@ function createImportErrorSheet(workbook: ExcelJS.Workbook, errors: ImportError[
     const headerRow = errorSheet.getRow(errorSheet.rowCount);
     headerRow.font = { bold: true, color: { argb: "FFFFFFFF" } };
     headerRow.alignment = { horizontal: "center" };
-    
+
     headerRow.eachCell((cell) => {
       cell.fill = {
         type: "pattern",
@@ -167,7 +167,7 @@ function createImportErrorSheet(workbook: ExcelJS.Workbook, errors: ImportError[
       // Color code by error type
       errorRow.eachCell((cell) => {
         let bgColor = "FFFFFFFF"; // Default white
-        
+
         switch (error.errorType) {
           case "MISSING_TAG_NO":
             bgColor = "FFFFCCCC"; // Light red
@@ -285,8 +285,8 @@ export async function importExcelData({
       saveAs(new Blob([outBuffer]), `import-error-report_${Date.now()}.xlsx`);
 
       toast.error(`Header validation failed. ${headerErrors.length} errors found. Check downloaded error report.`);
-      return { 
-        success: false, 
+      return {
+        success: false,
         error: "Header validation errors",
         errorCount: headerErrors.length,
         hasErrors: true
@@ -312,30 +312,28 @@ export async function importExcelData({
     for (let rowNum = 2; rowNum <= lastRowNumber; rowNum++) {
       const row = worksheet.getRow(rowNum);
       const rowValues = row.values as ExcelJS.CellValue[];
-      if (!rowValues || rowValues.length <= 1) continue; // Skip empty rows
+      // 실제 값이 있는지 확인 (빈 문자열이 아닌 실제 내용)
+      const hasAnyValue = rowValues && rowValues.slice(1).some(val =>
+        val !== undefined &&
+        val !== null &&
+        String(val).trim() !== ""
+      );
+
+      if (!hasAnyValue) {
+        console.log(`Row ${rowNum} is empty, skipping...`);
+        continue; // 완전히 빈 행은 건너뛰기
+      }
 
       const rowObj: Record<string, any> = {};
       const skippedFields: string[] = []; // 현재 행에서 건너뛴 필드들
       let hasErrors = false;
-      
+
       // Get the TAG_NO first to identify existing data
       const tagNoColIndex = keyToIndexMap.get("TAG_NO");
       const tagNo = tagNoColIndex ? String(rowValues[tagNoColIndex] ?? "").trim() : "";
       const existingRowData = existingDataMap.get(tagNo);
 
-      // Validate TAG_NO first
-      if (!tagNo) {
-        validationErrors.push({
-          tagNo: `Row-${rowNum}`,
-          rowIndex: rowNum,
-          columnKey: "TAG_NO",
-          columnLabel: "TAG NO",
-          errorType: "MISSING_TAG_NO",
-          errorMessage: "TAG_NO is empty or missing",
-          currentValue: tagNo,
-        });
-        hasErrors = true;
-      } else if (!existingTagNumbers.has(tagNo)) {
+      if (!existingTagNumbers.has(tagNo)) {
         validationErrors.push({
           tagNo: tagNo,
           rowIndex: rowNum,
@@ -355,7 +353,7 @@ export async function importExcelData({
 
         // Check if this field is editable for this TAG_NO
         const fieldEditable = isFieldEditable(col, tagNo, editableFieldsMap);
-        
+
         if (!fieldEditable) {
           // If field is not editable, preserve existing value
           if (existingRowData && existingRowData[col.key] !== undefined) {
@@ -373,7 +371,7 @@ export async function importExcelData({
                 break;
             }
           }
-          
+
           // Determine skip reason
           let skipReason = "";
           if (col.shi === "OUT" || col.shi === null) {
@@ -383,10 +381,10 @@ export async function importExcelData({
           } else {
             skipReason = "Not editable for this TAG";
           }
-          
+
           // Log skipped field
           skippedFields.push(`${col.label} (${skipReason})`);
-          
+
           // Check if Excel contains a value for a read-only field and warn
           const cellValue = rowValues[colIndex] ?? "";
           const stringVal = String(cellValue).trim();
@@ -403,7 +401,7 @@ export async function importExcelData({
             });
             hasErrors = true;
           }
-          
+
           return; // Skip processing Excel value for this column
         }
 
@@ -492,13 +490,13 @@ export async function importExcelData({
 
       const outBuffer = await workbook.xlsx.writeBuffer();
       saveAs(new Blob([outBuffer]), `import-error-report_${Date.now()}.xlsx`);
-      
+
       toast.error(
         `Data validation failed. ${validationErrors.length} errors found across ${new Set(validationErrors.map(e => e.tagNo)).size} TAG(s). Check downloaded error report.`
       );
-      
-      return { 
-        success: false, 
+
+      return {
+        success: false,
         error: "Data validation errors",
         errorCount: validationErrors.length,
         hasErrors: true,
@@ -510,14 +508,14 @@ export async function importExcelData({
     // Create locally merged data for UI update
     const mergedData = [...tableData];
     const dataMap = new Map<string, GenericData>();
-    
+
     // Map existing data by TAG_NO
     mergedData.forEach(item => {
       if (item.TAG_NO) {
         dataMap.set(item.TAG_NO, item);
       }
     });
-    
+
     // Update with imported data
     importedData.forEach(item => {
       if (item.TAG_NO) {
@@ -530,137 +528,137 @@ export async function importExcelData({
     });
 
     // If formCode and contractItemId are provided, save directly to DB
-  // importExcelData 함수에서 DB 저장 부분
-if (formCode && contractItemId) {
-  try {
-    // 배치 업데이트 함수 호출
-    const result = await updateFormDataBatchInDB(
-      formCode,
-      contractItemId,
-      importedData // 모든 imported rows를 한번에 전달
-    );
-    
-    if (result.success) {
-      // 로컬 상태 업데이트
-      if (onDataUpdate) {
-        onDataUpdate(() => mergedData);
-      }
-      
-      // 성공 메시지 구성
-      const { updatedCount, notFoundTags } = result.data || {};
-      
-      let message = `Successfully updated ${updatedCount || importedData.length} rows`;
-      
-      // 건너뛴 필드가 있는 경우
-      if (skippedFieldsLog.length > 0) {
-        const totalSkippedFields = skippedFieldsLog.reduce((sum, log) => sum + log.fields.length, 0);
-        message += ` (${totalSkippedFields} read-only fields preserved)`;
-      }
-      
-      // 찾을 수 없는 TAG가 있는 경우
-      if (notFoundTags && notFoundTags.length > 0) {
-        console.warn("Tags not found in database:", notFoundTags);
-        message += `. Warning: ${notFoundTags.length} tags not found in database`;
-      }
-      
-      toast.success(message);
-      
-      return { 
-        success: true, 
-        importedCount: updatedCount || importedData.length,
-        message: message,
-        errorCount: 0,
-        hasErrors: false,
-        skippedFields: skippedFieldsLog,
-        notFoundTags: notFoundTags
-      };
-      
-    } else {
-      // 배치 업데이트 실패
-      console.error("Batch update failed:", result.message);
-      
-      // 부분 성공인 경우
-      if (result.data?.updatedCount > 0) {
-        // 부분적으로라도 업데이트된 경우 로컬 상태 업데이트
-        if (onDataUpdate) {
-          onDataUpdate(() => mergedData);
-        }
-        
-        toast.warning(
-          `Partially updated: ${result.data.updatedCount} of ${importedData.length} rows updated. ` +
-          `${result.data.failedCount || 0} failed.`
+    // importExcelData 함수에서 DB 저장 부분
+    if (formCode && contractItemId) {
+      try {
+        // 배치 업데이트 함수 호출
+        const result = await updateFormDataBatchInDB(
+          formCode,
+          contractItemId,
+          importedData // 모든 imported rows를 한번에 전달
         );
-        
-        return {
-          success: true, // 부분 성공도 success로 처리
-          importedCount: result.data.updatedCount,
-          message: result.message,
-          errorCount: result.data.failedCount || 0,
-          hasErrors: true,
-          skippedFields: skippedFieldsLog
-        };
-        
-      } else {
-        // 완전 실패
-        toast.error(result.message || "Failed to update data to database");
-        
+
+        if (result.success) {
+          // 로컬 상태 업데이트
+          if (onDataUpdate) {
+            onDataUpdate(() => mergedData);
+          }
+
+          // 성공 메시지 구성
+          const { updatedCount, notFoundTags } = result.data || {};
+
+          let message = `Successfully updated ${updatedCount || importedData.length} rows`;
+
+          // 건너뛴 필드가 있는 경우
+          if (skippedFieldsLog.length > 0) {
+            const totalSkippedFields = skippedFieldsLog.reduce((sum, log) => sum + log.fields.length, 0);
+            message += ` (${totalSkippedFields} read-only fields preserved)`;
+          }
+
+          // 찾을 수 없는 TAG가 있는 경우
+          if (notFoundTags && notFoundTags.length > 0) {
+            console.warn("Tags not found in database:", notFoundTags);
+            message += `. Warning: ${notFoundTags.length} tags not found in database`;
+          }
+
+          toast.success(message);
+
+          return {
+            success: true,
+            importedCount: updatedCount || importedData.length,
+            message: message,
+            errorCount: 0,
+            hasErrors: false,
+            skippedFields: skippedFieldsLog,
+            notFoundTags: notFoundTags
+          };
+
+        } else {
+          // 배치 업데이트 실패
+          console.error("Batch update failed:", result.message);
+
+          // 부분 성공인 경우
+          if (result.data?.updatedCount > 0) {
+            // 부분적으로라도 업데이트된 경우 로컬 상태 업데이트
+            if (onDataUpdate) {
+              onDataUpdate(() => mergedData);
+            }
+
+            toast.warning(
+              `Partially updated: ${result.data.updatedCount} of ${importedData.length} rows updated. ` +
+              `${result.data.failedCount || 0} failed.`
+            );
+
+            return {
+              success: true, // 부분 성공도 success로 처리
+              importedCount: result.data.updatedCount,
+              message: result.message,
+              errorCount: result.data.failedCount || 0,
+              hasErrors: true,
+              skippedFields: skippedFieldsLog
+            };
+
+          } else {
+            // 완전 실패
+            toast.error(result.message || "Failed to update data to database");
+
+            return {
+              success: false,
+              error: result.message,
+              errorCount: importedData.length,
+              hasErrors: true,
+              skippedFields: skippedFieldsLog
+            };
+          }
+        }
+
+      } catch (saveError) {
+        // 예외 발생 처리
+        console.error("Failed to save imported data:", saveError);
+
+        const errorMessage = saveError instanceof Error
+          ? saveError.message
+          : "Unknown error occurred";
+
+        toast.error(`Database update failed: ${errorMessage}`);
+
         return {
           success: false,
-          error: result.message,
+          error: saveError,
+          message: errorMessage,
           errorCount: importedData.length,
           hasErrors: true,
           skippedFields: skippedFieldsLog
         };
       }
+
+    } else {
+      // formCode나 contractItemId가 없는 경우 - 로컬 업데이트만
+      if (onDataUpdate) {
+        onDataUpdate(() => mergedData);
+      }
+
+      const successMessage = skippedFieldsLog.length > 0
+        ? `Imported ${importedData.length} rows successfully (read-only fields preserved)`
+        : `Imported ${importedData.length} rows successfully`;
+
+      toast.success(`${successMessage} (local only - no database connection)`);
+
+      return {
+        success: true,
+        importedCount: importedData.length,
+        message: "Data imported locally only",
+        errorCount: 0,
+        hasErrors: false,
+        skippedFields: skippedFieldsLog
+      };
     }
-    
-  } catch (saveError) {
-    // 예외 발생 처리
-    console.error("Failed to save imported data:", saveError);
-    
-    const errorMessage = saveError instanceof Error 
-      ? saveError.message 
-      : "Unknown error occurred";
-    
-    toast.error(`Database update failed: ${errorMessage}`);
-    
-    return { 
-      success: false, 
-      error: saveError,
-      message: errorMessage,
-      errorCount: importedData.length,
-      hasErrors: true,
-      skippedFields: skippedFieldsLog
-    };
-  }
-  
-} else {
-  // formCode나 contractItemId가 없는 경우 - 로컬 업데이트만
-  if (onDataUpdate) {
-    onDataUpdate(() => mergedData);
-  }
-  
-  const successMessage = skippedFieldsLog.length > 0 
-    ? `Imported ${importedData.length} rows successfully (read-only fields preserved)`
-    : `Imported ${importedData.length} rows successfully`;
-  
-  toast.success(`${successMessage} (local only - no database connection)`);
-  
-  return { 
-    success: true, 
-    importedCount: importedData.length,
-    message: "Data imported locally only",
-    errorCount: 0,
-    hasErrors: false,
-    skippedFields: skippedFieldsLog
-  };
-}
-    
+
   } catch (err) {
     console.error("Excel import error:", err);
     toast.error("Excel import failed.");
-    return { 
-      success: false, 
+    return {
+      success: false,
       error: err,
       errorCount: 1,
       hasErrors: true
-- 
cgit v1.2.3

