From 53ad72732f781e6c6d5ddb3776ea47aec010af8e Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 4 Aug 2025 09:39:21 +0000 Subject: (최겸) PQ/실사 수정 및 개발 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pq-input/pq-review-wrapper.tsx | 392 ++++++++++++++++++++++++++++-- 1 file changed, 371 insertions(+), 21 deletions(-) (limited to 'components/pq-input/pq-review-wrapper.tsx') diff --git a/components/pq-input/pq-review-wrapper.tsx b/components/pq-input/pq-review-wrapper.tsx index 216df422..1056189e 100644 --- a/components/pq-input/pq-review-wrapper.tsx +++ b/components/pq-input/pq-review-wrapper.tsx @@ -7,8 +7,7 @@ import { CardContent, CardHeader, CardTitle, - CardDescription, - CardFooter + CardDescription } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" @@ -22,16 +21,18 @@ import { DialogTitle } from "@/components/ui/dialog" import { useToast } from "@/hooks/use-toast" -import { CheckCircle, AlertCircle, FileText, Paperclip } from "lucide-react" +import { CheckCircle, AlertCircle, Paperclip } from "lucide-react" import { PQGroupData } from "@/lib/pq/service" -import { approvePQAction, rejectPQAction } from "@/lib/pq/service" +import { approvePQAction, rejectPQAction, updateSHICommentAction } from "@/lib/pq/service" +// import * as ExcelJS from 'exceljs'; +// import { saveAs } from "file-saver"; // PQ 제출 정보 타입 interface PQSubmission { id: number vendorId: number - vendorName: string - vendorCode: string + vendorName: string | null + vendorCode: string | null type: string status: string projectId: number | null @@ -63,6 +64,21 @@ export function PQReviewWrapper({ const [showApproveDialog, setShowApproveDialog] = React.useState(false) const [showRejectDialog, setShowRejectDialog] = React.useState(false) const [rejectReason, setRejectReason] = React.useState("") + const [shiComments, setShiComments] = React.useState>({}) + const [isUpdatingComment, setIsUpdatingComment] = React.useState(null) + + // 기존 SHI 코멘트를 로컬 상태에 초기화 + React.useEffect(() => { + const initialComments: Record = {} + pqData.forEach(group => { + group.items.forEach(item => { + if (item.answerId && item.shiComment) { + initialComments[item.answerId] = item.shiComment + } + }) + }) + setShiComments(initialComments) + }, [pqData]) // PQ 승인 처리 const handleApprove = async () => { @@ -101,6 +117,178 @@ export function PQReviewWrapper({ } } + // SHI 코멘트 업데이트 처리 + const handleSHICommentUpdate = async (answerId: number) => { + const comment = shiComments[answerId] || "" + + try { + setIsUpdatingComment(answerId) + const result = await updateSHICommentAction({ + answerId, + shiComment: comment, + }) + + if (result.ok) { + toast({ + title: "SHI 코멘트 저장 완료", + description: "SHI 코멘트가 저장되었습니다.", + }) + // 페이지 새로고침 + router.refresh() + } else { + toast({ + title: "저장 실패", + description: result.error || "SHI 코멘트 저장 중 오류가 발생했습니다.", + variant: "destructive" + }) + } + } catch (error) { + console.error("SHI 코멘트 저장 오류:", error) + toast({ + title: "저장 실패", + description: "SHI 코멘트 저장 중 오류가 발생했습니다.", + variant: "destructive" + }) + } finally { + setIsUpdatingComment(null) + } + } + + // // Excel export 처리 + // const handleExportToExcel = async () => { + // try { + // setIsExporting(true) + + // // 워크북 생성 + // const workbook = new ExcelJS.Workbook() + // workbook.creator = 'PQ Management System' + // workbook.created = new Date() + + // // 메인 시트 생성 + // const worksheet = workbook.addWorksheet("PQ 항목") + + // // 헤더 정의 + // const headers = [ + // "그룹명", + // "코드", + // "체크포인트", + // "설명", + // "입력형식", + // "필수여부", + // "벤더답변", + // "SHI 코멘트", + // "벤더 답변", + // ] + + // // 헤더 추가 + // worksheet.addRow(headers) + + // // 헤더 스타일 적용 + // const headerRow = worksheet.getRow(1) + // headerRow.font = { bold: true } + // headerRow.fill = { + // type: 'pattern', + // pattern: 'solid', + // fgColor: { argb: 'FFE0E0E0' } + // } + // headerRow.alignment = { vertical: 'middle', horizontal: 'center' } + + // // 컬럼 너비 설정 + // worksheet.columns = [ + // { header: "그룹명", key: "groupName", width: 15 }, + // { header: "코드", key: "code", width: 12 }, + // { header: "체크포인트", key: "checkPoint", width: 30 }, + // { header: "설명", key: "description", width: 40 }, + // { header: "입력형식", key: "inputFormat", width: 12 }, + + // { header: "벤더답변", key: "answer", width: 30 }, + // { header: "SHI 코멘트", key: "shiComment", width: 30 }, + // { header: "벤더 답변", key: "vendorReply", width: 30 }, + // ] + + // // 데이터 추가 + // pqData.forEach(group => { + // group.items.forEach(item => { + // const rowData = [ + // group.groupName, + // item.code, + // item.checkPoint, + // item.description || "", + // item.inputFormat || "", + + // item.answer || "", + // item.shiComment || "", + // item.vendorReply || "", + // ] + // worksheet.addRow(rowData) + // }) + // }) + + // // 전체 셀에 테두리 추가 + // worksheet.eachRow((row, rowNumber) => { + // row.eachCell((cell) => { + // cell.border = { + // top: { style: 'thin' }, + // left: { style: 'thin' }, + // bottom: { style: 'thin' }, + // right: { style: 'thin' } + // } + // // 긴 텍스트는 자동 줄바꿈 + // cell.alignment = { + // vertical: 'top', + // horizontal: 'left', + // wrapText: true + // } + // }) + // }) + + // // 정보 시트 생성 + // const infoSheet = workbook.addWorksheet("정보") + // infoSheet.addRow(["벤더명", pqSubmission.vendorName]) + // if (pqSubmission.projectName) { + // infoSheet.addRow(["프로젝트명", pqSubmission.projectName]) + // } + // infoSheet.addRow(["생성일", new Date().toLocaleDateString('ko-KR')]) + // infoSheet.addRow(["총 항목 수", pqData.reduce((total, group) => total + group.items.length, 0)]) + + // // 정보 시트 스타일링 + // infoSheet.columns = [ + // { header: "항목", key: "item", width: 20 }, + // { header: "값", key: "value", width: 40 } + // ] + + // const infoHeaderRow = infoSheet.getRow(1) + // infoHeaderRow.font = { bold: true } + // infoHeaderRow.fill = { + // type: 'pattern', + // pattern: 'solid', + // fgColor: { argb: 'FFE6F3FF' } + // } + + // // 파일명 생성 + // const defaultFilename = pqSubmission.projectName + // ? `${pqSubmission.vendorName}_${pqSubmission.projectName}_PQ_${new Date().toISOString().slice(0, 10)}` + // : `${pqSubmission.vendorName}_PQ_${new Date().toISOString().slice(0, 10)}` + // const finalFilename = defaultFilename + + // // 파일 다운로드 + // const buffer = await workbook.xlsx.writeBuffer() + // const blob = new Blob([buffer], { + // type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + // }) + // saveAs(blob, `${finalFilename}.xlsx`) + // } catch (error) { + // console.error("Excel export 오류:", error) + // toast({ + // title: "내보내기 실패", + // description: "Excel 내보내기 중 오류가 발생했습니다.", + // variant: "destructive" + // }) + // } finally { + // setIsExporting(false) + // } + // } + // PQ 거부 처리 const handleReject = async () => { if (!rejectReason.trim()) { @@ -163,12 +351,20 @@ export function PQReviewWrapper({
{item.code} - {item.checkPoint} + + {item.description && ( {item.description} )} + {/*
+ 생성일: {item.createdAt?.toLocaleString('ko-KR')} +
+
+ 수정일: {item.updatedAt?.toLocaleString('ko-KR')} +
*/}
{/* 항목 상태 표시 */} {!!item.answer || item.attachments.length > 0 ? ( @@ -182,6 +378,7 @@ export function PQReviewWrapper({ 답변 없음 )} + @@ -204,19 +401,134 @@ export function PQReviewWrapper({ )} - {/* 벤더 답변 */} + + + {/* 벤더 답변 - 입력 형식에 따라 다르게 표시 */}

- 벤더 답변 + {item.inputFormat && ( + + {item.inputFormat === "TEXT" && "텍스트"} + {item.inputFormat === "EMAIL" && "이메일"} + {item.inputFormat === "PHONE" && "전화번호"} + {item.inputFormat === "NUMBER" && "숫자"} + {item.inputFormat === "FILE" && "파일"} + {item.inputFormat === "TEXT_FILE" && "텍스트+파일"} + + )} +

+
+ {(() => { + const inputFormat = item.inputFormat || "TEXT"; + + switch (inputFormat) { + case "EMAIL": + return ( +
+
이메일 주소:
+
+ {item.answer || 답변 없음} +
+
+ ); + case "PHONE": + return ( +
+
전화번호:
+
+ {item.answer || 답변 없음} +
+
+ ); + case "NUMBER": + return ( +
+
숫자 값:
+
+ {item.answer || 답변 없음} +
+
+ ); + case "FILE": + return ( +
+
파일 업로드 항목:
+
+ {item.attachments.length > 0 ? "파일이 업로드되었습니다." : "파일이 업로드되지 않았습니다."} +
+
+ ); + case "TEXT_FILE": + return ( +
+
텍스트 답변:
+
+ {item.answer || 텍스트 답변 없음} +
+
파일 업로드:
+
+ {item.attachments.length > 0 ? "파일이 업로드되었습니다." : "파일이 업로드되지 않았습니다."} +
+
+ ); + default: // TEXT + return ( +
+ {item.answer || 답변 없음} +
+ ); + } + })()} +
+
+ {/* SHI 코멘트 필드 (편집 가능) */} +
+

+ SHI 코멘트

-
- {item.answer || 답변 없음} +
+