diff options
Diffstat (limited to 'lib/legal-review/status/create-legal-work-dialog.tsx')
| -rw-r--r-- | lib/legal-review/status/create-legal-work-dialog.tsx | 506 |
1 files changed, 0 insertions, 506 deletions
diff --git a/lib/legal-review/status/create-legal-work-dialog.tsx b/lib/legal-review/status/create-legal-work-dialog.tsx deleted file mode 100644 index 0ee1c430..00000000 --- a/lib/legal-review/status/create-legal-work-dialog.tsx +++ /dev/null @@ -1,506 +0,0 @@ -"use client" - -import * as React from "react" -import { useRouter } from "next/navigation" -import { useForm } from "react-hook-form" -import { zodResolver } from "@hookform/resolvers/zod" -import * as z from "zod" -import { Loader2, Check, ChevronsUpDown, Calendar, User } from "lucide-react" -import { toast } from "sonner" - -import { Button } from "@/components/ui/button" -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog" -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command" -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover" -import { Input } from "@/components/ui/input" -import { Badge } from "@/components/ui/badge" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Switch } from "@/components/ui/switch" -import { cn } from "@/lib/utils" -import { getVendorsForSelection } from "@/lib/b-rfq/service" -import { createLegalWork } from "../service" -import { useSession } from "next-auth/react" - -interface CreateLegalWorkDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - onSuccess?: () => void - onDataChange?: () => void -} - -// legalWorks 테이블에 맞춘 단순화된 폼 스키마 -const createLegalWorkSchema = z.object({ - category: z.enum(["CP", "GTC", "기타"]), - vendorId: z.number().min(1, "벤더를 선택해주세요"), - isUrgent: z.boolean().default(false), - requestDate: z.string().min(1, "답변요청일을 선택해주세요"), - expectedAnswerDate: z.string().optional(), - reviewer: z.string().min(1, "검토요청자를 입력해주세요"), -}) - -type CreateLegalWorkFormValues = z.infer<typeof createLegalWorkSchema> - -interface Vendor { - id: number - vendorName: string - vendorCode: string - country: string - taxId: string - status: string -} - -export function CreateLegalWorkDialog({ - open, - onOpenChange, - onSuccess, - onDataChange -}: CreateLegalWorkDialogProps) { - const router = useRouter() - const [isSubmitting, setIsSubmitting] = React.useState(false) - const [vendors, setVendors] = React.useState<Vendor[]>([]) - const [vendorsLoading, setVendorsLoading] = React.useState(false) - const [vendorOpen, setVendorOpen] = React.useState(false) - const { data: session } = useSession() - - const userName = React.useMemo(() => { - return session?.user?.name || ""; - }, [session]); - - const userEmail = React.useMemo(() => { - return session?.user?.email || ""; - }, [session]); - - const defaultReviewer = React.useMemo(() => { - if (userName && userEmail) { - return `${userName} (${userEmail})`; - } else if (userName) { - return userName; - } else if (userEmail) { - return userEmail; - } - return ""; - }, [userName, userEmail]); - - 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) - } - }, []) - - // 오늘 날짜 + 7일 후를 기본 답변요청일로 설정 - const getDefaultRequestDate = () => { - const date = new Date() - date.setDate(date.getDate() + 7) - return date.toISOString().split('T')[0] - } - - // 답변요청일 + 3일 후를 기본 답변예정일로 설정 - const getDefaultExpectedDate = (requestDate: string) => { - if (!requestDate) return "" - const date = new Date(requestDate) - date.setDate(date.getDate() + 3) - return date.toISOString().split('T')[0] - } - - const form = useForm<CreateLegalWorkFormValues>({ - resolver: zodResolver(createLegalWorkSchema), - defaultValues: { - category: "CP", - vendorId: 0, - isUrgent: false, - requestDate: getDefaultRequestDate(), - expectedAnswerDate: "", - reviewer: defaultReviewer, - }, - }) - - React.useEffect(() => { - if (open) { - loadVendors() - } - }, [open, loadVendors]) - - // 세션 정보가 로드되면 검토요청자 필드 업데이트 - React.useEffect(() => { - if (defaultReviewer) { - form.setValue("reviewer", defaultReviewer) - } - }, [defaultReviewer, form]) - - // 답변요청일 변경시 답변예정일 자동 설정 - const requestDate = form.watch("requestDate") - React.useEffect(() => { - if (requestDate) { - const expectedDate = getDefaultExpectedDate(requestDate) - form.setValue("expectedAnswerDate", expectedDate) - } - }, [requestDate, form]) - - // 폼 제출 - 서버 액션 적용 - async function onSubmit(data: CreateLegalWorkFormValues) { - console.log("Form submitted with data:", data) - setIsSubmitting(true) - - try { - // legalWorks 테이블에 맞춘 데이터 구조 - const legalWorkData = { - ...data, - // status는 서버에서 "검토요청"으로 설정 - // consultationDate는 서버에서 오늘 날짜로 설정 - // hasAttachment는 서버에서 false로 설정 - } - - const result = await createLegalWork(legalWorkData) - - if (result.success) { - toast.success(result.data?.message || "법무업무가 성공적으로 등록되었습니다.") - onOpenChange(false) - form.reset({ - category: "CP", - vendorId: 0, - isUrgent: false, - requestDate: getDefaultRequestDate(), - expectedAnswerDate: "", - reviewer: defaultReviewer, - }) - onSuccess?.() - onDataChange?.() - router.refresh() - } else { - toast.error(result.error || "등록 중 오류가 발생했습니다.") - } - } catch (error) { - console.error("Error creating legal work:", error) - toast.error("등록 중 오류가 발생했습니다.") - } finally { - setIsSubmitting(false) - } - } - - // 다이얼로그 닫기 핸들러 - const handleOpenChange = (open: boolean) => { - onOpenChange(open) - if (!open) { - form.reset({ - category: "CP", - vendorId: 0, - isUrgent: false, - requestDate: getDefaultRequestDate(), - expectedAnswerDate: "", - reviewer: defaultReviewer, - }) - } - } - - // 선택된 벤더 정보 - const selectedVendor = vendors.find(v => v.id === form.watch("vendorId")) - - return ( - <Dialog open={open} onOpenChange={handleOpenChange}> - <DialogContent className="max-w-2xl h-[80vh] p-0 flex flex-col"> - {/* 고정 헤더 */} - <div className="flex-shrink-0 p-6 border-b"> - <DialogHeader> - <DialogTitle>법무업무 신규 등록</DialogTitle> - <DialogDescription> - 새로운 법무업무를 등록합니다. 상세한 검토 요청은 등록 후 별도로 진행할 수 있습니다. - </DialogDescription> - </DialogHeader> - </div> - - <Form {...form}> - <form - onSubmit={form.handleSubmit(onSubmit)} - className="flex flex-col flex-1 min-h-0" - > - {/* 스크롤 가능한 콘텐츠 영역 */} - <div className="flex-1 overflow-y-auto p-6"> - <div className="space-y-6"> - {/* 기본 정보 */} - <Card> - <CardHeader> - <CardTitle className="text-lg">기본 정보</CardTitle> - </CardHeader> - <CardContent className="space-y-4"> - <div className="grid grid-cols-2 gap-4"> - {/* 구분 */} - <FormField - control={form.control} - name="category" - render={({ field }) => ( - <FormItem> - <FormLabel>구분</FormLabel> - <Select onValueChange={field.onChange} defaultValue={field.value}> - <FormControl> - <SelectTrigger> - <SelectValue placeholder="구분 선택" /> - </SelectTrigger> - </FormControl> - <SelectContent> - <SelectItem value="CP">CP</SelectItem> - <SelectItem value="GTC">GTC</SelectItem> - <SelectItem value="기타">기타</SelectItem> - </SelectContent> - </Select> - <FormMessage /> - </FormItem> - )} - /> - - {/* 긴급여부 */} - <FormField - control={form.control} - name="isUrgent" - render={({ field }) => ( - <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4"> - <div className="space-y-0.5"> - <FormLabel className="text-base">긴급 요청</FormLabel> - <div className="text-sm text-muted-foreground"> - 긴급 처리가 필요한 경우 체크 - </div> - </div> - <FormControl> - <Switch - checked={field.value} - onCheckedChange={field.onChange} - /> - </FormControl> - </FormItem> - )} - /> - </div> - - {/* 벤더 선택 */} - <FormField - control={form.control} - name="vendorId" - render={({ field }) => ( - <FormItem> - <FormLabel>벤더</FormLabel> - <Popover open={vendorOpen} onOpenChange={setVendorOpen}> - <PopoverTrigger asChild> - <FormControl> - <Button - variant="outline" - role="combobox" - aria-expanded={vendorOpen} - className="w-full justify-between" - > - {selectedVendor ? ( - <span className="flex items-center gap-2"> - <Badge variant="outline">{selectedVendor.vendorCode}</Badge> - {selectedVendor.vendorName} - </span> - ) : ( - "벤더 선택..." - )} - <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> - </Button> - </FormControl> - </PopoverTrigger> - <PopoverContent className="w-full p-0" align="start"> - <Command> - <CommandInput placeholder="벤더 검색..." /> - <CommandList - onWheel={(e) => { - e.stopPropagation(); // 이벤트 전파 차단 - const target = e.currentTarget; - target.scrollTop += e.deltaY; // 직접 스크롤 처리 - }}> - <CommandEmpty>검색 결과가 없습니다.</CommandEmpty> - <CommandGroup> - {vendors.map((vendor) => ( - <CommandItem - key={vendor.id} - value={`${vendor.vendorCode} ${vendor.vendorName}`} - onSelect={() => { - field.onChange(vendor.id) - setVendorOpen(false) - }} - > - <Check - className={cn( - "mr-2 h-4 w-4", - vendor.id === field.value ? "opacity-100" : "opacity-0" - )} - /> - <div className="flex items-center gap-2"> - <Badge variant="outline">{vendor.vendorCode}</Badge> - <span>{vendor.vendorName}</span> - </div> - </CommandItem> - ))} - </CommandGroup> - </CommandList> - </Command> - </PopoverContent> - </Popover> - <FormMessage /> - </FormItem> - )} - /> - </CardContent> - </Card> - - {/* 담당자 및 일정 정보 */} - <Card> - <CardHeader> - <CardTitle className="text-lg flex items-center gap-2"> - <Calendar className="h-5 w-5" /> - 담당자 및 일정 - </CardTitle> - </CardHeader> - <CardContent className="space-y-4"> - {/* 검토요청자 */} - <FormField - control={form.control} - name="reviewer" - render={({ field }) => ( - <FormItem> - <FormLabel className="flex items-center gap-2"> - <User className="h-4 w-4" /> - 검토요청자 - </FormLabel> - <FormControl> - <Input - placeholder={defaultReviewer || "검토요청자 이름을 입력하세요"} - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <div className="grid grid-cols-2 gap-4"> - {/* 답변요청일 */} - <FormField - control={form.control} - name="requestDate" - render={({ field }) => ( - <FormItem> - <FormLabel>답변요청일</FormLabel> - <FormControl> - <Input - type="date" - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* 답변예정일 */} - <FormField - control={form.control} - name="expectedAnswerDate" - render={({ field }) => ( - <FormItem> - <FormLabel>답변예정일 (선택사항)</FormLabel> - <FormControl> - <Input - type="date" - {...field} - /> - </FormControl> - <div className="text-xs text-muted-foreground"> - 답변요청일 기준으로 자동 설정됩니다 - </div> - <FormMessage /> - </FormItem> - )} - /> - </div> - </CardContent> - </Card> - - {/* 안내 메시지 */} - <Card className="bg-blue-50 border-blue-200"> - <CardContent className="pt-6"> - <div className="flex items-start gap-3"> - <div className="h-2 w-2 rounded-full bg-blue-500 mt-2"></div> - <div className="space-y-1"> - <p className="text-sm font-medium text-blue-900"> - 법무업무 등록 안내 - </p> - <p className="text-sm text-blue-700"> - 기본 정보 등록 후, 목록에서 해당 업무를 선택하여 상세한 검토 요청을 진행할 수 있습니다. - </p> - <p className="text-xs text-blue-600"> - • 상태: "검토요청"으로 자동 설정<br/> - • 의뢰일: 오늘 날짜로 자동 설정<br/> - • 법무답변자: 나중에 배정 - </p> - </div> - </div> - </CardContent> - </Card> - </div> - </div> - - {/* 고정 버튼 영역 */} - <div className="flex-shrink-0 border-t bg-background p-6"> - <div className="flex justify-end gap-3"> - <Button - type="button" - variant="outline" - onClick={() => handleOpenChange(false)} - disabled={isSubmitting} - > - 취소 - </Button> - <Button - type="submit" - disabled={isSubmitting} - > - {isSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} - 등록 - </Button> - </div> - </div> - </form> - </Form> - </DialogContent> - </Dialog> - ) -}
\ No newline at end of file |
