summaryrefslogtreecommitdiff
path: root/lib/vendors/table/request-vendor-investigate-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vendors/table/request-vendor-investigate-dialog.tsx')
-rw-r--r--lib/vendors/table/request-vendor-investigate-dialog.tsx345
1 files changed, 0 insertions, 345 deletions
diff --git a/lib/vendors/table/request-vendor-investigate-dialog.tsx b/lib/vendors/table/request-vendor-investigate-dialog.tsx
deleted file mode 100644
index a0d84128..00000000
--- a/lib/vendors/table/request-vendor-investigate-dialog.tsx
+++ /dev/null
@@ -1,345 +0,0 @@
-"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<typeof Dialog> {
- vendors: Row<Vendor>["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<any[]>([])
- 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 (
- <>
- <div className="space-y-4">
- {isLoading ? (
- <div className="flex items-center justify-center py-4">
- <Loader className="size-6 animate-spin text-muted-foreground" />
- </div>
- ) : (
- <>
- {vendorsWithInvestigations.withInvestigations.length > 0 && (
- <Alert>
- <AlertTriangle className="h-4 w-4" />
- <AlertTitle>기존 실사 요청 정보가 있습니다</AlertTitle>
- <AlertDescription>
- 선택한 {vendors.length}개 업체 중 {vendorsWithInvestigations.withInvestigations.length}개 업체에 대한
- 기존 실사 요청이 있습니다. 새로운 요청은 기존 데이터가 없는 업체에만 적용됩니다.
- </AlertDescription>
- </Alert>
- )}
-
- {vendorsWithInvestigations.withInvestigations.length > 0 && (
- <Accordion type="single" collapsible className="w-full">
- <AccordionItem value="existing-investigations">
- <AccordionTrigger className="font-medium">
- 기존 실사 요청 ({vendorsWithInvestigations.withInvestigations.length})
- </AccordionTrigger>
- <AccordionContent>
- <ScrollArea className="max-h-[200px]">
- <Table>
- <TableHeader>
- <TableRow>
- <TableHead>업체명</TableHead>
- <TableHead>상태</TableHead>
- <TableHead>요청일</TableHead>
- <TableHead>예정 일정</TableHead>
- </TableRow>
- </TableHeader>
- <TableBody>
- {vendorsWithInvestigations.withInvestigations.map(({ vendor, investigation }) => {
- const status = getStatusBadge(investigation.investigationStatus)
- return (
- <TableRow key={investigation.investigationId}>
- <TableCell className="font-medium">{vendor.vendorName}</TableCell>
- <TableCell>
- <Badge variant={status.variant as any}>{status.text}</Badge>
- </TableCell>
- <TableCell>{formatDate(investigation.createdAt, "KR")}</TableCell>
- <TableCell>
- {investigation.scheduledStartAt
- ? formatDate(investigation.scheduledStartAt, "KR")
- : "미정"}
- </TableCell>
- </TableRow>
- )
- })}
- </TableBody>
- </Table>
- </ScrollArea>
- </AccordionContent>
- </AccordionItem>
- </Accordion>
- )}
-
- <div>
- <h3 className="text-sm font-medium mb-2">
- 새로운 실사가 요청될 업체 ({vendorsWithInvestigations.withoutInvestigations.length})
- </h3>
- {vendorsWithInvestigations.withoutInvestigations.length > 0 ? (
- <ScrollArea className="max-h-[200px]">
- <ul className="space-y-1">
- {vendorsWithInvestigations.withoutInvestigations.map((vendor) => (
- <li key={vendor.id} className="text-sm py-1 px-2 border-b">
- {vendor.vendorName} ({vendor.vendorCode || "코드 없음"})
- </li>
- ))}
- </ul>
- </ScrollArea>
- ) : (
- <p className="text-sm text-muted-foreground py-2">
- 모든 선택된 업체에 이미 실사 요청이 있습니다.
- </p>
- )}
- </div>
- </>
- )}
- </div>
- </>
- )
- }
-
- if (isDesktop) {
- return (
- <Dialog {...props}>
- {showTrigger ? (
- <DialogTrigger asChild>
- <Button variant="outline" size="sm" className="gap-2">
- <SendHorizonal className="size-4" aria-hidden="true" />
- 실사 요청 ({vendors.length})
- </Button>
- </DialogTrigger>
- ) : null}
- <DialogContent className="max-w-md">
- <DialogHeader>
- <DialogTitle>Confirm Vendor Investigation Request</DialogTitle>
- <DialogDescription>
- 선택한 {vendors.length}개 업체에 대한 실사 요청을 확인합니다.
- 요청 후 협력업체실사담당자에게 알림이 전송됩니다.
- </DialogDescription>
- </DialogHeader>
-
- {renderContent()}
-
- <DialogFooter className="gap-2 sm:space-x-0 mt-4">
- <DialogClose asChild>
- <Button variant="outline">취소</Button>
- </DialogClose>
- <Button
- aria-label="Request selected vendors"
- variant="default"
- onClick={onApprove}
- disabled={isApprovePending || isLoading || vendorsWithInvestigations.withoutInvestigations.length === 0}
- >
- {isApprovePending && (
- <Loader
- className="mr-2 size-4 animate-spin"
- aria-hidden="true"
- />
- )}
- 요청하기
- </Button>
- </DialogFooter>
- </DialogContent>
- </Dialog>
- )
- }
-
- return (
- <Drawer {...props}>
- {showTrigger ? (
- <DrawerTrigger asChild>
- <Button variant="outline" size="sm" className="gap-2">
- <Check className="size-4" aria-hidden="true" />
- Investigation Request ({vendors.length})
- </Button>
- </DrawerTrigger>
- ) : null}
- <DrawerContent>
- <DrawerHeader>
- <DrawerTitle>Confirm Vendor Investigation</DrawerTitle>
- <DrawerDescription>
- 선택한 {vendors.length}개 업체에 대한 실사 요청을 확인합니다.
- 요청 후 협력업체실사담당자에게 알림이 전송됩니다.
- </DrawerDescription>
- </DrawerHeader>
-
- <div className="px-4">
- {renderContent()}
- </div>
-
- <DrawerFooter className="gap-2 sm:space-x-0 mt-4">
- <DrawerClose asChild>
- <Button variant="outline">취소</Button>
- </DrawerClose>
- <Button
- aria-label="Request selected vendors"
- variant="default"
- onClick={onApprove}
- disabled={isApprovePending || isLoading || vendorsWithInvestigations.withoutInvestigations.length === 0}
- >
- {isApprovePending && (
- <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
- )}
- 요청하기
- </Button>
- </DrawerFooter>
- </DrawerContent>
- </Drawer>
- )
-} \ No newline at end of file