summaryrefslogtreecommitdiff
path: root/components/form-data/import-excel-form.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-10-03 04:48:47 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-10-03 04:48:47 +0000
commitdefda07c0bb4b0bd444ca8dc4fd3f89322bda0ce (patch)
treed7f257781f107d7ec2fd4ef76cb4f840f5065a06 /components/form-data/import-excel-form.tsx
parent00743c8b4190fac9117c2d9c08981bbfdce576de (diff)
(대표님) edp, tbe, dolce 등
Diffstat (limited to 'components/form-data/import-excel-form.tsx')
-rw-r--r--components/form-data/import-excel-form.tsx312
1 files changed, 155 insertions, 157 deletions
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