fix: user changes

This commit is contained in:
sajjadtalkhabi
2026-05-28 13:34:30 +03:30
parent cb86920015
commit 7ed6ec9ae1
46 changed files with 328 additions and 1114 deletions
-21
View File
@@ -11,24 +11,3 @@ export const apiUpdateAdminCourse = (id, payload) =>
http.patch(buildUrl(endpoints.updateCourse, { id }), payload)
export const apiDeleteAdminCourse = (id) => http.delete(buildUrl(endpoints.deleteCourse, { id }))
export const apiGetAdminCourseStudents = (courseId, params) =>
http.get(buildUrl(endpoints.listCourseStudents, { courseId }), { params })
export const apiGetCourseAttendees = (courseId, params) =>
http.get(buildUrl(endpoints.getCourseAttendees, { courseId }), { params })
export const apiAddAdminCourseStudent = (courseId, payload) =>
http.post(buildUrl(endpoints.addCourseStudent, { courseId }), payload)
export const apiRemoveAdminCourseStudent = (courseId, userId) =>
http.delete(buildUrl(endpoints.removeCourseStudent, { courseId, userId }))
export const apiGetAdminCourseSessions = (courseId, params) =>
http.get(buildUrl(endpoints.listCourseSessions, { courseId }), { params })
export const apiAttachAdminCourseSession = (courseId, payload) =>
http.post(buildUrl(endpoints.attachCourseSession, { courseId }), payload)
export const apiDetachAdminCourseSession = (courseId, sessionId) =>
http.delete(buildUrl(endpoints.detachCourseSession, { courseId, sessionId }))
+2 -2
View File
@@ -17,8 +17,8 @@ export const apiCloneAdminTerm = (id) => http.post(buildUrl(endpoints.cloneTerm,
export const apiGetAdminTermStudents = (termId, params) =>
http.get(buildUrl(endpoints.listUserTerm, { termId }), { params })
export const apiGetTermAttendees = (termId, params) =>
http.get(buildUrl(endpoints.getTermAttendees, { termId }), { params })
export const apiGetAdminTermEnrollees = (termId, params) =>
http.get(buildUrl(endpoints.getTermEnrollees, { termId }), { params })
export const apiAddAdminTermStudents = (termId, payload) =>
http.post(buildUrl(endpoints.addUserTerm, { termId }), payload)
+8 -21
View File
@@ -23,16 +23,12 @@ export const endpoints = {
citiesList: '/provinces/:provinceId/cities',
// ─── Student-facing ────────────────────────────────────────────────────────
getStudentCourses: '/student/courses',
showStudentCourse: '/student/courses/:id',
getStudentTerms: '/student/terms',
showStudentTerm: '/student/terms/:id',
showStudentLesson: '/student/lessons/:id',
showStudentSession: '/student/sessions/:id',
submitStudentSessionHomework: '/student/sessions/:id/homework',
showStudentExam: '/student/exams/:id',
startStudentExamAttempt: '/student/exams/:id/attempts',
submitStudentExamAttempt: '/student/exams/:id/submit',
// Courses, sessions, exams use the shared backend-aligned endpoints below
// (/courses, /sessions/:id, /exams/:id, /exams/:id/submit) — the backend
// infers role from the auth token and scopes the response. Terms have a
// dedicated student route (returns the membership row with the nested term).
getStudentTerms: '/student/my-terms',
showStudentTerm: '/student/my-terms/:id',
getMissionaryProfile: '/student/missionary/profile',
getMissionaryRequests: '/student/missionary/requests',
@@ -71,7 +67,7 @@ export const endpoints = {
showTerm: '/terms/:id',
updateTerm: '/terms/:id',
deleteTerm: '/terms/:id',
getTermAttendees: '/terms/:termId/attendees',
getTermEnrollees: '/admin/terms/:termId/enrollees',
// ─── Backend-aligned: Courses ──────────────────────────────────────────────
getCoursesList: '/courses',
@@ -79,7 +75,6 @@ export const endpoints = {
showCourse: '/courses/:id',
updateCourse: '/courses/:id',
deleteCourse: '/courses/:id',
getCourseAttendees: '/courses/:courseId/attendees',
// ─── Backend-aligned: Sessions ─────────────────────────────────────────────
getSessionsList: '/sessions',
@@ -120,21 +115,13 @@ export const endpoints = {
// Terms — clone + status + students-in-term + courses-in-term
cloneTerm: '/admin/terms/:id/clone',
listUserTerm: '/admin/terms/:termId/students',
addUserTerm: '/admin/terms/:termId/students',
addUserTerm: '/admin/terms/:termId/enrollees',
removeUserTerm: '/admin/terms/:termId/students/:userId',
changeLeaveStatus: '/admin/terms/:termId/students/:userId/toggle-leave',
listCourseTerm: '/admin/terms/:termId/courses',
addCourseTerm: '/admin/terms/:termId/courses',
removeCourseTerm: '/admin/terms/:termId/courses/:courseId',
// Courses — students-in-course + attach/detach sessions (backend uses 1:N via session.course_id)
listCourseStudents: '/admin/courses/:courseId/students',
addCourseStudent: '/admin/courses/:courseId/students',
removeCourseStudent: '/admin/courses/:courseId/students/:userId',
listCourseSessions: '/admin/courses/:courseId/sessions',
attachCourseSession: '/admin/courses/:courseId/sessions',
detachCourseSession: '/admin/courses/:courseId/sessions/:sessionId',
// Sessions — attendance roster
getSessionsAttendance: '/admin/sessions/:sessionId/attendances',
+2 -2
View File
@@ -1,6 +1,6 @@
import { http } from '@/services/api/http'
import { buildUrl, endpoints } from '@/services/api/endpoints'
export const apiGetStudentCourses = (params) => http.get(endpoints.getStudentCourses, { params })
export const apiGetStudentCourses = (params) => http.get(endpoints.getCoursesList, { params })
export const apiShowStudentCourse = (id) => http.get(buildUrl(endpoints.showStudentCourse, { id }))
export const apiShowStudentCourse = (id) => http.get(buildUrl(endpoints.showCourse, { id }))
+2 -5
View File
@@ -1,10 +1,7 @@
import { http } from '@/services/api/http'
import { buildUrl, endpoints } from '@/services/api/endpoints'
export const apiShowStudentExam = (id) => http.get(buildUrl(endpoints.showStudentExam, { id }))
export const apiStartStudentExamAttempt = (id) =>
http.post(buildUrl(endpoints.startStudentExamAttempt, { id }))
export const apiShowStudentExam = (id) => http.get(buildUrl(endpoints.showExam, { id }))
export const apiSubmitStudentExamAttempt = (id, payload) =>
http.post(buildUrl(endpoints.submitStudentExamAttempt, { id }), payload)
http.post(buildUrl(endpoints.submitExam, { examId: id }), payload)
-4
View File
@@ -1,4 +0,0 @@
import { http } from '@/services/api/http'
import { buildUrl, endpoints } from '@/services/api/endpoints'
export const apiShowStudentLesson = (id) => http.get(buildUrl(endpoints.showStudentLesson, { id }))
+3 -4
View File
@@ -1,8 +1,7 @@
import { http } from '@/services/api/http'
import { buildUrl, endpoints } from '@/services/api/endpoints'
export const apiShowStudentSession = (id) =>
http.get(buildUrl(endpoints.showStudentSession, { id }))
export const apiShowStudentSession = (id) => http.get(buildUrl(endpoints.showSession, { id }))
export const apiSubmitStudentSessionHomework = (id, formData) =>
http.post(buildUrl(endpoints.submitStudentSessionHomework, { id }), formData)
export const apiSubmitStudentHomework = (homeworkId, payload) =>
http.post(buildUrl(endpoints.submitHomework, { homeworkId }), payload)
@@ -1,24 +0,0 @@
import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
import { findOrThrow, paginate } from '@/services/mock/helpers'
import { studentCourses } from '@/services/mock/fixtures/student-courses'
const filterByStatus = (status) => {
if (!status) return studentCourses
if (status === 'current') {
return studentCourses.filter((c) => ['watching', 'waitForExam'].includes(c.status))
}
if (status === 'ended') {
return studentCourses.filter((c) => ['completed', 'failed'].includes(c.status))
}
return studentCourses.filter((c) => c.status === status)
}
register('GET', endpoints.getStudentCourses, ({ query }) => {
const list = filterByStatus(query.status)
return paginate(list, query)
})
register('GET', endpoints.showStudentCourse, ({ params }) => ({
data: findOrThrow(studentCourses, params.id),
}))
-27
View File
@@ -1,27 +0,0 @@
import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
import { studentExams } from '@/services/mock/fixtures/student-exams'
register('GET', endpoints.showStudentExam, ({ params }) => {
const exam = studentExams.find((e) => String(e.id) === String(params.id))
if (exam) return { data: exam }
const fallback = studentExams[1]
return { data: { ...fallback, id: params.id } }
})
register('POST', endpoints.startStudentExamAttempt, ({ params }) => ({
data: { id: params.id, status: 'started', message: 'آزمون آغاز شد' },
}))
register('POST', endpoints.submitStudentExamAttempt, ({ params, data }) => {
const answers = data?.answers || {}
const total = Object.keys(answers).length
return {
data: {
id: params.id,
submitted: true,
score: total > 0 ? (12 + Math.random() * 6).toFixed(2) : 0,
message: 'پاسخ‌های شما با موفقیت ثبت شد.',
},
}
})
@@ -1,11 +0,0 @@
import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
import { studentLessons } from '@/services/mock/fixtures/student-lessons'
register('GET', endpoints.showStudentLesson, ({ params }) => {
const lesson = studentLessons.find((l) => String(l.id) === String(params.id))
if (lesson) return { data: lesson }
// fallback so any term/lesson combo resolves
const fallback = studentLessons[0]
return { data: { ...fallback, id: params.id } }
})
@@ -1,14 +0,0 @@
import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
import { studentSessions } from '@/services/mock/fixtures/student-sessions'
register('GET', endpoints.showStudentSession, ({ params }) => {
const session = studentSessions.find((s) => String(s.id) === String(params.id))
if (session) return { data: session }
const fallback = studentSessions[0]
return { data: { ...fallback, id: params.id } }
})
register('POST', endpoints.submitStudentSessionHomework, ({ params }) => ({
data: { id: params.id, status: 'submitted', message: 'تکلیف با موفقیت ارسال شد' },
}))
-21
View File
@@ -1,21 +0,0 @@
import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
import { findOrThrow, paginate } from '@/services/mock/helpers'
import { studentTerms } from '@/services/mock/fixtures/student-terms'
const CURRENT_STATUSES = new Set(['watching', 'waitForExam'])
const ENDED_STATUSES = new Set(['completed', 'failed'])
register('GET', endpoints.getStudentTerms, ({ query }) => {
let list = [...studentTerms]
if (query.status === 'current') {
list = list.filter((t) => CURRENT_STATUSES.has(t.status))
} else if (query.status === 'ended') {
list = list.filter((t) => ENDED_STATUSES.has(t.status))
}
return paginate(list, query)
})
register('GET', endpoints.showStudentTerm, ({ params }) => ({
data: findOrThrow(studentTerms, params.id),
}))
-80
View File
@@ -2,15 +2,8 @@ import { cleanFilters } from '@/utils/clean-filters'
import { useMutation, useQuery } from '@tanstack/vue-query'
import {
apiAddAdminCourse,
apiAddAdminCourseStudent,
apiAttachAdminCourseSession,
apiDeleteAdminCourse,
apiDetachAdminCourseSession,
apiGetAdminCourseSessions,
apiGetAdminCourseStudents,
apiGetAdminCourses,
apiGetCourseAttendees,
apiRemoveAdminCourseStudent,
apiShowAdminCourse,
apiUpdateAdminCourse,
} from '@/services/api/admin-courses'
@@ -19,22 +12,6 @@ export const adminCoursesKeys = {
all: ['admin', 'courses'],
list: (filters, pagination) => ['admin', 'courses', 'list', filters, pagination],
detail: (id) => ['admin', 'courses', 'detail', id],
students: (courseId, filters, pagination) => [
'admin',
'courses',
'students',
courseId,
filters,
pagination,
],
sessions: (courseId, filters, pagination) => [
'admin',
'courses',
'sessions',
courseId,
filters,
pagination,
],
}
export const useAdminCoursesListQuery = (filtersRef, paginationRef, options = {}) =>
@@ -67,60 +44,3 @@ export const useUpdateAdminCourseMutation = () =>
export const useDeleteAdminCourseMutation = () =>
useMutation({ mutationFn: (id) => apiDeleteAdminCourse(id) })
export const useAdminCourseStudentsQuery = (courseIdRef, filtersRef, paginationRef, options = {}) =>
useQuery({
queryKey: ['admin', 'courses', 'students', courseIdRef, filtersRef, paginationRef],
queryFn: () =>
apiGetAdminCourseStudents(courseIdRef.value, {
...cleanFilters(filtersRef?.value || {}),
...paginationRef?.value,
}),
...options,
})
export const useCourseAttendeesQuery = (courseIdRef, filtersRef, paginationRef, options = {}) =>
useQuery({
queryKey: ['courses', 'attendees', courseIdRef, filtersRef, paginationRef],
queryFn: () =>
apiGetCourseAttendees(courseIdRef.value, {
...cleanFilters(filtersRef?.value || {}),
...paginationRef?.value,
}),
select: (response) => ({
data: response?.data?.items ?? response?.data ?? [],
meta: response?.data?.meta ?? response?.meta,
}),
...options,
})
export const useAddAdminCourseStudentMutation = () =>
useMutation({
mutationFn: ({ courseId, payload }) => apiAddAdminCourseStudent(courseId, payload),
})
export const useRemoveAdminCourseStudentMutation = () =>
useMutation({
mutationFn: ({ courseId, userId }) => apiRemoveAdminCourseStudent(courseId, userId),
})
export const useAdminCourseSessionsQuery = (courseIdRef, filtersRef, paginationRef, options = {}) =>
useQuery({
queryKey: ['admin', 'courses', 'sessions', courseIdRef, filtersRef, paginationRef],
queryFn: () =>
apiGetAdminCourseSessions(courseIdRef.value, {
...cleanFilters(filtersRef?.value || {}),
...paginationRef?.value,
}),
...options,
})
export const useAttachAdminCourseSessionMutation = () =>
useMutation({
mutationFn: ({ courseId, payload }) => apiAttachAdminCourseSession(courseId, payload),
})
export const useDetachAdminCourseSessionMutation = () =>
useMutation({
mutationFn: ({ courseId, sessionId }) => apiDetachAdminCourseSession(courseId, sessionId),
})
+7 -6
View File
@@ -8,8 +8,8 @@ import {
apiDeleteAdminTerm,
apiGetAdminTerms,
apiGetAdminTermCourses,
apiGetAdminTermEnrollees,
apiGetAdminTermStudents,
apiGetTermAttendees,
apiRemoveAdminTermCourse,
apiRemoveAdminTermStudent,
apiShowAdminTerm,
@@ -29,9 +29,10 @@ export const adminTermsKeys = {
filters,
pagination,
],
attendees: (termId, filters, pagination) => [
enrollees: (termId, filters, pagination) => [
'admin',
'terms',
'attendees',
'enrollees',
termId,
filters,
pagination,
@@ -88,11 +89,11 @@ export const useAdminTermStudentsQuery = (termIdRef, filtersRef, paginationRef,
...options,
})
export const useTermAttendeesQuery = (termIdRef, filtersRef, paginationRef, options = {}) =>
export const useAdminTermEnrolleesQuery = (termIdRef, filtersRef, paginationRef, options = {}) =>
useQuery({
queryKey: ['terms', 'attendees', termIdRef, filtersRef, paginationRef],
queryKey: ['admin', 'terms', 'enrollees', termIdRef, filtersRef, paginationRef],
queryFn: () =>
apiGetTermAttendees(termIdRef.value, {
apiGetAdminTermEnrollees(termIdRef.value, {
...cleanFilters(filtersRef?.value || {}),
...paginationRef?.value,
}),
+1 -1
View File
@@ -49,7 +49,7 @@ export const useGetMeQuery = (options = {}) =>
useQuery({
queryKey: authKeys.me(),
queryFn: () => apiGetMe(),
select: (response) => response?.data?.user ?? response?.data ?? response,
select: (response) => response?.data ?? response,
...options,
})
+10 -1
View File
@@ -8,6 +8,15 @@ export const studentCoursesKeys = {
detail: (id) => ['student', 'courses', 'detail', id],
}
const adaptCourseResponse = (response) => {
const course = response?.data ?? response
if (!course) return course
return {
...course,
image: course.image ?? course.coverUrl ?? null,
}
}
export const useStudentCoursesListQuery = (filtersRef, paginationRef, options = {}) =>
useQuery({
queryKey: ['student', 'courses', 'list', filtersRef, paginationRef],
@@ -23,6 +32,6 @@ export const useStudentCourseQuery = (idRef, options = {}) =>
useQuery({
queryKey: ['student', 'courses', 'detail', idRef],
queryFn: () => apiShowStudentCourse(idRef.value),
select: (response) => response?.data ?? response,
select: adaptCourseResponse,
...options,
})
+1 -8
View File
@@ -1,9 +1,5 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import {
apiShowStudentExam,
apiStartStudentExamAttempt,
apiSubmitStudentExamAttempt,
} from '@/services/api/student-exams'
import { apiShowStudentExam, apiSubmitStudentExamAttempt } from '@/services/api/student-exams'
export const studentExamsKeys = {
detail: (id) => ['student', 'exams', 'detail', id],
@@ -17,9 +13,6 @@ export const useStudentExamQuery = (idRef, options = {}) =>
...options,
})
export const useStartStudentExamAttemptMutation = () =>
useMutation({ mutationFn: ({ id }) => apiStartStudentExamAttempt(id) })
export const useSubmitStudentExamAttemptMutation = () =>
useMutation({
mutationFn: ({ id, payload }) => apiSubmitStudentExamAttempt(id, payload),
-14
View File
@@ -1,14 +0,0 @@
import { useQuery } from '@tanstack/vue-query'
import { apiShowStudentLesson } from '@/services/api/student-lessons'
export const studentLessonsKeys = {
detail: (id) => ['student', 'lessons', 'detail', id],
}
export const useStudentLessonQuery = (idRef, options = {}) =>
useQuery({
queryKey: ['student', 'lessons', 'detail', idRef],
queryFn: () => apiShowStudentLesson(idRef.value),
select: (response) => response?.data ?? response,
...options,
})
+17 -7
View File
@@ -1,22 +1,32 @@
import { useMutation, useQuery } from '@tanstack/vue-query'
import {
apiShowStudentSession,
apiSubmitStudentSessionHomework,
} from '@/services/api/student-sessions'
import { apiShowStudentSession, apiSubmitStudentHomework } from '@/services/api/student-sessions'
export const studentSessionsKeys = {
detail: (id) => ['student', 'sessions', 'detail', id],
}
const adaptSessionResponse = (response) => {
const session = response?.data ?? response ?? {}
const media = Array.isArray(session.media) ? session.media : []
const byCollection = (name) => media.find((m) => m.collectionName === name)
return {
...session,
videoUrl: byCollection('videos')?.url ?? null,
audioUrl: byCollection('voices')?.url ?? null,
pdfUrl: byCollection('pdfs')?.url ?? null,
poster: byCollection('cover')?.url ?? null,
}
}
export const useStudentSessionQuery = (idRef, options = {}) =>
useQuery({
queryKey: ['student', 'sessions', 'detail', idRef],
queryFn: () => apiShowStudentSession(idRef.value),
select: (response) => response?.data ?? response,
select: adaptSessionResponse,
...options,
})
export const useSubmitStudentSessionHomeworkMutation = () =>
export const useSubmitStudentHomeworkMutation = () =>
useMutation({
mutationFn: ({ id, payload }) => apiSubmitStudentSessionHomework(id, payload),
mutationFn: ({ homeworkId, payload }) => apiSubmitStudentHomework(homeworkId, payload),
})
+7
View File
@@ -16,6 +16,13 @@ export const useStudentTermsListQuery = (filtersRef, paginationRef, options = {}
...cleanFilters(filtersRef.value),
...paginationRef.value,
}),
select: (response) => {
const payload = response?.data ?? response ?? {}
return {
data: Array.isArray(payload.items) ? payload.items : payload ?? [],
meta: payload.meta,
}
},
...options,
})