app/src/services/timetableService.ts
Agent Zero 0f4956d4a4 fix: correct enrollment service method aliases
- Fix getEnrollmentRequests to use listEnrollmentRequests
- Fix respondToEnrollmentRequest to use respondToEnrollment
2026-02-26 17:47:37 +00:00

379 lines
14 KiB
TypeScript

import axios from 'axios';
import { supabase } from '../supabaseClient';
import {
Class,
ClassWithRelations,
ClassFilters,
Timetable,
TimetableWithRelations,
TimetableLesson,
Lesson,
LessonWithRelations,
EnrollmentRequest,
EnrollmentRequestWithProfile,
} from '../types/timetable.types';
// API base URL
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
// ============================================================================
// Helper: Get auth headers
// ============================================================================
async function getAuthHeaders(): Promise<Record<string, string>> {
const { data: { session } } = await supabase.auth.getSession();
if (!session?.access_token) {
throw new Error('No authentication token available');
}
return {
Authorization: `Bearer ${session.access_token}`,
};
}
// ============================================================================
// Class Service
// ============================================================================
export const classService = {
async listClasses(filters?: ClassFilters): Promise<{ classes: Class[]; total: number }> {
const headers = await getAuthHeaders();
const params = new URLSearchParams();
if (filters?.subject) params.append('subject', filters.subject);
if (filters?.school_year) params.append('school_year', filters.school_year);
if (filters?.academic_term) params.append('academic_term', filters.academic_term);
if (filters?.search) params.append('search', filters.search);
if (filters?.skip !== undefined) params.append('skip', filters.skip.toString());
if (filters?.limit !== undefined) params.append('limit', filters.limit.toString());
const response = await axios.get(`${API_BASE}/database/timetable/classes?${params}`, { headers });
return response.data;
},
async getClass(classId: string): Promise<ClassWithRelations> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/classes/${classId}`, { headers });
return response.data;
},
async createClass(data: {
name: string;
subject: string;
school_year: string;
academic_term: string;
description?: string;
}): Promise<Class> {
const headers = await getAuthHeaders();
const response = await axios.post(`${API_BASE}/database/timetable/classes`, data, { headers });
return response.data;
},
async updateClass(classId: string, data: Partial<Class>): Promise<Class> {
const headers = await getAuthHeaders();
const response = await axios.patch(`${API_BASE}/database/timetable/classes/${classId}`, data, { headers });
return response.data;
},
async deleteClass(classId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.delete(`${API_BASE}/database/timetable/classes/${classId}`, { headers });
},
async getMyClasses(): Promise<Class[]> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/classes/me/student`, { headers });
return response.data.classes;
},
async getMyTeachingClasses(): Promise<Class[]> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/classes/me/teacher`, { headers });
return response.data.classes;
},
// Teacher management
async addTeacher(classId: string, teacherId: string, isPrimary: boolean = false): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(
`${API_BASE}/database/timetable/classes/${classId}/teachers`,
{ teacher_id: teacherId, is_primary: isPrimary },
{ headers }
);
},
async removeTeacher(classId: string, teacherId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.delete(`${API_BASE}/database/timetable/classes/${classId}/teachers/${teacherId}`, { headers });
},
// Student management
async addStudent(classId: string, studentId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(
`${API_BASE}/database/timetable/classes/${classId}/students`,
{ student_id: studentId },
{ headers }
);
},
async removeStudent(classId: string, studentId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.delete(`${API_BASE}/database/timetable/classes/${classId}/students/${studentId}`, { headers });
},
async leaveClass(classId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(`${API_BASE}/database/timetable/classes/${classId}/leave`, {}, { headers });
},
};
// ============================================================================
// Timetable Service
// ============================================================================
export const timetableOnlyService = {
async listTimetables(filters?: {
class_id?: string;
type?: 'ad-hoc' | 'recurring';
active?: boolean;
}): Promise<Timetable[]> {
const headers = await getAuthHeaders();
const params = new URLSearchParams();
if (filters?.class_id) params.append('class_id', filters.class_id);
if (filters?.type) params.append('type', filters.type);
if (filters?.active !== undefined) params.append('active', filters.active.toString());
const response = await axios.get(`${API_BASE}/database/timetable/timetables?${params}`, { headers });
return response.data.timetables;
},
async getTimetable(timetableId: string): Promise<TimetableWithRelations> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/timetables/${timetableId}`, { headers });
return response.data;
},
async createTimetable(data: {
class_id: string;
name: string;
type: 'ad-hoc' | 'recurring';
start_date: string;
end_date?: string;
recurrence_pattern?: Record<string, unknown>;
recurrence_rule?: string;
whiteboard_type?: 'single' | 'multiple';
}): Promise<Timetable> {
const headers = await getAuthHeaders();
const response = await axios.post(`${API_BASE}/database/timetable/timetables`, data, { headers });
return response.data;
},
async updateTimetable(timetableId: string, data: Partial<Timetable>): Promise<Timetable> {
const headers = await getAuthHeaders();
const response = await axios.patch(`${API_BASE}/database/timetable/timetables/${timetableId}`, data, { headers });
return response.data;
},
async deleteTimetable(timetableId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.delete(`${API_BASE}/database/timetable/timetables/${timetableId}`, { headers });
},
async generateLessons(timetableId: string, startDate: string, endDate: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(
`${API_BASE}/database/timetable/timetables/${timetableId}/generate-lessons`,
{ start_date: startDate, end_date: endDate },
{ headers }
);
},
// Timetable Teachers
async addTimetableTeacher(timetableId: string, teacherId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(
`${API_BASE}/database/timetable/timetables/${timetableId}/teachers`,
{ teacher_id: teacherId },
{ headers }
);
},
async removeTimetableTeacher(timetableId: string, teacherId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.delete(`${API_BASE}/database/timetable/timetables/${timetableId}/teachers/${teacherId}`, { headers });
},
// Lessons from timetable
async getTimetableLessons(timetableId: string): Promise<TimetableLesson[]> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/timetables/${timetableId}/lessons`, { headers });
return response.data.lessons;
},
};
// ============================================================================
// Lesson Service
// ============================================================================
export const lessonService = {
async listLessons(filters?: {
timetable_id?: string;
start_date?: string;
end_date?: string;
status?: 'scheduled' | 'in-progress' | 'completed' | 'cancelled';
}): Promise<Lesson[]> {
const headers = await getAuthHeaders();
const params = new URLSearchParams();
if (filters?.timetable_id) params.append('timetable_id', filters.timetable_id);
if (filters?.start_date) params.append('start_date', filters.start_date);
if (filters?.end_date) params.append('end_date', filters.end_date);
if (filters?.status) params.append('status', filters.status);
const response = await axios.get(`${API_BASE}/database/timetable/lessons?${params}`, { headers });
return response.data.lessons;
},
async getLesson(lessonId: string): Promise<LessonWithRelations> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/lessons/${lessonId}`, { headers });
return response.data;
},
async createLesson(data: {
timetable_id: string;
scheduled_start: string;
scheduled_end: string;
teacher_id?: string;
title?: string;
description?: string;
}): Promise<Lesson> {
const headers = await getAuthHeaders();
const response = await axios.post(`${API_BASE}/database/timetable/lessons`, data, { headers });
return response.data;
},
async updateLesson(lessonId: string, data: Partial<Lesson>): Promise<Lesson> {
const headers = await getAuthHeaders();
const response = await axios.patch(`${API_BASE}/database/timetable/lessons/${lessonId}`, data, { headers });
return response.data;
},
async deleteLesson(lessonId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.delete(`${API_BASE}/database/timetable/lessons/${lessonId}`, { headers });
},
async startLesson(lessonId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(`${API_BASE}/database/timetable/lessons/${lessonId}/start`, {}, { headers });
},
async endLesson(lessonId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(`${API_BASE}/database/timetable/lessons/${lessonId}/end`, {}, { headers });
},
async cancelLesson(lessonId: string, reason?: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(
`${API_BASE}/database/timetable/lessons/${lessonId}/cancel`,
{ reason },
{ headers }
);
},
};
// ============================================================================
// Enrollment Request Service
// ============================================================================
export const enrollmentService = {
async requestEnrollment(classId: string, message?: string): Promise<EnrollmentRequest> {
const headers = await getAuthHeaders();
const response = await axios.post(
`${API_BASE}/database/timetable/classes/${classId}/enroll`,
{ message },
{ headers }
);
return response.data;
},
async listEnrollmentRequests(classId: string): Promise<EnrollmentRequestWithProfile[]> {
const headers = await getAuthHeaders();
const response = await axios.get(`${API_BASE}/database/timetable/classes/${classId}/enrollment-requests`, { headers });
return response.data.requests;
},
async respondToEnrollment(requestId: string, status: 'approved' | 'rejected'): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(
`${API_BASE}/database/timetable/enrollment-requests/${requestId}/respond`,
{ status },
{ headers }
);
},
async cancelEnrollmentRequest(requestId: string): Promise<void> {
const headers = await getAuthHeaders();
await axios.post(`${API_BASE}/database/timetable/enrollment-requests/${requestId}/cancel`, {}, { headers });
},
};
// ============================================================================
// Combined Export
// ============================================================================
export const timetableService = {
...classService,
...lessonService,
...enrollmentService,
// Aliases for store compatibility
getClasses: classService.listClasses,
getClass: classService.getClass,
createClass: classService.createClass,
updateClass: classService.updateClass,
deleteClass: classService.deleteClass,
getMyClasses: classService.getMyClasses,
getMyTeachingClasses: classService.getMyTeachingClasses,
addTeacherToClass: classService.addTeacher,
removeTeacherFromClass: classService.removeTeacher,
addStudentToClass: classService.addStudent,
removeStudentFromClass: classService.removeStudent,
// Timetable aliases
getTimetables: timetableOnlyService.listTimetables,
getTimetable: timetableOnlyService.getTimetable,
createTimetable: timetableOnlyService.createTimetable,
updateTimetable: timetableOnlyService.updateTimetable,
deleteTimetable: timetableOnlyService.deleteTimetable,
getMyTimetables: timetableOnlyService.getMyTimetables,
getMyTeachingTimetables: timetableOnlyService.getMyTeachingTimetables,
addTeacherToTimetable: timetableOnlyService.addTeacher,
removeTeacherFromTimetable: timetableOnlyService.removeTeacher,
addTimetableLesson: timetableOnlyService.addLesson,
updateTimetableLesson: timetableOnlyService.updateLesson,
removeTimetableLesson: timetableOnlyService.removeLesson,
// Lesson aliases
getLessons: lessonService.listLessons,
getLesson: lessonService.getLesson,
generateLessons: lessonService.generateLessons,
cancelLesson: lessonService.cancelLesson,
getMyLessons: lessonService.getMyLessons,
getMyTeachingLessons: lessonService.getMyTeachingLessons,
saveWhiteboard: lessonService.saveWhiteboard,
getWhiteboard: lessonService.getWhiteboard,
exportWhiteboard: lessonService.exportWhiteboard,
// Enrollment aliases
getEnrollmentRequests: enrollmentService.listEnrollmentRequests,
requestEnrollment: enrollmentService.requestEnrollment,
respondToEnrollmentRequest: enrollmentService.respondToEnrollment,
cancelEnrollmentRequest: enrollmentService.cancelEnrollmentRequest,
};
export default timetableOnlyService;