From 748bb1720fd81e97a84c3e92f89d606e976b52e3 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Wed, 28 May 2025 00:18:16 +0000 Subject: (대표님) 컴포넌트 파트 커밋 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pq-input/pq-review-wrapper.tsx | 330 ++++++++++++++++++++++++++++++ 1 file changed, 330 insertions(+) create mode 100644 components/pq-input/pq-review-wrapper.tsx (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 new file mode 100644 index 00000000..216df422 --- /dev/null +++ b/components/pq-input/pq-review-wrapper.tsx @@ -0,0 +1,330 @@ +"use client" + +import * as React from "react" +import { useRouter } from "next/navigation" +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, + CardFooter +} from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Textarea } from "@/components/ui/textarea" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle +} from "@/components/ui/dialog" +import { useToast } from "@/hooks/use-toast" +import { CheckCircle, AlertCircle, FileText, Paperclip } from "lucide-react" +import { PQGroupData } from "@/lib/pq/service" +import { approvePQAction, rejectPQAction } from "@/lib/pq/service" + +// PQ 제출 정보 타입 +interface PQSubmission { + id: number + vendorId: number + vendorName: string + vendorCode: string + type: string + status: string + projectId: number | null + projectName: string | null + projectCode: string | null + submittedAt: Date | null + approvedAt: Date | null + rejectedAt: Date | null + rejectReason: string | null +} + +interface PQReviewWrapperProps { + pqData: PQGroupData[] + vendorId: number + pqSubmission: PQSubmission + canReview: boolean +} + +export function PQReviewWrapper({ + pqData, + vendorId, + pqSubmission, + canReview +}: PQReviewWrapperProps) { + const router = useRouter() + const { toast } = useToast() + const [isApproving, setIsApproving] = React.useState(false) + const [isRejecting, setIsRejecting] = React.useState(false) + const [showApproveDialog, setShowApproveDialog] = React.useState(false) + const [showRejectDialog, setShowRejectDialog] = React.useState(false) + const [rejectReason, setRejectReason] = React.useState("") + + // PQ 승인 처리 + const handleApprove = async () => { + try { + setIsApproving(true) + + const result = await approvePQAction({ + pqSubmissionId: pqSubmission.id, + vendorId: vendorId + }) + + if (result.ok) { + toast({ + title: "PQ 승인 완료", + description: "PQ가 성공적으로 승인되었습니다.", + }) + // 페이지 새로고침 + router.refresh() + } else { + toast({ + title: "승인 실패", + description: result.error || "PQ 승인 중 오류가 발생했습니다.", + variant: "destructive" + }) + } + } catch (error) { + console.error("PQ 승인 오류:", error) + toast({ + title: "승인 실패", + description: "PQ 승인 중 오류가 발생했습니다.", + variant: "destructive" + }) + } finally { + setIsApproving(false) + setShowApproveDialog(false) + } + } + + // PQ 거부 처리 + const handleReject = async () => { + if (!rejectReason.trim()) { + toast({ + title: "거부 사유 필요", + description: "거부 사유를 입력해주세요.", + variant: "destructive" + }) + return + } + + try { + setIsRejecting(true) + + const result = await rejectPQAction({ + pqSubmissionId: pqSubmission.id, + vendorId: vendorId, + rejectReason: rejectReason + }) + + if (result.ok) { + toast({ + title: "PQ 거부 완료", + description: "PQ가 거부되었습니다.", + }) + // 페이지 새로고침 + router.refresh() + } else { + toast({ + title: "거부 실패", + description: result.error || "PQ 거부 중 오류가 발생했습니다.", + variant: "destructive" + }) + } + } catch (error) { + console.error("PQ 거부 오류:", error) + toast({ + title: "거부 실패", + description: "PQ 거부 중 오류가 발생했습니다.", + variant: "destructive" + }) + } finally { + setIsRejecting(false) + setShowRejectDialog(false) + } + } + + return ( +
+ {/* 그룹별 PQ 항목 표시 */} + {pqData.map((group) => ( +
+

{group.groupName}

+ +
+ {group.items.map((item) => ( + + +
+
+ + {item.code} - {item.checkPoint} + + {item.description && ( + + {item.description} + + )} +
+ {/* 항목 상태 표시 */} + {!!item.answer || item.attachments.length > 0 ? ( + + + 답변 있음 + + ) : ( + + + 답변 없음 + + )} +
+
+ + {/* 프로젝트별 추가 정보 */} + {pqSubmission.projectId && item.contractInfo && ( +
+

계약 정보

+
+ {item.contractInfo} +
+
+ )} + + {pqSubmission.projectId && item.additionalRequirement && ( +
+

추가 요구사항

+
+ {item.additionalRequirement} +
+
+ )} + + {/* 벤더 답변 */} +
+

+ + 벤더 답변 +

+
+ {item.answer || 답변 없음} +
+
+ + {/* 첨부 파일 */} + {item.attachments.length > 0 && ( +
+

+ + 첨부 파일 ({item.attachments.length}) +

+
+ +
+
+ )} +
+
+ ))} +
+
+ ))} + + {/* 검토 버튼 */} + {canReview && ( +
+
+ + +
+
+ )} + + {/* 승인 확인 다이얼로그 */} + + + + PQ 승인 확인 + + {pqSubmission.vendorName}의 {pqSubmission.type === "GENERAL" ? "일반" : "프로젝트"} PQ를 승인하시겠습니까? + {pqSubmission.projectId && ( + 프로젝트: {pqSubmission.projectName} + )} + + + + + + + + + + {/* 거부 확인 다이얼로그 */} + + + + PQ 거부 + + {pqSubmission.vendorName}의 {pqSubmission.type === "GENERAL" ? "일반" : "프로젝트"} PQ를 거부하는 이유를 입력해주세요. + {pqSubmission.projectId && ( + 프로젝트: {pqSubmission.projectName} + )} + + +