"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} from "lucide-react" import { toast } from "sonner" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } 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 { Dropzone, DropzoneDescription, DropzoneInput, DropzoneTitle, DropzoneUploadIcon, DropzoneZone, } from "@/components/ui/dropzone" import { FileList, FileListAction, FileListDescription, FileListHeader, FileListIcon, FileListInfo, FileListItem, FileListName, FileListSize, } from "@/components/ui/file-list" import { Button } from "@/components/ui/button" import { Textarea } from "@/components/ui/textarea" import { addRfqAttachmentRecord } from "../service" // 첨부파일 추가 폼 스키마 (단일 파일) const addAttachmentSchema = z.object({ attachmentType: z.enum(["구매", "설계"], { required_error: "문서 타입을 선택해주세요.", }), description: z.string().optional(), file: z.instanceof(File, { message: "파일을 선택해주세요.", }), }) type AddAttachmentFormData = z.infer interface AddAttachmentDialogProps { rfqId: number } export function AddAttachmentDialog({ rfqId }: AddAttachmentDialogProps) { const [open, setOpen] = React.useState(false) const [isSubmitting, setIsSubmitting] = React.useState(false) const [uploadProgress, setUploadProgress] = React.useState(0) const form = useForm({ resolver: zodResolver(addAttachmentSchema), defaultValues: { attachmentType: undefined, description: "", file: undefined, }, }) const selectedFile = form.watch("file") // 다이얼로그 닫기 핸들러 const handleOpenChange = (newOpen: boolean) => { if (!newOpen && !isSubmitting) { form.reset() } setOpen(newOpen) } // 파일 선택 처리 const handleFileChange = (files: File[]) => { if (files.length === 0) return const file = files[0] // 첫 번째 파일만 사용 // 파일 크기 검증 const maxFileSize = 10 * 1024 * 1024 // 10MB if (file.size > maxFileSize) { toast.error(`파일이 너무 큽니다. (최대 10MB)`) return } form.setValue("file", file) form.clearErrors("file") } // 파일 제거 const removeFile = () => { form.resetField("file") } // 파일 업로드 API 호출 const uploadFile = async (file: File): Promise<{ fileName: string originalFileName: string filePath: string fileSize: number fileType: string }> => { const formData = new FormData() formData.append("rfqId", rfqId.toString()) formData.append("file", file) const response = await fetch("/api/upload/rfq-attachment", { method: "POST", body: formData, }) if (!response.ok) { const error = await response.json() throw new Error(error.message || "파일 업로드 실패") } return response.json() } // 폼 제출 const onSubmit = async (data: AddAttachmentFormData) => { setIsSubmitting(true) setUploadProgress(0) try { // 1단계: 파일 업로드 setUploadProgress(30) const uploadedFile = await uploadFile(data.file) // 2단계: DB 레코드 생성 (시리얼 번호 자동 생성) setUploadProgress(70) const attachmentRecord = { rfqId, attachmentType: data.attachmentType, description: data.description, fileName: uploadedFile.fileName, originalFileName: uploadedFile.originalFileName, filePath: uploadedFile.filePath, fileSize: uploadedFile.fileSize, fileType: uploadedFile.fileType, } const result = await addRfqAttachmentRecord(attachmentRecord) setUploadProgress(100) if (result.success) { toast.success(result.message) form.reset() handleOpenChange(false) } else { toast.error(result.message) } } catch (error) { console.error("Upload error:", error) toast.error(error instanceof Error ? error.message : "파일 업로드 중 오류가 발생했습니다.") } finally { setIsSubmitting(false) setUploadProgress(0) } } return ( 새 첨부파일 추가 RFQ에 첨부할 문서를 업로드합니다. 시리얼 번호는 자동으로 부여됩니다.
{/* 문서 타입 선택 */} ( 문서 타입 )} /> {/* 설명 */} ( 설명 (선택)