From dfdfae3018f8499240f48d28ce634f4a5c56e006 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 2 Apr 2025 09:54:08 +0000 Subject: 벤더 코멘트 처리 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pq/table/pq-excel-template.tsx | 205 +++++++++++++++++++++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 lib/pq/table/pq-excel-template.tsx (limited to 'lib/pq/table/pq-excel-template.tsx') diff --git a/lib/pq/table/pq-excel-template.tsx b/lib/pq/table/pq-excel-template.tsx new file mode 100644 index 00000000..aa8c1b3a --- /dev/null +++ b/lib/pq/table/pq-excel-template.tsx @@ -0,0 +1,205 @@ +"use client" + +import * as ExcelJS from 'exceljs'; +import { saveAs } from 'file-saver'; +import { toast } from 'sonner'; + +/** + * PQ 기준 Excel 템플릿을 다운로드하는 함수 (exceljs 사용) + * @param isProjectSpecific 프로젝트별 PQ 템플릿 여부 + */ +export async function exportPqTemplate(isProjectSpecific: boolean = false) { + try { + // 워크북 생성 + const workbook = new ExcelJS.Workbook(); + + // 워크시트 생성 + const sheetName = isProjectSpecific ? "Project PQ Template" : "General PQ Template"; + const worksheet = workbook.addWorksheet(sheetName); + + // 그룹 옵션 정의 - 드롭다운 목록에 사용 + const groupOptions = [ + "GENERAL", + "Quality Management System", + "Workshop & Environment", + "Warranty", + ]; + + // 일반 PQ 필드 (기본 필드) + const basicFields = [ + { header: "Code", key: "code", width: 90 }, + { header: "Check Point", key: "checkPoint", width: 180 }, + { header: "Group Name", key: "groupName", width: 150 }, + { header: "Description", key: "description", width: 240 }, + { header: "Remarks", key: "remarks", width: 180 }, + ]; + + // 프로젝트별 PQ 추가 필드 + const projectFields = isProjectSpecific + ? [ + { header: "Contract Info", key: "contractInfo", width: 180 }, + { header: "Additional Requirements", key: "additionalRequirement", width: 240 }, + ] + : []; + + // 모든 필드 합치기 + const fields = [...basicFields, ...projectFields]; + + // 지침 행 추가 + const instructionTitle = worksheet.addRow(["Instructions:"]); + instructionTitle.font = { bold: true, size: 12 }; + worksheet.mergeCells(1, 1, 1, fields.length); + + const instructions = [ + "1. 'Code' 필드는 고유해야 합니다 (예: 1-1, A.2.3).", + "2. 'Check Point'는 필수 항목입니다.", + "3. 'Group Name'은 드롭다운 목록에서 선택하세요: GENERAL, Quality Management System, Workshop & Environment, Warranty", + "4. 여러 줄 텍스트는 \\n으로 줄바꿈을 표시합니다.", + "5. 아래 회색 배경의 예시 행은 참고용입니다. 실제 데이터 입력 전에 이 행을 수정하거나 삭제해야 합니다.", + ]; + + // 프로젝트별 PQ일 경우 추가 지침 + if (isProjectSpecific) { + instructions.push( + "6. 'Contract Info'와 'Additional Requirements'는 프로젝트별 세부 정보를 위한 필드입니다." + ); + } + + // 지침 행 추가 + instructions.forEach((instruction, idx) => { + const row = worksheet.addRow([instruction]); + worksheet.mergeCells(idx + 2, 1, idx + 2, fields.length); + row.font = { color: { argb: '00808080' } }; + }); + + // 빈 행 추가 + worksheet.addRow([]); + + // 헤더 행 추가 + const headerRow = worksheet.addRow(fields.map(field => field.header)); + headerRow.font = { bold: true, color: { argb: 'FFFFFFFF' } }; + headerRow.fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: 'FF4472C4' } + }; + headerRow.alignment = { vertical: 'middle', horizontal: 'center' }; + + // 예시 행 표시를 위한 첫 번째 열 값 수정 + const exampleData: Record = { + code: "[예시 - 수정/삭제 필요] 1-1", + checkPoint: "Selling / 1 year Property", + groupName: "GENERAL", + description: "Address :\nTel. / Fax :\ne-mail :", + remarks: "Optional remarks", + }; + + // 프로젝트별 PQ인 경우 예시 데이터에 추가 필드 추가 + if (isProjectSpecific) { + exampleData.contractInfo = "Contract details for this project"; + exampleData.additionalRequirement = "Additional technical requirements"; + } + + const exampleRow = worksheet.addRow(fields.map(field => exampleData[field.key] || "")); + exampleRow.font = { italic: true }; + exampleRow.fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: 'FFEDEDED' } + }; + // 예시 행 첫 번째 셀에 코멘트 추가 + const codeCell = worksheet.getCell(exampleRow.number, 1); + codeCell.note = '이 예시 행은 참고용입니다. 실제 데이터 입력 전에 수정하거나 삭제하세요.'; + + // Group Name 열 인덱스 찾기 (0-based) + const groupNameIndex = fields.findIndex(field => field.key === "groupName"); + + // 열 너비 설정 + fields.forEach((field, index) => { + const column = worksheet.getColumn(index + 1); + column.width = field.width / 6.5; // ExcelJS에서는 픽셀과 다른 단위 사용 + }); + + // 각 셀에 테두리 추가 + const headerRowNum = instructions.length + 3; + const exampleRowNum = headerRowNum + 1; + + for (let i = 1; i <= fields.length; i++) { + // 헤더 행에 테두리 추가 + worksheet.getCell(headerRowNum, i).border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + + // 예시 행에 테두리 추가 + worksheet.getCell(exampleRowNum, i).border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + } + + // 사용자 입력용 빈 행 추가 (10개) + for (let rowIdx = 0; rowIdx < 10; rowIdx++) { + // 빈 행 추가 + const emptyRow = worksheet.addRow(Array(fields.length).fill('')); + const currentRowNum = exampleRowNum + 1 + rowIdx; + + // 각 셀에 테두리 추가 + for (let colIdx = 1; colIdx <= fields.length; colIdx++) { + const cell = worksheet.getCell(currentRowNum, colIdx); + cell.border = { + top: { style: 'thin' }, + left: { style: 'thin' }, + bottom: { style: 'thin' }, + right: { style: 'thin' } + }; + + // Group Name 열에 데이터 유효성 검사 (드롭다운) 추가 + if (colIdx === groupNameIndex + 1) { + cell.dataValidation = { + type: 'list', + allowBlank: true, + formulae: [`"${groupOptions.join(',')}"`], + showErrorMessage: true, + errorStyle: 'error', + error: '유효하지 않은 그룹입니다', + errorTitle: '입력 오류', + prompt: '목록에서 선택하세요', + promptTitle: '그룹 선택' + }; + } + } + } + + // 예시 행이 있는 열에도 Group Name 드롭다운 적용 + const exampleGroupCell = worksheet.getCell(exampleRowNum, groupNameIndex + 1); + exampleGroupCell.dataValidation = { + type: 'list', + allowBlank: true, + formulae: [`"${groupOptions.join(',')}"`], + showErrorMessage: true, + errorStyle: 'error', + error: '유효하지 않은 그룹입니다', + errorTitle: '입력 오류', + prompt: '목록에서 선택하세요', + promptTitle: '그룹 선택' + }; + + // 워크북을 Excel 파일로 변환 + const buffer = await workbook.xlsx.writeBuffer(); + + // 파일명 설정 및 저장 + const fileName = isProjectSpecific ? "project-pq-template.xlsx" : "general-pq-template.xlsx"; + const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); + saveAs(blob, fileName); + + toast.success(`${isProjectSpecific ? '프로젝트별' : '일반'} PQ 템플릿이 다운로드되었습니다.`); + } catch (error) { + console.error("템플릿 다운로드 중 오류 발생:", error); + toast.error("템플릿 다운로드 중 오류가 발생했습니다."); + } +} \ No newline at end of file -- cgit v1.2.3