diff options
Diffstat (limited to 'lib/b-rfq/summary-table/add-new-rfq-dialog.tsx')
| -rw-r--r-- | lib/b-rfq/summary-table/add-new-rfq-dialog.tsx | 523 |
1 files changed, 0 insertions, 523 deletions
diff --git a/lib/b-rfq/summary-table/add-new-rfq-dialog.tsx b/lib/b-rfq/summary-table/add-new-rfq-dialog.tsx deleted file mode 100644 index 2333d9cf..00000000 --- a/lib/b-rfq/summary-table/add-new-rfq-dialog.tsx +++ /dev/null @@ -1,523 +0,0 @@ -"use client" - -import * as React from "react" -import { useForm } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import { z } from "zod" -import { format } from "date-fns" -import { CalendarIcon, Plus, Loader2, Eye } from "lucide-react" -import { useRouter } from "next/navigation" -import { useSession } from "next-auth/react" - -import { Button } from "@/components/ui/button" -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, - FormDescription, -} from "@/components/ui/form" -import { Input } from "@/components/ui/input" -import { Textarea } from "@/components/ui/textarea" -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover" -import { Calendar } from "@/components/ui/calendar" -import { Badge } from "@/components/ui/badge" -import { cn } from "@/lib/utils" -import { toast } from "sonner" -import { ProjectSelector } from "@/components/ProjectSelector" -import { createRfqAction, previewNextRfqCode } from "../service" - -export type Project = { - id: number; - projectCode: string; - projectName: string; -} - -// 클라이언트 폼 스키마 (projectId 필수로 변경) -const createRfqSchema = z.object({ - projectId: z.number().min(1, "프로젝트를 선택해주세요"), // 필수로 변경 - dueDate: z.date({ - required_error: "마감일을 선택해주세요", - }), - picCode: z.string().min(1, "구매 담당자 코드를 입력해주세요"), - picName: z.string().optional(), - engPicName: z.string().optional(), - packageNo: z.string().min(1, "패키지 번호를 입력해주세요"), - packageName: z.string().min(1, "패키지명을 입력해주세요"), - remark: z.string().optional(), - projectCompany: z.string().optional(), - projectFlag: z.string().optional(), - projectSite: z.string().optional(), -}) - -type CreateRfqFormValues = z.infer<typeof createRfqSchema> - -interface CreateRfqDialogProps { - onSuccess?: () => void; -} - -export function CreateRfqDialog({ onSuccess }: CreateRfqDialogProps) { - const [open, setOpen] = React.useState(false) - const [isLoading, setIsLoading] = React.useState(false) - const [previewCode, setPreviewCode] = React.useState<string>("") - const [isLoadingPreview, setIsLoadingPreview] = React.useState(false) - const router = useRouter() - const { data: session } = useSession() - - const userId = React.useMemo(() => { - return session?.user?.id ? Number(session.user.id) : null; - }, [session]); - - const form = useForm<CreateRfqFormValues>({ - resolver: zodResolver(createRfqSchema), - defaultValues: { - projectId: undefined, - dueDate: undefined, - picCode: "", - picName: "", - engPicName: "", - packageNo: "", - packageName: "", - remark: "", - projectCompany: "", - projectFlag: "", - projectSite: "", - }, - }) - - // picCode 변경 시 미리보기 업데이트 - const watchedPicCode = form.watch("picCode") - - React.useEffect(() => { - if (watchedPicCode && watchedPicCode.length > 0) { - setIsLoadingPreview(true) - const timer = setTimeout(async () => { - try { - const preview = await previewNextRfqCode(watchedPicCode) - setPreviewCode(preview) - } catch (error) { - console.error("미리보기 오류:", error) - setPreviewCode("") - } finally { - setIsLoadingPreview(false) - } - }, 500) // 500ms 디바운스 - - return () => clearTimeout(timer) - } else { - setPreviewCode("") - } - }, [watchedPicCode]) - - // 다이얼로그 열림/닫힘 처리 및 폼 리셋 - const handleOpenChange = (newOpen: boolean) => { - setOpen(newOpen) - - // 다이얼로그가 닫힐 때 폼과 상태 초기화 - if (!newOpen) { - form.reset() - setPreviewCode("") - setIsLoadingPreview(false) - } - } - - const handleCancel = () => { - form.reset() - setOpen(false) - } - - - const onSubmit = async (data: CreateRfqFormValues) => { - if (!userId) { - toast.error("로그인이 필요합니다") - return - } - - setIsLoading(true) - - try { - // 서버 액션 호출 - Date 객체를 직접 전달 - const result = await createRfqAction({ - projectId: data.projectId, // 이제 항상 값이 있음 - dueDate: data.dueDate, // Date 객체 직접 전달 - picCode: data.picCode, - picName: data.picName || "", - engPicName: data.engPicName || "", - packageNo: data.packageNo, - packageName: data.packageName, - remark: data.remark || "", - projectCompany: data.projectCompany || "", - projectFlag: data.projectFlag || "", - projectSite: data.projectSite || "", - createdBy: userId, - updatedBy: userId, - }) - - if (result.success) { - toast.success(result.message, { - description: `RFQ 코드: ${result.data?.rfqCode}`, - }) - - // 다이얼로그 닫기 (handleOpenChange에서 리셋 처리됨) - setOpen(false) - - // 성공 콜백 실행 - if (onSuccess) { - onSuccess() - } - - } else { - toast.error(result.error || "RFQ 생성에 실패했습니다") - } - - } catch (error) { - console.error('RFQ 생성 오류:', error) - toast.error("RFQ 생성에 실패했습니다", { - description: "알 수 없는 오류가 발생했습니다", - }) - } finally { - setIsLoading(false) - } - } - - const handleProjectSelect = (project: Project | null) => { - if (project === null) { - form.setValue("projectId", undefined as any); // 타입 에러 방지 - return; - } - form.setValue("projectId", project.id); - }; - - return ( - <Dialog open={open} onOpenChange={handleOpenChange}> - <DialogTrigger asChild> - <Button size="sm" variant="outline"> - <Plus className="mr-2 h-4 w-4" /> - 새 RFQ - </Button> - </DialogTrigger> - <DialogContent className="max-w-3xl h-[90vh] flex flex-col"> - {/* 고정된 헤더 */} - <DialogHeader className="flex-shrink-0"> - <DialogTitle>새 RFQ 생성</DialogTitle> - <DialogDescription> - 새로운 RFQ를 생성합니다. 필수 정보를 입력해주세요. - </DialogDescription> - </DialogHeader> - - {/* 스크롤 가능한 컨텐츠 영역 */} - <div className="flex-1 overflow-y-auto px-1"> - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4 py-2"> - - {/* 프로젝트 선택 (필수) */} - <FormField - control={form.control} - name="projectId" - render={({ field }) => ( - <FormItem> - <FormLabel> - 프로젝트 <span className="text-red-500">*</span> - </FormLabel> - <FormControl> - <ProjectSelector - selectedProjectId={field.value} - onProjectSelect={handleProjectSelect} - placeholder="프로젝트 선택..." - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* 마감일 (필수) */} - <FormField - control={form.control} - name="dueDate" - render={({ field }) => ( - <FormItem className="flex flex-col"> - <FormLabel> - 마감일 <span className="text-red-500">*</span> - </FormLabel> - <Popover> - <PopoverTrigger asChild> - <FormControl> - <Button - variant="outline" - className={cn( - "w-full pl-3 text-left font-normal", - !field.value && "text-muted-foreground" - )} - > - {field.value ? ( - format(field.value, "yyyy-MM-dd") - ) : ( - <span>마감일을 선택하세요</span> - )} - <CalendarIcon className="ml-auto h-4 w-4 opacity-50" /> - </Button> - </FormControl> - </PopoverTrigger> - <PopoverContent className="w-auto p-0" align="start"> - <Calendar - mode="single" - selected={field.value} - onSelect={field.onChange} - disabled={(date) => - date < new Date() || date < new Date("1900-01-01") - } - initialFocus - /> - </PopoverContent> - </Popover> - <FormMessage /> - </FormItem> - )} - /> - - {/* 구매 담당자 코드 (필수) + 미리보기 */} - <FormField - control={form.control} - name="picCode" - render={({ field }) => ( - <FormItem> - <FormLabel> - 구매 담당자 코드 <span className="text-red-500">*</span> - </FormLabel> - <FormControl> - <div className="space-y-2"> - <Input - placeholder="예: P001, P002, MGR01 등" - {...field} - /> - {/* RFQ 코드 미리보기 */} - {previewCode && ( - <div className="flex items-center gap-2 p-2 bg-muted rounded-md"> - <Eye className="h-4 w-4 text-muted-foreground" /> - <span className="text-sm text-muted-foreground"> - 생성될 RFQ 코드: - </span> - <Badge variant="outline" className="font-mono"> - {isLoadingPreview ? "생성 중..." : previewCode} - </Badge> - </div> - )} - </div> - </FormControl> - <FormDescription> - RFQ 코드는 N + 담당자코드 + 시리얼번호(5자리) 형식으로 자동 생성됩니다 - </FormDescription> - <FormMessage /> - </FormItem> - )} - /> - - {/* 담당자 정보 (두 개 나란히) */} - <div className="space-y-3"> - <h4 className="text-sm font-medium">담당자 정보</h4> - <div className="grid grid-cols-2 gap-4"> - {/* 구매 담당자 */} - <FormField - control={form.control} - name="picName" - render={({ field }) => ( - <FormItem> - <FormLabel>구매 담당자명</FormLabel> - <FormControl> - <Input - placeholder="구매 담당자명" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* 설계 담당자 */} - <FormField - control={form.control} - name="engPicName" - render={({ field }) => ( - <FormItem> - <FormLabel>설계 담당자명</FormLabel> - <FormControl> - <Input - placeholder="설계 담당자명" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - </div> - - {/* 패키지 정보 (두 개 나란히) - 필수 */} - <div className="space-y-3"> - <h4 className="text-sm font-medium">패키지 정보</h4> - <div className="grid grid-cols-2 gap-4"> - {/* 패키지 번호 (필수) */} - <FormField - control={form.control} - name="packageNo" - render={({ field }) => ( - <FormItem> - <FormLabel> - 패키지 번호 <span className="text-red-500">*</span> - </FormLabel> - <FormControl> - <Input - placeholder="패키지 번호" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* 패키지명 (필수) */} - <FormField - control={form.control} - name="packageName" - render={({ field }) => ( - <FormItem> - <FormLabel> - 패키지명 <span className="text-red-500">*</span> - </FormLabel> - <FormControl> - <Input - placeholder="패키지명" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - </div> - - {/* 프로젝트 상세 정보 */} - <div className="space-y-3"> - <h4 className="text-sm font-medium">프로젝트 상세 정보</h4> - <div className="grid grid-cols-1 gap-3"> - <FormField - control={form.control} - name="projectCompany" - render={({ field }) => ( - <FormItem> - <FormLabel>프로젝트 회사</FormLabel> - <FormControl> - <Input - placeholder="프로젝트 회사명" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <div className="grid grid-cols-2 gap-3"> - <FormField - control={form.control} - name="projectFlag" - render={({ field }) => ( - <FormItem> - <FormLabel>프로젝트 플래그</FormLabel> - <FormControl> - <Input - placeholder="프로젝트 플래그" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <FormField - control={form.control} - name="projectSite" - render={({ field }) => ( - <FormItem> - <FormLabel>프로젝트 사이트</FormLabel> - <FormControl> - <Input - placeholder="프로젝트 사이트" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </div> - </div> - </div> - - {/* 비고 */} - <FormField - control={form.control} - name="remark" - render={({ field }) => ( - <FormItem> - <FormLabel>비고</FormLabel> - <FormControl> - <Textarea - placeholder="추가 비고사항을 입력하세요" - className="resize-none" - rows={3} - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - </form> - </Form> - </div> - - {/* 고정된 푸터 */} - <DialogFooter className="flex-shrink-0"> - <Button - type="button" - variant="outline" - onClick={handleCancel} - disabled={isLoading} - > - 취소 - </Button> - <Button - type="submit" - onClick={form.handleSubmit(onSubmit)} - disabled={isLoading} - > - {isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} - {isLoading ? "생성 중..." : "RFQ 생성"} - </Button> - </DialogFooter> - </DialogContent> - </Dialog> - ) -}
\ No newline at end of file |
