diff options
Diffstat (limited to 'lib/b-rfq/attachment/add-revision-dialog.tsx')
| -rw-r--r-- | lib/b-rfq/attachment/add-revision-dialog.tsx | 336 |
1 files changed, 0 insertions, 336 deletions
diff --git a/lib/b-rfq/attachment/add-revision-dialog.tsx b/lib/b-rfq/attachment/add-revision-dialog.tsx deleted file mode 100644 index 1abefb02..00000000 --- a/lib/b-rfq/attachment/add-revision-dialog.tsx +++ /dev/null @@ -1,336 +0,0 @@ -"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 { - 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 { addRevisionToAttachment } from "../service" - -// 리비전 추가 폼 스키마 -const addRevisionSchema = z.object({ - revisionComment: z.string().optional(), - file: z.instanceof(File, { - message: "파일을 선택해주세요.", - }), -}) - -type AddRevisionFormData = z.infer<typeof addRevisionSchema> - -interface AddRevisionDialogProps { - open: boolean - onOpenChange: (open: boolean) => void - attachmentId: number - currentRevision: string - originalFileName: string - onSuccess?: () => void -} - -export function AddRevisionDialog({ - open, - onOpenChange, - attachmentId, - currentRevision, - originalFileName, - onSuccess -}: AddRevisionDialogProps) { - const [isSubmitting, setIsSubmitting] = React.useState(false) - const [uploadProgress, setUploadProgress] = React.useState<number>(0) - - const form = useForm<AddRevisionFormData>({ - resolver: zodResolver(addRevisionSchema), - defaultValues: { - revisionComment: "", - file: undefined, - }, - }) - - const selectedFile = form.watch("file") - - // 다이얼로그 닫기 핸들러 - const handleOpenChange = (newOpen: boolean) => { - if (!newOpen && !isSubmitting) { - form.reset() - } - onOpenChange(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("attachmentId", attachmentId.toString()) - formData.append("file", file) - formData.append("isRevision", "true") - - const response = await fetch("/api/upload/rfq-attachment-revision", { - method: "POST", - body: formData, - }) - - if (!response.ok) { - const error = await response.json() - throw new Error(error.message || "파일 업로드 실패") - } - - return response.json() - } - - // 폼 제출 - const onSubmit = async (data: AddRevisionFormData) => { - setIsSubmitting(true) - setUploadProgress(0) - - try { - // 1단계: 파일 업로드 - setUploadProgress(30) - const uploadedFile = await uploadFile(data.file) - - // 2단계: DB 리비전 레코드 생성 - setUploadProgress(70) - const result = await addRevisionToAttachment(attachmentId, { - fileName: uploadedFile.fileName, - originalFileName: uploadedFile.originalFileName, - filePath: uploadedFile.filePath, - fileSize: uploadedFile.fileSize, - fileType: uploadedFile.fileType, - revisionComment: data.revisionComment, - }) - - setUploadProgress(100) - - if (result.success) { - toast.success(result.message) - form.reset() - handleOpenChange(false) - onSuccess?.() - } else { - toast.error(result.message) - } - - } catch (error) { - console.error("Upload error:", error) - toast.error(error instanceof Error ? error.message : "리비전 추가 중 오류가 발생했습니다.") - } finally { - setIsSubmitting(false) - setUploadProgress(0) - } - } - - // 다음 리비전 번호 계산 - const getNextRevision = (current: string) => { - const match = current.match(/Rev\.(\d+)/) - if (match) { - const num = parseInt(match[1]) + 1 - return `Rev.${num}` - } - return "Rev.1" - } - - const nextRevision = getNextRevision(currentRevision) - - return ( - <Dialog open={open} onOpenChange={handleOpenChange}> - <DialogContent className="sm:max-w-[500px]"> - <DialogHeader> - <DialogTitle className="flex items-center gap-2"> - <Upload className="h-5 w-5" /> - 새 리비전 추가 - </DialogTitle> - <DialogDescription> - "{originalFileName}"의 새 버전을 업로드합니다. - 현재 {currentRevision} → {nextRevision} - </DialogDescription> - </DialogHeader> - - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> - {/* 리비전 코멘트 */} - <FormField - control={form.control} - name="revisionComment" - render={({ field }) => ( - <FormItem> - <FormLabel>리비전 코멘트 (선택)</FormLabel> - <FormControl> - <Textarea - placeholder={`${nextRevision} 업데이트 내용을 입력하세요`} - className="resize-none" - rows={3} - {...field} - /> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - {/* 파일 선택 - Dropzone (단일 파일) */} - <FormField - control={form.control} - name="file" - render={({ field }) => ( - <FormItem> - <FormLabel>새 파일 선택</FormLabel> - <FormControl> - <div className="space-y-3"> - <Dropzone - onDrop={(acceptedFiles) => { - handleFileChange(acceptedFiles) - }} - accept={{ - 'application/pdf': ['.pdf'], - 'application/msword': ['.doc'], - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'], - 'application/vnd.ms-excel': ['.xls'], - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'], - 'application/vnd.ms-powerpoint': ['.ppt'], - 'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['.pptx'], - 'application/zip': ['.zip'], - 'application/x-rar-compressed': ['.rar'] - }} - maxSize={10 * 1024 * 1024} // 10MB - multiple={false} - disabled={isSubmitting} - > - <DropzoneZone> - <DropzoneUploadIcon /> - <DropzoneTitle>클릭하여 파일 선택 또는 드래그 앤 드롭</DropzoneTitle> - <DropzoneDescription> - PDF, DOC, XLS, PPT 등 (최대 10MB, 파일 1개) - </DropzoneDescription> - <DropzoneInput /> - </DropzoneZone> - </Dropzone> - - {/* 선택된 파일 표시 */} - {selectedFile && ( - <div className="space-y-2"> - <FileListHeader> - 선택된 파일 ({nextRevision}) - </FileListHeader> - <FileList> - <FileListItem> - <FileListIcon /> - <FileListInfo> - <FileListName>{selectedFile.name}</FileListName> - <FileListDescription> - <FileListSize>{selectedFile.size}</FileListSize> - </FileListDescription> - </FileListInfo> - <FileListAction - onClick={removeFile} - disabled={isSubmitting} - /> - </FileListItem> - </FileList> - </div> - )} - - {/* 업로드 진행률 */} - {isSubmitting && uploadProgress > 0 && ( - <div className="space-y-2"> - <div className="flex justify-between text-sm"> - <span>업로드 진행률</span> - <span>{uploadProgress}%</span> - </div> - <div className="w-full bg-gray-200 rounded-full h-2"> - <div - className="bg-blue-600 h-2 rounded-full transition-all duration-300" - style={{ width: `${uploadProgress}%` }} - /> - </div> - </div> - )} - </div> - </FormControl> - <FormMessage /> - </FormItem> - )} - /> - - <DialogFooter> - <Button - type="button" - variant="outline" - onClick={() => handleOpenChange(false)} - disabled={isSubmitting} - > - 취소 - </Button> - <Button type="submit" disabled={isSubmitting || !selectedFile}> - {isSubmitting ? "업로드 중..." : `${nextRevision} 추가`} - </Button> - </DialogFooter> - </form> - </Form> - </DialogContent> - </Dialog> - ) -}
\ No newline at end of file |
