fix: education
Deploy banu-front / deploy (push) Successful in 1m21s

This commit is contained in:
sajjadtalkhabi
2026-05-28 19:49:00 +03:30
parent 9660e3fe8f
commit d42b7aaa7b
27 changed files with 362 additions and 219 deletions
+4 -3
View File
@@ -22,9 +22,10 @@ export const apiAddAdminExamQuestion = (examId, payload) =>
export const apiAddAdminQuestionOption = (questionId, payload) =>
http.post(buildUrl(endpoints.addQuestionOption, { questionId }), payload)
// Mock-only — backend has no /exams/:id/participants endpoint yet.
export const apiGetAdminExamParticipants = (examId, params) =>
http.get(buildUrl(endpoints.getExamParticipants, { examId }), { params })
// GET /exams/:examId/attempts — paginated list, each row is one attempt with
// the attempting user embedded (multiple rows per user possible).
export const apiGetAdminExamAttempts = (examId, params) =>
http.get(buildUrl(endpoints.getExamAttempts, { examId }), { params })
export const apiShowAdminExamParticipant = (examId, participantId) =>
http.get(buildUrl(endpoints.showExamParticipant, { examId, participantId }))
+4 -2
View File
@@ -122,8 +122,10 @@ export const endpoints = {
// Sessions — attendance roster
getSessionsAttendance: '/admin/sessions/:sessionId/attendances',
// Exams — participants only (backend has /exams/:id but no participants endpoints)
getExamParticipants: '/admin/exams/:examId/participants',
// Exams — admin/teacher view of attempts. Each row is one attempt with the
// attempting user embedded; multiple rows per user are possible. The
// single-attempt detail endpoint is still mock-only.
getExamAttempts: '/exams/:examId/attempts',
showExamParticipant: '/admin/exams/:examId/participants/:participantId',
// Homeworks — list, show, submissions list, show submission
+65 -9
View File
@@ -77,13 +77,69 @@ export const adminExams = [
},
]
// FE-mock-only — no backend endpoint for exam participants.
// Mirrors GET /exams/:examId/attempts. Each row is one attempt with the user
// embedded; multiple rows per user are possible.
export const examAttempts = new Map([
[
201,
[
{
id: 14,
examId: 201,
score: 85,
isPassed: true,
startedAt: '2026-04-12T15:00:00+03:30',
submittedAt: '2026-04-12T15:30:00+03:30',
deadlineAt: '2026-04-12T15:30:00+03:30',
user: {
id: 100,
name: 'فاطمه رضایی',
email: 'fatemeh.r@example.com',
phone: '+989121234567',
provinceId: 8,
cityId: 47,
province: { id: 8, name: 'تهران' },
city: { id: 47, provinceId: 8, name: 'تهران' },
roles: ['student'],
avatarUrl: null,
avatarDownloadUrl: null,
createdAt: '2026-02-01T10:00:00+00:00',
},
},
{
id: 15,
examId: 201,
score: 42,
isPassed: false,
startedAt: '2026-04-13T09:00:00+03:30',
submittedAt: '2026-04-13T09:25:00+03:30',
deadlineAt: '2026-04-13T09:30:00+03:30',
user: {
id: 102,
name: 'مریم احمدی',
email: 'maryam.a@example.com',
phone: '+989121111111',
provinceId: 15,
cityId: 90,
province: { id: 15, name: 'اصفهان' },
city: { id: 90, provinceId: 15, name: 'اصفهان' },
roles: ['student'],
avatarUrl: null,
avatarDownloadUrl: null,
createdAt: '2026-02-03T10:00:00+00:00',
},
},
],
],
])
// Kept for the FE-mock-only single-participant detail endpoint (no backend yet).
export const examParticipants = new Map([
[
201,
[
{
id: 1,
id: 100,
userId: 100,
firstName: 'فاطمه',
lastName: 'رضایی',
@@ -91,21 +147,21 @@ export const examParticipants = new Map([
avatarUrl: '',
address: { province: { name: 'تهران' }, city: { name: 'تهران' } },
location: 'تهران، تهران',
submittedAt: '2026-05-05T10:30:00.000Z',
submittedAt: '2026-04-12T15:30:00+03:30',
attempts: [
{
id: 1,
id: 14,
title: 'تلاش اول',
courseTitle: 'اصول اخلاق اسلامی',
sessionTitle: 'مقدمه‌ای بر اخلاق اسلامی',
date: '2026-05-05T10:30:00.000Z',
date: '2026-04-12T15:30:00+03:30',
score: 17,
scoreTone: 'good',
},
],
},
{
id: 2,
id: 102,
userId: 102,
firstName: 'مریم',
lastName: 'احمدی',
@@ -113,14 +169,14 @@ export const examParticipants = new Map([
avatarUrl: '',
address: { province: { name: 'اصفهان' }, city: { name: 'اصفهان' } },
location: 'اصفهان، اصفهان',
submittedAt: '2026-05-06T11:00:00.000Z',
submittedAt: '2026-04-13T09:25:00+03:30',
attempts: [
{
id: 2,
id: 15,
title: 'تلاش اول',
courseTitle: 'اصول اخلاق اسلامی',
sessionTitle: 'مقدمه‌ای بر اخلاق اسلامی',
date: '2026-05-06T11:00:00.000Z',
date: '2026-04-13T09:25:00+03:30',
score: 9,
scoreTone: 'bad',
},
+6 -5
View File
@@ -2,7 +2,7 @@ import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
import { adminCourses } from '@/services/mock/fixtures/admin-courses'
import { adminSessions } from '@/services/mock/fixtures/admin-sessions'
import { adminExams, examParticipants } from '@/services/mock/fixtures/admin-exams'
import { adminExams, examAttempts, examParticipants } from '@/services/mock/fixtures/admin-exams'
import {
filterDateRange,
filterItems,
@@ -151,12 +151,13 @@ register('POST', endpoints.addQuestionOption, ({ params, data }) => {
return { success: false, message: 'Question not found.', data: null }
})
// FE-mock-only.
register('GET', endpoints.getExamParticipants, ({ params, query }) => {
const list = examParticipants.get(Number(params.examId)) || []
return paginate(list, query)
register('GET', endpoints.getExamAttempts, ({ params, query }) => {
const list = examAttempts.get(Number(params.examId)) || []
const { data: items, meta } = paginate(list, query)
return { success: true, message: 'OK', data: { items, meta } }
})
// FE-mock-only — single-participant detail not yet on the backend.
register('GET', endpoints.showExamParticipant, ({ params }) => {
const list = examParticipants.get(Number(params.examId)) || []
const found = list.find((p) => String(p.id) === String(params.participantId))
+10 -6
View File
@@ -6,7 +6,7 @@ import {
apiAddAdminQuestionOption,
apiDeleteAdminExam,
apiGetAdminExams,
apiGetAdminExamParticipants,
apiGetAdminExamAttempts,
apiShowAdminExam,
apiShowAdminExamParticipant,
apiUpdateAdminExam,
@@ -16,10 +16,10 @@ export const adminExamsKeys = {
all: ['admin', 'exams'],
list: (filters, pagination) => ['admin', 'exams', 'list', filters, pagination],
detail: (id) => ['admin', 'exams', 'detail', id],
participants: (examId, filters, pagination) => [
attempts: (examId, filters, pagination) => [
'admin',
'exams',
'participants',
'attempts',
examId,
filters,
pagination,
@@ -53,14 +53,18 @@ export const useAdminExamQuery = (idRef, options = {}) =>
...options,
})
export const useAdminExamParticipantsQuery = (examIdRef, filtersRef, paginationRef, options = {}) =>
export const useAdminExamAttemptsQuery = (examIdRef, filtersRef, paginationRef, options = {}) =>
useQuery({
queryKey: ['admin', 'exams', 'participants', examIdRef, filtersRef, paginationRef],
queryKey: ['admin', 'exams', 'attempts', examIdRef, filtersRef, paginationRef],
queryFn: () =>
apiGetAdminExamParticipants(examIdRef.value, {
apiGetAdminExamAttempts(examIdRef.value, {
...cleanFilters(filtersRef?.value || {}),
...paginationRef?.value,
}),
select: (response) => ({
data: response?.data?.items ?? response?.data ?? [],
meta: response?.data?.meta ?? response?.meta,
}),
...options,
})