добавил денение на подгруппы для лабораторных работ
This commit is contained in:
@@ -72,6 +72,7 @@ export async function initSchedule() {
|
||||
let teachers = [];
|
||||
let classrooms = [];
|
||||
let lessonTypes = [];
|
||||
let subgroups = [];
|
||||
let timeSlots = [];
|
||||
let semesters = [];
|
||||
|
||||
@@ -99,17 +100,24 @@ export async function initSchedule() {
|
||||
renderRuleSlots(slots.length ? slots : [{}]);
|
||||
});
|
||||
|
||||
ruleGroupsContainer.addEventListener('change', () => renderRuleSlots(readRuleSlots(false)));
|
||||
ruleSlotsContainer.addEventListener('change', (event) => {
|
||||
if (event.target.closest('.slot-lesson-type')) {
|
||||
renderRuleSlots(readRuleSlots(false));
|
||||
}
|
||||
});
|
||||
ruleForm.addEventListener('submit', saveRule);
|
||||
rulesTbody.addEventListener('click', handleRuleTableClick);
|
||||
}
|
||||
|
||||
async function loadBaseLists() {
|
||||
[subjects, groups, teachers, classrooms, lessonTypes] = await Promise.all([
|
||||
[subjects, groups, teachers, classrooms, lessonTypes, subgroups] = await Promise.all([
|
||||
api.get('/api/subjects'),
|
||||
api.get('/api/groups'),
|
||||
api.get('/api/users/teachers'),
|
||||
api.get('/api/classrooms'),
|
||||
api.get('/api/lesson-types')
|
||||
api.get('/api/lesson-types'),
|
||||
api.get('/api/subgroups')
|
||||
]);
|
||||
populateSubjectSelects();
|
||||
}
|
||||
@@ -159,6 +167,7 @@ export async function initSchedule() {
|
||||
slot.dayName,
|
||||
slot.timeSlotLabel,
|
||||
parity,
|
||||
slot.subgroupName,
|
||||
slot.teacherName,
|
||||
slot.classroomName,
|
||||
slot.lessonTypeName,
|
||||
@@ -290,6 +299,7 @@ export async function initSchedule() {
|
||||
<div class="form-group"><label>Преподаватель</label><select class="slot-teacher" required>${options(teachers.map(toTeacherOption), slot.teacherId)}</select></div>
|
||||
<div class="form-group"><label>Аудитория</label><select class="slot-classroom" required>${options(classrooms.map(toClassroomOption), slot.classroomId)}</select></div>
|
||||
<div class="form-group"><label>Тип</label><select class="slot-lesson-type" required>${options(lessonTypes.map(toLessonTypeOption), slot.lessonTypeId)}</select></div>
|
||||
<div class="form-group"><label>Подгруппа</label><select class="slot-subgroup" ${isLaboratorySlot(slot) ? '' : 'disabled'}>${subgroupOptions(slot)}</select></div>
|
||||
<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-delete schedule-slot-remove" data-index="${index}">Удалить</button>
|
||||
</div>
|
||||
@@ -299,12 +309,13 @@ export async function initSchedule() {
|
||||
function readRuleSlots(validate) {
|
||||
const rows = Array.from(ruleSlotsContainer.querySelectorAll('.schedule-slot-row'));
|
||||
return rows.map((row, index) => {
|
||||
const subgroupValue = row.querySelector('.slot-subgroup')?.value || '';
|
||||
const slot = {
|
||||
id: null,
|
||||
dayOfWeek: Number(row.querySelector('.slot-day').value),
|
||||
parity: row.querySelector('.slot-parity').value,
|
||||
timeSlotId: Number(row.querySelector('.slot-time').value),
|
||||
subgroupId: null,
|
||||
subgroupId: subgroupValue ? Number(subgroupValue) : null,
|
||||
teacherId: Number(row.querySelector('.slot-teacher').value),
|
||||
classroomId: Number(row.querySelector('.slot-classroom').value),
|
||||
lessonTypeId: Number(row.querySelector('.slot-lesson-type').value),
|
||||
@@ -314,6 +325,9 @@ export async function initSchedule() {
|
||||
|| !slot.teacherId || !slot.classroomId || !slot.lessonTypeId || !slot.lessonFormat)) {
|
||||
throw new Error(`Заполните слот ${index + 1}`);
|
||||
}
|
||||
if (validate) {
|
||||
validateSlotSubgroup(slot, index);
|
||||
}
|
||||
return slot;
|
||||
});
|
||||
}
|
||||
@@ -354,6 +368,23 @@ export async function initSchedule() {
|
||||
if (!hasHours) throw new Error('Укажите часы хотя бы для одного типа занятий');
|
||||
}
|
||||
|
||||
function validateSlotSubgroup(slot, index) {
|
||||
const typeKey = lessonTypeKeyById(slot.lessonTypeId);
|
||||
if (slot.subgroupId && typeKey !== 'laboratory') {
|
||||
throw new Error(`В слоте ${index + 1} подгруппа доступна только для лабораторных`);
|
||||
}
|
||||
if (!slot.subgroupId) return;
|
||||
|
||||
const selectedSubgroup = subgroups.find(item => String(item.id) === String(slot.subgroupId));
|
||||
if (!selectedSubgroup) {
|
||||
throw new Error(`В слоте ${index + 1} подгруппа не найдена`);
|
||||
}
|
||||
const selectedGroups = new Set(selectedRuleGroupIds().map(String));
|
||||
if (!selectedGroups.has(String(selectedSubgroup.groupId))) {
|
||||
throw new Error(`В слоте ${index + 1} подгруппа должна относиться к выбранным группам правила`);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTimeSlots() {
|
||||
const slots = await api.get('/api/admin/time-slots');
|
||||
timeSlots = slots
|
||||
@@ -382,6 +413,26 @@ export async function initSchedule() {
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function subgroupOptions(slot) {
|
||||
if (!isLaboratorySlot(slot)) {
|
||||
return '<option value="">Только для лабораторных</option>';
|
||||
}
|
||||
const selected = slot.subgroupId == null ? '' : String(slot.subgroupId);
|
||||
const selectedGroups = new Set(selectedRuleGroupIds().map(String));
|
||||
const matchingSubgroups = subgroups
|
||||
.filter(subgroup => selectedGroups.has(String(subgroup.groupId)))
|
||||
.sort((a, b) => subgroupLabel(a).localeCompare(subgroupLabel(b), 'ru-RU'));
|
||||
return '<option value="">Вся группа</option>' + matchingSubgroups.map(subgroup => {
|
||||
const value = String(subgroup.id);
|
||||
return `<option value="${escapeHtml(value)}" ${value === selected ? 'selected' : ''}>${escapeHtml(subgroupLabel(subgroup))}</option>`;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
function selectedRuleGroupIds() {
|
||||
return Array.from(ruleGroupsContainer.querySelectorAll('input[type="checkbox"]:checked'))
|
||||
.map(input => Number(input.value));
|
||||
}
|
||||
|
||||
function toTimeSlotOption(slot) {
|
||||
return { value: slot.id, label: `${slot.orderNumber} пара, ${trimTime(slot.startTime)} - ${trimTime(slot.endTime)}` };
|
||||
}
|
||||
@@ -404,6 +455,14 @@ export async function initSchedule() {
|
||||
return lessonType ? lessonTypeKey(lessonType) : null;
|
||||
}
|
||||
|
||||
function isLaboratorySlot(slot) {
|
||||
return lessonTypeKeyById(slot.lessonTypeId) === 'laboratory';
|
||||
}
|
||||
|
||||
function subgroupLabel(subgroup) {
|
||||
return `${subgroup.groupName || `Группа ${subgroup.groupId}`}: ${subgroup.name}`;
|
||||
}
|
||||
|
||||
function lessonTypeKey(lessonType) {
|
||||
const name = String(lessonType.name || lessonType.lessonType || '').toLowerCase();
|
||||
if (name.includes('лекц')) return 'lecture';
|
||||
|
||||
Reference in New Issue
Block a user