/** * 관리자 페이지 * - 탭 UI: 콘솔 / 플레이어 / 설정 */ import { useEffect, useState, useRef } from 'react'; import { useNavigate, useLocation, Link } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; import { Shield, ArrowLeft, Loader2, Terminal, Users, Settings, Send, Ban, UserX, Crown, Sun, Moon, Cloud, CloudRain, CloudLightning, ChevronDown, FileText, Download, Trash2, Check } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; // 더미 로그 데이터 const DUMMY_LOGS = [ { time: '15:01:23', type: 'info', message: '[Server] Starting minecraft server version 1.21.1' }, { time: '15:01:24', type: 'info', message: '[Server] Loading properties' }, { time: '15:01:25', type: 'info', message: '[Server] Preparing level "world"' }, { time: '15:01:28', type: 'info', message: '[Server] Done (3.245s)! For help, type "help"' }, { time: '15:05:12', type: 'info', message: '[Server] 비머[/127.0.0.1:54321] logged in' }, { time: '15:05:15', type: 'info', message: '[Server] 비머 joined the game' }, { time: '15:10:30', type: 'warning', message: '[Server] Can\'t keep up! Is the server overloaded?' }, { time: '15:15:00', type: 'info', message: '[Server] 비머부캐 joined the game' }, ]; // 더미 로그 파일 데이터 const DUMMY_LOG_FILES = [ { name: '2024-12-22.log', size: '2.4 MB', date: '2024-12-22' }, { name: '2024-12-21.log', size: '1.8 MB', date: '2024-12-21' }, { name: '2024-12-20.log', size: '3.1 MB', date: '2024-12-20' }, ]; // 더미 플레이어 데이터 const DUMMY_PLAYERS = [ { uuid: '1234-5678-9012-3456', name: '비머', isOnline: true, isOp: true }, { uuid: '2345-6789-0123-4567', name: '비머부캐', isOnline: true, isOp: false }, { uuid: '3456-7890-1234-5678', name: 'Steve', isOnline: false, isOp: false }, { uuid: '4567-8901-2345-6789', name: 'Alex', isOnline: false, isOp: false }, ]; // 더미 게임규칙 데이터 const DUMMY_GAMERULES = [ { name: 'keepInventory', value: false, label: '인벤토리 유지' }, { name: 'doDaylightCycle', value: true, label: '낮/밤 주기' }, { name: 'doMobSpawning', value: true, label: '몹 스폰' }, { name: 'doFireTick', value: true, label: '불 번짐' }, { name: 'mobGriefing', value: true, label: '몹 그리핑' }, { name: 'pvp', value: true, label: 'PvP' }, ]; export default function Admin({ isMobile = false }) { const { isLoggedIn, isAdmin, user, loading } = useAuth(); const navigate = useNavigate(); const location = useLocation(); const [toast, setToast] = useState(null); // 탭 상태 const [activeTab, setActiveTab] = useState('console'); // 콘솔 관련 상태 const [logs, setLogs] = useState(DUMMY_LOGS); const [command, setCommand] = useState(''); const [logFiles] = useState(DUMMY_LOG_FILES); const logEndRef = useRef(null); // 플레이어 관련 상태 const [players, setPlayers] = useState(DUMMY_PLAYERS); const [playerFilter, setPlayerFilter] = useState('all'); // all, online, offline, banned const [selectedPlayer, setSelectedPlayer] = useState(null); const [showPlayerDialog, setShowPlayerDialog] = useState(false); const [dialogAction, setDialogAction] = useState(null); // kick, ban, op const [actionReason, setActionReason] = useState(''); // 설정 관련 상태 const [gamerules, setGamerules] = useState(DUMMY_GAMERULES); const [difficulty, setDifficulty] = useState('normal'); const [timeOfDay, setTimeOfDay] = useState('day'); const [weather, setWeather] = useState('clear'); // 권한 확인 useEffect(() => { if (!loading) { if (!isLoggedIn) { navigate('/login', { state: { from: location.pathname } }); } else if (!isAdmin) { setToast('관리자 권한이 필요합니다.'); setTimeout(() => navigate('/'), 1500); } } }, [isLoggedIn, isAdmin, loading, navigate, location.pathname]); // 토스트 자동 숨기기 useEffect(() => { if (toast) { const timer = setTimeout(() => setToast(null), 3000); return () => clearTimeout(timer); } }, [toast]); // 로그 스크롤 useEffect(() => { logEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [logs]); // 명령어 실행 (더미) const handleCommand = () => { if (!command.trim()) return; const newLogs = [ { time: new Date().toLocaleTimeString('ko-KR', { hour12: false }), type: 'command', message: `> ${command}` }, { time: new Date().toLocaleTimeString('ko-KR', { hour12: false }), type: 'info', message: `[Server] 명령어 실행됨: ${command}` } ]; setLogs(prev => [...prev, ...newLogs]); setCommand(''); setToast('명령어가 실행되었습니다.'); }; // 플레이어 액션 핸들러 const handlePlayerAction = () => { if (!selectedPlayer || !dialogAction) return; let message = ''; switch (dialogAction) { case 'kick': message = `${selectedPlayer.name}님을 추방했습니다.`; break; case 'ban': message = `${selectedPlayer.name}님을 차단했습니다.`; break; case 'op': const isOp = players.find(p => p.uuid === selectedPlayer.uuid)?.isOp; setPlayers(prev => prev.map(p => p.uuid === selectedPlayer.uuid ? { ...p, isOp: !isOp } : p )); message = isOp ? `${selectedPlayer.name}님의 OP를 해제했습니다.` : `${selectedPlayer.name}님에게 OP를 부여했습니다.`; break; } setShowPlayerDialog(false); setSelectedPlayer(null); setDialogAction(null); setActionReason(''); setToast(message); }; // 게임규칙 토글 const toggleGamerule = (name) => { setGamerules(prev => prev.map(rule => rule.name === name ? { ...rule, value: !rule.value } : rule )); setToast('게임규칙이 변경되었습니다.'); }; // 로그 색상 const getLogColor = (type) => { switch (type) { case 'error': return 'text-red-400'; case 'warning': return 'text-yellow-400'; case 'command': return 'text-mc-green'; default: return 'text-zinc-300'; } }; // 필터된 플레이어 const filteredPlayers = players.filter(p => { if (playerFilter === 'online') return p.isOnline; if (playerFilter === 'offline') return !p.isOnline; return true; }); if (loading) { return (
); } if (!isLoggedIn || !isAdmin) { return ( <> {toast && ( {toast} )}
); } // 탭 설정 const tabs = [ { id: 'console', label: '콘솔', icon: Terminal }, { id: 'players', label: '플레이어', icon: Users }, { id: 'settings', label: '설정', icon: Settings }, ]; return (
{/* 토스트 */} {toast && ( {toast} )} {/* 모바일용 헤더 */} {isMobile && (

관리자

)}
{/* 데스크탑용 타이틀 */} {!isMobile && (

관리자 페이지

서버 관리 및 설정

)} {/* 탭 네비게이션 */}
{tabs.map(tab => ( ))}
{/* 탭 콘텐츠 */} {/* 콘솔 탭 */} {activeTab === 'console' && ( {/* 로그 영역 */}
{logs.map((log, index) => (
[{log.time}] {log.message}
))}
{/* 명령어 입력 */}
{'>'} setCommand(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleCommand()} placeholder="명령어 입력..." className="flex-1 bg-transparent py-3 text-white placeholder-zinc-500 focus:outline-none font-mono" />
{/* 로그 파일 목록 */}

로그 파일

{logFiles.map((file, index) => (

{file.name}

{file.size}

))}
)} {/* 플레이어 탭 */} {activeTab === 'players' && ( {/* 필터 */}
{[ { id: 'all', label: '전체' }, { id: 'online', label: '온라인' }, { id: 'offline', label: '오프라인' }, ].map(filter => ( ))}
{/* 플레이어 그리드 */}
{filteredPlayers.map((player) => (
{/* OP 뱃지 */} {player.isOp && (
)} {/* 온/오프라인 표시 */}
{/* 전신 아바타 */} {player.name}

{player.name}

{/* 액션 버튼 */}
{player.isOnline && ( )}
))}
)} {/* 설정 탭 */} {activeTab === 'settings' && ( {/* 게임규칙 */}

🎮 게임규칙

{gamerules.map(rule => ( ))}
{/* 난이도 */}

⚔️ 난이도

{[ { id: 'peaceful', label: '평화로움' }, { id: 'easy', label: '쉬움' }, { id: 'normal', label: '보통' }, { id: 'hard', label: '어려움' }, ].map(d => ( ))}
{/* 시간 */}

🕐 시간

{[ { id: 'day', label: '아침', icon: Sun }, { id: 'noon', label: '낮', icon: Sun }, { id: 'night', label: '밤', icon: Moon }, ].map(t => ( ))}
{/* 날씨 */}

🌤️ 날씨

{[ { id: 'clear', label: '맑음', icon: Sun }, { id: 'rain', label: '비', icon: CloudRain }, { id: 'thunder', label: '천둥', icon: CloudLightning }, ].map(w => ( ))}
)}
{/* 플레이어 액션 다이얼로그 */} {showPlayerDialog && selectedPlayer && ( setShowPlayerDialog(false)} > e.stopPropagation()} >

{dialogAction === 'kick' && <> 플레이어 킥} {dialogAction === 'ban' && <> 플레이어 밴} {dialogAction === 'op' && <> OP {players.find(p => p.uuid === selectedPlayer.uuid)?.isOp ? '해제' : '부여'}}

{selectedPlayer.name}

{selectedPlayer.name}

{selectedPlayer.uuid}

{(dialogAction === 'kick' || dialogAction === 'ban') && ( setActionReason(e.target.value)} placeholder="사유 (선택)" className="w-full bg-zinc-800 rounded-xl p-3 text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-mc-green/50 mb-4" /> )}
)}
); }