From c691e816c56bbe0069e7f2da90466b8480f0cd15 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 1 Dec 2025 11:20:08 +0000 Subject: (임수민) 공동인증 테스트 파일 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../basic-contract-sign-dialog-test.tsx | 1166 ++++++++++++++++++++ 1 file changed, 1166 insertions(+) create mode 100644 lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx (limited to 'lib') diff --git a/lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx b/lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx new file mode 100644 index 00000000..ace34454 --- /dev/null +++ b/lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx @@ -0,0 +1,1166 @@ +"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 { useToast } from "@/hooks/use-toast"; +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, + ArrowRight, + Trophy, + Target, + Shield +} 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, processBuyerSignatureAction } from "../service"; + +// 계약서 상태 타입 정의 +interface ContractStatus { + id: number; + status: 'pending' | 'completed' | 'error' | 'vendor_signed'; + errorMessage?: string; +} + +interface BasicContractSignDialogProps { + contracts: BasicContractView[]; + onSuccess?: () => void; + hasSelectedRows?: boolean; + mode?: 'vendor' | 'buyer'; + onBuyerSignComplete?: (contractId: number, signedData: ArrayBuffer) => void; + t: (key: string) => string; + // 외부 상태 제어를 위한 새로운 props (선택적) + open?: boolean; + onOpenChange?: (open: boolean) => void; +} + +export function BasicContractSignDialog({ + contracts, + onSuccess, + hasSelectedRows = false, + mode = 'vendor', + onBuyerSignComplete, + t, + // 새로 추가된 props + open: externalOpen, + onOpenChange: externalOnOpenChange +}: BasicContractSignDialogProps) { + const { toast } = useToast(); + + // 내부 상태 (외부 제어가 없을 때 사용) + const [internalOpen, setInternalOpen] = 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 [contractStatuses, setContractStatuses] = React.useState([]); + + // 서명/설문/GTC 코멘트 완료 상태 관리 + const [surveyCompletionStatus, setSurveyCompletionStatus] = React.useState>({}); + const [signatureStatus, setSignatureStatus] = React.useState>({}); + const [gtcCommentStatus, setGtcCommentStatus] = React.useState>({}); + + const router = useRouter() + + // 실제 사용할 open 상태 (외부 제어가 있으면 외부 상태 사용, 없으면 내부 상태 사용) + const isControlledExternally = externalOpen !== undefined; + const open = isControlledExternally ? externalOpen : internalOpen; + + // 모드에 따른 텍스트 + const isBuyerMode = mode === 'buyer'; + const dialogTitle = isBuyerMode ? "구매자 최종승인 서명" : t("basicContracts.dialog.title"); + const signButtonText = isBuyerMode ? "최종승인 완료" : "서명 완료 및 저장"; + + // 버튼 비활성화 조건 + 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 canCompleteCurrentContract = React.useMemo(() => { + if (!selectedContract) return false; + + const contractId = selectedContract.id; + + // 구매자 모드: 기존 로직 유지 + if (isBuyerMode) { + const signatureCompleted = signatureStatus[contractId] === true; + return signatureCompleted; + } + + // 협력업체 모드 + 내자: TrustNet으로 이동만 하면 되므로 버튼은 항상 활성화 + if (isDomesticVendorForContract(selectedContract)) { + return true; + } + + const isComplianceTemplate = selectedContract.templateName?.includes('준법'); + const isGTCTemplate = selectedContract.templateName?.includes('GTC'); + const requiresNegotiationComplete = isComplianceTemplate || isGTCTemplate; + + const surveyCompleted = isComplianceTemplate ? surveyCompletionStatus[contractId] === true : true; + + const negotiationStatus = gtcCommentStatus[contractId]; + const negotiationCleared = requiresNegotiationComplete + ? (!negotiationStatus?.hasComments || negotiationStatus?.isComplete === true) + : true; + + const signatureCompleted = signatureStatus[contractId] === true; + + return surveyCompleted && negotiationCleared && signatureCompleted; +}, [selectedContract, surveyCompletionStatus, signatureStatus, gtcCommentStatus, isBuyerMode]); + + + // 계약서별 상태 초기화 + // Vendor signed 상태의 계약서도 포함하여 초기화 + React.useEffect(() => { + if (contracts.length > 0 && contractStatuses.length === 0) { + setContractStatuses( + contracts.map(contract => { + // 이미 서명된 계약서는 vendor_signed 상태로 초기화 + const isSigned = contract.vendorSignedAt || + contract.status === "COMPLETED" || + contract.status === "VENDOR_SIGNED"; + return { + id: contract.id, + status: isSigned ? ('vendor_signed' as const) : ('pending' as const) + }; + }) + ); + } + }, [contracts, contractStatuses.length]); + + // 완료된 계약서 수 계산 + const completedCount = contractStatuses.filter(status => + status.status === 'completed' || status.status === 'vendor_signed' + ).length; + const totalCount = contracts.length; + const allCompleted = completedCount === totalCount && totalCount > 0; + + // 현재 선택된 계약서의 상태 + const currentContractStatus = selectedContract + ? contractStatuses.find(status => status.id === selectedContract.id) + : null; + + // 내자/외자 판별 (vendors.country 기반, KR 앞 2자리) + const getVendorCountryCode = React.useCallback((contract: BasicContractView | null) => { + if (!contract || !contract.vendorCountry) return null; + return contract.vendorCountry.substring(0, 2).toUpperCase(); + }, []); + + const isDomesticVendorForContract = React.useCallback((contract: BasicContractView | null) => { + if (!contract) return false; + const countryCode = getVendorCountryCode(contract); + return countryCode === "KR"; + }, [getVendorCountryCode]); + + const isDomesticVendor = React.useMemo(() => { + return isDomesticVendorForContract(selectedContract); + }, [selectedContract, isDomesticVendorForContract]); + + // 다음 미완료 계약서 찾기 + const getNextPendingContract = () => { + const pendingStatuses = contractStatuses.filter(status => + status.status === 'pending' || status.status === 'error' + ); + if (pendingStatuses.length === 0) return null; + + const nextPendingId = pendingStatuses[0].id; + return contracts.find(contract => contract.id === nextPendingId) || null; + }; + + // 다이얼로그 열기/닫기 핸들러 (외부 제어 지원) + const handleOpenChange = (isOpen: boolean) => { + if (!isOpen && !allCompleted && completedCount > 0) { + // 완료되지 않은 계약서가 있으면 확인 대화상자 + // const confirmClose = window.confirm( + // `${completedCount}/${totalCount}개 계약서가 완료되었습니다. 정말 나가시겠습니까?` + // ); + const confirmClose = window.confirm( + `정말 나가시겠습니까?` + ); + if (!confirmClose) return; + } + + // 외부 제어가 있으면 외부 콜백 호출, 없으면 내부 상태 업데이트 + if (isControlledExternally && externalOnOpenChange) { + externalOnOpenChange(isOpen); + } else { + setInternalOpen(isOpen); + } + + if (!isOpen) { + // 다이얼로그 닫을 때 상태 초기화 + setSelectedContract(null); + setSearchTerm(""); + setAdditionalFiles([]); + setContractStatuses([]); + setSurveyCompletionStatus({}); + setSignatureStatus({}); + setGtcCommentStatus({}); + // 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) { + const firstPending = getNextPendingContract(); + if (firstPending) { + setSelectedContract(firstPending); + } else { + setSelectedContract(contracts[0]); + } + } + }, [open, contracts, selectedContract, contractStatuses]); + + // 추가 파일 가져오기 useEffect (구매자 모드에서는 스킵) + React.useEffect(() => { + if (isBuyerMode) { + setAdditionalFiles([]); + return; + } + + const fetchAdditionalFiles = async () => { + if (!selectedContract) { + setAdditionalFiles([]); + return; + } + + // "비밀유지 계약서"인 경우에만 추가 파일 가져오기 + if (selectedContract.templateName === "비밀유지 계약서" && selectedContract.vendorId) { + 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, isBuyerMode]); + + // 설문조사 완료 콜백 함수 + const handleSurveyComplete = React.useCallback((contractId: number) => { + console.log(`📋 설문조사 완료: 계약서 ${contractId}`); + setSurveyCompletionStatus(prev => ({ + ...prev, + [contractId]: true + })); + }, []); + + // 서명 완료 콜백 함수 + const handleSignatureComplete = React.useCallback((contractId: number) => { + console.log(`✍️ 서명 완료: 계약서 ${contractId}`); + setSignatureStatus(prev => ({ + ...prev, + [contractId]: true + })); + }, []); + + // GTC 코멘트 상태 변경 콜백 함수 + const handleGtcCommentStatusChange = React.useCallback(( + contractId: number, + hasComments: boolean, + commentCount: number, + reviewStatus?: string, + isComplete?: boolean + ) => { + console.log(`📋 GTC 상태 변경: 계약서 ${contractId}, 코멘트 ${commentCount}개, 상태: ${reviewStatus}, 완료: ${isComplete}`); + setGtcCommentStatus(prev => ({ + ...prev, + [contractId]: { hasComments, commentCount, reviewStatus, isComplete } + })); + }, []); + + // 서명 완료 핸들러 + const completeSign = async () => { + if (!selectedContract) return; + + // 협력업체 모드 + 내자일 때는 TrustNet으로 이동 (기존 PDF 서명 로직 사용 안 함) + if (!isBuyerMode && isDomesticVendor) { + try { + const baseUrl = "https://partners.sevcp.com/trustnet/contract-req.html"; + const origin = typeof window !== "undefined" ? window.location.origin : ""; + const url = `${baseUrl}?contractId=${encodeURIComponent( + String(selectedContract.id) + )}&origin=${encodeURIComponent(origin)}`; + + window.open(url, "_blank", "noopener,noreferrer"); + + toast({ + title: "공동인증서 서명 페이지가 새 창으로 열렸습니다.", + description: "TrustNet 화면에서 전자서명을 완료해주세요.", + }); + } catch (error) { + console.error("TrustNet 서명 페이지 열기 실패:", error); + toast({ + title: "TrustNet 서명 페이지를 여는 중 오류가 발생했습니다.", + variant: "destructive", + }); + } + + return; + } + + if (!instance) return; + + // 서명 완료 가능 여부 재확인 + if (!canCompleteCurrentContract) { + const contractId = selectedContract.id; + + if (isBuyerMode) { + const signatureCompleted = signatureStatus[contractId] === true; + + if (!signatureCompleted) { + toast({ + title: "계약서에 서명을 먼저 완료해주세요.", + description: "문서의 서명 필드에 서명해주세요.", + variant: "destructive" + }); + return; + } + } else { + // 협력업체 모드의 기존 검증 로직 + const isComplianceTemplate = selectedContract.templateName?.includes('준법'); + const isGTCTemplate = selectedContract.templateName?.includes('GTC'); + const surveyCompleted = isComplianceTemplate ? surveyCompletionStatus[contractId] === true : true; + const requiresNegotiationComplete = isComplianceTemplate || isGTCTemplate; + const negotiationStatus = gtcCommentStatus[contractId]; + const negotiationCleared = requiresNegotiationComplete + ? (!negotiationStatus?.hasComments || negotiationStatus?.isComplete === true) + : true; + const signatureCompleted = signatureStatus[contractId] === true; + + if (!surveyCompleted) { + toast({ + title: "준법 설문조사를 먼저 완료해주세요.", + description: "설문조사 탭에서 모든 필수 항목을 완료해주세요.", + variant: "destructive" + }); + return; + } + + if (!negotiationCleared) { + toast({ + title: "코멘트가 있어 서명할 수 없습니다.", + description: "협의 코멘트 탭에서 모든 코멘트를 삭제하거나 협의를 완료해주세요.", + variant: "destructive" + }); + return; + } + + if (!signatureCompleted) { + toast({ + title: "계약서에 서명을 먼저 완료해주세요.", + description: "문서의 서명 필드에 서명해주세요.", + variant: "destructive" + }); + return; + } + } + + 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", + }); + + if (isBuyerMode) { + // 구매자 모드: 최종승인 처리 + const result = await processBuyerSignatureAction( + selectedContract.id, + data, + selectedContract.signedFileName || `contract_${selectedContract.id}_buyer_signed.pdf` + ); + + if (result.success) { + // 성공시 해당 계약서 상태를 완료로 업데이트 + setContractStatuses(prev => + prev.map(status => + status.id === selectedContract.id + ? { ...status, status: 'completed' as const } + : status + ) + ); + + toast({ + title: "최종승인이 완료되었습니다!", + description: `${selectedContract.templateName} - ${completedCount + 1}/${totalCount}개 완료` + }); + + // 구매자 서명 완료 콜백 호출 + if (onBuyerSignComplete) { + onBuyerSignComplete(selectedContract.id, data); + } + + // 다음 미완료 계약서로 자동 이동 + const nextContract = getNextPendingContract(); + if (nextContract) { + setSelectedContract(nextContract); + } else { + // 모든 계약서 완료시 + toast({ + title: "🎉 모든 계약서 최종승인이 완료되었습니다!", + description: `총 ${totalCount}개 계약서 승인 완료` + }); + } + + router.refresh(); + } else { + // 실패시 에러 상태 업데이트 + setContractStatuses(prev => + prev.map(status => + status.id === selectedContract.id + ? { ...status, status: 'error' as const, errorMessage: result.message } + : status + ) + ); + + toast({ + title: "최종승인 처리 중 오류가 발생했습니다", + description: result.message, + variant: "destructive" + }); + } + } else { + // 협력업체 모드: 기존 로직 + 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)); + } + + // 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) { + // 성공시 해당 계약서 상태를 완료로 업데이트 + setContractStatuses(prev => + prev.map(status => + status.id === selectedContract.id + ? { ...status, status: 'completed' as const } + : status + ) + ); + + toast({ + title: "계약서 서명이 완료되었습니다!", + description: `${selectedContract.templateName} - ${completedCount + 1}/${totalCount}개 완료` + }); + + // 다음 미완료 계약서로 자동 이동 + const nextContract = getNextPendingContract(); + if (nextContract) { + setSelectedContract(nextContract); + } else { + // 모든 계약서 완료시 + toast({ + title: "🎉 모든 계약서 서명이 완료되었습니다!", + description: `총 ${totalCount}개 계약서 서명 완료` + }); + } + + router.refresh(); + } else { + // 실패시 에러 상태 업데이트 + setContractStatuses(prev => + prev.map(status => + status.id === selectedContract.id + ? { ...status, status: 'error' as const, errorMessage: result.error } + : status + ) + ); + + toast({ + title: "서명 처리 중 오류가 발생했습니다", + description: result.error, + variant: "destructive" + }); + } + } + } catch (error) { + console.error("서명 완료 중 오류:", error); + + // 에러 상태 업데이트 + setContractStatuses(prev => + prev.map(status => + status.id === selectedContract.id + ? { ...status, status: 'error' as const, errorMessage: '서명 처리 중 오류가 발생했습니다' } + : status + ) + ); + + toast({ + title: "서명 처리 중 오류가 발생했습니다", + variant: "destructive" + }); + } finally { + setIsSubmitting(false); + } + }; + + // 모든 서명 완료 핸들러 + const completeAllSigns = () => { + handleOpenChange(false); + if (onSuccess) { + onSuccess(); + } + const successMessage = isBuyerMode + ? "모든 계약서 최종승인이 완료되었습니다!" + : "모든 계약서 서명이 완료되었습니다!"; + + toast({ + title: successMessage, + description: "계약서 관리 페이지가 새고침됩니다." + }); + }; + + return ( + <> + {/* 서명 버튼 - 외부 제어가 없을 때만 표시 */} + {!isControlledExternally && ( + + )} + + {/* 서명 다이얼로그 */} + + + {/* 고정 헤더 - 진행 상황 표시 */} + + +
+ {isBuyerMode ? ( + + ) : ( + + )} + {dialogTitle} + {/* 진행 상황 표시 */} + {/* + {completedCount}/{totalCount} 완료 + */} + {/* 추가 파일 로딩 표시 */} + {isLoadingAttachments && ( + + )} +
+ + {allCompleted && ( + + + 전체 완료! + + )} +
+ + {/* 진행률 바 */} + {totalCount > 1 && ( +
+
+ 전체 진행률 + {Math.round((completedCount / totalCount) * 100)}% +
+
+
+
+
+ )} + + + {/* 메인 컨텐츠 영역 - Flexbox 사용 */} +
+ {/* 왼쪽 영역 - 계약서 목록 (고정 너비) */} +
+
+
+
+ +
+ setSearchTerm(e.target.value)} + /> +
+
+ + +
+ {filteredContracts.length === 0 ? ( +
+ +

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

+
+ ) : ( +
+ {filteredContracts.map((contract) => { + const contractStatus = contractStatuses.find(status => status.id === contract.id); + const isCompleted = contractStatus?.status === 'completed' || contractStatus?.status === 'vendor_signed'; + const hasError = contractStatus?.status === 'error'; + + // 계약서별 완료 상태 확인 + const isComplianceTemplate = contract.templateName?.includes('준법'); + const isGTCTemplate = contract.templateName?.includes('GTC'); + const requiresNegotiation = isComplianceTemplate || isGTCTemplate; + const hasSurveyCompleted = isComplianceTemplate ? surveyCompletionStatus[contract.id] === true : true; + const negotiationStatus = gtcCommentStatus[contract.id]; + const hasNegotiationCompleted = requiresNegotiation + ? (!negotiationStatus?.hasComments || negotiationStatus?.isComplete === true) + : true; + const hasSignatureCompleted = signatureStatus[contract.id] === true; + + return ( + + ); + })} +
+ )} +
+
+
+ + {/* 오른쪽 영역 - 문서 뷰어 (확장 가능) */} +
+ {selectedContract ? ( + <> + {/* 뷰어 헤더 */} +
+

+ {isBuyerMode ? ( + + ) : ( + + )} + {selectedContract.templateName || t("basicContracts.dialog.document")} + + {/* 현재 계약서 상태 표시 */} + {(currentContractStatus?.status === 'completed' || currentContractStatus?.status === 'vendor_signed') ? ( + + + {isBuyerMode ? "승인 완료" : "서명 완료"} + + ) : currentContractStatus?.status === 'error' ? ( + + + 처리 실패 + + ) : ( + + {isBuyerMode ? "승인 대기" : "서명 대기"} + + )} + + {/* 구매자 모드 배지 */} + {isBuyerMode && ( + + 구매자 모드 + + )} + + {/* 준법/GTC 템플릿 표시 (구매자 모드가 아닐 때만) */} + {!isBuyerMode && selectedContract.templateName?.includes('준법') && ( + + 준법 서류 + + )} + + {!isBuyerMode && selectedContract.templateName?.includes('GTC') && ( + + GTC 계약서 + + )} + + {/* 비밀유지 계약서인 경우 추가 파일 수 표시 (구매자 모드가 아닐 때만) */} + {!isBuyerMode && selectedContract.templateName === "비밀유지 계약서" && additionalFiles.length > 0 && ( + + 첨부파일 {additionalFiles.length}개 + + )} +

+
+ + + {t("basicContracts.dialog.requester")}: {selectedContract.requestedByName || t("basicContracts.dialog.unknown")} + + + + {formatDate(selectedContract.createdAt)} + +
+
+ + {/* 뷰어 영역 - 남은 공간 모두 사용 */} +
+ handleSurveyComplete(selectedContract.id)} + onSignatureComplete={() => handleSignatureComplete(selectedContract.id)} + onGtcCommentStatusChange={(hasComments, commentCount, reviewStatus, isComplete) => + handleGtcCommentStatusChange(selectedContract.id, hasComments, commentCount, reviewStatus, isComplete) + } + mode={mode} + t={t} + negotiationCompletedAt={(selectedContract as any).negotiationCompletedAt || null} + /> +
+ + {/* 고정 푸터 - 동적 버튼 */} +
+
+ {/* 현재 계약서가 완료된 경우 */} + {currentContractStatus?.status === 'completed' || currentContractStatus?.status === 'vendor_signed' ? ( +

+ + 이 계약서는 이미 {isBuyerMode ? "승인이" : "서명이"} 완료되었습니다. 코멘트를 확인할 수 있습니다. +

+ ) : currentContractStatus?.status === 'error' ? ( +

+ + {isBuyerMode ? "승인" : "서명"} 처리 중 오류가 발생했습니다. 다시 시도해주세요. +

+ ) : ( + <> + {/* 완료 조건 안내 메시지 */} +
+

+ + {isBuyerMode + ? "계약서에 구매자 서명을 완료해주세요." + : t("basicContracts.dialog.signWarning") + } +

+ + {/* 완료 상태 체크리스트 */} + {!isBuyerMode && ( +
+ {selectedContract.templateName?.includes('준법') && ( + + + 설문조사 {surveyCompletionStatus[selectedContract.id] ? '완료' : '미완료'} + + )} + {(selectedContract.templateName?.includes('GTC') || selectedContract.templateName?.includes('준법')) && ( + + + 협의 {(!gtcCommentStatus[selectedContract.id]?.hasComments || gtcCommentStatus[selectedContract.id]?.isComplete === true) + ? '완료' + : `미완료 (코멘트 ${gtcCommentStatus[selectedContract.id]?.commentCount || 0}개)`} + + )} + + + 서명 {signatureStatus[selectedContract.id] ? '완료' : '미완료'} + +
+ )} + + {/* 구매자 모드의 간소화된 체크리스트 */} + {isBuyerMode && ( +
+ + + 구매자 서명 {signatureStatus[selectedContract.id] ? '완료' : '미완료'} + +
+ )} +
+ + )} +
+ + {/* 동적 버튼 영역 */} +
+ {allCompleted ? ( + // 모든 계약서 완료시 + + ) : (currentContractStatus?.status === 'completed' || currentContractStatus?.status === 'vendor_signed') ? ( + // 현재 계약서가 완료된 경우 - 코멘트 확인만 가능 +
+ +

+ 서명 완료 - 코멘트 확인 가능 +

+
+ ) : ( + // 현재 계약서를 서명해야 하는 경우 + + )} +
+
+ + ) : ( +
+
+ {isBuyerMode ? ( + + ) : ( + + )} +
+

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

+

+ {isBuyerMode + ? "승인할 계약서를 선택해주세요." + : t("basicContracts.dialog.selectDocumentDescription") + } +

+
+ )} +
+
+ +
+ + ); +} \ No newline at end of file -- cgit v1.2.3