"use client" import { type Table } from "@tanstack/react-table" import { toast } from "sonner" import { Button } from "@/components/ui/button" import { Mail, FileWarning, Scale, FileText } from "lucide-react" import type { VendorRegularRegistration } from "@/config/vendorRegularRegistrationsColumnsConfig" import { sendMissingContractRequestEmails, sendAdditionalInfoRequestEmails, skipLegalReview } from "../service" import { useState } from "react" import { SkipReasonDialog } from "@/components/vendor-regular-registrations/skip-reason-dialog" import { RegistrationRequestDialog } from "@/components/vendor-regular-registrations/registration-request-dialog" import { ApprovalPreviewDialog } from "@/lib/approval/approval-preview-dialog" import { useRouter } from "next/navigation" import { useSession } from "next-auth/react" import { registerVendorWithApproval } from "../approval-actions" import { mapRegistrationToTemplateVariables } from "../handlers" import type { RegistrationRequestData } from "@/components/vendor-regular-registrations/registration-request-dialog" interface VendorRegularRegistrationsTableToolbarActionsProps { table: Table } export function VendorRegularRegistrationsTableToolbarActions({ table, }: VendorRegularRegistrationsTableToolbarActionsProps) { const router = useRouter() const { data: session } = useSession() const [syncLoading, setSyncLoading] = useState<{ missingContract: boolean; additionalInfo: boolean; legalSkip: boolean; registrationRequest: boolean; }>({ missingContract: false, additionalInfo: false, legalSkip: false, registrationRequest: false, }) const [skipDialogs, setSkipDialogs] = useState<{ legalReview: boolean; }>({ legalReview: false, }) // 2-step 결재 프로세스를 위한 상태 const [registrationRequestDialog, setRegistrationRequestDialog] = useState<{ open: boolean; registration: VendorRegularRegistration | null; }>({ open: false, registration: null, }) const [approvalDialog, setApprovalDialog] = useState<{ open: boolean; registration: VendorRegularRegistration | null; }>({ open: false, registration: null, }) // 결재를 위한 중간 상태 저장 const [registrationFormData, setRegistrationFormData] = useState(null) const [approvalVariables, setApprovalVariables] = useState>({}) const selectedRows = table.getFilteredSelectedRowModel().rows.map(row => row.original) const handleSendMissingContractRequest = async () => { if (selectedRows.length === 0) { toast.error("이메일을 발송할 업체를 선택해주세요.") return } setSyncLoading(prev => ({ ...prev, missingContract: true })) try { const vendorIds = selectedRows.map(row => row.vendorId) const result = await sendMissingContractRequestEmails(vendorIds) if (result.success) { toast.success(result.message) } else { toast.error(result.error) } } catch (error) { console.error("Error sending missing contract request:", error) toast.error("누락계약요청 이메일 발송 중 오류가 발생했습니다.") } finally { setSyncLoading(prev => ({ ...prev, missingContract: false })) } } const handleSendAdditionalInfoRequest = async () => { if (selectedRows.length === 0) { toast.error("이메일을 발송할 업체를 선택해주세요.") return } setSyncLoading(prev => ({ ...prev, additionalInfo: true })) try { const vendorIds = selectedRows.map(row => row.vendorId) const result = await sendAdditionalInfoRequestEmails(vendorIds) if (result.success) { toast.success(result.message) } else { toast.error(result.error) } } catch (error) { console.error("Error sending additional info request:", error) toast.error("추가정보요청 이메일 발송 중 오류가 발생했습니다.") } finally { setSyncLoading(prev => ({ ...prev, additionalInfo: false })) } } const handleLegalReviewSkip = async (reason: string) => { const cpReviewRows = selectedRows.filter(row => row.status === "cp_review"); if (cpReviewRows.length === 0) { toast.error("CP검토 상태인 업체를 선택해주세요."); return; } setSyncLoading(prev => ({ ...prev, legalSkip: true })); try { const vendorIds = cpReviewRows.map(row => row.vendorId); const result = await skipLegalReview(vendorIds, reason); if (result.success) { toast.success(result.message); router.refresh(); } else { toast.error(result.error); } } catch (error) { console.error("Error skipping legal review:", error); toast.error("법무검토 Skip 처리 중 오류가 발생했습니다."); } finally { setSyncLoading(prev => ({ ...prev, legalSkip: false })); } }; // 등록요청 핸들러 - Step 1: 정보 입력 const handleRegistrationRequest = () => { // 모든 항목이 완료된 행 필터링 const eligibleRows = selectedRows.filter(row => isAllItemsComplete(row)); if (eligibleRows.length === 0) { toast.error("모든 항목이 완료된 벤더를 선택해주세요."); return; } if (eligibleRows.length > 1) { toast.error("정규업체 등록 요청은 한 번에 하나씩만 가능합니다."); return; } setRegistrationRequestDialog({ open: true, registration: eligibleRows[0], }); }; // 등록요청 정보 입력 완료 - Step 1에서 Step 2로 전환 const handleRegistrationRequestSubmit = async (requestData: RegistrationRequestData) => { if (!registrationRequestDialog.registration || !session?.user) return; try { // 폼 데이터 저장 setRegistrationFormData(requestData); // 결재 템플릿 변수 생성 (vendorId 포함) const requestedAt = new Date(); const variables = await mapRegistrationToTemplateVariables({ requestData, requestedAt, vendorId: registrationRequestDialog.registration.vendorId, // vendors 테이블에서 추가 정보 가져오기 }); setApprovalVariables(variables); // RegistrationRequestDialog 닫고 ApprovalPreviewDialog 열기 setRegistrationRequestDialog({ open: false, registration: null }); setApprovalDialog({ open: true, registration: registrationRequestDialog.registration, }); } catch (error) { console.error("결재 준비 중 오류 발생:", error); toast.error("결재 준비 중 오류가 발생했습니다."); } }; // 결재 상신 - Step 2: 결재선 선택 후 최종 상신 const handleApprovalSubmit = async ({ approvers, title, attachments }: { approvers: string[], title: string, attachments?: File[] }) => { if (!approvalDialog.registration || !registrationFormData || !session?.user) { toast.error("세션 정보가 없습니다."); return; } setSyncLoading(prev => ({ ...prev, registrationRequest: true })); try { // 결재 워크플로우 시작 (approvers는 이미 EP ID 배열) const result = await registerVendorWithApproval({ registrationId: approvalDialog.registration.id, requestData: registrationFormData, vendorId: approvalDialog.registration.vendorId, // vendors 테이블에서 정보를 가져오기 위한 vendorId currentUser: { id: Number(session.user.id), epId: session.user.epId || null, email: session.user.email || undefined, }, approvers: approvers, attachments: attachments, // 첨부파일 전달 title: title, // 미리보기에서 수정한 제목 전달 }); if (result.status === 'pending_approval') { // 성공 시에만 상태 초기화 및 페이지 리로드 setRegistrationFormData(null); setApprovalVariables({}); setApprovalDialog({ open: false, registration: null }); toast.success("정규업체 등록 결재가 상신되었습니다."); router.refresh(); } } catch (error) { console.error("결재 상신 중 오류:", error); toast.error("결재 상신 중 오류가 발생했습니다."); } finally { setSyncLoading(prev => ({ ...prev, registrationRequest: false })); } }; // CP검토 상태인 선택된 행들 개수 const cpReviewCount = selectedRows.filter(row => row.status === "cp_review").length; // 모든 항목 완료 여부 체크 함수 (table-columns.tsx의 진행현황 계산 로직과 동일) const isAllItemsComplete = (registration: VendorRegularRegistration): boolean => { // 문서 현황 계산 (국가별 요구사항 적용) const isForeign = registration.country !== 'KR' const requiredDocs = isForeign ? 4 : 3 // 외자: 4개(통장사본 포함), 내자: 3개(통장사본 제외) const submittedDocs = Object.values(registration.documentSubmissions).filter(Boolean).length const incompleteDocs = requiredDocs - submittedDocs // 기본계약 현황 계산 const totalContracts = registration.basicContracts?.length || 0 const completedContracts = registration.basicContracts?.filter(c => c.status === "VENDOR_SIGNED" || c.status === "COMPLETED").length || 0 const incompleteContracts = totalContracts - completedContracts // 추가정보 현황 const additionalInfoCompleted = registration.additionalInfo // 전체 미완료 항목 계산 (table-columns.tsx와 동일한 로직) const totalIncomplete = (incompleteDocs > 0 ? 1 : 0) + incompleteContracts + (!additionalInfoCompleted ? 1 : 0) const result = totalIncomplete === 0 // 디버깅 로그 if (selectedRows.some(r => r.id === registration.id)) { console.log('등록요청 버튼 활성화 체크:', { registrationId: registration.id, companyName: registration.companyName, status: registration.status, incompleteDocs, incompleteContracts, additionalInfoCompleted, totalIncomplete, isAllComplete: result }) } return result } // 모든 항목이 완료된 선택된 행들 개수 const allItemsCompleteCount = selectedRows.filter(row => isAllItemsComplete(row)).length; return (
setSkipDialogs(prev => ({ ...prev, legalReview: open }))} title="GTC Skip" description={`선택된 ${cpReviewCount}개 업체의 GTC를 Skip하고 CP완료 상태로 변경합니다. Skip 사유를 입력해주세요.`} onConfirm={handleLegalReviewSkip} loading={syncLoading.legalSkip} /> setRegistrationRequestDialog(prev => ({ ...prev, open }))} registration={registrationRequestDialog.registration} onSubmit={handleRegistrationRequestSubmit} /> {/* 결재 미리보기 Dialog - 정규업체 등록 */} {session?.user && session.user.epId && approvalDialog.registration && ( { setApprovalDialog(prev => ({ ...prev, open })); if (!open) { // 다이얼로그가 닫히면 폼 데이터도 초기화 setRegistrationFormData(null); setApprovalVariables({}); } }} templateName="정규업체 등록" variables={approvalVariables} title={`정규업체 등록 - ${approvalDialog.registration.companyName}`} currentUser={{ id: Number(session.user.id), epId: session.user.epId, name: session.user.name || undefined, email: session.user.email || undefined, }} onConfirm={handleApprovalSubmit} enableAttachments={true} maxAttachments={10} maxFileSize={100 * 1024 * 1024} // 100MB /> )}
) }