"use client" import * as React from "react" import { zodResolver } from "@hookform/resolvers/zod" import { useForm, useFieldArray } from "react-hook-form" import { toast } from "sonner" import { Loader, Download, X, FileText } from "lucide-react" import { useRouter, useParams } from "next/navigation" import { useTranslation } from "@/i18n/client" import { z } from "zod" import { Button } from "@/components/ui/button" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Dropzone, DropzoneZone, DropzoneInput, DropzoneUploadIcon, DropzoneTitle, DropzoneDescription, } from "@/components/ui/dropzone" import { Textarea } from "@/components/ui/textarea" import { Switch } from "@/components/ui/switch" import { updateInformationData, uploadInformationAttachment, deleteInformationAttachmentAction, downloadInformationAttachment } from "@/lib/information/service" import type { PageInformation, InformationAttachment } from "@/db/schema/information" // downloadFile은 동적으로 import import prettyBytes from "pretty-bytes" import { useSession } from "next-auth/react" const MAX_FILE_SIZE = 50 * 1024 * 1024 // 50MB // 폼 스키마 const updateInformationSchema = z.object({ id: z.number(), informationContent: z.string().min(1, "안내사항 내용을 입력해주세요"), isActive: z.boolean(), newFiles: z.array(z.any()).optional(), }) type UpdateInformationSchema = z.infer interface UpdateInformationDialogProps { open: boolean onOpenChange: (open: boolean) => void information?: PageInformation & { attachments?: InformationAttachment[] } onSuccess?: () => void } export function UpdateInformationDialog({ open, onOpenChange, information, onSuccess, }: UpdateInformationDialogProps) { const router = useRouter() const params = useParams() const lng = (params?.lng as string) || 'ko' const { t } = useTranslation(lng, 'common') const [isLoading, setIsLoading] = React.useState(false) const [isUploadingFiles, setIsUploadingFiles] = React.useState(false) const [existingAttachments, setExistingAttachments] = React.useState([]) const session = useSession() const userId = session.data?.user?.id const form = useForm({ resolver: zodResolver(updateInformationSchema), defaultValues: { id: 0, informationContent: "", isActive: true, newFiles: [], }, }) const { fields: newFileFields, append: appendFile, remove: removeFile } = useFieldArray({ control: form.control, name: "newFiles", }) // 안내사항 데이터가 변경되면 폼 업데이트 React.useEffect(() => { if (information && open) { form.reset({ id: information.id, informationContent: information.informationContent || "", isActive: information.isActive, newFiles: [], }) setExistingAttachments(information.attachments || []) } }, [information, open, form]) // 파일 드롭 핸들러 const handleDropAccepted = React.useCallback((acceptedFiles: File[]) => { acceptedFiles.forEach(file => { appendFile(file) }) }, [appendFile]) const handleDropRejected = React.useCallback((rejectedFiles: any[]) => { rejectedFiles.forEach(rejection => { toast.error(`파일 업로드 실패: ${rejection.file.name}`) }) }, []) // 기존 첨부파일 다운로드 const handleDownloadAttachment = async (attachment: InformationAttachment) => { try { const result = await downloadInformationAttachment(attachment.id) if (result.success && result.data) { // 동적으로 downloadFile 함수 import const { downloadFile } = await import('@/lib/file-download') await downloadFile(result.data.filePath, result.data.fileName) toast.success("파일 다운로드가 시작되었습니다.") } else { toast.error(result.message || "파일 다운로드에 실패했습니다.") } } catch (error) { console.error("파일 다운로드 오류:", error) toast.error("파일 다운로드 중 오류가 발생했습니다.") } } // 기존 첨부파일 삭제 const handleDeleteAttachment = async (attachmentId: number) => { if (!confirm("정말로 이 첨부파일을 삭제하시겠습니까?")) return try { const result = await deleteInformationAttachmentAction(attachmentId) if (result.success) { setExistingAttachments(prev => prev.filter(att => att.id !== attachmentId)) toast.success(result.message) } else { toast.error(result.message) } } catch (error) { console.error("첨부파일 삭제 오류:", error) toast.error("첨부파일 삭제 중 오류가 발생했습니다.") } } const onSubmit = async (values: UpdateInformationSchema) => { setIsLoading(true) try { // 1. 안내사항 정보 업데이트 const updateResult = await updateInformationData({ id: values.id, informationContent: values.informationContent, isActive: values.isActive, }, userId) if (!updateResult.success) { toast.error(updateResult.message) return } // 2. 새 첨부파일 업로드 if (values.newFiles && values.newFiles.length > 0) { setIsUploadingFiles(true) for (const file of values.newFiles) { const formData = new FormData() formData.append("informationId", String(values.id)) formData.append("file", file) const uploadResult = await uploadInformationAttachment(formData) if (!uploadResult.success) { toast.error(`파일 업로드 실패: ${file.name} - ${uploadResult.message}`) } } setIsUploadingFiles(false) } toast.success("안내사항이 성공적으로 수정되었습니다.") if (onSuccess) onSuccess() onOpenChange(false) router.refresh() } catch (error) { console.error("안내사항 수정 오류:", error) toast.error("안내사항 수정에 실패했습니다.") } finally { setIsLoading(false) setIsUploadingFiles(false) } } const handleClose = () => { form.reset() setExistingAttachments([]) onOpenChange(false) } return ( {t('information.edit.title', '안내사항 수정')} {t('information.edit.description', '페이지 안내사항 정보를 수정합니다.')}
{/* 페이지 정보 */}
{t('information.page.info', '페이지 정보')}
{t('information.page.name', '페이지명')}: {information?.pageName}
{t('information.page.path', '경로')}: {information?.pagePath}
{/* 인포메이션 내용 */} ( {t('information.content.label', '안내사항')}