Files
GSM/gsm-frontend/src/components/ServerCard.jsx
Alexander Zielonka ff6adb093b Add Factorio World Management feature to GSM
- Add gsm-frontend to repository (React + Vite + TailwindCSS)
- New "Worlds" tab for Factorio server with:
  - List saved worlds with Start/Delete actions
  - Create new world with full map generation parameters
  - Preset selection (Default, Rich Resources, Rail World, etc.)
  - Save custom configurations as templates
- Show which save will be loaded in Overview tab
- Lock world management while server is running
- Backend changes deployed to server separately

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

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

191 lines
6.1 KiB
JavaScript

const serverInfo = {
minecraft: {
address: 'minecraft.dimension47.de',
logo: '/minecraft.png',
links: [
{ label: 'ATM10 Modpack', url: 'https://www.curseforge.com/minecraft/modpacks/all-the-mods-10' }
]
},
factorio: {
hint: 'Serverpasswort: affe',
address: 'factorio.dimension47.de',
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/' }
]
}
}
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
return null
}
export default function ServerCard({ server, onClick, isAuthenticated }) {
const info = getServerInfo(server.name)
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...' }
default:
return { class: 'badge badge-destructive', text: 'Offline' }
}
}
const statusBadge = getStatusBadge()
return (
<div
className="card card-clickable p-5"
onClick={onClick}
>
{/* Header */}
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
{info && info.logo && <img src={info.logo} alt="" className="h-8 w-8 object-contain" />}
<h3 className="text-lg font-semibold text-white">{server.name}</h3>
</div>
<span className={statusBadge.class}>
{statusBadge.text}
</span>
</div>
{/* Server Address & Links */}
{info && (
<div className="mb-4 flex items-center gap-3 text-sm">
<code className="text-neutral-400 bg-neutral-800 px-2 py-0.5 rounded">
{info.address}
</code>
{info.links.map((link, i) => (
<a
key={i}
href={link.url}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
className="text-blue-400 hover:text-blue-300 hover:underline"
>
{link.label}
</a>
))}
</div>
)}
{/* Whitelist notice for Minecraft - only for authenticated users */}
{isAuthenticated && server.type === 'minecraft' && (
<div className="mb-4 text-xs text-neutral-500">
Whitelist erforderlich - im Whitelist-Tab freischalten
</div>
)}
{/* Factorio notice - only for authenticated users */}
{isAuthenticated && server.type === 'factorio' && (
<div className="mb-4 text-xs text-neutral-500">
Serverpasswort: affe
</div>
)}
{/* V Rising notice - only for authenticated users */}
{isAuthenticated && server.type === 'vrising' && (
<div className="mb-4 text-xs text-neutral-500">
In der Serverliste suchen - Passwort: affe
</div>
)}
{/* Metrics */}
<div className="space-y-3">
{/* CPU */}
<div>
<div className="flex justify-between text-sm mb-1">
<span className="text-neutral-400">CPU</span>
<span className="text-white">{server.metrics.cpu.toFixed(1)}%</span>
</div>
<div className="progress">
<div
className={'progress-bar ' + getProgressColor(cpuPercent)}
style={{ width: cpuPercent + '%' }}
/>
</div>
</div>
{/* RAM */}
<div>
<div className="flex justify-between text-sm mb-1">
<span className="text-neutral-400">Memory</span>
<span className="text-white">
{server.metrics.memoryUsed?.toFixed(1) || 0} / {server.metrics.memoryTotal?.toFixed(1) || 0} {server.metrics.memoryUnit}
</span>
</div>
<div className="progress">
<div
className={'progress-bar ' + getProgressColor(memPercent)}
style={{ width: memPercent + '%' }}
/>
</div>
</div>
</div>
{/* Footer Stats */}
<div className="flex items-center justify-between mt-4 pt-4 border-t border-neutral-800 text-sm">
<div className="text-neutral-400">
<span className="text-white font-medium">{server.players.online}</span>
{server.players.max ? ' / ' + server.players.max : ''} players
</div>
{server.running && (
<div className="text-neutral-400">
Uptime: <span className="text-white">{formatUptime(server.metrics.uptime)}</span>
</div>
)}
</div>
{/* Players List */}
{server.players?.list?.length > 0 && (
<div className="mt-3 pt-3 border-t border-neutral-800">
<div className="flex flex-wrap gap-1.5">
{server.players.list.map((player, i) => (
<span key={i} className="badge badge-secondary">
{player}
</span>
))}
</div>
</div>
)}
</div>
)
}