import { useState, useEffect, useRef } from 'react' import { api } from '../../api/client' function UploadModal({ open, onClose, onUpload, uploading }) { const [file, setFile] = useState(null) const [name, setName] = useState('') const [preview, setPreview] = useState(null) const fileInputRef = useRef(null) const [dragOver, setDragOver] = useState(false) useEffect(() => { if (!open) { setFile(null) setName('') setPreview(null) } }, [open]) const handleFile = (f) => { if (!f || !f.type.startsWith('image/')) return setFile(f) setName(f.name.replace(/\.[^.]+$/, '')) const reader = new FileReader() reader.onload = (e) => setPreview(e.target.result) reader.readAsDataURL(f) } const handleSubmit = async (e) => { e.preventDefault() if (!file || !name.trim()) return await onUpload({ file, name: name.trim() }) } if (!open) return null return (
e.stopPropagation()}>

이미지 업로드

{/* 파일 업로드 영역 */} {/* 이름 */}
setName(e.target.value)} placeholder="예: 강렬한 힘의 결정" className="w-full rounded-lg border border-white/10 bg-gray-950 px-3 py-2 text-sm outline-none focus:border-emerald-500/50 transition" />
{/* 버튼 */}
) } function ImageCard({ image, onDelete }) { const [showMenu, setShowMenu] = useState(false) const [copied, setCopied] = useState(false) const copyUrl = () => { navigator.clipboard.writeText(image.url) setCopied(true) setTimeout(() => setCopied(false), 1500) } return (
{/* 이미지 영역 */}
{image.name} {/* 액션 버튼 */}
{/* 정보 */}
{image.name}
{image.url}
) } export default function AdminImages() { const [images, setImages] = useState([]) const [loading, setLoading] = useState(true) const [modalOpen, setModalOpen] = useState(false) const [uploading, setUploading] = useState(false) const [search, setSearch] = useState('') const fetchImages = async () => { setLoading(true) try { const data = await api('/api/admin/images') setImages(data) } catch { setImages([]) } finally { setLoading(false) } } useEffect(() => { fetchImages() }, []) const handleUpload = async ({ file, name }) => { setUploading(true) try { const formData = new FormData() formData.append('file', file) formData.append('name', name) const adminKey = localStorage.getItem('maple-admin-key') const res = await fetch('/api/admin/images', { method: 'POST', headers: { 'x-admin-key': adminKey }, body: formData, }) if (!res.ok) { const err = await res.json().catch(() => ({})) throw new Error(err.error || '업로드 실패') } setModalOpen(false) await fetchImages() } catch (err) { alert(err.message) } finally { setUploading(false) } } const handleDelete = async (image) => { if (!confirm(`"${image.name}" 이미지를 삭제하시겠습니까?`)) return try { await api(`/api/admin/images/${image.id}`, { method: 'DELETE' }) await fetchImages() } catch (err) { alert(err.message) } } const filtered = images.filter((img) => img.name.toLowerCase().includes(search.toLowerCase()) ) return (

이미지 관리

공용 이미지를 업로드하고 관리합니다

{/* 검색 */} {images.length > 0 && (
setSearch(e.target.value)} placeholder="이미지 이름으로 검색..." className="w-full rounded-lg border border-white/10 bg-gray-900/50 pl-10 pr-4 py-2.5 text-sm outline-none focus:border-emerald-500/50 transition" /> 🔍
)} {/* 이미지 그리드 */} {loading ? (
{Array.from({ length: 8 }).map((_, i) => (
))}
) : filtered.length === 0 ? (
🖼️

{images.length === 0 ? '업로드된 이미지가 없습니다' : '검색 결과가 없습니다'}

{images.length === 0 && ( )}
) : (
{filtered.map((image) => ( ))}
)} setModalOpen(false)} onUpload={handleUpload} uploading={uploading} />
) }