Initial commit: admin dashboard for GameNo.

Vue 3 + Vite dashboard with PrimeVue, i18n, and API integration for institution management.
This commit is contained in:
2026-08-09 04:18:09 +02:00
commit 1a34c3b133
94 changed files with 12016 additions and 0 deletions
+166
View File
@@ -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>