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,162 @@
|
||||
<!-- /src/views/classes/ClassDetailView.vue -->
|
||||
<template>
|
||||
<div class="class-detail-view" v-if="classData">
|
||||
<PageHeader :title="classData.name" :subtitle="`دوره: ${classData.course?.title || '-'}`">
|
||||
<Button label="ویرایش" icon="pi pi-pencil" class="ml-2" @click="$router.push(`/classes/edit/${classId}`)" />
|
||||
<Button label="بازگشت" icon="pi pi-arrow-left" text severity="secondary" @click="$router.push('/classes')" />
|
||||
</PageHeader>
|
||||
|
||||
<div class="surface-card p-4 sm:p-5 border-round border-1 border-color shadow-sm mb-4">
|
||||
<h2 class="text-lg font-bold text-color mb-3">مشخصات کلاس</h2>
|
||||
<div class="grid">
|
||||
<div class="col-12 sm:col-4">
|
||||
<span class="text-muted text-xs block mb-1">استاد مدرس</span>
|
||||
<span class="font-bold text-color text-sm">
|
||||
{{ classData.professor ? `${classData.professor.name || ''} ${classData.professor.surname || ''}`.trim() : '—' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-4">
|
||||
<span class="text-muted text-xs block mb-1">ظرفیت کل</span>
|
||||
<span class="font-bold text-color text-sm">{{ toPersianDigits(classData.capacity || 0) }} نفر</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-4">
|
||||
<span class="text-muted text-xs block mb-1">تعداد ثبتنامیها</span>
|
||||
<span class="font-bold text-color text-sm">{{ toPersianDigits((classData.students || []).length) }} نفر</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-4">
|
||||
<span class="text-muted text-xs block mb-1">شهریه</span>
|
||||
<span class="font-bold text-color text-sm">{{ toPersianDigits((classData.tuitionFee || 0).toLocaleString('en-US')) }} تومان</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-4">
|
||||
<span class="text-muted text-xs block mb-1">بازه زمانی</span>
|
||||
<span class="font-bold text-color text-sm">
|
||||
{{ formatJalali(classData.startDate) }} — {{ formatJalali(classData.endDate) }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-4">
|
||||
<span class="text-muted text-xs block mb-1">وضعیت</span>
|
||||
<StatusTag :status="classData.isActive !== false" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface-card p-4 sm:p-5 border-round border-1 border-color shadow-sm mb-4">
|
||||
<h2 class="text-lg font-bold mb-3">دانشجویان</h2>
|
||||
<DataTable :value="classData.students || []" class="p-datatable-sm text-sm" emptyMessage="دانشجویی ثبت نشده">
|
||||
<Column header="#">
|
||||
<template #body="{ index }">{{ toPersianDigits(index + 1) }}</template>
|
||||
</Column>
|
||||
<Column header="نام">
|
||||
<template #body="{ data }">{{ data.name }} {{ data.surname }}</template>
|
||||
</Column>
|
||||
<Column header="موبایل">
|
||||
<template #body="{ data }"><span dir="ltr">{{ data.phoneNumber || '—' }}</span></template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
|
||||
<div class="surface-card p-4 sm:p-5 border-round border-1 border-color shadow-sm">
|
||||
<div class="flex align-items-center justify-content-between mb-3">
|
||||
<h2 class="text-lg font-bold m-0">جلسات کلاس</h2>
|
||||
<Button
|
||||
label="مدیریت جلسات"
|
||||
icon="pi pi-calendar"
|
||||
text
|
||||
size="small"
|
||||
@click="$router.push(`/classes/edit/${classId}`)"
|
||||
/>
|
||||
</div>
|
||||
<TableSkeleton v-if="loadingSessions" :rows="5" :columns="5" />
|
||||
<DataTable
|
||||
v-else
|
||||
:value="sessions"
|
||||
class="p-datatable-sm text-sm"
|
||||
emptyMessage="جلسهای برای این کلاس ثبت نشده است"
|
||||
>
|
||||
<Column header="#">
|
||||
<template #body="{ index }">{{ toPersianDigits(index + 1) }}</template>
|
||||
</Column>
|
||||
<Column header="موضوع">
|
||||
<template #body="{ data }">{{ data.topic || '—' }}</template>
|
||||
</Column>
|
||||
<Column header="تاریخ">
|
||||
<template #body="{ data }">{{ formatJalali(data.day || data.date) }}</template>
|
||||
</Column>
|
||||
<Column header="زمان">
|
||||
<template #body="{ data }">
|
||||
<span dir="ltr">{{ data.startTime || '—' }} – {{ data.endTime || '—' }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
<Column header="وضعیت">
|
||||
<template #body="{ data }">
|
||||
<StatusTag :status="data.status || 'scheduled'" type="session" />
|
||||
</template>
|
||||
</Column>
|
||||
</DataTable>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { classApi } from '@/api/classApi';
|
||||
import { sessionApi } from '@/api/sessionApi';
|
||||
import { usePersianDate } from '@/composables/usePersianDate';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import PageHeader from '@/components/common/PageHeader.vue';
|
||||
import StatusTag from '@/components/common/StatusTag.vue';
|
||||
import TableSkeleton from '@/components/common/TableSkeleton.vue';
|
||||
import Button from 'primevue/button';
|
||||
import DataTable from 'primevue/datatable';
|
||||
import Column from 'primevue/column';
|
||||
|
||||
const route = useRoute();
|
||||
const classId = route.params.id;
|
||||
const { toPersianDigits, formatJalali } = usePersianDate();
|
||||
const { showError } = useToast();
|
||||
|
||||
const classData = ref(null);
|
||||
const sessions = ref([]);
|
||||
const loadingSessions = ref(false);
|
||||
|
||||
const fetchClass = async () => {
|
||||
try {
|
||||
const res = await classApi.getOne(classId);
|
||||
classData.value = res.data || res;
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchSessions = async () => {
|
||||
loadingSessions.value = true;
|
||||
try {
|
||||
const res = await sessionApi.getAll({
|
||||
limit: 100,
|
||||
class: classId,
|
||||
sortBy: 'day',
|
||||
sortOrder: 'asc'
|
||||
});
|
||||
if (Array.isArray(res)) {
|
||||
sessions.value = res;
|
||||
} else if (Array.isArray(res?.data)) {
|
||||
sessions.value = res.data;
|
||||
} else if (Array.isArray(res?.items)) {
|
||||
sessions.value = res.items;
|
||||
} else {
|
||||
sessions.value = [];
|
||||
}
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
sessions.value = [];
|
||||
} finally {
|
||||
loadingSessions.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchClass();
|
||||
fetchSessions();
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user