fix: design
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
import { http } from '@/services/api/http'
|
||||
import { buildUrl, endpoints } from '@/services/api/endpoints'
|
||||
|
||||
export const apiGetAdminCourseTemplates = (params) =>
|
||||
http.get(endpoints.getCourseTemplatesList, { params })
|
||||
export const apiGetAdminCourseTemplates = (params) => http.get(endpoints.getCoursesList, { params })
|
||||
|
||||
export const apiShowAdminCourseTemplate = (id) =>
|
||||
http.get(buildUrl(endpoints.showCourseTemplate, { id }))
|
||||
export const apiShowAdminCourseTemplate = (id) => http.get(buildUrl(endpoints.showCourse, { id }))
|
||||
|
||||
export const apiAddAdminCourseTemplate = (payload) =>
|
||||
http.post(endpoints.addNewCourseTemplate, payload)
|
||||
export const apiAddAdminCourseTemplate = (payload) => http.post(endpoints.addNewCourse, payload)
|
||||
|
||||
export const apiUpdateAdminCourseTemplate = (id, payload) =>
|
||||
http.put(buildUrl(endpoints.updateCourseTemplate, { id }), payload)
|
||||
|
||||
@@ -8,7 +8,7 @@ export const apiShowAdminCourse = (id) => http.get(buildUrl(endpoints.showCourse
|
||||
export const apiAddAdminCourse = (payload) => http.post(endpoints.addNewCourse, payload)
|
||||
|
||||
export const apiUpdateAdminCourse = (id, payload) =>
|
||||
http.put(buildUrl(endpoints.updateCourse, { id }), payload)
|
||||
http.patch(buildUrl(endpoints.updateCourse, { id }), payload)
|
||||
|
||||
export const apiDeleteAdminCourse = (id) => http.delete(buildUrl(endpoints.deleteCourse, { id }))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export const apiShowAdminSession = (id) => http.get(buildUrl(endpoints.showSessi
|
||||
export const apiAddAdminSession = (payload) => http.post(endpoints.addNewSession, payload)
|
||||
|
||||
export const apiUpdateAdminSession = (id, payload) =>
|
||||
http.put(buildUrl(endpoints.updateSession, { id }), payload)
|
||||
http.patch(buildUrl(endpoints.updateSession, { id }), payload)
|
||||
|
||||
export const apiDeleteAdminSession = (id) => http.delete(buildUrl(endpoints.deleteSession, { id }))
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ export const apiShowAdminTerm = (id) => http.get(buildUrl(endpoints.showTerm, {
|
||||
export const apiAddAdminTerm = (payload) => http.post(endpoints.addNewTerm, payload)
|
||||
|
||||
export const apiUpdateAdminTerm = (id, payload) =>
|
||||
http.put(buildUrl(endpoints.updateTerm, { id }), payload)
|
||||
http.patch(buildUrl(endpoints.updateTerm, { id }), payload)
|
||||
|
||||
export const apiDeleteAdminTerm = (id) => http.delete(buildUrl(endpoints.deleteTerm, { id }))
|
||||
|
||||
|
||||
@@ -8,10 +8,10 @@ export const apiShowAdminUser = (id) => http.get(buildUrl(endpoints.showUserDeta
|
||||
export const apiAddAdminUser = (payload) => http.post(endpoints.addNewUser, payload)
|
||||
|
||||
export const apiUpdateAdminUser = (id, payload) =>
|
||||
http.put(buildUrl(endpoints.updateUser, { id }), payload)
|
||||
http.patch(buildUrl(endpoints.updateUser, { id }), payload)
|
||||
|
||||
export const apiUpdateAdminUserRole = (id, payload) =>
|
||||
http.post(buildUrl(endpoints.updateUserRole, { id }), payload)
|
||||
http.patch(buildUrl(endpoints.updateUserRole, { id }), payload)
|
||||
|
||||
export const apiChangeAdminUserStatus = (id, payload) =>
|
||||
http.post(buildUrl(endpoints.changeUserStatus, { id }), payload)
|
||||
|
||||
@@ -21,7 +21,7 @@ export const apiResetPassword = (payload) => http.post(endpoints.resetPassword,
|
||||
|
||||
export const apiLogout = () => http.post(endpoints.logout)
|
||||
|
||||
export const apiGetProfile = () => http.get(endpoints.getProfile)
|
||||
export const apiGetMe = () => http.get(endpoints.me)
|
||||
|
||||
export const apiCompleteProfile = (payload) => http.post(endpoints.completeProfile, payload)
|
||||
|
||||
@@ -30,4 +30,4 @@ export const apiUpdateProfile = (payload) => http.put(endpoints.updateProfile, p
|
||||
export const apiGetRegistrationQuestionVideo = () =>
|
||||
http.get(endpoints.getRegistrationQuestionVideo)
|
||||
|
||||
export const apiUploadTemporary = (formData) => http.post(endpoints.uploadMediaTemporary, formData)
|
||||
export const apiUploadMedia = (formData) => http.post(endpoints.uploadMedia, formData)
|
||||
|
||||
@@ -10,12 +10,12 @@ export const endpoints = {
|
||||
verifyForgotPasswordCode: '/verify-forgot-password-code',
|
||||
resetPassword: '/reset-password',
|
||||
logout: '/logout',
|
||||
uploadMediaTemporary: '/upload-temp',
|
||||
uploadMedia: '/media',
|
||||
|
||||
provinceList: '/provinces',
|
||||
citiesList: '/provinces/:provinceId/cities',
|
||||
|
||||
getProfile: '/profile',
|
||||
me: '/auth/me',
|
||||
updateProfile: '/profile',
|
||||
getStudentCourses: '/student/courses',
|
||||
showStudentCourse: '/student/courses/:id',
|
||||
@@ -59,11 +59,11 @@ export const endpoints = {
|
||||
changeUserStatus: '/admin/users/:id/status',
|
||||
deleteUser: '/admin/users/:id',
|
||||
|
||||
getTermsList: '/admin/terms',
|
||||
addNewTerm: '/admin/terms',
|
||||
showTerm: '/admin/terms/:id',
|
||||
updateTerm: '/admin/terms/:id',
|
||||
deleteTerm: '/admin/terms/:id',
|
||||
getTermsList: '/terms',
|
||||
addNewTerm: '/terms',
|
||||
showTerm: '/terms/:id',
|
||||
updateTerm: '/terms/:id',
|
||||
deleteTerm: '/terms/:id',
|
||||
cloneTerm: '/admin/terms/:id/clone',
|
||||
changeStatusTerm: '/admin/terms/:id/status',
|
||||
|
||||
@@ -76,18 +76,18 @@ export const endpoints = {
|
||||
addCourseTerm: '/admin/terms/:termId/courses',
|
||||
removeCourseTerm: '/admin/terms/:termId/courses/:courseId',
|
||||
|
||||
getCoursesList: '/admin/courses',
|
||||
addNewCourse: '/admin/courses',
|
||||
showCourse: '/admin/courses/:id',
|
||||
updateCourse: '/admin/courses/:id',
|
||||
deleteCourse: '/admin/courses/:id',
|
||||
getCoursesList: '/courses',
|
||||
addNewCourse: '/courses',
|
||||
showCourse: '/courses/:id',
|
||||
updateCourse: '/courses/:id',
|
||||
deleteCourse: '/courses/:id',
|
||||
changeStatusCourse: '/admin/courses/:id/toggle-status',
|
||||
|
||||
getCourseTemplatesList: '/admin/course-templates',
|
||||
addNewCourseTemplate: '/admin/course-templates',
|
||||
showCourseTemplate: '/admin/course-templates/:id',
|
||||
updateCourseTemplate: '/admin/course-templates/:id',
|
||||
deleteCourseTemplate: '/admin/course-templates/:id',
|
||||
getCourseTemplatesList: '/admin/courses',
|
||||
addNewCourseTemplate: '/admin/courses',
|
||||
showCourseTemplate: '/admin/courses/:id',
|
||||
updateCourseTemplate: '/admin/courses/:id',
|
||||
deleteCourseTemplate: '/admin/courses/:id',
|
||||
changeStatusCourseTemplate: '/admin/course-templates/:id/status',
|
||||
|
||||
listTemplateStudents: '/admin/course-templates/:templateId/students',
|
||||
@@ -98,11 +98,11 @@ export const endpoints = {
|
||||
attachTemplateSession: '/admin/course-templates/:templateId/sessions',
|
||||
detachTemplateSession: '/admin/course-templates/:templateId/sessions/:sessionId',
|
||||
|
||||
getSessionsList: '/admin/sessions',
|
||||
addNewSession: '/admin/sessions',
|
||||
showSession: '/admin/sessions/:id',
|
||||
updateSession: '/admin/sessions/:id',
|
||||
deleteSession: '/admin/sessions/:id',
|
||||
getSessionsList: '/sessions',
|
||||
addNewSession: '/sessions',
|
||||
showSession: '/sessions/:id',
|
||||
updateSession: '/sessions/:id',
|
||||
deleteSession: '/sessions/:id',
|
||||
changeStatusSession: '/admin/sessions/:id/toggle-status',
|
||||
|
||||
getSessionsAttendance: '/admin/sessions/:sessionId/attendances',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const TOKEN = 'user_token'
|
||||
const REFRESH_TOKEN = 'refresh_user_token'
|
||||
const USER_INFO = 'user_info'
|
||||
const TOKEN = 'accessToken'
|
||||
const REFRESH_TOKEN = 'refreshToken'
|
||||
const USER_INFO = 'userInfo'
|
||||
const STUDENT_REGISTRATION_INFO = 'StudentRegistration'
|
||||
|
||||
export const tokenService = {
|
||||
|
||||
@@ -37,53 +37,95 @@ export const adminCourseTemplates = [
|
||||
},
|
||||
]
|
||||
|
||||
// Offered courses carry both the spec keys (term_id/teacher_id/
|
||||
// description/capacity/is_active/cover_url) and the UI-only keys the
|
||||
// current frontend reads (image, template/templateId, term/teacher
|
||||
// nested objects, prerequisitesCount, startDate, endDate). See
|
||||
// docs/backend-api-todo.md.
|
||||
const makeTeacherSnapshot = (overrides) => ({
|
||||
id: overrides.id,
|
||||
// --- spec ---
|
||||
name: overrides.name ?? `${overrides.firstName ?? ''} ${overrides.lastName ?? ''}`.trim(),
|
||||
email: overrides.email ?? '',
|
||||
phone: overrides.phone ?? null,
|
||||
roles: overrides.roles ?? ['teacher'],
|
||||
avatarUrl: overrides.avatarUrl ?? null,
|
||||
avatarDownloadUrl: overrides.avatarDownloadUrl ?? null,
|
||||
createdAt: overrides.createdAt ?? '',
|
||||
// --- ui-only ---
|
||||
firstName: overrides.firstName ?? '',
|
||||
lastName: overrides.lastName ?? '',
|
||||
fullName:
|
||||
overrides.fullName ?? `${overrides.firstName ?? ''} ${overrides.lastName ?? ''}`.trim(),
|
||||
})
|
||||
|
||||
export const adminOfferedCourses = [
|
||||
{
|
||||
// --- spec ---
|
||||
id: 11,
|
||||
termId: 1,
|
||||
teacherId: 5,
|
||||
title: 'اصول اخلاق اسلامی - پاییز',
|
||||
description: 'دوره مقدماتی اخلاق اسلامی برای ترم پاییز.',
|
||||
capacity: 30,
|
||||
isActive: true,
|
||||
coverUrl: 'https://picsum.photos/seed/offered1/200/200',
|
||||
|
||||
// --- ui-only ---
|
||||
image: 'https://picsum.photos/seed/offered1/200/200',
|
||||
template: { id: 1, title: 'اصول اخلاق اسلامی' },
|
||||
templateId: 1,
|
||||
term: { id: 1, title: 'ترم پاییز ۱۴۰۴' },
|
||||
termId: 1,
|
||||
teacher: { id: 5, firstName: 'علی', lastName: 'حسنی', fullName: 'علی حسنی' },
|
||||
capacity: 30,
|
||||
isActive: true,
|
||||
teacher: makeTeacherSnapshot({ id: 5, firstName: 'علی', lastName: 'حسنی' }),
|
||||
prerequisitesCount: 0,
|
||||
startDate: '2025-09-23T00:00:00.000Z',
|
||||
endDate: '2025-11-20T00:00:00.000Z',
|
||||
createdAt: '2025-09-01T08:00:00.000Z',
|
||||
},
|
||||
{
|
||||
// --- spec ---
|
||||
id: 12,
|
||||
termId: 1,
|
||||
teacherId: 6,
|
||||
title: 'مفاهیم قرآنی - پاییز',
|
||||
description: 'مرور مفاهیم قرآنی به همراه تفسیر مختصر.',
|
||||
capacity: 25,
|
||||
isActive: true,
|
||||
coverUrl: 'https://picsum.photos/seed/offered2/200/200',
|
||||
|
||||
// --- ui-only ---
|
||||
image: 'https://picsum.photos/seed/offered2/200/200',
|
||||
template: { id: 2, title: 'مفاهیم قرآنی' },
|
||||
templateId: 2,
|
||||
term: { id: 1, title: 'ترم پاییز ۱۴۰۴' },
|
||||
termId: 1,
|
||||
teacher: { id: 6, firstName: 'حسین', lastName: 'مرادی', fullName: 'حسین مرادی' },
|
||||
capacity: 25,
|
||||
isActive: true,
|
||||
teacher: makeTeacherSnapshot({ id: 6, firstName: 'حسین', lastName: 'مرادی' }),
|
||||
prerequisitesCount: 1,
|
||||
startDate: '2025-10-01T00:00:00.000Z',
|
||||
endDate: '2025-12-15T00:00:00.000Z',
|
||||
createdAt: '2025-09-10T08:00:00.000Z',
|
||||
},
|
||||
{
|
||||
// --- spec ---
|
||||
id: 13,
|
||||
termId: 2,
|
||||
teacherId: 7,
|
||||
title: 'فقه عبادات - زمستان',
|
||||
description: 'مرور احکام عملی نماز و روزه برای ترم زمستان.',
|
||||
capacity: 20,
|
||||
isActive: false,
|
||||
coverUrl: '',
|
||||
|
||||
// --- ui-only ---
|
||||
image: '',
|
||||
template: { id: 3, title: 'فقه عبادات' },
|
||||
templateId: 3,
|
||||
term: { id: 2, title: 'ترم زمستان ۱۴۰۴' },
|
||||
termId: 2,
|
||||
teacher: { id: 7, firstName: 'مهدی', lastName: 'سهرابی', fullName: 'مهدی سهرابی' },
|
||||
capacity: 20,
|
||||
isActive: false,
|
||||
teacher: makeTeacherSnapshot({ id: 7, firstName: 'مهدی', lastName: 'سهرابی' }),
|
||||
prerequisitesCount: 0,
|
||||
startDate: '2026-01-22T00:00:00.000Z',
|
||||
endDate: '2026-03-15T00:00:00.000Z',
|
||||
createdAt: '2025-12-10T08:00:00.000Z',
|
||||
},
|
||||
]
|
||||
|
||||
export { makeTeacherSnapshot }
|
||||
|
||||
@@ -1,6 +1,50 @@
|
||||
// Maps the FE's 7-value `sessionType` to the spec's 3-value `type`.
|
||||
const SESSION_TYPE_TO_SPEC = {
|
||||
in_person: 'offline',
|
||||
online: 'online',
|
||||
video: 'content',
|
||||
audio: 'content',
|
||||
text: 'content',
|
||||
slide: 'content',
|
||||
pdf: 'content',
|
||||
}
|
||||
|
||||
const makeSession = (overrides) => {
|
||||
const sessionType = overrides.sessionType ?? ''
|
||||
const sessionConfig = overrides.sessionConfig ?? {}
|
||||
const startsAt = overrides.startsAt ?? sessionConfig.startTime ?? null
|
||||
const location = overrides.location ?? sessionConfig.location ?? null
|
||||
const link = overrides.link ?? sessionConfig.meetingLink ?? null
|
||||
return {
|
||||
// --- spec ---
|
||||
id: overrides.id,
|
||||
courseId: overrides.courseId ?? null,
|
||||
title: overrides.title ?? '',
|
||||
description: overrides.description ?? '',
|
||||
type: overrides.type ?? SESSION_TYPE_TO_SPEC[sessionType] ?? null,
|
||||
startsAt,
|
||||
location,
|
||||
link,
|
||||
media: overrides.media ?? [],
|
||||
|
||||
// --- ui-only ---
|
||||
image: overrides.image ?? '',
|
||||
courseTemplate: overrides.courseTemplate ?? null,
|
||||
sessionType,
|
||||
sessionTypeFa: overrides.sessionTypeFa ?? '',
|
||||
durationMinutes: overrides.durationMinutes ?? 0,
|
||||
order: overrides.order ?? 1,
|
||||
sessionConfig,
|
||||
materials: overrides.materials ?? [],
|
||||
usedInTerms: overrides.usedInTerms ?? [],
|
||||
createdAt: overrides.createdAt ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export const adminSessions = [
|
||||
{
|
||||
makeSession({
|
||||
id: 101,
|
||||
courseId: 11,
|
||||
title: 'مقدمهای بر اخلاق اسلامی',
|
||||
description: 'جلسه نخست؛ تعاریف و چارچوب دوره.',
|
||||
image: 'https://picsum.photos/seed/session1/200/200',
|
||||
@@ -13,15 +57,14 @@ export const adminSessions = [
|
||||
startTime: '2025-09-25T16:00:00.000Z',
|
||||
location: 'سالن آمفیتئاتر ۲ - مدرسه قم',
|
||||
},
|
||||
materials: [],
|
||||
usedInTerms: [{ termId: 1 }],
|
||||
createdAt: '2025-09-10T08:00:00.000Z',
|
||||
},
|
||||
{
|
||||
}),
|
||||
makeSession({
|
||||
id: 102,
|
||||
courseId: 12,
|
||||
title: 'تفسیر سوره حمد',
|
||||
description: 'تحلیل آیات سوره حمد.',
|
||||
image: '',
|
||||
courseTemplate: { id: 2, title: 'مفاهیم قرآنی' },
|
||||
sessionType: 'online',
|
||||
sessionTypeFa: 'آنلاین',
|
||||
@@ -32,15 +75,14 @@ export const adminSessions = [
|
||||
platform: 'google_meet',
|
||||
startTime: '2025-10-04T19:00:00.000Z',
|
||||
},
|
||||
materials: [],
|
||||
usedInTerms: [{ termId: 1 }],
|
||||
createdAt: '2025-09-25T08:00:00.000Z',
|
||||
},
|
||||
{
|
||||
}),
|
||||
makeSession({
|
||||
id: 103,
|
||||
courseId: 13,
|
||||
title: 'احکام نماز جماعت',
|
||||
description: 'مرور احکام و شرایط نماز جماعت.',
|
||||
image: '',
|
||||
courseTemplate: { id: 3, title: 'فقه عبادات' },
|
||||
sessionType: 'video',
|
||||
sessionTypeFa: 'ویدئو',
|
||||
@@ -50,12 +92,12 @@ export const adminSessions = [
|
||||
minWatchedPercent: 80,
|
||||
mustCompleteBeforeNext: true,
|
||||
},
|
||||
materials: [],
|
||||
usedInTerms: [],
|
||||
createdAt: '2026-01-12T08:00:00.000Z',
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
export { makeSession, SESSION_TYPE_TO_SPEC }
|
||||
|
||||
export const sessionAttendance = new Map([
|
||||
[
|
||||
101,
|
||||
|
||||
@@ -1,42 +1,72 @@
|
||||
// Each term carries both the spec keys (title/description/is_active/
|
||||
// starts_at/ends_at/cover_url/created_at — camelized) and the UI-only
|
||||
// keys the current frontend reads (image, startDate, endDate,
|
||||
// studentsCount, coursesCount). See docs/backend-api-todo.md.
|
||||
const makeTerm = (overrides) => {
|
||||
const startsAt = overrides.startsAt ?? overrides.startDate ?? ''
|
||||
const endsAt = overrides.endsAt ?? overrides.endDate ?? ''
|
||||
const coverUrl = overrides.coverUrl ?? overrides.image ?? ''
|
||||
return {
|
||||
// --- spec ---
|
||||
id: overrides.id,
|
||||
title: overrides.title ?? '',
|
||||
description: overrides.description ?? '',
|
||||
isActive: overrides.isActive ?? true,
|
||||
startsAt,
|
||||
endsAt,
|
||||
coverUrl,
|
||||
createdAt: overrides.createdAt ?? '',
|
||||
|
||||
// --- ui-only ---
|
||||
image: coverUrl,
|
||||
startDate: startsAt,
|
||||
endDate: endsAt,
|
||||
studentsCount: overrides.studentsCount ?? 0,
|
||||
coursesCount: overrides.coursesCount ?? 0,
|
||||
}
|
||||
}
|
||||
|
||||
export const adminTerms = [
|
||||
{
|
||||
makeTerm({
|
||||
id: 1,
|
||||
title: 'ترم پاییز ۱۴۰۴',
|
||||
description: 'ترم پاییز با محوریت آموزش معارف اسلامی.',
|
||||
image: 'https://picsum.photos/seed/term1/200/200',
|
||||
startDate: '2025-09-23T00:00:00.000Z',
|
||||
endDate: '2026-01-20T00:00:00.000Z',
|
||||
coverUrl: 'https://picsum.photos/seed/term1/200/200',
|
||||
startsAt: '2025-09-23T00:00:00.000Z',
|
||||
endsAt: '2026-01-20T00:00:00.000Z',
|
||||
isActive: true,
|
||||
studentsCount: 18,
|
||||
coursesCount: 4,
|
||||
createdAt: '2025-08-01T08:00:00.000Z',
|
||||
},
|
||||
{
|
||||
}),
|
||||
makeTerm({
|
||||
id: 2,
|
||||
title: 'ترم زمستان ۱۴۰۴',
|
||||
description: 'ترم زمستان با تمرکز بر فقه و اصول.',
|
||||
image: 'https://picsum.photos/seed/term2/200/200',
|
||||
startDate: '2026-01-21T00:00:00.000Z',
|
||||
endDate: '2026-04-20T00:00:00.000Z',
|
||||
coverUrl: 'https://picsum.photos/seed/term2/200/200',
|
||||
startsAt: '2026-01-21T00:00:00.000Z',
|
||||
endsAt: '2026-04-20T00:00:00.000Z',
|
||||
isActive: true,
|
||||
studentsCount: 12,
|
||||
coursesCount: 3,
|
||||
createdAt: '2025-11-10T08:00:00.000Z',
|
||||
},
|
||||
{
|
||||
}),
|
||||
makeTerm({
|
||||
id: 3,
|
||||
title: 'ترم بهار ۱۴۰۵',
|
||||
description: 'ترم بهار ویژه دورههای تخصصی.',
|
||||
image: '',
|
||||
startDate: '2026-04-21T00:00:00.000Z',
|
||||
endDate: '2026-08-21T00:00:00.000Z',
|
||||
coverUrl: '',
|
||||
startsAt: '2026-04-21T00:00:00.000Z',
|
||||
endsAt: '2026-08-21T00:00:00.000Z',
|
||||
isActive: false,
|
||||
studentsCount: 0,
|
||||
coursesCount: 2,
|
||||
createdAt: '2026-02-15T08:00:00.000Z',
|
||||
},
|
||||
}),
|
||||
]
|
||||
|
||||
export { makeTerm }
|
||||
|
||||
export const termStudentLinks = new Map([
|
||||
[
|
||||
1,
|
||||
|
||||
@@ -66,16 +66,26 @@ const makeUser = (i, overrides = {}) => {
|
||||
const province = provinceById((i % 4) + 1)
|
||||
const city = cityForProvince(province.id)
|
||||
const role = roles[(i % 4) + 1]?.name || 'student'
|
||||
const id = 100 + i
|
||||
const phoneNumber = `0912${String(1_000_000 + i).padStart(7, '0')}`
|
||||
return {
|
||||
id: 100 + i,
|
||||
// --- spec (GET /admin/users) ---
|
||||
id,
|
||||
name: `${firstName} ${lastName}`,
|
||||
email: `user${id}@example.com`,
|
||||
phone: phoneNumber.replace(/^0/, '+98'),
|
||||
roles: [role],
|
||||
avatarUrl: '',
|
||||
avatarDownloadUrl: '',
|
||||
createdAt: new Date(Date.now() - i * 86_400_000).toISOString(),
|
||||
|
||||
// --- ui-only (pending backend) ---
|
||||
firstName,
|
||||
lastName,
|
||||
fullName: `${firstName} ${lastName}`,
|
||||
phoneNumber: `0912${String(1_000_000 + i).padStart(7, '0')}`,
|
||||
phoneNumber,
|
||||
nationalCode: String(1_000_000_000 + i * 7).padStart(10, '0'),
|
||||
avatarUrl: '',
|
||||
status: i % 5 === 0 ? 'pending' : 'approved',
|
||||
roles: [role],
|
||||
roleId: roles.find((r) => r.name === role)?.id,
|
||||
address: {
|
||||
address: 'آدرس نمونه',
|
||||
@@ -88,7 +98,6 @@ const makeUser = (i, overrides = {}) => {
|
||||
maritalStatus: i % 2 ? 'single' : 'married',
|
||||
gender: i % 2 ? 'female' : 'male',
|
||||
},
|
||||
createdAt: new Date(Date.now() - i * 86_400_000).toISOString(),
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
export const currentProfile = {
|
||||
id: 1,
|
||||
// Backend `/auth/me` currently returns the keys marked "spec" below.
|
||||
// The "ui-only" keys are extras the UI already depends on (edit-profile form,
|
||||
// avatars, address, etc.) — they must be preserved in the mock until the
|
||||
// backend completes the response model. See docs/backend-api-todo.md.
|
||||
export const currentMe = {
|
||||
// --- spec (GET /auth/me) ---
|
||||
id: 5,
|
||||
name: 'سجاد محمدی',
|
||||
email: 'sajjad@example.com',
|
||||
phone: '+989121234567',
|
||||
roles: ['student'],
|
||||
avatarUrl: 'http://localhost:8080/storage/users/5/avatar.jpg',
|
||||
avatarDownloadUrl: 'http://localhost:8080/api/media/9/download',
|
||||
createdAt: '2026-02-01T10:00:00+00:00',
|
||||
|
||||
// --- ui-only (pending backend) ---
|
||||
firstName: 'سجاد',
|
||||
lastName: 'محمدی',
|
||||
fullName: 'سجاد محمدی',
|
||||
phoneNumber: '09123456789',
|
||||
nationalCode: '0079827498',
|
||||
avatarUrl: '',
|
||||
status: 'approved',
|
||||
roles: ['user'],
|
||||
address: {
|
||||
address: 'خیابان آزادی، پلاک ۱۲',
|
||||
province: { id: 1, name: 'تهران' },
|
||||
@@ -2,7 +2,11 @@ import { register } from '@/services/mock/registry'
|
||||
import { endpoints } from '@/services/api/endpoints'
|
||||
import { adminTerms } from '@/services/mock/fixtures/admin-terms'
|
||||
import { adminUsers } from '@/services/mock/fixtures/admin-users'
|
||||
import { adminCourseTemplates, adminOfferedCourses } from '@/services/mock/fixtures/admin-courses'
|
||||
import {
|
||||
adminCourseTemplates,
|
||||
adminOfferedCourses,
|
||||
makeTeacherSnapshot,
|
||||
} from '@/services/mock/fixtures/admin-courses'
|
||||
import {
|
||||
filterDateRange,
|
||||
filterItems,
|
||||
@@ -99,66 +103,128 @@ register('GET', endpoints.getCoursesList, ({ query }) => {
|
||||
status: (item, v) => String(item.isActive ? 1 : 0) === String(v),
|
||||
})
|
||||
list = filterDateRange(list, query, 'startDate')
|
||||
return paginate(list, query)
|
||||
const { data: items, meta } = paginate(list, query)
|
||||
return {
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: { items, meta },
|
||||
}
|
||||
})
|
||||
|
||||
register('GET', endpoints.showCourse, ({ params }) => ({
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: findOrThrow(adminOfferedCourses, params.id),
|
||||
}))
|
||||
|
||||
register('POST', endpoints.addNewCourse, ({ data }) => {
|
||||
const template = adminCourseTemplates.find((t) => t.id === Number(data.templateId))
|
||||
const term = adminTerms.find((t) => t.id === Number(data.termId))
|
||||
const teacher = adminUsers.find((u) => u.id === Number(data.teacherId))
|
||||
const teacherSource = adminUsers.find((u) => u.id === Number(data.teacherId))
|
||||
const teacher = teacherSource
|
||||
? makeTeacherSnapshot({
|
||||
id: teacherSource.id,
|
||||
firstName: teacherSource.firstName,
|
||||
lastName: teacherSource.lastName,
|
||||
name: teacherSource.name,
|
||||
email: teacherSource.email,
|
||||
phone: teacherSource.phone,
|
||||
roles: teacherSource.roles,
|
||||
avatarUrl: teacherSource.avatarUrl,
|
||||
avatarDownloadUrl: teacherSource.avatarDownloadUrl,
|
||||
createdAt: teacherSource.createdAt,
|
||||
})
|
||||
: null
|
||||
const coverFromUpload = data.imageId
|
||||
? `https://picsum.photos/seed/offered-${data.imageId}/200/200`
|
||||
: ''
|
||||
const coverUrl = data.coverUrl ?? coverFromUpload
|
||||
const item = {
|
||||
// --- spec ---
|
||||
id: makeId(),
|
||||
termId: term?.id ?? Number(data.termId) ?? null,
|
||||
teacherId: teacher?.id ?? (Number(data.teacherId) || null),
|
||||
title: data.title || template?.title || '',
|
||||
image: data.imageId ? `https://picsum.photos/seed/offered-${data.imageId}/200/200` : '',
|
||||
description: data.description || '',
|
||||
capacity: Number(data.capacity) || 0,
|
||||
isActive: data.isActive !== undefined ? !!data.isActive : true,
|
||||
coverUrl,
|
||||
|
||||
// --- ui-only ---
|
||||
image: coverUrl,
|
||||
template: template ? { id: template.id, title: template.title } : null,
|
||||
templateId: template?.id,
|
||||
term: term ? { id: term.id, title: term.title } : null,
|
||||
termId: term?.id,
|
||||
teacher: teacher
|
||||
? {
|
||||
id: teacher.id,
|
||||
firstName: teacher.firstName,
|
||||
lastName: teacher.lastName,
|
||||
fullName: `${teacher.firstName} ${teacher.lastName}`,
|
||||
}
|
||||
: null,
|
||||
capacity: Number(data.capacity) || 0,
|
||||
isActive: !!data.isActive,
|
||||
teacher,
|
||||
prerequisitesCount: 0,
|
||||
startDate: term?.startDate || '',
|
||||
endDate: term?.endDate || '',
|
||||
createdAt: isoNow(),
|
||||
}
|
||||
adminOfferedCourses.unshift(item)
|
||||
return { data: item }
|
||||
return {
|
||||
success: true,
|
||||
message: 'Course created.',
|
||||
data: item,
|
||||
}
|
||||
})
|
||||
|
||||
register('PUT', endpoints.updateCourse, ({ params, data }) => {
|
||||
const template = adminCourseTemplates.find((t) => t.id === Number(data.templateId))
|
||||
const term = adminTerms.find((t) => t.id === Number(data.termId))
|
||||
register('PATCH', endpoints.updateCourse, ({ params, data }) => {
|
||||
const patch = {}
|
||||
if (data.title !== undefined) patch.title = data.title
|
||||
if (data.description !== undefined) patch.description = data.description
|
||||
if (data.capacity !== undefined) patch.capacity = Number(data.capacity) || 0
|
||||
if (data.isActive !== undefined) patch.isActive = !!data.isActive
|
||||
if (data.termId !== undefined) {
|
||||
patch.termId = Number(data.termId) || null
|
||||
const term = adminTerms.find((t) => t.id === Number(data.termId))
|
||||
if (term) patch.term = { id: term.id, title: term.title }
|
||||
}
|
||||
if (data.teacherId !== undefined) {
|
||||
patch.teacherId = Number(data.teacherId) || null
|
||||
const t = adminUsers.find((u) => u.id === Number(data.teacherId))
|
||||
if (t) {
|
||||
patch.teacher = makeTeacherSnapshot({
|
||||
id: t.id,
|
||||
firstName: t.firstName,
|
||||
lastName: t.lastName,
|
||||
name: t.name,
|
||||
email: t.email,
|
||||
phone: t.phone,
|
||||
roles: t.roles,
|
||||
avatarUrl: t.avatarUrl,
|
||||
avatarDownloadUrl: t.avatarDownloadUrl,
|
||||
createdAt: t.createdAt,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (data.templateId !== undefined) {
|
||||
const template = adminCourseTemplates.find((t) => t.id === Number(data.templateId))
|
||||
patch.templateId = template?.id
|
||||
if (template) patch.template = { id: template.id, title: template.title }
|
||||
}
|
||||
if (data.coverUrl !== undefined || data.imageId !== undefined) {
|
||||
const url =
|
||||
data.coverUrl ??
|
||||
(data.imageId ? `https://picsum.photos/seed/offered-${data.imageId}/200/200` : '')
|
||||
patch.coverUrl = url
|
||||
patch.image = url
|
||||
}
|
||||
const updated = updateById(adminOfferedCourses, params.id, patch)
|
||||
return {
|
||||
data: updateById(adminOfferedCourses, params.id, {
|
||||
title: data.title,
|
||||
image: data.imageId
|
||||
? `https://picsum.photos/seed/offered-${data.imageId}/200/200`
|
||||
: undefined,
|
||||
template: template ? { id: template.id, title: template.title } : undefined,
|
||||
templateId: template?.id,
|
||||
term: term ? { id: term.id, title: term.title } : undefined,
|
||||
termId: term?.id,
|
||||
capacity: Number(data.capacity) || 0,
|
||||
isActive: !!data.isActive,
|
||||
}),
|
||||
success: true,
|
||||
message: 'Course updated.',
|
||||
data: updated,
|
||||
}
|
||||
})
|
||||
|
||||
register('DELETE', endpoints.deleteCourse, ({ params }) => {
|
||||
removeById(adminOfferedCourses, params.id)
|
||||
return { data: { message: 'حذف موفق' } }
|
||||
return {
|
||||
success: true,
|
||||
message: 'Course deleted.',
|
||||
data: null,
|
||||
}
|
||||
})
|
||||
|
||||
register('POST', endpoints.changeStatusCourse, ({ params, data }) => ({
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { SESSION_TYPE } from '@/enums'
|
||||
import { register } from '@/services/mock/registry'
|
||||
import { endpoints } from '@/services/api/endpoints'
|
||||
import { adminCourseTemplates } from '@/services/mock/fixtures/admin-courses'
|
||||
import { adminSessions, sessionAttendance } from '@/services/mock/fixtures/admin-sessions'
|
||||
import { adminCourseTemplates, adminOfferedCourses } from '@/services/mock/fixtures/admin-courses'
|
||||
import {
|
||||
adminSessions,
|
||||
makeSession,
|
||||
sessionAttendance,
|
||||
} from '@/services/mock/fixtures/admin-sessions'
|
||||
import {
|
||||
filterDateRange,
|
||||
filterItems,
|
||||
@@ -19,27 +23,62 @@ const enrich = (session) => ({
|
||||
sessionTypeFa: SESSION_TYPE[session.sessionType] || session.sessionTypeFa || '',
|
||||
})
|
||||
|
||||
const courseSnapshot = (offered) =>
|
||||
offered
|
||||
? {
|
||||
id: offered.id,
|
||||
termId: offered.termId,
|
||||
teacherId: offered.teacherId,
|
||||
title: offered.title,
|
||||
description: offered.description,
|
||||
capacity: offered.capacity,
|
||||
isActive: offered.isActive,
|
||||
coverUrl: offered.coverUrl,
|
||||
}
|
||||
: null
|
||||
|
||||
register('GET', endpoints.getSessionsList, ({ query }) => {
|
||||
let list = filterItems(adminSessions, query, {
|
||||
title: 'includes',
|
||||
courseTemplateId: (item, v) => String(item.courseTemplate?.id) === String(v),
|
||||
courseId: 'eq',
|
||||
sessionType: 'eq',
|
||||
type: 'eq',
|
||||
})
|
||||
list = filterDateRange(list, query)
|
||||
// eslint-disable-next-line unicorn/no-array-callback-reference
|
||||
return paginate(list.map(enrich), query)
|
||||
const { data: items, meta } = paginate(list.map(enrich), query)
|
||||
return {
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: { items, meta },
|
||||
}
|
||||
})
|
||||
|
||||
register('GET', endpoints.showSession, ({ params }) => ({
|
||||
data: enrich(findOrThrow(adminSessions, params.id)),
|
||||
}))
|
||||
register('GET', endpoints.showSession, ({ params }) => {
|
||||
const session = enrich(findOrThrow(adminSessions, params.id))
|
||||
const offered = adminOfferedCourses.find((c) => c.id === session.courseId)
|
||||
return {
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: { ...session, course: courseSnapshot(offered) },
|
||||
}
|
||||
})
|
||||
|
||||
register('POST', endpoints.addNewSession, ({ data }) => {
|
||||
const tpl = adminCourseTemplates.find((c) => c.id === Number(data.courseTemplateId))
|
||||
const item = {
|
||||
const offered =
|
||||
adminOfferedCourses.find((c) => c.id === Number(data.courseId)) ||
|
||||
(tpl ? adminOfferedCourses.find((c) => c.templateId === tpl.id) : null)
|
||||
const item = makeSession({
|
||||
id: makeId(),
|
||||
courseId: data.courseId ?? offered?.id ?? null,
|
||||
title: data.title || '',
|
||||
description: data.description || '',
|
||||
type: data.type,
|
||||
startsAt: data.startsAt ?? data.sessionConfig?.startTime ?? null,
|
||||
location: data.location ?? data.sessionConfig?.location ?? null,
|
||||
link: data.link ?? data.sessionConfig?.meetingLink ?? null,
|
||||
image: data.imageId ? `https://picsum.photos/seed/session-${data.imageId}/200/200` : '',
|
||||
courseTemplate: tpl ? { id: tpl.id, title: tpl.title } : null,
|
||||
sessionType: data.sessionType || 'in_person',
|
||||
@@ -47,32 +86,65 @@ register('POST', endpoints.addNewSession, ({ data }) => {
|
||||
order: Number(data.order) || 1,
|
||||
sessionConfig: data.sessionConfig || {},
|
||||
materials: data.materials || [],
|
||||
usedInTerms: [],
|
||||
createdAt: isoNow(),
|
||||
}
|
||||
})
|
||||
adminSessions.unshift(item)
|
||||
return { data: enrich(item) }
|
||||
return {
|
||||
success: true,
|
||||
message: 'Session created.',
|
||||
data: enrich(item),
|
||||
}
|
||||
})
|
||||
|
||||
register('PUT', endpoints.updateSession, ({ params, data }) => {
|
||||
const tpl = adminCourseTemplates.find((c) => c.id === Number(data.courseTemplateId))
|
||||
const updated = updateById(adminSessions, params.id, {
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
image: data.imageId ? `https://picsum.photos/seed/session-${data.imageId}/200/200` : undefined,
|
||||
courseTemplate: tpl ? { id: tpl.id, title: tpl.title } : undefined,
|
||||
sessionType: data.sessionType,
|
||||
durationMinutes: Number(data.durationMinutes) || 0,
|
||||
order: Number(data.order) || 1,
|
||||
sessionConfig: data.sessionConfig ?? undefined,
|
||||
materials: data.materials ?? undefined,
|
||||
})
|
||||
return { data: enrich(updated) }
|
||||
register('PATCH', endpoints.updateSession, ({ params, data }) => {
|
||||
const patch = {}
|
||||
if (data.title !== undefined) patch.title = data.title
|
||||
if (data.description !== undefined) patch.description = data.description
|
||||
if (data.type !== undefined) patch.type = data.type
|
||||
if (data.startsAt !== undefined) patch.startsAt = data.startsAt
|
||||
if (data.location !== undefined) patch.location = data.location
|
||||
if (data.link !== undefined) patch.link = data.link
|
||||
if (data.courseId !== undefined) patch.courseId = Number(data.courseId) || null
|
||||
if (data.courseTemplateId !== undefined) {
|
||||
const tpl = adminCourseTemplates.find((c) => c.id === Number(data.courseTemplateId))
|
||||
if (tpl) patch.courseTemplate = { id: tpl.id, title: tpl.title }
|
||||
}
|
||||
if (data.sessionType !== undefined) patch.sessionType = data.sessionType
|
||||
if (data.durationMinutes !== undefined) patch.durationMinutes = Number(data.durationMinutes) || 0
|
||||
if (data.order !== undefined) patch.order = Number(data.order) || 1
|
||||
if (data.sessionConfig !== undefined) {
|
||||
patch.sessionConfig = data.sessionConfig
|
||||
if (data.startsAt === undefined && data.sessionConfig.startTime !== undefined) {
|
||||
patch.startsAt = data.sessionConfig.startTime
|
||||
}
|
||||
if (data.location === undefined && data.sessionConfig.location !== undefined) {
|
||||
patch.location = data.sessionConfig.location
|
||||
}
|
||||
if (data.link === undefined && data.sessionConfig.meetingLink !== undefined) {
|
||||
patch.link = data.sessionConfig.meetingLink
|
||||
}
|
||||
}
|
||||
if (data.materials !== undefined) patch.materials = data.materials
|
||||
if (data.imageId !== undefined) {
|
||||
patch.image = data.imageId
|
||||
? `https://picsum.photos/seed/session-${data.imageId}/200/200`
|
||||
: ''
|
||||
}
|
||||
const updated = updateById(adminSessions, params.id, patch)
|
||||
return {
|
||||
success: true,
|
||||
message: 'Session updated.',
|
||||
data: enrich(updated),
|
||||
}
|
||||
})
|
||||
|
||||
register('DELETE', endpoints.deleteSession, ({ params }) => {
|
||||
removeById(adminSessions, params.id)
|
||||
return { data: { message: 'حذف موفق' } }
|
||||
return {
|
||||
success: true,
|
||||
message: 'Session deleted.',
|
||||
data: null,
|
||||
}
|
||||
})
|
||||
|
||||
register('POST', endpoints.changeStatusSession, ({ params, data }) => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { register } from '@/services/mock/registry'
|
||||
import { endpoints } from '@/services/api/endpoints'
|
||||
import { adminUsers } from '@/services/mock/fixtures/admin-users'
|
||||
import { adminOfferedCourses } from '@/services/mock/fixtures/admin-courses'
|
||||
import { adminTerms, termStudentLinks } from '@/services/mock/fixtures/admin-terms'
|
||||
import { adminTerms, makeTerm, termStudentLinks } from '@/services/mock/fixtures/admin-terms'
|
||||
import {
|
||||
filterDateRange,
|
||||
filterItems,
|
||||
@@ -18,45 +18,85 @@ register('GET', endpoints.getTermsList, ({ query }) => {
|
||||
let list = filterItems(adminTerms, query, {
|
||||
title: 'includes',
|
||||
status: (item, v) => String(item.isActive ? 1 : 0) === String(v),
|
||||
activeOnly: (item, v) => (String(v) === '1' ? !!item.isActive : true),
|
||||
})
|
||||
list = filterDateRange(list, query, 'startDate')
|
||||
return paginate(list, query)
|
||||
const { data: items, meta } = paginate(list, query)
|
||||
return {
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: { items, meta },
|
||||
}
|
||||
})
|
||||
|
||||
register('GET', endpoints.showTerm, ({ params }) => ({
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: findOrThrow(adminTerms, params.id),
|
||||
}))
|
||||
|
||||
register('POST', endpoints.addNewTerm, ({ data }) => {
|
||||
const term = {
|
||||
const coverFromUpload = data.imageId
|
||||
? `https://picsum.photos/seed/term-${data.imageId}/200/200`
|
||||
: ''
|
||||
const term = makeTerm({
|
||||
id: makeId(),
|
||||
title: data.title || '',
|
||||
description: data.description || '',
|
||||
image: data.imageId ? `https://picsum.photos/seed/term-${data.imageId}/200/200` : '',
|
||||
startDate: data.startDate || '',
|
||||
endDate: data.endDate || '',
|
||||
isActive: true,
|
||||
isActive: data.isActive ?? true,
|
||||
startsAt: data.startsAt ?? data.startDate ?? '',
|
||||
endsAt: data.endsAt ?? data.endDate ?? '',
|
||||
coverUrl: data.coverUrl ?? coverFromUpload,
|
||||
studentsCount: 0,
|
||||
coursesCount: 0,
|
||||
createdAt: isoNow(),
|
||||
}
|
||||
})
|
||||
adminTerms.unshift(term)
|
||||
return { data: term }
|
||||
return {
|
||||
success: true,
|
||||
message: 'Term created.',
|
||||
data: term,
|
||||
}
|
||||
})
|
||||
|
||||
register('PUT', endpoints.updateTerm, ({ params, data }) => ({
|
||||
data: updateById(adminTerms, params.id, {
|
||||
title: data.title,
|
||||
description: data.description,
|
||||
startDate: data.startDate,
|
||||
endDate: data.endDate,
|
||||
image: data.imageId ? `https://picsum.photos/seed/term-${data.imageId}/200/200` : undefined,
|
||||
}),
|
||||
}))
|
||||
register('PATCH', endpoints.updateTerm, ({ params, data }) => {
|
||||
const coverFromUpload = data.imageId
|
||||
? `https://picsum.photos/seed/term-${data.imageId}/200/200`
|
||||
: undefined
|
||||
const startsAt = data.startsAt ?? data.startDate
|
||||
const endsAt = data.endsAt ?? data.endDate
|
||||
const coverUrl = data.coverUrl ?? coverFromUpload
|
||||
const patch = {}
|
||||
if (data.title !== undefined) patch.title = data.title
|
||||
if (data.description !== undefined) patch.description = data.description
|
||||
if (data.isActive !== undefined) patch.isActive = data.isActive
|
||||
if (startsAt !== undefined) {
|
||||
patch.startsAt = startsAt
|
||||
patch.startDate = startsAt
|
||||
}
|
||||
if (endsAt !== undefined) {
|
||||
patch.endsAt = endsAt
|
||||
patch.endDate = endsAt
|
||||
}
|
||||
if (coverUrl !== undefined) {
|
||||
patch.coverUrl = coverUrl
|
||||
patch.image = coverUrl
|
||||
}
|
||||
const updated = updateById(adminTerms, params.id, patch)
|
||||
return {
|
||||
success: true,
|
||||
message: 'Term updated.',
|
||||
data: updated,
|
||||
}
|
||||
})
|
||||
|
||||
register('DELETE', endpoints.deleteTerm, ({ params }) => {
|
||||
removeById(adminTerms, params.id)
|
||||
return { data: { message: 'حذف موفق' } }
|
||||
return {
|
||||
success: true,
|
||||
message: 'Term deleted.',
|
||||
data: null,
|
||||
}
|
||||
})
|
||||
|
||||
register('POST', endpoints.cloneTerm, ({ params }) => {
|
||||
|
||||
@@ -21,28 +21,51 @@ register('GET', endpoints.getApprovedUsers, ({ query }) => {
|
||||
roleId: (item, v) => String(item.roleId) === String(v),
|
||||
})
|
||||
list = filterDateRange(list, query)
|
||||
return paginate(list, query)
|
||||
const { data: items, meta } = paginate(list, query)
|
||||
return {
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: { items, meta },
|
||||
}
|
||||
})
|
||||
|
||||
register('GET', endpoints.showUserDetails, ({ params }) => ({
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: findOrThrow(adminUsers, params.id),
|
||||
}))
|
||||
|
||||
register('POST', endpoints.addNewUser, ({ data }) => {
|
||||
const id = makeId()
|
||||
const role = roles.find((r) => r.id === Number(data.roleId)) || null
|
||||
const roleByName = (n) => roles.find((r) => r.name === n)
|
||||
const role =
|
||||
roles.find((r) => r.id === Number(data.roleId)) ||
|
||||
(Array.isArray(data.roles) && data.roles[0] ? roleByName(data.roles[0]) : null) ||
|
||||
null
|
||||
const province = data.provinceId ? { id: data.provinceId, name: '' } : null
|
||||
const city = data.cityId ? { id: data.cityId, name: '' } : null
|
||||
const firstName = data.firstName || ''
|
||||
const lastName = data.lastName || ''
|
||||
const fullName = `${firstName} ${lastName}`.trim()
|
||||
const phoneNumber = data.phoneNumber || ''
|
||||
const user = {
|
||||
// --- spec ---
|
||||
id,
|
||||
firstName: data.firstName || '',
|
||||
lastName: data.lastName || '',
|
||||
fullName: `${data.firstName || ''} ${data.lastName || ''}`.trim(),
|
||||
phoneNumber: data.phoneNumber || '',
|
||||
nationalCode: data.nationalCode || '',
|
||||
name: data.name || fullName,
|
||||
email: data.email || '',
|
||||
phone: data.phone || (phoneNumber ? phoneNumber.replace(/^0/, '+98') : null),
|
||||
roles: Array.isArray(data.roles) ? data.roles : role ? [role.name] : [],
|
||||
avatarUrl: '',
|
||||
avatarDownloadUrl: null,
|
||||
createdAt: isoNow(),
|
||||
|
||||
// --- ui-only ---
|
||||
firstName,
|
||||
lastName,
|
||||
fullName,
|
||||
phoneNumber,
|
||||
nationalCode: data.nationalCode || '',
|
||||
status: 'approved',
|
||||
roles: role ? [role.name] : [],
|
||||
roleId: role?.id,
|
||||
address: { address: data.address || '', province, city },
|
||||
profile: {
|
||||
@@ -52,22 +75,39 @@ register('POST', endpoints.addNewUser, ({ data }) => {
|
||||
gender: data.gender || '',
|
||||
avatarId: data.avatarId || null,
|
||||
},
|
||||
createdAt: isoNow(),
|
||||
}
|
||||
adminUsers.unshift(user)
|
||||
return { data: user }
|
||||
return {
|
||||
success: true,
|
||||
message: 'User created.',
|
||||
data: user,
|
||||
}
|
||||
})
|
||||
|
||||
register('PUT', endpoints.updateUser, ({ params, data }) => {
|
||||
const role = roles.find((r) => r.id === Number(data.roleId))
|
||||
register('PATCH', endpoints.updateUser, ({ params, data }) => {
|
||||
const roleByName = (n) => roles.find((r) => r.name === n)
|
||||
const role =
|
||||
roles.find((r) => r.id === Number(data.roleId)) ||
|
||||
(Array.isArray(data.roles) && data.roles[0] ? roleByName(data.roles[0]) : null) ||
|
||||
null
|
||||
const firstName = data.firstName ?? ''
|
||||
const lastName = data.lastName ?? ''
|
||||
const fullName = `${firstName} ${lastName}`.trim()
|
||||
const phoneNumber = data.phoneNumber ?? ''
|
||||
const patch = {
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
fullName: `${data.firstName || ''} ${data.lastName || ''}`.trim(),
|
||||
phoneNumber: data.phoneNumber,
|
||||
// --- spec ---
|
||||
name: data.name ?? fullName,
|
||||
email: data.email,
|
||||
phone: data.phone ?? (phoneNumber ? phoneNumber.replace(/^0/, '+98') : undefined),
|
||||
roles: Array.isArray(data.roles) ? data.roles : role ? [role.name] : [],
|
||||
|
||||
// --- ui-only ---
|
||||
firstName,
|
||||
lastName,
|
||||
fullName,
|
||||
phoneNumber,
|
||||
nationalCode: data.nationalCode,
|
||||
roleId: role?.id,
|
||||
roles: role ? [role.name] : [],
|
||||
address: {
|
||||
address: data.address || '',
|
||||
province: data.provinceId ? { id: data.provinceId, name: '' } : null,
|
||||
@@ -82,13 +122,32 @@ register('PUT', endpoints.updateUser, ({ params, data }) => {
|
||||
},
|
||||
}
|
||||
const updated = updateById(adminUsers, params.id, patch)
|
||||
return { data: updated }
|
||||
return {
|
||||
success: true,
|
||||
message: 'User updated.',
|
||||
data: updated,
|
||||
}
|
||||
})
|
||||
|
||||
register('POST', endpoints.updateUserRole, ({ params, data }) => {
|
||||
const role = roles.find((r) => r.id === Number(data.roleId))
|
||||
const patch = role ? { roleId: role.id, roles: [role.name] } : {}
|
||||
return { data: updateById(adminUsers, params.id, patch) }
|
||||
register('PATCH', endpoints.updateUserRole, ({ params, data }) => {
|
||||
const byName = (n) => roles.find((r) => r.name === n)
|
||||
const nextRoleNames = Array.isArray(data.roles)
|
||||
? data.roles.filter(Boolean)
|
||||
: data.role
|
||||
? [data.role]
|
||||
: data.roleId
|
||||
? [roles.find((r) => r.id === Number(data.roleId))?.name].filter(Boolean)
|
||||
: []
|
||||
const primary = nextRoleNames[0] ? byName(nextRoleNames[0]) : null
|
||||
const patch = nextRoleNames.length
|
||||
? { roles: nextRoleNames, roleId: primary?.id }
|
||||
: {}
|
||||
const updated = updateById(adminUsers, params.id, patch)
|
||||
return {
|
||||
success: true,
|
||||
message: 'Roles updated.',
|
||||
data: updated,
|
||||
}
|
||||
})
|
||||
|
||||
register('POST', endpoints.changeUserStatus, ({ params, data }) => ({
|
||||
@@ -97,7 +156,11 @@ register('POST', endpoints.changeUserStatus, ({ params, data }) => ({
|
||||
|
||||
register('DELETE', endpoints.deleteUser, ({ params }) => {
|
||||
removeById(adminUsers, params.id)
|
||||
return { data: { message: 'حذف موفق' } }
|
||||
return {
|
||||
success: true,
|
||||
message: 'User deleted.',
|
||||
data: null,
|
||||
}
|
||||
})
|
||||
|
||||
register('GET', endpoints.getRolesList, () => ({ data: roles }))
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { makeId } from '@/services/mock/helpers'
|
||||
import { register } from '@/services/mock/registry'
|
||||
import { endpoints } from '@/services/api/endpoints'
|
||||
import { currentProfile } from '@/services/mock/fixtures/profile'
|
||||
import { currentMe } from '@/services/mock/fixtures/me'
|
||||
|
||||
const fakeToken = 'mock-token-1234567890'
|
||||
|
||||
register('POST', endpoints.login, () => ({
|
||||
data: { user: currentProfile, token: fakeToken },
|
||||
data: { user: currentMe, token: fakeToken },
|
||||
}))
|
||||
|
||||
register('POST', endpoints.loginWithCode2Step, () => ({
|
||||
data: { user: currentProfile, token: fakeToken },
|
||||
data: { user: currentMe, token: fakeToken },
|
||||
}))
|
||||
|
||||
register('POST', endpoints.verifyCode, () => ({
|
||||
data: { user: currentProfile, token: fakeToken },
|
||||
data: { user: currentMe, token: fakeToken },
|
||||
}))
|
||||
|
||||
register('POST', endpoints.register, ({ data }) => ({
|
||||
data: { user: { ...currentProfile, ...data }, token: fakeToken },
|
||||
data: { user: { ...currentMe, ...data }, token: fakeToken },
|
||||
}))
|
||||
|
||||
register('POST', endpoints.resendVerificationCodeForRegister, () => ({
|
||||
@@ -39,47 +39,56 @@ register('POST', endpoints.resetPassword, () => ({
|
||||
|
||||
register('POST', endpoints.logout, () => ({ data: { message: 'خروج موفق' } }))
|
||||
|
||||
register('GET', endpoints.getProfile, () => ({ data: currentProfile }))
|
||||
register('GET', endpoints.me, () => ({
|
||||
success: true,
|
||||
message: 'OK',
|
||||
data: currentMe,
|
||||
}))
|
||||
|
||||
register('PUT', endpoints.updateProfile, ({ data }) => {
|
||||
Object.assign(currentProfile, {
|
||||
firstName: data.firstName ?? currentProfile.firstName,
|
||||
lastName: data.lastName ?? currentProfile.lastName,
|
||||
phoneNumber: data.phoneNumber ?? currentProfile.phoneNumber,
|
||||
nationalCode: data.nationalCode ?? currentProfile.nationalCode,
|
||||
Object.assign(currentMe, {
|
||||
name: data.name ?? currentMe.name,
|
||||
email: data.email ?? currentMe.email,
|
||||
phone: data.phone ?? currentMe.phone,
|
||||
firstName: data.firstName ?? currentMe.firstName,
|
||||
lastName: data.lastName ?? currentMe.lastName,
|
||||
phoneNumber: data.phoneNumber ?? currentMe.phoneNumber,
|
||||
nationalCode: data.nationalCode ?? currentMe.nationalCode,
|
||||
})
|
||||
if (data.provinceId) {
|
||||
currentProfile.address = currentProfile.address || {}
|
||||
currentProfile.address.province = { id: data.provinceId, name: '' }
|
||||
currentMe.address = currentMe.address || {}
|
||||
currentMe.address.province = { id: data.provinceId, name: '' }
|
||||
}
|
||||
if (data.cityId) {
|
||||
currentProfile.address = currentProfile.address || {}
|
||||
currentProfile.address.city = { id: data.cityId, name: '' }
|
||||
currentMe.address = currentMe.address || {}
|
||||
currentMe.address.city = { id: data.cityId, name: '' }
|
||||
}
|
||||
if (data.address) {
|
||||
currentProfile.address = { ...currentProfile.address, address: data.address }
|
||||
currentMe.address = { ...currentMe.address, address: data.address }
|
||||
}
|
||||
currentProfile.profile = {
|
||||
...currentProfile.profile,
|
||||
bio: data.bio ?? currentProfile.profile?.bio,
|
||||
birthDate: data.birthDate ?? currentProfile.profile?.birthDate,
|
||||
maritalStatus: data.maritalStatus ?? currentProfile.profile?.maritalStatus,
|
||||
gender: data.gender ?? currentProfile.profile?.gender,
|
||||
currentMe.profile = {
|
||||
...currentMe.profile,
|
||||
bio: data.bio ?? currentMe.profile?.bio,
|
||||
birthDate: data.birthDate ?? currentMe.profile?.birthDate,
|
||||
maritalStatus: data.maritalStatus ?? currentMe.profile?.maritalStatus,
|
||||
gender: data.gender ?? currentMe.profile?.gender,
|
||||
}
|
||||
return { data: currentProfile }
|
||||
return { data: currentMe }
|
||||
})
|
||||
|
||||
register('POST', endpoints.completeProfile, ({ data }) => ({
|
||||
data: { user: { ...currentProfile, ...data } },
|
||||
data: { user: { ...currentMe, ...data } },
|
||||
}))
|
||||
|
||||
register('GET', endpoints.getRegistrationQuestionVideo, () => ({
|
||||
data: { url: '', id: 0 },
|
||||
}))
|
||||
|
||||
register('POST', endpoints.uploadMediaTemporary, () => {
|
||||
register('POST', endpoints.uploadMedia, () => {
|
||||
const id = makeId()
|
||||
return {
|
||||
success: true,
|
||||
message: 'Media uploaded.',
|
||||
data: {
|
||||
id,
|
||||
uploadId: id,
|
||||
|
||||
@@ -20,6 +20,10 @@ export const useAdminCoursesListQuery = (filtersRef, paginationRef, options = {}
|
||||
queryKey: ['admin', 'courses', 'list', filtersRef, paginationRef],
|
||||
queryFn: () =>
|
||||
apiGetAdminCourses({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
|
||||
select: (response) => ({
|
||||
data: response?.data?.items ?? response?.data ?? [],
|
||||
meta: response?.data?.meta ?? response?.meta,
|
||||
}),
|
||||
...options,
|
||||
})
|
||||
|
||||
|
||||
@@ -29,6 +29,10 @@ export const useAdminSessionsListQuery = (filtersRef, paginationRef, options = {
|
||||
queryKey: ['admin', 'sessions', 'list', filtersRef, paginationRef],
|
||||
queryFn: () =>
|
||||
apiGetAdminSessions({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
|
||||
select: (response) => ({
|
||||
data: response?.data?.items ?? response?.data ?? [],
|
||||
meta: response?.data?.meta ?? response?.meta,
|
||||
}),
|
||||
...options,
|
||||
})
|
||||
|
||||
|
||||
@@ -43,6 +43,10 @@ export const useAdminTermsListQuery = (filtersRef, paginationRef, options = {})
|
||||
useQuery({
|
||||
queryKey: ['admin', 'terms', 'list', filtersRef, paginationRef],
|
||||
queryFn: () => apiGetAdminTerms({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
|
||||
select: (response) => ({
|
||||
data: response?.data?.items ?? response?.data ?? [],
|
||||
meta: response?.data?.meta ?? response?.meta,
|
||||
}),
|
||||
...options,
|
||||
})
|
||||
|
||||
|
||||
@@ -22,6 +22,10 @@ export const useAdminUsersListQuery = (filtersRef, paginationRef, options = {})
|
||||
useQuery({
|
||||
queryKey: ['admin', 'users', 'list', filtersRef, paginationRef],
|
||||
queryFn: () => apiGetAdminUsers({ ...cleanFilters(filtersRef.value), ...paginationRef.value }),
|
||||
select: (response) => ({
|
||||
data: response?.data?.items ?? response?.data ?? [],
|
||||
meta: response?.data?.meta ?? response?.meta,
|
||||
}),
|
||||
...options,
|
||||
})
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useMutation, useQuery } from '@tanstack/vue-query'
|
||||
import {
|
||||
apiCompleteProfile,
|
||||
apiForgotPassword,
|
||||
apiGetProfile,
|
||||
apiGetMe,
|
||||
apiGetRegistrationQuestionVideo,
|
||||
apiLogin,
|
||||
apiLoginWithCode,
|
||||
@@ -12,13 +12,13 @@ import {
|
||||
apiResendVerificationCode,
|
||||
apiResetPassword,
|
||||
apiUpdateProfile,
|
||||
apiUploadTemporary,
|
||||
apiUploadMedia,
|
||||
apiVerifyCode,
|
||||
apiVerifyForgotPasswordCode,
|
||||
} from '@/services/api/auth'
|
||||
|
||||
export const authKeys = {
|
||||
profile: () => ['auth', 'profile'],
|
||||
me: () => ['auth', 'me'],
|
||||
registrationVideo: () => ['auth', 'registration-video'],
|
||||
}
|
||||
|
||||
@@ -42,10 +42,10 @@ export const useResetPasswordMutation = () => useMutation({ mutationFn: apiReset
|
||||
|
||||
export const useLogoutMutation = () => useMutation({ mutationFn: apiLogout })
|
||||
|
||||
export const useGetProfileQuery = (options = {}) =>
|
||||
export const useGetMeQuery = (options = {}) =>
|
||||
useQuery({
|
||||
queryKey: authKeys.profile(),
|
||||
queryFn: () => apiGetProfile(),
|
||||
queryKey: authKeys.me(),
|
||||
queryFn: () => apiGetMe(),
|
||||
select: (response) => response?.data?.user ?? response?.data ?? response,
|
||||
...options,
|
||||
})
|
||||
@@ -62,4 +62,4 @@ export const useGetRegistrationVideoQuery = (options = {}) =>
|
||||
...options,
|
||||
})
|
||||
|
||||
export const useUploadTemporaryMutation = () => useMutation({ mutationFn: apiUploadTemporary })
|
||||
export const useUploadMediaMutation = () => useMutation({ mutationFn: apiUploadMedia })
|
||||
|
||||
Reference in New Issue
Block a user