import { useState } from 'react' import { serverAction } from '../api' import ConfirmModal from './ConfirmModal' const serverInfo = { minecraft: { address: 'minecraft.zeasy.dev', logo: '/minecraft.png', links: [ { label: 'ATM10 Modpack', url: 'https://www.curseforge.com/minecraft/modpacks/all-the-mods-10' } ] }, factorio: { hint: 'Serverpasswort: affe', address: 'factorio.zeasy.dev', logo: '/factorio.png', links: [ { label: 'Steam', url: 'https://store.steampowered.com/app/427520/Factorio/' } ] }, vrising: { address: 'Zeasy Software Vampire', logo: '/vrising.png', links: [ { label: 'Steam', url: 'https://store.steampowered.com/app/1604030/V_Rising/' } ] }, zomboid: { hint: 'Version 42.13.1', address: 'pz.zeasy.dev:16261', logo: '/zomboid.png', links: [ { label: 'Steam', url: 'https://store.steampowered.com/app/108600/Project_Zomboid/' } ] }, palworld: { address: 'palworld.zeasy.dev:8211', logo: '/palworld.png', links: [ { label: 'Steam', url: 'https://store.steampowered.com/app/1623730/Palworld/' } ] }, terraria: { address: 'terraria.zeasy.dev:7777', logo: '/terraria.png', links: [ { label: 'Steam', url: 'https://store.steampowered.com/app/105600/Terraria/' } ] }, openttd: { address: 'openttd.zeasy.dev:3979', logo: '/openttd.png', links: [ { label: 'Steam', url: 'https://store.steampowered.com/app/1536610/OpenTTD/' } ] }, hytale: { address: 'hytale.zeasy.dev', logo: '/hytale.png', links: [ { label: 'Website', url: 'https://hytale.com/' } ] } } const getServerInfo = (serverName) => { const name = serverName.toLowerCase() if (name.includes('minecraft') || name.includes('all the mods')) return serverInfo.minecraft if (name.includes('factorio')) return serverInfo.factorio if (name.includes('vrising') || name.includes('v rising')) return serverInfo.vrising if (name.includes('zomboid')) return serverInfo.zomboid if (name.includes('palworld')) return serverInfo.palworld if (name.includes('terraria')) return serverInfo.terraria if (name.includes('openttd')) return serverInfo.openttd if (name.includes('hytale')) return serverInfo.hytale return null } export default function ServerCard({ server, onClick, isAuthenticated, isGuest, displaySettings, isModerator, token, onServerAction }) { const defaultInfo = getServerInfo(server.name) const [confirmAction, setConfirmAction] = useState(null) const [actionLoading, setActionLoading] = useState(false) // Merge default info with database display settings (database takes priority) const info = defaultInfo ? { ...defaultInfo, address: displaySettings?.address || defaultInfo.address, hint: displaySettings?.hint || defaultInfo.hint } : (displaySettings ? { address: displaySettings.address, hint: displaySettings.hint, logo: null, links: [] } : null) const formatUptime = (seconds) => { const hours = Math.floor(seconds / 3600) if (hours > 24) { const days = Math.floor(hours / 24) return days + 'd ' + (hours % 24) + 'h' } const minutes = Math.floor((seconds % 3600) / 60) return hours + 'h ' + minutes + 'm' } const cpuPercent = Math.min(server.metrics.cpu, 100) const memPercent = Math.min(server.metrics.memory, 100) const getProgressColor = (percent) => { if (percent > 80) return 'progress-bar-danger' if (percent > 60) return 'progress-bar-warning' return 'progress-bar-success' } const getStatusBadge = () => { const status = server.status || (server.running ? 'online' : 'offline') switch (status) { case 'online': return { class: 'badge badge-success', text: 'Online' } case 'starting': return { class: 'badge badge-warning', text: 'Starting...' } case 'stopping': return { class: 'badge badge-warning', text: 'Stopping...' } case 'unreachable': return { class: 'badge bg-neutral-600 text-neutral-400', text: 'Nicht erreichbar' } default: return { class: 'badge badge-destructive', text: 'Offline' } } } const statusBadge = getStatusBadge() const isUnreachable = server.status === 'unreachable' const isClickable = !isUnreachable && !isGuest && onClick const handleAction = async (action) => { setActionLoading(true) try { await serverAction(token, server.id, action) if (onServerAction) onServerAction() } catch (err) { console.error(err) } finally { setActionLoading(false) setConfirmAction(null) } } const handleActionClick = (action, e) => { e.stopPropagation() if (action === 'start') { handleAction(action) } else { setConfirmAction(action) } } const isActionDisabled = actionLoading || server.status === 'starting' || server.status === 'stopping' return (
{/* Header */}
{info && info.logo && }

{server.name}

{statusBadge.text}
{/* Server Address & Links */} {info && (
{info.address} {info.links.map((link, i) => ( e.stopPropagation()} className="text-blue-400 hover:text-blue-300 hover:underline" > {link.label} ))}
)} {/* Server hint - only for authenticated users */} {isAuthenticated && info?.hint && (
{info.hint}
)} {/* Whitelist notice for Minecraft - only if no custom hint is set */} {isAuthenticated && server.type === 'minecraft' && !displaySettings?.hint && (
Whitelist erforderlich - im Whitelist-Tab freischalten
)} {/* Metrics */}
{/* CPU */}
CPU {server.metrics.cpu.toFixed(1)}%
{/* RAM */}
Memory {server.metrics.memoryUsed?.toFixed(1) || 0} / {server.metrics.memoryTotal?.toFixed(1) || 0} {server.metrics.memoryUnit}
{/* Footer Stats */}
{server.players.online} {server.players.max ? ' / ' + server.players.max : ''} Spieler
{server.running && server.autoShutdown?.enabled && server.autoShutdown?.emptySinceMinutes !== null && (
Shutdown in {server.autoShutdown.timeoutMinutes - server.autoShutdown.emptySinceMinutes}m
)}
{server.running && (
Laufzeit: {formatUptime(server.metrics.uptime)}
)}
{/* Players List */} {server.players?.list?.length > 0 && (
{server.players.list.map((player, i) => ( {player} ))}
)} {/* Server Controls - only for moderators */} {isModerator && !isUnreachable && (
{(server.status === 'online' || (server.running && server.status !== 'starting' && server.status !== 'stopping')) ? ( <> ) : ( )}
)} {/* Confirmation Modal */} {confirmAction && ( handleAction(confirmAction)} onCancel={() => setConfirmAction(null)} /> )}
) }