fix: bugs
Deploy banu-front / deploy (push) Successful in 1m28s

This commit is contained in:
sajjadtalkhabi
2026-07-09 13:25:59 +03:30
parent 38a396ba21
commit da34813b1d
45 changed files with 2486 additions and 1048 deletions
@@ -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)
+9 -5
View File
@@ -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 = {}) =>
+14
View File
@@ -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 }))
-13
View File
@@ -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 })
@@ -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',
},
]
@@ -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,
},
]
@@ -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 }),
}))
@@ -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)
)
@@ -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),
})
+34
View File
@@ -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 })
+10 -5
View File
@@ -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,
})
-56
View File
@@ -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,
})