uchill/front_material/api/schedule.ts

261 lines
8.2 KiB
TypeScript

/**
* API модуль для расписания занятий
*/
import apiClient from '@/lib/api-client';
export interface Lesson {
id: string;
title: string;
subject?: string;
description?: string;
start_time: string;
end_time: string;
status?: 'scheduled' | 'in_progress' | 'completed' | 'cancelled';
mentor?: {
id: string;
first_name: string;
last_name: string;
email: string;
};
client?: {
id: string;
user?: {
id: string;
first_name: string;
last_name: string;
email: string;
};
};
client_name?: string;
mentor_notes?: string;
mentor_grade?: number;
school_grade?: number;
homework_text?: string;
price?: number;
meeting_url?: string;
duration?: number;
group?: number;
group_name?: string;
livekit_room_name?: string;
completed_at?: string;
}
/** Файл урока (для экрана завершения занятия) */
export interface LessonFile {
id: string | number;
lesson: string | number;
file?: string;
material?: string | number;
source?: 'uploaded' | 'material';
filename: string;
file_size?: number;
file_size_display?: string;
file_url?: string;
description?: string;
uploaded_by?: number;
uploaded_by_name?: string;
created_at?: string;
}
export interface CreateLessonFileData {
lesson: string;
file?: File;
material?: string;
filename?: string;
description?: string;
}
/**
* Получить список занятий
* Для родителя передать child_id (user_id ребёнка).
* Для ментора передать client_id (Client.id) — занятия конкретного студента.
*/
export async function getLessons(params?: {
start_date?: string;
end_date?: string;
status?: string;
child_id?: string;
client_id?: string;
}): Promise<{ results: Lesson[]; count?: number }> {
const queryParams = new URLSearchParams();
if (params?.start_date) queryParams.append('start_date', params.start_date);
if (params?.end_date) queryParams.append('end_date', params.end_date);
if (params?.status) queryParams.append('status', params.status);
if (params?.child_id) queryParams.append('child_id', params.child_id);
if (params?.client_id) queryParams.append('client_id', params.client_id);
const queryString = queryParams.toString();
const url = `/schedule/lessons/${queryString ? `?${queryString}` : ''}`;
const response = await apiClient.get<Lesson[] | { results: Lesson[]; count?: number }>(url);
if (Array.isArray(response.data)) {
return { results: response.data };
}
return response.data;
}
/** Ответ calendar API */
interface CalendarResponse {
success: boolean;
data: { start_date: string; end_date: string; lessons: Lesson[]; total: number };
}
/**
* Занятия для календаря (лёгкий endpoint по диапазону дат).
* Для родителя передать child_id (user_id ребёнка).
*/
export async function getLessonsCalendar(params: {
start_date: string;
end_date: string;
status?: string;
child_id?: string;
}): Promise<{ lessons: Lesson[] }> {
const q = new URLSearchParams({ start_date: params.start_date, end_date: params.end_date });
if (params.status) q.append('status', params.status);
if (params.child_id) q.append('child_id', params.child_id);
// cache: false — после создания/редактирования/удаления занятия интерфейс должен обновиться без перезагрузки
const res = await apiClient.get<CalendarResponse>(`/schedule/lessons/calendar/?${q}`, { cache: false });
const lessons = res.data?.data?.lessons;
return { lessons: Array.isArray(lessons) ? lessons : [] };
}
/**
* Получить занятие по ID
*/
export async function getLesson(id: string): Promise<Lesson> {
const response = await apiClient.get<Lesson>(`/schedule/lessons/${id}/`);
return response.data;
}
export interface CreateLessonData {
client: string;
title?: string;
description?: string;
start_time: string;
duration: number;
price?: number;
is_recurring?: boolean;
subject_id?: number;
mentor_subject_id?: number;
subject_name?: string;
}
export interface UpdateLessonData {
title?: string;
description?: string;
start_time?: string;
duration?: number;
price?: number;
/** Для завершённых занятий — можно изменить статус (cancelled и т.д.) */
status?: 'scheduled' | 'in_progress' | 'completed' | 'cancelled';
}
/**
* Создать занятие
*/
export async function createLesson(data: CreateLessonData): Promise<Lesson> {
const response = await apiClient.post<Lesson>('/schedule/lessons/', data);
return response.data;
}
/**
* Обновить занятие
*/
export async function updateLesson(id: string, data: UpdateLessonData): Promise<Lesson> {
const response = await apiClient.patch<Lesson>(`/schedule/lessons/${id}/`, data);
return response.data;
}
/**
* Удалить занятие
*/
export async function deleteLesson(id: string, deleteAllFuture = false): Promise<void> {
await apiClient.delete(`/schedule/lessons/${id}/`, {
data: { delete_all_future: deleteAllFuture },
});
}
/** Ответ API завершения занятия */
export interface CompleteLessonResponse {
success: boolean;
message?: string;
data?: Lesson;
}
/**
* Завершить занятие / обновить обратную связь.
* lessonFileIds — ID файлов урока, которые нужно привязать к ДЗ (только они попадут в «Файлы задания»).
*/
export async function completeLesson(
id: string,
notes?: string,
mentorGrade?: number,
schoolGrade?: number,
homeworkText?: string,
hasHomeworkFiles?: boolean,
lessonFileIds?: number[]
): Promise<CompleteLessonResponse> {
const body: Record<string, unknown> = {
notes: notes ?? '',
mentor_grade: mentorGrade,
school_grade: schoolGrade,
homework_text: homeworkText,
has_homework_files: hasHomeworkFiles,
};
if (lessonFileIds != null) {
body.lesson_file_ids = lessonFileIds;
}
const response = await apiClient.post<CompleteLessonResponse>(`/schedule/lessons/${id}/complete/`, body);
return response.data;
}
/**
* Получить файлы урока (для экрана завершения занятия).
*/
export async function getLessonFiles(lessonId: string): Promise<LessonFile[]> {
const response = await apiClient.get<LessonFile[] | { results: LessonFile[] }>(
`/schedule/lesson-files/?lesson=${lessonId}`
);
const data = response.data;
if (Array.isArray(data)) return data;
return (data as { results: LessonFile[] })?.results ?? [];
}
/**
* Создать файл урока (загрузка файла или привязка материала).
*/
export async function createLessonFile(data: CreateLessonFileData): Promise<LessonFile> {
const formData = new FormData();
formData.append('lesson', data.lesson);
if (data.file) formData.append('file', data.file);
if (data.material) formData.append('material', data.material);
if (data.filename) formData.append('filename', data.filename);
if (data.description) formData.append('description', data.description);
const response = await apiClient.post<LessonFile>('/schedule/lesson-files/', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
/**
* Удалить файл урока.
*/
export async function deleteLessonFile(fileId: string): Promise<void> {
await apiClient.delete(`/schedule/lesson-files/${fileId}/`);
}
/**
* Прикрепить файл к уроку (для ДЗ при завершении занятия).
* Возвращает созданный LessonFile (нужен id для передачи в complete как lesson_file_ids).
*/
export async function uploadLessonFile(lessonId: number | string, file: File): Promise<LessonFile> {
const formData = new FormData();
formData.append('lesson', String(lessonId));
formData.append('file', file);
const response = await apiClient.post<LessonFile>('/schedule/lesson-files/', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}