diff options
Diffstat (limited to 'lib/bidding/selection/selection-result-form.tsx')
| -rw-r--r-- | lib/bidding/selection/selection-result-form.tsx | 98 |
1 files changed, 67 insertions, 31 deletions
diff --git a/lib/bidding/selection/selection-result-form.tsx b/lib/bidding/selection/selection-result-form.tsx index 7f1229a2..54687cc9 100644 --- a/lib/bidding/selection/selection-result-form.tsx +++ b/lib/bidding/selection/selection-result-form.tsx @@ -8,14 +8,13 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form' -import { FileUpload } from '@/components/ui/file-upload' import { useToast } from '@/hooks/use-toast' import { saveSelectionResult } from './actions' -import { Loader2, Save } from 'lucide-react' +import { Loader2, Save, FileText } from 'lucide-react' +import { Dropzone, DropzoneZone, DropzoneUploadIcon, DropzoneTitle, DropzoneDescription, DropzoneInput } from '@/components/ui/dropzone' const selectionResultSchema = z.object({ summary: z.string().min(1, '결과요약을 입력해주세요'), - attachments: z.array(z.any()).optional(), }) type SelectionResultFormData = z.infer<typeof selectionResultSchema> @@ -28,22 +27,26 @@ interface SelectionResultFormProps { export function SelectionResultForm({ biddingId, onSuccess }: SelectionResultFormProps) { const { toast } = useToast() const [isSubmitting, setIsSubmitting] = React.useState(false) + const [attachmentFiles, setAttachmentFiles] = React.useState<File[]>([]) const form = useForm<SelectionResultFormData>({ resolver: zodResolver(selectionResultSchema), defaultValues: { summary: '', - attachments: [], }, }) + const removeAttachmentFile = (index: number) => { + setAttachmentFiles(prev => prev.filter((_, i) => i !== index)) + } + const onSubmit = async (data: SelectionResultFormData) => { setIsSubmitting(true) try { const result = await saveSelectionResult({ biddingId, summary: data.summary, - attachments: data.attachments + attachments: attachmentFiles }) if (result.success) { @@ -99,33 +102,66 @@ export function SelectionResultForm({ biddingId, onSuccess }: SelectionResultFor /> {/* 첨부파일 */} - <FormField - control={form.control} - name="attachments" - render={({ field }) => ( - <FormItem> - <FormLabel>첨부파일</FormLabel> - <FormControl> - <FileUpload - value={field.value || []} - onChange={field.onChange} - 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'], - 'image/*': ['.png', '.jpg', '.jpeg', '.gif'], - }} - maxSize={10 * 1024 * 1024} // 10MB - maxFiles={5} - placeholder="선정결과 관련 문서를 업로드해주세요" - /> - </FormControl> - <FormMessage /> - </FormItem> + <div className="space-y-4"> + <FormLabel>첨부파일</FormLabel> + <Dropzone + maxSize={10 * 1024 * 1024} // 10MB + onDropAccepted={(files) => { + const newFiles = Array.from(files) + setAttachmentFiles(prev => [...prev, ...newFiles]) + }} + onDropRejected={() => { + toast({ + title: "파일 업로드 거부", + description: "파일 크기 및 형식을 확인해주세요.", + variant: "destructive", + }) + }} + > + <DropzoneZone> + <DropzoneUploadIcon className="mx-auto h-12 w-12 text-muted-foreground" /> + <DropzoneTitle className="text-lg font-medium"> + 파일을 드래그하거나 클릭하여 업로드 + </DropzoneTitle> + <DropzoneDescription className="text-sm text-muted-foreground"> + PDF, Word, Excel, 이미지 파일 (최대 10MB) + </DropzoneDescription> + </DropzoneZone> + <DropzoneInput /> + </Dropzone> + + {attachmentFiles.length > 0 && ( + <div className="space-y-2"> + <h4 className="text-sm font-medium">업로드된 파일</h4> + <div className="space-y-2"> + {attachmentFiles.map((file, index) => ( + <div + key={index} + className="flex items-center justify-between p-3 bg-muted rounded-lg" + > + <div className="flex items-center gap-3"> + <FileText className="h-4 w-4 text-muted-foreground" /> + <div> + <p className="text-sm font-medium">{file.name}</p> + <p className="text-xs text-muted-foreground"> + {(file.size / 1024 / 1024).toFixed(2)} MB + </p> + </div> + </div> + <Button + type="button" + variant="ghost" + size="sm" + onClick={() => removeAttachmentFile(index)} + > + 제거 + </Button> + </div> + ))} + </div> + </div> )} - /> + </div> {/* 저장 버튼 */} <div className="flex justify-end"> |
