'use client' import * as React from 'react' import { useSession } from 'next-auth/react' import { type DataTableAdvancedFilterField, type DataTableFilterField } from '@/types/table' import { useDataTable } from '@/hooks/use-data-table' import { DataTable } from '@/components/data-table/data-table' import { DataTableAdvancedToolbar } from '@/components/data-table/data-table-advanced-toolbar' import { BiddingDetailVendorToolbarActions } from './bidding-detail-vendor-toolbar-actions' import { BiddingDetailVendorEditDialog } from './bidding-detail-vendor-edit-dialog' import { BiddingAwardDialog } from './bidding-award-dialog' import { getBiddingDetailVendorColumns } from './bidding-detail-vendor-columns' import { QuotationVendor } from '@/lib/bidding/detail/service' import { Bidding } from '@/db/schema' import { VendorPriceAdjustmentViewDialog } from './vendor-price-adjustment-view-dialog' import { QuotationHistoryDialog } from './quotation-history-dialog' import { ApprovalPreviewDialog } from '@/lib/approval/approval-preview-dialog' import { ApplicationReasonDialog } from '@/lib/rfq-last/vendor/application-reason-dialog' import { requestBiddingAwardWithApproval } from '@/lib/bidding/approval-actions' import { useToast } from '@/hooks/use-toast' interface BiddingDetailVendorTableContentProps { biddingId: number bidding: Bidding vendors: QuotationVendor[] onRefresh: () => void onOpenSelectionReasonDialog: () => void onViewItemDetails?: (vendor: QuotationVendor) => void onViewQuotationHistory?: (vendor: QuotationVendor) => void readOnly?: boolean } const filterFields: DataTableFilterField[] = [ { id: 'vendorName', label: '업체명', placeholder: '업체명으로 검색...', }, { id: 'vendorCode', label: '업체코드', placeholder: '업체코드로 검색...', }, { id: 'contactPerson', label: '담당자', placeholder: '담당자로 검색...', }, ] const advancedFilterFields: DataTableAdvancedFilterField[] = [ { id: 'vendorName', label: '업체명', type: 'text', }, { id: 'vendorCode', label: '업체코드', type: 'text', }, { id: 'contactPerson', label: '담당자', type: 'text', }, { id: 'quotationAmount', label: '견적금액', type: 'number', }, { id: 'invitationStatus', label: '상태', type: 'multi-select', options: [ { label: '제출완료', value: 'bidding_submitted' }, { label: '선정완료', value: 'bidding_accepted' }, { label: '미제출', value: 'pending' }, ], }, ] export function BiddingDetailVendorTableContent({ biddingId, bidding, vendors, onRefresh, onViewItemDetails, onViewQuotationHistory, readOnly = false }: BiddingDetailVendorTableContentProps) { const { data: session } = useSession() const { toast } = useToast() // 세션에서 사용자 ID 가져오기 const userId = session?.user?.id || '' const [selectedVendor, setSelectedVendor] = React.useState(null) const [isAwardDialogOpen, setIsAwardDialogOpen] = React.useState(false) const [isAwardRatioDialogOpen, setIsAwardRatioDialogOpen] = React.useState(false) const [isVendorPriceAdjustmentDialogOpen, setIsVendorPriceAdjustmentDialogOpen] = React.useState(false) const [quotationHistoryData, setQuotationHistoryData] = React.useState(null) const [isQuotationHistoryDialogOpen, setIsQuotationHistoryDialogOpen] = React.useState(false) const [approvalPreviewData, setApprovalPreviewData] = React.useState<{ templateName: string variables: Record title: string selectionReason: string awardedCompanies: { companyId: number companyName: string | null finalQuoteAmount: number awardRatio: number }[] } | null>(null) const [isApprovalPreviewDialogOpen, setIsApprovalPreviewDialogOpen] = React.useState(false) const handleViewPriceAdjustment = (vendor: QuotationVendor) => { setSelectedVendor(vendor) setIsVendorPriceAdjustmentDialogOpen(true) } const handleViewQuotationHistory = async (vendor: QuotationVendor) => { try { const { getQuotationHistory } = await import('@/lib/bidding/selection/actions') const result = await getQuotationHistory(biddingId, vendor.vendorId) console.log(result) if (result.success) { setQuotationHistoryData({ vendorName: vendor.vendorName, history: result.data?.history || [], biddingCurrency: bidding.currency || 'KRW', targetPrice: bidding.targetPrice ? parseFloat(bidding.targetPrice.toString()) : undefined }) setSelectedVendor(vendor) setIsQuotationHistoryDialogOpen(true) } else { toast({ title: '오류', description: result.error, variant: 'destructive', }) } } catch (error) { console.error('Failed to load quotation history:', error) toast({ title: '오류', description: '견적 히스토리를 불러오는데 실패했습니다.', variant: 'destructive', }) } } const columns = React.useMemo( () => getBiddingDetailVendorColumns({ onViewPriceAdjustment: handleViewPriceAdjustment, onViewItemDetails: onViewItemDetails, onViewQuotationHistory: onViewQuotationHistory || handleViewQuotationHistory, biddingStatus: bidding.status, biddingTargetPrice: bidding.targetPrice, biddingFinalBidPrice: bidding.finalBidPrice, biddingCurrency: bidding.currency || undefined }), [handleViewPriceAdjustment, onViewItemDetails, onViewQuotationHistory, handleViewQuotationHistory, bidding.status, bidding.targetPrice, bidding.finalBidPrice, bidding.currency] ) const { table } = useDataTable({ data: vendors, columns, pageCount: 1, filterFields, enableAdvancedFilter: true, initialState: { sorting: [{ id: 'vendorName', desc: false }], columnPinning: { right: ['actions'] }, }, getRowId: (originalRow) => originalRow.id.toString(), shallow: false, clearOnDefault: true, }) // single select된 vendor 가져오기 const selectedRows = table.getSelectedRowModel().rows const singleSelectedVendor = selectedRows.length === 1 ? selectedRows[0].original : null // 발주비율 산정 버튼 핸들러 const handleOpenAwardRatioDialog = () => { if (singleSelectedVendor) { setSelectedVendor(singleSelectedVendor) setIsAwardRatioDialogOpen(true) } } // 낙찰 결재 상신 핸들러 const handleAwardApprovalConfirm = async (data: { approvers: string[]; title: string; attachments?: File[] }) => { if (!session?.user?.id || !approvalPreviewData) return try { const result = await requestBiddingAwardWithApproval({ biddingId, selectionReason: approvalPreviewData.selectionReason, awardedCompanies: approvalPreviewData.awardedCompanies, currentUser: { id: Number(session.user.id), epId: session.user.epId || null, email: session.user.email || undefined }, approvers: data.approvers, }) if (result.status === 'pending_approval') { toast({ title: '성공', description: `낙찰 결재가 상신되었습니다. (ID: ${result.approvalId})`, }) setIsApprovalPreviewDialogOpen(false) setApprovalPreviewData(null) onRefresh() } else { toast({ title: '오류', description: '낙찰 결재 상신 중 오류가 발생했습니다.', variant: 'destructive', }) } } catch (error) { console.error('낙찰 결재 상신 실패:', error) toast({ title: '오류', description: '낙찰 결재 상신 중 오류가 발생했습니다.', variant: 'destructive', }) } } return ( <> setIsAwardDialogOpen(true)} onOpenAwardRatioDialog={handleOpenAwardRatioDialog} onSuccess={onRefresh} winnerVendor={vendors.find(v => v.awardRatio === 100)} singleSelectedVendor={singleSelectedVendor} readOnly={readOnly} /> {/* 발주비율 산정 Dialog */} { setApprovalPreviewData(data) setIsAwardDialogOpen(false) setIsApprovalPreviewDialogOpen(true) }} /> {/* 낙찰 결재 미리보기 다이얼로그 */} {session?.user && session.user.epId && approvalPreviewData && ( { setIsApprovalPreviewDialogOpen(open) if (!open) { setApprovalPreviewData(null) } }} templateName={approvalPreviewData.templateName} variables={approvalPreviewData.variables} title={approvalPreviewData.title} currentUser={{ id: Number(session.user.id), epId: session.user.epId, name: session.user.name || undefined, email: session.user.email || undefined }} onConfirm={handleAwardApprovalConfirm} /> )} ) }