import React, { useState, useRef, useEffect } from 'react';
import { NavLink, useLocation, Link, useNavigate } from 'react-router-dom';
import { Home, Globe, Users, Menu, X, Gamepad2, Map, Shield, LogIn, LogOut, User, Settings, BarChart2, Package } from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import { useAuth } from '../contexts/AuthContext';
import { io } from 'socket.io-client';
// 사이드바 네비게이션 컴포넌트
const Sidebar = ({ isMobile = false }) => {
const [isOpen, setIsOpen] = useState(false);
const [showProfileMenu, setShowProfileMenu] = useState(false);
const [showLogoutDialog, setShowLogoutDialog] = useState(false);
const [minecraftLink, setMinecraftLink] = useState(null);
const [serverOnline, setServerOnline] = useState(false);
const [toast, setToast] = useState(null);
const location = useLocation();
const navigate = useNavigate();
const { isLoggedIn, isAdmin, user, logout, checkAuth } = useAuth();
const profileMenuRef = useRef(null);
const menuItems = [
{ path: '/', icon: Home, label: '홈' },
{ path: '/players', icon: Users, label: '플레이어', matchPaths: ['/players', '/player/'] },
{ path: '/worlds', icon: Globe, label: '월드 정보' },
{ path: '/worldmap', icon: Map, label: '월드맵' },
{ path: '/modpack', icon: Package, label: '모드팩' },
];
// 커스텀 활성 상태 확인 함수
const isMenuActive = (item) => {
if (item.matchPaths) {
return item.matchPaths.some(path => location.pathname.startsWith(path));
}
return location.pathname === item.path;
};
// 프로필 메뉴 외부 클릭 시 닫기
useEffect(() => {
const handleClickOutside = (e) => {
if (profileMenuRef.current && !profileMenuRef.current.contains(e.target)) {
setShowProfileMenu(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
// 연동 상태 확인
useEffect(() => {
if (!isLoggedIn) {
setMinecraftLink(null);
return;
}
const fetchLinkStatus = async () => {
try {
const token = localStorage.getItem('token');
const res = await fetch('/link/status', {
headers: { 'Authorization': `Bearer ${token}` }
});
const data = await res.json();
if (data.linked) {
setMinecraftLink(data);
}
} catch (error) {
// 에러 무시
}
};
fetchLinkStatus();
}, [isLoggedIn, user?.id]);
// 서버 상태 확인 및 닉네임 동기화 (socket.io)
// useRef로 최신 값을 참조하여 의존성 루프 방지
const userNameRef = useRef(user?.name);
const checkAuthRef = useRef(checkAuth);
const minecraftUuidRef = useRef(minecraftLink?.minecraftUuid);
const lastSyncedServerNameRef = useRef(null); // 마지막으로 확인한 서버 닉네임
useEffect(() => {
userNameRef.current = user?.name;
}, [user?.name]);
useEffect(() => {
checkAuthRef.current = checkAuth;
}, [checkAuth]);
useEffect(() => {
minecraftUuidRef.current = minecraftLink?.minecraftUuid;
}, [minecraftLink?.minecraftUuid]);
useEffect(() => {
const socket = io(window.location.origin, { path: '/socket.io' });
let isSyncing = false;
// 서버 상태만 업데이트
socket.on('status', (status) => {
setServerOnline(status?.online || false);
});
// 닉네임 동기화는 players 이벤트에서 처리 (displayName 포함)
socket.on('players', async (playersList) => {
const currentUuid = minecraftUuidRef.current;
if (!currentUuid || !playersList) return;
if (isSyncing) return;
const playerInGame = playersList.find(p => p.uuid === currentUuid);
// displayName이 있으면 displayName 사용, 없으면 name 사용
const serverName = playerInGame?.displayName || playerInGame?.name;
// 서버 닉네임이 변경되었고, 아직 동기화하지 않은 경우에만 실행
if (playerInGame && serverName &&
serverName !== lastSyncedServerNameRef.current &&
serverName !== userNameRef.current) {
isSyncing = true;
lastSyncedServerNameRef.current = serverName; // 동기화 시도한 이름 저장
try {
// /link/status 호출하여 DB 업데이트 트리거
const token = localStorage.getItem('token');
await fetch('/link/status', {
headers: { 'Authorization': `Bearer ${token}` }
});
// user 상태 갱신
await checkAuthRef.current();
} catch (error) {
// 에러 무시
} finally {
isSyncing = false;
}
}
});
return () => socket.disconnect();
}, []); // 의존성 없음 - ref로 최신 값 참조
// 토스트 자동 숨기기
useEffect(() => {
if (toast) {
const timer = setTimeout(() => setToast(null), 3000);
return () => clearTimeout(timer);
}
}, [toast]);
// 통계 페이지 이동 핸들러
const handleStatsClick = () => {
if (!serverOnline) {
setToast('서버가 오프라인입니다.');
setShowProfileMenu(false);
return;
}
setShowProfileMenu(false);
navigate(`/player/${minecraftLink.minecraftUuid}/stats`);
};
// 로그아웃 핸들러
const handleLogout = () => {
setShowProfileMenu(false);
setShowLogoutDialog(true);
};
const confirmLogout = () => {
logout();
setShowLogoutDialog(false);
navigate('/');
};
// 프로필 메뉴 컴포넌트
const ProfileMenu = () => (
{user?.name} {user?.email} 정말 로그아웃 하시겠습니까? { e.target.src = 'https://via.placeholder.com/48'; }}
/>
로그아웃
{user?.name}
{user?.email}
서버 대시보드