diff options
Diffstat (limited to 'lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx')
| -rw-r--r-- | lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx | 311 |
1 files changed, 0 insertions, 311 deletions
diff --git a/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx b/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx deleted file mode 100644 index bd078192..00000000 --- a/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx +++ /dev/null @@ -1,311 +0,0 @@ -'use client' - -import * as React from 'react' -import { Button } from '@/components/ui/button' -import { Label } from '@/components/ui/label' -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 { Check, ChevronsUpDown, Loader2, X, Plus, Search } from 'lucide-react' -import { cn } from '@/lib/utils' -import { createBiddingCompany } from '@/lib/bidding/pre-quote/service' -import { searchVendorsForBidding } 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' -import { ScrollArea } from '@/components/ui/scroll-area' -import { Alert, AlertDescription } from '@/components/ui/alert' -import { Info } from 'lucide-react' - -interface BiddingPreQuoteVendorCreateDialogProps { - biddingId: number - open: boolean - onOpenChange: (open: boolean) => void - onSuccess: () => void -} - -interface Vendor { - id: number - vendorName: string - vendorCode: string - status: string -} - -export function BiddingPreQuoteVendorCreateDialog({ - biddingId, - open, - onOpenChange, - onSuccess -}: BiddingPreQuoteVendorCreateDialogProps) { - const { toast } = useToast() - const [isPending, startTransition] = useTransition() - - // Vendor 검색 상태 - const [vendorList, setVendorList] = React.useState<Vendor[]>([]) - const [selectedVendors, setSelectedVendors] = React.useState<Vendor[]>([]) - const [vendorOpen, setVendorOpen] = React.useState(false) - - - // 벤더 로드 - 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]) - - React.useEffect(() => { - if (open) { - loadVendors() - } - }, [open, loadVendors]) - - // 초기화 - React.useEffect(() => { - if (!open) { - setSelectedVendors([]) - } - }, [open]) - - // 벤더 추가 - const handleAddVendor = (vendor: Vendor) => { - if (!selectedVendors.find(v => v.id === vendor.id)) { - setSelectedVendors([...selectedVendors, vendor]) - } - setVendorOpen(false) - } - - // 벤더 제거 - const handleRemoveVendor = (vendorId: number) => { - setSelectedVendors(selectedVendors.filter(v => v.id !== vendorId)) - } - - // 이미 선택된 벤더인지 확인 - const isVendorSelected = (vendorId: number) => { - return selectedVendors.some(v => v.id === vendorId) - } - - const handleCreate = () => { - if (selectedVendors.length === 0) { - toast({ - title: '오류', - description: '업체를 선택해주세요.', - variant: 'destructive', - }) - return - } - - startTransition(async () => { - let successCount = 0 - let errorMessages: string[] = [] - - for (const vendor of selectedVendors) { - try { - const response = await createBiddingCompany({ - biddingId, - companyId: vendor.id, - }) - - if (response.success) { - successCount++ - } else { - errorMessages.push(`${vendor.vendorName}: ${response.error}`) - } - } catch (error) { - errorMessages.push(`${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 = () => { - setSelectedVendors([]) - } - - return ( - <Dialog open={open} onOpenChange={onOpenChange}> - <DialogContent className="max-w-4xl max-h-[90vh] p-0 flex flex-col"> - {/* 헤더 */} - <DialogHeader className="p-6 pb-0"> - <DialogTitle>사전견적 업체 추가</DialogTitle> - <DialogDescription> - 견적 요청을 보낼 업체를 선택하세요. 여러 개 선택 가능합니다. - </DialogDescription> - </DialogHeader> - - {/* 메인 컨텐츠 */} - <div className="flex-1 px-6 py-4 overflow-y-auto"> - <div className="space-y-6"> - {/* 업체 선택 카드 */} - <Card> - <CardHeader> - <CardTitle className="text-lg">업체 선택</CardTitle> - <CardDescription> - 사전견적을 발송할 업체를 선택하세요. - </CardDescription> - </CardHeader> - <CardContent> - <div className="space-y-4"> - {/* 업체 추가 버튼 */} - <Popover open={vendorOpen} onOpenChange={setVendorOpen}> - <PopoverTrigger asChild> - <Button - variant="outline" - role="combobox" - aria-expanded={vendorOpen} - className="w-full justify-between" - disabled={vendorList.length === 0} - > - <span className="flex items-center gap-2"> - <Plus className="h-4 w-4" /> - 업체 선택하기 - </span> - <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> - </Button> - </PopoverTrigger> - <PopoverContent className="w-[500px] p-0" align="start"> - <Command> - <CommandInput placeholder="업체명 또는 코드로 검색..." /> - <CommandList> - <CommandEmpty>검색 결과가 없습니다.</CommandEmpty> - <CommandGroup> - {vendorList - .filter(vendor => !isVendorSelected(vendor.id)) - .map((vendor) => ( - <CommandItem - key={vendor.id} - value={`${vendor.vendorCode} ${vendor.vendorName}`} - onSelect={() => handleAddVendor(vendor)} - > - <div className="flex items-center gap-2 w-full"> - <Badge variant="outline" className="shrink-0"> - {vendor.vendorCode} - </Badge> - <span className="truncate">{vendor.vendorName}</span> - </div> - </CommandItem> - ))} - </CommandGroup> - </CommandList> - </Command> - </PopoverContent> - </Popover> - - {/* 선택된 업체 목록 */} - {selectedVendors.length > 0 && ( - <div className="space-y-2"> - <div className="flex items-center justify-between"> - <h4 className="text-sm font-medium">선택된 업체 ({selectedVendors.length}개)</h4> - </div> - <div className="space-y-2"> - {selectedVendors.map((vendor, index) => ( - <div - key={vendor.id} - className="flex items-center justify-between p-3 rounded-lg bg-secondary/50" - > - <div className="flex items-center gap-3"> - <span className="text-sm text-muted-foreground"> - {index + 1}. - </span> - <Badge variant="outline"> - {vendor.vendorCode} - </Badge> - <span className="text-sm font-medium"> - {vendor.vendorName} - </span> - </div> - <Button - variant="ghost" - size="sm" - onClick={() => handleRemoveVendor(vendor.id)} - className="h-8 w-8 p-0" - > - <X className="h-4 w-4" /> - </Button> - </div> - ))} - </div> - </div> - )} - - {selectedVendors.length === 0 && ( - <div className="text-center py-8 text-muted-foreground"> - <p className="text-sm">아직 선택된 업체가 없습니다.</p> - <p className="text-xs mt-1">위 버튼을 클릭하여 업체를 추가하세요.</p> - </div> - )} - </div> - </CardContent> - </Card> - </div> - </div> - - {/* 푸터 */} - <DialogFooter className="p-6 pt-0 border-t"> - <Button - variant="outline" - onClick={() => onOpenChange(false)} - disabled={isPending} - > - 취소 - </Button> - <Button - onClick={handleCreate} - disabled={isPending || selectedVendors.length === 0} - > - {isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} - {selectedVendors.length > 0 - ? `${selectedVendors.length}개 업체 추가` - : '업체 추가' - } - </Button> - </DialogFooter> - </DialogContent> - </Dialog> - ) -} |
