"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 approvalReadyRows = selectedRows.filter(row => row.status === "approval_ready"); if (approvalReadyRows.length === 0) { toast.error("조건충족 상태의 벤더를 선택해주세요."); return; } if (approvalReadyRows.length > 1) { toast.error("정규업체 등록 요청은 한 번에 하나씩만 가능합니다."); return; } setRegistrationRequestDialog({ open: true, registration: approvalReadyRows[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; // 조건충족 상태인 선택된 행들 개수 const approvalReadyCount = selectedRows.filter(row => row.status === "approval_ready").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 /> )}
) }