summaryrefslogtreecommitdiff
path: root/lib/b-rfq/attachment/add-attachment-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/b-rfq/attachment/add-attachment-dialog.tsx')
-rw-r--r--lib/b-rfq/attachment/add-attachment-dialog.tsx355
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