From b67e36df49f067cbd5ba899f9fbcc755f38d4b4f Mon Sep 17 00:00:00 2001 From: dujinkim Date: Thu, 4 Sep 2025 08:31:31 +0000 Subject: (대표님, 최겸, 임수민) 작업사항 커밋 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/rfq-last/attachment/update-revision-dialog.tsx | 216 +++++++++++++++++++++ 1 file changed, 216 insertions(+) create mode 100644 lib/rfq-last/attachment/update-revision-dialog.tsx (limited to 'lib/rfq-last/attachment/update-revision-dialog.tsx') diff --git a/lib/rfq-last/attachment/update-revision-dialog.tsx b/lib/rfq-last/attachment/update-revision-dialog.tsx new file mode 100644 index 00000000..ce31da64 --- /dev/null +++ b/lib/rfq-last/attachment/update-revision-dialog.tsx @@ -0,0 +1,216 @@ +"use client" + +import * as React from "react" +import { useForm } from "react-hook-form" +import { zodResolver } from "@hookform/resolvers/zod" +import { z } from "zod" +import { Upload } from "lucide-react" +import { toast } from "sonner" +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Button } from "@/components/ui/button" +import { Textarea } from "@/components/ui/textarea" +import { Input } from "@/components/ui/input" +import { Progress } from "@/components/ui/progress" + +const updateRevisionSchema = z.object({ + revisionComment: z.string().min(1, "리비전 설명을 입력해주세요"), + file: z.instanceof(File, { + message: "새 버전 파일을 선택해주세요", + }).refine((file) => file.size <= 100 * 1024 * 1024, { + message: "파일 크기는 100MB를 초과할 수 없습니다.", + }), +}) + +type UpdateRevisionFormData = z.infer + +interface UpdateRevisionDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + attachment: { + id: number; + originalFileName?: string | null; + currentRevision?: string | null; + }; + onSuccess?: () => void; +} + +export function UpdateRevisionDialog({ + open, + onOpenChange, + attachment, + onSuccess, +}: UpdateRevisionDialogProps) { + const [isSubmitting, setIsSubmitting] = React.useState(false) + const [uploadProgress, setUploadProgress] = React.useState(0) + + const form = useForm({ + resolver: zodResolver(updateRevisionSchema), + defaultValues: { + revisionComment: "", + }, + }) + + const onSubmit = async (data: UpdateRevisionFormData) => { + setIsSubmitting(true); + setUploadProgress(0); + + try { + const formData = new FormData(); + formData.append("attachmentId", attachment.id.toString()); + formData.append("revisionComment", data.revisionComment); + formData.append("file", data.file); + + // 진행률 시뮬레이션 + setUploadProgress(30); + + const response = await fetch("/api/rfq-attachments/revision", { + method: "POST", + body: formData, + }); + + setUploadProgress(70); + + if (!response.ok) { + const error = await response.json(); + throw new Error(error.message || "리비전 업데이트 실패"); + } + + const result = await response.json(); + setUploadProgress(100); + + if (result.success) { + toast.success(result.message); + form.reset(); + onOpenChange(false); + onSuccess?.(); + } else { + toast.error(result.message); + } + } catch (error) { + toast.error(error instanceof Error ? error.message : "파일 업로드 중 오류가 발생했습니다"); + } finally { + setIsSubmitting(false); + setUploadProgress(0); + } + } + + return ( + + + + 새 버전 업로드 + + {attachment.originalFileName && ( +
+
현재 파일:
+
+ {attachment.originalFileName} + {attachment.currentRevision && ` (Rev. ${attachment.currentRevision})`} +
+
+ )} +
+
+ +
+ + ( + + 새 버전 파일 + + { + const file = e.target.files?.[0]; + if (file) { + onChange(file); + // 파일 크기 검증 + if (file.size > 100 * 1024 * 1024) { + form.setError("file", { + message: "파일 크기는 100MB를 초과할 수 없습니다." + }); + } + } + }} + disabled={isSubmitting} + {...field} + /> + + {value && ( +

+ 선택된 파일: {value.name} ({(value.size / 1024 / 1024).toFixed(2)}MB) +

+ )} + +
+ )} + /> + + ( + + 리비전 설명 + +