first commit
This commit is contained in:
286
app/api/client.ts
Normal file
286
app/api/client.ts
Normal file
@@ -0,0 +1,286 @@
|
||||
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();
|
||||
Reference in New Issue
Block a user