import { useState, useEffect } from 'react' import { useQuery, useQueries } from '@tanstack/react-query' import { api } from '../../api/client' import { useLayout } from '../../components/Layout' import CharacterPanel from './user/CharacterPanel' import BossSelector from './user/BossSelector' const STORAGE_CHARS = 'maple-bc-characters' const STORAGE_SELECTIONS = 'maple-bc-selections' const MAX_PER_CHARACTER = 12 export default function BossCrystal() { const [characters, setCharacters] = useState(() => { const saved = localStorage.getItem(STORAGE_CHARS) return saved ? JSON.parse(saved) : [] }) const [selectedChar, setSelectedChar] = useState(() => { const saved = localStorage.getItem(STORAGE_CHARS) const list = saved ? JSON.parse(saved) : [] return list[0]?.character_name || null }) const [allSelections, setAllSelections] = useState(() => { const saved = localStorage.getItem(STORAGE_SELECTIONS) return saved ? JSON.parse(saved) : {} }) useEffect(() => { localStorage.setItem(STORAGE_CHARS, JSON.stringify(characters)) }, [characters]) useEffect(() => { localStorage.setItem(STORAGE_SELECTIONS, JSON.stringify(allSelections)) }, [allSelections]) // 풀스크린 모드 (푸터 숨김 + 내부 스크롤) const { setFullscreen } = useLayout() useEffect(() => { setFullscreen(true) return () => setFullscreen(false) }, [setFullscreen]) const { data: bosses = [], isLoading } = useQuery({ queryKey: ['boss-crystal', 'bosses'], queryFn: () => api('/api/boss-crystal/bosses').catch(() => []), }) // 저장된 캐릭터의 기본 정보(코디 이미지 포함) 새로고침 const charRefreshQueries = useQueries({ queries: characters.map((c) => ({ queryKey: ['character', 'basic', c.character_name], queryFn: () => api(`/api/character/search?name=${encodeURIComponent(c.character_name)}`), enabled: !!c.character_name, refetchOnMount: 'always', staleTime: 0, retry: false, })), }) useEffect(() => { if (!charRefreshQueries.length) return let changed = false const next = characters.map((c, i) => { const d = charRefreshQueries[i]?.data if (!d) return c if (d.character_image !== c.character_image || d.character_level !== c.character_level || d.job_name !== c.job_name) { changed = true return { ...c, ...d } } return c }) if (changed) setCharacters(next) // eslint-disable-next-line react-hooks/exhaustive-deps }, [charRefreshQueries.map((q) => q.dataUpdatedAt).join(',')]) const handleAddCharacter = (char) => { setCharacters((prev) => [...prev, char]) setSelectedChar(char.character_name) } const handleRemoveCharacter = (name) => { setCharacters((prev) => { const next = prev.filter((c) => c.character_name !== name) if (selectedChar === name) { setSelectedChar(next[0]?.character_name || null) } return next }) setAllSelections((prev) => { const next = { ...prev } delete next[name] return next }) } const handleReorderCharacters = (next) => { setCharacters(next) } const handleBossChange = (bossId, sel) => { if (!selectedChar) return setAllSelections((prev) => { const charSel = { ...(prev[selectedChar] || {}) } if (sel === null) { delete charSel[bossId] } else { charSel[bossId] = sel } return { ...prev, [selectedChar]: charSel } }) } const currentSelections = selectedChar ? (allSelections[selectedChar] || {}) : {} const currentSelectedCount = Object.values(currentSelections).filter(Boolean).length const isMaxReached = currentSelectedCount >= MAX_PER_CHARACTER return (