Files
GSM/temp/Dashboard_current.jsx
Alexander Zielonka 2b1fbb9f02 Initial commit: Homelab documentation
- infrastructure.md: Network topology, server overview, credentials
- gsm.md: Gameserver Monitor detailed documentation
- todo.md: Project roadmap and completed tasks
- CLAUDE.md: AI assistant context
- temp/: Frontend component backups

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-05 06:16:05 +01:00

197 lines
6.7 KiB
JavaScript

import { useState, useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { getServers } from '../api'
import { useUser } from '../context/UserContext'
import ServerCard from '../components/ServerCard'
import SettingsModal from '../components/SettingsModal'
import UserManagement from '../components/UserManagement'
export default function Dashboard({ onLogout }) {
const navigate = useNavigate()
const { user, token, loading: userLoading, isSuperadmin, role } = useUser()
const [servers, setServers] = useState([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState('')
const [showSettings, setShowSettings] = useState(false)
const [showUserMgmt, setShowUserMgmt] = useState(false)
const [currentTime, setCurrentTime] = useState(new Date())
const fetchServers = async () => {
try {
const data = await getServers(token)
setServers(data)
setError('')
} catch (err) {
setError('CONNECTION_FAILED: Unable to reach server cluster')
if (err.message.includes('401') || err.message.includes('403')) {
onLogout()
}
} finally {
setLoading(false)
}
}
useEffect(() => {
if (!userLoading) {
fetchServers()
const interval = setInterval(fetchServers, 10000)
return () => clearInterval(interval)
}
}, [token, userLoading])
useEffect(() => {
const timer = setInterval(() => setCurrentTime(new Date()), 1000)
return () => clearInterval(timer)
}, [])
const roleLabels = {
user: 'VIEWER',
moderator: 'OPERATOR',
superadmin: 'SYSADMIN'
}
if (userLoading) {
return (
<div className="min-h-screen bg-[#0d0d0d] flex items-center justify-center">
<div className="text-[#00ff41] glow-green text-xl">INITIALIZING SYSTEM...</div>
</div>
)
}
const onlineCount = servers.filter(s => s.running).length
const totalPlayers = servers.reduce((sum, s) => sum + (s.players?.online || 0), 0)
return (
<div className="min-h-screen bg-[#0d0d0d] hex-grid relative overflow-x-hidden">
{/* Matrix rain overlay */}
<div className="matrix-bg" />
{/* Header */}
<header className="relative z-10 border-b border-[#00ff41]/30 bg-black/80 backdrop-blur-sm">
<div className="container-main py-4">
<div className="flex justify-between items-center">
<div className="flex items-center gap-6">
<h1 className="text-xl md:text-2xl font-bold text-[#00ff41] glow-green font-mono">
GAMESERVER_MONITOR
</h1>
<div className="hidden md:flex items-center gap-4 text-[#00ff41]/60 text-sm font-mono">
<span className="flex items-center gap-2">
<span className="w-2 h-2 rounded-full status-online"></span>
{onlineCount}/{servers.length} NODES
</span>
<span>|</span>
<span>{totalPlayers} USERS_CONNECTED</span>
</div>
</div>
<div className="flex items-center gap-4">
<div className="hidden sm:block text-[#00ff41]/60 font-mono text-sm">
{currentTime.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', second: '2-digit' })}
</div>
<div className="flex items-center gap-2">
<div className="text-right hidden sm:block">
<div className="text-[#00ff41] font-mono text-sm">{user?.username?.toUpperCase()}</div>
<div className="text-[#00ff41]/50 text-xs">[{roleLabels[role]}]</div>
</div>
{isSuperadmin && (
<button
onClick={() => setShowUserMgmt(true)}
className="btn-matrix px-3 py-1.5 text-sm font-mono"
>
USERS
</button>
)}
<button
onClick={() => setShowSettings(true)}
className="btn-matrix px-3 py-1.5 text-sm font-mono"
>
SETTINGS
</button>
<button
onClick={onLogout}
className="btn-matrix px-3 py-1.5 text-sm font-mono"
>
DISCONNECT
</button>
</div>
</div>
</div>
</div>
</header>
<main className="relative z-10 container-main py-8">
{/* System Status Bar */}
<div className="mb-8 p-4 border border-[#00ff41]/30 bg-black/60 backdrop-blur-sm rounded font-mono text-sm">
<div className="flex items-center gap-2 text-[#00ff41]/80">
<span className="text-[#00ff41]">&gt;</span>
<span>SYSTEM_STATUS: {error ? 'ERROR' : 'OPERATIONAL'}</span>
</div>
{error && (
<div className="mt-2 text-red-500 flex items-center gap-2">
<span className="animate-pulse">!</span>
{error}
</div>
)}
</div>
{loading ? (
<div className="text-center py-12">
<div className="text-[#00ff41] glow-green text-xl animate-pulse">
LOADING_NODES...
</div>
<div className="mt-4 flex justify-center gap-1">
{[...Array(5)].map((_, i) => (
<div
key={i}
className="w-3 h-8 bg-[#00ff41]/30"
style={{
animation: `pulse 1s ease-in-out ${i * 0.1}s infinite`
}}
/>
))}
</div>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{servers.map((server, index) => (
<div
key={server.id}
className="opacity-0 fade-in-up"
style={{ animationDelay: `${index * 0.1}s` }}
>
<ServerCard
server={server}
onClick={() => navigate(`/server/${server.id}`)}
/>
</div>
))}
</div>
)}
{/* Footer Stats */}
<div className="mt-12 pt-6 border-t border-[#00ff41]/20">
<div className="flex flex-wrap justify-center gap-6 text-[#00ff41]/50 font-mono text-xs">
<span>REFRESH_RATE: 10s</span>
<span>PROTOCOL: RCON/SSH</span>
<span>METRICS: PROMETHEUS</span>
<span>BUILD: v2.0.0-matrix</span>
</div>
</div>
</main>
{/* Modals */}
{showSettings && (
<SettingsModal onClose={() => setShowSettings(false)} />
)}
{showUserMgmt && (
<UserManagement onClose={() => setShowUserMgmt(false)} />
)}
</div>
)
}