287 lines
8.4 KiB
TypeScript
287 lines
8.4 KiB
TypeScript
import type {
|
|
AuthResponse,
|
|
UserLogin,
|
|
UserRegister,
|
|
User,
|
|
Subject,
|
|
SubjectCreate,
|
|
SubjectUpdate,
|
|
Grade,
|
|
GradeCreate,
|
|
GradeUpdate,
|
|
ReportPeriod,
|
|
ReportPeriodCreate,
|
|
ReportPeriodUpdate,
|
|
TeacherGrade,
|
|
TeacherGradeCreate,
|
|
TeacherGradeUpdate,
|
|
} from "~/types/api";
|
|
|
|
const API_BASE_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api";
|
|
|
|
class ApiClient {
|
|
private getHeaders(includeAuth: boolean = false): HeadersInit {
|
|
const headers: HeadersInit = {
|
|
"Content-Type": "application/json",
|
|
};
|
|
|
|
if (includeAuth) {
|
|
const token = localStorage.getItem("access_token");
|
|
if (token) {
|
|
headers["Authorization"] = `Bearer ${token}`;
|
|
}
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
private async handleResponse<T>(response: Response): Promise<T> {
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({ detail: "An error occurred" }));
|
|
throw new Error(error.detail || `HTTP ${response.status}`);
|
|
}
|
|
|
|
if (response.status === 204) {
|
|
return undefined as T;
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Auth endpoints
|
|
async register(data: UserRegister): Promise<AuthResponse> {
|
|
const response = await fetch(`${API_BASE_URL}/auth/register`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(),
|
|
body: JSON.stringify(data),
|
|
});
|
|
const result = await this.handleResponse<AuthResponse>(response);
|
|
localStorage.setItem("access_token", result.access_token);
|
|
return result;
|
|
}
|
|
|
|
async login(data: UserLogin): Promise<AuthResponse> {
|
|
const response = await fetch(`${API_BASE_URL}/auth/login`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(),
|
|
body: JSON.stringify(data),
|
|
});
|
|
const result = await this.handleResponse<AuthResponse>(response);
|
|
localStorage.setItem("access_token", result.access_token);
|
|
return result;
|
|
}
|
|
|
|
async getMe(): Promise<User> {
|
|
const response = await fetch(`${API_BASE_URL}/auth/me`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<User>(response);
|
|
}
|
|
|
|
logout(): void {
|
|
localStorage.removeItem("access_token");
|
|
}
|
|
|
|
isAuthenticated(): boolean {
|
|
return !!localStorage.getItem("access_token");
|
|
}
|
|
|
|
// Subject endpoints
|
|
async getSubjects(): Promise<Subject[]> {
|
|
const response = await fetch(`${API_BASE_URL}/subjects`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<Subject[]>(response);
|
|
}
|
|
|
|
async getSubject(id: string): Promise<Subject> {
|
|
const response = await fetch(`${API_BASE_URL}/subjects/${id}`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<Subject>(response);
|
|
}
|
|
|
|
async createSubject(data: SubjectCreate): Promise<Subject> {
|
|
const response = await fetch(`${API_BASE_URL}/subjects`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<Subject>(response);
|
|
}
|
|
|
|
async updateSubject(id: string, data: SubjectUpdate): Promise<Subject> {
|
|
const response = await fetch(`${API_BASE_URL}/subjects/${id}`, {
|
|
method: "PUT",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<Subject>(response);
|
|
}
|
|
|
|
async deleteSubject(id: string): Promise<void> {
|
|
const response = await fetch(`${API_BASE_URL}/subjects/${id}`, {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<void>(response);
|
|
}
|
|
|
|
// Grade endpoints
|
|
async getGrades(subjectId?: string): Promise<Grade[]> {
|
|
const url = subjectId
|
|
? `${API_BASE_URL}/grades?subject_id=${subjectId}`
|
|
: `${API_BASE_URL}/grades`;
|
|
const response = await fetch(url, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<Grade[]>(response);
|
|
}
|
|
|
|
async getGrade(id: string): Promise<Grade> {
|
|
const response = await fetch(`${API_BASE_URL}/grades/${id}`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<Grade>(response);
|
|
}
|
|
|
|
async createGrade(data: GradeCreate): Promise<Grade> {
|
|
const response = await fetch(`${API_BASE_URL}/grades`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<Grade>(response);
|
|
}
|
|
|
|
async updateGrade(id: string, data: GradeUpdate): Promise<Grade> {
|
|
const response = await fetch(`${API_BASE_URL}/grades/${id}`, {
|
|
method: "PUT",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<Grade>(response);
|
|
}
|
|
|
|
async deleteGrade(id: string): Promise<void> {
|
|
const response = await fetch(`${API_BASE_URL}/grades/${id}`, {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<void>(response);
|
|
}
|
|
|
|
// Report Period endpoints
|
|
async getPeriods(): Promise<ReportPeriod[]> {
|
|
const response = await fetch(`${API_BASE_URL}/periods`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<ReportPeriod[]>(response);
|
|
}
|
|
|
|
async getPeriod(id: string): Promise<ReportPeriod> {
|
|
const response = await fetch(`${API_BASE_URL}/periods/${id}`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<ReportPeriod>(response);
|
|
}
|
|
|
|
async createPeriod(data: ReportPeriodCreate): Promise<ReportPeriod> {
|
|
const response = await fetch(`${API_BASE_URL}/periods`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<ReportPeriod>(response);
|
|
}
|
|
|
|
async updatePeriod(id: string, data: ReportPeriodUpdate): Promise<ReportPeriod> {
|
|
const response = await fetch(`${API_BASE_URL}/periods/${id}`, {
|
|
method: "PUT",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<ReportPeriod>(response);
|
|
}
|
|
|
|
async deletePeriod(id: string): Promise<void> {
|
|
const response = await fetch(`${API_BASE_URL}/periods/${id}`, {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<void>(response);
|
|
}
|
|
|
|
// Teacher Grade endpoints
|
|
async getTeacherGrades(subjectId?: string, periodId?: string): Promise<TeacherGrade[]> {
|
|
const params = new URLSearchParams();
|
|
if (subjectId) params.append("subject_id", subjectId);
|
|
if (periodId) params.append("period_id", periodId);
|
|
const url = `${API_BASE_URL}/teacher-grades${params.toString() ? `?${params.toString()}` : ""}`;
|
|
|
|
const response = await fetch(url, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<TeacherGrade[]>(response);
|
|
}
|
|
|
|
async getTeacherGrade(id: string): Promise<TeacherGrade> {
|
|
const response = await fetch(`${API_BASE_URL}/teacher-grades/${id}`, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<TeacherGrade>(response);
|
|
}
|
|
|
|
async createTeacherGrade(data: TeacherGradeCreate): Promise<TeacherGrade> {
|
|
const response = await fetch(`${API_BASE_URL}/teacher-grades`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(true),
|
|
body: JSON.stringify(data),
|
|
});
|
|
return this.handleResponse<TeacherGrade>(response);
|
|
}
|
|
|
|
async deleteTeacherGrade(id: string): Promise<void> {
|
|
const response = await fetch(`${API_BASE_URL}/teacher-grades/${id}`, {
|
|
method: "DELETE",
|
|
headers: this.getHeaders(true),
|
|
});
|
|
return this.handleResponse<void>(response);
|
|
}
|
|
|
|
// Export endpoints
|
|
async exportGradesCSV(periodId?: string): Promise<void> {
|
|
const url = periodId
|
|
? `${API_BASE_URL}/export/grades?period_id=${periodId}`
|
|
: `${API_BASE_URL}/export/grades`;
|
|
|
|
const response = await fetch(url, {
|
|
headers: this.getHeaders(true),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error("Failed to export grades");
|
|
}
|
|
|
|
// Create a blob from the response and trigger download
|
|
const blob = await response.blob();
|
|
const downloadUrl = window.URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = downloadUrl;
|
|
|
|
// Extract filename from Content-Disposition header or use default
|
|
const contentDisposition = response.headers.get("Content-Disposition");
|
|
const filename = contentDisposition
|
|
? contentDisposition.split("filename=")[1]?.replace(/"/g, "")
|
|
: "grades_export.csv";
|
|
|
|
a.download = filename;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
document.body.removeChild(a);
|
|
window.URL.revokeObjectURL(downloadUrl);
|
|
}
|
|
}
|
|
|
|
export const api = new ApiClient();
|