import { useState, useEffect } from 'react' import { Link } from 'react-router-dom' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { DndContext, DragOverlay, closestCenter, PointerSensor, KeyboardSensor, useSensor, useSensors, } from '@dnd-kit/core' import { SortableContext, sortableKeyboardCoordinates, useSortable, rectSortingStrategy, arrayMove, } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import { api } from '../../../api/client' import Tooltip from '../../../components/Tooltip' import { DIFFICULTIES, formatMeso, getDifficultyBadgeStyle } from './constants' function BossCardContent({ boss, dragging = false }) { return (
{/* 핸들 자리 */}
{boss.name}

{boss.name}

최대 {boss.max_party_size}인
{DIFFICULTIES.filter((d) => boss.difficulties?.some((bd) => bd.difficulty === d.key)).map((d) => { const bd = boss.difficulties.find((x) => x.difficulty === d.key) return ( {d.label} ) })}
) } function SortableBossCard({ boss }) { const { attributes, listeners, setNodeRef, transform, transition, isDragging, setActivatorNodeRef } = useSortable({ id: boss.id, transition: { duration: 200, easing: 'cubic-bezier(0.25, 0.1, 0.25, 1)' }, }) const style = { transform: CSS.Transform.toString(transform), transition, } return (
) } export default function BossList() { const queryClient = useQueryClient() const { data: bosses = [], isLoading } = useQuery({ queryKey: ['admin', 'boss-crystal', 'bosses'], queryFn: () => api('/api/admin/boss-crystal/bosses').catch(() => []), }) const [items, setItems] = useState([]) const [activeId, setActiveId] = useState(null) useEffect(() => { setItems(bosses) }, [bosses]) const sensors = useSensors( useSensor(PointerSensor, { activationConstraint: { distance: 5 } }), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }) ) const reorderMutation = useMutation({ mutationFn: (ids) => api('/api/admin/boss-crystal/bosses/reorder', { method: 'POST', body: { ids }, }), onError: (err) => { alert(err.message) queryClient.invalidateQueries({ queryKey: ['admin', 'boss-crystal', 'bosses'] }) }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['boss-crystal'] }) }, }) const handleDragEnd = (event) => { const { active, over } = event setActiveId(null) if (!over || active.id === over.id) return const oldIdx = items.findIndex((b) => b.id === active.id) const newIdx = items.findIndex((b) => b.id === over.id) const next = arrayMove(items, oldIdx, newIdx) setItems(next) reorderMutation.mutate(next.map((b) => b.id)) } const activeBoss = items.find((b) => b.id === activeId) return (

보스 결정 관리

보스 정보 및 난이도별 결정 가격을 관리합니다

+ 보스 추가
{isLoading ? (
{Array.from({ length: 4 }).map((_, i) => (
))}
) : items.length === 0 ? (
⚔️

등록된 보스가 없습니다

첫 보스 추가하기 →
) : ( setActiveId(e.active.id)} onDragCancel={() => setActiveId(null)} onDragEnd={handleDragEnd} > b.id)} strategy={rectSortingStrategy}>
{items.map((boss) => ( ))}
{activeBoss ? : null}
)}
) }