feat: Добавил попап зачем монеты
Frontend CI / build-and-check (push) Failing after 5m15s
🚀 Create and publish a Docker image / Detect changes in backend and frontend (push) Successful in 16s
🚀 Create and publish a Docker image / Build & publish backend image (push) Successful in 19s
🚀 Create and publish a Docker image / Build & publish frontend image (push) Successful in 32s
🚀 Create and publish a Docker image / Update stack on Portainer (push) Successful in 13s

This commit is contained in:
2026-05-18 05:17:04 +03:00
parent 55369301f0
commit b52318b992
3 changed files with 119 additions and 14 deletions
+1 -1
View File
@@ -147,7 +147,7 @@
--gradient-coin-chip-hover: linear-gradient(135deg, var(--color-primary-a40) 0%, var(--color-aqua-a40) 100%);
--color-coin-chip-border: var(--color-primary-a45);
--color-coin-chip-text: var(--color-primary-border);
--color-coin-chip-label: var(--color-aqua-dark);
--color-coin-chip-label: var(--color-primary-border);
--radius-sm: 8px;
--radius-md: 12px;
+104 -7
View File
@@ -5,6 +5,7 @@ import { useAuthStore } from '@/stores/auth'
import { useUserStore } from '@/stores/user'
import CoinChip from '@/components/ui/CoinChip.vue'
import AppIcon from '@/components/ui/AppIcon.vue'
import ModalDialog from '@/components/ui/ModalDialog.vue'
import { formatUserName } from '@/utils/formatUserName'
import type { UserRole } from '@/types'
@@ -12,6 +13,7 @@ const auth = useAuthStore()
const userStore = useUserStore()
const router = useRouter()
const isProfileMenuOpen = ref(false)
const isCoinDialogOpen = ref(false)
const profileMenuRef = ref<HTMLElement | null>(null)
const unreadCount = computed(() => userStore.unreadCount())
@@ -42,10 +44,20 @@ function closeProfileMenu() {
isProfileMenuOpen.value = false
}
function closeCoinDialog() {
isCoinDialogOpen.value = false
}
function toggleProfileMenu() {
closeCoinDialog()
isProfileMenuOpen.value = !isProfileMenuOpen.value
}
function openCoinDialog() {
closeProfileMenu()
isCoinDialogOpen.value = true
}
function openProfile() {
closeProfileMenu()
router.push('/profile')
@@ -65,9 +77,9 @@ async function logout() {
}
function handleDocumentPointerDown(event: PointerEvent) {
if (!isProfileMenuOpen.value) return
const target = event.target
if (!(target instanceof Node)) return
if (!profileMenuRef.value?.contains(target)) closeProfileMenu()
}
@@ -90,9 +102,20 @@ onBeforeUnmount(() => {
</div>
<div class="topbar-right">
<CoinChip v-if="auth.user" :amount="auth.user.coins" />
<CoinChip
v-if="auth.user"
:amount="auth.user.coins"
aria-haspopup="dialog"
@click="openCoinDialog"
/>
<button class="notif-btn" @click="$router.push('/notifications')">
<div v-if="auth.user" class="level-chip">
<AppIcon class="level-icon" icon="star" :size="15" />
<span class="level-label">Ур.</span>
<span class="level-value">{{ auth.user.level }}</span>
</div>
<button class="notif-btn" type="button" aria-label="Уведомления" @click="$router.push('/notifications')">
<AppIcon icon="bell" :size="18" />
<span class="notif-dot" v-if="auth.user && unreadCount > 0">
{{ unreadCount }}
@@ -140,6 +163,19 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
<ModalDialog v-model="isCoinDialogOpen">
<div class="coin-dialog-content">
<div class="coin-dialog-title">
<AppIcon class="coin-dialog-icon" icon="coin" :size="22" />
<span>Монеты UniVerse</span>
</div>
<p>
В будущем монеты можно будет использовать во внутреннем магазине. Сейчас магазин еще не запущен, поэтому монеты просто копятся на вашем балансе.
</p>
<button class="btn-primary coin-dialog-ok" type="button" @click="closeCoinDialog">ОК</button>
</div>
</ModalDialog>
</header>
</template>
@@ -185,13 +221,74 @@ onBeforeUnmount(() => {
gap: 12px;
flex-shrink: 0;
}
.coin-dialog-content {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 14px;
}
.coin-dialog-title {
display: flex;
align-items: center;
gap: 9px;
color: var(--color-text);
font-size: 17px;
font-weight: 800;
}
.coin-dialog-icon {
flex: 0 0 auto;
color: var(--color-coin-chip-text);
}
.coin-dialog-content p {
margin: 0;
color: var(--color-text-secondary);
font-size: 14px;
line-height: 1.5;
}
.coin-dialog-ok {
min-width: 92px;
align-self: center;
justify-content: center;
margin-top: 2px;
}
.level-chip {
display: inline-flex;
align-items: center;
gap: 5px;
height: 28px;
padding: 0 11px;
border: 1px solid var(--color-primary-a20);
border-radius: 20px;
background: var(--color-primary-a10);
color: var(--color-primary-dark);
cursor: default;
white-space: nowrap;
}
.level-icon {
color: var(--color-primary);
}
.level-label {
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 700;
}
.level-value {
color: var(--color-primary-dark);
font-size: 14px;
font-weight: 800;
}
.notif-btn {
position: relative;
background: none;
border: none;
font-size: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
background: transparent;
border: 0;
color: var(--color-text-secondary);
cursor: pointer;
padding: 4px;
padding: 0;
border-radius: 50%;
transition: background 0.2s;
}
+14 -6
View File
@@ -2,14 +2,15 @@
import AppIcon from '@/components/ui/AppIcon.vue'
defineProps<{ amount: number }>()
defineEmits<{ click: [] }>()
</script>
<template>
<div class="coin-chip">
<button class="coin-chip" type="button" @click="$emit('click')">
<AppIcon class="coin-icon" icon="coin" :size="16" />
<span class="coin-amount">{{ amount }}</span>
<span class="coin-label">монет</span>
</div>
</button>
</template>
<style scoped>
@@ -17,16 +18,23 @@ defineProps<{ amount: number }>()
display: inline-flex;
align-items: center;
gap: 5px;
background: var(--gradient-coin-chip);
background: var(--color-primary-a10);
border: 1px solid var(--color-coin-chip-border);
border-radius: 20px;
padding: 5px 12px;
cursor: default;
transition: all 0.2s;
color: inherit;
cursor: pointer;
font: inherit;
transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
white-space: nowrap;
}
.coin-chip:hover {
background: var(--gradient-coin-chip-hover);
background: var(--color-primary-a18);
border-color: var(--color-primary-a50);
}
.coin-chip:focus-visible {
outline: 2px solid var(--color-primary-a45);
outline-offset: 2px;
}
.coin-icon { color: var(--color-coin-chip-text); }
.coin-amount { font-weight: 800; font-size: 14px; color: var(--color-coin-chip-text); }