import { useEffect, useState } from 'react';
import { formatIsoDate, toIsoDate } from '../shared/dates.js';
import {
activeDatePreferences,
activeWeekPreferences,
createPreference,
deletePreference,
fetchPreferenceMeta,
fetchPreferences,
findCompactPreference,
findWeekPreference,
preferenceStatusLabel,
preferenceTypeClass,
preferenceTypeLabel,
preferenceTypeSymbol,
resolveCurrentSemester,
resolveSelectedSemester,
semesterLabel
} from './teacher-service.js';
const WEEK_DAY_LABELS = Object.freeze(['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']);
export function PreferencesTab() {
const [meta, setMeta] = useState({ semesters: [], timeSlots: [] });
const [metaError, setMetaError] = useState(false);
const [semesterId, setSemesterId] = useState('');
const [preferences, setPreferences] = useState([]);
const [brush, setBrush] = useState('SOFT_PREFERRED');
const [message, setMessage] = useState({ text: '', isError: false });
const [dateValue, setDateValue] = useState('');
const [dateComment, setDateComment] = useState('');
const [busyCell, setBusyCell] = useState('');
useEffect(() => {
let active = true;
(async () => {
try {
const loaded = await fetchPreferenceMeta();
if (!active) return;
setMeta(loaded);
const semesters = loaded.semesters || [];
const current = resolveCurrentSemester(semesters, toIsoDate(new Date()));
if (current) setSemesterId(String(current.id));
} catch {
if (active) setMetaError(true);
}
})();
return () => {
active = false;
};
}, []);
useEffect(() => {
if (!semesterId) return undefined;
let active = true;
(async () => {
try {
const loaded = await fetchPreferences(semesterId);
if (!active) return;
setPreferences(loaded);
setMessage({ text: '', isError: false });
} catch {
if (active) setMessage({ text: 'Не удалось загрузить пожелания', isError: true });
}
})();
return () => {
active = false;
};
}, [semesterId]);
const semester = resolveSelectedSemester(meta.semesters, semesterId);
useEffect(() => {
const min = semester?.startDate || '';
const max = semester?.endDate || '';
if (!dateValue || dateValue < min || dateValue > max) {
setDateValue(min);
}
}, [semester, dateValue]);
async function reloadPreferences() {
try {
setPreferences(await fetchPreferences(semesterId));
} catch {
setMessage({ text: 'Не удалось загрузить пожелания', isError: true });
}
}
function showMessage(text, isError) {
setMessage({ text, isError });
}
async function handleCell(dayOfWeek, timeSlotId) {
const existing = findWeekPreference(preferences, dayOfWeek, timeSlotId);
if (existing) {
if (existing.status === 'APPROVED') {
showMessage('Согласованную отметку может снять кафедра или учебный отдел', true);
return;
}
try {
await deletePreference(existing.id);
showMessage('Ожидающее пожелание отменено', false);
await reloadPreferences();
} catch {
showMessage('Не удалось отменить пожелание', true);
}
return;
}
const cellKey = `${dayOfWeek}:${timeSlotId}`;
setBusyCell(cellKey);
try {
await createPreference({
dayOfWeek,
preferenceType: brush,
semesterId: Number(semesterId),
timeSlotId: Number(timeSlotId)
});
showMessage('Отметка отправлена на согласование', false);
await reloadPreferences();
} catch {
showMessage('Не удалось сохранить пожелание', true);
} finally {
setBusyCell('');
}
}
async function submitDate(event) {
event.preventDefault();
if (!dateValue) return;
try {
await createPreference({
comment: dateComment.trim() || null,
preferenceDate: dateValue,
preferenceType: 'HARD_UNAVAILABLE',
semesterId: Number(semesterId)
});
setDateComment('');
showMessage('Дата недоступности отправлена на согласование', false);
await reloadPreferences();
} catch {
showMessage('Не удалось сохранить пожелание', true);
}
}
async function cancelDatePreference(id) {
try {
await deletePreference(id);
showMessage('Ожидающее пожелание отменено', false);
await reloadPreferences();
} catch {
showMessage('Не удалось отменить пожелание', true);
}
}
async function toggleCompact(type, checked, existingId) {
try {
if (!checked && existingId) {
await deletePreference(existingId);
} else if (checked) {
await createPreference({
preferenceType: type,
semesterId: Number(semesterId)
});
}
await reloadPreferences();
} catch {
showMessage('Не удалось сохранить пожелание', true);
await reloadPreferences();
}
}
const slots = meta.timeSlots || [];
const weekCells = activeWeekPreferences(preferences);
const dateItems = activeDatePreferences(preferences);
return (
<>
Строгая недоступность блокирует назначение. Мягкие отметки и пожелания к компактности помогают учебному отделу выбрать удобный вариант.Соберите ритм семестра до составления расписания
Отмечайте интервалы одним касанием
Добавьте отдельную дату внутри семестра — все пары этого дня будут считаться строгим ограничением.
Эти мягкие пожелания не отменяют занятия автоматически, но видны при составлении правил.