From 02b1cf005cf3e1df64183d20ba42930eb2767a9f Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Thu, 21 Aug 2025 06:57:36 +0000
Subject: (대표님, 최겸) 설계메뉴추가, 작업사항 업데이트 설계메뉴 - 문서관리
 설계메뉴 - 벤더 데이터 gtc 메뉴 업데이트 정보시스템 - 메뉴리스트 및 정보
 업데이트 파일 라우트 업데이트 엑셀임포트 개선 기본계약 개선 벤더 가입과정
 변경 및 개선 벤더 기본정보 - pq 돌체 오류 수정 및 개선 벤더 로그인 과정
 이메일 오류 수정
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/form-data/export-excel-form.tsx | 156 +++++++++++++++++++++++++----
 1 file changed, 137 insertions(+), 19 deletions(-)

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

diff --git a/components/form-data/export-excel-form.tsx b/components/form-data/export-excel-form.tsx
index 64e9ea3d..1efa5819 100644
--- a/components/form-data/export-excel-form.tsx
+++ b/components/form-data/export-excel-form.tsx
@@ -11,7 +11,7 @@ export interface DataTableColumnJSON {
   label: string;
   type: ColumnType;
   options?: string[];
-  shi?: boolean; // SHI-only field indicator
+  shi?: string | null; // Updated to support both string and boolean for backward compatibility
   required?: boolean; // Required field indicator
   // Add any other properties that might be in columnsJSON
 }
@@ -39,6 +39,7 @@ export interface ExportExcelOptions {
   tableData: GenericData[];
   columnsJSON: DataTableColumnJSON[];
   formCode: string;
+  editableFieldsMap?: Map<string, string[]>; // 새로 추가
   onPendingChange?: (isPending: boolean) => void;
   validateData?: boolean; // Option to enable/disable data validation
 }
@@ -51,6 +52,63 @@ export interface ExportExcelResult {
   hasErrors?: boolean;
 }
 
+/**
+ * Check if a field is editable for a specific TAG_NO
+ */
+function isFieldEditable(
+  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);
+}
+
+/**
+ * Get the read-only reason for a field
+ */
+function getReadOnlyReason(
+  column: DataTableColumnJSON, 
+  tagNo: string, 
+  editableFieldsMap: Map<string, string[]>
+): string {
+  if (column.shi === "OUT" || column.shi === null) {
+    return "SHI-only field";
+  }
+  
+  if (column.key === "TAG_NO" || column.key === "TAG_DESC" || column.key === "status") {
+    return "System field";
+  }
+  
+  if (!editableFieldsMap || editableFieldsMap.size === 0) {
+    return "No restrictions";
+  }
+  
+  if (!editableFieldsMap.has(tagNo)) {
+    return "No editable fields for this TAG";
+  }
+  
+  const editableFields = editableFieldsMap.get(tagNo) || [];
+  if (!editableFields.includes(column.key)) {
+    return "Not editable for this TAG";
+  }
+  
+  return "Editable";
+}
+
 /**
  * Validate data and collect errors
  */
@@ -276,6 +334,7 @@ export async function exportExcelData({
   tableData,
   columnsJSON,
   formCode,
+  editableFieldsMap = new Map(), // 새로 추가
   onPendingChange,
   validateData = true
 }: ExportExcelOptions): Promise<ExportExcelResult> {
@@ -346,7 +405,7 @@ export async function exportExcelData({
       const columnIndex = colNumber - 1;
       const column = columnsJSON[columnIndex];
       
-      if (column?.shi === true) {
+      if (column?.shi === "OUT" || column?.shi === null ) {
         // SHI-only 필드는 더 진한 음영으로 표시
         cell.fill = {
           type: "pattern",
@@ -384,24 +443,53 @@ export async function exportExcelData({
       const rowErrors = errors.filter(err => err.rowIndex === rowIndex + 2);
       const hasErrors = rowErrors.length > 0;
       
-      // SHI-only 컬럼의 데이터 셀에도 음영 적용
+      // 각 데이터 셀에 적절한 스타일 적용
       dataRow.eachCell((cell, colNumber) => {
         const columnIndex = colNumber - 1;
         const column = columnsJSON[columnIndex];
+        const tagNo = rowData.TAG_NO || "";
         
         // Check if this cell has errors
         const cellHasError = rowErrors.some(err => err.columnKey === column.key);
         
-        if (column?.shi === true) {
-          // SHI-only 필드의 데이터 셀에 연한 음영 적용
+        // Check if this field is editable for this specific TAG_NO
+        const fieldEditable = isFieldEditable(column, tagNo, editableFieldsMap);
+        const readOnlyReason = getReadOnlyReason(column, tagNo, editableFieldsMap);
+        
+        if (!fieldEditable) {
+          // Read-only field styling
+          let bgColor = "FFFFCCCC"; // Default light red for read-only
+          let fontColor = "FF666666"; // Gray text
+          
+          if (column?.shi === "OUT" || column?.shi === null ) {
+            // SHI-only fields get a more distinct styling
+            bgColor = cellHasError ? "FFFF6666" : "FFFFCCCC"; // Darker red if error
+            fontColor = "FF800000"; // Dark red text
+          } else {
+            // Other read-only fields (editableFieldsMap restrictions)
+            bgColor = cellHasError ? "FFFFAA99" : "FFFFDDCC"; // Orange-ish tint
+            fontColor = "FF996633"; // Brown text
+          }
+          
           cell.fill = {
             type: "pattern",
             pattern: "solid",
-            fgColor: { argb: cellHasError ? "FFFF6666" : "FFFFCCCC" }, // 에러가 있으면 더 진한 빨간색
+            fgColor: { argb: bgColor },
           };
-          cell.font = { italic: true, color: { argb: "FF666666" } };
+          cell.font = { italic: true, color: { argb: fontColor } };
+          
+          // Add comment to explain why it's read-only
+          if (readOnlyReason !== "Editable") {
+            cell.note = {
+              texts: [{ text: `Read-only: ${readOnlyReason}` }],
+              margins: {
+                insetmode: "custom",
+                inset: [0.13, 0.13, 0.25, 0.25]
+              }
+            };
+          }
         } else if (cellHasError) {
-          // 에러가 있는 셀은 연한 빨간색 배경
+          // Editable field with validation error
           cell.fill = {
             type: "pattern",
             pattern: "solid",
@@ -409,6 +497,7 @@ export async function exportExcelData({
           };
           cell.font = { color: { argb: "FFCC0000" } };
         }
+        // If field is editable and has no errors, no special styling needed
       });
     });
 
@@ -418,8 +507,8 @@ export async function exportExcelData({
     columnsJSON.forEach((col, idx) => {
       const colLetter = worksheet.getColumn(idx + 1).letter;
       
-      // SHI-only 필드가 아닌 LIST 타입에만 유효성 검사 적용
-      if (col.type === "LIST" && validationRanges.has(col.key) && col.shi !== true) {
+      // LIST 타입이고 유효성 검사 범위가 있는 경우에만 적용
+      if (col.type === "LIST" && validationRanges.has(col.key)) {
         const validationRange = validationRanges.get(col.key)!;
 
         // 유효성 검사 정의
@@ -439,25 +528,34 @@ export async function exportExcelData({
           rowIdx <= Math.min(tableData.length + 1, maxRows);
           rowIdx++
         ) {
-          worksheet.getCell(`${colLetter}${rowIdx}`).dataValidation =
-            validation;
+          const cell = worksheet.getCell(`${colLetter}${rowIdx}`);
+          
+          // Only apply validation to editable cells
+          const rowData = tableData[rowIdx - 2]; // rowIdx is 1-based, data array is 0-based
+          if (rowData) {
+            const tagNo = rowData.TAG_NO || "";
+            const fieldEditable = isFieldEditable(col, tagNo, editableFieldsMap);
+            
+            if (fieldEditable) {
+              cell.dataValidation = validation;
+            }
+          }
         }
 
-        // 빈 행에도 적용 (최대 maxRows까지)
+        // 빈 행에도 적용 (최대 maxRows까지) - 기본적으로 편집 가능하다고 가정
         if (tableData.length + 1 < maxRows) {
           for (
             let rowIdx = tableData.length + 2;
             rowIdx <= maxRows;
             rowIdx++
           ) {
-            worksheet.getCell(`${colLetter}${rowIdx}`).dataValidation =
-              validation;
+            worksheet.getCell(`${colLetter}${rowIdx}`).dataValidation = validation;
           }
         }
       }
       
-      // SHI-only 필드의 빈 행들에도 음영 처리 적용
-      if (col.shi === true) {
+      // Read-only 필드의 빈 행들에도 음영 처리 적용 (기본적으로 SHI-only 필드에만)
+      if (col.shi === "OUT" || col.shi === null ) {
         for (let rowIdx = tableData.length + 2; rowIdx <= maxRows; rowIdx++) {
           const cell = worksheet.getCell(`${colLetter}${rowIdx}`);
           cell.fill = {
@@ -503,8 +601,9 @@ export async function exportExcelData({
     legendSheet.addRow(["Red background header", "SHI-only fields that cannot be edited"]);
     legendSheet.addRow(["Blue background header", "Required fields (marked with *)"]);
     legendSheet.addRow(["Gray background header", "Regular optional fields"]);
-    legendSheet.addRow(["Light red background cells", "Cells with validation errors"]);
-    legendSheet.addRow(["Light red data cells", "Data in SHI-only fields (read-only)"]);
+    legendSheet.addRow(["Light red background cells", "Cells with validation errors OR SHI-only fields"]);
+    legendSheet.addRow(["Light orange background cells", "Fields not editable for specific TAG (based on editableFieldsMap)"]);
+    legendSheet.addRow(["Cell comments", "Hover over read-only cells to see the reason why they cannot be edited"]);
     
     if (errors.length > 0) {
       legendSheet.addRow([]);
@@ -512,6 +611,25 @@ export async function exportExcelData({
       const errorNoteRow = legendSheet.getRow(legendSheet.rowCount);
       errorNoteRow.font = { bold: true, color: { argb: "FFCC0000" } };
     }
+
+    // Add editableFieldsMap summary if available
+    if (editableFieldsMap.size > 0) {
+      legendSheet.addRow([]);
+      legendSheet.addRow([`Editable Fields Map Summary (${editableFieldsMap.size} TAGs):`]);
+      const summaryHeaderRow = legendSheet.getRow(legendSheet.rowCount);
+      summaryHeaderRow.font = { bold: true, color: { argb: "FF000080" } };
+      
+      // Show first few examples
+      let count = 0;
+      for (const [tagNo, editableFields] of editableFieldsMap) {
+        if (count >= 5) { // Show only first 5 examples
+          legendSheet.addRow([`... and ${editableFieldsMap.size - 5} more TAGs`]);
+          break;
+        }
+        legendSheet.addRow([`${tagNo}:`, editableFields.join(", ")]);
+        count++;
+      }
+    }
     
     // 범례 스타일 적용
     const legendHeaderRow = legendSheet.getRow(1);
-- 
cgit v1.2.3

