'use client' import * as React from 'react' import { Bidding } from '@/db/schema' import { QuotationDetails, QuotationVendor } from '@/lib/bidding/detail/service' import { BiddingDetailVendorTableContent } from './bidding-detail-vendor-table' import { BiddingDetailItemsDialog } from './bidding-detail-items-dialog' import { BiddingDetailTargetPriceDialog } from './bidding-detail-target-price-dialog' import { BiddingPreQuoteItemDetailsDialog } from '../../../bidding/pre-quote/table/bidding-pre-quote-item-details-dialog' import { getPrItemsForBidding } from '../../../bidding/pre-quote/service' import { checkAllVendorsFinalSubmitted, performBidOpening } from '../bidding-actions' import { useToast } from '@/hooks/use-toast' import { useTransition } from 'react' import { useSession } from 'next-auth/react' import { BiddingNoticeEditor } from '@/lib/bidding/bidding-notice-editor' import { getBiddingNotice } from '@/lib/bidding/service' import { Button } from '@/components/ui/button' import { Card, CardContent } from '@/components/ui/card' import { Badge } from '@/components/ui/badge' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog' import { FileText, Eye, CheckCircle2, AlertCircle } from 'lucide-react' interface BiddingDetailContentProps { bidding: Bidding quotationDetails: QuotationDetails | null quotationVendors: QuotationVendor[] prItems: any[] } export function BiddingDetailContent({ bidding, quotationDetails, quotationVendors, prItems }: BiddingDetailContentProps) { const { toast } = useToast() const [isPending, startTransition] = useTransition() const session = useSession() const [dialogStates, setDialogStates] = React.useState({ items: false, targetPrice: false, selectionReason: false, award: false, biddingNotice: false }) const [, setRefreshTrigger] = React.useState(0) // PR 아이템 다이얼로그 관련 state const [isItemDetailsDialogOpen, setIsItemDetailsDialogOpen] = React.useState(false) const [selectedVendorForDetails, setSelectedVendorForDetails] = React.useState(null) const [prItemsForDialog, setPrItemsForDialog] = React.useState([]) // 입찰공고 관련 state const [biddingNotice, setBiddingNotice] = React.useState(null) const [isBiddingNoticeLoading, setIsBiddingNoticeLoading] = React.useState(false) // 최종제출 현황 관련 state const [finalSubmissionStatus, setFinalSubmissionStatus] = React.useState<{ allSubmitted: boolean totalCompanies: number submittedCompanies: number }>({ allSubmitted: false, totalCompanies: 0, submittedCompanies: 0 }) const [isPerformingBidOpening, setIsPerformingBidOpening] = React.useState(false) const handleRefresh = React.useCallback(() => { setRefreshTrigger(prev => prev + 1) }, []) // 입찰공고 로드 함수 const loadBiddingNotice = React.useCallback(async () => { if (!bidding.id) return setIsBiddingNoticeLoading(true) try { const notice = await getBiddingNotice(bidding.id) setBiddingNotice(notice) } catch (error) { console.error('Failed to load bidding notice:', error) toast({ title: '오류', description: '입찰공고문을 불러오는데 실패했습니다.', variant: 'destructive', }) } finally { setIsBiddingNoticeLoading(false) } }, [bidding.id, toast]) const openDialog = React.useCallback((type: keyof typeof dialogStates) => { setDialogStates(prev => ({ ...prev, [type]: true })) }, []) // 최종제출 현황 로드 함수 const loadFinalSubmissionStatus = React.useCallback(async () => { if (!bidding.id) return try { const status = await checkAllVendorsFinalSubmitted(bidding.id) setFinalSubmissionStatus(status) } catch (error) { console.error('Failed to load final submission status:', error) } }, [bidding.id]) // 개찰 핸들러 const handlePerformBidOpening = async (isEarly: boolean = false) => { if (!session.data?.user?.id) { toast({ title: '권한 없음', description: '로그인이 필요합니다.', variant: 'destructive', }) return } if (!finalSubmissionStatus.allSubmitted) { toast({ title: '개찰 불가', description: `모든 벤더가 최종 제출해야 개찰할 수 있습니다. (${finalSubmissionStatus.submittedCompanies}/${finalSubmissionStatus.totalCompanies})`, variant: 'destructive', }) return } const message = isEarly ? '조기개찰을 진행하시겠습니까?' : '개찰을 진행하시겠습니까?' if (!window.confirm(message)) { return } setIsPerformingBidOpening(true) try { const result = await performBidOpening(bidding.id, session.data.user.id.toString(), isEarly) if (result.success) { toast({ title: '개찰 완료', description: result.message, }) // 페이지 새로고침 window.location.reload() } else { toast({ title: '개찰 실패', description: result.error, variant: 'destructive', }) } } catch (error) { console.error('Failed to perform bid opening:', error) toast({ title: '오류', description: '개찰에 실패했습니다.', variant: 'destructive', }) } finally { setIsPerformingBidOpening(false) } } // 컴포넌트 마운트 시 입찰공고 및 최종제출 현황 로드 React.useEffect(() => { loadBiddingNotice() loadFinalSubmissionStatus() }, [loadBiddingNotice, loadFinalSubmissionStatus]) const closeDialog = React.useCallback((type: keyof typeof dialogStates) => { setDialogStates(prev => ({ ...prev, [type]: false })) }, []) const handleViewItemDetails = React.useCallback((vendor: QuotationVendor) => { startTransition(async () => { try { // PR 아이템 정보 로드 const prItemsData = await getPrItemsForBidding(bidding.id) setPrItemsForDialog(prItemsData) setSelectedVendorForDetails(vendor) setIsItemDetailsDialogOpen(true) } catch (error) { console.error('Failed to load PR items:', error) toast({ title: '오류', description: '품목 정보를 불러오는데 실패했습니다.', variant: 'destructive', }) } }) }, [bidding.id, toast]) // 개찰 버튼 표시 여부 (입찰평가중 상태에서만) const showBidOpeningButtons = bidding.status === 'evaluation_of_bidding' return (
{/* 입찰공고 편집 버튼 */}

입찰 상세

{bidding.title}

setDialogStates(prev => ({ ...prev, biddingNotice: open }))}> 입찰공고 편집
setDialogStates(prev => ({ ...prev, biddingNotice: false }))} />
{/* 최종제출 현황 및 개찰 버튼 */} {showBidOpeningButtons && (
{finalSubmissionStatus.allSubmitted ? ( ) : ( )}

최종제출 현황

최종 제출: {finalSubmissionStatus.submittedCompanies}/{finalSubmissionStatus.totalCompanies}개 업체 {finalSubmissionStatus.allSubmitted ? ( 모든 업체 제출 완료 ) : ( 제출 대기 중 )}
{/* 개찰 버튼들 */}
)} openDialog('targetPrice')} onOpenSelectionReasonDialog={() => openDialog('selectionReason')} onViewItemDetails={handleViewItemDetails} onEdit={undefined} /> closeDialog('items')} prItems={prItems} bidding={bidding} /> closeDialog('targetPrice')} quotationDetails={quotationDetails} bidding={bidding} onSuccess={handleRefresh} />
) }