From e2b7330c5ed9cb4cffddc94a519761f9e80c3ff9 Mon Sep 17 00:00:00 2001 From: wallace Date: Thu, 27 Nov 2025 01:00:36 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B5=90=EC=9C=A1=EA=B3=BC=EC=A0=95=20?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=AF=B8?= =?UTF-8?q?=EB=A6=AC=EB=B3=B4=EA=B8=B01?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/courses/CourseRegistrationModal.tsx | 205 +++++++++++++++--- 1 file changed, 180 insertions(+), 25 deletions(-) diff --git a/src/app/admin/courses/CourseRegistrationModal.tsx b/src/app/admin/courses/CourseRegistrationModal.tsx index 5c6daf3..c834bc7 100644 --- a/src/app/admin/courses/CourseRegistrationModal.tsx +++ b/src/app/admin/courses/CourseRegistrationModal.tsx @@ -3,6 +3,7 @@ import React, { useState, useRef, useEffect, useMemo } from "react"; import ModalCloseSvg from "@/app/svgs/closexsvg"; import DropdownIcon from "@/app/svgs/dropdownicon"; +import CloseXOSvg from "@/app/svgs/closexo"; import { getInstructors, type UserRow } from "@/app/admin/id/mockData"; import { type Course } from "./mockData"; @@ -20,8 +21,12 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [errors, setErrors] = useState>({}); const [isDeleteConfirmOpen, setIsDeleteConfirmOpen] = useState(false); + const [selectedImage, setSelectedImage] = useState(null); + const [previewUrl, setPreviewUrl] = useState(null); + const [isDragging, setIsDragging] = useState(false); const dropdownRef = useRef(null); const modalRef = useRef(null); + const fileInputRef = useRef(null); // 강사 목록 가져오기 const [instructors, setInstructors] = useState([]); @@ -66,6 +71,9 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet setIsDropdownOpen(false); setErrors({}); setIsDeleteConfirmOpen(false); + setSelectedImage(null); + setPreviewUrl(null); + setIsDragging(false); } }, [open, editingCourse, instructors]); @@ -135,6 +143,99 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet setIsDeleteConfirmOpen(false); }; + // 파일 유효성 검사 + const validateImageFile = (file: File): string | null => { + const maxSize = 30 * 1024 * 1024; // 30MB + const allowedTypes = ['image/png', 'image/jpeg', 'image/jpg']; + + if (!allowedTypes.includes(file.type)) { + return "PNG 또는 JPG 파일만 업로드 가능합니다."; + } + + if (file.size > maxSize) { + return "파일 크기는 30MB 미만이어야 합니다."; + } + + return null; + }; + + // 이미지 파일 처리 + const handleImageFile = (file: File) => { + const error = validateImageFile(file); + if (error) { + setErrors((prev) => ({ ...prev, image: error })); + return; + } + + setSelectedImage(file); + setErrors((prev) => { + const next = { ...prev }; + delete next.image; + return next; + }); + + // 미리보기 URL 생성 + const reader = new FileReader(); + reader.onloadend = () => { + setPreviewUrl(reader.result as string); + }; + reader.readAsDataURL(file); + }; + + // 파일 선택 핸들러 + const handleFileSelect = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + handleImageFile(file); + } + }; + + // 클릭으로 파일 선택 + const handleImageAreaClick = () => { + fileInputRef.current?.click(); + }; + + // 드래그 오버 핸들러 + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(true); + }; + + // 드래그 리브 핸들러 + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + }; + + // 드롭 핸들러 + const handleDrop = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setIsDragging(false); + + const file = e.dataTransfer.files?.[0]; + if (file) { + handleImageFile(file); + } + }; + + // 이미지 삭제 핸들러 + const handleRemoveImage = (e: React.MouseEvent) => { + e.stopPropagation(); + setSelectedImage(null); + setPreviewUrl(null); + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + setErrors((prev) => { + const next = { ...prev }; + delete next.image; + return next; + }); + }; + if (!open) return null; return ( @@ -281,32 +382,86 @@ export default function CourseRegistrationModal({ open, onClose, onSave, onDelet -
-
- - - -
-
-

- (클릭하여 이미지 업로드) - - 미첨부 시 기본 이미지가 노출됩니다. -

-
+ +
+ {previewUrl ? ( + <> +
+
+ 미리보기 + +
+
+
+
+

+ 클릭하여 이미지 변경 +

+
+
+ + ) : ( + <> +
+ + + +
+
+

+ (클릭하여 이미지 업로드) + + 미첨부 시 기본 이미지가 노출됩니다. +

+
+ + )}
+ {errors.image && ( +

{errors.image}

+ )}