Initial commit: admin dashboard for GameNo.
Vue 3 + Vite dashboard with PrimeVue, i18n, and API integration for institution management.
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
<!-- /src/components/layout/AppTopbar.vue -->
|
||||
<template>
|
||||
<header class="app-topbar flex align-items-center justify-content-between px-4 py-2 surface-card border-bottom-1 border-color">
|
||||
<div class="flex align-items-center gap-3">
|
||||
<Button
|
||||
icon="pi pi-bars"
|
||||
text
|
||||
rounded
|
||||
class="lg:hidden"
|
||||
@click="uiStore.toggleMobileSidebar"
|
||||
/>
|
||||
<Button
|
||||
icon="pi pi-bars"
|
||||
text
|
||||
rounded
|
||||
class="hidden lg:flex"
|
||||
@click="uiStore.toggleSidebar"
|
||||
/>
|
||||
<div class="flex align-items-center gap-2 font-bold text-xl text-primary">
|
||||
<i class="pi pi-building text-2xl"></i>
|
||||
<span>{{ $t('app.title') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex align-items-center gap-3">
|
||||
<!-- Dark/Light Theme Toggle -->
|
||||
<Button
|
||||
:icon="themeStore.isDarkMode ? 'pi pi-sun' : 'pi pi-moon'"
|
||||
text
|
||||
rounded
|
||||
:aria-label="themeStore.isDarkMode ? $t('app.lightMode') : $t('app.darkMode')"
|
||||
v-tooltip.bottom="themeStore.isDarkMode ? $t('app.lightMode') : $t('app.darkMode')"
|
||||
@click="themeStore.toggleTheme"
|
||||
/>
|
||||
|
||||
<!-- Notifications Icon -->
|
||||
<button
|
||||
type="button"
|
||||
class="notification-trigger"
|
||||
:aria-label="$t('navigation.notifications')"
|
||||
v-tooltip.bottom="$t('navigation.notifications')"
|
||||
@click="$router.push('/notifications')"
|
||||
>
|
||||
<i class="pi pi-bell"></i>
|
||||
<span v-if="notificationCount > 0" class="notification-badge">{{ notificationCount }}</span>
|
||||
</button>
|
||||
|
||||
<!-- User Menu -->
|
||||
<div class="flex align-items-center gap-2">
|
||||
<Avatar
|
||||
icon="pi pi-user"
|
||||
shape="circle"
|
||||
class="bg-primary text-white cursor-pointer"
|
||||
@click="toggleUserMenu"
|
||||
/>
|
||||
<span class="font-medium hidden sm:inline text-color cursor-pointer" @click="toggleUserMenu">
|
||||
{{ authStore.user?.name || authStore.user?.username || 'کاربر سیستم' }}
|
||||
</span>
|
||||
<Menu ref="userMenu" :model="menuItems" :popup="true" />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useAuthStore } from '@/stores/authStore';
|
||||
import { useThemeStore } from '@/stores/themeStore';
|
||||
import { useUiStore } from '@/stores/uiStore';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import Button from 'primevue/button';
|
||||
import Avatar from 'primevue/avatar';
|
||||
import Menu from 'primevue/menu';
|
||||
|
||||
const authStore = useAuthStore();
|
||||
const themeStore = useThemeStore();
|
||||
const uiStore = useUiStore();
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
|
||||
const userMenu = ref(null);
|
||||
const notificationCount = ref(3);
|
||||
|
||||
const toggleUserMenu = (event) => {
|
||||
userMenu.value.toggle(event);
|
||||
};
|
||||
|
||||
const menuItems = ref([
|
||||
{
|
||||
label: authStore.user?.name || authStore.user?.username || 'کاربر',
|
||||
items: [
|
||||
{
|
||||
label: t('app.profile'),
|
||||
icon: 'pi pi-user',
|
||||
command: () => {
|
||||
const id = authStore.user?._id || authStore.user?.id;
|
||||
if (id) {
|
||||
router.push(`/users/edit/${id}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('app.logout'),
|
||||
icon: 'pi pi-sign-out',
|
||||
command: async () => {
|
||||
await authStore.logout();
|
||||
router.push('/login');
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-topbar {
|
||||
height: 60px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.notification-trigger {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
padding: 0;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
color: var(--primary-color);
|
||||
cursor: pointer;
|
||||
transition: background-color 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--primary-color-light);
|
||||
}
|
||||
|
||||
.pi-bell {
|
||||
font-size: 1.15rem;
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.notification-badge {
|
||||
position: absolute;
|
||||
top: 0.2rem;
|
||||
inset-inline-end: 0.15rem;
|
||||
min-width: 1.05rem;
|
||||
height: 1.05rem;
|
||||
padding: 0 0.25rem;
|
||||
border-radius: 999px;
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.05rem;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 0 2px var(--surface-card, #1e293b);
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user