diff --git a/src/features/missionary/components/modals/completeInfo/AddCertificateModal.vue b/src/features/missionary/components/modals/completeInfo/AddCertificateModal.vue
new file mode 100644
index 0000000..d89de73
--- /dev/null
+++ b/src/features/missionary/components/modals/completeInfo/AddCertificateModal.vue
@@ -0,0 +1,132 @@
+
+
+
+
+
diff --git a/src/features/missionary/components/modals/completeInfo/AddEducationModal.vue b/src/features/missionary/components/modals/completeInfo/AddEducationModal.vue
new file mode 100644
index 0000000..8a0763f
--- /dev/null
+++ b/src/features/missionary/components/modals/completeInfo/AddEducationModal.vue
@@ -0,0 +1,145 @@
+
+
+
+
+
diff --git a/src/features/missionary/components/modals/completeInfo/AddSkillModal.vue b/src/features/missionary/components/modals/completeInfo/AddSkillModal.vue
new file mode 100644
index 0000000..691cc89
--- /dev/null
+++ b/src/features/missionary/components/modals/completeInfo/AddSkillModal.vue
@@ -0,0 +1,241 @@
+
+
+
+
+
diff --git a/src/features/missionary/components/modals/completeInfo/AddWorkExperienceModal.vue b/src/features/missionary/components/modals/completeInfo/AddWorkExperienceModal.vue
new file mode 100644
index 0000000..45e7da3
--- /dev/null
+++ b/src/features/missionary/components/modals/completeInfo/AddWorkExperienceModal.vue
@@ -0,0 +1,223 @@
+
+
+
+
+
diff --git a/src/features/missionary/components/modals/completeInfo/CompleteInfoModal.vue b/src/features/missionary/components/modals/completeInfo/CompleteInfoModal.vue
new file mode 100644
index 0000000..87eed30
--- /dev/null
+++ b/src/features/missionary/components/modals/completeInfo/CompleteInfoModal.vue
@@ -0,0 +1,259 @@
+
+
+
+
+
diff --git a/src/features/missionary/components/modals/completeInfo/EntryCard.vue b/src/features/missionary/components/modals/completeInfo/EntryCard.vue
new file mode 100644
index 0000000..ce67f78
--- /dev/null
+++ b/src/features/missionary/components/modals/completeInfo/EntryCard.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
diff --git a/src/features/missionary/constants/complete-info.js b/src/features/missionary/constants/complete-info.js
new file mode 100644
index 0000000..3e14c36
--- /dev/null
+++ b/src/features/missionary/constants/complete-info.js
@@ -0,0 +1,50 @@
+export const SKILL_LEVELS = Object.freeze({
+ beginner: 'مقدماتی',
+ intermediate: 'متوسط',
+ advanced: 'پیشرفته',
+})
+
+export const EDUCATION_DEGREES = Object.freeze({
+ associate: 'کاردانی',
+ bachelor: 'کارشناسی',
+ master: 'کارشناسی ارشد',
+ phd: 'دکترا',
+})
+
+export const MONTHS = Object.freeze([
+ { value: 1, label: 'فروردین' },
+ { value: 2, label: 'اردیبهشت' },
+ { value: 3, label: 'خرداد' },
+ { value: 4, label: 'تیر' },
+ { value: 5, label: 'مرداد' },
+ { value: 6, label: 'شهریور' },
+ { value: 7, label: 'مهر' },
+ { value: 8, label: 'آبان' },
+ { value: 9, label: 'آذر' },
+ { value: 10, label: 'دی' },
+ { value: 11, label: 'بهمن' },
+ { value: 12, label: 'اسفند' },
+])
+
+export const SKILL_SUGGESTIONS = Object.freeze([
+ 'زبان انگلیسی',
+ 'زبان عربی',
+ 'تدریس قرآن',
+ 'تدریس احکام',
+ 'روضهخوانی',
+ 'مداحی',
+ 'سخنرانی',
+ 'مشاوره خانواده',
+ 'مشاوره تحصیلی',
+ 'قصهگویی',
+ 'نویسندگی',
+ 'خطاطی',
+ 'نقاشی',
+ 'طراحی دکوراسیون',
+ 'طراحی گرافیک',
+ 'تولید محتوا',
+ 'تدوین ویدئو',
+ 'عکاسی',
+ 'مدیریت فضای مجازی',
+ 'کار با کامپیوتر',
+])
diff --git a/src/features/missionary/index.js b/src/features/missionary/index.js
index 588598c..d0c0b82 100644
--- a/src/features/missionary/index.js
+++ b/src/features/missionary/index.js
@@ -1 +1,7 @@
-export { default as MissionaryRequestDetailsModal } from './components/modals/MissionaryRequestDetailsModal.vue'
+export { default as DispatchDetailsModal } from './components/modals/DispatchDetailsModal.vue'
+export { default as MissionaryDispatchItem } from './components/MissionaryDispatchItem.vue'
+export { default as MissionaryNarrativeItem } from './components/MissionaryNarrativeItem.vue'
+export { default as MissionaryRequestItem } from './components/MissionaryRequestItem.vue'
+export { default as MissionaryUserCard } from './components/MissionaryUserCard.vue'
+export { default as NarrativeDetailsModal } from './components/modals/NarrativeDetailsModal.vue'
+export { default as RequestDetailsModal } from './components/modals/RequestDetailsModal.vue'
diff --git a/src/features/missionary/pages/MissionaryRequestsPage.vue b/src/features/missionary/pages/MissionaryRequestsPage.vue
index 88975b9..cc164c9 100644
--- a/src/features/missionary/pages/MissionaryRequestsPage.vue
+++ b/src/features/missionary/pages/MissionaryRequestsPage.vue
@@ -1,73 +1,277 @@
-
+
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/features/missionary/store/complete-info.js b/src/features/missionary/store/complete-info.js
new file mode 100644
index 0000000..bbb07e3
--- /dev/null
+++ b/src/features/missionary/store/complete-info.js
@@ -0,0 +1,75 @@
+import { ref } from 'vue'
+import { defineStore } from 'pinia'
+
+const upsert =
+ (listRef) =>
+ (entry, index = null) => {
+ if (index == null || index < 0) {
+ listRef.value = [...listRef.value, entry]
+ } else {
+ listRef.value = listRef.value.map((item, i) => (i === index ? entry : item))
+ }
+ }
+
+export const useCompleteInfoStore = defineStore('missionaryCompleteInfo', () => {
+ const introduction = ref('')
+ const skills = ref([])
+ const workExperiences = ref([])
+ const educationHistory = ref([])
+ const certificates = ref([])
+
+ const reset = () => {
+ introduction.value = ''
+ skills.value = []
+ workExperiences.value = []
+ educationHistory.value = []
+ certificates.value = []
+ }
+
+ // Keys mirror what POST /me/register-data persists — flat `{ key: value }`
+ // where array values hold the section entries.
+ const hydrateFromRegisterData = (data = {}) => {
+ if (typeof data.introduction === 'string') introduction.value = data.introduction
+ if (Array.isArray(data.skills)) skills.value = data.skills
+ if (Array.isArray(data.workExperiences)) workExperiences.value = data.workExperiences
+ if (Array.isArray(data.educationHistory)) educationHistory.value = data.educationHistory
+ if (Array.isArray(data.certificates)) certificates.value = data.certificates
+ }
+
+ const addSkill = (skill) => {
+ if (skills.value.some((s) => s.title === skill.title)) return
+ skills.value = [...skills.value, skill]
+ }
+
+ const removeSkill = (title) => {
+ skills.value = skills.value.filter((s) => s.title !== title)
+ }
+
+ const upsertWorkExperience = upsert(workExperiences)
+ const upsertEducation = upsert(educationHistory)
+ const upsertCertificate = upsert(certificates)
+
+ const buildPayload = () => ({
+ introduction: introduction.value,
+ skills: skills.value,
+ workExperiences: workExperiences.value,
+ educationHistory: educationHistory.value,
+ certificates: certificates.value,
+ })
+
+ return {
+ introduction,
+ skills,
+ workExperiences,
+ educationHistory,
+ certificates,
+ reset,
+ hydrateFromRegisterData,
+ addSkill,
+ removeSkill,
+ upsertWorkExperience,
+ upsertEducation,
+ upsertCertificate,
+ buildPayload,
+ }
+})
diff --git a/src/features/student/missionary/components/MissionaryRequestItem.vue b/src/features/student/missionary/components/MissionaryRequestItem.vue
deleted file mode 100644
index 8739bee..0000000
--- a/src/features/student/missionary/components/MissionaryRequestItem.vue
+++ /dev/null
@@ -1,257 +0,0 @@
-
-
-
-
{{ request.code }}
-
-
{{ request.title }}
-
شماره درخواست : {{ request.requestNumber }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/features/student/missionary/index.js b/src/features/student/missionary/index.js
deleted file mode 100644
index d0c0b82..0000000
--- a/src/features/student/missionary/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-export { default as DispatchDetailsModal } from './components/modals/DispatchDetailsModal.vue'
-export { default as MissionaryDispatchItem } from './components/MissionaryDispatchItem.vue'
-export { default as MissionaryNarrativeItem } from './components/MissionaryNarrativeItem.vue'
-export { default as MissionaryRequestItem } from './components/MissionaryRequestItem.vue'
-export { default as MissionaryUserCard } from './components/MissionaryUserCard.vue'
-export { default as NarrativeDetailsModal } from './components/modals/NarrativeDetailsModal.vue'
-export { default as RequestDetailsModal } from './components/modals/RequestDetailsModal.vue'
diff --git a/src/features/student/missionary/pages/StudentMissionaryPage.vue b/src/features/student/missionary/pages/StudentMissionaryPage.vue
deleted file mode 100644
index c0a6030..0000000
--- a/src/features/student/missionary/pages/StudentMissionaryPage.vue
+++ /dev/null
@@ -1,203 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/features/student/router.js b/src/features/student/router.js
index 8150bfb..3f3fdc1 100644
--- a/src/features/student/router.js
+++ b/src/features/student/router.js
@@ -109,17 +109,6 @@ export default [
subtitle: 'مهارتها و دورههای خود را دنبال کنید.',
},
},
- {
- path: '/missionary',
- name: 'student-missionary',
- component: () => import('@/features/student/missionary/pages/StudentMissionaryPage.vue'),
- meta: {
- layout: 'student',
- role: 'student',
- title: 'سیستم مدیریت اعزام',
- subtitle: 'سادهترین راه برای برنامهریزی و پایش مأموریتها',
- },
- },
{
path: '/user/services',
name: 'student-services',
diff --git a/src/features/student/sessions/pages/StudentSessionDetailsPage.vue b/src/features/student/sessions/pages/StudentSessionDetailsPage.vue
index 070a37c..7c07113 100644
--- a/src/features/student/sessions/pages/StudentSessionDetailsPage.vue
+++ b/src/features/student/sessions/pages/StudentSessionDetailsPage.vue
@@ -31,7 +31,10 @@
/>
-
+
diff --git a/src/layouts/AdminLayout.vue b/src/layouts/AdminLayout.vue
index e5c6d6a..8b1426c 100644
--- a/src/layouts/AdminLayout.vue
+++ b/src/layouts/AdminLayout.vue
@@ -156,6 +156,12 @@ const menuItems = computed(() => [
to: { name: 'admin-consultations' },
active: route.name === 'admin-consultations',
},
+ {
+ title: 'مدیریت مبلغین',
+ icon: 'user-sound',
+ to: { name: 'admin-missionary-requests' },
+ active: route.name === 'admin-missionary-requests',
+ },
])
diff --git a/src/layouts/MissionaryLayout.vue b/src/layouts/MissionaryLayout.vue
index f7d35b3..6ce9967 100644
--- a/src/layouts/MissionaryLayout.vue
+++ b/src/layouts/MissionaryLayout.vue
@@ -59,7 +59,7 @@ const pageSubtitle = computed(() => route.meta?.subtitle || '')
const menuItems = computed(() => [
{
- title: 'لیست درخواست ها',
+ title: 'سامانه اعزام',
icon: 'user-sound',
to: { name: 'missionary-requests' },
active: route.name === 'missionary-requests',
diff --git a/src/layouts/StudentLayout.vue b/src/layouts/StudentLayout.vue
index bdf9f48..a12b40c 100644
--- a/src/layouts/StudentLayout.vue
+++ b/src/layouts/StudentLayout.vue
@@ -78,12 +78,6 @@ const menuItems = computed(() => [
'student-course-details',
].includes(route.name),
},
- {
- title: 'سامانه اعزام',
- icon: 'user-sound',
- to: { name: 'student-missionary' },
- active: route.name === 'student-missionary',
- },
{
title: 'خدمات',
icon: 'folder-simple-star',
diff --git a/src/services/api/admin-missionary-requests.js b/src/services/api/admin-missionary-requests.js
new file mode 100644
index 0000000..6cfde74
--- /dev/null
+++ b/src/services/api/admin-missionary-requests.js
@@ -0,0 +1,11 @@
+import { http } from '@/services/api/http'
+import { buildUrl, endpoints } from '@/services/api/endpoints'
+
+export const apiListAdminMissionaryRequests = (params) =>
+ http.get(endpoints.listAdminMissionaryRequests, { params })
+
+export const apiShowAdminMissionaryRequest = (id) =>
+ http.get(buildUrl(endpoints.showAdminMissionaryRequest, { id }))
+
+export const apiChangeAdminMissionaryRequestStatus = (id, payload) =>
+ http.patch(buildUrl(endpoints.changeAdminMissionaryRequestStatus, { id }), payload)
diff --git a/src/services/api/endpoints.js b/src/services/api/endpoints.js
index a09c666..ca34790 100644
--- a/src/services/api/endpoints.js
+++ b/src/services/api/endpoints.js
@@ -35,11 +35,6 @@ export const endpoints = {
showHomework: '/homeworks/:id',
startExam: '/exams/:examId/start',
- getMissionaryProfile: '/student/missionary/profile',
- getMissionaryRequests: '/student/missionary/requests',
- getMissionaryDispatches: '/student/missionary/dispatches',
- getMissionaryNarratives: '/student/missionary/narratives',
-
// Unified student tickets — handles services (type=service), consultants
// (type=advise) and inbox (type=ticket) via the same endpoint with a filter.
getStudentTickets: '/student/tickets',
@@ -168,6 +163,15 @@ export const endpoints = {
listMissionaryRequests: '/missionary/requests',
showMissionaryRequest: '/missionary/requests/:id',
changeMissionaryRequestStatus: '/missionary/requests/:id/status',
+
+ listMissionaryMemories: '/missionary/memories',
+ createMissionaryMemory: '/missionary/memories',
+ updateMissionaryMemory: '/missionary/memories/:id',
+ deleteMissionaryMemory: '/missionary/memories/:id',
+
+ listAdminMissionaryRequests: '/admin/missionary-requests',
+ showAdminMissionaryRequest: '/admin/missionary-requests/:id',
+ changeAdminMissionaryRequestStatus: '/admin/missionary-requests/:id/status',
}
export const buildUrl = (template, params = {}) =>
diff --git a/src/services/api/missionary-memories.js b/src/services/api/missionary-memories.js
new file mode 100644
index 0000000..c5350a9
--- /dev/null
+++ b/src/services/api/missionary-memories.js
@@ -0,0 +1,14 @@
+import { http } from '@/services/api/http'
+import { buildUrl, endpoints } from '@/services/api/endpoints'
+
+export const apiListMissionaryMemories = (params) =>
+ http.get(endpoints.listMissionaryMemories, { params })
+
+export const apiCreateMissionaryMemory = (payload) =>
+ http.post(endpoints.createMissionaryMemory, payload)
+
+export const apiUpdateMissionaryMemory = (id, payload) =>
+ http.patch(buildUrl(endpoints.updateMissionaryMemory, { id }), payload)
+
+export const apiDeleteMissionaryMemory = (id) =>
+ http.delete(buildUrl(endpoints.deleteMissionaryMemory, { id }))
diff --git a/src/services/api/student-missionary.js b/src/services/api/student-missionary.js
deleted file mode 100644
index ba087a4..0000000
--- a/src/services/api/student-missionary.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import { http } from '@/services/api/http'
-import { endpoints } from '@/services/api/endpoints'
-
-export const apiGetMissionaryProfile = () => http.get(endpoints.getMissionaryProfile)
-
-export const apiGetMissionaryRequests = (params) =>
- http.get(endpoints.getMissionaryRequests, { params })
-
-export const apiGetMissionaryDispatches = (params) =>
- http.get(endpoints.getMissionaryDispatches, { params })
-
-export const apiGetMissionaryNarratives = (params) =>
- http.get(endpoints.getMissionaryNarratives, { params })
diff --git a/src/services/mock/fixtures/missionary-memories.js b/src/services/mock/fixtures/missionary-memories.js
new file mode 100644
index 0000000..ed90c63
--- /dev/null
+++ b/src/services/mock/fixtures/missionary-memories.js
@@ -0,0 +1,40 @@
+const SAMPLE_BODY = `لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.`
+
+export const missionaryMemories = [
+ {
+ id: 1,
+ missionaryId: 8,
+ title: 'سفر به شهر زیبای مشهد مقدس، شهر علم و ادب',
+ description: SAMPLE_BODY,
+ media: [],
+ createdAt: '2026-06-10T08:30:00+00:00',
+ },
+ {
+ id: 2,
+ missionaryId: 8,
+ title: 'سفر به اصفهان نصف جهان',
+ description: SAMPLE_BODY,
+ media: [
+ {
+ id: 63,
+ collectionName: 'memory',
+ fileName: 'trip-photo.png',
+ mimeType: 'image/png',
+ fileSize: 20_727,
+ url: '',
+ downloadUrl: '',
+ },
+ ],
+ missionaryRequestId: 3,
+ missionaryRequest: { id: 3, title: 'جلسه پرسش و پاسخ اعتقادی دانشگاه' },
+ createdAt: '2026-06-18T09:15:00+00:00',
+ },
+ {
+ id: 3,
+ missionaryId: 8,
+ title: 'روایت جلسات هفتگی قم',
+ description: SAMPLE_BODY,
+ media: [],
+ createdAt: '2026-06-25T14:45:00+00:00',
+ },
+]
diff --git a/src/services/mock/fixtures/student-missionary.js b/src/services/mock/fixtures/student-missionary.js
deleted file mode 100644
index 4109ec9..0000000
--- a/src/services/mock/fixtures/student-missionary.js
+++ /dev/null
@@ -1,157 +0,0 @@
-export const missionaryProfile = {
- id: 'me',
- fullName: 'آقای علیرضا احمدی',
- city: 'قم، ایران',
- level: 'پیشرفته',
- membershipDays: 23,
- rating: 4.8,
- requestsCount: 451,
- avatar: '',
-}
-
-const STATUS_LABELS = {
- pending: 'در انتظار بررسی',
- approved: 'تایید شده',
- rejected: 'رد شده توسط موسسه',
- cancelled: 'لغو شده',
-}
-
-const REQUEST_DESCRIPTION = `لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد.`
-
-export const missionaryRequests = [
- {
- id: 'r-1',
- code: 1,
- title: 'درخواست شماره یک',
- requestNumber: 387,
- requesterName: 'حسین رضایی',
- requestDate: '1404/05/12',
- status: 'rejected',
- statusLabel: STATUS_LABELS.rejected,
- phone: '0912 152 1413',
- postalCode: '1232146890345',
- address:
- 'قم، سالاریه، میدان پیچک، به سمت فرعی اول، میدان الهیه، ساختمان الیزیوم، طبقه منفی 1 واحد 152',
- description: REQUEST_DESCRIPTION,
- },
- {
- id: 'r-2',
- code: 2,
- title: 'درخواست شماره دو',
- requestNumber: 401,
- requesterName: 'مریم اکبری',
- requestDate: '1404/06/04',
- status: 'pending',
- statusLabel: STATUS_LABELS.pending,
- phone: '0912 884 2210',
- postalCode: '8136987710332',
- address: 'اصفهان، خیابان چهارباغ بالا، کوچه گلستان، پلاک 12',
- description: REQUEST_DESCRIPTION,
- },
- {
- id: 'r-3',
- code: 3,
- title: 'درخواست شماره سه',
- requestNumber: 412,
- requesterName: 'فاطمه حسینی',
- requestDate: '1404/06/18',
- status: 'approved',
- statusLabel: STATUS_LABELS.approved,
- phone: '0935 410 0091',
- postalCode: '9176548132201',
- address: 'مشهد، خیابان امام رضا، کوچه 14، پلاک 5',
- description: REQUEST_DESCRIPTION,
- },
-]
-
-const DISPATCH_RESULT = `لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد.`
-
-export const missionaryDispatches = [
- {
- id: 'd-1',
- code: 1_547_890,
- title: 'اعزام شماره یک',
- missionName: 'سفر به مشهد مقدس',
- location: 'زنجان، ایران',
- dispatchDate: '1404/12/12',
- statusLabel: 'انجام شده',
- requesterName: 'حسین مولایی',
- address:
- 'قم، سالاریه، میدان پیچک، به سمت فرعی اول، میدان الهیه، ساختمان الیزیوم، طبقه منفی 1 واحد 152',
- result: DISPATCH_RESULT,
- },
- {
- id: 'd-2',
- code: 1_547_891,
- title: 'اعزام شماره دو',
- missionName: 'سفر به اصفهان',
- location: 'اصفهان، ایران',
- dispatchDate: '1404/04/09',
- statusLabel: 'در حال انجام',
- requesterName: 'مریم اکبری',
- address: 'اصفهان، خیابان چهارباغ، کوچه گلستان، پلاک 12',
- result: DISPATCH_RESULT,
- },
- {
- id: 'd-3',
- code: 1_547_892,
- title: 'اعزام شماره سه',
- missionName: 'سفر به مشهد مقدس',
- location: 'مشهد، ایران',
- dispatchDate: '1404/03/21',
- statusLabel: 'انجام شده',
- requesterName: 'فاطمه حسینی',
- address: 'مشهد، خیابان امام رضا، کوچه 14، پلاک 5',
- result: DISPATCH_RESULT,
- },
-]
-
-const NARRATIVE_KIND = {
- manuscript: { key: 'manuscript', label: 'دست نوشته', tone: 'pink' },
- dispatched: { key: 'dispatched', label: 'اعزام شــــده', tone: 'gold' },
-}
-
-const SAMPLE_BODY = `لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد. کتابهای زیادی در شصت و سه درصد گذشته، حال و آینده شناخت فراوان جامعه و متخصصان را می طلبد.
-
-لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم است و برای شرایط فعلی تکنولوژی مورد نیاز و کاربردهای متنوع با هدف بهبود ابزارهای کاربردی می باشد.
-
-لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.`
-
-export const missionaryNarratives = [
- {
- id: 'n-1',
- code: 1,
- title: 'سفر به شهر زیبای مشهد مقدس، شهر علم و ادب',
- submittedAt: '۱۴۰۰/۷/۱۹, ۸:۳۰',
- kind: NARRATIVE_KIND.manuscript.key,
- kindLabel: NARRATIVE_KIND.manuscript.label,
- kindTone: NARRATIVE_KIND.manuscript.tone,
- body: SAMPLE_BODY,
- likes: 12,
- comments: 56,
- },
- {
- id: 'n-2',
- code: 2,
- title: 'سفر به اصفهان نصف جهان',
- submittedAt: '۱۴۰۰/۷/۱۹, ۸:۳۰',
- kind: NARRATIVE_KIND.dispatched.key,
- kindLabel: NARRATIVE_KIND.dispatched.label,
- kindTone: NARRATIVE_KIND.dispatched.tone,
- body: SAMPLE_BODY,
- likes: 24,
- comments: 9,
- },
- {
- id: 'n-3',
- code: 3,
- title: 'روایت شماره سه',
- submittedAt: '۱۴۰۰/۸/۲, ۹:۱۵',
- kind: NARRATIVE_KIND.manuscript.key,
- kindLabel: NARRATIVE_KIND.manuscript.label,
- kindTone: NARRATIVE_KIND.manuscript.tone,
- body: SAMPLE_BODY,
- likes: 3,
- comments: 1,
- },
-]
diff --git a/src/services/mock/routes/missionary-requests.js b/src/services/mock/routes/missionary-requests.js
index e07a849..c8546cd 100644
--- a/src/services/mock/routes/missionary-requests.js
+++ b/src/services/mock/routes/missionary-requests.js
@@ -1,13 +1,24 @@
import { register } from '@/services/mock/registry'
import { endpoints } from '@/services/api/endpoints'
-import { findOrThrow, updateById } from '@/services/mock/helpers'
import { missionaryRequests } from '@/services/mock/fixtures/missionary-requests'
+import { missionaryMemories } from '@/services/mock/fixtures/missionary-memories'
+import {
+ filterItems,
+ findOrThrow,
+ isoNow,
+ makeId,
+ paginate,
+ updateById,
+} from '@/services/mock/helpers'
-register('GET', endpoints.listMissionaryRequests, () => ({
- success: true,
- message: 'OK',
- data: missionaryRequests,
-}))
+const paginatedItems = (items, query) => {
+ const { data, meta } = paginate(items, query)
+ return { success: true, message: 'OK', data: { items: data, meta } }
+}
+
+register('GET', endpoints.listMissionaryRequests, ({ query }) =>
+ paginatedItems(filterItems(missionaryRequests, query, { status: 'eq' }), query)
+)
register('GET', endpoints.showMissionaryRequest, ({ params }) => ({
success: true,
@@ -20,3 +31,68 @@ register('PATCH', endpoints.changeMissionaryRequestStatus, ({ params, data }) =>
message: 'Status updated.',
data: updateById(missionaryRequests, params.id, { status: data.status }),
}))
+
+register('GET', endpoints.listMissionaryMemories, ({ query }) =>
+ paginatedItems(missionaryMemories, query)
+)
+
+register('POST', endpoints.createMissionaryMemory, ({ data }) => {
+ const memory = {
+ id: makeId(),
+ missionaryId: 8,
+ title: data.title || '',
+ description: data.description || '',
+ missionaryRequestId: data.missionaryRequestId ?? null,
+ media: (data.mediaIds ?? []).map((id) => ({
+ id,
+ collectionName: 'memory',
+ fileName: `memory-${id}`,
+ mimeType: 'image/png',
+ fileSize: 0,
+ url: '',
+ downloadUrl: '',
+ })),
+ createdAt: isoNow(),
+ }
+ missionaryMemories.unshift(memory)
+ return { success: true, message: 'Memory created.', data: memory }
+})
+
+register('PATCH', endpoints.updateMissionaryMemory, ({ params, data }) => {
+ const memory = findOrThrow(missionaryMemories, params.id)
+ if (data.title != null) memory.title = data.title
+ if (data.description != null) memory.description = data.description
+ const appended = (data.mediaIds ?? []).map((id) => ({
+ id,
+ collectionName: 'memory',
+ fileName: `memory-${id}`,
+ mimeType: 'image/png',
+ fileSize: 0,
+ url: '',
+ downloadUrl: '',
+ }))
+ memory.media = [...(memory.media ?? []), ...appended]
+ return { success: true, message: 'Memory updated.', data: memory }
+})
+
+register('DELETE', endpoints.deleteMissionaryMemory, ({ params }) => {
+ const index = missionaryMemories.findIndex((m) => String(m.id) === String(params.id))
+ if (index >= 0) missionaryMemories.splice(index, 1)
+ return { success: true, message: 'Memory deleted.', data: null }
+})
+
+register('GET', endpoints.listAdminMissionaryRequests, ({ query }) =>
+ paginatedItems(filterItems(missionaryRequests, query, { status: 'eq' }), query)
+)
+
+register('GET', endpoints.showAdminMissionaryRequest, ({ params }) => ({
+ success: true,
+ message: 'OK',
+ data: findOrThrow(missionaryRequests, params.id),
+}))
+
+register('PATCH', endpoints.changeAdminMissionaryRequestStatus, ({ params, data }) => ({
+ success: true,
+ message: 'Status updated.',
+ data: updateById(missionaryRequests, params.id, { status: data.status }),
+}))
diff --git a/src/services/mock/routes/student-missionary.js b/src/services/mock/routes/student-missionary.js
deleted file mode 100644
index c087dc9..0000000
--- a/src/services/mock/routes/student-missionary.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { paginate } from '@/services/mock/helpers'
-import { register } from '@/services/mock/registry'
-import { endpoints } from '@/services/api/endpoints'
-import {
- missionaryDispatches,
- missionaryNarratives,
- missionaryProfile,
- missionaryRequests,
-} from '@/services/mock/fixtures/student-missionary'
-
-register('GET', endpoints.getMissionaryProfile, () => ({ data: missionaryProfile }))
-
-register('GET', endpoints.getMissionaryRequests, ({ query }) => paginate(missionaryRequests, query))
-
-register('GET', endpoints.getMissionaryDispatches, ({ query }) =>
- paginate(missionaryDispatches, query)
-)
-
-register('GET', endpoints.getMissionaryNarratives, ({ query }) =>
- paginate(missionaryNarratives, query)
-)
diff --git a/src/services/query/admin-missionary-requests.js b/src/services/query/admin-missionary-requests.js
new file mode 100644
index 0000000..ee829e9
--- /dev/null
+++ b/src/services/query/admin-missionary-requests.js
@@ -0,0 +1,28 @@
+import { cleanFilters } from '@/utils/clean-filters'
+import { useMutation, useQuery } from '@tanstack/vue-query'
+import {
+ apiChangeAdminMissionaryRequestStatus,
+ apiListAdminMissionaryRequests,
+} from '@/services/api/admin-missionary-requests'
+
+export const adminMissionaryRequestsKeys = {
+ all: ['admin', 'missionary-requests'],
+ list: (filters, pagination) => ['admin', 'missionary-requests', 'list', filters, pagination],
+}
+
+export const useAdminMissionaryRequestsListQuery = (filtersRef, paginationRef, options = {}) =>
+ useQuery({
+ queryKey: ['admin', 'missionary-requests', 'list', filtersRef, paginationRef],
+ queryFn: () =>
+ apiListAdminMissionaryRequests({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
+ select: (response) => ({
+ data: response?.data?.items ?? response?.data ?? [],
+ meta: response?.data?.meta ?? response?.meta,
+ }),
+ ...options,
+ })
+
+export const useChangeAdminMissionaryRequestStatusMutation = () =>
+ useMutation({
+ mutationFn: ({ id, payload }) => apiChangeAdminMissionaryRequestStatus(id, payload),
+ })
diff --git a/src/services/query/missionary-memories.js b/src/services/query/missionary-memories.js
new file mode 100644
index 0000000..162758b
--- /dev/null
+++ b/src/services/query/missionary-memories.js
@@ -0,0 +1,34 @@
+import { cleanFilters } from '@/utils/clean-filters'
+import { useMutation, useQuery } from '@tanstack/vue-query'
+import {
+ apiCreateMissionaryMemory,
+ apiDeleteMissionaryMemory,
+ apiListMissionaryMemories,
+ apiUpdateMissionaryMemory,
+} from '@/services/api/missionary-memories'
+
+export const missionaryMemoriesKeys = {
+ all: ['missionary', 'memories'],
+ list: (filters, pagination) => ['missionary', 'memories', 'list', filters, pagination],
+}
+
+export const useMissionaryMemoriesListQuery = (filtersRef, paginationRef, options = {}) =>
+ useQuery({
+ queryKey: ['missionary', 'memories', 'list', filtersRef, paginationRef],
+ queryFn: () =>
+ apiListMissionaryMemories({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
+ select: (response) => ({
+ data: response?.data ?? [],
+ meta: response?.data?.meta ?? response?.meta,
+ }),
+ ...options,
+ })
+
+export const useCreateMissionaryMemoryMutation = () =>
+ useMutation({ mutationFn: apiCreateMissionaryMemory })
+
+export const useUpdateMissionaryMemoryMutation = () =>
+ useMutation({ mutationFn: ({ id, payload }) => apiUpdateMissionaryMemory(id, payload) })
+
+export const useDeleteMissionaryMemoryMutation = () =>
+ useMutation({ mutationFn: apiDeleteMissionaryMemory })
diff --git a/src/services/query/missionary-requests.js b/src/services/query/missionary-requests.js
index 0a53411..c79e4c2 100644
--- a/src/services/query/missionary-requests.js
+++ b/src/services/query/missionary-requests.js
@@ -1,3 +1,4 @@
+import { cleanFilters } from '@/utils/clean-filters'
import { useMutation, useQuery } from '@tanstack/vue-query'
import {
apiChangeMissionaryRequestStatus,
@@ -7,15 +8,19 @@ import {
export const missionaryRequestsKeys = {
all: ['missionary', 'requests'],
- list: () => ['missionary', 'requests', 'list'],
+ list: (filters, pagination) => ['missionary', 'requests', 'list', filters, pagination],
detail: (id) => ['missionary', 'requests', 'detail', id],
}
-export const useMissionaryRequestsListQuery = (options = {}) =>
+export const useMissionaryRequestsListQuery = (filtersRef, paginationRef, options = {}) =>
useQuery({
- queryKey: missionaryRequestsKeys.list(),
- queryFn: () => apiListMissionaryRequests(),
- select: (response) => response?.data ?? [],
+ queryKey: ['missionary', 'requests', 'list', filtersRef, paginationRef],
+ queryFn: () =>
+ apiListMissionaryRequests({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
+ select: (response) => ({
+ data: response?.data ?? [],
+ meta: response?.data?.meta ?? response?.meta,
+ }),
...options,
})
diff --git a/src/services/query/student-missionary.js b/src/services/query/student-missionary.js
deleted file mode 100644
index f2fdd02..0000000
--- a/src/services/query/student-missionary.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import { useQuery } from '@tanstack/vue-query'
-import { cleanFilters } from '@/utils/clean-filters'
-import {
- apiGetMissionaryDispatches,
- apiGetMissionaryNarratives,
- apiGetMissionaryProfile,
- apiGetMissionaryRequests,
-} from '@/services/api/student-missionary'
-
-export const missionaryKeys = {
- profile: ['student', 'missionary', 'profile'],
- requests: (filters, pagination) => ['student', 'missionary', 'requests', filters, pagination],
- dispatches: (filters, pagination) => ['student', 'missionary', 'dispatches', filters, pagination],
- narratives: (filters, pagination) => ['student', 'missionary', 'narratives', filters, pagination],
-}
-
-export const useMissionaryProfileQuery = (options = {}) =>
- useQuery({
- queryKey: missionaryKeys.profile,
- queryFn: () => apiGetMissionaryProfile(),
- select: (response) => response?.data ?? response,
- ...options,
- })
-
-export const useMissionaryRequestsQuery = (filtersRef, paginationRef, options = {}) =>
- useQuery({
- queryKey: ['student', 'missionary', 'requests', filtersRef, paginationRef],
- queryFn: () =>
- apiGetMissionaryRequests({
- ...cleanFilters(filtersRef.value),
- ...paginationRef.value,
- }),
- ...options,
- })
-
-export const useMissionaryDispatchesQuery = (filtersRef, paginationRef, options = {}) =>
- useQuery({
- queryKey: ['student', 'missionary', 'dispatches', filtersRef, paginationRef],
- queryFn: () =>
- apiGetMissionaryDispatches({
- ...cleanFilters(filtersRef.value),
- ...paginationRef.value,
- }),
- ...options,
- })
-
-export const useMissionaryNarrativesQuery = (filtersRef, paginationRef, options = {}) =>
- useQuery({
- queryKey: ['student', 'missionary', 'narratives', filtersRef, paginationRef],
- queryFn: () =>
- apiGetMissionaryNarratives({
- ...cleanFilters(filtersRef.value),
- ...paginationRef.value,
- }),
- ...options,
- })