maplestory/frontend/src/features/admin/AdminLayout.jsx
caadiq 85b9a6b6d2 API 키 로그인 + 캐릭터 드롭다운 + 관리자 네비게이션
- API 키 로그인 다이얼로그 + 헤더 로그인 버튼
- /api/character/list 프록시 엔드포인트 (월드 아이콘 매핑 포함)
- 캐릭터 입력 포커스 시 드롭다운 (월드 아이콘, 레벨 정렬, 기존 캐릭 제외, 페이드 애니메이션)
- 관리자 인증을 API 키로 통합 (URL ?key= 파라미터 폐기)
- 헤더에 관리자 링크 버튼 / 홈 링크 버튼 (경로별 배타적 표시)
- 관리자 페이지에서 타이틀 우측에 "관리자" 텍스트
- 이미지 관리 페이지 테마 토큰화

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 10:54:12 +09:00

41 lines
1.2 KiB
JavaScript

import { Outlet, Navigate } from 'react-router-dom'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { api } from '../../api/client'
import { useAuthStore } from '../../stores/auth'
export default function AdminLayout() {
const queryClient = useQueryClient()
const apiKey = useAuthStore((s) => s.apiKey)
const clearApiKey = useAuthStore((s) => s.clearApiKey)
const { data, isLoading } = useQuery({
queryKey: ['admin', 'verify', apiKey],
queryFn: async () => {
await api('/api/admin/verify', { method: 'POST', body: { key: apiKey } })
return true
},
enabled: !!apiKey,
retry: false,
staleTime: Infinity,
})
const verified = data === true
const handleLogout = () => {
clearApiKey()
queryClient.removeQueries({ queryKey: ['admin'] })
window.location.href = '/'
}
if (apiKey && isLoading) {
return (
<div className="flex items-center justify-center pt-20">
<div className="w-6 h-6 border-2 border-t-transparent rounded-full animate-spin" style={{ borderColor: 'var(--accent)', borderTopColor: 'transparent' }} />
</div>
)
}
if (!verified) return <Navigate to="/" replace />
return <Outlet context={{ handleLogout }} />
}