Dev #11

Merged
serega404 merged 87 commits from dev into main 2026-05-25 03:22:55 +03:00
Showing only changes of commit 27a2811806 - Show all commits
+92 -10
View File
@@ -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);