From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- components/pq/pq-review-detail.tsx | 712 +++++++++++++++++++++++++++++++++++++ 1 file changed, 712 insertions(+) create mode 100644 components/pq/pq-review-detail.tsx (limited to 'components/pq/pq-review-detail.tsx') diff --git a/components/pq/pq-review-detail.tsx b/components/pq/pq-review-detail.tsx new file mode 100644 index 00000000..e5cd080e --- /dev/null +++ b/components/pq/pq-review-detail.tsx @@ -0,0 +1,712 @@ +"use client" + +import React from "react" +import { Button } from "@/components/ui/button" +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog" +import { Textarea } from "@/components/ui/textarea" +import { useToast } from "@/hooks/use-toast" +import { PQGroupData, requestPqChangesAction, updateVendorStatusAction, getItemReviewLogsAction } from "@/lib/pq/service" +import { Vendor } from "@/db/schema/vendors" +import { Separator } from "@/components/ui/separator" +import { ChevronsUpDown, MessagesSquare, Download, Loader2, X } from "lucide-react" +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/components/ui/collapsible" +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" +import { Card } from "@/components/ui/card" +import { formatDate } from "@/lib/utils" +import { downloadFileAction } from "@/lib/downloadFile" + +// 코멘트 상태를 위한 인터페이스 정의 +interface PendingComment { + answerId: number; + checkPoint: string; + code: string; + comment: string; + createdAt: Date; +} + +interface ReviewLog { + id: number + reviewerComment: string + reviewerName: string | null + createdAt: Date +} + +export default function VendorPQAdminReview({ + data, + vendor, +}: { + data: PQGroupData[] + vendor: Vendor +}) { + const { toast } = useToast() + + // 다이얼로그 상태들 + const [showRequestDialog, setShowRequestDialog] = React.useState(false) + const [showApproveDialog, setShowApproveDialog] = React.useState(false) + const [showRejectDialog, setShowRejectDialog] = React.useState(false) + + // 코멘트 상태들 + const [requestComment, setRequestComment] = React.useState("") + const [approveComment, setApproveComment] = React.useState("") + const [rejectComment, setRejectComment] = React.useState("") + const [isLoading, setIsLoading] = React.useState(false) + + // 항목별 코멘트 상태 추적 (메모리에만 저장) + const [pendingComments, setPendingComments] = React.useState([]) + + // 코멘트 추가 핸들러 - 실제 서버 저장이 아닌 메모리에 저장 + const handleCommentAdded = (newComment: PendingComment) => { + setPendingComments(prev => [...prev, newComment]); + toast({ + title: "Comment Added", + description: `Comment added for ${newComment.code}. Please "Request Changes" to save.` + }); + } + + // 코멘트 삭제 핸들러 + const handleRemoveComment = (index: number) => { + setPendingComments(prev => prev.filter((_, i) => i !== index)); + } + + // 1) 승인 다이얼로그 표시 + const handleApprove = () => { + // 코멘트가 있는데 승인하려고 하면 경고 + if (pendingComments.length > 0) { + if (!confirm('You have unsaved comments. Are you sure you want to approve without requesting changes?')) { + return; + } + } + setShowApproveDialog(true) + } + + // 실제 승인 처리 + const handleSubmitApprove = async () => { + try { + setIsLoading(true) + setShowApproveDialog(false) + + const res = await updateVendorStatusAction(vendor.id, "APPROVED") + if (res.ok) { + toast({ title: "Approved", description: "Vendor PQ has been approved." }) + // 코멘트 초기화 + setPendingComments([]); + } else { + toast({ title: "Error", description: res.error, variant: "destructive" }) + } + } catch (error) { + toast({ title: "Error", description: String(error), variant: "destructive" }) + } finally { + setIsLoading(false) + setApproveComment("") + } + } + + // 2) 거부 다이얼로그 표시 + const handleReject = () => { + // 코멘트가 있는데 거부하려고 하면 경고 + if (pendingComments.length > 0) { + if (!confirm('You have unsaved comments. Are you sure you want to reject without requesting changes?')) { + return; + } + } + setShowRejectDialog(true) + } + + // 실제 거부 처리 + const handleSubmitReject = async () => { + try { + setIsLoading(true) + setShowRejectDialog(false) + + const res = await updateVendorStatusAction(vendor.id, "REJECTED") + if (res.ok) { + toast({ title: "Rejected", description: "Vendor PQ has been rejected." }) + // 코멘트 초기화 + setPendingComments([]); + } else { + toast({ title: "Error", description: res.error, variant: "destructive" }) + } + } catch (error) { + toast({ title: "Error", description: String(error), variant: "destructive" }) + } finally { + setIsLoading(false) + setRejectComment("") + } + } + + // 3) 변경 요청 다이얼로그 표시 + const handleRequestChanges = () => { + setShowRequestDialog(true) + } + + // 4) 변경 요청 처리 - 이제 모든 코멘트를 한 번에 저장 +// 4) 변경 요청 처리 - 이제 모든 코멘트를 한 번에 저장 +const handleSubmitRequestChanges = async () => { + try { + setIsLoading(true); + setShowRequestDialog(false); + + // 항목별 코멘트 준비 - answerId와 함께 checkPoint와 code도 전송 + const itemComments = pendingComments.map(pc => ({ + answerId: pc.answerId, + checkPoint: pc.checkPoint, // 추가: 체크포인트 정보 전송 + code: pc.code, // 추가: 코드 정보 전송 + comment: pc.comment + })); + + // 서버 액션 호출 + const res = await requestPqChangesAction({ + vendorId: vendor.id, + comment: itemComments, + generalComment: requestComment || undefined + }); + + if (res.ok) { + toast({ + title: "Changes Requested", + description: "Vendor was notified of your comments.", + }); + // 코멘트 초기화 + setPendingComments([]); + } else { + toast({ title: "Error", description: res.error, variant: "destructive" }); + } + } catch (error) { + toast({ title: "Error", description: String(error), variant: "destructive" }); + } finally { + setIsLoading(false); + setRequestComment(""); + } +}; + + return ( +
+ {/* Top header */} +
+

+ {vendor.vendorCode} - {vendor.vendorName} PQ Review +

+
+ + + +
+
+ +

+ Review the submitted PQ items below, then approve, reject, or request more info. +

+ + {/* 코멘트가 있을 때 알림 표시 */} + {pendingComments.length > 0 && ( +
+

+ ⚠️ + You have {pendingComments.length} pending comments. Click "Request Changes" to save them. +

+
+ )} + + + + {/* VendorPQReviewPage 컴포넌트 대신 직접 구현 */} + + + {/* 변경 요청 다이얼로그 */} + + + + Request PQ Changes + + Review your comments and add any additional notes. The vendor will receive these changes. + + + + {/* 항목별 코멘트 목록 */} + {pendingComments.length > 0 && ( +
+

Item Comments:

+ {pendingComments.map((comment, index) => ( +
+
+
+ {comment.code} + {comment.checkPoint} +
+

{comment.comment}

+

+ {formatDate(comment.createdAt)} +

+
+ +
+ ))} +
+ )} + + {/* 추가 코멘트 입력 */} +
+ +