"use client"; import * as React from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { ScrollArea } from "@/components/ui/scroll-area"; import { formatDate } from "@/lib/utils"; import { toast } from "sonner"; import { cn } from "@/lib/utils"; import type { WebViewerInstance } from "@pdftron/webviewer"; import type { BasicContractView } from "@/db/schema"; import { Upload, FileSignature, CheckCircle2, Search, Clock, FileText, User, AlertCircle, Calendar, Loader2 } from "lucide-react"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { useRouter } from "next/navigation" import { BasicContractSignViewer } from "../viewer/basic-contract-sign-viewer"; import { getVendorAttachments } from "../service"; interface BasicContractSignDialogProps { contracts: BasicContractView[]; onSuccess?: () => void; hasSelectedRows?: boolean; t: (key: string) => string; } export function BasicContractSignDialog({ contracts, onSuccess, hasSelectedRows = false, t }: BasicContractSignDialogProps) { const [open, setOpen] = React.useState(false); const [selectedContract, setSelectedContract] = React.useState(null); const [instance, setInstance] = React.useState(null); const [searchTerm, setSearchTerm] = React.useState(""); const [isSubmitting, setIsSubmitting] = React.useState(false); // 추가된 state들 const [additionalFiles, setAdditionalFiles] = React.useState([]); const [isLoadingAttachments, setIsLoadingAttachments] = React.useState(false); const router = useRouter() console.log(selectedContract,"selectedContract") console.log(additionalFiles,"additionalFiles") // 버튼 비활성화 조건 const isButtonDisabled = !hasSelectedRows || contracts.length === 0; // 비활성화 이유 텍스트 const getDisabledReason = () => { if (!hasSelectedRows) { return t("basicContracts.toolbar.selectRows"); } if (contracts.length === 0) { return t("basicContracts.toolbar.noPendingContracts"); } return ""; }; // 다이얼로그 열기/닫기 핸들러 const handleOpenChange = (isOpen: boolean) => { setOpen(isOpen); if (!isOpen) { // 다이얼로그 닫을 때 상태 초기화 setSelectedContract(null); setSearchTerm(""); setAdditionalFiles([]); // 추가 파일 상태 초기화 // WebViewer 인스턴스 정리 if (instance) { try { instance.UI.dispose(); } catch (error) { console.log("WebViewer dispose error:", error); } setInstance(null); } } }; // 계약서 선택 핸들러 const handleSelectContract = (contract: BasicContractView) => { console.log("계약서 선택:", contract.id, contract.templateName); setSelectedContract(contract); }; // 검색된 계약서 필터링 const filteredContracts = React.useMemo(() => { if (!searchTerm.trim()) return contracts; const term = searchTerm.toLowerCase(); return contracts.filter(contract => (contract.templateName || '').toLowerCase().includes(term) || (contract.requestedByName || '').toLowerCase().includes(term) ); }, [contracts, searchTerm]); // 다이얼로그가 열릴 때 첫 번째 계약서 자동 선택 React.useEffect(() => { if (open && contracts.length > 0 && !selectedContract) { setSelectedContract(contracts[0]); } }, [open, contracts, selectedContract]); // 추가 파일 가져오기 useEffect React.useEffect(() => { const fetchAdditionalFiles = async () => { if (!selectedContract) { setAdditionalFiles([]); return; } // "비밀유지 계약서"인 경우에만 추가 파일 가져오기 if (selectedContract.templateName === "비밀유지 계약서") { setIsLoadingAttachments(true); try { const result = await getVendorAttachments(selectedContract.vendorId); if (result.success) { setAdditionalFiles(result.data); console.log("추가 파일 로드됨:", result.data); } else { console.error("Failed to fetch attachments:", result.error); setAdditionalFiles([]); } } catch (error) { console.error("Error fetching attachments:", error); setAdditionalFiles([]); } finally { setIsLoadingAttachments(false); } } else { setAdditionalFiles([]); } }; fetchAdditionalFiles(); }, [selectedContract]); // 서명 완료 핸들러 const completeSign = async () => { if (!instance || !selectedContract) return; setIsSubmitting(true); try { const { documentViewer, annotationManager } = instance.Core; const doc = documentViewer.getDocument(); const xfdfString = await annotationManager.exportAnnotations(); // 폼 필드 데이터 수집 const fieldManager = annotationManager.getFieldManager(); const fields = fieldManager.getFields(); const formData: any = {}; fields.forEach((field: any) => { formData[field.name] = field.value; }); const data = await doc.getFileData({ xfdfString, downloadType: "pdf", }); // FormData 생성 및 파일 추가 const submitFormData = new FormData(); submitFormData.append('file', new Blob([data], { type: 'application/pdf' })); submitFormData.append('tableRowId', selectedContract.id.toString()); submitFormData.append('templateName', selectedContract.signedFileName || ''); // 폼 필드 데이터 추가 if (Object.keys(formData).length > 0) { submitFormData.append('formData', JSON.stringify(formData)); } // 준법 템플릿인 경우 필수 필드 검증 if (selectedContract.templateName?.includes('준법')) { const requiredFields = ['compliance_agreement', 'legal_review', 'risk_assessment']; const missingFields = requiredFields.filter(field => !formData[field]); if (missingFields.length > 0) { toast.error("필수 준법 항목이 누락되었습니다.", { description: `다음 항목을 완료해주세요: ${missingFields.join(', ')}`, icon: }); setIsSubmitting(false); return; } } // API 호출 const response = await fetch('/api/upload/signed-contract', { method: 'POST', body: submitFormData, next: { tags: ["basicContractView-vendor"] }, }); const result = await response.json(); if (result.result) { toast.success(t("basicContracts.messages.signSuccess"), { description: t("basicContracts.messages.documentProcessed"), icon: }); router.refresh(); setOpen(false); if (onSuccess) { onSuccess(); } } else { toast.error(t("basicContracts.messages.signError"), { description: result.error, icon: }); } } catch (error) { console.error("서명 완료 중 오류:", error); toast.error(t("basicContracts.messages.signError")); } finally { setIsSubmitting(false); } }; return ( <> {/* 서명 버튼 */} {/* 서명 다이얼로그 - 레이아웃 개선 */} {/* 고정 헤더 */} {t("basicContracts.dialog.title")} {/* 추가 파일 로딩 표시 */} {isLoadingAttachments && ( )} {/* 메인 컨텐츠 영역 - Flexbox 사용 */}
{/* 왼쪽 영역 - 계약서 목록 (고정 너비) */}
setSearchTerm(e.target.value)} />
{filteredContracts.length === 0 ? (

{t("basicContracts.dialog.noDocuments")}

) : (
{filteredContracts.map((contract) => ( ))}
)}
{/* 오른쪽 영역 - 문서 뷰어 (확장 가능) */}
{selectedContract ? ( <> {/* 뷰어 헤더 */}

{selectedContract.templateName || t("basicContracts.dialog.document")} {/* 준법 템플릿 표시 */} {selectedContract.templateName?.includes('준법') && ( 준법 서류 )} {/* 비밀유지 계약서인 경우 추가 파일 수 표시 */} {selectedContract.templateName === "비밀유지 계약서" && additionalFiles.length > 0 && ( 첨부파일 {additionalFiles.length}개 )}

{t("basicContracts.dialog.requester")}: {selectedContract.requestedByName || t("basicContracts.dialog.unknown")} {formatDate(selectedContract.createdAt)}
{/* 뷰어 영역 - 남은 공간 모두 사용 */}
{/* 고정 푸터 */}

{t("basicContracts.dialog.signWarning")}

{/* 준법 템플릿인 경우 추가 안내 */} {selectedContract.templateName?.includes('준법') && (

모든 준법 항목을 체크해주세요

)} {/* 비밀유지 계약서인 경우 추가 안내 */} {selectedContract.templateName === "비밀유지 계약서" && additionalFiles.length > 0 && (

첨부 서류도 확인해주세요

)}
) : (

{t("basicContracts.dialog.selectDocument")}

{t("basicContracts.dialog.selectDocumentDescription")}

)}
); }