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%); --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-border: var(--color-primary-a45);
--color-coin-chip-text: var(--color-primary-border); --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-sm: 8px;
--radius-md: 12px; --radius-md: 12px;
+104 -7
View File
@@ -5,6 +5,7 @@ import { useAuthStore } from '@/stores/auth'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import CoinChip from '@/components/ui/CoinChip.vue' import CoinChip from '@/components/ui/CoinChip.vue'
import AppIcon from '@/components/ui/AppIcon.vue' import AppIcon from '@/components/ui/AppIcon.vue'
import ModalDialog from '@/components/ui/ModalDialog.vue'
import { formatUserName } from '@/utils/formatUserName' import { formatUserName } from '@/utils/formatUserName'
import type { UserRole } from '@/types' import type { UserRole } from '@/types'
@@ -12,6 +13,7 @@ const auth = useAuthStore()
const userStore = useUserStore() const userStore = useUserStore()
const router = useRouter() const router = useRouter()
const isProfileMenuOpen = ref(false) const isProfileMenuOpen = ref(false)
const isCoinDialogOpen = ref(false)
const profileMenuRef = ref<HTMLElement | null>(null) const profileMenuRef = ref<HTMLElement | null>(null)
const unreadCount = computed(() => userStore.unreadCount()) const unreadCount = computed(() => userStore.unreadCount())
@@ -42,10 +44,20 @@ function closeProfileMenu() {
isProfileMenuOpen.value = false isProfileMenuOpen.value = false
} }
function closeCoinDialog() {
isCoinDialogOpen.value = false
}
function toggleProfileMenu() { function toggleProfileMenu() {
closeCoinDialog()
isProfileMenuOpen.value = !isProfileMenuOpen.value isProfileMenuOpen.value = !isProfileMenuOpen.value
} }
function openCoinDialog() {
closeProfileMenu()
isCoinDialogOpen.value = true
}
function openProfile() { function openProfile() {
closeProfileMenu() closeProfileMenu()
router.push('/profile') router.push('/profile')
@@ -65,9 +77,9 @@ async function logout() {
} }
function handleDocumentPointerDown(event: PointerEvent) { function handleDocumentPointerDown(event: PointerEvent) {
if (!isProfileMenuOpen.value) return
const target = event.target const target = event.target
if (!(target instanceof Node)) return if (!(target instanceof Node)) return
if (!profileMenuRef.value?.contains(target)) closeProfileMenu() if (!profileMenuRef.value?.contains(target)) closeProfileMenu()
} }
@@ -90,9 +102,20 @@ onBeforeUnmount(() => {
</div> </div>
<div class="topbar-right"> <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" /> <AppIcon icon="bell" :size="18" />
<span class="notif-dot" v-if="auth.user && unreadCount > 0"> <span class="notif-dot" v-if="auth.user && unreadCount > 0">
{{ unreadCount }} {{ unreadCount }}
@@ -140,6 +163,19 @@ onBeforeUnmount(() => {
</div> </div>
</div> </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> </header>
</template> </template>
@@ -185,13 +221,74 @@ onBeforeUnmount(() => {
gap: 12px; gap: 12px;
flex-shrink: 0; 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 { .notif-btn {
position: relative; position: relative;
background: none; display: inline-flex;
border: none; align-items: center;
font-size: 20px; justify-content: center;
width: 32px;
height: 32px;
background: transparent;
border: 0;
color: var(--color-text-secondary);
cursor: pointer; cursor: pointer;
padding: 4px; padding: 0;
border-radius: 50%; border-radius: 50%;
transition: background 0.2s; transition: background 0.2s;
} }
+14 -6
View File
@@ -2,14 +2,15 @@
import AppIcon from '@/components/ui/AppIcon.vue' import AppIcon from '@/components/ui/AppIcon.vue'
defineProps<{ amount: number }>() defineProps<{ amount: number }>()
defineEmits<{ click: [] }>()
</script> </script>
<template> <template>
<div class="coin-chip"> <button class="coin-chip" type="button" @click="$emit('click')">
<AppIcon class="coin-icon" icon="coin" :size="16" /> <AppIcon class="coin-icon" icon="coin" :size="16" />
<span class="coin-amount">{{ amount }}</span> <span class="coin-amount">{{ amount }}</span>
<span class="coin-label">монет</span> <span class="coin-label">монет</span>
</div> </button>
</template> </template>
<style scoped> <style scoped>
@@ -17,16 +18,23 @@ defineProps<{ amount: number }>()
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
gap: 5px; gap: 5px;
background: var(--gradient-coin-chip); background: var(--color-primary-a10);
border: 1px solid var(--color-coin-chip-border); border: 1px solid var(--color-coin-chip-border);
border-radius: 20px; border-radius: 20px;
padding: 5px 12px; padding: 5px 12px;
cursor: default; color: inherit;
transition: all 0.2s; cursor: pointer;
font: inherit;
transition: background 0.2s, border-color 0.2s, box-shadow 0.2s;
white-space: nowrap; white-space: nowrap;
} }
.coin-chip:hover { .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-icon { color: var(--color-coin-chip-text); }
.coin-amount { font-weight: 800; font-size: 14px; color: var(--color-coin-chip-text); } .coin-amount { font-weight: 800; font-size: 14px; color: var(--color-coin-chip-text); }