From 14f61e24947fb92dd71ec0a7196a6e815f8e66da Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Mon, 21 Jul 2025 07:54:26 +0000
Subject: (최겸)기술영업 RFQ 담당자 초대, 요구사항 반영
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../table/excel-export.tsx                         | 106 +++++++++++++++++++--
 1 file changed, 99 insertions(+), 7 deletions(-)

(limited to 'lib/tech-vendor-possible-items/table/excel-export.tsx')

diff --git a/lib/tech-vendor-possible-items/table/excel-export.tsx b/lib/tech-vendor-possible-items/table/excel-export.tsx
index d3c4dea5..e6fcceed 100644
--- a/lib/tech-vendor-possible-items/table/excel-export.tsx
+++ b/lib/tech-vendor-possible-items/table/excel-export.tsx
@@ -5,7 +5,7 @@ import { format } from 'date-fns';
 import { ko } from 'date-fns/locale';
 
 /**
- * 기술영업 벤더 가능 아이템 데이터를 Excel 파일로 내보내기
+ * 기술영업 벤더 가능 아이템 데이터를 Excel 파일로 내보내기 (새로운 스키마 버전)
  */
 export async function exportTechVendorPossibleItemsToExcel(
   data: TechVendorPossibleItemsData[]
@@ -19,13 +19,18 @@ export async function exportTechVendorPossibleItemsToExcel(
     // 워크시트 생성
     const worksheet = workbook.addWorksheet('기술영업 벤더 가능 아이템');
 
-    // 컬럼 헤더 정의 및 스타일 적용
+    // 컬럼 헤더 정의 및 스타일 적용 (새로운 스키마에 맞춰)
     worksheet.columns = [
       { header: '번호', key: 'id', width: 10 },
       { header: '벤더코드', key: 'vendorCode', width: 15 },
       { header: '벤더명', key: 'vendorName', width: 25 },
+      { header: '벤더이메일', key: 'vendorEmail', width: 30 },
       { header: '벤더타입', key: 'techVendorType', width: 20 },
       { header: '아이템코드', key: 'itemCode', width: 20 },
+      { header: '공종', key: 'workType', width: 15 },
+      { header: '선종', key: 'shipTypes', width: 20 },
+      { header: '아이템리스트', key: 'itemList', width: 30 },
+      { header: '서브아이템리스트', key: 'subItemList', width: 30 },
       { header: '생성일시', key: 'createdAt', width: 20 },
     ];
 
@@ -53,7 +58,7 @@ export async function exportTechVendorPossibleItemsToExcel(
       };
     });
 
-    // 데이터 추가
+    // 데이터 추가 (새로운 스키마 필드들 포함)
     data.forEach((item, index) => {
       // 벤더 타입 파싱
       let vendorTypes = '';
@@ -68,8 +73,13 @@ export async function exportTechVendorPossibleItemsToExcel(
         id: item.id,
         vendorCode: item.vendorCode || '-',
         vendorName: item.vendorName,
+        vendorEmail: item.vendorEmail || '-',
         techVendorType: vendorTypes,
         itemCode: item.itemCode,
+        workType: item.workType || '-',
+        shipTypes: item.shipTypes || '-',
+        itemList: item.itemList || '-',
+        subItemList: item.subItemList || '-',
         createdAt: format(item.createdAt, 'yyyy-MM-dd HH:mm', { locale: ko }),
       });
 
@@ -89,6 +99,15 @@ export async function exportTechVendorPossibleItemsToExcel(
           // 나머지 컬럼 왼쪽 정렬
           cell.alignment = { vertical: 'middle', horizontal: 'left' };
         }
+
+        // 텍스트 줄바꿈 처리 (긴 텍스트 필드들)
+        if (colNumber >= 9 && colNumber <= 10) { // itemList, subItemList 컬럼
+          cell.alignment = { 
+            vertical: 'top', 
+            horizontal: 'left',
+            wrapText: true 
+          };
+        }
       });
 
       // 홀수 행 배경색
@@ -103,19 +122,29 @@ export async function exportTechVendorPossibleItemsToExcel(
       }
     });
 
-    // 요약 정보 워크시트 생성
+    // 요약 정보 워크시트 생성 (새로운 스키마 통계 포함)
     const summarySheet = workbook.addWorksheet('요약 정보');
     
     const summaryData = [
-      ['기술영업 벤더 가능 아이템 현황', ''],
+      ['기술영업 벤더 가능 아이템 현황 (새로운 스키마)', ''],
       ['', ''],
+      ['📊 기본 통계:', ''],
       ['총 항목 수:', data.length.toLocaleString()],
       ['고유 벤더 수:', new Set(data.map(item => item.vendorId)).size.toLocaleString()],
       ['고유 아이템 수:', new Set(data.map(item => item.itemCode)).size.toLocaleString()],
       ['', ''],
-      ['벤더 타입별 분포:', ''],
+      ['🏢 벤더 타입별 분포:', ''],
       ...getVendorTypeDistribution(data),
       ['', ''],
+      ['⚙️ 공종별 분포:', ''],
+      ...getWorkTypeDistribution(data),
+      ['', ''],
+      ['🚢 선종별 분포:', ''],
+      ...getShipTypeDistribution(data),
+      ['', ''],
+      ['📈 데이터 완성도:', ''],
+      ...getDataCompleteness(data),
+      ['', ''],
       ['내보내기 일시:', format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ko })],
     ];
 
@@ -127,10 +156,13 @@ export async function exportTechVendorPossibleItemsToExcel(
       } else if (typeof rowData[0] === 'string' && rowData[0].includes(':') && rowData[1] === '') {
         // 섹션 제목 스타일
         row.getCell(1).font = { bold: true, color: { argb: 'FF1F4E79' } };
+      } else if (typeof rowData[0] === 'string' && rowData[0].includes('📊') || rowData[0].includes('🏢') || rowData[0].includes('⚙️') || rowData[0].includes('🚢') || rowData[0].includes('📈')) {
+        // 이모지 섹션 제목 스타일
+        row.getCell(1).font = { bold: true, color: { argb: 'FF1F4E79' } };
       }
     });
 
-    summarySheet.getColumn(1).width = 30;
+    summarySheet.getColumn(1).width = 40;
     summarySheet.getColumn(2).width = 20;
 
     // 파일 생성 및 다운로드
@@ -178,4 +210,64 @@ function getVendorTypeDistribution(data: TechVendorPossibleItemsData[]): [string
   return Array.from(typeCount.entries())
     .sort((a, b) => b[1] - a[1])
     .map(([type, count]) => [`  - ${type}`, count.toLocaleString()]);
+}
+
+/**
+ * 공종별 분포 계산
+ */
+function getWorkTypeDistribution(data: TechVendorPossibleItemsData[]): [string, string][] {
+  const workTypeCount = new Map<string, number>();
+  
+  data.forEach(item => {
+    const workType = item.workType || '미분류';
+    workTypeCount.set(workType, (workTypeCount.get(workType) || 0) + 1);
+  });
+
+  return Array.from(workTypeCount.entries())
+    .sort((a, b) => b[1] - a[1])
+    .slice(0, 10) // 상위 10개만 표시
+    .map(([type, count]) => [`  - ${type}`, count.toLocaleString()]);
+}
+
+/**
+ * 선종별 분포 계산
+ */
+function getShipTypeDistribution(data: TechVendorPossibleItemsData[]): [string, string][] {
+  const shipTypeCount = new Map<string, number>();
+  
+  data.forEach(item => {
+    if (item.shipTypes) {
+      // 여러 선종이 콤마로 구분되어 있을 수 있음
+      const shipTypes = item.shipTypes.split(',').map(s => s.trim());
+      shipTypes.forEach(shipType => {
+        if (shipType) {
+          shipTypeCount.set(shipType, (shipTypeCount.get(shipType) || 0) + 1);
+        }
+      });
+    } else {
+      shipTypeCount.set('미분류', (shipTypeCount.get('미분류') || 0) + 1);
+    }
+  });
+
+  return Array.from(shipTypeCount.entries())
+    .sort((a, b) => b[1] - a[1])
+    .slice(0, 10) // 상위 10개만 표시
+    .map(([type, count]) => [`  - ${type}`, count.toLocaleString()]);
+}
+
+/**
+ * 데이터 완성도 계산
+ */
+function getDataCompleteness(data: TechVendorPossibleItemsData[]): [string, string][] {
+  const total = data.length;
+  
+  const completeness = [
+    ['벤더이메일 있음', `${data.filter(item => item.vendorEmail).length}/${total} (${((data.filter(item => item.vendorEmail).length / total) * 100).toFixed(1)}%)`],
+    ['공종 있음', `${data.filter(item => item.workType).length}/${total} (${((data.filter(item => item.workType).length / total) * 100).toFixed(1)}%)`],
+    ['선종 있음', `${data.filter(item => item.shipTypes).length}/${total} (${((data.filter(item => item.shipTypes).length / total) * 100).toFixed(1)}%)`],
+    ['아이템리스트 있음', `${data.filter(item => item.itemList).length}/${total} (${((data.filter(item => item.itemList).length / total) * 100).toFixed(1)}%)`],
+    ['서브아이템리스트 있음', `${data.filter(item => item.subItemList).length}/${total} (${((data.filter(item => item.subItemList).length / total) * 100).toFixed(1)}%)`],
+  ];
+
+  return completeness.map(([label, stat]) => [`  - ${label}`, stat]);
 } 
\ No newline at end of file
-- 
cgit v1.2.3

