diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-30 06:41:26 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-09-30 06:41:26 +0000 |
| commit | 9e3458481a65bb5572b7f1916e7c068b54a434c5 (patch) | |
| tree | 27cc8dfd5fc0ed2efba4b87998caf6b2747ad312 /lib/vendors/table | |
| parent | f9afa89a4f27283f5b115cd89ececa08145b5c89 (diff) | |
(최겸) 구매 협력업체 정기평가, 가입승인, 기본계약 리비전 등
Diffstat (limited to 'lib/vendors/table')
| -rw-r--r-- | lib/vendors/table/approve-vendor-dialog.tsx | 154 | ||||
| -rw-r--r-- | lib/vendors/table/vendors-table-toolbar-actions.tsx | 4 |
2 files changed, 127 insertions, 31 deletions
diff --git a/lib/vendors/table/approve-vendor-dialog.tsx b/lib/vendors/table/approve-vendor-dialog.tsx index 940710f5..980953aa 100644 --- a/lib/vendors/table/approve-vendor-dialog.tsx +++ b/lib/vendors/table/approve-vendor-dialog.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { type Row } from "@tanstack/react-table" -import { Loader, Check } from "lucide-react" +import { Loader, Check, X } from "lucide-react" import { toast } from "sonner" import { useMediaQuery } from "@/hooks/use-media-query" @@ -28,23 +28,24 @@ import { DrawerTrigger, } from "@/components/ui/drawer" import { Vendor } from "@/db/schema/vendors" -import { approveVendors } from "../service" +import { approveVendors, rejectVendors } from "../service" import { useSession } from "next-auth/react" -interface ApprovalVendorDialogProps +interface VendorDecisionDialogProps extends React.ComponentPropsWithoutRef<typeof Dialog> { vendors: Row<Vendor>["original"][] showTrigger?: boolean onSuccess?: () => void } -export function ApproveVendorsDialog({ +export function VendorDecisionDialog({ vendors, showTrigger = true, onSuccess, ...props -}: ApprovalVendorDialogProps) { +}: VendorDecisionDialogProps) { const [isApprovePending, startApproveTransition] = React.useTransition() + const [isRejectPending, startRejectTransition] = React.useTransition() const isDesktop = useMediaQuery("(min-width: 640px)") const { data: session } = useSession() @@ -58,10 +59,10 @@ export function ApproveVendorsDialog({ try { console.log("🔍 [DEBUG] 승인 요청 시작 - vendors:", vendors.map(v => ({ id: v.id, vendorName: v.vendorName, email: v.email }))); console.log("🔍 [DEBUG] 세션 정보:", { userId: session.user.id, userType: typeof session.user.id }); - + const { error } = await approveVendors({ ids: vendors.map((vendor) => vendor.id), - userId: Number(session.user.id) + userId: Number(session.user.id) }) if (error) { @@ -72,7 +73,40 @@ export function ApproveVendorsDialog({ console.log("✅ [DEBUG] 승인 처리 성공"); props.onOpenChange?.(false) - toast.success("Vendors successfully approved for review") + toast.success("협력업체 등록이 승인되었습니다.") + onSuccess?.() + } catch (error) { + console.error("🚨 [DEBUG] 예상치 못한 에러:", error); + toast.error("예상치 못한 오류가 발생했습니다.") + } + }) + } + + function onReject() { + if (!session?.user?.id) { + toast.error("사용자 인증 정보를 찾을 수 없습니다.") + return + } + + startRejectTransition(async () => { + try { + console.log("🔍 [DEBUG] 거절 요청 시작 - vendors:", vendors.map(v => ({ id: v.id, vendorName: v.vendorName, email: v.email }))); + console.log("🔍 [DEBUG] 세션 정보:", { userId: session.user.id, userType: typeof session.user.id }); + + const { error } = await rejectVendors({ + ids: vendors.map((vendor) => vendor.id), + userId: Number(session.user.id) + }) + + if (error) { + console.error("🚨 [DEBUG] 거절 처리 에러:", error); + toast.error(error) + return + } + + console.log("✅ [DEBUG] 거절 처리 성공"); + props.onOpenChange?.(false) + toast.success("협력업체 등록이 거절되었습니다.") onSuccess?.() } catch (error) { console.error("🚨 [DEBUG] 예상치 못한 에러:", error); @@ -88,29 +122,58 @@ export function ApproveVendorsDialog({ <DialogTrigger asChild> <Button variant="outline" size="sm" className="gap-2"> <Check className="size-4" aria-hidden="true" /> - 가입 Approve ({vendors.length}) + 가입 결정 ({vendors.length}) </Button> </DialogTrigger> ) : null} - <DialogContent> + <DialogContent className="max-w-2xl"> <DialogHeader> - <DialogTitle>Confirm Vendor Approval</DialogTitle> + <DialogTitle>협력업체 가입 결정</DialogTitle> <DialogDescription> - Are you sure you want to approve{" "} - <span className="font-medium">{vendors.length}</span> - {vendors.length === 1 ? " vendor" : " vendors"}? - After approval, vendors will be notified and can login to submit PQ information. + 선택한 <span className="font-medium">{vendors.length}</span>개 협력업체에 대한 가입 결정을 해주세요. </DialogDescription> </DialogHeader> + + {/* 선택한 벤더 목록 표시 */} + <div className="max-h-64 overflow-y-auto border rounded-md p-4"> + <h4 className="font-medium mb-2">선택된 협력업체:</h4> + <div className="space-y-2"> + {vendors.map((vendor) => ( + <div key={vendor.id} className="flex items-center justify-between p-2 bg-gray-50 rounded"> + <div> + <div className="font-medium">{vendor.vendorName}</div> + <div className="text-sm text-gray-600">{vendor.email}</div> + </div> + <div className="text-sm text-gray-500">ID: {vendor.id}</div> + </div> + ))} + </div> + </div> + <DialogFooter className="gap-2 sm:space-x-0"> <DialogClose asChild> - <Button variant="outline">Cancel</Button> + <Button variant="outline">취소</Button> </DialogClose> <Button + aria-label="Reject selected vendors" + variant="destructive" + onClick={onReject} + disabled={isRejectPending || isApprovePending} + > + {isRejectPending && ( + <Loader + className="mr-2 size-4 animate-spin" + aria-hidden="true" + /> + )} + <X className="mr-2 size-4" aria-hidden="true" /> + 거절 + </Button> + <Button aria-label="Approve selected vendors" variant="default" onClick={onApprove} - disabled={isApprovePending} + disabled={isApprovePending || isRejectPending} > {isApprovePending && ( <Loader @@ -118,7 +181,8 @@ export function ApproveVendorsDialog({ aria-hidden="true" /> )} - Approve + <Check className="mr-2 size-4" aria-hidden="true" /> + 승인 </Button> </DialogFooter> </DialogContent> @@ -132,34 +196,66 @@ export function ApproveVendorsDialog({ <DrawerTrigger asChild> <Button variant="outline" size="sm" className="gap-2"> <Check className="size-4" aria-hidden="true" /> - Approve ({vendors.length}) + 가입 결정 ({vendors.length}) </Button> </DrawerTrigger> ) : null} - <DrawerContent> + <DrawerContent className="max-h-[80vh]"> <DrawerHeader> - <DrawerTitle>Confirm Vendor Approval</DrawerTitle> + <DrawerTitle>협력업체 가입 결정</DrawerTitle> <DrawerDescription> - Are you sure you want to approve{" "} - <span className="font-medium">{vendors.length}</span> - {vendors.length === 1 ? " vendor" : " vendors"}? - After approval, vendors will be notified and can login to submit PQ information. + 선택한 <span className="font-medium">{vendors.length}</span>개 협력업체에 대한 가입 결정을 해주세요. </DrawerDescription> </DrawerHeader> + + {/* 선택한 벤더 목록 표시 */} + <div className="max-h-48 overflow-y-auto px-4"> + <h4 className="font-medium mb-2">선택된 협력업체:</h4> + <div className="space-y-2"> + {vendors.map((vendor) => ( + <div key={vendor.id} className="flex items-center justify-between p-2 bg-gray-50 rounded"> + <div> + <div className="font-medium">{vendor.vendorName}</div> + <div className="text-sm text-gray-600">{vendor.email}</div> + </div> + <div className="text-sm text-gray-500">ID: {vendor.id}</div> + </div> + ))} + </div> + </div> + <DrawerFooter className="gap-2 sm:space-x-0"> <DrawerClose asChild> - <Button variant="outline">Cancel</Button> + <Button variant="outline">취소</Button> </DrawerClose> <Button + aria-label="Reject selected vendors" + variant="destructive" + onClick={onReject} + disabled={isRejectPending || isApprovePending} + > + {isRejectPending && ( + <Loader + className="mr-2 size-4 animate-spin" + aria-hidden="true" + /> + )} + <X className="mr-2 size-4" aria-hidden="true" /> + 거절 + </Button> + <Button aria-label="Approve selected vendors" variant="default" onClick={onApprove} - disabled={isApprovePending} + disabled={isApprovePending || isRejectPending} > {isApprovePending && ( - <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" /> + <Loader className="mr-2 size-4 animate-spin" + aria-hidden="true" + /> )} - Approve + <Check className="mr-2 size-4" aria-hidden="true" /> + 승인 </Button> </DrawerFooter> </DrawerContent> diff --git a/lib/vendors/table/vendors-table-toolbar-actions.tsx b/lib/vendors/table/vendors-table-toolbar-actions.tsx index 3d77486d..def46168 100644 --- a/lib/vendors/table/vendors-table-toolbar-actions.tsx +++ b/lib/vendors/table/vendors-table-toolbar-actions.tsx @@ -15,7 +15,7 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { VendorWithType } from "@/db/schema/vendors" -import { ApproveVendorsDialog } from "./approve-vendor-dialog" +import { VendorDecisionDialog } from "./approve-vendor-dialog" import { RequestPQVendorsDialog } from "./request-vendor-pg-dialog" import { RequestPQDialog } from "./request-pq-dialog" import { RequestProjectPQDialog } from "./request-project-pq-dialog" @@ -147,7 +147,7 @@ export function VendorsTableToolbarActions({ table }: VendorsTableToolbarActions <div className="flex items-center gap-2"> {/* 승인 다이얼로그: PENDING_REVIEW 상태인 협력업체가 있을 때만 표시 */} {pendingReviewVendors.length > 0 && ( - <ApproveVendorsDialog + <VendorDecisionDialog vendors={pendingReviewVendors} onSuccess={() => table.toggleAllRowsSelected(false)} /> |
