'use client' import * as React from 'react' import { Bidding } from '@/db/schema' import { QuotationDetails, updateTargetPrice, calculateAndUpdateTargetPrice, getPreQuoteData } from '@/lib/bidding/detail/service' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { useToast } from '@/hooks/use-toast' import { useTransition } from 'react' interface BiddingDetailTargetPriceDialogProps { open: boolean onOpenChange: (open: boolean) => void quotationDetails: QuotationDetails | null bidding: Bidding onSuccess: () => void } export function BiddingDetailTargetPriceDialog({ open, onOpenChange, quotationDetails, bidding, onSuccess }: BiddingDetailTargetPriceDialogProps) { const { toast } = useToast() const [isPending, startTransition] = useTransition() const [targetPrice, setTargetPrice] = React.useState( bidding.targetPrice ? Number(bidding.targetPrice) : 0 ) const [calculationCriteria, setCalculationCriteria] = React.useState( (bidding as any).targetPriceCalculationCriteria || '' ) const [preQuoteData, setPreQuoteData] = React.useState(null) const [isAutoCalculating, setIsAutoCalculating] = React.useState(false) // Dialog가 열릴 때 상태 초기화 및 사전견적 데이터 로드 React.useEffect(() => { if (open) { setTargetPrice(bidding.targetPrice ? Number(bidding.targetPrice) : 0) setCalculationCriteria((bidding as any).targetPriceCalculationCriteria || '') // 사전견적 데이터 로드 const loadPreQuoteData = async () => { try { const data = await getPreQuoteData(bidding.id) setPreQuoteData(data) } catch (error) { console.error('Failed to load pre-quote data:', error) } } loadPreQuoteData() } }, [open, bidding]) // 자동 산정 함수 const handleAutoCalculate = () => { setIsAutoCalculating(true) startTransition(async () => { try { const result = await calculateAndUpdateTargetPrice( bidding.id ) if (result.success && result.data) { setTargetPrice(result.data.targetPrice) setCalculationCriteria(result.data.criteria) setPreQuoteData(result.data.preQuoteData) toast({ title: '성공', description: result.message, }) onSuccess() } else { toast({ title: '오류', description: result.error, variant: 'destructive', }) } } catch (error) { toast({ title: '오류', description: '내정가 자동 산정에 실패했습니다.', variant: 'destructive', }) } finally { setIsAutoCalculating(false) } }) } const handleSave = () => { // 필수값 검증 if (targetPrice <= 0) { toast({ title: '유효성 오류', description: '내정가는 0보다 큰 값을 입력해주세요.', variant: 'destructive', }) return } if (!calculationCriteria.trim()) { toast({ title: '유효성 오류', description: '내정가 산정 기준을 입력해주세요.', variant: 'destructive', }) return } startTransition(async () => { const result = await updateTargetPrice( bidding.id, targetPrice, calculationCriteria.trim() ) if (result.success) { toast({ title: '성공', description: result.message, }) onSuccess() onOpenChange(false) } else { toast({ title: '오류', description: result.error, variant: 'destructive', }) } }) } const formatCurrency = (amount: number) => { return new Intl.NumberFormat('ko-KR', { style: 'currency', currency: bidding.currency || 'KRW', }).format(amount) } return ( 내정가 산정 입찰번호: {bidding.biddingNumber} - 견적 통계 및 내정가 설정
{/* 사전견적 리스트 */} {preQuoteData?.quotes && preQuoteData.quotes.length > 0 && (

사전견적 현황

업체명 사전견적가 제출일 {preQuoteData.quotes.map((quote: any) => ( {quote.vendorName || `업체 ${quote.companyId}`} {formatCurrency(Number(quote.preQuoteAmount))} {quote.submittedAt ? new Date(quote.submittedAt).toLocaleDateString('ko-KR') : '-' } ))}
)} 항목 {/* 사전견적 통계 정보 */} 사전견적 수 {preQuoteData?.quotationCount || 0}개 {preQuoteData?.lowestQuote && ( 최저 사전견적가 {formatCurrency(preQuoteData.lowestQuote)} )} {preQuoteData?.highestQuote && ( 최고 사전견적가 {formatCurrency(preQuoteData.highestQuote)} )} {preQuoteData?.averageQuote && ( 평균 사전견적가 {formatCurrency(preQuoteData.averageQuote)} )} {/* 입찰 유형 */} 입찰 유형 {bidding.biddingType || '-'} {/* 예산 정보 */} {bidding.budget && ( 예산 {formatCurrency(Number(bidding.budget))} )} {/* 최종 업데이트 시간 */} {quotationDetails?.lastUpdated && ( 최종 업데이트 {new Date(quotationDetails.lastUpdated).toLocaleString('ko-KR')} )} {/* 내정가 입력 */}
setTargetPrice(Number(e.target.value))} placeholder="내정가를 입력하세요" className="flex-1" />
{targetPrice > 0 ? formatCurrency(targetPrice) : ''}
{preQuoteData?.quotationCount === 0 && (
사전견적 데이터가 없어 자동 산정이 불가능합니다.
)}
{/* 내정가 산정 기준 입력 */}