export const endpoints = { // ─── Auth & profile ──────────────────────────────────────────────────────── // Backend-aligned (Postman: Auth collection) register: '/auth/register', login: '/auth/login', logout: '/auth/logout', me: '/auth/me', updateProfile: '/me', registerData: '/me/register-data', requestLoginOtp: '/auth/login/otp/request', verifyLoginOtp: '/auth/login/otp/verify', forgotPassword: '/auth/password/forgot', verifyForgotPasswordCode: '/auth/password/verify-otp', resetPassword: '/auth/password/reset', resendVerificationCodeForRegister: '/auth/login/otp/request', verifyCode: '/auth/login/otp/verify', completeProfile: '/complete-profile', getRegistrationQuestionVideo: '/verification-registration-media', // ─── Geo ─────────────────────────────────────────────────────────────────── provinceList: '/provinces', citiesList: '/provinces/:provinceId/cities', // ─── Student-facing ──────────────────────────────────────────────────────── // Courses, sessions, exams use the shared backend-aligned endpoints below // (/courses, /sessions/:id, /exams/:id, /exams/:id/submit) — the backend // infers role from the auth token and scopes the response. Terms have a // dedicated student route (returns the membership row with the nested term). getStudentDashboard: '/student/dashboard', getStudentTerms: '/student/my-terms', // Missionary — passed (completed) education. List-only; same shapes as // /student/my-terms, /courses, /sessions. Detail views reuse the shared // /sessions/:id and the already-fetched list data (via query cache). getMissionaryPassedTerms: '/missionary/passed-terms', getMissionaryPassedCourses: '/missionary/passed-courses', getMissionaryPassedSessions: '/missionary/passed-sessions', showStudentTerm: '/student/my-terms/:id', getStudentExamResults: '/student/exam-results', getStudentHomeworks: '/student/homeworks', showHomework: '/homeworks/:id', startExam: '/exams/:examId/start', // Unified student tickets — handles services (type=service), consultants // (type=advise) and inbox (type=ticket) via the same endpoint with a filter. getStudentTickets: '/student/tickets', getStudentTicketStats: '/student/tickets/stats', createStudentTicket: '/student/tickets', showStudentTicket: '/student/tickets/:id', sendStudentTicketMessage: '/student/tickets/:id/messages', getStudentCertificates: '/student/certificates', downloadStudentCertificate: '/student/certificates/:id/download', // ─── Backend-aligned: Service types (settings) ──────────────────────────── // Read is open to any authenticated role (populates the student "request // service" category select); create/update/delete are admin-only. getServiceTypesList: '/service-types', addServiceType: '/service-types', updateServiceType: '/service-types/:id', deleteServiceType: '/service-types/:id', // ─── Per-role aggregated dashboards ──────────────────────────────────────── // Each requires the matching role's token; backend scopes the payload. getAdminDashboard: '/admin/dashboard', getCounselorDashboard: '/counselor/dashboard', getMissionaryDashboard: '/missionary/dashboard', // ─── Admin: users (out of scope of the Terms/Courses backend doc) ────────── getPendingStudents: '/admin/pending-students', showPendingStudent: '/admin/pending-students/:id', changeStatusPendingStudent: '/admin/pending-students/:id/status', downloadPdfPendingStudentInfo: '/admin/pending-students/:id/pdf', getApprovedUsers: '/admin/users', showUserDetails: '/admin/users/:id', addNewUser: '/admin/users', updateUser: '/admin/users/:id', updateUserRole: '/admin/users/:id/role', changeUserStatus: '/admin/users/:id/status', deleteUser: '/admin/users/:id', getUserRegisterData: '/admin/users/:id/register-data', getUserEvaluation: '/admin/users/:id/evaluation', // ─── Backend-aligned: Terms ──────────────────────────────────────────────── getTermsList: '/terms', addNewTerm: '/terms', showTerm: '/terms/:id', updateTerm: '/terms/:id', deleteTerm: '/terms/:id', getTermEnrollees: '/admin/terms/:termId/enrollees', // ─── Backend-aligned: Courses ────────────────────────────────────────────── getCoursesList: '/courses', addNewCourse: '/courses', showCourse: '/courses/:id', updateCourse: '/courses/:id', deleteCourse: '/courses/:id', // ─── Backend-aligned: Sessions ───────────────────────────────────────────── getSessionsList: '/sessions', addNewSession: '/sessions', showSession: '/sessions/:id', updateSession: '/sessions/:id', deleteSession: '/sessions/:id', // ─── Backend-aligned: Exams ──────────────────────────────────────────────── getExamsList: '/admin/exams', getStudentExamsList: '/student/exams', showExam: '/exams/:id', addNewExam: '/exams', updateExam: '/exams/:id', deleteExam: '/exams/:id', addExamQuestion: '/exams/:examId/questions', addQuestionOption: '/questions/:questionId/options', submitExam: '/exams/:examId/submit', // ─── Backend-aligned: Homeworks (FE calls them "assignments") ────────────── addNewAssignment: '/homeworks', updateAssignment: '/homeworks/:id', deleteAssignment: '/homeworks/:id', submitHomework: '/homeworks/:homeworkId/submit', reviewAssignmentSubmission: '/homework-submissions/:submissionId/review', // ─── Backend-aligned: Media ──────────────────────────────────────────────── uploadMedia: '/media', downloadMedia: '/media/:id/download', deleteMedia: '/media/:id', // ─── FE mock-only — NOT in backend Postman doc ───────────────────────────── // Kept so existing UI screens continue to work against the mock layer. // See docs/backend-vs-ui-gaps.md for the list of decisions pending. // Terms — clone + status + students-in-term + courses-in-term cloneTerm: '/admin/terms/:id/clone', listUserTerm: '/admin/terms/:termId/students', addUserTerm: '/admin/terms/:termId/enrollees', removeUserTerm: '/admin/terms/:termId/students/:userId', changeLeaveStatus: '/admin/terms/:termId/students/:userId/toggle-leave', listCourseTerm: '/admin/terms/:termId/courses', addCourseTerm: '/admin/terms/:termId/courses', removeCourseTerm: '/admin/terms/:termId/courses/:courseId', // Sessions — attendance roster getSessionsAttendance: '/admin/sessions/:sessionId/attendances', // Exams — admin/teacher view of attempts. Each row is one attempt with the // attempting user embedded; multiple rows per user are possible. The // single-attempt detail endpoint is still mock-only. getExamAttempts: '/exams/:examId/attempts', showExamAttempt: '/exam-attempts/:id', showExamParticipant: '/admin/exams/:examId/participants/:participantId', // Homeworks — list, show, submissions list, show submission // Paths aligned to backend `/homeworks` and `/homework-submissions`. The // backend Postman doc currently exposes only create/update/delete/submit/review; // list + show + submissions list + show-submission stay mock-only until backend // adds them, but URL shape is locked in. getAssignmentsList: '/admin/homeworks', showAssignment: '/homeworks/:id', getAssignmentSubmissions: '/homeworks/:homeworkId/submissions', showAssignmentSubmission: '/homework-submissions/:submissionId', // ─── Backend-aligned: Tickets ───────────────────────────────────────────── // Admin sees every type via /admin/tickets, filtered by ?type=: // - messages page → ?type=ticket // - services page → ?type=service // - consultations page → ?type=advise // Counselor is restricted to /counselor/tickets (advise-only at the backend). getTicketsList: '/admin/tickets', showTicket: '/admin/tickets/:id', sendTicketMessage: '/admin/tickets/:id/messages', changeTicketStatus: '/admin/tickets/:id/status', getConsultationsList: '/admin/tickets', showConsultation: '/admin/tickets/:id', sendConsultationMessage: '/admin/tickets/:id/messages', changeConsultationStatus: '/admin/tickets/:id/status', getCounselorTickets: '/counselor/tickets', showCounselorTicket: '/counselor/tickets/:id', sendCounselorTicketMessage: '/counselor/tickets/:id/messages', changeCounselorTicketStatus: '/counselor/tickets/:id/status', 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 = {}) => Object.entries(params).reduce( (url, [key, value]) => url.replace(`:${key}`, encodeURIComponent(value)), template )