fix: admin courses
This commit is contained in:
@@ -10,5 +10,6 @@ export const apiShowAdminConsultation = (id) =>
|
||||
export const apiSendAdminConsultationMessage = (id, payload) =>
|
||||
http.post(buildUrl(endpoints.sendConsultationMessage, { id }), payload)
|
||||
|
||||
// Backend uses PATCH (not POST).
|
||||
export const apiChangeAdminConsultationStatus = (id, payload) =>
|
||||
http.post(buildUrl(endpoints.changeConsultationStatus, { id }), payload)
|
||||
http.patch(buildUrl(endpoints.changeConsultationStatus, { id }), payload)
|
||||
|
||||
@@ -15,6 +15,9 @@ export const apiDeleteAdminCourse = (id) => http.delete(buildUrl(endpoints.delet
|
||||
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)
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ 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 apiAddAdminTermStudents = (termId, payload) =>
|
||||
http.post(buildUrl(endpoints.addUserTerm, { termId }), payload)
|
||||
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { http } from '@/services/api/http'
|
||||
import { endpoints } from '@/services/api/endpoints'
|
||||
|
||||
// Backend (Postman: Auth) is the source of truth. Payloads use the backend's
|
||||
// field names — `name`, `phone`, `password`, `password_confirmation`,
|
||||
// `device_name`, `avatar_media_id` — and responses are passed through unchanged.
|
||||
// Callers (forms / store) are responsible for translating UI-local field names
|
||||
// (firstName/lastName/phoneNumber) into this shape.
|
||||
|
||||
export const apiLogin = (payload) => http.post(endpoints.login, payload)
|
||||
|
||||
export const apiLoginWithCode = (payload) => http.post(endpoints.loginWithCode2Step, payload)
|
||||
@@ -25,7 +31,12 @@ export const apiGetMe = () => http.get(endpoints.me)
|
||||
|
||||
export const apiCompleteProfile = (payload) => http.post(endpoints.completeProfile, payload)
|
||||
|
||||
export const apiUpdateProfile = (payload) => http.put(endpoints.updateProfile, payload)
|
||||
export const apiUpdateProfile = (payload) => http.patch(endpoints.updateProfile, payload)
|
||||
|
||||
// `data` is an array of `{ key, value }` entries. The backend echoes whatever
|
||||
// we POST back from the GET, so this is effectively a free-form key/value bag.
|
||||
export const apiGetRegisterData = () => http.get(endpoints.registerData)
|
||||
export const apiSaveRegisterData = (data) => http.post(endpoints.registerData, { data })
|
||||
|
||||
export const apiGetRegistrationQuestionVideo = () =>
|
||||
http.get(endpoints.getRegistrationQuestionVideo)
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
export const endpoints = {
|
||||
// ─── Auth & profile ────────────────────────────────────────────────────────
|
||||
register: '/register',
|
||||
// Backend-aligned (Postman: Auth collection)
|
||||
register: '/auth/register',
|
||||
login: '/auth/login',
|
||||
logout: '/auth/logout',
|
||||
me: '/auth/me',
|
||||
updateProfile: '/me',
|
||||
registerData: '/me/register-data',
|
||||
|
||||
// FE-only — NOT in backend Postman doc; kept so existing UI/mock layer works.
|
||||
resendVerificationCodeForRegister: '/resend-verification-code',
|
||||
verifyCode: '/verify-code',
|
||||
completeProfile: '/complete-profile',
|
||||
getRegistrationQuestionVideo: '/verification-registration-media',
|
||||
login: '/login',
|
||||
loginWithCode2Step: '/login-with-code',
|
||||
forgotPassword: '/forgot-password',
|
||||
verifyForgotPasswordCode: '/verify-forgot-password-code',
|
||||
resetPassword: '/reset-password',
|
||||
logout: '/logout',
|
||||
me: '/auth/me',
|
||||
updateProfile: '/profile',
|
||||
|
||||
// ─── Geo ───────────────────────────────────────────────────────────────────
|
||||
provinceList: '/provinces',
|
||||
@@ -67,6 +71,7 @@ export const endpoints = {
|
||||
showTerm: '/terms/:id',
|
||||
updateTerm: '/terms/:id',
|
||||
deleteTerm: '/terms/:id',
|
||||
getTermAttendees: '/terms/:termId/attendees',
|
||||
|
||||
// ─── Backend-aligned: Courses ──────────────────────────────────────────────
|
||||
getCoursesList: '/courses',
|
||||
@@ -74,6 +79,7 @@ export const endpoints = {
|
||||
showCourse: '/courses/:id',
|
||||
updateCourse: '/courses/:id',
|
||||
deleteCourse: '/courses/:id',
|
||||
getCourseAttendees: '/courses/:courseId/attendees',
|
||||
|
||||
// ─── Backend-aligned: Sessions ─────────────────────────────────────────────
|
||||
getSessionsList: '/sessions',
|
||||
@@ -146,16 +152,19 @@ export const endpoints = {
|
||||
getAssignmentSubmissions: '/homeworks/:homeworkId/submissions',
|
||||
showAssignmentSubmission: '/homework-submissions/:submissionId',
|
||||
|
||||
// ─── Tickets / Consultations (out of scope of Terms/Courses backend doc) ───
|
||||
// ─── Backend-aligned: Tickets ─────────────────────────────────────────────
|
||||
// Admin "messages" UI talks to /admin/tickets.
|
||||
// Admin "consultations" UI talks to /counselor/tickets (same schema; the
|
||||
// counselor scope is the consultation pool).
|
||||
getTicketsList: '/admin/tickets',
|
||||
showTicket: '/admin/tickets/:id',
|
||||
sendTicketMessage: '/admin/tickets/:id/messages',
|
||||
changeTicketStatus: '/admin/tickets/:id/status',
|
||||
|
||||
getConsultationsList: '/admin/consultations',
|
||||
showConsultation: '/admin/consultations/:id',
|
||||
sendConsultationMessage: '/admin/consultations/:id/messages',
|
||||
changeConsultationStatus: '/admin/consultations/:id/status',
|
||||
getConsultationsList: '/counselor/tickets',
|
||||
showConsultation: '/counselor/tickets/:id',
|
||||
sendConsultationMessage: '/counselor/tickets/:id/messages',
|
||||
changeConsultationStatus: '/counselor/tickets/:id/status',
|
||||
}
|
||||
|
||||
export const buildUrl = (template, params = {}) =>
|
||||
|
||||
@@ -89,7 +89,7 @@ const createInstance = (baseUrl, { headers = {}, ...configs } = {}) => {
|
||||
addResponseUnwrapInterceptor(instance)
|
||||
addCamelizeInterceptor(instance)
|
||||
addSnakizeInterceptor(instance)
|
||||
addUnauthorizeInterceptor(instance)
|
||||
// addUnauthorizeInterceptor(instance)
|
||||
return instance
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ register('GET', endpoints.me, () => ({
|
||||
data: currentMe,
|
||||
}))
|
||||
|
||||
register('PUT', endpoints.updateProfile, ({ data }) => {
|
||||
register('PATCH', endpoints.updateProfile, ({ data }) => {
|
||||
Object.assign(currentMe, {
|
||||
name: data.name ?? currentMe.name,
|
||||
email: data.email ?? currentMe.email,
|
||||
|
||||
@@ -18,6 +18,10 @@ export const useAdminConsultationsListQuery = (filtersRef, paginationRef, option
|
||||
queryKey: ['admin', 'consultations', 'list', filtersRef, paginationRef],
|
||||
queryFn: () =>
|
||||
apiGetAdminConsultations({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
|
||||
select: (response) => ({
|
||||
data: response?.data ?? [],
|
||||
meta: response?.meta,
|
||||
}),
|
||||
...options,
|
||||
})
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
apiGetAdminCourseSessions,
|
||||
apiGetAdminCourseStudents,
|
||||
apiGetAdminCourses,
|
||||
apiGetCourseAttendees,
|
||||
apiRemoveAdminCourseStudent,
|
||||
apiShowAdminCourse,
|
||||
apiUpdateAdminCourse,
|
||||
@@ -78,6 +79,21 @@ export const useAdminCourseStudentsQuery = (courseIdRef, filtersRef, paginationR
|
||||
...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),
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
apiGetAdminTerms,
|
||||
apiGetAdminTermCourses,
|
||||
apiGetAdminTermStudents,
|
||||
apiGetTermAttendees,
|
||||
apiRemoveAdminTermCourse,
|
||||
apiRemoveAdminTermStudent,
|
||||
apiShowAdminTerm,
|
||||
@@ -28,6 +29,13 @@ export const adminTermsKeys = {
|
||||
filters,
|
||||
pagination,
|
||||
],
|
||||
attendees: (termId, filters, pagination) => [
|
||||
'terms',
|
||||
'attendees',
|
||||
termId,
|
||||
filters,
|
||||
pagination,
|
||||
],
|
||||
courses: (termId, filters, pagination) => [
|
||||
'admin',
|
||||
'terms',
|
||||
@@ -80,6 +88,21 @@ export const useAdminTermStudentsQuery = (termIdRef, filtersRef, paginationRef,
|
||||
...options,
|
||||
})
|
||||
|
||||
export const useTermAttendeesQuery = (termIdRef, filtersRef, paginationRef, options = {}) =>
|
||||
useQuery({
|
||||
queryKey: ['terms', 'attendees', termIdRef, filtersRef, paginationRef],
|
||||
queryFn: () =>
|
||||
apiGetTermAttendees(termIdRef.value, {
|
||||
...cleanFilters(filtersRef?.value || {}),
|
||||
...paginationRef?.value,
|
||||
}),
|
||||
select: (response) => ({
|
||||
data: response?.data?.items ?? response?.data ?? [],
|
||||
meta: response?.data?.meta ?? response?.meta,
|
||||
}),
|
||||
...options,
|
||||
})
|
||||
|
||||
export const useAddAdminTermStudentsMutation = () =>
|
||||
useMutation({
|
||||
mutationFn: ({ termId, payload }) => apiAddAdminTermStudents(termId, payload),
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
apiCompleteProfile,
|
||||
apiForgotPassword,
|
||||
apiGetMe,
|
||||
apiGetRegisterData,
|
||||
apiGetRegistrationQuestionVideo,
|
||||
apiLogin,
|
||||
apiLoginWithCode,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
apiRegister,
|
||||
apiResendVerificationCode,
|
||||
apiResetPassword,
|
||||
apiSaveRegisterData,
|
||||
apiUpdateProfile,
|
||||
apiUploadMedia,
|
||||
apiVerifyCode,
|
||||
@@ -20,6 +22,7 @@ import {
|
||||
export const authKeys = {
|
||||
me: () => ['auth', 'me'],
|
||||
registrationVideo: () => ['auth', 'registration-video'],
|
||||
registerData: () => ['auth', 'register-data'],
|
||||
}
|
||||
|
||||
export const useLoginMutation = () => useMutation({ mutationFn: apiLogin })
|
||||
@@ -63,3 +66,19 @@ export const useGetRegistrationVideoQuery = (options = {}) =>
|
||||
})
|
||||
|
||||
export const useUploadMediaMutation = () => useMutation({ mutationFn: apiUploadMedia })
|
||||
|
||||
export const useGetRegisterDataQuery = (options = {}) =>
|
||||
useQuery({
|
||||
queryKey: authKeys.registerData(),
|
||||
queryFn: () => apiGetRegisterData(),
|
||||
select: (response) => {
|
||||
const data = response?.data ?? response
|
||||
if (Array.isArray(data)) return data
|
||||
if (Array.isArray(data?.data)) return data.data
|
||||
return []
|
||||
},
|
||||
...options,
|
||||
})
|
||||
|
||||
export const useSaveRegisterDataMutation = () =>
|
||||
useMutation({ mutationFn: apiSaveRegisterData })
|
||||
|
||||
@@ -7,11 +7,48 @@ export const commonKeys = {
|
||||
cities: (provinceId) => ['common', 'cities', provinceId],
|
||||
}
|
||||
|
||||
// Sample data so the register form is usable while the backend geo endpoints
|
||||
// are still being built. Once /provinces and /provinces/:id/cities are live,
|
||||
// the real response overwrites these and the keys stay the same.
|
||||
const SAMPLE_PROVINCES = [
|
||||
{ id: 1, name: 'تهران' },
|
||||
{ id: 2, name: 'اصفهان' },
|
||||
{ id: 3, name: 'فارس' },
|
||||
{ id: 4, name: 'خراسان رضوی' },
|
||||
{ id: 5, name: 'قم' },
|
||||
]
|
||||
|
||||
const SAMPLE_CITIES_BY_PROVINCE = {
|
||||
1: [
|
||||
{ id: 101, name: 'تهران' },
|
||||
{ id: 102, name: 'ری' },
|
||||
{ id: 103, name: 'شمیرانات' },
|
||||
{ id: 104, name: 'اسلامشهر' },
|
||||
],
|
||||
2: [
|
||||
{ id: 201, name: 'اصفهان' },
|
||||
{ id: 202, name: 'کاشان' },
|
||||
{ id: 203, name: 'نجفآباد' },
|
||||
],
|
||||
3: [
|
||||
{ id: 301, name: 'شیراز' },
|
||||
{ id: 302, name: 'مرودشت' },
|
||||
{ id: 303, name: 'کازرون' },
|
||||
],
|
||||
4: [
|
||||
{ id: 401, name: 'مشهد' },
|
||||
{ id: 402, name: 'نیشابور' },
|
||||
{ id: 403, name: 'سبزوار' },
|
||||
],
|
||||
5: [{ id: 501, name: 'قم' }],
|
||||
}
|
||||
|
||||
export const useGetProvincesQuery = (options = {}) =>
|
||||
useQuery({
|
||||
queryKey: commonKeys.provinces(),
|
||||
queryFn: () => apiGetProvinces(),
|
||||
select: (response) => response?.data ?? response,
|
||||
initialData: SAMPLE_PROVINCES,
|
||||
...options,
|
||||
})
|
||||
|
||||
@@ -20,5 +57,6 @@ export const useGetCitiesOfProvinceQuery = (provinceIdRef, options = {}) =>
|
||||
queryKey: ['common', 'cities', provinceIdRef],
|
||||
queryFn: () => apiGetCitiesOfProvince(provinceIdRef.value),
|
||||
select: (response) => response?.data ?? response,
|
||||
initialData: () => SAMPLE_CITIES_BY_PROVINCE[provinceIdRef.value] || [],
|
||||
...options,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user