"use client" import * as React from "react" import { useForm } from "react-hook-form" import { zodResolver } from "@hookform/resolvers/zod" import { z } from "zod" import { Plus, X, ChevronUp, ChevronDown } from "lucide-react" import { useRouter } from "next/navigation" 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 { useToast } from "@/hooks/use-toast" import { createDocument, CreateDocumentInputType, invalidateDocumentCache } from "../service" // 스테이지 객체로 변경 type StageItem = { name: string; order: number; } // Zod 스키마 정의 - 스테이지 구조 변경 const createDocumentSchema = z.object({ docNumber: z.string().min(1, "Document number is required"), title: z.string().min(1, "Title is required"), stages: z.array(z.object({ name: z.string().min(1, "Stage name cannot be empty"), order: z.number().int().positive("Order must be a positive integer") })) .min(1, "At least one stage is required") .refine(stages => !stages.some(stage => stage.name.trim() === ""), { message: "Stage names cannot be empty" }) .refine(stages => { // 중복된 order 값이 없는지 확인 const orders = stages.map(s => s.order); return orders.length === new Set(orders).size; }, { message: "Stage orders must be unique" }) }); type CreateDocumentSchema = z.infer; interface AddDocumentListDialogProps { projectType: "ship" | "plant"; contractId: number; onSuccess?: () => void; } export function AddDocumentListDialog({ projectType, contractId, onSuccess }: AddDocumentListDialogProps) { const [open, setOpen] = React.useState(false); const [isSubmitting, setIsSubmitting] = React.useState(false); const router = useRouter(); const { toast } = useToast() // 기본 스테이지 설정 - 객체 배열로 변경 const defaultStages: StageItem[] = projectType === "ship" ? [ { name: "For Approval", order: 1 }, { name: "For Working", order: 2 } ] : [{ name: "", order: 1 }]; // react-hook-form 설정 const form = useForm({ resolver: zodResolver(createDocumentSchema), defaultValues: { docNumber: "", title: "", stages: defaultStages }, }); // 스테이지 추가 기능 const addStage = () => { const currentStages = form.getValues().stages; const nextOrder = Math.max(...currentStages.map(s => s.order), 0) + 1; form.setValue('stages', [...currentStages, { name: "", order: nextOrder }], { shouldValidate: true }); }; // 스테이지 제거 기능 const removeStage = (index: number) => { const currentStages = form.getValues().stages; const newStages = currentStages.filter((_, i) => i !== index); // 순서 재정렬 const reorderedStages = newStages.map((stage, i) => ({ ...stage, order: i + 1 })); form.setValue('stages', reorderedStages, { shouldValidate: true }); }; // 스테이지 순서 이동 기능 const moveStage = (index: number, direction: 'up' | 'down') => { const currentStages = [...form.getValues().stages]; const targetIndex = direction === 'up' ? index - 1 : index + 1; if (targetIndex < 0 || targetIndex >= currentStages.length) return; // 스테이지 위치 교환 [currentStages[index], currentStages[targetIndex]] = [currentStages[targetIndex], currentStages[index]]; // order 값 재정렬 const reorderedStages = currentStages.map((stage, i) => ({ ...stage, order: i + 1 })); form.setValue('stages', reorderedStages, { shouldValidate: true }); }; async function onSubmit(data: CreateDocumentSchema) { try { setIsSubmitting(true); // 빈 문자열 필터링 및 순서 정렬 const filteredStages = data.stages .filter(stage => stage.name.trim() !== "") .sort((a, b) => a.order - b.order); if (filteredStages.length === 0) { toast({ title: "Error", description: "At least one valid stage name is required", variant: "destructive", }); return; } // 서버로 전달할 데이터 형태 변환 const result = await createDocument({ docNumber: data.docNumber, title: data.title, stages: filteredStages, // { name, order } 객체 배열로 전달 status: "pending", contractId, } as CreateDocumentInputType); if (result.success) { // 캐시 무효화 시도 try { await invalidateDocumentCache(contractId); } catch (cacheError) { console.warn('Cache invalidation failed:', cacheError); } toast({ title: "Success", description: "Document created successfully", variant: "default", }); // 모달 닫기 및 폼 리셋 form.reset({ docNumber: "", title: "", stages: defaultStages }); setOpen(false); if (onSuccess) { onSuccess(); } setTimeout(() => { router.refresh(); }, 100); } else { toast({ title: "Error", description: result.message || "Failed to create document", variant: "destructive", }); } } catch (error) { console.error('Error creating document:', error); toast({ title: "Error", description: "An unexpected error occurred", variant: "destructive", }); } finally { setIsSubmitting(false); } } function handleDialogOpenChange(nextOpen: boolean) { if (!nextOpen) { form.reset({ docNumber: "", title: "", stages: defaultStages }); } setOpen(nextOpen); } return ( Create New Document 새 문서 정보를 입력하고 Create 버튼을 누르세요.
{/* 문서 번호 필드 */} ( Document Number )} /> {/* 문서 제목 필드 */} ( Title )} /> {/* 스테이지 섹션 */}
Stages {projectType === "plant" && ( )}
{form.watch("stages").map((stage, index) => (
{/* 순서 표시 */}
{stage.order} {projectType === "plant" && (
)}
{/* 스테이지 이름 입력 */} ( )} /> {/* 제거 버튼 */} {projectType === "plant" && index > 0 && ( )}
))} {form.formState.errors.stages?.message}
); }