import { useState, useEffect } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { motion } from 'framer-motion'; import { Disc3, Calendar, Users, LogOut, Home, ChevronRight } from 'lucide-react'; // 슬롯머신 스타일 롤링 숫자 컴포넌트 (아래에서 위로) function AnimatedNumber({ value }) { const digits = String(value).split(''); return ( {digits.map((digit, i) => ( {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(n => ( {n} ))} ))} ); } function AdminDashboard() { const navigate = useNavigate(); const [user, setUser] = useState(null); const [stats, setStats] = useState({ albums: 0, photos: 0, schedules: 0, members: 0 }); useEffect(() => { // 로그인 상태 확인 const token = localStorage.getItem('adminToken'); const userData = localStorage.getItem('adminUser'); if (!token || !userData) { navigate('/admin'); return; } setUser(JSON.parse(userData)); // 토큰 유효성 검증 fetch('/api/admin/verify', { headers: { Authorization: `Bearer ${token}` } }) .then(res => { if (!res.ok) throw new Error('Invalid token'); return res.json(); }) .catch(() => { localStorage.removeItem('adminToken'); localStorage.removeItem('adminUser'); navigate('/admin'); }); // 통계 데이터 가져오기 fetchStats(); }, [navigate]); const fetchStats = async () => { // 각 통계를 개별적으로 가져와서 하나가 실패해도 다른 것은 표시 try { const membersRes = await fetch('/api/members'); if (membersRes.ok) { const members = await membersRes.json(); setStats(prev => ({ ...prev, members: members.filter(m => !m.is_former).length })); } } catch (e) { console.error('멤버 통계 오류:', e); } try { const albumsRes = await fetch('/api/albums'); if (albumsRes.ok) { const albums = await albumsRes.json(); setStats(prev => ({ ...prev, albums: albums.length })); // 사진 수 계산 let totalPhotos = 0; for (const album of albums) { try { const detailRes = await fetch(`/api/albums/${album.id}`); if (detailRes.ok) { const detail = await detailRes.json(); if (detail.conceptPhotos) { Object.values(detail.conceptPhotos).forEach(photos => { totalPhotos += photos.length; }); } if (detail.teasers) { totalPhotos += detail.teasers.length; } } } catch (e) { /* 개별 앨범 오류 무시 */ } } setStats(prev => ({ ...prev, photos: totalPhotos })); } } catch (e) { console.error('앨범 통계 오류:', e); } try { const schedulesRes = await fetch('/api/schedules'); if (schedulesRes.ok) { const schedules = await schedulesRes.json(); setStats(prev => ({ ...prev, schedules: Array.isArray(schedules) ? schedules.length : 0 })); } } catch (e) { console.error('일정 통계 오류:', e); } }; const handleLogout = () => { localStorage.removeItem('adminToken'); localStorage.removeItem('adminUser'); navigate('/admin'); }; // 메뉴 아이템 const menuItems = [ { icon: Users, label: '멤버 관리', description: '멤버 정보 및 프로필 관리', path: '/admin/members', color: 'bg-primary' }, { icon: Disc3, label: '앨범 관리', description: '앨범, 트랙, 사진 업로드 및 관리', path: '/admin/albums', color: 'bg-purple-500' }, { icon: Calendar, label: '일정 관리', description: '일정 추가 및 관리', path: '/admin/schedule', color: 'bg-blue-500' }, ]; return (
{/* 헤더 */}
fromis_9 Admin
안녕하세요, {user?.username}
{/* 메인 콘텐츠 */}
{/* 브레드크럼 */}
관리자 대시보드
{/* 타이틀 */}

관리자 대시보드

fromis_9 팬사이트를 관리하세요

{/* 메뉴 그리드 */}
{menuItems.map((item, index) => (

{item.label}

{item.description}

))}
{/* 빠른 통계 */}

빠른 통계

멤버

총 앨범

총 사진

총 일정

); } export default AdminDashboard;