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
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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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); }
|
||||
|
||||
Reference in New Issue
Block a user