"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, updateProjectPQStatusAction, getItemReviewLogsAction } from "@/lib/pq/service" import { Vendor } from "@/db/schema/vendors" import { Separator } from "@/components/ui/separator" import { Badge } from "@/components/ui/badge" 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" import { useSession } from "next-auth/react" // Importando o hook do next-auth // 코멘트 상태를 위한 인터페이스 정의 interface PendingComment { answerId: number; checkPoint: string; code: string; comment: string; createdAt: Date; } interface ReviewLog { id: number reviewerComment: string reviewerName: string | null createdAt: Date } // Updated props interface to support both general and project PQs interface VendorPQAdminReviewProps { data: PQGroupData[] vendor: Vendor projectId?: number projectName?: string projectStatus?: string // loadData: () => Promise loadData: (vendorId: number, projectId?: number) => Promise pqType: 'general' | 'project' } export default function VendorPQAdminReview({ data, vendor, projectId, projectName, projectStatus, loadData, pqType }: VendorPQAdminReviewProps) { const { toast } = useToast() const { data: session } = useSession() const reviewerName = session?.user?.name || "Unknown Reviewer" const reviewerId = session?.user?.id // State for dynamically loaded data const [pqData, setPqData] = React.useState(data) const [isDataLoading, setIsDataLoading] = React.useState(false) // Load data if not provided initially (for tab switching) React.useEffect(() => { if (data.length === 0) { const fetchData = async () => { setIsDataLoading(true) try { const freshData = await loadData(vendor.id, projectId) setPqData(freshData) } catch (error) { console.error("Error loading PQ data:", error) toast({ title: "Error", description: "Failed to load PQ data", variant: "destructive" }) } finally { setIsDataLoading(false) } } fetchData() } else { setPqData(data) } }, [data, loadData, vendor.id, projectId, toast]) // 다이얼로그 상태들 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) } // 실제 승인 처리 - 일반 PQ와 프로젝트 PQ 분리 const handleSubmitApprove = async () => { try { setIsLoading(true) setShowApproveDialog(false) let res; if (pqType === 'general') { // 일반 PQ 승인 res = await updateVendorStatusAction(vendor.id, "PQ_APPROVED") } else if (projectId) { // 프로젝트 PQ 승인 res = await updateProjectPQStatusAction({ vendorId: vendor.id, projectId, status: "APPROVED", comment: approveComment.trim() || undefined }) } if (res?.ok) { toast({ title: "Approved", description: `${pqType === 'general' ? 'General' : 'Project'} PQ has been approved.` }) // 코멘트 초기화 setPendingComments([]); } else { toast({ title: "Error", description: res?.error || "An error occurred", 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) } // 실제 거부 처리 - 일반 PQ와 프로젝트 PQ 분리 const handleSubmitReject = async () => { try { setIsLoading(true) setShowRejectDialog(false) if (!rejectComment.trim()) { toast({ title: "Error", description: "Please provide a reason for rejection", variant: "destructive" }) return; } let res; if (pqType === 'general') { // 일반 PQ 거부 res = await updateVendorStatusAction(vendor.id, "REJECTED") } else if (projectId) { // 프로젝트 PQ 거부 res = await updateProjectPQStatusAction({ vendorId: vendor.id, projectId, status: "REJECTED", comment: rejectComment }) } if (res?.ok) { toast({ title: "Rejected", description: `${pqType === 'general' ? 'General' : 'Project'} PQ has been rejected.` }) // 코멘트 초기화 setPendingComments([]); } else { toast({ title: "Error", description: res?.error || "An error occurred", variant: "destructive" }) } } catch (error) { toast({ title: "Error", description: String(error), variant: "destructive" }) } finally { setIsLoading(false) setRejectComment("") } } // 3) 변경 요청 다이얼로그 표시 const handleRequestChanges = () => { setShowRequestDialog(true) } // 4) 변경 요청 처리 - 이제 프로젝트 ID 포함 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 })); // 서버 액션 호출 (프로젝트 ID 추가) const res = await requestPqChangesAction({ vendorId: vendor.id, projectId: pqType === 'project' ? projectId : undefined, comment: itemComments, generalComment: requestComment || undefined, reviewerName, reviewerId }); if (res.ok) { toast({ title: "Changes Requested", description: `${pqType === 'general' ? 'Vendor' : 'Project'} 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(""); } }; // 현재 상태에 따른 액션 버튼 비활성화 여부 판단 const getDisabledState = () => { if (pqType === 'general') { // 일반 PQ는 vendor 상태에 따라 결정 return vendor.status === 'PQ_APPROVED' || vendor.status === 'APPROVED'; } else if (pqType === 'project' && projectStatus) { // 프로젝트 PQ는 project 상태에 따라 결정 return projectStatus === 'APPROVED' || projectStatus === 'REJECTED'; } return false; }; const areActionsDisabled = getDisabledState(); return (
{/* PQ Type indicators and status */} {pqType === 'project' && projectName && (
{projectName} {projectStatus && ( {projectStatus} )}
{areActionsDisabled && (

This PQ has already been { pqType !== 'project' ? (vendor.status === 'PQ_APPROVED' || vendor.status === 'APPROVED' ? 'approved' : 'rejected') : (projectStatus === 'APPROVED' ? 'approved' : 'rejected') }. No further actions can be taken.

)}
)} {/* Loading indicator */} {isDataLoading && (
)} {!isDataLoading && ( <> {/* Top header */}

{vendor.vendorCode} - {vendor.vendorName} {pqType === 'project' ? 'Project' : 'General'} 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.

)} {/* PQ 데이터 표시 */} {pqData.length > 0 ? ( ) : (

No PQ data available for review.

)} )} {/* 변경 요청 다이얼로그 */} 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, "KR")}

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