diff options
Diffstat (limited to 'lib/rfqs/table/add-rfq-dialog.tsx')
| -rw-r--r-- | lib/rfqs/table/add-rfq-dialog.tsx | 468 |
1 files changed, 0 insertions, 468 deletions
diff --git a/lib/rfqs/table/add-rfq-dialog.tsx b/lib/rfqs/table/add-rfq-dialog.tsx deleted file mode 100644 index 67561b4f..00000000 --- a/lib/rfqs/table/add-rfq-dialog.tsx +++ /dev/null @@ -1,468 +0,0 @@ -"use client" - -import * as React from "react" -import { useForm } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -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 { useSession } from "next-auth/react" -import { createRfqSchema, type CreateRfqSchema, RfqType } from "../validations" -import { createRfq, generateNextRfqCode, getBudgetaryRfqs } from "../service" -import { ProjectSelector } from "@/components/ProjectSelector" -import { type Project } from "../service" -import { ParentRfqSelector } from "./ParentRfqSelector" -import { EstimateProjectSelector } from "@/components/BidProjectSelector" - -// 부모 RFQ 정보 타입 정의 -interface ParentRfq { - id: number; - rfqCode: string; - description: string | null; - rfqType: RfqType; - projectId: number | null; - projectCode: string | null; - projectName: 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 [parentRfqs, setParentRfqs] = React.useState<ParentRfq[]>([]) - const [isLoadingParents, setIsLoadingParents] = React.useState(false) - const [selectedParentRfq, setSelectedParentRfq] = React.useState<ParentRfq | null>(null) - const [isLoadingRfqCode, setIsLoadingRfqCode] = React.useState(false) - - // 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; - - return id; - }, [session, status]); - - // RfqType에 따른 타이틀 생성 - const getTitle = () => { - switch (rfqType) { - case RfqType.PURCHASE: - return "Purchase RFQ"; - case RfqType.BUDGETARY: - return "Budgetary RFQ"; - case RfqType.PURCHASE_BUDGETARY: - return "Purchase Budgetary RFQ"; - default: - return "RFQ"; - } - }; - - // RfqType 설명 가져오기 - const getTypeDescription = () => { - switch (rfqType) { - case RfqType.PURCHASE: - return "실제 구매 발주 전에 가격을 요청"; - case RfqType.BUDGETARY: - return "기술영업 단계에서 입찰가 산정을 위한 견적 요청"; - case RfqType.PURCHASE_BUDGETARY: - return "프로젝트 수주 후, 공식 입찰 전 예산 책정을 위한 가격 요청"; - default: - return ""; - } - }; - - // RHF + Zod - const form = useForm<CreateRfqSchema>({ - 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]); - - // 다이얼로그가 열릴 때 자동으로 RFQ 코드 생성 - React.useEffect(() => { - if (open) { - const generateRfqCode = async () => { - setIsLoadingRfqCode(true); - try { - // 서버 액션 호출 - const result = await generateNextRfqCode(rfqType); - - if (result.error) { - toast.error(`RFQ 코드 생성 실패: ${result.error}`); - return; - } - - // 생성된 코드를 폼에 설정 - form.setValue("rfqCode", result.code); - } catch (error) { - console.error("RFQ 코드 생성 오류:", error); - toast.error("RFQ 코드 생성에 실패했습니다"); - } finally { - setIsLoadingRfqCode(false); - } - }; - - generateRfqCode(); - } - }, [open, rfqType, form]); - - // 현재 RFQ 타입에 따라 선택 가능한 부모 RFQ 타입들 결정 - const getParentRfqTypes = (): RfqType[] => { - switch (rfqType) { - case RfqType.PURCHASE: - // PURCHASE는 BUDGETARY와 PURCHASE_BUDGETARY를 부모로 가질 수 있음 - return [RfqType.BUDGETARY, RfqType.PURCHASE_BUDGETARY]; - case RfqType.PURCHASE_BUDGETARY: - // PURCHASE_BUDGETARY는 BUDGETARY만 부모로 가질 수 있음 - return [RfqType.BUDGETARY]; - default: - return []; - } - }; - - // 선택 가능한 부모 RFQ 목록 로드 - React.useEffect(() => { - if ((rfqType === RfqType.PURCHASE || rfqType === RfqType.PURCHASE_BUDGETARY) && open) { - const loadParentRfqs = async () => { - setIsLoadingParents(true); - try { - // 현재 RFQ 타입에 따라 선택 가능한, 부모가 될 수 있는 RFQ 타입들 가져오기 - const parentTypes = getParentRfqTypes(); - - // 부모 RFQ 타입이 있을 때만 API 호출 - if (parentTypes.length > 0) { - const result = await getBudgetaryRfqs({ - rfqTypes: parentTypes // 서비스에 rfqTypes 파라미터 추가 필요 - }); - - if ('rfqs' in result) { - setParentRfqs(result.rfqs as unknown as ParentRfq[]); - } else if ('error' in result) { - console.error("부모 RFQ 로드 오류:", result.error); - } - } - } catch (error) { - console.error("부모 RFQ 로드 오류:", error); - } finally { - setIsLoadingParents(false); - } - }; - - loadParentRfqs(); - } - }, [rfqType, open]); - - // 프로젝트 선택 처리 - const handleProjectSelect = (project: Project | null) => { - if (project === null) { - return; - } - - form.setValue("projectId", project.id); - }; - - const handleBidProjectSelect = (project: Project | null) => { - if (project === null) { - return; - } - - form.setValue("bidProjectId", project.id); - }; - - // 부모 RFQ 선택 처리 - const handleParentRfqSelect = (rfq: ParentRfq | null) => { - setSelectedParentRfq(rfq); - form.setValue("parentRfqId", rfq?.id); - }; - - 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(); - setSelectedParentRfq(null); - setOpen(false); - } - - function handleDialogOpenChange(nextOpen: boolean) { - if (!nextOpen) { - form.reset(); - setSelectedParentRfq(null); - } - setOpen(nextOpen); - } - - // Return a message or disabled state if user is not authenticated - if (status === "loading") { - return <Button variant="outline" size="sm" disabled>Loading...</Button>; - } - - // 타입에 따라 부모 RFQ 선택 필드를 보여줄지 결정 - const shouldShowParentRfqSelector = rfqType === RfqType.PURCHASE || rfqType === RfqType.PURCHASE_BUDGETARY; - const shouldShowEstimateSelector = rfqType === RfqType.BUDGETARY; - - // 부모 RFQ 선택기 레이블 및 설명 가져오기 - const getParentRfqSelectorLabel = () => { - if (rfqType === RfqType.PURCHASE) { - return "부모 RFQ (BUDGETARY/PURCHASE_BUDGETARY)"; - } else if (rfqType === RfqType.PURCHASE_BUDGETARY) { - return "부모 RFQ (BUDGETARY)"; - } - return "부모 RFQ"; - }; - - const getParentRfqDescription = () => { - if (rfqType === RfqType.PURCHASE) { - return "BUDGETARY 또는 PURCHASE_BUDGETARY 타입의 RFQ를 부모로 선택할 수 있습니다."; - } else if (rfqType === RfqType.PURCHASE_BUDGETARY) { - return "BUDGETARY 타입의 RFQ만 부모로 선택할 수 있습니다."; - } - return ""; - }; - - return ( - <Dialog open={open} onOpenChange={handleDialogOpenChange}> - {/* 모달을 열기 위한 버튼 */} - <DialogTrigger asChild> - <Button variant="default" size="sm"> - Add {getTitle()} - </Button> - </DialogTrigger> - - <DialogContent> - <DialogHeader> - <DialogTitle>Create New {getTitle()}</DialogTitle> - <DialogDescription> - 새 {getTitle()} 정보를 입력하고 <b>Create</b> 버튼을 누르세요. - <div className="mt-1 text-xs text-muted-foreground"> - {getTypeDescription()} - </div> - </DialogDescription> - </DialogHeader> - - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)}> - <div className="space-y-4 py-4"> - {/* rfqType - hidden field */} - <FormField - control={form.control} - name="rfqType" - render={({ field }) => ( - <input type="hidden" {...field} /> - )} - /> - - {/* Project Selector */} - <FormField - control={form.control} - name="projectId" - render={({ field }) => ( - <FormItem> - <FormLabel>Project</FormLabel> - <FormControl> - - {shouldShowEstimateSelector ? - <EstimateProjectSelector - selectedProjectId={field.value} - onProjectSelect={handleBidProjectSelect} - placeholder="견적 프로젝트 선택..." - /> : - <ProjectSelector - selectedProjectId={field.value} - onProjectSelect={handleProjectSelect} - placeholder="프로젝트 선택..." - />} - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* Parent RFQ Selector - PURCHASE 또는 PURCHASE_BUDGETARY 타입일 때만 표시 */} - {shouldShowParentRfqSelector && ( - <FormField - control={form.control} - name="parentRfqId" - render={({ field }) => ( - <FormItem> - <FormLabel>{getParentRfqSelectorLabel()}</FormLabel> - <FormControl> - <ParentRfqSelector - selectedRfqId={field.value as number | undefined} - onRfqSelect={handleParentRfqSelect} - rfqType={rfqType} - parentRfqTypes={getParentRfqTypes()} - placeholder={ - rfqType === RfqType.PURCHASE - ? "BUDGETARY 또는 PURCHASE_BUDGETARY RFQ 선택..." - : "BUDGETARY RFQ 선택..." - } - /> - </FormControl> - <div className="text-xs text-muted-foreground mt-1"> - {getParentRfqDescription()} - </div> - <FormMessage /> - </FormItem> - )} - /> - )} - - {/* rfqCode - 자동 생성되고 읽기 전용 */} - <FormField - control={form.control} - name="rfqCode" - render={({ field }) => ( - <FormItem> - <FormLabel>RFQ Code</FormLabel> - <FormControl> - <div className="flex"> - <Input - placeholder="자동으로 생성 중..." - {...field} - disabled={true} - className="bg-muted" - /> - {isLoadingRfqCode && ( - <div className="ml-2 flex items-center"> - <div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent"></div> - </div> - )} - </div> - </FormControl> - <div className="text-xs text-muted-foreground mt-1"> - RFQ 타입과 현재 날짜를 기준으로 자동 생성됩니다 - </div> - <FormMessage /> - </FormItem> - )} - /> - - {/* description */} - <FormField - control={form.control} - name="description" - render={({ field }) => ( - <FormItem> - <FormLabel>RFQ Description</FormLabel> - <FormControl> - <Input placeholder="e.g. 설명을 입력하세요" {...field} /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* dueDate */} - <FormField - control={form.control} - name="dueDate" - render={({ field }) => ( - <FormItem> - <FormLabel>Due Date</FormLabel> - <FormControl> - <Input - type="date" - value={field.value ? field.value.toISOString().slice(0, 10) : ""} - onChange={(e) => { - const val = e.target.value - if (val) { - const date = new Date(val); - // 날짜 1일씩 밀리는 문제로 우선 KTC로 입력 - // 추후 아래와 같이 수정 - // 1. 해당 유저 타임존 값으로 입력 - // 2. DB에는 UTC 타임존 값으로 저장 - // 3. 출력시 유저별 타임존 값으로 변환해 출력 - // 4. 어떤 타임존으로 나오는지도 함께 렌더링 - // field.onChange(new Date(val + "T00:00:00")) - field.onChange(date); - } - }} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* status (Read-only) */} - <FormField - control={form.control} - name="status" - render={({ field }) => ( - <FormItem> - <FormLabel>Status</FormLabel> - <FormControl> - <Input - disabled - className="capitalize" - {...field} - onChange={() => { }} // Prevent changes - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - - <DialogFooter> - <Button - type="button" - variant="outline" - onClick={() => setOpen(false)} - > - Cancel - </Button> - <Button - type="submit" - disabled={form.formState.isSubmitting || status !== "authenticated"} - > - Create - </Button> - </DialogFooter> - </form> - </Form> - </DialogContent> - </Dialog> - ) -}
\ No newline at end of file |
