From ea8aed1e1d62fb9fa6716347de73e4ef13040929 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 9 Dec 2025 05:35:23 +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 deletions(-) delete mode 100644 lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx (limited to 'lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx') 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 deleted file mode 100644 index ace34454..00000000 --- a/lib/basic-contract/vendor-table/basic-contract-sign-dialog-test.tsx +++ /dev/null @@ -1,1166 +0,0 @@ -"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