Improve dashboard and navigation access to attendance, group the right-side menu by category, and let each account save default theme and font size.
153 lines
4.5 KiB
Vue
153 lines
4.5 KiB
Vue
<!-- /src/components/layout/AppSidebar.vue -->
|
|
<template>
|
|
<div class="app-sidebar p-3 surface-card border-left-1 border-color flex flex-column h-full flex-shrink-0">
|
|
<div class="logo flex align-items-center gap-3 px-2 py-3 mb-3 border-bottom-1 border-color">
|
|
<i class="pi pi-graduation-cap text-primary text-3xl"></i>
|
|
<span class="font-bold text-xl text-color">گام نو</span>
|
|
</div>
|
|
|
|
<div class="menu-list flex-grow-1 overflow-y-auto">
|
|
<div v-for="group in menuGroups" :key="group.key" class="menu-group mb-3">
|
|
<div class="menu-group-label px-3 py-2 text-xs font-bold text-muted uppercase">
|
|
{{ group.label }}
|
|
</div>
|
|
<router-link
|
|
v-for="item in group.items"
|
|
:key="item.to"
|
|
:to="item.to"
|
|
class="menu-item flex align-items-center gap-3 px-3 py-2 border-round text-color mb-1 transition-colors"
|
|
:class="{ 'active-item': isMenuActive(item.to) }"
|
|
>
|
|
<i :class="item.icon" class="text-lg"></i>
|
|
<span class="font-medium text-sm">{{ item.label }}</span>
|
|
</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue';
|
|
import { useRoute } from 'vue-router';
|
|
import { usePermissionStore } from '@/stores/permissionStore';
|
|
import { PERMISSIONS } from '@/constants/permissions';
|
|
|
|
const route = useRoute();
|
|
const permissionStore = usePermissionStore();
|
|
|
|
const menuGroups = computed(() => {
|
|
const groups = [
|
|
{
|
|
key: 'main',
|
|
label: 'اصلی',
|
|
items: [
|
|
{ label: 'داشبورد اصلی', icon: 'pi pi-home', to: '/' }
|
|
]
|
|
},
|
|
{
|
|
key: 'education',
|
|
label: 'آموزش',
|
|
items: [
|
|
{ label: 'مدیریت کاربران', icon: 'pi pi-users', to: '/users' },
|
|
{ label: 'مدیریت اساتید', icon: 'pi pi-id-card', to: '/professors' },
|
|
{ label: 'دورههای آموزشی', icon: 'pi pi-book', to: '/courses' },
|
|
{ label: 'کلاسها', icon: 'pi pi-desktop', to: '/classes' },
|
|
{ label: 'جلسات آموزشی', icon: 'pi pi-calendar', to: '/sessions' }
|
|
]
|
|
},
|
|
{
|
|
key: 'finance',
|
|
label: 'مالی و ارتباطات',
|
|
items: [
|
|
{ label: 'امور مالی و پرداختها', icon: 'pi pi-wallet', to: '/payments' },
|
|
{ label: 'اطلاعیهها', icon: 'pi pi-bell', to: '/notifications' },
|
|
{ label: 'درخواستهای تماس', icon: 'pi pi-comments', to: '/contact-inquiries' }
|
|
]
|
|
},
|
|
{
|
|
key: 'system',
|
|
label: 'مدیریت سیستم',
|
|
items: [
|
|
{ label: 'گزارش فعالیتها', icon: 'pi pi-history', to: '/logs', permission: PERMISSIONS.LOGS_READ },
|
|
{ label: 'نقشها و دسترسیها', icon: 'pi pi-shield', to: '/roles' }
|
|
]
|
|
},
|
|
{
|
|
key: 'account',
|
|
label: 'حساب کاربری',
|
|
items: [
|
|
{ label: 'رابط کاربری', icon: 'pi pi-palette', to: '/interface' }
|
|
]
|
|
}
|
|
];
|
|
|
|
if (permissionStore.hasPermission(PERMISSIONS.SESSIONS_ATTENDANCE)) {
|
|
const educationGroup = groups.find((g) => g.key === 'education');
|
|
educationGroup.items.push({
|
|
label: 'حضور و غیاب',
|
|
icon: 'pi pi-check-square',
|
|
to: '/attendance'
|
|
});
|
|
}
|
|
|
|
if (permissionStore.roleName === 'SuperAdmin') {
|
|
const systemGroup = groups.find((g) => g.key === 'system');
|
|
systemGroup.items.push({ label: 'تنظیمات', icon: 'pi pi-cog', to: '/settings' });
|
|
}
|
|
|
|
return groups
|
|
.map((group) => ({
|
|
...group,
|
|
items: group.items.filter((item) => !item.permission || permissionStore.hasPermission(item.permission))
|
|
}))
|
|
.filter((group) => group.items.length > 0);
|
|
});
|
|
|
|
function isMenuActive(to) {
|
|
if (to === '/') {
|
|
return route.path === '/';
|
|
}
|
|
if (to === '/attendance') {
|
|
return route.path === '/attendance' || route.path.startsWith('/sessions/attendance/');
|
|
}
|
|
return route.path === to || route.path.startsWith(`${to}/`);
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.app-sidebar {
|
|
width: 240px;
|
|
min-width: 240px;
|
|
max-width: 240px;
|
|
flex-shrink: 0;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.menu-group-label {
|
|
letter-spacing: 0.04em;
|
|
opacity: 0.85;
|
|
}
|
|
|
|
.menu-item {
|
|
color: var(--text-secondary);
|
|
text-decoration: none;
|
|
white-space: nowrap;
|
|
flex-shrink: 0;
|
|
|
|
&:hover {
|
|
background-color: var(--surface-hover);
|
|
color: var(--text-primary);
|
|
}
|
|
|
|
&.active-item {
|
|
background-color: var(--primary-color-light);
|
|
color: var(--primary-color);
|
|
font-weight: 700;
|
|
}
|
|
|
|
span {
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
</style>
|