Dev #11
@@ -14,13 +14,17 @@ const userStore = useUserStore()
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const isProfileMenuOpen = ref(false)
|
const isProfileMenuOpen = ref(false)
|
||||||
const isCoinDialogOpen = ref(false)
|
const isCoinDialogOpen = ref(false)
|
||||||
|
const isLevelDialogOpen = ref(false)
|
||||||
|
const isSlotsDialogOpen = ref(false)
|
||||||
const profileMenuRef = ref<HTMLElement | null>(null)
|
const profileMenuRef = ref<HTMLElement | null>(null)
|
||||||
|
|
||||||
const unreadCount = computed(() => userStore.unreadCount())
|
const unreadCount = computed(() => userStore.unreadCount())
|
||||||
|
const showStudentChips = computed(() => auth.user?.activeRole === 'student')
|
||||||
const enrollmentSlotText = computed(() => {
|
const enrollmentSlotText = computed(() => {
|
||||||
if (auth.user?.activeRole !== 'student') return ''
|
const user = auth.user
|
||||||
if (typeof auth.user.enrollmentSlotLimit !== 'number') return ''
|
if (!user || user.activeRole !== 'student') return ''
|
||||||
return `${auth.user.activeEnrollments ?? 0}/${auth.user.enrollmentSlotLimit}`
|
if (typeof user.enrollmentSlotLimit !== 'number') return ''
|
||||||
|
return `${user.activeEnrollments ?? 0}/${user.enrollmentSlotLimit}`
|
||||||
})
|
})
|
||||||
const roleLabels: Record<UserRole, string> = {
|
const roleLabels: Record<UserRole, string> = {
|
||||||
student: 'Студент',
|
student: 'Студент',
|
||||||
@@ -53,8 +57,18 @@ function closeCoinDialog() {
|
|||||||
isCoinDialogOpen.value = false
|
isCoinDialogOpen.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeLevelDialog() {
|
||||||
|
isLevelDialogOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeSlotsDialog() {
|
||||||
|
isSlotsDialogOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
function toggleProfileMenu() {
|
function toggleProfileMenu() {
|
||||||
closeCoinDialog()
|
closeCoinDialog()
|
||||||
|
closeLevelDialog()
|
||||||
|
closeSlotsDialog()
|
||||||
isProfileMenuOpen.value = !isProfileMenuOpen.value
|
isProfileMenuOpen.value = !isProfileMenuOpen.value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +77,25 @@ function openCoinDialog() {
|
|||||||
isCoinDialogOpen.value = true
|
isCoinDialogOpen.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openLevelDialog() {
|
||||||
|
closeProfileMenu()
|
||||||
|
closeCoinDialog()
|
||||||
|
closeSlotsDialog()
|
||||||
|
isLevelDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSlotsDialog() {
|
||||||
|
closeProfileMenu()
|
||||||
|
closeCoinDialog()
|
||||||
|
closeLevelDialog()
|
||||||
|
isSlotsDialogOpen.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openMyLecturesFromSlotsDialog() {
|
||||||
|
closeSlotsDialog()
|
||||||
|
router.push('/my-lectures')
|
||||||
|
}
|
||||||
|
|
||||||
function openProfile() {
|
function openProfile() {
|
||||||
closeProfileMenu()
|
closeProfileMenu()
|
||||||
router.push('/profile')
|
router.push('/profile')
|
||||||
@@ -109,22 +142,35 @@ onBeforeUnmount(() => {
|
|||||||
|
|
||||||
<div class="topbar-right">
|
<div class="topbar-right">
|
||||||
<CoinChip
|
<CoinChip
|
||||||
v-if="auth.user"
|
v-if="auth.user && showStudentChips"
|
||||||
:amount="auth.user.coins"
|
:amount="auth.user.coins"
|
||||||
aria-haspopup="dialog"
|
aria-haspopup="dialog"
|
||||||
@click="openCoinDialog"
|
@click="openCoinDialog"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div v-if="auth.user" class="level-chip">
|
<button
|
||||||
|
v-if="auth.user && showStudentChips"
|
||||||
|
class="level-chip"
|
||||||
|
type="button"
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
@click="openLevelDialog"
|
||||||
|
>
|
||||||
<AppIcon class="level-icon" icon="star" :size="15" />
|
<AppIcon class="level-icon" icon="star" :size="15" />
|
||||||
<span class="level-label">Ур.</span>
|
<span class="level-label">Ур.</span>
|
||||||
<span class="level-value">{{ auth.user.level }}</span>
|
<span class="level-value">{{ auth.user.level }}</span>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<div v-if="enrollmentSlotText" class="slot-chip" title="Занятые слоты записи / доступные слоты">
|
<button
|
||||||
|
v-if="enrollmentSlotText"
|
||||||
|
class="slot-chip"
|
||||||
|
type="button"
|
||||||
|
aria-haspopup="dialog"
|
||||||
|
title="Занятые слоты записи / доступные слоты"
|
||||||
|
@click="openSlotsDialog"
|
||||||
|
>
|
||||||
<AppIcon class="slot-icon" icon="calendar" :size="15" />
|
<AppIcon class="slot-icon" icon="calendar" :size="15" />
|
||||||
<span class="slot-value">{{ enrollmentSlotText }}</span>
|
<span class="slot-value">{{ enrollmentSlotText }}</span>
|
||||||
</div>
|
</button>
|
||||||
|
|
||||||
<button class="notif-btn" type="button" aria-label="Уведомления" @click="$router.push('/notifications')">
|
<button class="notif-btn" type="button" aria-label="Уведомления" @click="$router.push('/notifications')">
|
||||||
<AppIcon icon="bell" :size="18" />
|
<AppIcon icon="bell" :size="18" />
|
||||||
@@ -183,6 +229,27 @@ onBeforeUnmount(() => {
|
|||||||
<button class="btn-primary" type="button" @click="closeCoinDialog">ОК</button>
|
<button class="btn-primary" type="button" @click="closeCoinDialog">ОК</button>
|
||||||
</template>
|
</template>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
|
||||||
|
<ModalDialog v-model="isLevelDialogOpen" title="Уровень студента" icon="star" size="sm">
|
||||||
|
<p>
|
||||||
|
Уровень показывает ваш прогресс в UniVerse. Он растет вместе с активностью: посещением открытых лекций,
|
||||||
|
полезными отзывами и достижениями.
|
||||||
|
</p>
|
||||||
|
<template #footer>
|
||||||
|
<button class="btn-primary" type="button" @click="closeLevelDialog">Понятно</button>
|
||||||
|
</template>
|
||||||
|
</ModalDialog>
|
||||||
|
|
||||||
|
<ModalDialog v-model="isSlotsDialogOpen" title="Слоты записи" icon="calendar" size="sm">
|
||||||
|
<p>
|
||||||
|
Слоты показывают, сколько открытых лекций уже занято в вашем текущем лимите. Когда лимит заполнен,
|
||||||
|
освободите слот отменой записи или повышайте уровень, чтобы получить больше возможностей.
|
||||||
|
</p>
|
||||||
|
<template #footer>
|
||||||
|
<button class="btn-secondary" type="button" @click="openMyLecturesFromSlotsDialog">Мои записи</button>
|
||||||
|
<button class="btn-primary" type="button" @click="closeSlotsDialog">Понятно</button>
|
||||||
|
</template>
|
||||||
|
</ModalDialog>
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -238,8 +305,10 @@ onBeforeUnmount(() => {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background: var(--color-primary-a10);
|
background: var(--color-primary-a10);
|
||||||
color: var(--color-primary-dark);
|
color: var(--color-primary-dark);
|
||||||
cursor: default;
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
transition: background-color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
||||||
}
|
}
|
||||||
.level-icon {
|
.level-icon {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
@@ -264,8 +333,21 @@ onBeforeUnmount(() => {
|
|||||||
border-radius: 20px;
|
border-radius: 20px;
|
||||||
background: var(--color-primary-a10);
|
background: var(--color-primary-a10);
|
||||||
color: var(--color-success-text);
|
color: var(--color-success-text);
|
||||||
cursor: default;
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
transition: background-color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
|
||||||
|
}
|
||||||
|
.level-chip:hover,
|
||||||
|
.slot-chip:hover {
|
||||||
|
background: var(--color-primary-a18);
|
||||||
|
border-color: var(--color-primary-a40);
|
||||||
|
}
|
||||||
|
.level-chip:focus-visible,
|
||||||
|
.slot-chip:focus-visible {
|
||||||
|
outline: 2px solid var(--color-primary-a45);
|
||||||
|
outline-offset: 2px;
|
||||||
|
box-shadow: 0 0 0 3px var(--color-primary-a12);
|
||||||
}
|
}
|
||||||
.slot-icon {
|
.slot-icon {
|
||||||
color: var(--color-primary);
|
color: var(--color-primary);
|
||||||
|
|||||||
Reference in New Issue
Block a user