summaryrefslogtreecommitdiff
path: root/lib/vendors/table/approve-vendor-dialog.tsx
diff options
context:
space:
mode:
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>