From 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Tue, 25 Mar 2025 15:55:45 +0900 Subject: initial commit --- lib/rfqs/table/add-rfq-dialog.tsx | 349 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 lib/rfqs/table/add-rfq-dialog.tsx (limited to 'lib/rfqs/table/add-rfq-dialog.tsx') diff --git a/lib/rfqs/table/add-rfq-dialog.tsx b/lib/rfqs/table/add-rfq-dialog.tsx new file mode 100644 index 00000000..1d824bc0 --- /dev/null +++ b/lib/rfqs/table/add-rfq-dialog.tsx @@ -0,0 +1,349 @@ +"use client" + +import * as React from "react" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { Check, ChevronsUpDown } from "lucide-react" +import { toast } from "sonner" + +import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form" +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover" +import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem } from "@/components/ui/command" + +import { useSession } from "next-auth/react" +import { createRfqSchema, type CreateRfqSchema, RfqType } from "../validations" +import { createRfq, getBudgetaryRfqs } from "../service" +import { ProjectSelector } from "@/components/ProjectSelector" +import { type Project } from "../service" +import { cn } from "@/lib/utils" +import { BudgetaryRfqSelector } from "./BudgetaryRfqSelector" +import { type BudgetaryRfq as ServiceBudgetaryRfq } from "../service"; + +// 부모 RFQ 정보 타입 정의 +interface BudgetaryRfq { + id: number; + rfqCode: string; + description: string | null; +} + +interface AddRfqDialogProps { + rfqType?: RfqType; +} + +export function AddRfqDialog({ rfqType = RfqType.PURCHASE }: AddRfqDialogProps) { + const [open, setOpen] = React.useState(false) + const { data: session, status } = useSession() + const [budgetaryRfqs, setBudgetaryRfqs] = React.useState([]) + const [isLoadingBudgetary, setIsLoadingBudgetary] = React.useState(false) + const [budgetarySearchOpen, setBudgetarySearchOpen] = React.useState(false) + const [budgetarySearchTerm, setBudgetarySearchTerm] = React.useState("") + const [selectedBudgetaryRfq, setSelectedBudgetaryRfq] = React.useState(null) + + // Get the user ID safely, ensuring it's a valid number + const userId = React.useMemo(() => { + const id = session?.user?.id ? Number(session.user.id) : null; + + // Debug logging - remove in production + console.log("Session status:", status); + console.log("Session data:", session); + console.log("User ID:", id); + + return id; + }, [session, status]); + + // RfqType에 따른 타이틀 생성 + const getTitle = () => { + return rfqType === RfqType.PURCHASE + ? "Purchase RFQ" + : "Budgetary RFQ"; + }; + + // RHF + Zod + const form = useForm({ + resolver: zodResolver(createRfqSchema), + defaultValues: { + rfqCode: "", + description: "", + projectId: undefined, + parentRfqId: undefined, + dueDate: new Date(), + status: "DRAFT", + rfqType: rfqType, + // Don't set createdBy yet - we'll set it when the form is submitted + createdBy: undefined, + }, + }); + + // Update form values when session loads + React.useEffect(() => { + if (status === "authenticated" && userId) { + form.setValue("createdBy", userId); + } + }, [status, userId, form]); + + // Budgetary RFQ 목록 로드 (Purchase RFQ 생성 시만) + React.useEffect(() => { + if (rfqType === RfqType.PURCHASE && open) { + const loadBudgetaryRfqs = async () => { + setIsLoadingBudgetary(true); + try { + const result = await getBudgetaryRfqs(); + if ('rfqs' in result) { + setBudgetaryRfqs(result.rfqs as unknown as BudgetaryRfq[]); + } else if ('error' in result) { + console.error("Budgetary RFQs 로드 오류:", result.error); + } + } catch (error) { + console.error("Budgetary RFQs 로드 오류:", error); + } finally { + setIsLoadingBudgetary(false); + } + }; + + loadBudgetaryRfqs(); + } + }, [rfqType, open]); + + // 검색어로 필터링된 Budgetary RFQ 목록 + const filteredBudgetaryRfqs = React.useMemo(() => { + if (!budgetarySearchTerm.trim()) return budgetaryRfqs; + + const lowerSearch = budgetarySearchTerm.toLowerCase(); + return budgetaryRfqs.filter( + rfq => + rfq.rfqCode.toLowerCase().includes(lowerSearch) || + (rfq.description && rfq.description.toLowerCase().includes(lowerSearch)) + ); + }, [budgetaryRfqs, budgetarySearchTerm]); + + // 프로젝트 선택 처리 + const handleProjectSelect = (project: Project) => { + form.setValue("projectId", project.id); + }; + + // Budgetary RFQ 선택 처리 + const handleBudgetaryRfqSelect = (rfq: BudgetaryRfq) => { + setSelectedBudgetaryRfq(rfq); + form.setValue("parentRfqId", rfq.id); + setBudgetarySearchOpen(false); + }; + + async function onSubmit(data: CreateRfqSchema) { + // Check if user is authenticated before submitting + if (status !== "authenticated" || !userId) { + toast.error("사용자 인증이 필요합니다. 다시 로그인해주세요."); + return; + } + + // Make sure createdBy is set with the current user ID + const submitData = { + ...data, + createdBy: userId + }; + + console.log("Submitting form data:", submitData); + + const result = await createRfq(submitData); + if (result.error) { + toast.error(`에러: ${result.error}`); + return; + } + + toast.success("RFQ가 성공적으로 생성되었습니다."); + form.reset(); + setSelectedBudgetaryRfq(null); + setOpen(false); + } + + function handleDialogOpenChange(nextOpen: boolean) { + if (!nextOpen) { + form.reset(); + setSelectedBudgetaryRfq(null); + } + setOpen(nextOpen); + } + + // Return a message or disabled state if user is not authenticated + if (status === "loading") { + return ; + } + + return ( + + {/* 모달을 열기 위한 버튼 */} + + + + + + + Create New {getTitle()} + + 새 {getTitle()} 정보를 입력하고 Create 버튼을 누르세요. + + + +
+ +
+ {/* rfqType - hidden field */} + ( + + )} + /> + + {/* Project Selector */} + ( + + Project + + + + + + )} + /> + + {/* Budgetary RFQ Selector - 구매용 RFQ 생성 시에만 표시 */} + {rfqType === RfqType.PURCHASE && ( + ( + + Budgetary RFQ (Optional) + + { + setSelectedBudgetaryRfq(rfq as any); + form.setValue("parentRfqId", rfq?.id); + }} + placeholder="Budgetary RFQ 선택..." + /> + + + + )} + /> + )} + + {/* rfqCode */} + ( + + RFQ Code + + + + + + )} + /> + + {/* description */} + ( + + RFQ Description + + + + + + )} + /> + + {/* dueDate */} + ( + + Due Date + + { + const val = e.target.value + if (val) { + field.onChange(new Date(val + "T00:00:00")) + } + }} + /> + + + + )} + /> + + {/* status (Read-only) */} + ( + + Status + + {}} // Prevent changes + /> + + + + )} + /> +
+ + + + + +
+ +
+
+ ) +} \ No newline at end of file -- cgit v1.2.3