Add attendance hub, categorized sidebar, and UI preferences page.

Improve dashboard and navigation access to attendance, group the right-side menu by category, and let each account save default theme and font size.
This commit is contained in:
2026-08-16 07:28:58 +03:30
parent 9c4182ea5a
commit ba509734ae
12 changed files with 731 additions and 39 deletions
+92 -28
View File
@@ -7,16 +7,21 @@
</div>
<div class="menu-list flex-grow-1 overflow-y-auto">
<router-link
v-for="item in menuItems"
: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 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>
@@ -25,36 +30,86 @@
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 baseMenuItems = [
{ label: 'داشبورد اصلی', icon: 'pi pi-home', to: '/' },
{ 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' },
{ label: 'امور مالی و پرداخت‌ها', icon: 'pi pi-wallet', to: '/payments' },
{ label: 'اطلاعیه‌ها', icon: 'pi pi-bell', to: '/notifications' },
{ label: 'درخواست‌های تماس', icon: 'pi pi-comments', to: '/contact-inquiries' },
{ label: 'گزارش فعالیت‌ها', icon: 'pi pi-history', to: '/logs' },
{ label: 'نقش‌ها و دسترسی‌ها', icon: 'pi pi-shield', to: '/roles' }
];
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' }
]
}
];
const menuItems = computed(() => {
const items = [...baseMenuItems];
if (permissionStore.roleName === 'SuperAdmin') {
items.push({ label: 'تنظیمات', icon: 'pi pi-cog', to: '/settings' });
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'
});
}
return items;
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>
@@ -67,20 +122,29 @@ function isMenuActive(to) {
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;
}
+7
View File
@@ -125,6 +125,13 @@ const menuItems = ref([
router.push('/profile');
}
},
{
label: 'رابط کاربری',
icon: 'pi pi-palette',
command: () => {
router.push('/interface');
}
},
{
label: t('app.logout'),
icon: 'pi pi-sign-out',