'use client' import * as React from 'react' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { DollarSign, Building, TrendingDown, TrendingUp } from 'lucide-react' import { useToast } from '@/hooks/use-toast' import { getVendorPricesForBidding } from '../service' interface VendorPrice { companyId: number companyName: string biddingCompanyId: number totalAmount: number currency: string itemPrices: Array<{ prItemId: number itemName: string quantity: number quantityUnit: string unitPrice: number amount: number proposedDeliveryDate?: string }> } interface BiddingVendorPricesDialogProps { open: boolean onOpenChange: (open: boolean) => void biddingId: number biddingTitle: string budget?: number | null targetPrice?: number | null currency?: string } export function BiddingVendorPricesDialog({ open, onOpenChange, biddingId, biddingTitle, budget, targetPrice, currency = 'KRW' }: BiddingVendorPricesDialogProps) { const { toast } = useToast() const [vendorPrices, setVendorPrices] = React.useState([]) const [isLoading, setIsLoading] = React.useState(false) const loadVendorPrices = React.useCallback(async () => { setIsLoading(true) try { const data = await getVendorPricesForBidding(biddingId) setVendorPrices(data) } catch (error) { console.error('Failed to load vendor prices:', error) toast({ title: '오류', description: '입찰가 정보를 불러오는데 실패했습니다.', variant: 'destructive', }) } finally { setIsLoading(false) } }, [biddingId, toast]) // 다이얼로그가 열릴 때 데이터 로드 React.useEffect(() => { if (open) { loadVendorPrices() } }, [open, loadVendorPrices]) // 금액 포맷팅 const formatCurrency = (amount: number) => { return new Intl.NumberFormat('ko-KR', { style: 'currency', currency: currency, minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(amount) } // 수량 포맷팅 const formatQuantity = (quantity: number, unit: string) => { return `${quantity.toLocaleString()} ${unit}` } // 최저가 계산 const getLowestPrice = (itemPrices: VendorPrice['itemPrices']) => { const validPrices = itemPrices.filter(item => item.quantity > 0) if (validPrices.length === 0) return null const prices = validPrices.map(item => item.unitPrice) return Math.min(...prices) } // 최고가 계산 const getHighestPrice = (itemPrices: VendorPrice['itemPrices']) => { const validPrices = itemPrices.filter(item => item.quantity > 0) if (validPrices.length === 0) return null const prices = validPrices.map(item => item.unitPrice) return Math.max(...prices) } return ( 입찰가 비교 분석 {biddingTitle} 협력업체별 품목별 입찰가 정보를 비교하여 최적의 낙찰 대상을 선정할 수 있습니다.
{/* 상단 요약 정보 */}
예산
{budget ? formatCurrency(budget) : '-'}
내정가
{targetPrice ? formatCurrency(targetPrice) : '-'}
참여 업체 수
{vendorPrices.length}개사
{isLoading ? (

입찰가 정보를 불러오는 중...

) : vendorPrices.length > 0 ? (
{vendorPrices.map((vendor) => (
{vendor.companyName}
{formatCurrency(vendor.totalAmount)}
총 입찰금액
품목명 수량 단가 금액 가격대 납기일 {vendor.itemPrices .filter(item => item.quantity > 0) .map((item, index) => { const lowestPrice = getLowestPrice(vendor.itemPrices) const highestPrice = getHighestPrice(vendor.itemPrices) const isLowest = item.unitPrice === lowestPrice const isHighest = item.unitPrice === highestPrice return ( {item.itemName} {formatQuantity(item.quantity, item.quantityUnit)} {formatCurrency(item.unitPrice)} {formatCurrency(item.amount)}
{isLowest && ( 최저 )} {isHighest && ( 최고 )}
{item.proposedDeliveryDate ? new Date(item.proposedDeliveryDate).toLocaleDateString('ko-KR') : '-' }
) })}
))}
) : (

입찰가 정보가 없습니다

협력업체들이 아직 입찰가를 제출하지 않았습니다.

)}
) }