Major Features: - Authentication & Authorization with JWT and role-based access - Complete User Management System with CRUD operations - Course Management System with publishing and enrollment - Modern React UI with Tailwind CSS and responsive design - Internationalization (i18n) with English and Arabic support - File Upload System for images and documents - RESTful API with Express.js and Sequelize ORM - PostgreSQL database with proper relationships - Super Admin password change functionality - CSV import for bulk user creation - Modal-based user add/edit operations - Search, filter, and pagination capabilities Technical Improvements: - Fixed homepage routing and accessibility issues - Resolved API endpoint mismatches and data rendering - Enhanced security with proper validation and hashing - Optimized performance with React Query and caching - Improved error handling and user feedback - Clean code structure with ESLint compliance Bug Fixes: - Fixed non-functional Add/Edit/Delete buttons - Resolved CSV import BOM issues - Fixed modal rendering and accessibility - Corrected API base URL configuration - Enhanced backend stability and error handling This version represents a complete, production-ready Course Management System.
120 lines
No EOL
4.1 KiB
JavaScript
120 lines
No EOL
4.1 KiB
JavaScript
import axios from 'axios';
|
|
|
|
// Create axios instance
|
|
const api = axios.create({
|
|
baseURL: '/api',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
// Request interceptor to add auth token
|
|
api.interceptors.request.use(
|
|
(config) => {
|
|
const token = localStorage.getItem('token');
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
return config;
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// Response interceptor to handle auth errors
|
|
api.interceptors.response.use(
|
|
(response) => response,
|
|
(error) => {
|
|
if (error.response?.status === 401) {
|
|
localStorage.removeItem('token');
|
|
window.location.href = '/login';
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// Auth API
|
|
export const authAPI = {
|
|
login: (email, password) => api.post('/auth/login', { email, password }),
|
|
getCurrentUser: () => api.get('/auth/me'),
|
|
updateProfile: (data) => api.put('/auth/profile', data),
|
|
changePassword: (currentPassword, newPassword) =>
|
|
api.put('/auth/change-password', { currentPassword, newPassword }),
|
|
firstPasswordChange: (data) => api.put('/auth/first-password-change', data),
|
|
register: (userData) => api.post('/auth/register', userData),
|
|
};
|
|
|
|
// Users API
|
|
export const usersAPI = {
|
|
getAll: (params) => api.get('users', { params }),
|
|
getById: (id) => api.get(`users/${id}`),
|
|
update: (id, data) => api.put(`users/${id}`, data),
|
|
delete: (id) => api.delete(`users/${id}`),
|
|
changePassword: (id, password) => api.put(`users/${id}/password`, { password }),
|
|
getStats: () => api.get('users/stats/overview'),
|
|
importUsers: (data) => api.post('users/import', data, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
}),
|
|
uploadSliderImage: (file) => {
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
return api.post('users/slider/image', formData, {
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
});
|
|
},
|
|
};
|
|
|
|
// Courses API
|
|
export const coursesAPI = {
|
|
getAll: (params) => api.get('/courses', { params }).then(res => res.data),
|
|
getById: (id) => api.get(`/courses/${id}`).then(res => res.data),
|
|
create: (data) => api.post('/courses', data),
|
|
update: (id, data) => api.put(`/courses/${id}`, data),
|
|
delete: (id) => api.delete(`/courses/${id}`),
|
|
publish: (id, isPublished) => api.put(`/courses/${id}/publish`, { isPublished }),
|
|
getCategories: () => api.get('/courses/categories/all'),
|
|
getStats: () => api.get('/courses/stats/overview'),
|
|
uploadCourseImage: (courseName, file) => {
|
|
const formData = new FormData();
|
|
formData.append('image', file);
|
|
return api.post(`/courses/${courseName}/image`, formData, {
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
});
|
|
},
|
|
};
|
|
|
|
// Enrollments API
|
|
export const enrollmentsAPI = {
|
|
create: (data) => api.post('/enrollments', data),
|
|
getMy: (params) => api.get('/enrollments/my', { params }),
|
|
getById: (id) => api.get(`/enrollments/${id}`),
|
|
updateStatus: (id, data) => api.put(`/enrollments/${id}/status`, data),
|
|
updatePayment: (id, data) => api.put(`/enrollments/${id}/payment`, data),
|
|
cancel: (id) => api.delete(`/enrollments/${id}`),
|
|
};
|
|
|
|
// Attendance API
|
|
export const attendanceAPI = {
|
|
signIn: (data) => api.post('/attendance/sign-in', data),
|
|
signOut: (data) => api.post('/attendance/sign-out', data),
|
|
getMy: (params) => api.get('/attendance/my', { params }),
|
|
getByCourse: (courseId, params) => api.get(`/attendance/course/${courseId}`, { params }),
|
|
update: (id, data) => api.put(`/attendance/${id}`, data),
|
|
getStats: (params) => api.get('/attendance/stats/my', { params }),
|
|
};
|
|
|
|
// Assignments API
|
|
export const assignmentsAPI = {
|
|
create: (data) => api.post('/assignments', data),
|
|
getByCourse: (courseId, params) => api.get(`/assignments/course/${courseId}`, { params }),
|
|
getById: (id) => api.get(`/assignments/${id}`),
|
|
update: (id, data) => api.put(`/assignments/${id}`, data),
|
|
delete: (id) => api.delete(`/assignments/${id}`),
|
|
publish: (id, isPublished) => api.put(`/assignments/${id}/publish`, { isPublished }),
|
|
getMy: (params) => api.get('/assignments/my', { params }),
|
|
};
|
|
|
|
export default api;
|