"use client" import * as React from "react" import { zodResolver } from "@hookform/resolvers/zod" import { useForm } from "react-hook-form" import { toast } from "sonner" import { z } from "zod" import { Loader2, Building, CheckCircle2, XCircle } from "lucide-react" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Badge } from "@/components/ui/badge" import { Separator } from "@/components/ui/separator" import { ScrollArea } from "@/components/ui/scroll-area" import { shortListConfirm } from "../service" import { InitialRfqDetailView } from "@/db/schema" const shortListSchema = z.object({ selectedVendorIds: z.array(z.number()).min(1, "최소 1개 이상의 벤더를 선택해야 합니다."), }) type ShortListFormData = z.infer interface ShortListConfirmDialogProps { open: boolean onOpenChange: (open: boolean) => void rfqId: number vendors: InitialRfqDetailView[] onSuccess?: () => void } export function ShortListConfirmDialog({ open, onOpenChange, rfqId, vendors, onSuccess }: ShortListConfirmDialogProps) { const [isLoading, setIsLoading] = React.useState(false) const form = useForm({ resolver: zodResolver(shortListSchema), defaultValues: { selectedVendorIds: vendors .filter(vendor => vendor.shortList === true) .map(vendor => vendor.vendorId) .filter(Boolean) as number[] }, }) const watchedSelectedIds = form.watch("selectedVendorIds") // 선택된/탈락된 벤더 계산 const selectedVendors = vendors.filter(vendor => vendor.vendorId && watchedSelectedIds.includes(vendor.vendorId) ) const rejectedVendors = vendors.filter(vendor => vendor.vendorId && !watchedSelectedIds.includes(vendor.vendorId) ) async function onSubmit(data: ShortListFormData) { if (!rfqId) return setIsLoading(true) try { const result = await shortListConfirm({ rfqId, selectedVendorIds: data.selectedVendorIds, rejectedVendorIds: vendors .filter(v => v.vendorId && !data.selectedVendorIds.includes(v.vendorId)) .map(v => v.vendorId!) }) if (result.success) { toast.success(result.message) onOpenChange(false) form.reset() onSuccess?.() } else { toast.error(result.message || "Short List 확정에 실패했습니다.") } } catch (error) { console.error("Short List confirm error:", error) toast.error("Short List 확정 중 오류가 발생했습니다.") } finally { setIsLoading(false) } } const handleVendorToggle = (vendorId: number, checked: boolean) => { const currentSelected = form.getValues("selectedVendorIds") if (checked) { form.setValue("selectedVendorIds", [...currentSelected, vendorId]) } else { form.setValue("selectedVendorIds", currentSelected.filter(id => id !== vendorId)) } } return ( Short List 확정 최종 RFQ로 진행할 벤더를 선택해주세요. 선택되지 않은 벤더에게는 자동으로 Letter of Regret이 발송됩니다.
( 벤더 선택 ({vendors.length}개 업체)
{vendors.map((vendor) => { const isSelected = vendor.vendorId && watchedSelectedIds.includes(vendor.vendorId) return (
vendor.vendorId && handleVendorToggle(vendor.vendorId, !!checked) } className="mt-1" />
{vendor.vendorName} {isSelected ? ( 선택됨 ) : ( 탈락 )}
{vendor.vendorCode} {vendor.vendorCountry && ( <> {vendor.vendorCountry === "KR" ? "국내" : "해외"} )} {vendor.vendorCategory && ( <> {vendor.vendorCategory} )} {vendor.vendorBusinessSize && ( <> {vendor.vendorBusinessSize} )}
RFQ 상태: {vendor.initialRfqStatus}
) })}
)} /> {/* 요약 정보 */}
선택된 벤더
{selectedVendors.length}개 업체
{selectedVendors.length > 0 && (
{selectedVendors.map(v => v.vendorName).join(", ")}
)}
탈락 벤더
{rejectedVendors.length}개 업체
{rejectedVendors.length > 0 && (
Letter of Regret 발송 예정
)}
) }