Initial commit: admin dashboard for GameNo.

Vue 3 + Vite dashboard with PrimeVue, i18n, and API integration for institution management.
This commit is contained in:
2026-08-09 04:18:09 +02:00
commit 1a34c3b133
94 changed files with 12016 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
// /src/api/activityLogApi.js
import axiosInstance from './axiosInstance';
export const activityLogApi = {
getAll: (params) => axiosInstance.get('/activity-logs/admin/get-all', { params }),
getActions: () => axiosInstance.get('/activity-logs/admin/actions')
};
+8
View File
@@ -0,0 +1,8 @@
// /src/api/attendanceApi.js
import axiosInstance from './axiosInstance';
export const attendanceApi = {
recordSessionAttendance: (sessionId, attendanceList) =>
axiosInstance.put(`/sessions/admin/${sessionId}/attendance`, { attendanceList }),
getSessionAttendance: (sessionId) => axiosInstance.get(`/sessions/admin/get-one/${sessionId}`)
};
+9
View File
@@ -0,0 +1,9 @@
// /src/api/authApi.js
import axiosInstance from './axiosInstance';
export const authApi = {
login: (data) => axiosInstance.post('/auth/login', data),
refresh: (refreshToken) => axiosInstance.post('/auth/refresh', { refreshToken }),
logout: (refreshToken) => axiosInstance.post('/auth/logout', { refreshToken }),
getSelf: () => axiosInstance.get('/users/user/get-self')
};
+121
View File
@@ -0,0 +1,121 @@
// /src/api/axiosInstance.js
import axios from 'axios';
import Cookies from 'js-cookie';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:5000/api';
const axiosInstance = axios.create({
baseURL: API_BASE_URL,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
timeout: 15000
});
// Request interceptor: attach token
axiosInstance.interceptors.request.use(
(config) => {
const token = localStorage.getItem('accessToken') || Cookies.get('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// Ask backend for Persian (or stored) localized error messages
const lang = localStorage.getItem('lang') || 'fa';
config.headers['Accept-Language'] = lang;
if (!config.params) config.params = {};
if (config.params.lang == null) {
config.params.lang = lang;
}
return config;
},
(error) => Promise.reject(error)
);
// Response interceptor: auto-refresh token on 401
let isRefreshing = false;
let failedQueue = [];
const processQueue = (error, token = null) => {
failedQueue.forEach(prom => {
if (error) {
prom.reject(error);
} else {
prom.resolve(token);
}
});
failedQueue = [];
};
axiosInstance.interceptors.response.use(
(response) => response.data,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
if (originalRequest.url.includes('/auth/login') || originalRequest.url.includes('/auth/refresh')) {
return Promise.reject(error);
}
if (isRefreshing) {
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
})
.then((token) => {
originalRequest.headers.Authorization = `Bearer ${token}`;
return axiosInstance(originalRequest);
})
.catch((err) => Promise.reject(err));
}
originalRequest._retry = true;
isRefreshing = true;
const refreshToken = localStorage.getItem('refreshToken') || Cookies.get('refreshToken');
if (!refreshToken) {
isRefreshing = false;
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
window.location.href = '/login';
return Promise.reject(error);
}
try {
const refreshResponse = await axios.post(`${API_BASE_URL}/auth/refresh`, { refreshToken });
const data = refreshResponse.data?.data || refreshResponse.data;
const newAccessToken = data.accessToken;
const newRefreshToken = data.refreshToken;
localStorage.setItem('accessToken', newAccessToken);
Cookies.set('accessToken', newAccessToken);
if (newRefreshToken) {
localStorage.setItem('refreshToken', newRefreshToken);
Cookies.set('refreshToken', newRefreshToken);
}
axiosInstance.defaults.headers.common.Authorization = `Bearer ${newAccessToken}`;
originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
processQueue(null, newAccessToken);
return axiosInstance(originalRequest);
} catch (refreshErr) {
processQueue(refreshErr, null);
localStorage.removeItem('accessToken');
localStorage.removeItem('refreshToken');
Cookies.remove('accessToken');
Cookies.remove('refreshToken');
window.location.href = '/login';
return Promise.reject(refreshErr);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
}
);
export default axiosInstance;
+16
View File
@@ -0,0 +1,16 @@
// /src/api/certificateApi.js
import axiosInstance from './axiosInstance';
export const certificateApi = {
getAll: (params) => axiosInstance.get('/certificates/admin/get-all', { params }),
search: (params) => axiosInstance.get('/certificates/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/certificates/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/certificates/admin/create', data),
update: (id, data) => axiosInstance.put(`/certificates/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/certificates/admin/delete/${id}`),
uploadTemp: (formData) => axiosInstance.post('/files/admin/upload-temp', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
}),
userUpload: (data) => axiosInstance.post('/certificates/user/upload', data),
getUserCertificates: () => axiosInstance.get('/certificates/user/my-certificates')
};
+12
View File
@@ -0,0 +1,12 @@
// /src/api/classApi.js
import axiosInstance from './axiosInstance';
export const classApi = {
getAll: (params) => axiosInstance.get('/classes/admin/get-all', { params }),
search: (params) => axiosInstance.get('/classes/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/classes/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/classes/admin/create', data),
update: (id, data) => axiosInstance.put(`/classes/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/classes/admin/delete/${id}`),
registerUsers: (classId, userIds) => axiosInstance.post(`/classes/admin/${classId}/register-users`, { userIds })
};
+7
View File
@@ -0,0 +1,7 @@
import axiosInstance from './axiosInstance';
export const contactInquiryApi = {
getAll: (params) => axiosInstance.get('/contact-inquiries/admin/get-all', { params }),
getOne: (id) => axiosInstance.get(`/contact-inquiries/admin/get-one/${id}`),
update: (id, data) => axiosInstance.put(`/contact-inquiries/admin/update/${id}`, data)
};
+11
View File
@@ -0,0 +1,11 @@
// /src/api/courseApi.js
import axiosInstance from './axiosInstance';
export const courseApi = {
getAll: (params) => axiosInstance.get('/courses/admin/get-all', { params }),
search: (params) => axiosInstance.get('/courses/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/courses/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/courses/admin/create', data),
update: (id, data) => axiosInstance.put(`/courses/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/courses/admin/delete/${id}`)
};
+6
View File
@@ -0,0 +1,6 @@
// /src/api/dashboardApi.js
import axiosInstance from './axiosInstance';
export const dashboardApi = {
getStats: () => axiosInstance.get('/dashboard/admin/stats')
};
+12
View File
@@ -0,0 +1,12 @@
// /src/api/notificationApi.js
import axiosInstance from './axiosInstance';
export const notificationApi = {
getAll: (params) => axiosInstance.get('/notifications/admin/get-all', { params }),
search: (params) => axiosInstance.get('/notifications/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/notifications/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/notifications/admin/create', data),
update: (id, data) => axiosInstance.put(`/notifications/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/notifications/admin/delete/${id}`),
retry: (id) => axiosInstance.post(`/notifications/admin/${id}/retry`)
};
+12
View File
@@ -0,0 +1,12 @@
// /src/api/paymentApi.js
import axiosInstance from './axiosInstance';
export const paymentApi = {
getAll: (params) => axiosInstance.get('/payments/admin/get-all', { params }),
search: (params) => axiosInstance.get('/payments/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/payments/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/payments/admin/create', data),
update: (id, data) => axiosInstance.put(`/payments/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/payments/admin/delete/${id}`),
recordTransaction: (paymentId, data) => axiosInstance.post(`/payments/user/pay/${paymentId}`, data)
};
+11
View File
@@ -0,0 +1,11 @@
// /src/api/professorApi.js
import axiosInstance from './axiosInstance';
export const professorApi = {
getAll: (params) => axiosInstance.get('/professors/admin/get-all', { params }),
search: (params) => axiosInstance.get('/professors/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/professors/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/professors/admin/create', data),
update: (id, data) => axiosInstance.put(`/professors/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/professors/admin/delete/${id}`)
};
+11
View File
@@ -0,0 +1,11 @@
// /src/api/roleApi.js
import axiosInstance from './axiosInstance';
export const roleApi = {
getAll: (params) => axiosInstance.get('/roles/admin/get-all', { params }),
search: (params) => axiosInstance.get('/roles/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/roles/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/roles/admin/create', data),
update: (id, data) => axiosInstance.put(`/roles/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/roles/admin/delete/${id}`)
};
+14
View File
@@ -0,0 +1,14 @@
// /src/api/sessionApi.js
import axiosInstance from './axiosInstance';
export const sessionApi = {
getAll: (params) => axiosInstance.get('/sessions/admin/get-all', { params }),
search: (params) => axiosInstance.get('/sessions/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/sessions/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/sessions/admin/create', data),
update: (id, data) => axiosInstance.put(`/sessions/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/sessions/admin/delete/${id}`),
bulkDelete: (ids) => axiosInstance.post('/sessions/admin/bulk-delete', { ids }),
bulkUpdateStatus: (ids, status) => axiosInstance.post('/sessions/admin/bulk-status', { ids, status }),
recordAttendance: (sessionId, data) => axiosInstance.put(`/sessions/admin/${sessionId}/attendance`, data)
};
+12
View File
@@ -0,0 +1,12 @@
// /src/api/userApi.js
import axiosInstance from './axiosInstance';
export const userApi = {
getAll: (params) => axiosInstance.get('/users/admin/get-all', { params }),
search: (params) => axiosInstance.get('/users/admin/search', { params }),
getOne: (id) => axiosInstance.get(`/users/admin/get-one/${id}`),
create: (data) => axiosInstance.post('/users/admin/create', data),
update: (id, data) => axiosInstance.put(`/users/admin/update/${id}`, data),
delete: (id) => axiosInstance.delete(`/users/admin/delete/${id}`),
enroll: (userId, data) => axiosInstance.post(`/users/admin/${userId}/enroll`, data)
};