From be5d5ab488ae875e7c56306403aba923e1784021 Mon Sep 17 00:00:00 2001 From: dujinkim Date: Tue, 18 Nov 2025 10:33:51 +0000 Subject: (최겸) 기술영업 첨부파일 결재 등록 및 요구사항 개발 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../table/detail-table/rfq-detail-table.tsx | 127 +++++++++++++++++++++ 1 file changed, 127 insertions(+) (limited to 'lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx') diff --git a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx index 8ce55d56..52758412 100644 --- a/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx +++ b/lib/techsales-rfq/table/detail-table/rfq-detail-table.tsx @@ -22,6 +22,11 @@ import { TechSalesQuotationAttachmentsSheet, type QuotationAttachment } from ".. import type { QuotationInfo } from "./rfq-detail-column" import { VendorContactSelectionDialog } from "./vendor-contact-selection-dialog" import { QuotationContactsViewDialog } from "./quotation-contacts-view-dialog" +import { ApprovalPreviewDialog } from "@/lib/approval/client" +import { ApplicationReasonDialog } from "@/lib/rfq-last/vendor/application-reason-dialog" +import { requestTechSalesRfqSendWithApproval } from "@/lib/techsales-rfq/approval-actions" +import { mapTechSalesRfqSendToTemplateVariables } from "@/lib/techsales-rfq/approval-handlers" +import { useSession } from "next-auth/react" // 기본적인 RFQ 타입 정의 interface TechSalesRfq { @@ -48,6 +53,8 @@ interface RfqDetailTablesProps { export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps) { // console.log("selectedRfq", selectedRfq) + const session = useSession() + // 상태 관리 const [isLoading, setIsLoading] = useState(false) const [details, setDetails] = useState([]) @@ -89,6 +96,29 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps const [contactsDialogOpen, setContactsDialogOpen] = useState(false) const [selectedQuotationForContacts, setSelectedQuotationForContacts] = useState<{ id: number; vendorName?: string } | null>(null) + // 결재 관련 상태 관리 + const [showApplicationReasonDialog, setShowApplicationReasonDialog] = useState(false) + const [showApprovalPreview, setShowApprovalPreview] = useState(false) + const [approvalPreviewData, setApprovalPreviewData] = useState<{ + vendors: Array<{ + vendorId: number + vendorName: string + }> + drmAttachments: Array<{ + fileName?: string | null + fileSize?: number | null + }> + drmAttachmentIds: number[] + selectedContacts?: Array<{ + vendorId: number + contactId: number + contactEmail: string + contactName: string + }> + templateVariables?: Record + applicationReason?: string + } | null>(null) + // selectedRfq ID 메모이제이션 (객체 참조 변경 방지) const selectedRfqId = useMemo(() => selectedRfq?.id, [selectedRfq?.id]) @@ -239,6 +269,25 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps selectedContacts: selectedContacts }); + // DRM 파일이 있어서 결재가 필요한 경우 + if (!result.success && result.requiresApproval) { + // 결재 데이터 저장 + setApprovalPreviewData({ + vendors: selectedRows.map(row => ({ + vendorId: row.vendorId!, + vendorName: row.vendorName || "", + })), + drmAttachments: result.drmAttachments || [], + drmAttachmentIds: result.drmAttachmentIds || [], + selectedContacts: selectedContacts, + }); + + // 신청사유 입력 다이얼로그 표시 + setShowApplicationReasonDialog(true); + setIsSendingRfq(false); + return; + } + if (result.success) { toast.success(result.message || `${selectedContacts.length}명의 연락처에게 RFQ가 발송되었습니다.`); } else { @@ -412,6 +461,84 @@ export function RfqDetailTables({ selectedRfq, maxHeight }: RfqDetailTablesProps setContactsDialogOpen(true) }, []) + // 신청사유 입력 완료 핸들러 + const handleApplicationReasonConfirm = useCallback(async (reason: string) => { + if (!approvalPreviewData) { + toast.error("결재 데이터가 없습니다."); + return; + } + + try { + // 템플릿 변수 생성 (신청사유 포함) + const templateVariables = await mapTechSalesRfqSendToTemplateVariables({ + attachments: approvalPreviewData.drmAttachments, + vendorNames: approvalPreviewData.vendors.map(v => v.vendorName), + applicationReason: reason, + }); + + // 결재 미리보기 데이터 업데이트 + setApprovalPreviewData({ + ...approvalPreviewData, + templateVariables, + applicationReason: reason, + }); + + // 신청사유 다이얼로그 닫고 결재 미리보기 열기 + setShowApplicationReasonDialog(false); + setShowApprovalPreview(true); + } catch (error) { + console.error("템플릿 변수 생성 실패:", error); + toast.error("결재 문서 생성에 실패했습니다."); + } + }, [approvalPreviewData]); + + // 결재 미리보기 확인 핸들러 + const handleApprovalConfirm = useCallback(async (approvalData: { + approvers: string[]; + title: string; + description?: string; + }) => { + if (!approvalPreviewData || !selectedRfq || !session.data?.user) { + toast.error("결재 데이터가 없습니다."); + return; + } + + if (!session.data.user.epId) { + toast.error("Knox EP ID가 필요합니다."); + return; + } + + try { + const result = await requestTechSalesRfqSendWithApproval({ + rfqId: selectedRfq.id, + rfqCode: selectedRfq.rfqCode || undefined, + vendorIds: approvalPreviewData.vendors.map(v => v.vendorId), + selectedContacts: approvalPreviewData.selectedContacts, + drmAttachmentIds: approvalPreviewData.drmAttachmentIds, + drmAttachments: approvalPreviewData.drmAttachments, + applicationReason: approvalPreviewData.applicationReason || '', + currentUser: { + id: Number(session.data.user.id), + epId: session.data.user.epId || null, + name: session.data.user.name || undefined, + email: session.data.user.email || undefined, + }, + approvers: approvalData.approvers, + }); + + if (result.success) { + toast.success(result.message); + setShowApprovalPreview(false); + setApprovalPreviewData(null); + setSelectedRows([]); + await handleRefreshData(); + } + } catch (error) { + console.error("결재 상신 실패:", error); + toast.error(error instanceof Error ? error.message : "결재 상신에 실패했습니다."); + } + }, [approvalPreviewData, selectedRfq, session, handleRefreshData]); + // 칼럼 정의 - unreadMessages 상태 전달 (메모이제이션) const columns = useMemo(() => getRfqDetailColumns({ -- cgit v1.2.3