Files
corrosion-admin-panel/frontend/src/views/admin/DashboardView.vue
Vantz Stockwell 0fdbad0d07
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
fix: Resolve TypeScript build errors blocking Docker nginx build
- DashboardView: Add non-null assertion on upcoming[0] (guarded by length check)
- EarlyAccessView: Add missing `computed` import from Vue

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 14:21:58 -05:00

156 lines
5.5 KiB
Vue

<script setup lang="ts">
import { computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useServerStore } from '@/stores/server'
import { useWipeStore } from '@/stores/wipe'
const router = useRouter()
const auth = useAuthStore()
const server = useServerStore()
const wipe = useWipeStore()
onMounted(async () => {
server.fetchServer()
try {
await wipe.fetchSchedules()
} catch {
// Non-critical — dashboard still loads without wipe data
}
})
const nextWipeDate = computed<string>(() => {
const upcoming = wipe.schedules
.filter(s => s.is_active && s.next_scheduled_run)
.map(s => new Date(s.next_scheduled_run!))
.sort((a, b) => a.getTime() - b.getTime())
if (upcoming.length === 0) return 'Not Scheduled'
return upcoming[0]!.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
})
})
function statusColor(status: string | undefined): string {
switch (status) {
case 'connected': return 'bg-green-500'
case 'degraded': return 'bg-yellow-500'
default: return 'bg-red-500'
}
}
function statusLabel(status: string | undefined): string {
switch (status) {
case 'connected': return 'Online'
case 'degraded': return 'Degraded'
default: return 'Offline'
}
}
function formatUptime(seconds: number | undefined): string {
if (!seconds) return '\u2014'
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
if (h > 0) return `${h}h ${m}m`
return `${m}m`
}
</script>
<template>
<div class="p-6 space-y-8">
<!-- Welcome header -->
<div>
<h1 class="text-2xl font-bold text-neutral-100">
Welcome back, {{ auth.user?.username }}
</h1>
<p class="text-sm text-neutral-500 mt-1">Here's what's happening with your server.</p>
</div>
<!-- Stat cards grid -->
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4">
<!-- Server Status -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<p class="text-sm text-neutral-400 mb-2">Server Status</p>
<div class="flex items-center gap-2">
<span class="h-2.5 w-2.5 rounded-full" :class="statusColor(server.connection?.connection_status)"></span>
<span class="text-2xl font-bold text-neutral-100">{{ statusLabel(server.connection?.connection_status) }}</span>
</div>
</div>
<!-- Players Online -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<p class="text-sm text-neutral-400 mb-2">Players Online</p>
<p class="text-2xl font-bold text-neutral-100">
{{ server.stats?.player_count ?? 0 }}/{{ server.stats?.max_players ?? server.config?.max_players ?? 0 }}
</p>
</div>
<!-- Next Wipe -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<p class="text-sm text-neutral-400 mb-2">Next Wipe</p>
<p class="text-2xl font-bold text-neutral-100">{{ nextWipeDate }}</p>
</div>
<!-- Uptime -->
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<p class="text-sm text-neutral-400 mb-2">Uptime</p>
<p class="text-2xl font-bold text-neutral-100">{{ formatUptime(server.stats?.uptime_seconds) }}</p>
</div>
</div>
<!-- Quick Actions -->
<div>
<h2 class="text-lg font-semibold text-neutral-200 mb-4">Quick Actions</h2>
<div class="flex flex-wrap gap-3">
<button
:disabled="server.connection?.connection_status === 'connected'"
@click="server.startServer()"
class="px-4 py-2.5 bg-green-600/20 hover:bg-green-600/30 disabled:opacity-30 disabled:cursor-not-allowed text-green-400 border border-green-600/30 rounded-lg text-sm font-medium transition-colors"
>
Start Server
</button>
<button
:disabled="server.connection?.connection_status !== 'connected'"
@click="server.stopServer()"
class="px-4 py-2.5 bg-red-600/20 hover:bg-red-600/30 disabled:opacity-30 disabled:cursor-not-allowed text-red-400 border border-red-600/30 rounded-lg text-sm font-medium transition-colors"
>
Stop Server
</button>
<button
@click="router.push('/wipes')"
class="px-4 py-2.5 bg-neutral-800 hover:bg-neutral-700 text-neutral-300 rounded-lg text-sm font-medium transition-colors"
>
Trigger Wipe
</button>
</div>
</div>
<!-- Server Info (if configured) -->
<div v-if="server.config" class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
<h2 class="text-lg font-semibold text-neutral-200 mb-3">Server Configuration</h2>
<div class="grid grid-cols-2 lg:grid-cols-4 gap-4 text-sm">
<div>
<span class="text-neutral-500">Server Name</span>
<p class="text-neutral-200 mt-0.5">{{ server.config.server_name || 'Not set' }}</p>
</div>
<div>
<span class="text-neutral-500">Max Players</span>
<p class="text-neutral-200 mt-0.5">{{ server.config.max_players ?? 'Not set' }}</p>
</div>
<div>
<span class="text-neutral-500">World Size</span>
<p class="text-neutral-200 mt-0.5">{{ server.config.world_size ?? 'Not set' }}</p>
</div>
<div>
<span class="text-neutral-500">Current Seed</span>
<p class="text-neutral-200 mt-0.5">{{ server.config.current_seed ?? 'Not set' }}</p>
</div>
</div>
</div>
</div>
</template>