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
+130
View File
@@ -0,0 +1,130 @@
<!-- /src/views/professors/ProfessorListView.vue -->
<template>
<div class="professor-list-view">
<PageHeader :title="$t('professors.title')" :subtitle="$t('professors.subtitle')">
<PermissionGate permission="professors:create">
<Button :label="$t('professors.addProfessor')" icon="pi pi-plus" @click="$router.push('/professors/create')" />
</PermissionGate>
</PageHeader>
<DataTableWrapper
:items="items"
:totalCount="totalCount"
:page="queryParams.page"
:limit="queryParams.limit"
:sortBy="queryParams.sortBy"
:sortOrder="queryParams.sortOrder"
:loading="isLoading"
@page-change="onPageChange"
@sort-change="onSort"
@search-change="onSearch"
>
<Column field="name" header="نام و نام خانوادگی" sortable>
<template #body="{ data }">
<span class="font-bold text-color">{{ data.name }} {{ data.surname }}</span>
</template>
</Column>
<Column field="specialization" header="تخصص">
<template #body="{ data }">
<Tag :value="data.specialization || 'عمومی'" severity="secondary" />
</template>
</Column>
<Column field="phoneNumber" header="شماره همراه">
<template #body="{ data }">
{{ toPersianDigits(data.phoneNumber || data.phone) || '-' }}
</template>
</Column>
<Column field="email" header="ایمیل">
<template #body="{ data }">
{{ data.email || '-' }}
</template>
</Column>
<Column field="activeCoursesCount" header="دوره‌های فعال">
<template #body="{ data }">
{{ toPersianDigits(data.activeCoursesCount || data.courses?.length || 0) }}
</template>
</Column>
<Column header="عملیات" style="width: 110px">
<template #body="{ data }">
<div class="flex align-items-center gap-1">
<PermissionGate permission="professors:update">
<Button icon="pi pi-pencil" text rounded size="small" severity="warning" v-tooltip.top="'ویرایش'" @click="$router.push(`/professors/edit/${data._id || data.id}`)" />
</PermissionGate>
<PermissionGate permission="professors:delete">
<Button icon="pi pi-trash" text rounded size="small" severity="danger" v-tooltip.top="'حذف'" @click="confirmDelete(data)" />
</PermissionGate>
</div>
</template>
</Column>
</DataTableWrapper>
<ConfirmDeleteDialog
v-model="deleteDialogVisible"
:loading="isDeleting"
@confirm="handleDelete"
/>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useDataTable } from '@/composables/useDataTable';
import { usePersianDate } from '@/composables/usePersianDate';
import { useToast } from '@/composables/useToast';
import { professorApi } from '@/api/professorApi';
import PageHeader from '@/components/common/PageHeader.vue';
import DataTableWrapper from '@/components/common/DataTableWrapper.vue';
import ConfirmDeleteDialog from '@/components/common/ConfirmDeleteDialog.vue';
import PermissionGate from '@/components/common/PermissionGate.vue';
import Button from 'primevue/button';
import Column from 'primevue/column';
import Tag from 'primevue/tag';
const { toPersianDigits } = usePersianDate();
const { showSuccess, showError } = useToast();
const {
items,
totalCount,
isLoading,
queryParams,
loadData,
onPageChange,
onSort,
onSearch
} = useDataTable(professorApi.getAll);
const deleteDialogVisible = ref(false);
const selectedProf = ref(null);
const isDeleting = ref(false);
const confirmDelete = (prof) => {
selectedProf.value = prof;
deleteDialogVisible.value = true;
};
const handleDelete = async () => {
if (!selectedProf.value) return;
isDeleting.value = true;
try {
await professorApi.delete(selectedProf.value._id || selectedProf.value.id);
showSuccess('استاد با موفقیت حذف شد');
deleteDialogVisible.value = false;
loadData();
} catch (err) {
showError(err);
} finally {
isDeleting.value = false;
}
};
onMounted(() => {
loadData();
});
</script>