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, + })