import { useState, useEffect, useCallback, useRef } from 'react' import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragOverlay, type DragEndEvent, } from '@dnd-kit/core' import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, } from '@dnd-kit/sortable' import { fetchMenuConfig, updateMenuConfigApi, type MenuConfigItem, } from '@common/services/authApi' import { useMenuStore } from '@common/store/menuStore' import SortableMenuItem from './SortableMenuItem' // ─── 메뉴 관리 패널 ───────────────────────────────────────── function MenusPanel() { const [menus, setMenus] = useState([]) const [originalMenus, setOriginalMenus] = useState([]) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [editingId, setEditingId] = useState(null) const [emojiPickerId, setEmojiPickerId] = useState(null) const [activeId, setActiveId] = useState(null) const emojiPickerRef = useRef(null) const { setMenuConfig } = useMenuStore() const hasChanges = JSON.stringify(menus) !== JSON.stringify(originalMenus) const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ) const loadMenus = useCallback(async () => { setLoading(true) try { const config = await fetchMenuConfig() setMenus(config) setOriginalMenus(config) } catch (err) { console.error('메뉴 설정 조회 실패:', err) } finally { setLoading(false) } }, []) useEffect(() => { loadMenus() }, [loadMenus]) useEffect(() => { if (!emojiPickerId) return const handler = (e: MouseEvent) => { if (emojiPickerRef.current && !emojiPickerRef.current.contains(e.target as Node)) { setEmojiPickerId(null) } } document.addEventListener('mousedown', handler) return () => document.removeEventListener('mousedown', handler) }, [emojiPickerId]) const toggleMenu = (id: string) => { setMenus(prev => prev.map(m => m.id === id ? { ...m, enabled: !m.enabled } : m)) } const updateMenuField = (id: string, field: 'label' | 'icon', value: string) => { setMenus(prev => prev.map(m => m.id === id ? { ...m, [field]: value } : m)) } const handleEmojiSelect = (emoji: { native: string }) => { if (emojiPickerId) { updateMenuField(emojiPickerId, 'icon', emoji.native) setEmojiPickerId(null) } } const moveMenu = (idx: number, direction: -1 | 1) => { const targetIdx = idx + direction if (targetIdx < 0 || targetIdx >= menus.length) return setMenus(prev => { const arr = [...prev] ;[arr[idx], arr[targetIdx]] = [arr[targetIdx], arr[idx]] return arr.map((m, i) => ({ ...m, order: i + 1 })) }) } const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event setActiveId(null) if (!over || active.id === over.id) return setMenus(prev => { const oldIndex = prev.findIndex(m => m.id === active.id) const newIndex = prev.findIndex(m => m.id === over.id) const reordered = arrayMove(prev, oldIndex, newIndex) return reordered.map((m, i) => ({ ...m, order: i + 1 })) }) } const handleSave = async () => { setSaving(true) try { const updated = await updateMenuConfigApi(menus) setMenus(updated) setOriginalMenus(updated) setMenuConfig(updated) } catch (err) { console.error('메뉴 설정 저장 실패:', err) } finally { setSaving(false) } } if (loading) { return (
메뉴 설정을 불러오는 중...
) } const activeMenu = activeId ? menus.find(m => m.id === activeId) : null return (

메뉴 관리

메뉴 표시 여부, 순서, 라벨, 아이콘을 관리합니다

setActiveId(event.active.id as string)} onDragEnd={handleDragEnd} > m.id)} strategy={verticalListSortingStrategy}>
{menus.map((menu, idx) => ( { setEditingId(null); setEmojiPickerId(null) }} onEmojiPickerToggle={setEmojiPickerId} onLabelChange={(id, value) => updateMenuField(id, 'label', value)} onEmojiSelect={handleEmojiSelect} /> ))}
{activeMenu ? (
{activeMenu.icon} {activeMenu.label}
) : null}
) } export default MenusPanel