fix: resolve course/class fields, surname i18n, optional nationalId, auto class end date, login styling, and mobile sidebar navigation
This commit is contained in:
@@ -1,39 +1,109 @@
|
||||
<!-- /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>
|
||||
<!-- Desktop Sidebar -->
|
||||
<aside
|
||||
class="app-sidebar desktop-sidebar surface-card border-left-1 border-color flex flex-column h-full flex-shrink-0"
|
||||
:class="{ 'sidebar-collapsed': !uiStore.isSidebarOpen }"
|
||||
>
|
||||
<div class="sidebar-inner flex flex-column h-full p-3">
|
||||
<div class="logo flex align-items-center gap-3 px-2 py-3 mb-3 border-bottom-1 border-color flex-shrink-0">
|
||||
<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 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>
|
||||
<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>
|
||||
</aside>
|
||||
|
||||
<!-- Mobile Drawer & Backdrop -->
|
||||
<Teleport to="body">
|
||||
<Transition name="sidebar-fade">
|
||||
<div
|
||||
v-if="uiStore.isMobileSidebarOpen"
|
||||
class="mobile-sidebar-backdrop"
|
||||
@click="uiStore.closeMobileSidebar"
|
||||
/>
|
||||
</Transition>
|
||||
|
||||
<Transition name="sidebar-slide">
|
||||
<div
|
||||
v-if="uiStore.isMobileSidebarOpen"
|
||||
class="mobile-sidebar-drawer surface-card flex flex-column p-3 shadow-8 border-left-1 border-color"
|
||||
dir="rtl"
|
||||
>
|
||||
<div class="flex align-items-center justify-content-between px-2 py-3 mb-3 border-bottom-1 border-color flex-shrink-0">
|
||||
<div class="logo flex align-items-center gap-2">
|
||||
<i class="pi pi-graduation-cap text-primary text-2xl"></i>
|
||||
<span class="font-bold text-lg text-color">گام نو</span>
|
||||
</div>
|
||||
<Button
|
||||
icon="pi pi-times"
|
||||
text
|
||||
rounded
|
||||
severity="secondary"
|
||||
size="small"
|
||||
@click="uiStore.closeMobileSidebar"
|
||||
/>
|
||||
</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) }"
|
||||
@click="uiStore.closeMobileSidebar"
|
||||
>
|
||||
<i :class="item.icon" class="text-lg"></i>
|
||||
<span class="font-medium text-sm">{{ item.label }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { usePermissionStore } from '@/stores/permissionStore';
|
||||
import { useUiStore } from '@/stores/uiStore';
|
||||
import { PERMISSIONS } from '@/constants/permissions';
|
||||
import Button from 'primevue/button';
|
||||
|
||||
const route = useRoute();
|
||||
const permissionStore = usePermissionStore();
|
||||
const uiStore = useUiStore();
|
||||
|
||||
// Automatically close mobile sidebar on navigation
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
uiStore.closeMobileSidebar();
|
||||
}
|
||||
);
|
||||
|
||||
const menuGroups = computed(() => {
|
||||
const groups = [
|
||||
@@ -124,6 +194,72 @@ function isMenuActive(to) {
|
||||
max-width: 240px;
|
||||
flex-shrink: 0;
|
||||
min-height: 100vh;
|
||||
transition: width 0.25s ease, min-width 0.25s ease, max-width 0.25s ease, opacity 0.2s ease, padding 0.25s ease;
|
||||
overflow: hidden;
|
||||
|
||||
.sidebar-inner {
|
||||
width: 240px;
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
&.sidebar-collapsed {
|
||||
width: 0 !important;
|
||||
min-width: 0 !important;
|
||||
max-width: 0 !important;
|
||||
padding: 0 !important;
|
||||
border-left: none !important;
|
||||
border-right: none !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 991px) {
|
||||
.desktop-sidebar {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-sidebar-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(15, 23, 42, 0.65);
|
||||
backdrop-filter: blur(3px);
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
.mobile-sidebar-drawer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 280px;
|
||||
max-width: 85vw;
|
||||
height: 100vh;
|
||||
height: 100dvh;
|
||||
z-index: 1101;
|
||||
background: var(--surface-card);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.sidebar-slide-enter-active,
|
||||
.sidebar-slide-leave-active {
|
||||
transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.sidebar-slide-enter-from,
|
||||
.sidebar-slide-leave-to {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
.sidebar-fade-enter-active,
|
||||
.sidebar-fade-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.sidebar-fade-enter-from,
|
||||
.sidebar-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.menu-group-label {
|
||||
|
||||
Reference in New Issue
Block a user