From e8b1e4add6a1c14b07a412efa53fda407bc8248d Mon Sep 17 00:00:00 2001
From: sajjadtalkhabi <97734061+sajjadtalkhabi@users.noreply.github.com>
Date: Tue, 21 Jul 2026 12:24:58 +0330
Subject: [PATCH] fix: missionary education
---
.../pages/MissionaryLessonDetailsPage.vue | 95 ++++++++++++++
.../pages/MissionaryTermDetailsPage.vue | 101 +++++++++++++++
.../education/pages/MissionaryTermsPage.vue | 70 ++++++++++
src/features/missionary/router.js | 45 +++++++
src/layouts/MissionaryLayout.vue | 6 +
src/services/api/endpoints.js | 8 ++
src/services/api/missionary-education.js | 11 ++
.../mock/fixtures/missionary-education.js | 122 ++++++++++++++++++
.../mock/routes/missionary-education.js | 27 ++++
src/services/query/missionary-education.js | 49 +++++++
10 files changed, 534 insertions(+)
create mode 100644 src/features/missionary/education/pages/MissionaryLessonDetailsPage.vue
create mode 100644 src/features/missionary/education/pages/MissionaryTermDetailsPage.vue
create mode 100644 src/features/missionary/education/pages/MissionaryTermsPage.vue
create mode 100644 src/services/api/missionary-education.js
create mode 100644 src/services/mock/fixtures/missionary-education.js
create mode 100644 src/services/mock/routes/missionary-education.js
create mode 100644 src/services/query/missionary-education.js
diff --git a/src/features/missionary/education/pages/MissionaryLessonDetailsPage.vue b/src/features/missionary/education/pages/MissionaryLessonDetailsPage.vue
new file mode 100644
index 0000000..4e56256
--- /dev/null
+++ b/src/features/missionary/education/pages/MissionaryLessonDetailsPage.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/missionary/education/pages/MissionaryTermDetailsPage.vue b/src/features/missionary/education/pages/MissionaryTermDetailsPage.vue
new file mode 100644
index 0000000..d5ce4ed
--- /dev/null
+++ b/src/features/missionary/education/pages/MissionaryTermDetailsPage.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/missionary/education/pages/MissionaryTermsPage.vue b/src/features/missionary/education/pages/MissionaryTermsPage.vue
new file mode 100644
index 0000000..04b8d34
--- /dev/null
+++ b/src/features/missionary/education/pages/MissionaryTermsPage.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/missionary/router.js b/src/features/missionary/router.js
index 4a1b7dc..389bd97 100644
--- a/src/features/missionary/router.js
+++ b/src/features/missionary/router.js
@@ -32,4 +32,49 @@ export default [
subtitle: 'اینجا پل ارتباطی شما با پشتیبانی است.',
},
},
+ {
+ path: '/missionary-panel/education',
+ name: 'missionary-education',
+ component: () => import('@/features/missionary/education/pages/MissionaryTermsPage.vue'),
+ meta: {
+ layout: 'missionary',
+ role: 'missionary',
+ title: 'آموزش من',
+ subtitle: 'ترمها و دورههای گذراندهشده خود را مرور کنید.',
+ },
+ },
+ {
+ path: '/missionary-panel/education/terms/:id',
+ name: 'missionary-education-term',
+ component: () => import('@/features/missionary/education/pages/MissionaryTermDetailsPage.vue'),
+ meta: {
+ layout: 'missionary',
+ role: 'missionary',
+ title: 'آموزش من',
+ subtitle: 'ترمها و دورههای گذراندهشده خود را مرور کنید.',
+ },
+ },
+ {
+ path: '/missionary-panel/education/lessons/:id',
+ name: 'missionary-education-lesson',
+ component: () =>
+ import('@/features/missionary/education/pages/MissionaryLessonDetailsPage.vue'),
+ meta: {
+ layout: 'missionary',
+ role: 'missionary',
+ title: 'آموزش من',
+ subtitle: 'ترمها و دورههای گذراندهشده خود را مرور کنید.',
+ },
+ },
+ {
+ path: '/missionary-panel/education/sessions/:id',
+ name: 'missionary-education-session',
+ component: () => import('@/features/student/sessions/pages/StudentSessionDetailsPage.vue'),
+ meta: {
+ layout: 'missionary',
+ role: 'missionary',
+ title: 'آموزش من',
+ subtitle: 'ترمها و دورههای گذراندهشده خود را مرور کنید.',
+ },
+ },
]
diff --git a/src/layouts/MissionaryLayout.vue b/src/layouts/MissionaryLayout.vue
index fd81849..dab4193 100644
--- a/src/layouts/MissionaryLayout.vue
+++ b/src/layouts/MissionaryLayout.vue
@@ -70,6 +70,12 @@ const menuItems = computed(() => [
to: { name: 'missionary-requests' },
active: route.name === 'missionary-requests',
},
+ {
+ title: 'آموزش من',
+ icon: 'graduation-cap',
+ to: { name: 'missionary-education' },
+ active: route.name?.startsWith('missionary-education'),
+ },
{
title: 'تیکت',
icon: 'envelope-open',
diff --git a/src/services/api/endpoints.js b/src/services/api/endpoints.js
index ad280b8..2565604 100644
--- a/src/services/api/endpoints.js
+++ b/src/services/api/endpoints.js
@@ -30,6 +30,14 @@ export const endpoints = {
// dedicated student route (returns the membership row with the nested term).
getStudentDashboard: '/student/dashboard',
getStudentTerms: '/student/my-terms',
+
+ // Missionary — passed (completed) education. List-only; same shapes as
+ // /student/my-terms, /courses, /sessions. Detail views reuse the shared
+ // /sessions/:id and the already-fetched list data (via query cache).
+ getMissionaryPassedTerms: '/missionary/passed-terms',
+ getMissionaryPassedCourses: '/missionary/passed-courses',
+ getMissionaryPassedSessions: '/missionary/passed-sessions',
+
showStudentTerm: '/student/my-terms/:id',
getStudentExamResults: '/student/exam-results',
getStudentHomeworks: '/student/homeworks',
diff --git a/src/services/api/missionary-education.js b/src/services/api/missionary-education.js
new file mode 100644
index 0000000..ceb9a44
--- /dev/null
+++ b/src/services/api/missionary-education.js
@@ -0,0 +1,11 @@
+import { http } from '@/services/api/http'
+import { endpoints } from '@/services/api/endpoints'
+
+export const apiGetMissionaryPassedTerms = (params) =>
+ http.get(endpoints.getMissionaryPassedTerms, { params })
+
+export const apiGetMissionaryPassedCourses = (params) =>
+ http.get(endpoints.getMissionaryPassedCourses, { params })
+
+export const apiGetMissionaryPassedSessions = (params) =>
+ http.get(endpoints.getMissionaryPassedSessions, { params })
diff --git a/src/services/mock/fixtures/missionary-education.js b/src/services/mock/fixtures/missionary-education.js
new file mode 100644
index 0000000..e30a18a
--- /dev/null
+++ b/src/services/mock/fixtures/missionary-education.js
@@ -0,0 +1,122 @@
+// Missionary passed (completed) education. Shapes mirror /student/my-terms,
+// /courses and /sessions. Session ids reuse existing adminSessions ids so the
+// shared GET /sessions/:id mock resolves the detail drill-down.
+
+const makeTerm = (id, title, over) => ({
+ id,
+ title,
+ description: 'ترم تکمیلشده',
+ isActive: false,
+ startsAt: '2025-09-22T00:00:00+00:00',
+ endsAt: '2026-01-20T00:00:00+00:00',
+ score: 20,
+ minimumScore: 12,
+ coverUrl: null,
+ coursesCount: 3,
+ sessionsCount: 18,
+ examsCount: 12,
+ homeworksCount: 15,
+ studentsCount: 40,
+ createdAt: '2025-08-01T10:00:00+00:00',
+ ...over,
+})
+
+export const passedTerms = [
+ {
+ id: 9,
+ userId: 14,
+ termId: 3,
+ status: 'completed',
+ completedAt: '2026-02-01T10:00:00+00:00',
+ term: makeTerm(3, 'پاییز ۱۴۰۴'),
+ },
+ {
+ id: 10,
+ userId: 14,
+ termId: 4,
+ status: 'completed',
+ completedAt: '2025-07-05T10:00:00+00:00',
+ term: makeTerm(4, 'تابستان ۱۴۰۴', { coursesCount: 1, sessionsCount: 6 }),
+ },
+]
+
+const teacher = { id: 2, name: 'علیرضا حسینی' }
+
+export const passedCourses = [
+ {
+ id: 21,
+ termId: 3,
+ teacherId: 2,
+ teacher,
+ title: 'مبانی اعتقادات',
+ description: 'درس پایه',
+ capacity: 50,
+ isActive: false,
+ coverUrl: null,
+ sessionsCount: 2,
+ examsCount: 6,
+ homeworksCount: 7,
+ prerequisiteCourseIds: [],
+ prerequisiteCourses: [],
+ },
+ {
+ id: 22,
+ termId: 3,
+ teacherId: 2,
+ teacher,
+ title: 'اخلاق کاربردی',
+ description: 'درس تکمیلی',
+ capacity: 40,
+ isActive: false,
+ coverUrl: null,
+ sessionsCount: 1,
+ examsCount: 2,
+ homeworksCount: 3,
+ prerequisiteCourseIds: [],
+ prerequisiteCourses: [],
+ },
+ {
+ id: 30,
+ termId: 4,
+ teacherId: 2,
+ teacher,
+ title: 'تاریخ اسلام',
+ description: 'درس پایه',
+ capacity: 45,
+ isActive: false,
+ coverUrl: null,
+ sessionsCount: 0,
+ examsCount: 1,
+ homeworksCount: 2,
+ prerequisiteCourseIds: [],
+ prerequisiteCourses: [],
+ },
+]
+
+const makeSession = (id, courseId, title, over) => ({
+ id,
+ courseId,
+ title,
+ description: 'جلسه گذراندهشده',
+ type: 'online',
+ startsAt: '2025-09-25T18:00:00+03:30',
+ durationMinutes: 90,
+ location: null,
+ link: 'https://meet.example.com/abc',
+ prerequisiteSessionIds: [],
+ examsCount: 1,
+ homeworksCount: 2,
+ prerequisiteSessions: [],
+ isComplete: true,
+ isSeen: true,
+ course: { id: courseId, termId: 3, teacherId: 2, title: 'مبانی اعتقادات' },
+ ...over,
+})
+
+export const passedSessions = [
+ makeSession(101, 21, 'جلسه اول — مقدمه'),
+ makeSession(102, 21, 'جلسه دوم — مفاهیم پایه'),
+ makeSession(103, 22, 'جلسه اول — کلیات', {
+ course: { id: 22, termId: 3, teacherId: 2, title: 'اخلاق کاربردی' },
+ }),
+]
diff --git a/src/services/mock/routes/missionary-education.js b/src/services/mock/routes/missionary-education.js
new file mode 100644
index 0000000..9d9ca98
--- /dev/null
+++ b/src/services/mock/routes/missionary-education.js
@@ -0,0 +1,27 @@
+import { paginate } from '@/services/mock/helpers'
+import { register } from '@/services/mock/registry'
+import { endpoints } from '@/services/api/endpoints'
+import {
+ passedTerms,
+ passedCourses,
+ passedSessions,
+} from '@/services/mock/fixtures/missionary-education'
+
+register('GET', endpoints.getMissionaryPassedTerms, ({ query }) => {
+ const { data: items, meta } = paginate(passedTerms, query)
+ return { success: true, message: 'OK', data: { items, meta } }
+})
+
+register('GET', endpoints.getMissionaryPassedCourses, ({ query }) => {
+ let list = passedCourses
+ if (query.termId) list = list.filter((c) => String(c.termId) === String(query.termId))
+ const { data: items, meta } = paginate(list, query)
+ return { success: true, message: 'OK', data: { items, meta } }
+})
+
+register('GET', endpoints.getMissionaryPassedSessions, ({ query }) => {
+ let list = passedSessions
+ if (query.courseId) list = list.filter((s) => String(s.courseId) === String(query.courseId))
+ const { data: items, meta } = paginate(list, query)
+ return { success: true, message: 'OK', data: { items, meta } }
+})
diff --git a/src/services/query/missionary-education.js b/src/services/query/missionary-education.js
new file mode 100644
index 0000000..53bf065
--- /dev/null
+++ b/src/services/query/missionary-education.js
@@ -0,0 +1,49 @@
+import { useQuery } from '@tanstack/vue-query'
+import { cleanFilters } from '@/utils/clean-filters'
+import {
+ apiGetMissionaryPassedTerms,
+ apiGetMissionaryPassedCourses,
+ apiGetMissionaryPassedSessions,
+} from '@/services/api/missionary-education'
+
+// All three passed-* endpoints return the paginated `{ items, meta }` envelope,
+// mirroring /student/my-terms, /courses and /sessions.
+const selectList = (response) => {
+ const payload = response?.data ?? response ?? {}
+ return {
+ data: Array.isArray(payload.items) ? payload.items : payload ?? [],
+ meta: payload.meta,
+ }
+}
+
+export const useMissionaryPassedTermsQuery = (paginationRef, options = {}) =>
+ useQuery({
+ queryKey: ['missionary', 'passed-terms', paginationRef],
+ queryFn: () => apiGetMissionaryPassedTerms({ ...paginationRef.value }),
+ select: selectList,
+ ...options,
+ })
+
+export const useMissionaryPassedCoursesQuery = (filtersRef, paginationRef, options = {}) =>
+ useQuery({
+ queryKey: ['missionary', 'passed-courses', filtersRef, paginationRef],
+ queryFn: () =>
+ apiGetMissionaryPassedCourses({
+ ...cleanFilters(filtersRef.value),
+ ...paginationRef.value,
+ }),
+ select: selectList,
+ ...options,
+ })
+
+export const useMissionaryPassedSessionsQuery = (filtersRef, paginationRef, options = {}) =>
+ useQuery({
+ queryKey: ['missionary', 'passed-sessions', filtersRef, paginationRef],
+ queryFn: () =>
+ apiGetMissionaryPassedSessions({
+ ...cleanFilters(filtersRef.value),
+ ...paginationRef.value,
+ }),
+ select: selectList,
+ ...options,
+ })