diff options
Diffstat (limited to 'lib/pq/pq-review-table-new/edit-investigation-dialog.tsx')
| -rw-r--r-- | lib/pq/pq-review-table-new/edit-investigation-dialog.tsx | 123 |
1 files changed, 118 insertions, 5 deletions
diff --git a/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx b/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx index 4df7a7ec..7fd1c3f8 100644 --- a/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx +++ b/lib/pq/pq-review-table-new/edit-investigation-dialog.tsx @@ -3,7 +3,7 @@ import * as React from "react"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
-import { CalendarIcon, Loader } from "lucide-react"
+import { CalendarIcon, Loader, Upload, X, FileText } from "lucide-react"
import { format } from "date-fns"
import { toast } from "sonner"
@@ -49,6 +49,7 @@ const editInvestigationSchema = z.object({ ]).optional(),
evaluationResult: z.enum(["APPROVED", "SUPPLEMENT", "REJECTED"]).optional(),
investigationNotes: z.string().max(1000, "QM 의견은 1000자 이내로 입력해주세요.").optional(),
+ attachments: z.array(z.instanceof(File)).optional(),
})
type EditInvestigationSchema = z.infer<typeof editInvestigationSchema>
@@ -72,6 +73,8 @@ export function EditInvestigationDialog({ onSubmit,
}: EditInvestigationDialogProps) {
const [isPending, startTransition] = React.useTransition()
+ const [selectedFiles, setSelectedFiles] = React.useState<File[]>([])
+ const fileInputRef = React.useRef<HTMLInputElement>(null)
const form = useForm<EditInvestigationSchema>({
resolver: zodResolver(editInvestigationSchema),
@@ -79,6 +82,7 @@ export function EditInvestigationDialog({ confirmedAt: investigation?.confirmedAt || undefined,
evaluationResult: investigation?.evaluationResult as "APPROVED" | "SUPPLEMENT" | "REJECTED" | undefined,
investigationNotes: investigation?.investigationNotes || "",
+ attachments: [],
},
})
@@ -89,14 +93,47 @@ export function EditInvestigationDialog({ confirmedAt: investigation.confirmedAt || undefined,
evaluationResult: investigation.evaluationResult as "APPROVED" | "SUPPLEMENT" | "REJECTED" | undefined,
investigationNotes: investigation.investigationNotes || "",
+ attachments: [],
})
+ setSelectedFiles([])
}
}, [investigation, form])
+ // 파일 선택 핸들러
+ const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
+ const files = Array.from(event.target.files || [])
+ if (files.length > 0) {
+ const newFiles = [...selectedFiles, ...files]
+ setSelectedFiles(newFiles)
+ form.setValue('attachments', newFiles, { shouldValidate: true })
+ }
+ }
+
+ // 파일 제거 핸들러
+ const removeFile = (index: number) => {
+ const updatedFiles = selectedFiles.filter((_, i) => i !== index)
+ setSelectedFiles(updatedFiles)
+ form.setValue('attachments', updatedFiles, { shouldValidate: true })
+ }
+
+ // 파일 크기 포맷팅
+ const formatFileSize = (bytes: number) => {
+ if (bytes === 0) return '0 Bytes'
+ const k = 1024
+ const sizes = ['Bytes', 'KB', 'MB', 'GB']
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
+ }
+
const handleSubmit = async (values: EditInvestigationSchema) => {
startTransition(async () => {
try {
- await onSubmit(values)
+ // 선택된 파일들을 values에 포함
+ const submitData = {
+ ...values,
+ attachments: selectedFiles,
+ }
+ await onSubmit(submitData)
toast.success("실사 정보가 업데이트되었습니다!")
onClose()
} catch (error) {
@@ -181,16 +218,16 @@ export function EditInvestigationDialog({ )}
/>
- {/* QM 의견 */}
+ {/* 구매 의견 */}
<FormField
control={form.control}
name="investigationNotes"
render={({ field }) => (
<FormItem>
- <FormLabel>QM 의견</FormLabel>
+ <FormLabel>구매 의견</FormLabel>
<FormControl>
<Textarea
- placeholder="실사에 대한 QM 의견을 입력하세요..."
+ placeholder="실사에 대한 구매 의견을 입력하세요..."
{...field}
className="min-h-[80px]"
/>
@@ -200,6 +237,82 @@ export function EditInvestigationDialog({ )}
/>
+ {/* 첨부파일 */}
+ <FormField
+ control={form.control}
+ name="attachments"
+ render={() => (
+ <FormItem>
+ <FormLabel>첨부파일</FormLabel>
+ <FormControl>
+ <div className="space-y-4">
+ {/* 파일 선택 영역 */}
+ <div className="border-2 border-dashed border-gray-300 rounded-lg p-6 text-center">
+ <input
+ ref={fileInputRef}
+ type="file"
+ multiple
+ accept=".pdf,.doc,.docx,.xls,.xlsx,.png,.jpg,.jpeg,.gif"
+ onChange={handleFileSelect}
+ className="hidden"
+ />
+ <Upload className="mx-auto h-8 w-8 text-gray-400 mb-2" />
+ <div className="text-sm text-gray-600 mb-2">
+ 파일을 드래그하거나 클릭하여 선택하세요
+ </div>
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => fileInputRef.current?.click()}
+ disabled={isPending}
+ >
+ 파일 선택
+ </Button>
+ <div className="text-xs text-gray-500 mt-2">
+ 지원 형식: PDF, DOC, DOCX, XLS, XLSX, PNG, JPG, JPEG, GIF (최대 10MB)
+ </div>
+ </div>
+
+ {/* 선택된 파일 목록 */}
+ {selectedFiles.length > 0 && (
+ <div className="space-y-2">
+ <div className="text-sm font-medium">선택된 파일:</div>
+ {selectedFiles.map((file, index) => (
+ <div
+ key={index}
+ className="flex items-center justify-between p-2 bg-gray-50 rounded border"
+ >
+ <div className="flex items-center space-x-2">
+ <FileText className="h-4 w-4 text-gray-500" />
+ <div className="flex-1 min-w-0">
+ <div className="text-sm font-medium truncate">
+ {file.name}
+ </div>
+ <div className="text-xs text-gray-500">
+ {formatFileSize(file.size)}
+ </div>
+ </div>
+ </div>
+ <Button
+ type="button"
+ variant="ghost"
+ size="sm"
+ onClick={() => removeFile(index)}
+ disabled={isPending}
+ >
+ <X className="h-4 w-4" />
+ </Button>
+ </div>
+ ))}
+ </div>
+ )}
+ </div>
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
<DialogFooter>
<Button type="button" variant="outline" onClick={onClose} disabled={isPending}>
취소
|
