3 Задача
This commit is contained in:
@@ -124,6 +124,7 @@ export async function initSchedule() {
|
||||
let timeSlots = [];
|
||||
let academicYears = [];
|
||||
let semesters = [];
|
||||
let approvedTeacherPreferences = [];
|
||||
let selectedVisualYearId = '';
|
||||
let selectedVisualSemesterId = '';
|
||||
let selectedVisualGroupIds = new Set();
|
||||
@@ -137,7 +138,7 @@ export async function initSchedule() {
|
||||
bindEvents();
|
||||
|
||||
try {
|
||||
await Promise.all([loadBaseLists(), loadTimeSlots(), loadYears()]);
|
||||
await Promise.all([loadBaseLists(), loadTimeSlots(), loadYears(), loadApprovedTeacherPreferences()]);
|
||||
renderRuleGroups([]);
|
||||
renderRuleSlots([{}]);
|
||||
await loadRules();
|
||||
@@ -185,6 +186,7 @@ export async function initSchedule() {
|
||||
renderVisualSchedule();
|
||||
});
|
||||
ruleSemesterSelect?.addEventListener('change', () => {
|
||||
updatePreferenceHints();
|
||||
if (visualPeriodTouched) return;
|
||||
selectDefaultVisualPeriod();
|
||||
renderVisualSchedule();
|
||||
@@ -252,6 +254,7 @@ export async function initSchedule() {
|
||||
if (event.target.closest('.slot-subgroup-checkboxes')) {
|
||||
updateSlotSubgroupText(event.target.closest('.schedule-slot-row'));
|
||||
}
|
||||
updatePreferenceHints();
|
||||
});
|
||||
ruleForm.addEventListener('submit', saveRule);
|
||||
rulesTbody.addEventListener('click', handleRuleTableClick);
|
||||
@@ -276,6 +279,11 @@ export async function initSchedule() {
|
||||
selectDefaultVisualPeriod();
|
||||
}
|
||||
|
||||
async function loadApprovedTeacherPreferences() {
|
||||
approvedTeacherPreferences = await api.get('/api/teacher-preferences?status=APPROVED');
|
||||
updatePreferenceHints();
|
||||
}
|
||||
|
||||
async function loadRules() {
|
||||
rulesTbody.innerHTML = '<tr><td colspan="7" class="loading-row">Загрузка...</td></tr>';
|
||||
try {
|
||||
@@ -1383,8 +1391,55 @@ export async function initSchedule() {
|
||||
${isLaboratorySlot(slot) ? renderSubgroupField(slot, index) : ''}
|
||||
<div class="form-group"><label>Формат</label><select class="slot-format" required>${options([{ value: 'Очно', label: 'Очно' }, { value: 'Онлайн', label: 'Онлайн' }], slot.lessonFormat || 'Очно')}</select></div>
|
||||
<button type="button" class="btn btn-md ${actionClass} schedule-slot-remove" data-index="${index}">${actionLabel}</button>
|
||||
<div class="schedule-preference-hint" data-preference-hint></div>
|
||||
</div>
|
||||
`).join('');
|
||||
updatePreferenceHints();
|
||||
}
|
||||
|
||||
function updatePreferenceHints() {
|
||||
const semesterId = Number(ruleSemesterSelect?.value);
|
||||
Array.from(ruleSlotsContainer?.querySelectorAll('.schedule-slot-row') || []).forEach(row => {
|
||||
const hint = row.querySelector('[data-preference-hint]');
|
||||
if (!hint) return;
|
||||
const teacherId = Number(row.querySelector('.slot-teacher')?.value);
|
||||
const dayOfWeek = Number(row.querySelector('.slot-day')?.value);
|
||||
const timeSlotId = Number(row.querySelector('.slot-time')?.value);
|
||||
if (!semesterId || !teacherId) {
|
||||
hint.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
const teacherItems = approvedTeacherPreferences.filter(item =>
|
||||
Number(item.semesterId) === semesterId && Number(item.teacherId) === teacherId
|
||||
);
|
||||
const exact = teacherItems.filter(item =>
|
||||
Number(item.dayOfWeek) === dayOfWeek && Number(item.timeSlotId) === timeSlotId
|
||||
);
|
||||
const dateCount = teacherItems.filter(item => item.preferenceType === 'HARD_UNAVAILABLE' && item.preferenceDate).length;
|
||||
const flags = teacherItems.filter(item => ['CONSECUTIVE', 'NO_GAPS'].includes(item.preferenceType));
|
||||
const pills = [
|
||||
...exact.map(item => ({
|
||||
text: preferenceHintLabel(item.preferenceType),
|
||||
kind: item.preferenceType === 'HARD_UNAVAILABLE' ? 'hard'
|
||||
: item.preferenceType === 'SOFT_PREFERRED' ? 'preferred' : 'unwanted'
|
||||
})),
|
||||
...(dateCount ? [{ text: `Недоступных дат: ${dateCount}`, kind: 'dates' }] : []),
|
||||
...flags.map(item => ({ text: preferenceHintLabel(item.preferenceType), kind: 'compact' }))
|
||||
];
|
||||
hint.innerHTML = pills.length
|
||||
? `<span class="schedule-preference-caption">Пожелания преподавателя</span>${pills.map(pill => `<span class="schedule-preference-pill ${pill.kind}">${escapeHtml(pill.text)}</span>`).join('')}`
|
||||
: '<span class="schedule-preference-caption muted">Согласованных пожеланий для слота нет</span>';
|
||||
});
|
||||
}
|
||||
|
||||
function preferenceHintLabel(type) {
|
||||
return {
|
||||
HARD_UNAVAILABLE: 'Строгая недоступность',
|
||||
SOFT_PREFERRED: 'Предпочтительно',
|
||||
SOFT_UNWANTED: 'Нежелательно',
|
||||
CONSECUTIVE: 'Пары подряд',
|
||||
NO_GAPS: 'Без окон'
|
||||
}[type] || type;
|
||||
}
|
||||
|
||||
function readRuleSlots(validate) {
|
||||
|
||||
Reference in New Issue
Block a user