summaryrefslogtreecommitdiff
path: root/lib/vendors/table/approve-vendor-dialog.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-09-30 06:41:26 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-09-30 06:41:26 +0000
commit9e3458481a65bb5572b7f1916e7c068b54a434c5 (patch)
tree27cc8dfd5fc0ed2efba4b87998caf6b2747ad312 /lib/vendors/table/approve-vendor-dialog.tsx
parentf9afa89a4f27283f5b115cd89ececa08145b5c89 (diff)
(최겸) 구매 협력업체 정기평가, 가입승인, 기본계약 리비전 등
Diffstat (limited to 'lib/vendors/table/approve-vendor-dialog.tsx')
-rw-r--r--lib/vendors/table/approve-vendor-dialog.tsx154
1 files changed, 125 insertions, 29 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>