"use client" import * as React from "react" import { type Row } from "@tanstack/react-table" import { Loader, Check, SendHorizonal, AlertCircle, AlertTriangle } from "lucide-react" import { toast } from "sonner" import { useMediaQuery } from "@/hooks/use-media-query" import { Button } from "@/components/ui/button" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger, } from "@/components/ui/drawer" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { ScrollArea } from "@/components/ui/scroll-area" import { Badge } from "@/components/ui/badge" import { Separator } from "@/components/ui/separator" import { Vendor } from "@/db/schema/vendors" import { requestInvestigateVendors, getExistingInvestigationsForVendors } from "@/lib/vendor-investigation/service" import { useSession } from "next-auth/react" import { formatDate } from "@/lib/utils" interface ApprovalVendorDialogProps extends React.ComponentPropsWithoutRef { vendors: Row["original"][] showTrigger?: boolean onSuccess?: () => void } // Helper function to get status badge variant and text function getStatusBadge(status: string) { switch (status) { case "REQUESTED": return { variant: "secondary", text: "Requested" } case "SCHEDULED": return { variant: "warning", text: "Scheduled" } case "IN_PROGRESS": return { variant: "default", text: "In Progress" } case "COMPLETED": return { variant: "success", text: "Completed" } case "CANCELLED": return { variant: "destructive", text: "Cancelled" } default: return { variant: "outline", text: status } } } export function RequestVendorsInvestigateDialog({ vendors, showTrigger = true, onSuccess, ...props }: ApprovalVendorDialogProps) { const [isApprovePending, startApproveTransition] = React.useTransition() const [isLoading, setIsLoading] = React.useState(true) const [existingInvestigations, setExistingInvestigations] = React.useState([]) const isDesktop = useMediaQuery("(min-width: 640px)") const { data: session } = useSession() // Fetch existing investigations when dialog opens React.useEffect(() => { if (vendors.length > 0) { setIsLoading(true) const fetchExistingInvestigations = async () => { try { const vendorIds = vendors.map(vendor => vendor.id) const result = await getExistingInvestigationsForVendors(vendorIds) setExistingInvestigations(result) } catch (error) { console.error("Failed to fetch existing investigations:", error) toast.error("Failed to fetch existing investigations") } finally { setIsLoading(false) } } fetchExistingInvestigations() } }, [vendors]) // Group vendors by investigation status const vendorsWithInvestigations = React.useMemo(() => { if (!existingInvestigations.length) return { withInvestigations: [], withoutInvestigations: vendors } const vendorMap = new Map(vendors.map(v => [v.id, v])) const withInvestigations: Array<{ vendor: typeof vendors[0], investigation: any }> = [] // Find vendors with existing investigations existingInvestigations.forEach(inv => { const vendor = vendorMap.get(inv.vendorId) if (vendor) { withInvestigations.push({ vendor, investigation: inv }) vendorMap.delete(inv.vendorId) } }) // Remaining vendors don't have investigations const withoutInvestigations = Array.from(vendorMap.values()) return { withInvestigations, withoutInvestigations } }, [vendors, existingInvestigations]) function onApprove() { if (!session?.user?.id) { toast.error("사용자 인증 정보를 찾을 수 없습니다.") return } // Only request investigations for vendors without existing ones const vendorsToRequest = vendorsWithInvestigations.withoutInvestigations if (vendorsToRequest.length === 0) { toast.info("모든 선택된 업체에 이미 실사 요청이 있습니다.") props.onOpenChange?.(false) return } startApproveTransition(async () => { const { error } = await requestInvestigateVendors({ ids: vendorsToRequest.map((vendor) => vendor.id), userId: Number(session.user.id) }) if (error) { toast.error(error) return } props.onOpenChange?.(false) toast.success(`${vendorsToRequest.length}개 업체에 대한 실사 요청을 보냈습니다.`) onSuccess?.() }) } const renderContent = () => { return ( <>
{isLoading ? (
) : ( <> {vendorsWithInvestigations.withInvestigations.length > 0 && ( 기존 실사 요청 정보가 있습니다 선택한 {vendors.length}개 업체 중 {vendorsWithInvestigations.withInvestigations.length}개 업체에 대한 기존 실사 요청이 있습니다. 새로운 요청은 기존 데이터가 없는 업체에만 적용됩니다. )} {vendorsWithInvestigations.withInvestigations.length > 0 && ( 기존 실사 요청 ({vendorsWithInvestigations.withInvestigations.length}) 업체명 상태 요청일 예정 일정 {vendorsWithInvestigations.withInvestigations.map(({ vendor, investigation }) => { const status = getStatusBadge(investigation.investigationStatus) return ( {vendor.vendorName} {status.text} {formatDate(investigation.createdAt, "KR")} {investigation.scheduledStartAt ? formatDate(investigation.scheduledStartAt, "KR") : "미정"} ) })}
)}

새로운 실사가 요청될 업체 ({vendorsWithInvestigations.withoutInvestigations.length})

{vendorsWithInvestigations.withoutInvestigations.length > 0 ? (
    {vendorsWithInvestigations.withoutInvestigations.map((vendor) => (
  • {vendor.vendorName} ({vendor.vendorCode || "코드 없음"})
  • ))}
) : (

모든 선택된 업체에 이미 실사 요청이 있습니다.

)}
)}
) } if (isDesktop) { return ( {showTrigger ? ( ) : null} Confirm Vendor Investigation Request 선택한 {vendors.length}개 업체에 대한 실사 요청을 확인합니다. 요청 후 협력업체실사담당자에게 알림이 전송됩니다. {renderContent()} ) } return ( {showTrigger ? ( ) : null} Confirm Vendor Investigation 선택한 {vendors.length}개 업체에 대한 실사 요청을 확인합니다. 요청 후 협력업체실사담당자에게 알림이 전송됩니다.
{renderContent()}
) }