This commit is contained in:
2026-06-29 23:45:08 +07:00
parent 5290eeeb91
commit c736326162
15 changed files with 3067 additions and 388 deletions

145
management/src/api.ts Normal file
View File

@@ -0,0 +1,145 @@
const API_BASE = 'http://127.0.0.1:8080/api';
export interface ClassItem {
rkId: number;
name: string;
classCode: string;
type: string;
studentCount: number;
specializeRkId?: number;
specializeName?: string;
systemRkId?: number;
systemCode?: string;
systemName?: string;
isStudying: boolean;
courses: { courseRkId: number; courseName: string }[];
}
export interface StudentItem {
id: number;
rkId: number;
studentCode: string;
fullName: string;
phone?: string;
email: string;
dateOfBirth?: string;
gender?: number;
status?: string;
location?: string;
systemId?: number;
systemName?: string;
}
export interface StatsResponse {
totalClasses: number;
activeClasses: number;
totalStudents: number;
}
export interface PaginatedResponse<T> {
data: T[];
total: number;
page: number;
pageSize: number;
}
export interface SyncStatus {
running: boolean;
done: boolean;
error?: string;
total: number;
synced: number;
updatedAt: number;
page?: number;
pageSize?: number;
}
export const api = {
getStats: async (): Promise<StatsResponse> => {
const res = await fetch(`${API_BASE}/stats`);
if (!res.ok) throw new Error('Failed to fetch dashboard stats');
return res.json();
},
getClasses: async (params: {
page: number;
pageSize: number;
q?: string;
systemRkId?: number;
studyingOnly?: boolean;
}): Promise<PaginatedResponse<ClassItem>> => {
const query = new URLSearchParams({
page: String(params.page),
pageSize: String(params.pageSize),
});
if (params.q) query.set('q', params.q);
if (params.systemRkId) query.set('systemRkId', String(params.systemRkId));
if (params.studyingOnly) query.set('studyingOnly', 'true');
const res = await fetch(`${API_BASE}/classes?${query}`);
if (!res.ok) throw new Error('Failed to fetch classes');
return res.json();
},
updateClassStudying: async (rkId: number, isStudying: boolean): Promise<any> => {
const res = await fetch(`${API_BASE}/classes/${rkId}/studying`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ isStudying }),
});
if (!res.ok) throw new Error('Failed to update class studying status');
return res.json();
},
getClassStudents: async (rkId: number): Promise<{ data: StudentItem[]; total: number }> => {
const res = await fetch(`${API_BASE}/classes/${rkId}/students`);
if (!res.ok) throw new Error('Failed to fetch class students roster');
return res.json();
},
getStudents: async (params: {
page: number;
pageSize: number;
q?: string;
}): Promise<PaginatedResponse<StudentItem>> => {
const query = new URLSearchParams({
page: String(params.page),
pageSize: String(params.pageSize),
});
if (params.q) query.set('q', params.q);
const res = await fetch(`${API_BASE}/students?${query}`);
if (!res.ok) throw new Error('Failed to fetch students');
return res.json();
},
startClassesSync: async (): Promise<{ ok: boolean; message: string }> => {
const res = await fetch(`${API_BASE}/sync/classes/start`, { method: 'POST' });
if (!res.ok) {
const errData = await res.json().catch(() => ({}));
throw new Error(errData.error || 'Failed to start classes sync');
}
return res.json();
},
getClassesSyncStatus: async (): Promise<SyncStatus> => {
const res = await fetch(`${API_BASE}/sync/classes/status`);
if (!res.ok) throw new Error('Failed to get classes sync status');
return res.json();
},
startStudentsSync: async (): Promise<{ ok: boolean; message: string }> => {
const res = await fetch(`${API_BASE}/sync/students/start`, { method: 'POST' });
if (!res.ok) {
const errData = await res.json().catch(() => ({}));
throw new Error(errData.error || 'Failed to start students sync');
}
return res.json();
},
getStudentsSyncStatus: async (): Promise<SyncStatus> => {
const res = await fetch(`${API_BASE}/sync/students/status`);
if (!res.ok) throw new Error('Failed to get students sync status');
return res.json();
},
};