213 lines
6.7 KiB
JavaScript
213 lines
6.7 KiB
JavaScript
import { useMemo, useState, useEffect } from 'react';
|
|
import { format, startOfMonth, endOfMonth, addMonths, subMonths } from 'date-fns';
|
|
import useSWR, { mutate } from 'swr';
|
|
|
|
import {
|
|
getCalendarLessons,
|
|
getMentorStudents,
|
|
getMentorSubjects,
|
|
createCalendarLesson,
|
|
updateCalendarLesson,
|
|
deleteCalendarLesson,
|
|
} from 'src/utils/dashboard-api';
|
|
import { getGroups } from 'src/utils/groups-api';
|
|
import { useAuthContext } from 'src/auth/hooks';
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
const STUDENTS_ENDPOINT = '/manage/clients/?page=1&page_size=200';
|
|
const SUBJECTS_ENDPOINT = '/schedule/subjects/';
|
|
const GROUPS_ENDPOINT = '/groups/';
|
|
|
|
const swrOptions = {
|
|
revalidateIfStale: true,
|
|
revalidateOnFocus: false,
|
|
revalidateOnReconnect: true,
|
|
};
|
|
|
|
// Ключ кэша для календаря (по месяцу)
|
|
function calendarKey(date = new Date()) {
|
|
const start = format(startOfMonth(subMonths(date, 1)), 'yyyy-MM-dd');
|
|
const end = format(endOfMonth(addMonths(date, 1)), 'yyyy-MM-dd');
|
|
return ['calendar', start, end];
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
export function useGetEvents(currentDate) {
|
|
const date = currentDate || new Date();
|
|
const start = format(startOfMonth(subMonths(date, 1)), 'yyyy-MM-dd');
|
|
const end = format(endOfMonth(addMonths(date, 1)), 'yyyy-MM-dd');
|
|
|
|
const { user } = useAuthContext();
|
|
|
|
const getChildId = () => {
|
|
if (user?.role !== 'parent') return null;
|
|
try { const s = localStorage.getItem('selected_child'); return s ? (JSON.parse(s)?.id || null) : null; } catch { return null; }
|
|
};
|
|
const [childId, setChildId] = useState(getChildId);
|
|
|
|
useEffect(() => {
|
|
if (user?.role !== 'parent') return undefined;
|
|
const handler = () => setChildId(getChildId());
|
|
window.addEventListener('child-changed', handler);
|
|
return () => window.removeEventListener('child-changed', handler);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [user?.role]);
|
|
|
|
const { data: response, isLoading, error, isValidating } = useSWR(
|
|
['calendar', start, end, childId],
|
|
([, s, e, cid]) => getCalendarLessons(s, e, cid ? { child_id: cid } : undefined),
|
|
swrOptions
|
|
);
|
|
|
|
const memoizedValue = useMemo(() => {
|
|
const lessonsArray = response?.data?.lessons || response?.lessons || [];
|
|
|
|
const events = lessonsArray.map((lesson) => {
|
|
const start = lesson.start_time || lesson.start;
|
|
const end = lesson.end_time || lesson.end || start;
|
|
|
|
const startTimeStr = start
|
|
? new Date(start).toLocaleTimeString('ru-RU', {
|
|
hour: '2-digit',
|
|
minute: '2-digit',
|
|
hourCycle: 'h23',
|
|
})
|
|
: '';
|
|
|
|
const subject = lesson.subject_name || lesson.subject || 'Урок';
|
|
const participant = lesson.group_name
|
|
? `Группа: ${lesson.group_name}`
|
|
: (lesson.client_name || '');
|
|
const displayTitle = `${startTimeStr} ${subject}${participant ? ` - ${participant}` : ''}`;
|
|
|
|
const status = String(lesson.status || 'scheduled').toLowerCase();
|
|
let eventColor = '#7635dc';
|
|
if (status === 'completed') eventColor = '#00ab55';
|
|
else if (status === 'cancelled') eventColor = '#ff4842';
|
|
else if (status === 'in_progress') eventColor = '#1890ff';
|
|
|
|
return {
|
|
id: String(lesson.id),
|
|
title: displayTitle,
|
|
start,
|
|
end,
|
|
allDay: false,
|
|
color: eventColor,
|
|
backgroundColor: eventColor,
|
|
borderColor: eventColor,
|
|
textColor: '#FFFFFF',
|
|
extendedProps: {
|
|
...lesson,
|
|
status,
|
|
student: lesson.client_name || '',
|
|
mentor: lesson.mentor_name || '',
|
|
group: lesson.group || null,
|
|
group_name: lesson.group_name || '',
|
|
},
|
|
};
|
|
});
|
|
|
|
return {
|
|
events,
|
|
eventsLoading: isLoading,
|
|
eventsError: error,
|
|
eventsValidating: isValidating,
|
|
eventsEmpty: !isLoading && !events.length,
|
|
};
|
|
}, [response, error, isLoading, isValidating]);
|
|
|
|
return memoizedValue;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
export function useGetStudents() {
|
|
const { data: response, isLoading, error } = useSWR(
|
|
STUDENTS_ENDPOINT,
|
|
getMentorStudents,
|
|
swrOptions
|
|
);
|
|
|
|
return useMemo(() => {
|
|
const rawData = response?.data?.results || response?.results || response || [];
|
|
return {
|
|
students: Array.isArray(rawData) ? rawData : [],
|
|
studentsLoading: isLoading,
|
|
studentsError: error,
|
|
};
|
|
}, [response, isLoading, error]);
|
|
}
|
|
|
|
export function useGetSubjects() {
|
|
const { data: response, isLoading, error } = useSWR(
|
|
SUBJECTS_ENDPOINT,
|
|
getMentorSubjects,
|
|
swrOptions
|
|
);
|
|
|
|
return useMemo(() => {
|
|
const rawData = response?.data || response?.results || response || [];
|
|
return {
|
|
subjects: Array.isArray(rawData) ? rawData : [],
|
|
subjectsLoading: isLoading,
|
|
subjectsError: error,
|
|
};
|
|
}, [response, isLoading, error]);
|
|
}
|
|
|
|
export function useGetGroups() {
|
|
const { data, isLoading, error } = useSWR(GROUPS_ENDPOINT, getGroups, swrOptions);
|
|
|
|
return useMemo(() => ({
|
|
groups: Array.isArray(data) ? data : [],
|
|
groupsLoading: isLoading,
|
|
groupsError: error,
|
|
}), [data, isLoading, error]);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
function revalidateCalendar() {
|
|
mutate((key) => Array.isArray(key) && key[0] === 'calendar', undefined, { revalidate: true });
|
|
}
|
|
|
|
export async function createEvent(eventData) {
|
|
const isGroup = !!eventData.group;
|
|
const payload = {
|
|
title: eventData.title || 'Занятие',
|
|
description: eventData.description || '',
|
|
start_time: eventData.start_time,
|
|
duration: eventData.duration || 60,
|
|
price: eventData.price,
|
|
is_recurring: eventData.is_recurring || false,
|
|
...(eventData.subject && { subject_id: Number(eventData.subject) }),
|
|
...(isGroup ? { group: eventData.group } : { client: String(eventData.client) }),
|
|
};
|
|
|
|
const res = await createCalendarLesson(payload);
|
|
revalidateCalendar();
|
|
return res;
|
|
}
|
|
|
|
export async function updateEvent(eventData, currentDate) {
|
|
const { id, ...data } = eventData;
|
|
|
|
const updatePayload = {};
|
|
if (data.start_time) updatePayload.start_time = data.start_time;
|
|
if (data.duration) updatePayload.duration = data.duration;
|
|
if (data.price != null) updatePayload.price = data.price;
|
|
if (data.description != null) updatePayload.description = data.description;
|
|
if (data.status) updatePayload.status = data.status;
|
|
|
|
const res = await updateCalendarLesson(String(id), updatePayload);
|
|
revalidateCalendar();
|
|
return res;
|
|
}
|
|
|
|
export async function deleteEvent(eventId, deleteAllFuture = false) {
|
|
await deleteCalendarLesson(String(eventId), deleteAllFuture);
|
|
revalidateCalendar();
|
|
}
|