import { useState, useEffect } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { api } from '../../api/client'
import ImagePicker from './components/ImagePicker'
import ConfirmDialog from '../../components/ConfirmDialog'
function Field({ label, hint, error, required, children }) {
return (
{hint && {hint}}
{children}
{error &&
{error}
}
)
}
const inputCls = 'w-full rounded-lg border px-3 py-2 text-sm outline-none focus:border-[var(--input-border-focus)] hover:border-[var(--input-border-hover)]'
const inputStyle = {
background: 'var(--input-bg)',
borderColor: 'var(--input-border)',
color: 'var(--text-strong)',
}
export default function AdminMenuForm() {
const navigate = useNavigate()
const queryClient = useQueryClient()
const { id } = useParams()
const isEdit = !!id
const [pickerOpen, setPickerOpen] = useState(false)
const [confirmDelete, setConfirmDelete] = useState(false)
const [form, setForm] = useState({
title: '',
description: '',
slug: '',
image_id: null,
image: null,
})
const [errors, setErrors] = useState({})
const { data: menuData } = useQuery({
queryKey: ['admin', 'menus', id],
queryFn: () => api(`/api/admin/menus/${id}`),
enabled: isEdit,
})
useEffect(() => {
if (!isEdit) {
setForm({ title: '', description: '', slug: '', image_id: null, image: null })
return
}
if (menuData) {
setForm({
title: menuData.title || '',
description: menuData.description || '',
slug: (menuData.url || '').replace(/^\/+/, ''),
image_id: menuData.image_id,
image: menuData.image,
})
}
}, [isEdit, id, menuData])
const update = (patch) => setForm((prev) => ({ ...prev, ...patch }))
const handleSlugChange = (value) => {
update({ slug: value.replace(/^\/+/, '') })
}
const fullUrl = `/${form.slug.trim()}`
const validate = () => {
const errs = {}
if (!form.title.trim()) errs.title = '제목을 입력해주세요'
if (!form.slug.trim()) errs.slug = '경로를 입력해주세요'
else if (!/^[a-zA-Z0-9\-/]+$/.test(form.slug.trim())) errs.slug = '영문, 숫자, 하이픈(-), 슬래시(/)만 사용할 수 있습니다'
setErrors(errs)
return Object.keys(errs).length === 0
}
const saveMutation = useMutation({
mutationFn: async () => {
const payload = {
title: form.title.trim(),
description: form.description.trim(),
url: fullUrl,
image_id: form.image_id,
}
if (isEdit) {
return api(`/api/admin/menus/${id}`, { method: 'PATCH', body: payload })
}
return api('/api/admin/menus', { method: 'POST', body: payload })
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin', 'menus'] })
queryClient.invalidateQueries({ queryKey: ['menus'] })
navigate('/admin')
},
onError: (err) => alert(err.message),
})
const handleSubmit = (e) => {
e.preventDefault()
if (!validate()) return
saveMutation.mutate()
}
const deleteMutation = useMutation({
mutationFn: () => api(`/api/admin/menus/${id}`, { method: 'DELETE' }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['admin', 'menus'] })
queryClient.invalidateQueries({ queryKey: ['menus'] })
navigate('/admin')
},
onError: (err) => alert(err.message),
})
return (
{isEdit ? '메뉴 항목 편집' : '메뉴 항목 추가'}
홈 화면에 표시되는 카드의 정보를 설정합니다
setConfirmDelete(false)}
onConfirm={() => deleteMutation.mutate()}
title="메뉴 삭제"
description={`"${form.title}" 메뉴를 삭제하시겠습니까?\n\n이 작업은 되돌릴 수 없습니다.`}
confirmText="삭제"
destructive
loading={deleteMutation.isPending}
/>
setPickerOpen(false)}
currentImageId={form.image_id}
onSelect={(img) => update({ image_id: img?.id || null, image: img })}
/>
)
}