'use client' import * as React from 'react' import { Button } from '@/components/ui/button' import { Label } from '@/components/ui/label' import { Checkbox } from '@/components/ui/checkbox' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@/components/ui/command' import { Popover, PopoverContent, PopoverTrigger, } from '@/components/ui/popover' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { ChevronsUpDown, Loader2, X, Plus } from 'lucide-react' import { createBiddingDetailVendor } from '@/lib/bidding/detail/service' import { searchVendorsForBidding, getVendorsBusinessSize } from '@/lib/bidding/service' import { useToast } from '@/hooks/use-toast' import { useTransition } from 'react' import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' interface BiddingDetailVendorCreateDialogProps { biddingId: number open: boolean onOpenChange: (open: boolean) => void onSuccess: () => void } interface Vendor { id: number vendorName: string vendorCode: string status: string } interface SelectedVendorWithQuestion { vendor: Vendor isPriceAdjustmentApplicableQuestion: boolean } export function BiddingDetailVendorCreateDialog({ biddingId, open, onOpenChange, onSuccess, }: BiddingDetailVendorCreateDialogProps) { const { toast } = useToast() const [isPending, startTransition] = useTransition() const [activeTab, setActiveTab] = React.useState('select') // Vendor 검색 상태 const [vendorList, setVendorList] = React.useState([]) const [selectedVendorsWithQuestion, setSelectedVendorsWithQuestion] = React.useState([]) const [vendorOpen, setVendorOpen] = React.useState(false) // Business size 정보 캐싱 const [businessSizeMap, setBusinessSizeMap] = React.useState>({}) // 벤더 로드 const loadVendors = React.useCallback(async () => { try { const result = await searchVendorsForBidding('', biddingId) // 빈 검색어로 모든 벤더 로드 setVendorList(result || []) } catch (error) { console.error('Failed to load vendors:', error) toast({ title: '오류', description: '벤더 목록을 불러오는데 실패했습니다.', variant: 'destructive', }) setVendorList([]) } }, [biddingId, toast]) React.useEffect(() => { if (open) { loadVendors() } }, [open, loadVendors]) // 초기화 React.useEffect(() => { if (!open) { setSelectedVendorsWithQuestion([]) setActiveTab('select') } }, [open]) // 벤더 추가 const handleAddVendor = (vendor: Vendor) => { if (!selectedVendorsWithQuestion.find(v => v.vendor.id === vendor.id)) { setSelectedVendorsWithQuestion([ ...selectedVendorsWithQuestion, { vendor, isPriceAdjustmentApplicableQuestion: false } ]) } setVendorOpen(false) } // 벤더 제거 const handleRemoveVendor = (vendorId: number) => { setSelectedVendorsWithQuestion( selectedVendorsWithQuestion.filter(v => v.vendor.id !== vendorId) ) } // 이미 선택된 벤더인지 확인 const isVendorSelected = (vendorId: number) => { return selectedVendorsWithQuestion.some(v => v.vendor.id === vendorId) } // 연동제 적용요건 문의 체크박스 토글 const handleTogglePriceAdjustmentQuestion = (vendorId: number, checked: boolean) => { setSelectedVendorsWithQuestion(prev => prev.map(item => item.vendor.id === vendorId ? { ...item, isPriceAdjustmentApplicableQuestion: checked } : item ) ) } const handleCreate = () => { if (selectedVendorsWithQuestion.length === 0) { toast({ title: '오류', description: '업체를 선택해주세요.', variant: 'destructive', }) return } // Tab 2로 이동하여 연동제 적용요건 문의를 확인하도록 유도 if (activeTab === 'select') { setActiveTab('question') toast({ title: '확인 필요', description: '선택한 업체들의 연동제 적용요건 문의를 확인해주세요.', }) return } startTransition(async () => { let successCount = 0 const errorMessages: string[] = [] for (const item of selectedVendorsWithQuestion) { try { const response = await createBiddingDetailVendor( biddingId, item.vendor.id, item.isPriceAdjustmentApplicableQuestion ) if (response.success) { successCount++ } else { errorMessages.push(`${item.vendor.vendorName}: ${response.error}`) } } catch { errorMessages.push(`${item.vendor.vendorName}: 처리 중 오류가 발생했습니다.`) } } if (successCount > 0) { toast({ title: '성공', description: `${successCount}개의 업체가 성공적으로 추가되었습니다.${errorMessages.length > 0 ? ` ${errorMessages.length}개는 실패했습니다.` : ''}`, }) onOpenChange(false) resetForm() onSuccess() } if (errorMessages.length > 0 && successCount === 0) { toast({ title: '오류', description: `업체 추가에 실패했습니다: ${errorMessages.join(', ')}`, variant: 'destructive', }) } }) } const resetForm = () => { setSelectedVendorsWithQuestion([]) setActiveTab('select') } const selectedVendors = selectedVendorsWithQuestion.map(item => item.vendor) // 선택된 vendor들의 businessSize 정보를 useMemo로 캐싱 const loadBusinessSize = React.useMemo(() => { const selectedVendorIds = selectedVendors.map(v => v.id) if (selectedVendorIds.length === 0) return // 이미 로드된 vendor들은 제외 const newVendorIds = selectedVendorIds.filter(id => !(id in businessSizeMap)) if (newVendorIds.length > 0) { getVendorsBusinessSize(newVendorIds).then(result => { setBusinessSizeMap(prev => ({ ...prev, ...result })) }).catch(error => { console.error('Failed to load business size:', error) }) } }, [selectedVendors, businessSizeMap]) // selectedVendors가 변경될 때마다 businessSize 로드 React.useEffect(() => { loadBusinessSize }, [loadBusinessSize]) return ( {/* 헤더 */} 협력업체 추가 입찰에 참여할 업체를 선택하고 연동제 적용요건 문의를 설정하세요. {/* 탭 네비게이션 */} 1. 입찰업체 선택 ({selectedVendors.length}개) 2. 연동제 적용요건 문의 {/* Tab 1: 입찰업체 선택 */} 업체 선택 입찰에 참여할 협력업체를 선택하세요.
{/* 업체 추가 버튼 */} 검색 결과가 없습니다. {vendorList .filter(vendor => !isVendorSelected(vendor.id)) .map((vendor) => ( handleAddVendor(vendor)} >
{vendor.vendorCode} {vendor.vendorName}
))}
{/* 선택된 업체 목록 */} {selectedVendors.length > 0 && (

선택된 업체 ({selectedVendors.length}개)

{selectedVendors.map((vendor, index) => (
{index + 1}. {vendor.vendorCode} {vendor.vendorName}
))}
)} {selectedVendors.length === 0 && (

아직 선택된 업체가 없습니다.

위 버튼을 클릭하여 업체를 추가하세요.

)}
{/* Tab 2: 연동제 적용요건 문의 체크 */} 연동제 적용요건 문의 선택한 업체별로 연동제 적용요건 문의 여부를 체크하세요. {selectedVendorsWithQuestion.length === 0 ? (

선택된 업체가 없습니다.

먼저 입찰업체 선택 탭에서 업체를 선택해주세요.

) : (
{selectedVendorsWithQuestion.map((item, index) => (
{index + 1}.
{item.vendor.vendorCode} {item.vendor.vendorName}
handleTogglePriceAdjustmentQuestion(item.vendor.id, checked as boolean) } /> 기업규모: {(() => { switch (businessSizeMap[item.vendor.id]) { case 'A': return '대기업'; case 'B': return '중견기업'; case 'C': return '중소기업'; case 'D': return '소기업'; default: return '-'; } })()}
))}
)}
{/* 푸터 */} {activeTab === 'select' ? ( ) : ( )}
) }