130 lines
3.3 KiB
Vue
130 lines
3.3 KiB
Vue
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { useRoute } from 'vue-router'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import AppIcon from '@/components/ui/AppIcon.vue'
|
|
|
|
const auth = useAuthStore()
|
|
const route = useRoute()
|
|
|
|
interface NavItem {
|
|
label: string
|
|
icon: string
|
|
to: string
|
|
roles: string[]
|
|
}
|
|
|
|
const navItems: NavItem[] = [
|
|
{ label: 'Главная', icon: 'home', to: '/', roles: ['student'] },
|
|
{ label: 'Каталог', icon: 'books', to: '/catalog', roles: ['student'] },
|
|
{ label: 'Мои записи', icon: 'clipboard-list', to: '/my-lectures', roles: ['student'] },
|
|
// Teacher
|
|
{ label: 'Дашборд', icon: 'chart-bar', to: '/teacher', roles: ['teacher'] },
|
|
{ label: 'Лекции', icon: 'book-2', to: '/teacher/lectures', roles: ['teacher'] },
|
|
{ label: 'Аналитика', icon: 'chart-line', to: '/teacher/analytics', roles: ['teacher'] },
|
|
// Admin
|
|
{ label: 'Дашборд', icon: 'shield', to: '/admin', roles: ['admin'] },
|
|
{ label: 'Пользователи', icon: 'users', to: '/admin/users', roles: ['admin'] },
|
|
{ label: 'Лекции', icon: 'books', to: '/admin/lectures', roles: ['admin'] },
|
|
{ label: 'Отзывы', icon: 'message-circle', to: '/admin/reviews', roles: ['admin'] },
|
|
]
|
|
|
|
const visible = computed(() =>
|
|
navItems.filter((n) => auth.user && n.roles.includes(auth.user.activeRole)),
|
|
)
|
|
|
|
function isActive(to: string) {
|
|
if (to === '/' || to === '/teacher' || to === '/admin') return route.path === to
|
|
return route.path.startsWith(to)
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<aside class="sidebar">
|
|
<nav class="sidebar-nav">
|
|
<RouterLink
|
|
v-for="item in visible"
|
|
:key="item.to + item.label"
|
|
:to="item.to"
|
|
class="nav-item"
|
|
:class="{ active: isActive(item.to) }"
|
|
>
|
|
<AppIcon class="nav-icon" :icon="item.icon" :size="17" />
|
|
<span class="nav-label">{{ item.label }}</span>
|
|
</RouterLink>
|
|
</nav>
|
|
|
|
<div class="sidebar-footer">
|
|
<span class="copyright">© 2026 UniVerse</span>
|
|
</div>
|
|
</aside>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.sidebar {
|
|
position: fixed;
|
|
top: var(--topbar-height);
|
|
left: 0;
|
|
bottom: 0;
|
|
width: var(--sidebar-width);
|
|
background: var(--color-white-glass);
|
|
backdrop-filter: blur(16px);
|
|
-webkit-backdrop-filter: blur(16px);
|
|
border-right: 1px solid var(--color-border-glass);
|
|
display: flex;
|
|
flex-direction: column;
|
|
z-index: 90;
|
|
padding: 16px 0;
|
|
}
|
|
.sidebar-nav {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 2px;
|
|
padding: 0 10px;
|
|
overflow-y: auto;
|
|
}
|
|
.nav-item {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 10px 12px;
|
|
border-radius: var(--radius-sm);
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--color-text-secondary);
|
|
text-decoration: none;
|
|
transition: all 0.2s;
|
|
}
|
|
.nav-item:hover {
|
|
background: var(--color-primary-a10);
|
|
color: var(--color-primary-dark);
|
|
}
|
|
.nav-item.active {
|
|
background: var(--gradient-nav-active);
|
|
color: var(--color-primary-dark);
|
|
font-weight: 700;
|
|
box-shadow: 0 2px 8px var(--color-primary-a12);
|
|
}
|
|
.nav-icon {
|
|
flex-shrink: 0;
|
|
color: currentColor;
|
|
}
|
|
.sidebar-footer {
|
|
padding: 10px 18px 8px;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
.copyright {
|
|
color: var(--color-text-secondary);
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.sidebar {
|
|
display: none;
|
|
}
|
|
}
|
|
</style>
|