feat: Implement 6 views + updated hero — Server, Chat, Team, Notifications, Settings, Setup Wizard
Server: Connection status, start/stop/restart controls, config editor with edit mode, automation toggles (crash recovery, force wipe, auto-update). Chat Log: Message feed with channel filter (global/team/server), search, flag/unflag per message, timestamped entries with channel badges. Team: Member table with role badges, invite form with role select, pending/active status, remove action. Notifications: Discord webhook, Pushbullet, email toggle cards. 6 event triggers (wipe start/complete/fail, crash, offline, purchase). Settings: 3-tab layout (Account, License, Domain). Account editing, license info display, subdomain + custom domain config with CNAME hint. Setup Wizard: 3-step flow (Configure → Install Agent → Done). Connection type radio cards, RCON/game port config, companion agent install instructions with license key pre-filled. Also swaps hero graphic to corrected version (two-column Control vs Infrastructure layout per brand brief). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,296 @@
|
||||
<script setup lang="ts">
|
||||
// TODO: Implement server configuration and start/stop/restart controls
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import {
|
||||
Server,
|
||||
Wifi,
|
||||
WifiOff,
|
||||
Play,
|
||||
Square,
|
||||
RotateCcw,
|
||||
Save,
|
||||
Loader2,
|
||||
} from 'lucide-vue-next'
|
||||
|
||||
const server = useServerStore()
|
||||
|
||||
const editMode = ref(false)
|
||||
const saving = ref(false)
|
||||
const actionLoading = ref<string | null>(null)
|
||||
|
||||
const form = ref({
|
||||
server_name: '',
|
||||
max_players: 0,
|
||||
world_size: 0,
|
||||
current_seed: 0,
|
||||
})
|
||||
|
||||
function loadFormFromConfig() {
|
||||
if (server.config) {
|
||||
form.value = {
|
||||
server_name: server.config.server_name || '',
|
||||
max_players: server.config.max_players ?? 100,
|
||||
world_size: server.config.world_size ?? 4000,
|
||||
current_seed: server.config.current_seed ?? 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveConfig() {
|
||||
saving.value = true
|
||||
try {
|
||||
await server.updateConfig(form.value)
|
||||
editMode.value = false
|
||||
} catch {
|
||||
// Handle error
|
||||
} finally {
|
||||
saving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function serverAction(action: 'start' | 'stop' | 'restart') {
|
||||
actionLoading.value = action
|
||||
try {
|
||||
if (action === 'start') await server.startServer()
|
||||
else if (action === 'stop') await server.stopServer()
|
||||
else await server.restartServer()
|
||||
await server.fetchServer()
|
||||
} catch {
|
||||
// Handle error
|
||||
} finally {
|
||||
actionLoading.value = null
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await server.fetchServer()
|
||||
loadFormFromConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-6">
|
||||
<h1 class="text-2xl font-bold text-neutral-100 mb-4">Server Management</h1>
|
||||
<p class="text-neutral-400">Configure server settings and control start, stop, and restart operations.</p>
|
||||
<div class="p-6 space-y-6">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center gap-3">
|
||||
<Server class="w-5 h-5 text-oxide-500" />
|
||||
<h1 class="text-2xl font-bold text-neutral-100">Server Management</h1>
|
||||
</div>
|
||||
|
||||
<!-- Connection Status Card -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider mb-4">Connection</h2>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Status</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<component
|
||||
:is="server.connection?.connection_status === 'connected' ? Wifi : WifiOff"
|
||||
class="w-4 h-4"
|
||||
:class="server.connection?.connection_status === 'connected' ? 'text-green-400' : 'text-red-400'"
|
||||
/>
|
||||
<span class="text-sm font-medium text-neutral-100 capitalize">
|
||||
{{ server.connection?.connection_status || 'Unknown' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Connection Type</p>
|
||||
<p class="text-sm font-medium text-neutral-100 uppercase">
|
||||
{{ server.connection?.connection_type || '\u2014' }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Server IP</p>
|
||||
<p class="text-sm font-mono text-neutral-300">
|
||||
{{ server.connection?.server_ip || '\u2014' }}:{{ server.connection?.server_port || '' }}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Game Port</p>
|
||||
<p class="text-sm font-mono text-neutral-300">
|
||||
{{ server.connection?.game_port || '\u2014' }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Server Controls -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider mb-4">Controls</h2>
|
||||
<div class="flex flex-wrap gap-3">
|
||||
<button
|
||||
@click="serverAction('start')"
|
||||
:disabled="server.connection?.connection_status === 'connected' || actionLoading !== null"
|
||||
class="flex items-center gap-2 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"
|
||||
>
|
||||
<Loader2 v-if="actionLoading === 'start'" class="w-4 h-4 animate-spin" />
|
||||
<Play v-else class="w-4 h-4" />
|
||||
Start Server
|
||||
</button>
|
||||
<button
|
||||
@click="serverAction('stop')"
|
||||
:disabled="server.connection?.connection_status !== 'connected' || actionLoading !== null"
|
||||
class="flex items-center gap-2 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"
|
||||
>
|
||||
<Loader2 v-if="actionLoading === 'stop'" class="w-4 h-4 animate-spin" />
|
||||
<Square v-else class="w-4 h-4" />
|
||||
Stop Server
|
||||
</button>
|
||||
<button
|
||||
@click="serverAction('restart')"
|
||||
:disabled="server.connection?.connection_status !== 'connected' || actionLoading !== null"
|
||||
class="flex items-center gap-2 px-4 py-2.5 bg-oxide-600/20 hover:bg-oxide-600/30 disabled:opacity-30 disabled:cursor-not-allowed text-oxide-400 border border-oxide-600/30 rounded-lg text-sm font-medium transition-colors"
|
||||
>
|
||||
<Loader2 v-if="actionLoading === 'restart'" class="w-4 h-4 animate-spin" />
|
||||
<RotateCcw v-else class="w-4 h-4" />
|
||||
Restart Server
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Configuration -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider">Configuration</h2>
|
||||
<button
|
||||
v-if="!editMode"
|
||||
@click="editMode = true; loadFormFromConfig()"
|
||||
class="text-sm text-oxide-400 hover:text-oxide-300 transition-colors"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Read mode -->
|
||||
<div v-if="!editMode" class="grid grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Server Name</p>
|
||||
<p class="text-sm text-neutral-200">{{ server.config?.server_name || 'Not configured' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Max Players</p>
|
||||
<p class="text-sm text-neutral-200">{{ server.config?.max_players ?? '\u2014' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">World Size</p>
|
||||
<p class="text-sm text-neutral-200">{{ server.config?.world_size ?? '\u2014' }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p class="text-xs text-neutral-500 mb-1">Current Seed</p>
|
||||
<p class="text-sm text-neutral-200">{{ server.config?.current_seed ?? '\u2014' }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit mode -->
|
||||
<form v-else @submit.prevent="saveConfig" class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="col-span-2">
|
||||
<label class="block text-xs text-neutral-500 mb-1">Server Name</label>
|
||||
<input
|
||||
v-model="form.server_name"
|
||||
type="text"
|
||||
class="w-full px-3 py-2 bg-neutral-800 border border-neutral-700 rounded-lg text-sm text-neutral-100 focus:outline-none focus:ring-2 focus:ring-oxide-500/50 focus:border-oxide-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-neutral-500 mb-1">Max Players</label>
|
||||
<input
|
||||
v-model.number="form.max_players"
|
||||
type="number"
|
||||
min="1"
|
||||
max="500"
|
||||
class="w-full px-3 py-2 bg-neutral-800 border border-neutral-700 rounded-lg text-sm text-neutral-100 focus:outline-none focus:ring-2 focus:ring-oxide-500/50 focus:border-oxide-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-neutral-500 mb-1">World Size</label>
|
||||
<input
|
||||
v-model.number="form.world_size"
|
||||
type="number"
|
||||
min="1000"
|
||||
max="8000"
|
||||
class="w-full px-3 py-2 bg-neutral-800 border border-neutral-700 rounded-lg text-sm text-neutral-100 focus:outline-none focus:ring-2 focus:ring-oxide-500/50 focus:border-oxide-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs text-neutral-500 mb-1">Current Seed</label>
|
||||
<input
|
||||
v-model.number="form.current_seed"
|
||||
type="number"
|
||||
class="w-full px-3 py-2 bg-neutral-800 border border-neutral-700 rounded-lg text-sm text-neutral-100 focus:outline-none focus:ring-2 focus:ring-oxide-500/50 focus:border-oxide-500 transition-colors"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-3 pt-2">
|
||||
<button
|
||||
type="submit"
|
||||
:disabled="saving"
|
||||
class="flex items-center gap-2 px-4 py-2 bg-oxide-600 hover:bg-oxide-700 disabled:opacity-50 text-white text-sm font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<Save class="w-4 h-4" />
|
||||
{{ saving ? 'Saving...' : 'Save Changes' }}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@click="editMode = false"
|
||||
class="px-4 py-2 text-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Settings -->
|
||||
<div class="bg-neutral-900 border border-neutral-800 rounded-lg p-5">
|
||||
<h2 class="text-sm font-medium text-neutral-400 uppercase tracking-wider mb-4">Automation</h2>
|
||||
<div class="grid grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-neutral-200">Auto-Restart</p>
|
||||
<p class="text-xs text-neutral-500">Restart on crash detection</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-9 h-5 rounded-full transition-colors cursor-pointer"
|
||||
:class="server.config?.crash_recovery_enabled ? 'bg-oxide-500' : 'bg-neutral-700'"
|
||||
>
|
||||
<div
|
||||
class="w-4 h-4 bg-white rounded-full shadow transition-transform mt-0.5"
|
||||
:class="server.config?.crash_recovery_enabled ? 'translate-x-4.5' : 'translate-x-0.5'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-neutral-200">Auto-Update on Force Wipe</p>
|
||||
<p class="text-xs text-neutral-500">Update when Facepunch pushes</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-9 h-5 rounded-full transition-colors cursor-pointer"
|
||||
:class="server.config?.auto_update_on_force_wipe ? 'bg-oxide-500' : 'bg-neutral-700'"
|
||||
>
|
||||
<div
|
||||
class="w-4 h-4 bg-white rounded-full shadow transition-transform mt-0.5"
|
||||
:class="server.config?.auto_update_on_force_wipe ? 'translate-x-4.5' : 'translate-x-0.5'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<p class="text-sm text-neutral-200">Force Wipe Eligible</p>
|
||||
<p class="text-xs text-neutral-500">Server participates in force wipes</p>
|
||||
</div>
|
||||
<div
|
||||
class="w-9 h-5 rounded-full transition-colors cursor-pointer"
|
||||
:class="server.config?.force_wipe_eligible ? 'bg-oxide-500' : 'bg-neutral-700'"
|
||||
>
|
||||
<div
|
||||
class="w-4 h-4 bg-white rounded-full shadow transition-transform mt-0.5"
|
||||
:class="server.config?.force_wipe_eligible ? 'translate-x-4.5' : 'translate-x-0.5'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user