TRXTD/src/components/UserProfileBar.vue
Tronax f7ab56e475
fix(ui+game): responsive profile bar, auto-wave toggle, and stats on early exit
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.
2026-08-17 15:51:23 +02:00

120 lines
2.8 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>