diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-06 04:23:40 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-08-06 04:23:40 +0000 |
| commit | de2ac5a2860bc25180971e7a11f852d9d44675b7 (patch) | |
| tree | b931c363f2cb19e177a0a7b17190d5de2a82d709 /lib/legal-review/status/update-legal-work-dialog.tsx | |
| parent | 6c549b0f264e9be4d60af38f9efc05b189d6849f (diff) | |
(대표님) 정기평가, 법적검토, 정책, 가입관련 처리 및 관련 컴포넌트 추가, 메뉴 변경
Diffstat (limited to 'lib/legal-review/status/update-legal-work-dialog.tsx')
| -rw-r--r-- | lib/legal-review/status/update-legal-work-dialog.tsx | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/lib/legal-review/status/update-legal-work-dialog.tsx b/lib/legal-review/status/update-legal-work-dialog.tsx new file mode 100644 index 00000000..d9157d3c --- /dev/null +++ b/lib/legal-review/status/update-legal-work-dialog.tsx @@ -0,0 +1,385 @@ +"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, Edit } from "lucide-react" +import { toast } from "sonner" + +import { Button } from "@/components/ui/button" +import { + Sheet, + SheetClose, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, +} from "@/components/ui/sheet" +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 { ScrollArea } from "@/components/ui/scroll-area" +import { cn } from "@/lib/utils" +import { getVendorsForSelection } from "@/lib/b-rfq/service" +import { LegalWorksDetailView } from "@/db/schema" +// import { updateLegalWork } from "../service" + +type LegalWorkData = LegalWorksDetailView + +interface EditLegalWorkSheetProps { + open: boolean + onOpenChange: (open: boolean) => void + work: LegalWorkData | null + onSuccess?: () => void + onDataChange?: () => void +} + +// 편집용 폼 스키마 (신규등록 상태에서만 기본 정보만 편집) +const editLegalWorkSchema = z.object({ + category: z.enum(["CP", "GTC", "기타"]), + vendorId: z.number().min(1, "벤더를 선택해주세요"), + isUrgent: z.boolean().default(false), + requestDate: z.string().min(1, "답변요청일을 선택해주세요"), +}) + +type EditLegalWorkFormValues = z.infer<typeof editLegalWorkSchema> + +interface Vendor { + id: number + vendorName: string + vendorCode: string + country: string + taxId: string + status: string +} + +export function EditLegalWorkSheet({ + open, + onOpenChange, + work, + onSuccess, + onDataChange +}: EditLegalWorkSheetProps) { + 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 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) + } + }, []) + + const form = useForm<EditLegalWorkFormValues>({ + resolver: zodResolver(editLegalWorkSchema), + defaultValues: { + category: "CP", + vendorId: 0, + isUrgent: false, + requestDate: "", + }, + }) + + // work 데이터가 변경될 때 폼 값 업데이트 + React.useEffect(() => { + if (work && open) { + form.reset({ + category: work.category as "CP" | "GTC" | "기타", + vendorId: work.vendorId || 0, + isUrgent: work.isUrgent || false, + requestDate: work.requestDate ? new Date(work.requestDate).toISOString().split('T')[0] : "", + }) + } + }, [work, open, form]) + + React.useEffect(() => { + if (open) { + loadVendors() + } + }, [open, loadVendors]) + + // 폼 제출 + async function onSubmit(data: EditLegalWorkFormValues) { + if (!work) return + + console.log("Updating legal work with data:", data) + setIsSubmitting(true) + + try { + const result = await updateLegalWork(work.id, data) + + if (result.success) { + toast.success(result.data?.message || "법무업무가 성공적으로 수정되었습니다.") + onOpenChange(false) + onSuccess?.() + onDataChange?.() + router.refresh() + } else { + toast.error(result.error || "수정 중 오류가 발생했습니다.") + } + } catch (error) { + console.error("Error updating legal work:", error) + toast.error("수정 중 오류가 발생했습니다.") + } finally { + setIsSubmitting(false) + } + } + + // 시트 닫기 핸들러 + const handleOpenChange = (openState: boolean) => { + onOpenChange(openState) + if (!openState) { + form.reset() + } + } + + // 선택된 벤더 정보 + const selectedVendor = vendors.find(v => v.id === form.watch("vendorId")) + + if (!work) { + return null + } + + return ( + <Sheet open={open} onOpenChange={handleOpenChange}> + <SheetContent className="w-[600px] sm:w-[800px] p-0 flex flex-col" style={{maxWidth:900}}> + {/* 고정 헤더 */} + <SheetHeader className="flex-shrink-0 p-6 border-b"> + <SheetTitle className="flex items-center gap-2"> + <Edit className="h-5 w-5" /> + 법무업무 편집 + </SheetTitle> + <SheetDescription> + 법무업무 #{work.id}의 기본 정보를 수정합니다. (신규등록 상태에서만 편집 가능) + </SheetDescription> + </SheetHeader> + + <Form {...form}> + <form + onSubmit={form.handleSubmit(onSubmit)} + className="flex flex-col flex-1 min-h-0" + > + {/* 스크롤 가능한 콘텐츠 영역 */} + <ScrollArea className="flex-1 p-6"> + <div className="space-y-6"> + {/* 기본 정보 */} + <Card> + <CardHeader> + <CardTitle className="text-lg">기본 정보</CardTitle> + </CardHeader> + <CardContent className="space-y-4"> + {/* 구분 */} + <FormField + control={form.control} + name="category" + render={({ field }) => ( + <FormItem> + <FormLabel>구분</FormLabel> + <Select onValueChange={field.onChange} value={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> + )} + /> + + {/* 벤더 선택 */} + <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> + <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> + )} + /> + + {/* 답변요청일 */} + <FormField + control={form.control} + name="requestDate" + render={({ field }) => ( + <FormItem> + <FormLabel>답변요청일</FormLabel> + <FormControl> + <Input type="date" {...field} /> + </FormControl> + <FormMessage /> + </FormItem> + )} + /> + </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> + </div> + </div> + </CardContent> + </Card> + </div> + </ScrollArea> + + {/* 고정 버튼 영역 */} + <SheetFooter className="flex-shrink-0 border-t bg-background p-6"> + <div className="flex justify-end gap-3 w-full"> + <SheetClose asChild> + <Button + type="button" + variant="outline" + disabled={isSubmitting} + > + 취소 + </Button> + </SheetClose> + <Button + type="submit" + disabled={isSubmitting} + > + {isSubmitting && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} + 저장 + </Button> + </div> + </SheetFooter> + </form> + </Form> + </SheetContent> + </Sheet> + ) +}
\ No newline at end of file |
