diff options
Diffstat (limited to 'lib/b-rfq/attachment/add-attachment-dialog.tsx')
| -rw-r--r-- | lib/b-rfq/attachment/add-attachment-dialog.tsx | 355 |
1 files changed, 0 insertions, 355 deletions
diff --git a/lib/b-rfq/attachment/add-attachment-dialog.tsx b/lib/b-rfq/attachment/add-attachment-dialog.tsx deleted file mode 100644 index 665e0f88..00000000 --- a/lib/b-rfq/attachment/add-attachment-dialog.tsx +++ /dev/null @@ -1,355 +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 { 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<typeof addAttachmentSchema> - -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<number>(0) - - const form = useForm<AddAttachmentFormData>({ - 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 ( - <Dialog open={open} onOpenChange={handleOpenChange}> - <DialogTrigger asChild> - <Button variant="outline" size="sm" className="gap-2"> - <Plus className="size-4" aria-hidden="true" /> - <span className="hidden sm:inline">새 첨부</span> - </Button> - </DialogTrigger> - - <DialogContent className="sm:max-w-[500px]"> - <DialogHeader> - <DialogTitle>새 첨부파일 추가</DialogTitle> - <DialogDescription> - RFQ에 첨부할 문서를 업로드합니다. 시리얼 번호는 자동으로 부여됩니다. - </DialogDescription> - </DialogHeader> - - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4"> - {/* 문서 타입 선택 */} - <FormField - control={form.control} - name="attachmentType" - render={({ field }) => ( - <FormItem> - <FormLabel>문서 타입</FormLabel> - <Select onValueChange={field.onChange} value={field.value}> - <FormControl> - <SelectTrigger> - <SelectValue placeholder="문서 타입을 선택하세요" /> - </SelectTrigger> - </FormControl> - <SelectContent> - <SelectItem value="구매">구매</SelectItem> - <SelectItem value="설계">설계</SelectItem> - </SelectContent> - </Select> - <FormMessage /> - </FormItem> - )} - /> - - {/* 설명 */} - <FormField - control={form.control} - name="description" - render={({ field }) => ( - <FormItem> - <FormLabel>설명 (선택)</FormLabel> - <FormControl> - <Textarea - placeholder="첨부파일에 대한 설명을 입력하세요" - 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> - 선택된 파일 - </FileListHeader> - <FileList> - <FileListItem className="flex items-center justify-between gap-3"> - <FileListIcon /> - <FileListInfo> - <FileListName>{selectedFile.name}</FileListName> - <FileListDescription> - <FileListSize>{selectedFile.size}</FileListSize> - </FileListDescription> - </FileListInfo> - <FileListAction - onClick={removeFile} - disabled={isSubmitting} - > - <X className="h-4 w-4" /> - </FileListAction> - </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 ? "업로드 중..." : "업로드"} - </Button> - </DialogFooter> - </form> - </Form> - </DialogContent> - </Dialog> - ) -}
\ No newline at end of file |
