diff options
Diffstat (limited to 'lib/vendor-investigation/table/vendor-details-dialog.tsx')
| -rw-r--r-- | lib/vendor-investigation/table/vendor-details-dialog.tsx | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/lib/vendor-investigation/table/vendor-details-dialog.tsx b/lib/vendor-investigation/table/vendor-details-dialog.tsx new file mode 100644 index 00000000..27ed7826 --- /dev/null +++ b/lib/vendor-investigation/table/vendor-details-dialog.tsx @@ -0,0 +1,341 @@ +"use client" + +import * as React from "react" +import { Building, Globe, Mail, MapPin, Phone, RefreshCw, Search } from "lucide-react" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogFooter, +} from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Skeleton } from "@/components/ui/skeleton" +import { Badge } from "@/components/ui/badge" +import { Separator } from "@/components/ui/separator" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { useToast } from "@/hooks/use-toast" + +// Import vendor service +import { getVendorById, getVendorItemsByVendorId } from "@/lib/vendor-investigation/service" +import { useRouter } from "next/navigation" + +interface VendorDetailsDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + vendorId: number | null +} + +export function VendorDetailsDialog({ + open, + onOpenChange, + vendorId, +}: VendorDetailsDialogProps) { + const { toast } = useToast() + const router = useRouter() + const [loading, setLoading] = React.useState(false) + const [vendorData, setVendorData] = React.useState<any>(null) + const [vendorItems, setVendorItems] = React.useState<any[]>([]) + const [activeTab, setActiveTab] = React.useState("details") + + // Fetch vendor details when the dialog opens + React.useEffect(() => { + if (open && vendorId) { + setLoading(true) + + // Fetch vendor details + Promise.all([ + getVendorById(vendorId), + getVendorItemsByVendorId(vendorId) + ]) + .then(([vendorDetails, items]) => { + setVendorData(vendorDetails) + setVendorItems(items || []) + }) + .catch((error) => { + console.error("Error fetching vendor data:", error) + toast({ + title: "Error", + description: "Failed to load vendor details. Please try again.", + variant: "destructive", + }) + }) + .finally(() => { + setLoading(false) + }) + } else { + // Reset state when the dialog closes + setVendorData(null) + setVendorItems([]) + } + }, [open, vendorId, toast]) + + // Handle refresh button click + const handleRefresh = () => { + if (!vendorId) return + + setLoading(true) + Promise.all([ + getVendorById(vendorId), + getVendorItemsByVendorId(vendorId) + ]) + .then(([vendorDetails, items]) => { + setVendorData(vendorDetails) + setVendorItems(items || []) + toast({ + title: "Refreshed", + description: "Vendor information has been refreshed.", + }) + }) + .catch((error) => { + console.error("Error refreshing vendor data:", error) + toast({ + title: "Error", + description: "Failed to refresh vendor details.", + variant: "destructive", + }) + }) + .finally(() => { + setLoading(false) + }) + } + + // Get vendor status badge variant + const getStatusVariant = (status: string) => { + switch (status?.toUpperCase()) { + case "ACTIVE": + return "default" + case "PENDING": + return "secondary" + case "SUSPENDED": + return "destructive" + case "APPROVED": + return "outline" + default: + return "secondary" + } + } + + // Navigate to full vendor profile page + const navigateToVendorProfile = () => { + if (!vendorId) return + + // Close dialog + onOpenChange(false) + + // Navigate to vendor profile page with router + router.push(`/evcp/vendors/${vendorId}`) + } + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="sm:max-w-[700px]"> + <DialogHeader> + <div className="flex items-center justify-between"> + <DialogTitle>협력업체 상세정보</DialogTitle> + <Button + variant="outline" + size="icon" + onClick={handleRefresh} + disabled={loading} + > + <RefreshCw className={`h-4 w-4 ${loading ? "animate-spin" : ""}`} /> + <span className="sr-only">새로고침</span> + </Button> + </div> + <DialogDescription> + 협력업체 정보 상세보기 + </DialogDescription> + </DialogHeader> + + {loading ? ( + <div className="space-y-4 py-4"> + <div className="flex items-center space-x-4"> + <Skeleton className="h-12 w-12 rounded-full" /> + <div className="space-y-2"> + <Skeleton className="h-4 w-[200px]" /> + <Skeleton className="h-4 w-[150px]" /> + </div> + </div> + <Skeleton className="h-[200px] w-full" /> + </div> + ) : vendorData ? ( + <div className="py-4"> + {/* Vendor header with main info */} + <div className="flex items-start justify-between mb-6"> + <div> + <h2 className="text-xl font-semibold">{vendorData.name}</h2> + <div className="flex items-center mt-1 space-x-2"> + <span className="text-sm text-muted-foreground">업체코드: {vendorData.code}</span> + {vendorData.taxId && ( + <> + <span className="text-muted-foreground">•</span> + <span className="text-sm text-muted-foreground">사업자등록번호: {vendorData.taxId}</span> + </> + )} + </div> + </div> + {vendorData.status && ( + <Badge variant={getStatusVariant(vendorData.status)}> + {vendorData.status} + </Badge> + )} + </div> + + <Tabs defaultValue="details" onValueChange={setActiveTab}> + <TabsList className="mb-4"> + <TabsTrigger value="details">상세</TabsTrigger> + <TabsTrigger value="items">공급품목({vendorItems.length})</TabsTrigger> + </TabsList> + + {/* Details Tab */} + <TabsContent value="details" className="space-y-4"> + {/* Contact Information Card */} + <Card> + <CardHeader className="pb-2"> + <CardTitle className="text-base">연락처 정보</CardTitle> + </CardHeader> + <CardContent className="space-y-2"> + <div className="grid grid-cols-1 md:grid-cols-2 gap-4"> + {/* Email */} + <div className="flex items-center space-x-2"> + <Mail className="h-4 w-4 text-muted-foreground" /> + <span className="text-sm">{vendorData.email || "No email provided"}</span> + </div> + + {/* Phone */} + <div className="flex items-center space-x-2"> + <Phone className="h-4 w-4 text-muted-foreground" /> + <span className="text-sm">{vendorData.phone || "No phone provided"}</span> + </div> + + {/* Website */} + {vendorData.website && ( + <div className="flex items-center space-x-2"> + <Globe className="h-4 w-4 text-muted-foreground" /> + <a + href={vendorData.website.startsWith('http') ? vendorData.website : `https://${vendorData.website}`} + target="_blank" + rel="noopener noreferrer" + className="text-sm text-blue-600 hover:underline" + > + {vendorData.website} + </a> + </div> + )} + + {/* Address */} + {vendorData.address && ( + <div className="flex items-center space-x-2"> + <MapPin className="h-4 w-4 text-muted-foreground" /> + <span className="text-sm">{vendorData.address}</span> + </div> + )} + + {/* Country */} + {vendorData.country && ( + <div className="flex items-center space-x-2"> + <Building className="h-4 w-4 text-muted-foreground" /> + <span className="text-sm">{vendorData.country}</span> + </div> + )} + </div> + </CardContent> + </Card> + + {/* Additional Information */} + {vendorData.description && ( + <Card> + <CardHeader className="pb-2"> + <CardTitle className="text-base">협력업체 설명</CardTitle> + </CardHeader> + <CardContent> + <p className="text-sm">{vendorData.description}</p> + </CardContent> + </Card> + )} + + {/* Registration Information */} + <Card> + <CardHeader className="pb-2"> + <CardTitle className="text-base">등록 정보</CardTitle> + </CardHeader> + <CardContent> + <div className="grid grid-cols-2 gap-2"> + <div> + <p className="text-xs text-muted-foreground">협력업체 생성일</p> + <p className="text-sm"> + {vendorData.createdAt + ? new Date(vendorData.createdAt).toLocaleDateString() + : "Unknown" + } + </p> + </div> + <div> + <p className="text-xs text-muted-foreground">협력업체 정보 업데이트일</p> + <p className="text-sm"> + {vendorData.updatedAt + ? new Date(vendorData.updatedAt).toLocaleDateString() + : "Unknown" + } + </p> + </div> + </div> + </CardContent> + </Card> + </TabsContent> + + {/* Items Tab */} + <TabsContent value="items"> + <ScrollArea className="h-[300px] pr-4"> + {vendorItems.length > 0 ? ( + <div className="space-y-4"> + {vendorItems.map((item) => ( + <Card key={item.id}> + <CardHeader className="pb-2"> + <CardTitle className="text-base">{item.itemName}</CardTitle> + <CardDescription>Code: {item.itemCode}</CardDescription> + </CardHeader> + {item.description && ( + <CardContent> + <p className="text-sm">{item.description}</p> + </CardContent> + )} + </Card> + ))} + </div> + ) : ( + <div className="flex flex-col items-center justify-center h-full text-center p-8"> + <Search className="h-8 w-8 text-muted-foreground mb-2" /> + <h3 className="text-lg font-semibold">No items found</h3> + <p className="text-sm text-muted-foreground">해당 업체는 아직 공급품목이 등록되지 않았습니다.</p> + </div> + )} + </ScrollArea> + </TabsContent> + + </Tabs> + </div> + ) : ( + <div className="py-6 text-center"> + <p className="text-muted-foreground">No vendor information available</p> + </div> + )} + + <DialogFooter> + <Button variant="outline" onClick={() => onOpenChange(false)}> + 닫기 + </Button> + {vendorData && ( + <Button onClick={navigateToVendorProfile}> + 전체 정보 보러가기 + </Button> + )} + </DialogFooter> + </DialogContent> + </Dialog> + ) +}
\ No newline at end of file |
