diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-17 07:06:52 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-11-17 07:06:52 +0000 |
| commit | 1532c1bf4a3236ce9056f33e51ddfebeff79ed5a (patch) | |
| tree | c3f3c43d0f73c4b7df4e34e9b1aa308604c562c9 /lib/vendors/table | |
| parent | a5be73b70e7d8e6be1724252e6923c664c3771f4 (diff) | |
(최겸) 구매 피드백 처리
Diffstat (limited to 'lib/vendors/table')
| -rw-r--r-- | lib/vendors/table/request-vendor-investigate-dialog.tsx | 345 | ||||
| -rw-r--r-- | lib/vendors/table/vendors-table-toolbar-actions.tsx | 1 |
2 files changed, 0 insertions, 346 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 diff --git a/lib/vendors/table/vendors-table-toolbar-actions.tsx b/lib/vendors/table/vendors-table-toolbar-actions.tsx index def46168..a2611b64 100644 --- a/lib/vendors/table/vendors-table-toolbar-actions.tsx +++ b/lib/vendors/table/vendors-table-toolbar-actions.tsx @@ -20,7 +20,6 @@ import { RequestPQVendorsDialog } from "./request-vendor-pg-dialog" import { RequestPQDialog } from "./request-pq-dialog" import { RequestProjectPQDialog } from "./request-project-pq-dialog" import { SendVendorsDialog } from "./send-vendor-dialog" -import { RequestVendorsInvestigateDialog } from "./request-vendor-investigate-dialog" import { RequestInfoDialog } from "./request-additional-Info-dialog" import { RequestContractDialog } from "./request-basicContract-dialog" import { exportVendorsWithRelatedData } from "./vendor-all-export" |
