создал систему календарного учебного графика
This commit is contained in:
460
frontend/admin/js/views/groups.js
Executable file → Normal file
460
frontend/admin/js/views/groups.js
Executable file → Normal file
@@ -8,105 +8,408 @@ export async function initGroups() {
|
||||
const newGroupEfSelect = document.getElementById('new-group-ef');
|
||||
const newGroupDepartmentSelect = document.getElementById('new-group-department');
|
||||
const newGroupSpecialitySelect = document.getElementById('new-group-speciality-code');
|
||||
const newGroupProfileSelect = document.getElementById('new-group-profile');
|
||||
const filterEfSelect = document.getElementById('filter-ef');
|
||||
const modalEditGroup = document.getElementById('modal-edit-group');
|
||||
const modalEditGroupClose = document.getElementById('modal-edit-group-close');
|
||||
const editGroupForm = document.getElementById('edit-group-form');
|
||||
const editGroupEfSelect = document.getElementById('edit-group-ef');
|
||||
const editGroupDepartmentSelect = document.getElementById('edit-group-department');
|
||||
const editGroupSpecialitySelect = document.getElementById('edit-group-speciality-code');
|
||||
const editGroupProfileSelect = document.getElementById('edit-group-profile');
|
||||
const calendarForm = document.getElementById('group-calendar-form');
|
||||
const calendarGroupSelect = document.getElementById('calendar-group');
|
||||
const calendarYearSelect = document.getElementById('calendar-year');
|
||||
const calendarSelect = document.getElementById('calendar-id');
|
||||
const calendarAssignmentsTbody = document.getElementById('group-calendar-tbody');
|
||||
|
||||
let allGroups = [];
|
||||
let educationForms = [];
|
||||
let departments = [];
|
||||
let specialties = [];
|
||||
let profilesBySpecialty = new Map();
|
||||
let academicYears = [];
|
||||
let calendars = [];
|
||||
let currentAssignments = [];
|
||||
|
||||
bindEvents();
|
||||
await loadInitialData();
|
||||
|
||||
function bindEvents() {
|
||||
filterEfSelect.addEventListener('change', applyGroupFilter);
|
||||
newGroupSpecialitySelect.addEventListener('change', () => populateProfileSelect(newGroupProfileSelect, newGroupSpecialitySelect.value));
|
||||
editGroupSpecialitySelect.addEventListener('change', () => populateProfileSelect(editGroupProfileSelect, editGroupSpecialitySelect.value));
|
||||
calendarGroupSelect.addEventListener('change', () => {
|
||||
populateCalendarSelect();
|
||||
loadAssignments();
|
||||
});
|
||||
calendarYearSelect.addEventListener('change', populateCalendarSelect);
|
||||
createGroupForm.addEventListener('submit', createGroup);
|
||||
editGroupForm.addEventListener('submit', updateGroup);
|
||||
calendarForm.addEventListener('submit', saveAssignment);
|
||||
groupsTbody.addEventListener('click', handleGroupTableClick);
|
||||
calendarAssignmentsTbody.addEventListener('click', handleAssignmentTableClick);
|
||||
modalEditGroupClose.addEventListener('click', () => modalEditGroup.classList.remove('open'));
|
||||
modalEditGroup.addEventListener('click', (event) => {
|
||||
if (event.target === modalEditGroup) {
|
||||
modalEditGroup.classList.remove('open');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadInitialData() {
|
||||
try {
|
||||
[educationForms, departments, specialties] = await Promise.all([
|
||||
[educationForms, departments, specialties, academicYears, calendars] = await Promise.all([
|
||||
fetchEducationForms(),
|
||||
api.get('/api/departments'),
|
||||
api.get('/api/specialties')
|
||||
api.get('/api/specialties'),
|
||||
api.get('/api/admin/calendar/years'),
|
||||
api.get('/api/admin/academic-calendars')
|
||||
]);
|
||||
await loadProfiles();
|
||||
populateEfSelects(educationForms);
|
||||
populateDepartmentSelect(departments);
|
||||
populateSpecialitySelect(specialties);
|
||||
populateDepartmentSelects(departments);
|
||||
populateSpecialitySelects(specialties);
|
||||
populateYearSelect();
|
||||
populateCalendarSelect();
|
||||
await loadGroups();
|
||||
} catch (e) {
|
||||
groupsTbody.innerHTML = '<tr><td colspan="8" class="loading-row">Ошибка загрузки данных</td></tr>';
|
||||
} catch (error) {
|
||||
groupsTbody.innerHTML = `<tr><td colspan="9" class="loading-row">Ошибка загрузки данных: ${escapeHtml(error.message)}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadProfiles() {
|
||||
const pairs = await Promise.all(specialties.map(async (speciality) => {
|
||||
const profiles = await api.get(`/api/specialties/${speciality.id}/profiles`);
|
||||
return [String(speciality.id), profiles];
|
||||
}));
|
||||
profilesBySpecialty = new Map(pairs);
|
||||
}
|
||||
|
||||
async function loadGroups() {
|
||||
try {
|
||||
allGroups = await api.get('/api/groups');
|
||||
applyGroupFilter();
|
||||
} catch (e) {
|
||||
groupsTbody.innerHTML = '<tr><td colspan="8" class="loading-row">Ошибка загрузки</td></tr>';
|
||||
populateGroupSelect();
|
||||
await loadAssignments();
|
||||
} catch (error) {
|
||||
groupsTbody.innerHTML = `<tr><td colspan="9" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
function applyGroupFilter() {
|
||||
const filterId = filterEfSelect.value;
|
||||
const filtered = filterId
|
||||
? allGroups.filter(g => g.educationFormId == filterId)
|
||||
? allGroups.filter(group => group.educationFormId == filterId)
|
||||
: allGroups;
|
||||
renderGroups(filtered);
|
||||
}
|
||||
|
||||
filterEfSelect.addEventListener('change', applyGroupFilter);
|
||||
function renderGroups(groups) {
|
||||
if (!groups || !groups.length) {
|
||||
groupsTbody.innerHTML = '<tr><td colspan="9" class="loading-row">Нет групп</td></tr>';
|
||||
return;
|
||||
}
|
||||
groupsTbody.innerHTML = groups.map(group => `
|
||||
<tr>
|
||||
<td>${group.id}</td>
|
||||
<td>${escapeHtml(group.name)}</td>
|
||||
<td>${escapeHtml(String(group.groupSize))}</td>
|
||||
<td><span class="badge badge-ef">${escapeHtml(group.educationFormName)}</span></td>
|
||||
<td>${escapeHtml(departmentLabel(group.departmentId))}</td>
|
||||
<td>${group.course || '-'}</td>
|
||||
<td>${escapeHtml(specialityLabel(group.specialtyId || group.specialityCode))}</td>
|
||||
<td>${escapeHtml(group.specialtyProfileName || '-')}</td>
|
||||
<td style="text-align: right;">
|
||||
<button class="btn-edit-classroom btn-edit-group" data-id="${group.id}">Изменить</button>
|
||||
<button class="btn-delete" data-id="${group.id}" style="margin-left: 0.5rem;">Удалить</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function populateEfSelects(forms) {
|
||||
// Group creation select
|
||||
const currentVal = newGroupEfSelect.value;
|
||||
newGroupEfSelect.innerHTML = forms.map(ef =>
|
||||
`<option value="${ef.id}">${escapeHtml(ef.name)}</option>`
|
||||
).join('');
|
||||
if (currentVal && forms.find(f => f.id == currentVal)) {
|
||||
newGroupEfSelect.value = currentVal;
|
||||
}
|
||||
syncSelects(newGroupEfSelect);
|
||||
|
||||
// Filter select
|
||||
populateEfSelect(newGroupEfSelect, forms);
|
||||
populateEfSelect(editGroupEfSelect, forms);
|
||||
const currentFilter = filterEfSelect.value;
|
||||
filterEfSelect.innerHTML = '<option value="">Все формы</option>' +
|
||||
forms.map(ef =>
|
||||
`<option value="${ef.id}">${escapeHtml(ef.name)}</option>`
|
||||
).join('');
|
||||
forms.map(form => `<option value="${form.id}">${escapeHtml(form.name)}</option>`).join('');
|
||||
if (currentFilter) filterEfSelect.value = currentFilter;
|
||||
syncSelects(filterEfSelect);
|
||||
}
|
||||
|
||||
function populateDepartmentSelect(items) {
|
||||
const currentVal = newGroupDepartmentSelect.value;
|
||||
if (!items || !items.length) {
|
||||
newGroupDepartmentSelect.innerHTML = '<option value="">Нет кафедр</option>';
|
||||
return;
|
||||
function populateEfSelect(select, forms) {
|
||||
const currentVal = select.value;
|
||||
select.innerHTML = forms.map(form => `<option value="${form.id}">${escapeHtml(form.name)}</option>`).join('');
|
||||
if (currentVal && forms.find(form => form.id == currentVal)) {
|
||||
select.value = currentVal;
|
||||
}
|
||||
syncSelects(select);
|
||||
}
|
||||
|
||||
newGroupDepartmentSelect.innerHTML = '<option value="">Выберите кафедру</option>' +
|
||||
function populateDepartmentSelects(items) {
|
||||
populateDepartmentSelect(newGroupDepartmentSelect, items);
|
||||
populateDepartmentSelect(editGroupDepartmentSelect, items);
|
||||
}
|
||||
|
||||
function populateDepartmentSelect(select, items) {
|
||||
const currentVal = select.value;
|
||||
select.innerHTML = '<option value="">Выберите кафедру</option>' +
|
||||
items.map(department => {
|
||||
const name = department.departmentName || department.name || 'Без названия';
|
||||
const code = department.departmentCode || department.code || department.id;
|
||||
return `<option value="${department.id}">${escapeHtml(name)} (${escapeHtml(String(code))})</option>`;
|
||||
}).join('');
|
||||
if (currentVal && items.find(department => department.id == currentVal)) {
|
||||
newGroupDepartmentSelect.value = currentVal;
|
||||
select.value = currentVal;
|
||||
}
|
||||
syncSelects(newGroupDepartmentSelect);
|
||||
syncSelects(select);
|
||||
}
|
||||
|
||||
function populateSpecialitySelect(items) {
|
||||
const currentVal = newGroupSpecialitySelect.value;
|
||||
if (!items || !items.length) {
|
||||
newGroupSpecialitySelect.innerHTML = '<option value="">Нет специальностей</option>';
|
||||
function populateSpecialitySelects(items) {
|
||||
populateSpecialitySelect(newGroupSpecialitySelect, items);
|
||||
populateSpecialitySelect(editGroupSpecialitySelect, items);
|
||||
populateProfileSelect(newGroupProfileSelect, newGroupSpecialitySelect.value);
|
||||
populateProfileSelect(editGroupProfileSelect, editGroupSpecialitySelect.value);
|
||||
}
|
||||
|
||||
function populateSpecialitySelect(select, items) {
|
||||
const currentVal = select.value;
|
||||
select.innerHTML = '<option value="">Выберите специальность</option>' +
|
||||
items.map(speciality => `<option value="${speciality.id}">${escapeHtml(specialityLabel(speciality.id))}</option>`).join('');
|
||||
if (currentVal && items.find(speciality => speciality.id == currentVal)) {
|
||||
select.value = currentVal;
|
||||
}
|
||||
syncSelects(select);
|
||||
}
|
||||
|
||||
function populateProfileSelect(select, specialtyId, selectedProfileId = null) {
|
||||
const profiles = profilesBySpecialty.get(String(specialtyId)) || [];
|
||||
select.innerHTML = profiles.length
|
||||
? '<option value="">Выберите профиль</option>' + profiles.map(profile =>
|
||||
`<option value="${profile.id}">${escapeHtml(profile.name)}</option>`
|
||||
).join('')
|
||||
: '<option value="">Нет профилей</option>';
|
||||
if (selectedProfileId && profiles.some(profile => String(profile.id) === String(selectedProfileId))) {
|
||||
select.value = selectedProfileId;
|
||||
}
|
||||
syncSelects(select);
|
||||
}
|
||||
|
||||
function populateYearSelect() {
|
||||
calendarYearSelect.innerHTML = '<option value="">Выберите учебный год</option>' +
|
||||
academicYears.map(year => `<option value="${year.id}">${escapeHtml(year.title)}</option>`).join('');
|
||||
if (!calendarYearSelect.value && academicYears.length) {
|
||||
calendarYearSelect.value = String(academicYears[0].id);
|
||||
}
|
||||
syncSelects(calendarYearSelect);
|
||||
}
|
||||
|
||||
function populateGroupSelect() {
|
||||
const current = calendarGroupSelect.value;
|
||||
calendarGroupSelect.innerHTML = '<option value="">Выберите группу</option>' +
|
||||
allGroups.map(group => `<option value="${group.id}">${escapeHtml(group.name)}</option>`).join('');
|
||||
if (current && allGroups.some(group => String(group.id) === String(current))) {
|
||||
calendarGroupSelect.value = current;
|
||||
} else if (allGroups.length) {
|
||||
calendarGroupSelect.value = String(allGroups[0].id);
|
||||
}
|
||||
syncSelects(calendarGroupSelect);
|
||||
}
|
||||
|
||||
function populateCalendarSelect() {
|
||||
const group = selectedCalendarGroup();
|
||||
const yearId = calendarYearSelect.value;
|
||||
const matching = group && yearId
|
||||
? calendars.filter(calendar =>
|
||||
String(calendar.academicYearId) === String(yearId)
|
||||
&& String(calendar.specialtyId) === String(group.specialtyId || group.specialityCode)
|
||||
&& String(calendar.specialtyProfileId) === String(group.specialtyProfileId)
|
||||
)
|
||||
: [];
|
||||
calendarSelect.innerHTML = matching.length
|
||||
? '<option value="">Выберите график</option>' + matching.map(calendar =>
|
||||
`<option value="${calendar.id}">${escapeHtml(calendar.title)}</option>`
|
||||
).join('')
|
||||
: '<option value="">Нет подходящих графиков</option>';
|
||||
syncSelects(calendarSelect);
|
||||
}
|
||||
|
||||
async function createGroup(event) {
|
||||
event.preventDefault();
|
||||
hideAlert('create-group-alert');
|
||||
const payload = readGroupPayload('new');
|
||||
if (!payload) return;
|
||||
|
||||
try {
|
||||
const data = await api.post('/api/groups', payload);
|
||||
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || payload.name)}" создана`, 'success');
|
||||
createGroupForm.reset();
|
||||
syncSelects(newGroupEfSelect, newGroupDepartmentSelect, newGroupSpecialitySelect, newGroupProfileSelect);
|
||||
await loadGroups();
|
||||
} catch (error) {
|
||||
showAlert('create-group-alert', error.message || 'Ошибка создания', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function updateGroup(event) {
|
||||
event.preventDefault();
|
||||
hideAlert('edit-group-alert');
|
||||
const id = document.getElementById('edit-group-id').value;
|
||||
const payload = readGroupPayload('edit');
|
||||
if (!payload) return;
|
||||
|
||||
try {
|
||||
const data = await api.put('/api/groups/' + id, payload);
|
||||
modalEditGroup.classList.remove('open');
|
||||
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || payload.name)}" обновлена`, 'success');
|
||||
await loadGroups();
|
||||
} catch (error) {
|
||||
showAlert('edit-group-alert', error.message || 'Ошибка обновления', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function readGroupPayload(prefix) {
|
||||
const isEdit = prefix === 'edit';
|
||||
const alertId = isEdit ? 'edit-group-alert' : 'create-group-alert';
|
||||
const name = document.getElementById(`${prefix}-group-name`).value.trim();
|
||||
const groupSize = document.getElementById(`${prefix}-group-size`).value;
|
||||
const educationFormId = (isEdit ? editGroupEfSelect : newGroupEfSelect).value;
|
||||
const departmentId = (isEdit ? editGroupDepartmentSelect : newGroupDepartmentSelect).value;
|
||||
const yearStartStudy = document.getElementById(`${prefix}-group-yearStartStudy`).value;
|
||||
const specialtyId = (isEdit ? editGroupSpecialitySelect : newGroupSpecialitySelect).value;
|
||||
const specialtyProfileId = (isEdit ? editGroupProfileSelect : newGroupProfileSelect).value;
|
||||
|
||||
if (!name) { showAlert(alertId, 'Введите название группы', 'error'); return null; }
|
||||
if (!groupSize) { showAlert(alertId, 'Введите размер группы', 'error'); return null; }
|
||||
if (!educationFormId) { showAlert(alertId, 'Выберите форму обучения', 'error'); return null; }
|
||||
if (!departmentId) { showAlert(alertId, 'Выберите кафедру', 'error'); return null; }
|
||||
if (!yearStartStudy) { showAlert(alertId, 'Введите год начала обучения', 'error'); return null; }
|
||||
if (!specialtyId) { showAlert(alertId, 'Выберите специальность', 'error'); return null; }
|
||||
if (!specialtyProfileId) { showAlert(alertId, 'Выберите профиль обучения', 'error'); return null; }
|
||||
|
||||
return {
|
||||
name,
|
||||
groupSize: Number(groupSize),
|
||||
educationFormId: Number(educationFormId),
|
||||
departmentId: Number(departmentId),
|
||||
yearStartStudy: Number(yearStartStudy),
|
||||
specialtyId: Number(specialtyId),
|
||||
specialtyProfileId: Number(specialtyProfileId)
|
||||
};
|
||||
}
|
||||
|
||||
async function handleGroupTableClick(event) {
|
||||
const btnDelete = event.target.closest('.btn-delete');
|
||||
const btnEdit = event.target.closest('.btn-edit-group');
|
||||
|
||||
if (btnDelete) {
|
||||
if (!confirm('Удалить группу?')) return;
|
||||
try {
|
||||
await api.delete('/api/groups/' + btnDelete.dataset.id);
|
||||
await loadGroups();
|
||||
} catch (error) {
|
||||
alert(error.message || 'Ошибка удаления');
|
||||
}
|
||||
}
|
||||
|
||||
if (btnEdit) {
|
||||
openEditGroupModal(btnEdit.dataset.id);
|
||||
}
|
||||
}
|
||||
|
||||
function openEditGroupModal(id) {
|
||||
const group = allGroups.find(item => item.id == id);
|
||||
if (!group) {
|
||||
alert('Группа не найдена');
|
||||
return;
|
||||
}
|
||||
|
||||
newGroupSpecialitySelect.innerHTML = '<option value="">Выберите специальность</option>' +
|
||||
items.map(speciality => {
|
||||
const name = speciality.specialityName || speciality.name || 'Без названия';
|
||||
const code = speciality.specialityCode || speciality.specialtyCode || speciality.specialty_code || speciality.id;
|
||||
return `<option value="${speciality.id}">${escapeHtml(code)} — ${escapeHtml(name)}</option>`;
|
||||
}).join('');
|
||||
if (currentVal && items.find(speciality => speciality.id == currentVal)) {
|
||||
newGroupSpecialitySelect.value = currentVal;
|
||||
document.getElementById('edit-group-id').value = group.id;
|
||||
document.getElementById('edit-group-name').value = group.name || '';
|
||||
document.getElementById('edit-group-size').value = group.groupSize || '';
|
||||
editGroupEfSelect.value = group.educationFormId || '';
|
||||
editGroupDepartmentSelect.value = group.departmentId || '';
|
||||
document.getElementById('edit-group-yearStartStudy').value = group.yearStartStudy || '';
|
||||
editGroupSpecialitySelect.value = group.specialtyId || group.specialityCode || '';
|
||||
populateProfileSelect(editGroupProfileSelect, editGroupSpecialitySelect.value, group.specialtyProfileId);
|
||||
syncSelects(editGroupEfSelect, editGroupDepartmentSelect, editGroupSpecialitySelect, editGroupProfileSelect);
|
||||
hideAlert('edit-group-alert');
|
||||
modalEditGroup.classList.add('open');
|
||||
}
|
||||
|
||||
async function saveAssignment(event) {
|
||||
event.preventDefault();
|
||||
hideAlert('group-calendar-alert');
|
||||
const groupId = calendarGroupSelect.value;
|
||||
const academicYearId = calendarYearSelect.value;
|
||||
const calendarId = calendarSelect.value;
|
||||
|
||||
if (!groupId || !academicYearId || !calendarId) {
|
||||
showAlert('group-calendar-alert', 'Выберите группу, учебный год и график', 'error');
|
||||
return;
|
||||
}
|
||||
syncSelects(newGroupSpecialitySelect);
|
||||
|
||||
try {
|
||||
await api.put(`/api/groups/${groupId}/calendar-assignments`, {
|
||||
academicYearId: Number(academicYearId),
|
||||
calendarId: Number(calendarId)
|
||||
});
|
||||
showAlert('group-calendar-alert', 'Календарный график назначен', 'success');
|
||||
await loadAssignments();
|
||||
} catch (error) {
|
||||
showAlert('group-calendar-alert', error.message || 'Ошибка назначения графика', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadAssignments() {
|
||||
const groupId = calendarGroupSelect.value;
|
||||
if (!groupId) {
|
||||
currentAssignments = [];
|
||||
calendarAssignmentsTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Выберите группу</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
currentAssignments = await api.get(`/api/groups/${groupId}/calendar-assignments`);
|
||||
renderAssignments();
|
||||
} catch (error) {
|
||||
calendarAssignmentsTbody.innerHTML = `<tr><td colspan="3" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
function renderAssignments() {
|
||||
if (!currentAssignments.length) {
|
||||
calendarAssignmentsTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Графики не назначены</td></tr>';
|
||||
return;
|
||||
}
|
||||
calendarAssignmentsTbody.innerHTML = currentAssignments.map(assignment => `
|
||||
<tr>
|
||||
<td>${escapeHtml(assignment.academicYearTitle)}</td>
|
||||
<td>${escapeHtml(assignment.calendarTitle)}</td>
|
||||
<td>
|
||||
<button class="btn-delete btn-delete-assignment" data-id="${assignment.id}">Удалить</button>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
async function handleAssignmentTableClick(event) {
|
||||
const deleteButton = event.target.closest('.btn-delete-assignment');
|
||||
if (!deleteButton) return;
|
||||
if (!confirm('Удалить назначение графика?')) return;
|
||||
|
||||
try {
|
||||
await api.delete(`/api/groups/${calendarGroupSelect.value}/calendar-assignments/${deleteButton.dataset.id}`);
|
||||
showAlert('group-calendar-alert', 'Назначение удалено', 'success');
|
||||
await loadAssignments();
|
||||
} catch (error) {
|
||||
showAlert('group-calendar-alert', error.message || 'Ошибка удаления назначения', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function selectedCalendarGroup() {
|
||||
return allGroups.find(group => String(group.id) === String(calendarGroupSelect.value));
|
||||
}
|
||||
|
||||
function departmentLabel(departmentId) {
|
||||
@@ -123,73 +426,6 @@ export async function initGroups() {
|
||||
return name ? `${code} — ${name}` : code;
|
||||
}
|
||||
|
||||
function renderGroups(groups) {
|
||||
if (!groups || !groups.length) {
|
||||
groupsTbody.innerHTML = '<tr><td colspan="8" class="loading-row">Нет групп</td></tr>';
|
||||
return;
|
||||
}
|
||||
groupsTbody.innerHTML = groups.map(g => `
|
||||
<tr>
|
||||
<td>${g.id}</td>
|
||||
<td>${escapeHtml(g.name)}</td>
|
||||
<td>${escapeHtml(g.groupSize)}</td>
|
||||
<td><span class="badge badge-ef">${escapeHtml(g.educationFormName)}</span></td>
|
||||
<td>${escapeHtml(departmentLabel(g.departmentId))}</td>
|
||||
<td>${g.course || '-'}</td>
|
||||
<td>${escapeHtml(specialityLabel(g.specialityCode))}</td>
|
||||
<td><button class="btn-delete" data-id="${g.id}">Удалить</button></td>
|
||||
</tr>`).join('');
|
||||
}
|
||||
|
||||
createGroupForm.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
hideAlert('create-group-alert');
|
||||
const name = document.getElementById('new-group-name').value.trim();
|
||||
const groupSize = document.getElementById('new-group-size').value;
|
||||
const educationFormId = newGroupEfSelect.value;
|
||||
const departmentId = newGroupDepartmentSelect.value;
|
||||
const yearStartStudy = document.getElementById('new-group-yearStartStudy').value;
|
||||
const specialityCode = newGroupSpecialitySelect.value;
|
||||
|
||||
if (!name) { showAlert('create-group-alert', 'Введите название группы', 'error'); return; }
|
||||
if (!groupSize) { showAlert('create-group-alert', 'Введите размер группы', 'error'); return; }
|
||||
if (!educationFormId) { showAlert('create-group-alert', 'Выберите форму обучения', 'error'); return; }
|
||||
if (!departmentId) { showAlert('create-group-alert', 'Выберите кафедру', 'error'); return; }
|
||||
if (!yearStartStudy) { showAlert('create-group-alert', 'Введите год начала обучения', 'error'); return; }
|
||||
if (!specialityCode) { showAlert('create-group-alert', 'Выберите специальность', 'error'); return; }
|
||||
|
||||
try {
|
||||
const data = await api.post('/api/groups', {
|
||||
name,
|
||||
groupSize: Number(groupSize),
|
||||
educationFormId: Number(educationFormId),
|
||||
departmentId: Number(departmentId),
|
||||
yearStartStudy: Number(yearStartStudy),
|
||||
specialityCode: Number(specialityCode)
|
||||
});
|
||||
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || name)}" создана`, 'success');
|
||||
createGroupForm.reset();
|
||||
syncSelects(newGroupEfSelect, newGroupDepartmentSelect, newGroupSpecialitySelect);
|
||||
loadGroups();
|
||||
} catch (e) {
|
||||
showAlert('create-group-alert', e.message || 'Ошибка создания', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
groupsTbody.addEventListener('click', async (e) => {
|
||||
const btn = e.target.closest('.btn-delete');
|
||||
if (!btn) return;
|
||||
if (!confirm('Удалить группу?')) return;
|
||||
try {
|
||||
await api.delete('/api/groups/' + btn.dataset.id);
|
||||
loadGroups();
|
||||
} catch (e) {
|
||||
alert(e.message || 'Ошибка удаления');
|
||||
}
|
||||
});
|
||||
|
||||
await loadInitialData();
|
||||
|
||||
function syncSelects(...selects) {
|
||||
selects.filter(Boolean).forEach(select => {
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
|
||||
Reference in New Issue
Block a user