Profile bar on phones: - With the new Statistik/Admin chips the bar overflowed the viewport on small screens. It now wraps (flex-wrap + max-width) and compacts its paddings, fonts and chip sizes below 560px Auto-wave (🔁 button in the HUD controls): - New persistent toggle (localStorage) that starts the next wave on its own ~0.4s after intermission begins, collecting the full early-call bonus without pressing the wave button every round - Toggling it on during an active intermission starts the wave immediately; works in solo and multiplayer (uses the same action path as the manual button); active state shows green Stats are now saved when leaving via the menu: - Previously only victory/defeat recorded a result — exiting a running game via "🚪 Zurück zum Hauptmenü" or the pause menu discarded the whole run, which is why endless-mode progress beyond wave 20 never showed up in the stats - engine.abandon() records best score, stats and crystals (counted as a defeat) whenever a solo game is left mid-run; no-ops when no game is running or the result was already reported, so no double counting Verified in the browser (390x844, admin account): profile bar wraps compactly; 🔁 auto-started wave 1 during intermission without manual click; exiting mid-wave recorded the run (💎 2, 1 Runde, beste Welle 1). Build clean, 54/54 tests green.
120 lines
2.8 KiB
Vue
120 lines
2.8 KiB
Vue
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
import { logout } from '@/game/auth'
|
||
import { store } from '@/game/store'
|
||
|
||
const user = computed(() => store.auth.user)
|
||
const loggedIn = computed(() => !!user.value && user.value.id !== 'guest')
|
||
const isAdmin = computed(() => Boolean(user.value?.admin))
|
||
|
||
function openResearch(): void {
|
||
store.auth.showResearch = true
|
||
}
|
||
function openStats(): void {
|
||
store.auth.showStats = true
|
||
}
|
||
function openAuth(): void {
|
||
store.auth.showAuth = true
|
||
}
|
||
function openAdmin(): void {
|
||
window.location.href = '/admin'
|
||
}
|
||
async function doLogout(): Promise<void> {
|
||
await logout()
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="profile-bar">
|
||
<div class="crystals" :title="user?.stats ? `${user.stats.totalKills} Abschüsse · ${user.stats.gamesPlayed} Spiele` : ''">
|
||
💎 {{ user?.crystals ?? 0 }}
|
||
</div>
|
||
<template v-if="loggedIn">
|
||
<span class="name" :title="user?.username">👤 {{ user?.displayName }}</span>
|
||
<button class="chip stats" @click="openStats">📊 Statistik</button>
|
||
<button class="chip research" @click="openResearch">🧪 Forschung</button>
|
||
<button v-if="isAdmin" class="chip admin" title="Admin-Dashboard öffnen" @click="openAdmin">🛡️ Admin</button>
|
||
<button class="chip" @click="doLogout">Abmelden</button>
|
||
</template>
|
||
<template v-else>
|
||
<span class="name">👤 Gast</span>
|
||
<button class="chip stats" @click="openStats">📊 Statistik</button>
|
||
<button class="chip research" @click="openResearch">🧪 Forschung</button>
|
||
<button class="chip login" @click="openAuth">Anmelden</button>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.profile-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
max-width: 100%;
|
||
background: var(--panel);
|
||
border: 1px solid var(--panel-border);
|
||
border-radius: 12px;
|
||
padding: 6px 10px;
|
||
}
|
||
.crystals {
|
||
font-weight: 800;
|
||
color: #7fd8ff;
|
||
font-size: 14px;
|
||
}
|
||
.name {
|
||
color: var(--text-dim);
|
||
font-size: 13px;
|
||
max-width: 120px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
.chip {
|
||
background: var(--panel-inset);
|
||
border: 1px solid var(--panel-border);
|
||
color: var(--text);
|
||
font-family: inherit;
|
||
font-weight: 700;
|
||
font-size: 12px;
|
||
border-radius: 8px;
|
||
padding: 5px 10px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Phones: chips wrap into compact rows instead of overflowing */
|
||
@media (max-width: 560px) {
|
||
.profile-bar {
|
||
gap: 5px;
|
||
padding: 5px 8px;
|
||
}
|
||
.crystals {
|
||
font-size: 13px;
|
||
}
|
||
.name {
|
||
font-size: 11.5px;
|
||
max-width: 92px;
|
||
}
|
||
.chip {
|
||
font-size: 11px;
|
||
padding: 4px 8px;
|
||
}
|
||
}
|
||
.chip:hover {
|
||
border-color: var(--accent);
|
||
}
|
||
.chip.research {
|
||
border-color: #3d6f9e;
|
||
}
|
||
.chip.stats {
|
||
border-color: #6e5a9e;
|
||
}
|
||
.chip.admin {
|
||
border-color: #9e503d;
|
||
color: #ffb0a0;
|
||
}
|
||
.chip.login {
|
||
border-color: #3f8f37;
|
||
color: #b8e6b0;
|
||
}
|
||
</style>
|