'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 { BiddingDetailVendorCreateDialog } from './bidding-detail-vendor-create-dialog' import { BiddingDetailVendorEditDialog } from './bidding-detail-vendor-edit-dialog' import { BiddingAwardDialog } from './bidding-award-dialog' import { getBiddingDetailVendorColumns } from './bidding-detail-vendor-columns' import { QuotationVendor, getPriceAdjustmentFormByBiddingCompanyId } from '@/lib/bidding/detail/service' import { Bidding } from '@/db/schema' import { PriceAdjustmentDialog } from '@/components/bidding/price-adjustment-dialog' import { deleteQuotationVendor, selectWinner } from '@/lib/bidding/detail/service' import { selectWinnerSchema } from '@/lib/bidding/validation' import { useToast } from '@/hooks/use-toast' import { useTransition, useCallback } from 'react' interface BiddingDetailVendorTableContentProps { biddingId: number bidding: Bidding vendors: QuotationVendor[] onRefresh: () => void onOpenItemsDialog: () => void onOpenTargetPriceDialog: () => void onOpenSelectionReasonDialog: () => void onOpenAwardDialog: () => void onEdit?: (vendor: QuotationVendor) => void onDelete?: (vendor: QuotationVendor) => void onSelectWinner?: (vendor: QuotationVendor) => void onViewItemDetails?: (vendor: QuotationVendor) => void } 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: 'status', label: '상태', type: 'multi-select', options: [ { label: '제출완료', value: 'submitted' }, { label: '선정완료', value: 'selected' }, { label: '미제출', value: 'pending' }, ], }, ] export function BiddingDetailVendorTableContent({ biddingId, bidding, vendors, onRefresh, onOpenItemsDialog, onOpenTargetPriceDialog, onOpenSelectionReasonDialog, onOpenAwardDialog, onEdit, onDelete, onSelectWinner, onViewItemDetails }: BiddingDetailVendorTableContentProps) { const { data: session } = useSession() const { toast } = useToast() const [isPending, startTransition] = useTransition() // 세션에서 사용자 ID 가져오기 const userId = session?.user?.id || '' const [selectedVendor, setSelectedVendor] = React.useState(null) const [isEditDialogOpen, setIsEditDialogOpen] = React.useState(false) const [isAwardDialogOpen, setIsAwardDialogOpen] = React.useState(false) const [priceAdjustmentData, setPriceAdjustmentData] = React.useState(null) const [isPriceAdjustmentDialogOpen, setIsPriceAdjustmentDialogOpen] = React.useState(false) const handleSelectWinner = useCallback((vendor: QuotationVendor) => { if (!vendor.awardRatio || vendor.awardRatio <= 0) { toast({ title: '오류', description: '발주비율을 먼저 설정해주세요.', variant: 'destructive', }) return } if (!confirm(`${vendor.vendorName} 업체를 낙찰자로 선정하시겠습니까?`)) return startTransition(async () => { const result = selectWinnerSchema.safeParse({ biddingId, vendorId: vendor.id, awardRatio: vendor.awardRatio || 0, }) if (!result.success) { toast({ title: '유효성 오류', description: result.error.issues[0]?.message || '입력값을 확인해주세요.', variant: 'destructive', }) return } const response = await selectWinner(biddingId, vendor.id, vendor.awardRatio || 0, userId) if (response.success) { toast({ title: '성공', description: response.message, }) onRefresh() } else { toast({ title: '오류', description: response.error, variant: 'destructive', }) } }) }, [toast, startTransition, biddingId, userId, selectWinnerSchema, selectWinner, onRefresh]) const handleEdit = (vendor: QuotationVendor) => { setSelectedVendor(vendor) setIsEditDialogOpen(true) } const handleViewPriceAdjustment = async (vendor: QuotationVendor) => { try { const priceAdjustmentForm = await getPriceAdjustmentFormByBiddingCompanyId(vendor.id) if (priceAdjustmentForm) { setPriceAdjustmentData(priceAdjustmentForm) setSelectedVendor(vendor) setIsPriceAdjustmentDialogOpen(true) } else { toast({ title: '연동제 정보 없음', description: '해당 업체의 연동제 정보가 없습니다.', variant: 'default', }) } } catch (error) { console.error('Failed to load price adjustment form:', error) toast({ title: '오류', description: '연동제 정보를 불러오는데 실패했습니다.', variant: 'destructive', }) } } const columns = React.useMemo( () => getBiddingDetailVendorColumns({ onEdit: onEdit || handleEdit, onDelete: onDelete, onSelectWinner: onSelectWinner || handleSelectWinner, onViewPriceAdjustment: handleViewPriceAdjustment, onViewItemDetails: onViewItemDetails }), [onEdit, onDelete, onSelectWinner, handleEdit, handleSelectWinner, handleViewPriceAdjustment, onViewItemDetails] ) 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, }) return ( <> setIsAwardDialogOpen(true)} onSuccess={onRefresh} /> ) }