import { useEffect, useState } from 'react'; import { CONFLICT_CHECK_STATE, classroomOccupancyLabel, conflictCheckPresentation, currentSlotLabel, currentWeekDateRange, detectScheduleConflicts, fetchDashboardReferenceData, fetchEffectiveTimeSlots, fetchTodayLessons, findCurrentSlot, formatLocalDate, freeClassroomsForSlot, lessonsForSlot, loadWeekDepartmentSchedules, sortTimeSlots, totalStudents } from '../dashboard-service.js'; const REALTIME_UPDATE_MS = 30_000; export function DashboardTab() { const [reference, setReference] = useState(null); const [todayLessons, setTodayLessons] = useState([]); const [timeSlots, setTimeSlots] = useState([]); const [dataPhase, setDataPhase] = useState('loading'); const [conflictPhase, setConflictPhase] = useState('checking'); const [conflicts, setConflicts] = useState({ items: [], presentation: null }); const [recheckBusy, setRecheckBusy] = useState(false); const [now, setNow] = useState(() => new Date()); useEffect(() => { void loadData(); }, []); useEffect(() => { const timer = setInterval(() => setNow(new Date()), REALTIME_UPDATE_MS); return () => clearInterval(timer); }, []); async function loadData() { setDataPhase('loading'); setConflictPhase('checking'); try { const loadedReference = await fetchDashboardReferenceData(); const todayStr = formatLocalDate(new Date()); const [lessons, slots] = await Promise.all([ fetchTodayLessons(loadedReference.departments, todayStr), fetchEffectiveTimeSlots(todayStr) ]); setReference(loadedReference); setTodayLessons(lessons); setTimeSlots(sortTimeSlots(slots)); setDataPhase('ready'); await checkConflicts(loadedReference); } catch { setDataPhase('error'); setConflictPhase('error'); console.error('Не удалось загрузить данные панели'); } } async function checkConflicts(referenceData) { setConflictPhase('checking'); try { const range = currentWeekDateRange(new Date()); const scheduleLoad = await loadWeekDepartmentSchedules(referenceData.departments, range); const items = detectScheduleConflicts(scheduleLoad.lessons, referenceData.groups, referenceData.classrooms); const presentation = scheduleLoad.state === CONFLICT_CHECK_STATE.NOT_RUN ? conflictCheckPresentation(scheduleLoad, 0, range) : conflictCheckPresentation(scheduleLoad, items.length, range); setConflicts({ items, presentation }); setConflictPhase('ready'); } catch { setConflictPhase('error'); console.error('Не удалось выполнить проверку конфликтов'); } } async function recheck() { setRecheckBusy(true); try { if (dataPhase === 'ready' && reference) { await checkConflicts(reference); } else { await loadData(); } } finally { setRecheckBusy(false); } } const ready = dataPhase === 'ready' && reference !== null; const currentSlot = ready ? findCurrentSlot(timeSlots, now) : null; const currentLessons = currentSlot ? lessonsForSlot(todayLessons, currentSlot.orderNumber) : []; const freeRooms = ready ? freeClassroomsForSlot(reference.classrooms, todayLessons, currentSlot ? currentSlot.orderNumber : null) : []; return (

Мониторинг пар (Идет сейчас)

{ready ? currentSlotLabel(currentSlot) : '—'}
{!ready ? ( ) : !currentSlot ? ( ) : currentLessons.length === 0 ? ( ) : currentLessons.map(lesson => ( ))}
Аудитория Дисциплина Преподаватель Группа
Определение текущего времени...
В данный момент учебные пары не проводятся
В текущий слот занятий не запланировано
{lesson.classroomName} {lesson.subjectName}{' '} {lesson.lessonTypeName} {lesson.teacherName} {lesson.groupNames.join(', ')}

Свободные аудитории (ближайшие)

{!ready ? (
Загрузка...
) : freeRooms.length === 0 ? ( Все аудитории заняты ) : freeRooms.map(classroom => ( {classroom.name} ({classroom.capacity} м.) ))}

Быстрый переход

Конструктор расписания Точечные изменения / Просмотр

Центр предупреждений (Red Zone)

Анализ накладок и коллизий расписания на текущую неделю
{conflictPhase === 'checking' ? (
Анализ расписания и выявление конфликтов...
) : conflictPhase === 'error' ? ( ) : ( <> {conflicts.presentation && ( )} {conflicts.items.map((conflict, index) => ( ))} )}
); } function MetricCard({ tone, label, value, children }) { return (
{children}
{label}

{value}

); } function CheckStateCard({ tone, title, description, meta = '' }) { const icon = tone === 'success' ? : tone === 'warning' ? ( <> ) : ( <> ); return (
{title} {description} {meta ? {meta} : null}
); } function ConflictCard({ conflict }) { const isError = conflict.severity === 'error'; return (
{isError ? ( ) : ( )}

{conflict.title}

{(conflict.segments || []).map((segment, index) => ( typeof segment === 'string' ? segment : {segment.strong} ))}

); }