feat(panel): Beta sweep — multi-game coherence, honesty, UX fixes
Multi-game rebrand (no more Rust-only leftovers): game-neutral setup wizard + deploy/store defaults; player-id labels driven by game profile (Steam ID only for Rust); blueprint wipe type + verify-plugins gated to uMod games; oxide command examples + Rust-only plugin pages (AutoDoors/FurnaceSplitter/BetterChat) guarded behind mods==='umod' with empty-states for other games. Honesty: webstore checkout shows coming-soon (backend now 503s); 'integrated webstore' marketed as coming-soon; Discord references neutralized to community/webhook; migration FAQ marked in-development; analytics dev phase labels removed; Network pricing tier set to Custom/Contact (was a confusing duplicate of Operator); docs/PRICING.md rewritten to match live subscriptions. UX/bugs: fixed ServerView oxide-status operator-precedence bug; dead 'Deploy server' button wired; non-functional topbar search removed; alert()/confirm() replaced with toasts across schedules/alerts/migration/public store+server; analytics chart arrays null-guarded; production console.logs gated to DEV. Frontend build (vue-tsc + vite) green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { safeDate } from '@/utils/formatters'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -30,6 +31,7 @@ interface AlertHistoryEntry {
|
||||
}
|
||||
|
||||
const api = useApi()
|
||||
const toast = useToastStore()
|
||||
const config = ref<AlertConfig>({
|
||||
population_drop_enabled: false,
|
||||
population_drop_threshold_percent: 50,
|
||||
@@ -60,9 +62,9 @@ async function saveConfig() {
|
||||
isSaving.value = true
|
||||
try {
|
||||
await api.put('/alerts/config', config.value)
|
||||
alert('Alert configuration saved')
|
||||
toast.success('Alert configuration saved')
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to save configuration')
|
||||
toast.error(err instanceof Error ? err.message : 'Failed to save configuration')
|
||||
} finally {
|
||||
isSaving.value = false
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ const renderCharts = () => {
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: timeseries.value.timestamps.map(ts => new Date(ts).toLocaleString('en-US', {
|
||||
data: (timeseries.value.timestamps ?? []).map(ts => new Date(ts).toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit'
|
||||
@@ -116,7 +116,7 @@ const renderCharts = () => {
|
||||
{
|
||||
name: 'Players',
|
||||
type: 'line',
|
||||
data: timeseries.value.player_count,
|
||||
data: timeseries.value.player_count ?? [],
|
||||
smooth: true,
|
||||
lineStyle: { color: accent, width: 2 },
|
||||
areaStyle: {
|
||||
@@ -160,7 +160,7 @@ const renderCharts = () => {
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: timeseries.value.timestamps.map(ts => new Date(ts).toLocaleString('en-US', {
|
||||
data: (timeseries.value.timestamps ?? []).map(ts => new Date(ts).toLocaleString('en-US', {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: '2-digit'
|
||||
@@ -191,7 +191,7 @@ const renderCharts = () => {
|
||||
name: 'FPS',
|
||||
type: 'line',
|
||||
yAxisIndex: 0,
|
||||
data: timeseries.value.fps,
|
||||
data: timeseries.value.fps ?? [],
|
||||
smooth: true,
|
||||
lineStyle: { color: '#10b981', width: 2 },
|
||||
itemStyle: { color: '#10b981' }
|
||||
@@ -200,7 +200,7 @@ const renderCharts = () => {
|
||||
name: 'Entities',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: timeseries.value.entity_count,
|
||||
data: timeseries.value.entity_count ?? [],
|
||||
smooth: true,
|
||||
lineStyle: { color: '#6366f1', width: 2 },
|
||||
itemStyle: { color: '#6366f1' }
|
||||
@@ -287,7 +287,7 @@ onMounted(() => {
|
||||
label="Unique players"
|
||||
:value="summary.unique_players ?? '—'"
|
||||
icon="bar-chart-3"
|
||||
note="Phase 2.2"
|
||||
note="Coming soon"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -302,9 +302,9 @@ onMounted(() => {
|
||||
</div>
|
||||
|
||||
<!-- Player Retention placeholder -->
|
||||
<Panel eyebrow="Coming in phase 2" title="Player retention">
|
||||
<Panel eyebrow="Coming soon" title="Player retention">
|
||||
<template #title-append>
|
||||
<Badge tone="neutral">Phase 2</Badge>
|
||||
<Badge tone="neutral">Coming soon</Badge>
|
||||
</template>
|
||||
<div class="analytics-view__retention-grid">
|
||||
<div class="analytics-view__retention-cell">
|
||||
@@ -324,7 +324,7 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
<p class="analytics-view__retention-footer">
|
||||
Player retention analytics will be available in phase 2.
|
||||
Player retention analytics are coming soon.
|
||||
</p>
|
||||
</Panel>
|
||||
</template>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useAutoDoorsStore } from '@/stores/autodoors'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
import Icon from '@/components/ds/core/Icon.vue'
|
||||
@@ -8,6 +10,8 @@ import Switch from '@/components/ds/forms/Switch.vue'
|
||||
import EmptyState from '@/components/ds/feedback/EmptyState.vue'
|
||||
|
||||
const store = useAutoDoorsStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const gameProfile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const showCreateModal = ref(false)
|
||||
const showImportModal = ref(false)
|
||||
@@ -159,6 +163,16 @@ function getBool(path: string, def: boolean): boolean {
|
||||
|
||||
<template>
|
||||
<div class="adv">
|
||||
<!-- uMod-only guard: AutoDoors is an Oxide/uMod plugin -->
|
||||
<Panel v-if="gameProfile.mods !== 'umod'">
|
||||
<EmptyState
|
||||
icon="door-open"
|
||||
title="Rust / uMod only"
|
||||
description="Auto Doors is only available for Rust (uMod/Oxide) servers."
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<template v-else>
|
||||
<!-- Page head -->
|
||||
<div class="adv__head">
|
||||
<div class="adv__head-id">
|
||||
@@ -504,6 +518,7 @@ function getBool(path: string, def: boolean): boolean {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useBetterChatStore } from '@/stores/betterchat'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -13,6 +15,8 @@ import Switch from '@/components/ds/forms/Switch.vue'
|
||||
import EmptyState from '@/components/ds/feedback/EmptyState.vue'
|
||||
|
||||
const store = useBetterChatStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const gameProfile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const activeTab = ref<string>('groups')
|
||||
const showCreateModal = ref(false)
|
||||
@@ -276,6 +280,16 @@ const editGroupFormatConsole = computed<string>({
|
||||
|
||||
<template>
|
||||
<div class="bch">
|
||||
<!-- uMod-only guard: BetterChat is an Oxide/uMod plugin -->
|
||||
<Panel v-if="gameProfile.mods !== 'umod'">
|
||||
<EmptyState
|
||||
icon="message-square"
|
||||
title="Rust / uMod only"
|
||||
description="Better Chat is only available for Rust (uMod/Oxide) servers."
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<template v-else>
|
||||
<!-- Page head -->
|
||||
<div class="bch__head">
|
||||
<div class="bch__head-id">
|
||||
@@ -696,6 +710,7 @@ const editGroupFormatConsole = computed<string>({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import type { ChatMessage } from '@/types'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -14,6 +15,8 @@ import Tabs from '@/components/ds/navigation/Tabs.vue'
|
||||
|
||||
const api = useApi()
|
||||
const toast = useToastStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const playerIdLabel = computed(() => activeGame.value === 'rust' || activeGame.value === 'all' ? 'Steam ID' : 'Player ID')
|
||||
|
||||
const messages = ref<ChatMessage[]>([])
|
||||
const isLoading = ref(false)
|
||||
@@ -122,7 +125,7 @@ onMounted(() => {
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
icon="search"
|
||||
placeholder="Search messages, players, or Steam IDs…"
|
||||
:placeholder="`Search messages, players, or ${playerIdLabel}s…`"
|
||||
size="sm"
|
||||
style="max-width: 340px;"
|
||||
/>
|
||||
|
||||
@@ -383,7 +383,7 @@ function navServer() { router.push('/server') }
|
||||
v-model="consoleInput"
|
||||
:mono="true"
|
||||
size="sm"
|
||||
placeholder="say, kick, ban, oxide.reload …"
|
||||
:placeholder="profile.mods === 'umod' ? 'say, kick, ban, oxide.reload …' : 'say, kick, ban …'"
|
||||
:disabled="!isConnected"
|
||||
style="flex: 1"
|
||||
@keydown.enter="sendConsoleCommand"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useFurnaceSplitterStore } from '@/stores/furnacesplitter'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
import Icon from '@/components/ds/core/Icon.vue'
|
||||
@@ -8,6 +10,8 @@ import Switch from '@/components/ds/forms/Switch.vue'
|
||||
import EmptyState from '@/components/ds/feedback/EmptyState.vue'
|
||||
|
||||
const store = useFurnaceSplitterStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const gameProfile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const showCreateModal = ref(false)
|
||||
const showImportModal = ref(false)
|
||||
@@ -116,6 +120,16 @@ function getBool(path: string, def: boolean): boolean {
|
||||
|
||||
<template>
|
||||
<div class="fsv">
|
||||
<!-- uMod-only guard: Furnace Splitter is an Oxide/uMod plugin -->
|
||||
<Panel v-if="gameProfile.mods !== 'umod'">
|
||||
<EmptyState
|
||||
icon="flame"
|
||||
title="Rust / uMod only"
|
||||
description="Furnace Splitter is only available for Rust (uMod/Oxide) servers."
|
||||
/>
|
||||
</Panel>
|
||||
|
||||
<template v-else>
|
||||
<!-- Page head -->
|
||||
<div class="fsv__head">
|
||||
<div class="fsv__head-id">
|
||||
@@ -326,6 +340,7 @@ function getBool(path: string, def: boolean): boolean {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { safeFileSize, safeDate } from '@/utils/formatters'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -20,6 +21,7 @@ interface ExportRecord {
|
||||
|
||||
const api = useApi()
|
||||
const authStore = useAuthStore()
|
||||
const toast = useToastStore()
|
||||
const exports = ref<ExportRecord[]>([])
|
||||
const isExporting = ref(false)
|
||||
const isImporting = ref(false)
|
||||
@@ -37,7 +39,7 @@ async function createExport() {
|
||||
isExporting.value = true
|
||||
try {
|
||||
const result = await api.post<{ export_id: string }>('/migration/export', { export_type: exportType.value })
|
||||
alert(`Export created: ${result.export_id}`)
|
||||
toast.success(`Export created: ${result.export_id}`)
|
||||
await fetchExports()
|
||||
} finally {
|
||||
isExporting.value = false
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ref, computed, onMounted } from 'vue'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
import Badge from '@/components/ds/core/Badge.vue'
|
||||
@@ -15,6 +16,8 @@ import Tabs from '@/components/ds/navigation/Tabs.vue'
|
||||
const server = useServerStore()
|
||||
const api = useApi()
|
||||
const toast = useToastStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const playerIdLabel = computed(() => activeGame.value === 'rust' || activeGame.value === 'all' ? 'Steam ID' : 'Player ID')
|
||||
|
||||
interface Player {
|
||||
steam_id: string
|
||||
@@ -166,7 +169,7 @@ onMounted(() => {
|
||||
<Input
|
||||
v-model="searchQuery"
|
||||
icon="search"
|
||||
placeholder="Search by name or Steam ID…"
|
||||
:placeholder="`Search by name or ${playerIdLabel}…`"
|
||||
size="sm"
|
||||
:mono="false"
|
||||
style="max-width: 320px;"
|
||||
@@ -197,7 +200,7 @@ onMounted(() => {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Player</th>
|
||||
<th>Steam ID</th>
|
||||
<th>{{ playerIdLabel }}</th>
|
||||
<th>Status</th>
|
||||
<th>Session</th>
|
||||
<th>Playtime</th>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { safeDate } from '@/utils/formatters'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -22,6 +23,7 @@ interface ScheduledTask {
|
||||
}
|
||||
|
||||
const api = useApi()
|
||||
const toast = useToastStore()
|
||||
const tasks = ref<ScheduledTask[]>([])
|
||||
const isLoading = ref(false)
|
||||
const showModal = ref(false)
|
||||
@@ -93,7 +95,7 @@ async function saveTask() {
|
||||
showModal.value = false
|
||||
await fetchTasks()
|
||||
} catch (err) {
|
||||
alert(err instanceof Error ? err.message : 'Failed to save task')
|
||||
toast.error(err instanceof Error ? err.message : 'Failed to save task')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ const showCreds = ref(false)
|
||||
const tomlCopied = ref(false)
|
||||
|
||||
const deployForm = ref<DeploymentConfig>({
|
||||
server_name: 'My Rust Server',
|
||||
server_name: '',
|
||||
max_players: 100,
|
||||
world_size: 4000,
|
||||
seed: Math.floor(Math.random() * 2147483647),
|
||||
@@ -465,7 +465,7 @@ onMounted(async () => {
|
||||
}
|
||||
if (msg.type === 'event' && msg.event === 'oxide_status') {
|
||||
oxideStatus.value = msg.data as { stage: string; progress: number; message: string; error?: string }
|
||||
if (msg.data && (msg.data as any).stage === 'complete' || (msg.data as any).stage === 'failed') {
|
||||
if (msg.data && ((msg.data as any).stage === 'complete' || (msg.data as any).stage === 'failed')) {
|
||||
isInstallingOxide.value = false
|
||||
}
|
||||
}
|
||||
@@ -935,7 +935,7 @@ onMounted(async () => {
|
||||
|
||||
<!-- Conan Exiles special concepts (Clans / Thralls / Purge) -->
|
||||
<Panel
|
||||
v-if="profile.accent === 'conan'"
|
||||
v-if="activeGame === 'conan'"
|
||||
title="Conan Exiles concepts"
|
||||
subtitle="Key admin mechanics for Conan Exiles servers"
|
||||
>
|
||||
|
||||
@@ -166,7 +166,7 @@ onMounted(() => {
|
||||
<Input
|
||||
v-model="config.store_name"
|
||||
label="Store name"
|
||||
placeholder="My Rust Server Store"
|
||||
placeholder="My server store"
|
||||
:required="true"
|
||||
hint="Displayed to players on the store page"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import type { StoreCategory, StoreItem } from '@/types'
|
||||
import { safeFixed } from '@/utils/formatters'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
@@ -14,6 +16,8 @@ import Select from '@/components/ds/forms/Select.vue'
|
||||
import Checkbox from '@/components/ds/forms/Checkbox.vue'
|
||||
|
||||
const api = useApi()
|
||||
const { activeGame } = useThemeGame()
|
||||
const gameProfile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const tab = ref<'categories' | 'items'>('categories')
|
||||
const isLoading = ref(false)
|
||||
@@ -46,12 +50,19 @@ const itemForm = ref({
|
||||
enabled: true
|
||||
})
|
||||
|
||||
const itemTypes = [
|
||||
const itemTypesUmod = [
|
||||
{ value: 'kit', label: 'Kit', example: 'inventory.giveto {steam_id} rifle.ak 1' },
|
||||
{ value: 'rank', label: 'Rank', example: 'oxide.usergroup add {steam_id} vip' },
|
||||
{ value: 'currency', label: 'Currency', example: 'eco deposit {steam_id} 1000' },
|
||||
{ value: 'command', label: 'Custom command', example: 'yourplugin.givereward {steam_id}' }
|
||||
{ value: 'command', label: 'Custom command', example: 'yourplugin.givereward {steam_id}' },
|
||||
]
|
||||
const itemTypesGeneric = [
|
||||
{ value: 'kit', label: 'Kit', example: 'givecontent {steam_id} item_id 1' },
|
||||
{ value: 'rank', label: 'Rank', example: 'setrank {steam_id} vip' },
|
||||
{ value: 'currency', label: 'Currency', example: 'addcurrency {steam_id} 1000' },
|
||||
{ value: 'command', label: 'Custom command', example: 'yourplugin.givereward {steam_id}' },
|
||||
]
|
||||
const itemTypes = computed(() => gameProfile.value.mods === 'umod' ? itemTypesUmod : itemTypesGeneric)
|
||||
|
||||
const tabItems = computed(() => [
|
||||
{ value: 'categories', label: 'Categories', count: categories.value.length },
|
||||
@@ -251,7 +262,7 @@ function getCategoryName(categoryId: string | null): string {
|
||||
}
|
||||
|
||||
const selectedTypeExample = computed(() => {
|
||||
const type = itemTypes.find(t => t.value === itemForm.value.item_type)
|
||||
const type = itemTypes.value.find(t => t.value === itemForm.value.item_type)
|
||||
return type?.example ?? ''
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { useTimedExecuteStore } from '@/stores/timedexecute'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -12,6 +14,8 @@ import Switch from '@/components/ds/forms/Switch.vue'
|
||||
import EmptyState from '@/components/ds/feedback/EmptyState.vue'
|
||||
|
||||
const store = useTimedExecuteStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const gameProfile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const activeTab = ref<string>('timed')
|
||||
const showCreateModal = ref(false)
|
||||
@@ -360,7 +364,7 @@ const importConfigNameModel = computed<string>({
|
||||
<span class="te__presets-label">Quick add:</span>
|
||||
<button class="te__preset" @click="addPresetTimer('server.save', 300)">server.save (5 min)</button>
|
||||
<button class="te__preset" @click="addPresetTimer('say Server restart warning!', 3600)">Restart warning (1 h)</button>
|
||||
<button class="te__preset" @click="addPresetTimer('oxide.reload *', 7200)">Reload plugins (2 h)</button>
|
||||
<button v-if="gameProfile.mods === 'umod'" class="te__preset" @click="addPresetTimer('oxide.reload *', 7200)">Reload plugins (2 h)</button>
|
||||
</div>
|
||||
|
||||
<EmptyState
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useWipeStore } from '@/stores/wipe'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import type { WipeProfile } from '@/types'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
import Button from '@/components/ds/core/Button.vue'
|
||||
@@ -13,6 +15,8 @@ import EmptyState from '@/components/ds/feedback/EmptyState.vue'
|
||||
|
||||
const wipeStore = useWipeStore()
|
||||
const toast = useToastStore()
|
||||
const { activeGame } = useThemeGame()
|
||||
const gameProfile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const expandedId = ref<string | null>(null)
|
||||
const showModal = ref(false)
|
||||
@@ -242,7 +246,7 @@ onMounted(() => {
|
||||
{{ profile.post_wipe_config.verify_server_started ? 'Yes' : 'No' }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div class="detail-kv">
|
||||
<div v-if="gameProfile.mods === 'umod'" class="detail-kv">
|
||||
<span class="detail-k">Verify plugins loaded</span>
|
||||
<Badge :tone="profile.post_wipe_config.verify_plugins_loaded ? 'online' : 'neutral'">
|
||||
{{ profile.post_wipe_config.verify_plugins_loaded ? 'Yes' : 'No' }}
|
||||
@@ -359,6 +363,7 @@ onMounted(() => {
|
||||
label="Verify correct map"
|
||||
/>
|
||||
<Checkbox
|
||||
v-if="gameProfile.mods === 'umod'"
|
||||
v-model="form.post_wipe_config.verify_plugins_loaded"
|
||||
label="Verify plugins loaded"
|
||||
/>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useWipeStore } from '@/stores/wipe'
|
||||
import { useServerStore } from '@/stores/server'
|
||||
import { useToastStore } from '@/stores/toast'
|
||||
import { useApi } from '@/composables/useApi'
|
||||
import { useThemeGame } from '@/composables/useThemeGame'
|
||||
import { useGameProfile } from '@/config/gameProfiles'
|
||||
import { RouterLink } from 'vue-router'
|
||||
import { safeDate } from '@/utils/formatters'
|
||||
import Panel from '@/components/ds/data/Panel.vue'
|
||||
@@ -18,6 +20,8 @@ const wipeStore = useWipeStore()
|
||||
const server = useServerStore()
|
||||
const toast = useToastStore()
|
||||
const api = useApi()
|
||||
const { activeGame } = useThemeGame()
|
||||
const profile = computed(() => useGameProfile(activeGame.value === 'all' ? 'rust' : activeGame.value))
|
||||
|
||||
const triggerType = ref<'map' | 'blueprint' | 'full'>('map')
|
||||
const selectedProfileId = ref<string>('')
|
||||
@@ -71,11 +75,18 @@ async function toggleSchedule(scheduleId: string, currentlyActive: boolean) {
|
||||
}
|
||||
}
|
||||
|
||||
const WIPE_TYPE_OPTIONS = [
|
||||
const WIPE_TYPE_OPTIONS_BASE = [
|
||||
{ value: 'map', label: 'Map' },
|
||||
{ value: 'full', label: 'Full' },
|
||||
]
|
||||
const WIPE_TYPE_OPTIONS_RUST = [
|
||||
{ value: 'map', label: 'Map' },
|
||||
{ value: 'blueprint', label: 'Blueprint' },
|
||||
{ value: 'full', label: 'Full' },
|
||||
]
|
||||
const wipeTypeOptions = computed(() =>
|
||||
profile.value.mods === 'umod' ? WIPE_TYPE_OPTIONS_RUST : WIPE_TYPE_OPTIONS_BASE
|
||||
)
|
||||
|
||||
function profileOptions() {
|
||||
const opts: { value: string; label: string }[] = [{ value: '', label: 'No profile' }]
|
||||
@@ -148,7 +159,7 @@ onMounted(async () => {
|
||||
<div class="cc-field__label">Wipe type</div>
|
||||
<div class="type-seg">
|
||||
<button
|
||||
v-for="opt in WIPE_TYPE_OPTIONS"
|
||||
v-for="opt in wipeTypeOptions"
|
||||
:key="opt.value"
|
||||
type="button"
|
||||
class="type-seg__btn"
|
||||
|
||||
Reference in New Issue
Block a user