feat(panel): per-game UI adaptation — sidebar, Server view, and dashboard transform by selected game
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
Drives the panel off the active game (GameSwitcher selection) + the GameProfile registry, so each game visibly differs (not just accent color). Sidebar nav: Rust = full (uMod plugins + plugin configs); Conan/Soulmask/Dune drop uMod + plugin-configs and relabel reset (Wipe World / World Reset / Deep Desert), Dune relabels Console->Broadcast (no RCON) and is Docker-managed. ServerView: management-model badge + game-appropriate panels (Rust deploy + Oxide; Dune Docker/BattleGroup-Sietches; Conan clans/thralls/avatars/purge; Soulmask main-client cluster) with HONEST EmptyStates where no backend data exists yet. Dashboard: per-game reset terminology + stat labels. No invented routes (all map to existing router entries); no fabricated data. Build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import type { DeploymentConfig, DeploymentStatus } from '@/types'
|
||||
import { useWebSocket } from '@/composables/useWebSocket'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
@@ -11,6 +13,7 @@ import Badge from '@/components/ds/core/Badge.vue'
|
||||
import StatusDot from '@/components/ds/core/StatusDot.vue'
|
||||
import Icon from '@/components/ds/core/Icon.vue'
|
||||
import Alert from '@/components/ds/feedback/Alert.vue'
|
||||
import EmptyState from '@/components/ds/feedback/EmptyState.vue'
|
||||
import Input from '@/components/ds/forms/Input.vue'
|
||||
import Switch from '@/components/ds/forms/Switch.vue'
|
||||
import Tabs from '@/components/ds/navigation/Tabs.vue'
|
||||
@@ -18,6 +21,39 @@ import Tabs from '@/components/ds/navigation/Tabs.vue'
|
||||
const server = useServerStore()
|
||||
const auth = useAuthStore()
|
||||
const toast = useToastStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
|
||||
// Profile follows the GameSwitcher. 'all' defaults to rust (neutral house skin).
|
||||
const profile = computed(() => {
|
||||
const game = activeGame.value === 'all' ? 'rust' : activeGame.value
|
||||
return useGameProfile(game)
|
||||
})
|
||||
|
||||
// Game-specific derived flags
|
||||
const isRust = computed(() => profile.value.mods === 'umod')
|
||||
const hasPluginSystem = computed(() => profile.value.mods === 'umod')
|
||||
const isDockerManaged = computed(() => profile.value.managementModel === 'docker-compose')
|
||||
|
||||
// Management model human label for the identity badge
|
||||
const managementModelLabel = computed(() => {
|
||||
const m = profile.value.managementModel
|
||||
const c = profile.value.console
|
||||
if (m === 'docker-compose') {
|
||||
return profile.value.clustering === 'battlegroup' ? 'Docker · BattleGroup' : 'Docker · Compose'
|
||||
}
|
||||
if (c === 'rcon+ingame') return 'Process · RCON + In-game'
|
||||
if (c === 'rcon+gm') return 'Process · RCON + GM'
|
||||
return 'Process · RCON'
|
||||
})
|
||||
|
||||
// Clustering section label per game
|
||||
const clusterLabel = computed(() => {
|
||||
const cl = profile.value.clustering
|
||||
if (cl === 'battlegroup') return 'BattleGroups & Sietches'
|
||||
if (cl === 'main-client') return 'Cluster'
|
||||
if (cl === 'character-transfer') return 'Clans & Character Transfer'
|
||||
return ''
|
||||
})
|
||||
|
||||
const editMode = ref(false)
|
||||
const saving = ref(false)
|
||||
@@ -278,17 +314,18 @@ onMounted(async () => {
|
||||
|
||||
<template>
|
||||
<div class="sv">
|
||||
<!-- Page head -->
|
||||
<!-- Page head — game-aware identity -->
|
||||
<div class="sv__head">
|
||||
<div class="sv__head-id">
|
||||
<div class="sv__head-chip">
|
||||
<Icon name="server" :size="20" :stroke-width="2" />
|
||||
</div>
|
||||
<div>
|
||||
<div class="t-eyebrow">Server management</div>
|
||||
<div class="t-eyebrow">{{ profile.label }} · Server management</div>
|
||||
<h1 class="sv__title">Server</h1>
|
||||
</div>
|
||||
</div>
|
||||
<Badge tone="neutral" :mono="true" class="sv__model-badge">{{ managementModelLabel }}</Badge>
|
||||
</div>
|
||||
|
||||
<!-- Connection -->
|
||||
@@ -444,8 +481,8 @@ onMounted(async () => {
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<!-- Deploy Rust Server -->
|
||||
<Panel title="Deploy Rust server" subtitle="One-click: SteamCMD, download, configure, start">
|
||||
<!-- Deploy Server — Rust only (SteamCMD path). Other games use docker-compose or external tooling. -->
|
||||
<Panel v-if="isRust" title="Deploy Rust server" subtitle="One-click: SteamCMD, download, configure, start">
|
||||
<template #title-append>
|
||||
<Icon name="rocket" :size="15" />
|
||||
</template>
|
||||
@@ -560,8 +597,28 @@ onMounted(async () => {
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<!-- Install Oxide / uMod -->
|
||||
<Panel title="Install Oxide / uMod" subtitle="Required for all plugins including CorrosionCompanion">
|
||||
<!-- Non-Rust: Docker-managed server note -->
|
||||
<Panel
|
||||
v-if="isDockerManaged"
|
||||
:title="profile.label + ' server deployment'"
|
||||
subtitle="Managed via Docker Compose"
|
||||
>
|
||||
<template #title-append>
|
||||
<Icon name="box" :size="15" />
|
||||
</template>
|
||||
<EmptyState
|
||||
icon="box"
|
||||
title="Docker-managed deployment"
|
||||
:description="profile.label + ' servers are managed via Docker Compose. Connect the companion agent on your Docker host to enable lifecycle management.'"
|
||||
>
|
||||
<template #action>
|
||||
<Badge tone="info">Docker · Compose</Badge>
|
||||
</template>
|
||||
</EmptyState>
|
||||
</Panel>
|
||||
|
||||
<!-- Install Oxide / uMod — Rust only -->
|
||||
<Panel v-if="hasPluginSystem" title="Install Oxide / uMod" subtitle="Required for all plugins including CorrosionCompanion">
|
||||
<template #title-append>
|
||||
<Icon name="puzzle" :size="15" />
|
||||
</template>
|
||||
@@ -611,6 +668,79 @@ onMounted(async () => {
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<!-- Workshop Mods info — Conan / Soulmask (Steam Workshop, no install step needed) -->
|
||||
<Panel
|
||||
v-else-if="profile.mods === 'workshop'"
|
||||
:title="(profile.terminology.mods ?? 'Workshop Mods')"
|
||||
:subtitle="profile.label + ' uses Steam Workshop — no manual install step required'"
|
||||
>
|
||||
<template #title-append>
|
||||
<Icon name="layers" :size="15" />
|
||||
</template>
|
||||
<EmptyState
|
||||
icon="layers"
|
||||
:title="profile.label + ' mod management'"
|
||||
:description="profile.label + ' loads mods directly from Steam Workshop. Manage your mod list in server config — no Corrosion install step needed.'"
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<!-- Conan Exiles special concepts (Clans / Thralls / Purge) -->
|
||||
<Panel
|
||||
v-if="profile.accent === 'conan'"
|
||||
title="Conan Exiles concepts"
|
||||
subtitle="Key admin mechanics for Conan Exiles servers"
|
||||
>
|
||||
<div class="sv__concept-grid">
|
||||
<div class="sv__concept">
|
||||
<Icon name="users" :size="16" />
|
||||
<div>
|
||||
<div class="sv__concept-label">Clans</div>
|
||||
<div class="sv__concept-desc">Player factions. Clan management via in-game admin panel or RCON.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sv__concept">
|
||||
<Icon name="zap" :size="16" />
|
||||
<div>
|
||||
<div class="sv__concept-label">Thralls & Avatars</div>
|
||||
<div class="sv__concept-desc">Server-controlled NPCs and deity summons. Purge cycle managed via server settings.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sv__concept">
|
||||
<Icon name="shield" :size="16" />
|
||||
<div>
|
||||
<div class="sv__concept-label">Purge</div>
|
||||
<div class="sv__concept-desc">NPC raid events targeting player bases. Enable / tune via server config.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<!-- Soulmask clustering section -->
|
||||
<Panel
|
||||
v-if="profile.clustering === 'main-client'"
|
||||
:title="clusterLabel"
|
||||
subtitle="Main-client cluster topology for Soulmask"
|
||||
>
|
||||
<EmptyState
|
||||
icon="network"
|
||||
title="Cluster management coming soon"
|
||||
:description="'Connect a ' + profile.label + ' host to manage the main-client cluster from this panel. Cluster configuration requires the companion agent.'"
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<!-- Dune BattleGroup / Sietches section -->
|
||||
<Panel
|
||||
v-if="profile.clustering === 'battlegroup'"
|
||||
title="BattleGroups & Sietches"
|
||||
subtitle="Dune: Awakening server cluster topology"
|
||||
>
|
||||
<EmptyState
|
||||
icon="map"
|
||||
title="Sietch management requires a connected Dune host"
|
||||
description="Connect the companion agent on your Dune: Awakening Docker host to manage BattleGroups and Sietches from this panel."
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<!-- Configuration -->
|
||||
<Panel title="Configuration">
|
||||
<template #actions>
|
||||
@@ -708,8 +838,13 @@ onMounted(async () => {
|
||||
</div>
|
||||
<div class="sv__toggle-row">
|
||||
<div class="sv__toggle-body">
|
||||
<div class="sv__toggle-label">Auto-update on force wipe</div>
|
||||
<div class="sv__toggle-sub">Update when Facepunch pushes</div>
|
||||
<div class="sv__toggle-label">
|
||||
<!-- Rust: "force wipe" is a Facepunch concept. Others: plain "auto-update" -->
|
||||
{{ isRust ? 'Auto-update on force wipe' : 'Auto-update on patch' }}
|
||||
</div>
|
||||
<div class="sv__toggle-sub">
|
||||
{{ isRust ? 'Update when Facepunch pushes' : 'Update when the developer pushes a patch' }}
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
:model-value="server.config?.auto_update_on_force_wipe ?? false"
|
||||
@@ -717,7 +852,8 @@ onMounted(async () => {
|
||||
@update:model-value="toggleAutomation('auto_update_on_force_wipe')"
|
||||
/>
|
||||
</div>
|
||||
<div class="sv__toggle-row">
|
||||
<!-- Rust-only: force wipe eligibility is a Facepunch concept -->
|
||||
<div v-if="isRust" class="sv__toggle-row">
|
||||
<div class="sv__toggle-body">
|
||||
<div class="sv__toggle-label">Force wipe eligible</div>
|
||||
<div class="sv__toggle-sub">Server participates in force wipes</div>
|
||||
@@ -848,4 +984,19 @@ onMounted(async () => {
|
||||
.sv__toggle-row:first-child { padding-top: 0; }
|
||||
.sv__toggle-label { font-size: var(--text-sm); font-weight: 500; color: var(--text-primary); }
|
||||
.sv__toggle-sub { font-size: var(--text-xs); color: var(--text-tertiary); margin-top: 2px; }
|
||||
|
||||
/* Management model badge in page head */
|
||||
.sv__model-badge { align-self: center; }
|
||||
|
||||
/* Game concept cards (Conan Exiles special features) */
|
||||
.sv__concept-grid { display: flex; flex-direction: column; gap: 14px; }
|
||||
.sv__concept {
|
||||
display: flex; align-items: flex-start; gap: 12px;
|
||||
padding: 12px 14px;
|
||||
background: var(--surface-raised); border-radius: var(--radius-md);
|
||||
box-shadow: var(--ring-default);
|
||||
color: var(--accent);
|
||||
}
|
||||
.sv__concept-label { font-size: var(--text-sm); font-weight: 600; color: var(--text-primary); margin-bottom: 2px; }
|
||||
.sv__concept-desc { font-size: var(--text-xs); color: var(--text-tertiary); line-height: 1.5; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user