"use client" import * as React from "react" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { z } from "zod" import { Plus, Check, ChevronsUpDown, Search, Building, CalendarIcon } from "lucide-react" import { toast } from "sonner" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from "@/components/ui/command" import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Textarea } from "@/components/ui/textarea" import { Checkbox } from "@/components/ui/checkbox" import { cn, formatDate } from "@/lib/utils" import { addInitialRfqRecord, getIncotermsForSelection, getVendorsForSelection } from "../service" import { Calendar } from "@/components/ui/calendar" import { InitialRfqDetailView } from "@/db/schema" // Initial RFQ 추가 폼 스키마 const addInitialRfqSchema = z.object({ vendorId: z.number({ required_error: "벤더를 선택해주세요.", }), initialRfqStatus: z.enum(["DRAFT", "Init. RFQ Sent", "S/L Decline", "Init. RFQ Answered"], { required_error: "초기 RFQ 상태를 선택해주세요.", }).default("DRAFT"), dueDate: z.date({ required_error: "마감일을 선택해주세요.", }), validDate: z.date().optional(), incotermsCode: z.string().optional(), gtc: z.string().optional(), gtcValidDate: z.string().optional(), classification: z.string().optional(), sparepart: z.string().optional(), shortList: z.boolean().default(false), returnYn: z.boolean().default(false), cpRequestYn: z.boolean().default(false), prjectGtcYn: z.boolean().default(false), returnRevision: z.number().default(0), }) export type AddInitialRfqFormData = z.infer interface Vendor { id: number vendorName: string vendorCode: string country: string taxId: string status: string } interface Incoterm { id: number code: string description: string } interface AddInitialRfqDialogProps { rfqId: number onSuccess?: () => void defaultValues?: InitialRfqDetailView // 선택된 항목의 기본값 } export function AddInitialRfqDialog({ rfqId, onSuccess, defaultValues }: AddInitialRfqDialogProps) { const [open, setOpen] = React.useState(false) const [isSubmitting, setIsSubmitting] = React.useState(false) const [vendors, setVendors] = React.useState([]) const [vendorsLoading, setVendorsLoading] = React.useState(false) const [vendorSearchOpen, setVendorSearchOpen] = React.useState(false) const [incoterms, setIncoterms] = React.useState([]) const [incotermsLoading, setIncotermsLoading] = React.useState(false) const [incotermsSearchOpen, setIncotermsSearchOpen] = React.useState(false) // 기본값 설정 (선택된 항목이 있으면 해당 값 사용, 없으면 일반 기본값) const getDefaultFormValues = React.useCallback((): Partial => { if (defaultValues) { return { vendorId: defaultValues.vendorId, initialRfqStatus: "DRAFT", // 새로 추가할 때는 항상 DRAFT로 시작 dueDate: defaultValues.dueDate || new Date(), validDate: defaultValues.validDate, incotermsCode: defaultValues.incotermsCode || "", classification: defaultValues.classification || "", sparepart: defaultValues.sparepart || "", shortList: false, // 새로 추가할 때는 기본적으로 false returnYn: false, cpRequestYn: defaultValues.cpRequestYn || false, prjectGtcYn: defaultValues.prjectGtcYn || false, returnRevision: 0, } } return { initialRfqStatus: "DRAFT", shortList: false, returnYn: false, cpRequestYn: false, prjectGtcYn: false, returnRevision: 0, } }, [defaultValues]) const form = useForm({ resolver: zodResolver(addInitialRfqSchema), defaultValues: getDefaultFormValues(), }) // 벤더 목록 로드 const loadVendors = React.useCallback(async () => { setVendorsLoading(true) try { const vendorList = await getVendorsForSelection() setVendors(vendorList) } catch (error) { console.error("Failed to load vendors:", error) toast.error("벤더 목록을 불러오는데 실패했습니다.") } finally { setVendorsLoading(false) } }, []) // Incoterms 목록 로드 const loadIncoterms = React.useCallback(async () => { setIncotermsLoading(true) try { const incotermsList = await getIncotermsForSelection() setIncoterms(incotermsList) } catch (error) { console.error("Failed to load incoterms:", error) toast.error("Incoterms 목록을 불러오는데 실패했습니다.") } finally { setIncotermsLoading(false) } }, []) // 다이얼로그 열릴 때 실행 React.useEffect(() => { if (open) { // 폼을 기본값으로 리셋 form.reset(getDefaultFormValues()) // 데이터 로드 if (vendors.length === 0) { loadVendors() } if (incoterms.length === 0) { loadIncoterms() } } }, [open, vendors.length, incoterms.length, loadVendors, loadIncoterms, form, getDefaultFormValues]) // 다이얼로그 닫기 핸들러 const handleOpenChange = (newOpen: boolean) => { if (!newOpen && !isSubmitting) { form.reset(getDefaultFormValues()) } setOpen(newOpen) } // 폼 제출 const onSubmit = async (data: AddInitialRfqFormData) => { setIsSubmitting(true) try { const result = await addInitialRfqRecord({ ...data, rfqId, }) if (result.success) { toast.success(result.message || "초기 RFQ가 성공적으로 추가되었습니다.") form.reset(getDefaultFormValues()) handleOpenChange(false) onSuccess?.() } else { toast.error(result.message || "초기 RFQ 추가에 실패했습니다.") } } catch (error) { console.error("Submit error:", error) toast.error("초기 RFQ 추가 중 오류가 발생했습니다.") } finally { setIsSubmitting(false) } } // 선택된 벤더 정보 const selectedVendor = vendors.find(vendor => vendor.id === form.watch("vendorId")) const selectedIncoterm = incoterms.find(incoterm => incoterm.code === form.watch("incotermsCode")) // 기본값이 있을 때 버튼 텍스트 변경 const buttonText = defaultValues ? "유사 항목 추가" : "초기 RFQ 추가" const dialogTitle = defaultValues ? "유사 초기 RFQ 추가" : "초기 RFQ 추가" const dialogDescription = defaultValues ? "선택된 항목을 기본값으로 하여 새로운 초기 RFQ를 추가합니다." : "새로운 벤더를 대상으로 하는 초기 RFQ를 추가합니다." return ( {dialogTitle} {dialogDescription} {defaultValues && (
기본값 출처: {defaultValues.vendorName} ({defaultValues.vendorCode})
)}
{/* 벤더 선택 */} ( 벤더 선택 * 검색 결과가 없습니다. {vendors.map((vendor) => ( { field.onChange(vendor.id) setVendorSearchOpen(false) }} >
{vendor.vendorName}
{vendor.vendorCode} • {vendor.country} • {vendor.taxId}
))}
)} /> {/* 날짜 필드들 */}
( 견적 마감일 * date < new Date() || date < new Date("1900-01-01") } initialFocus /> )} /> ( 견적 유효일 date < new Date() || date < new Date("1900-01-01") } initialFocus /> )} />
{/* Incoterms 선택 */} ( Incoterms 검색 결과가 없습니다. {incoterms.map((incoterm) => ( { field.onChange(incoterm.code) setIncotermsSearchOpen(false) }} >
{incoterm.code} - {incoterm.description}
))}
)} /> {/* 옵션 체크박스 */}
(
CP 요청
)} /> (
Project용 GTC 사용
)} />
{/* 분류 정보 */}
( 선급 )} /> ( Spare part )} />
) }