summaryrefslogtreecommitdiff
path: root/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx
diff options
context:
space:
mode:
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.tsx205
1 files changed, 205 insertions, 0 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
new file mode 100644
index 00000000..e2a38547
--- /dev/null
+++ b/lib/bidding/pre-quote/table/bidding-pre-quote-vendor-create-dialog.tsx
@@ -0,0 +1,205 @@
+'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,
+} from '@/components/ui/command'
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from '@/components/ui/popover'
+import { Check, ChevronsUpDown } from 'lucide-react'
+import { cn } from '@/lib/utils'
+import { createBiddingCompany } from '@/lib/bidding/pre-quote/service'
+import { searchVendors } from '@/lib/vendors/service'
+import { useToast } from '@/hooks/use-toast'
+import { useTransition } from '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 [vendors, setVendors] = React.useState<Vendor[]>([])
+ const [selectedVendor, setSelectedVendor] = React.useState<Vendor | null>(null)
+ const [vendorSearchOpen, setVendorSearchOpen] = React.useState(false)
+ const [vendorSearchValue, setVendorSearchValue] = React.useState('')
+
+
+ // Vendor 검색
+ React.useEffect(() => {
+ const search = async () => {
+ if (vendorSearchValue.trim().length < 2) {
+ setVendors([])
+ return
+ }
+
+ try {
+ const result = await searchVendors(vendorSearchValue.trim(), 10)
+ setVendors(result)
+ } catch (error) {
+ console.error('Vendor search failed:', error)
+ setVendors([])
+ }
+ }
+
+ const debounceTimer = setTimeout(search, 300)
+ return () => clearTimeout(debounceTimer)
+ }, [vendorSearchValue])
+
+ const handleVendorSelect = (vendor: Vendor) => {
+ setSelectedVendor(vendor)
+ setVendorSearchValue(`${vendor.vendorName} (${vendor.vendorCode})`)
+ setVendorSearchOpen(false)
+ }
+
+ const handleCreate = () => {
+ if (!selectedVendor) {
+ toast({
+ title: '오류',
+ description: '업체를 선택해주세요.',
+ variant: 'destructive',
+ })
+ return
+ }
+
+ startTransition(async () => {
+ const response = await createBiddingCompany({
+ biddingId,
+ companyId: selectedVendor.id,
+ })
+ console.log(response)
+ if (response.success) {
+ toast({
+ title: '성공',
+ description: response.message,
+ })
+ onOpenChange(false)
+ resetForm()
+ onSuccess()
+ } else {
+ toast({
+ title: '오류',
+ description: response.error,
+ variant: 'destructive',
+ })
+ }
+ })
+ }
+
+ const resetForm = () => {
+ setSelectedVendor(null)
+ setVendorSearchValue('')
+ }
+
+ return (
+ <Dialog open={open} onOpenChange={onOpenChange}>
+ <DialogContent className="sm:max-w-[600px]">
+ <DialogHeader>
+ <DialogTitle>사전견적 업체 추가</DialogTitle>
+ <DialogDescription>
+ 검색해서 업체를 선택해주세요.
+ </DialogDescription>
+ </DialogHeader>
+ <div className="grid gap-4 py-4">
+ {/* Vendor 검색 */}
+ <div className="space-y-2">
+ <Label htmlFor="vendor-search">업체 검색</Label>
+ <Popover open={vendorSearchOpen} onOpenChange={setVendorSearchOpen}>
+ <PopoverTrigger asChild>
+ <Button
+ variant="outline"
+ role="combobox"
+ aria-expanded={vendorSearchOpen}
+ className="w-full justify-between"
+ >
+ {selectedVendor
+ ? `${selectedVendor.vendorName} (${selectedVendor.vendorCode})`
+ : "업체를 검색해서 선택하세요..."}
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent className="w-full p-0">
+ <Command>
+ <CommandInput
+ placeholder="업체명 또는 코드를 입력하세요..."
+ value={vendorSearchValue}
+ onValueChange={setVendorSearchValue}
+ />
+ <CommandEmpty>
+ {vendorSearchValue.length < 2
+ ? "최소 2자 이상 입력해주세요"
+ : "검색 결과가 없습니다"}
+ </CommandEmpty>
+ <CommandGroup className="max-h-64 overflow-auto">
+ {vendors.map((vendor) => (
+ <CommandItem
+ key={vendor.id}
+ value={`${vendor.vendorName} ${vendor.vendorCode}`}
+ onSelect={() => handleVendorSelect(vendor)}
+ >
+ <Check
+ className={cn(
+ "mr-2 h-4 w-4",
+ selectedVendor?.id === vendor.id ? "opacity-100" : "opacity-0"
+ )}
+ />
+ <div className="flex flex-col">
+ <span className="font-medium">{vendor.vendorName}</span>
+ <span className="text-sm text-muted-foreground">{vendor.vendorCode}</span>
+ </div>
+ </CommandItem>
+ ))}
+ </CommandGroup>
+ </Command>
+ </PopoverContent>
+ </Popover>
+ </div>
+
+ </div>
+ <DialogFooter>
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
+ 취소
+ </Button>
+ <Button onClick={handleCreate} disabled={isPending || !selectedVendor}>
+ 추가
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+}