обновил дизайн, добавил дашборд

This commit is contained in:
Zuev
2026-05-31 16:18:27 +03:00
parent e2a3553a09
commit 7926038bdc
36 changed files with 2364 additions and 1184 deletions

View File

@@ -1,6 +1,14 @@
import { api } from '../api.js';
import { escapeHtml, showAlert, hideAlert } from '../utils.js';
import { fetchEducationForms } from './edu-forms.js';
async function fetchEducationForms() {
try {
return await api.get('/api/education-forms');
} catch (e) {
console.error("Failed to fetch education forms", e);
return [];
}
}
export async function initGroups() {
const groupsTbody = document.getElementById('groups-tbody');
@@ -10,6 +18,8 @@ export async function initGroups() {
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');
@@ -17,19 +27,30 @@ export async function initGroups() {
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');
const subgroupForm = document.getElementById('subgroup-form');
const subgroupIdInput = document.getElementById('subgroup-id');
const subgroupGroupSelect = document.getElementById('subgroup-group');
const subgroupNameInput = document.getElementById('subgroup-name');
const subgroupCapacityInput = document.getElementById('subgroup-capacity');
const subgroupSubmitButton = document.getElementById('subgroup-submit');
const subgroupResetButton = document.getElementById('subgroup-reset');
const subgroupsTbody = document.getElementById('subgroups-tbody');
// Модалка подгрупп
const modalManageSubgroups = document.getElementById('modal-manage-subgroups');
const modalManageSubgroupsClose = document.getElementById('modal-manage-subgroups-close');
const subgroupsGroupContext = document.getElementById('subgroups-group-context');
const subgroupFormTitle = document.getElementById('subgroup-form-title');
const manageSubgroupForm = document.getElementById('manage-subgroup-form');
const manageSubgroupIdInput = document.getElementById('manage-subgroup-id');
const manageSubgroupGroupIdInput = document.getElementById('manage-subgroup-group-id');
const manageSubgroupNameInput = document.getElementById('manage-subgroup-name');
const manageSubgroupCapacityInput = document.getElementById('manage-subgroup-capacity');
const btnSaveSubgroup = document.getElementById('btn-save-subgroup');
const btnCancelSubgroupEdit = document.getElementById('btn-cancel-subgroup-edit');
const manageSubgroupsTbody = document.getElementById('manage-subgroups-tbody');
// Модалка календарей
const modalManageCalendar = document.getElementById('modal-manage-calendar');
const modalManageCalendarClose = document.getElementById('modal-manage-calendar-close');
const calendarGroupContext = document.getElementById('calendar-group-context');
const manageCalendarForm = document.getElementById('manage-calendar-form');
const manageCalendarGroupIdInput = document.getElementById('manage-calendar-group-id');
const manageCalendarYearSelect = document.getElementById('manage-calendar-year');
const manageCalendarSelect = document.getElementById('manage-calendar-id');
const manageCalendarTbody = document.getElementById('manage-calendar-tbody');
let allGroups = [];
let subgroups = [];
@@ -48,25 +69,35 @@ export async function initGroups() {
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);
subgroupForm.addEventListener('submit', saveSubgroup);
subgroupGroupSelect.addEventListener('change', renderSubgroups);
subgroupResetButton.addEventListener('click', resetSubgroupForm);
// Модалка подгрупп
manageSubgroupForm.addEventListener('submit', saveSubgroup);
btnCancelSubgroupEdit.addEventListener('click', resetSubgroupForm);
manageSubgroupsTbody.addEventListener('click', handleSubgroupTableClick);
// Модалка календарей
manageCalendarForm.addEventListener('submit', saveAssignment);
manageCalendarYearSelect.addEventListener('change', populateCalendarSelect);
groupsTbody.addEventListener('click', handleGroupTableClick);
subgroupsTbody.addEventListener('click', handleSubgroupTableClick);
calendarAssignmentsTbody.addEventListener('click', handleAssignmentTableClick);
// Закрытие модалок
modalEditGroupClose.addEventListener('click', () => modalEditGroup.classList.remove('open'));
modalEditGroup.addEventListener('click', (event) => {
if (event.target === modalEditGroup) {
modalEditGroup.classList.remove('open');
}
if (event.target === modalEditGroup) modalEditGroup.classList.remove('open');
});
modalManageSubgroupsClose.addEventListener('click', () => modalManageSubgroups.classList.remove('open'));
modalManageSubgroups.addEventListener('click', (event) => {
if (event.target === modalManageSubgroups) modalManageSubgroups.classList.remove('open');
});
modalManageCalendarClose.addEventListener('click', () => modalManageCalendar.classList.remove('open'));
modalManageCalendar.addEventListener('click', (event) => {
if (event.target === modalManageCalendar) modalManageCalendar.classList.remove('open');
});
}
@@ -84,12 +115,10 @@ export async function initGroups() {
populateDepartmentSelects(departments);
populateSpecialitySelects(specialties);
populateYearSelect();
populateCalendarSelect();
await loadGroups();
await loadSubgroups();
} catch (error) {
groupsTbody.innerHTML = `<tr><td colspan="10" class="loading-row">Ошибка загрузки данных: ${escapeHtml(error.message)}</td></tr>`;
subgroupsTbody.innerHTML = `<tr><td colspan="4" class="loading-row">Ошибка загрузки данных: ${escapeHtml(error.message)}</td></tr>`;
}
}
@@ -105,9 +134,6 @@ export async function initGroups() {
try {
allGroups = await api.get('/api/groups?includeArchived=true');
applyGroupFilter();
populateGroupSelect();
populateSubgroupGroupSelect();
await loadAssignments();
} catch (error) {
groupsTbody.innerHTML = `<tr><td colspan="10" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
}
@@ -116,9 +142,8 @@ export async function initGroups() {
async function loadSubgroups() {
try {
subgroups = await api.get('/api/subgroups');
renderSubgroups();
} catch (error) {
subgroupsTbody.innerHTML = `<tr><td colspan="4" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
console.error("Failed to load subgroups", error);
}
}
@@ -138,7 +163,7 @@ export async function initGroups() {
groupsTbody.innerHTML = groups.map(group => `
<tr>
<td>${group.id}</td>
<td>${escapeHtml(group.name)}</td>
<td><strong>${escapeHtml(group.name)}</strong></td>
<td>${escapeHtml(String(group.groupSize))}</td>
<td><span class="badge badge-ef">${escapeHtml(group.educationFormName)}</span></td>
<td>${escapeHtml(departmentLabel(group.departmentId))}</td>
@@ -146,11 +171,13 @@ export async function initGroups() {
<td>${escapeHtml(specialityLabel(group.specialtyId || group.specialityCode))}</td>
<td>${escapeHtml(group.specialtyProfileName || '-')}</td>
<td><span class="badge ${statusBadgeClass(group)}">${escapeHtml(statusLabel(group))}</span></td>
<td style="text-align: right;">
<td style="text-align: right; white-space: nowrap;">
<button class="btn-edit-classroom btn-edit-group" data-id="${group.id}">Изменить</button>
<button class="btn-primary btn-manage-subgroups" data-id="${group.id}" style="padding: 0.25rem 0.5rem; font-size: 0.85rem;">Подгруппы</button>
<button class="btn-primary btn-manage-calendar" data-id="${group.id}" style="padding: 0.25rem 0.5rem; font-size: 0.85rem;">Календарь</button>
${group.status === 'ARCHIVED'
? `<button class="btn-restore-group" data-id="${group.id}" style="margin-left: 0.5rem;">Восстановить</button>`
: `<button class="btn-delete" data-id="${group.id}" style="margin-left: 0.5rem;">Архивировать</button>`}
: `<button class="btn-delete" data-id="${group.id}" style="margin-left: 0.5rem;">Архив</button>`}
</td>
</tr>
`).join('');
@@ -225,43 +252,18 @@ export async function initGroups() {
}
function populateYearSelect() {
calendarYearSelect.innerHTML = '<option value="">Выберите учебный год</option>' +
manageCalendarYearSelect.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);
if (academicYears.length) {
manageCalendarYearSelect.value = String(academicYears[0].id);
}
syncSelects(calendarYearSelect);
}
function populateGroupSelect() {
const current = calendarGroupSelect.value;
const availableGroups = allGroups.filter(group => group.active);
calendarGroupSelect.innerHTML = '<option value="">Выберите группу</option>' +
availableGroups.map(group => `<option value="${group.id}">${escapeHtml(group.name)}</option>`).join('');
if (current && availableGroups.some(group => String(group.id) === String(current))) {
calendarGroupSelect.value = current;
} else if (availableGroups.length) {
calendarGroupSelect.value = String(availableGroups[0].id);
}
syncSelects(calendarGroupSelect);
}
function populateSubgroupGroupSelect() {
const current = subgroupGroupSelect.value;
const availableGroups = allGroups.filter(group => group.active);
subgroupGroupSelect.innerHTML = '<option value="">Выберите группу</option>' +
availableGroups.map(group => `<option value="${group.id}">${escapeHtml(group.name)}</option>`).join('');
if (current && availableGroups.some(group => String(group.id) === String(current))) {
subgroupGroupSelect.value = current;
} else if (availableGroups.length) {
subgroupGroupSelect.value = String(availableGroups[0].id);
}
syncSelects(subgroupGroupSelect);
syncSelects(manageCalendarYearSelect);
}
function populateCalendarSelect() {
const group = selectedCalendarGroup();
const yearId = calendarYearSelect.value;
const groupId = manageCalendarGroupIdInput.value;
const group = allGroups.find(g => g.id == groupId);
const yearId = manageCalendarYearSelect.value;
const matching = group && yearId
? calendars.filter(calendar =>
String(calendar.academicYearId) === String(yearId)
@@ -270,12 +272,12 @@ export async function initGroups() {
&& String(calendar.studyFormId) === String(group.educationFormId)
)
: [];
calendarSelect.innerHTML = matching.length
manageCalendarSelect.innerHTML = matching.length
? '<option value="">Выберите график</option>' + matching.map(calendar =>
`<option value="${calendar.id}">${escapeHtml(calendar.title)}</option>`
).join('')
: '<option value="">Нет подходящих графиков</option>';
syncSelects(calendarSelect);
syncSelects(manageCalendarSelect);
}
async function createGroup(event) {
@@ -290,7 +292,6 @@ export async function initGroups() {
createGroupForm.reset();
syncSelects(newGroupEfSelect, newGroupDepartmentSelect, newGroupSpecialitySelect, newGroupProfileSelect);
await loadGroups();
await loadSubgroups();
} catch (error) {
showAlert('create-group-alert', error.message || 'Ошибка создания', 'error');
}
@@ -308,7 +309,6 @@ export async function initGroups() {
modalEditGroup.classList.remove('open');
showAlert('create-group-alert', `Группа "${escapeHtml(data.name || payload.name)}" обновлена`, 'success');
await loadGroups();
await loadSubgroups();
} catch (error) {
showAlert('edit-group-alert', error.message || 'Ошибка обновления', 'error');
}
@@ -348,13 +348,14 @@ export async function initGroups() {
const btnDelete = event.target.closest('.btn-delete');
const btnEdit = event.target.closest('.btn-edit-group');
const btnRestore = event.target.closest('.btn-restore-group');
const btnSubgroups = event.target.closest('.btn-manage-subgroups');
const btnCalendar = event.target.closest('.btn-manage-calendar');
if (btnDelete) {
if (!confirm('Архивировать группу? История расписания сохранится.')) return;
try {
await api.delete('/api/groups/' + btnDelete.dataset.id);
await loadGroups();
await loadSubgroups();
} catch (error) {
alert(error.message || 'Ошибка архивирования');
}
@@ -364,7 +365,6 @@ export async function initGroups() {
try {
await api.post('/api/groups/' + btnRestore.dataset.id + '/restore', {});
await loadGroups();
await loadSubgroups();
} catch (error) {
alert(error.message || 'Ошибка восстановления');
}
@@ -373,6 +373,18 @@ export async function initGroups() {
if (btnEdit) {
openEditGroupModal(btnEdit.dataset.id);
}
if (btnSubgroups) {
const groupId = btnSubgroups.dataset.id;
const group = allGroups.find(g => g.id == groupId);
if (group) openSubgroupsModal(group);
}
if (btnCalendar) {
const groupId = btnCalendar.dataset.id;
const group = allGroups.find(g => g.id == groupId);
if (group) openCalendarModal(group);
}
}
function openEditGroupModal(id) {
@@ -395,21 +407,59 @@ export async function initGroups() {
modalEditGroup.classList.add('open');
}
// --- Управление подгруппами ---
function openSubgroupsModal(group) {
subgroupsGroupContext.textContent = `Группа: ${group.name} (${group.groupSize} чел.)`;
manageSubgroupGroupIdInput.value = group.id;
resetSubgroupForm();
modalManageSubgroups.classList.add('open');
renderSubgroupsList(group.id);
}
function renderSubgroupsList(groupId) {
const list = subgroups.filter(sub => String(sub.groupId) === String(groupId));
if (!list.length) {
manageSubgroupsTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Подгруппы не созданы</td></tr>';
return;
}
manageSubgroupsTbody.innerHTML = list.map(sub => `
<tr>
<td>${escapeHtml(sub.name)}</td>
<td>${escapeHtml(String(sub.studentCapacity ?? '-'))} чел.</td>
<td>
<button class="btn-edit-classroom btn-edit-subgroup" data-id="${sub.id}">Изменить</button>
<button class="btn-delete btn-delete-subgroup" data-id="${sub.id}">Удалить</button>
</td>
</tr>
`).join('');
}
function resetSubgroupForm() {
manageSubgroupIdInput.value = '';
manageSubgroupNameInput.value = '';
manageSubgroupCapacityInput.value = '';
subgroupFormTitle.textContent = 'Создание подгруппы';
btnSaveSubgroup.textContent = 'Создать';
btnCancelSubgroupEdit.style.display = 'none';
hideAlert('manage-subgroup-alert');
}
async function saveSubgroup(event) {
event.preventDefault();
hideAlert('subgroup-alert');
const groupId = subgroupGroupSelect.value;
const name = subgroupNameInput.value.trim();
const capacity = subgroupCapacityInput.value;
const subgroupId = subgroupIdInput.value;
hideAlert('manage-subgroup-alert');
const groupId = manageSubgroupGroupIdInput.value;
const subgroupId = manageSubgroupIdInput.value;
const name = manageSubgroupNameInput.value.trim();
const capacity = manageSubgroupCapacityInput.value;
if (!groupId) { showAlert('subgroup-alert', 'Выберите группу', 'error'); return; }
if (!name) { showAlert('subgroup-alert', 'Введите название подгруппы', 'error'); return; }
if (capacity && Number(capacity) < 0) { showAlert('subgroup-alert', 'Численность не может быть отрицательной', 'error'); return; }
if (!name || !capacity) {
showAlert('manage-subgroup-alert', 'Заполните все поля', 'error');
return;
}
const payload = {
name,
studentCapacity: capacity ? Number(capacity) : null
studentCapacity: Number(capacity)
};
try {
@@ -417,124 +467,74 @@ export async function initGroups() {
const saved = subgroupId
? await api.put(endpoint, payload)
: await api.post(endpoint, payload);
showAlert('subgroup-alert', `Подгруппа "${escapeHtml(saved.name)}" сохранена`, 'success');
showAlert('manage-subgroup-alert', `Подгруппа "${escapeHtml(saved.name)}" сохранена`, 'success');
resetSubgroupForm();
await loadSubgroups();
renderSubgroupsList(groupId);
} catch (error) {
showAlert('subgroup-alert', error.message || 'Ошибка сохранения подгруппы', 'error');
showAlert('manage-subgroup-alert', error.message || 'Ошибка сохранения подгруппы', 'error');
}
}
function renderSubgroups() {
const groupId = subgroupGroupSelect.value;
const visibleSubgroups = groupId
? subgroups.filter(subgroup => String(subgroup.groupId) === String(groupId))
: subgroups;
if (!visibleSubgroups.length) {
subgroupsTbody.innerHTML = '<tr><td colspan="4" class="loading-row">Подгруппы не созданы</td></tr>';
return;
}
subgroupsTbody.innerHTML = visibleSubgroups.map(subgroup => `
<tr>
<td>${escapeHtml(subgroup.groupName || groupName(subgroup.groupId))}</td>
<td>${escapeHtml(subgroup.name)}</td>
<td>${escapeHtml(String(subgroup.studentCapacity ?? '-'))}</td>
<td>
<button class="btn-edit-classroom btn-edit-subgroup" data-id="${subgroup.id}">Изменить</button>
<button class="btn-delete btn-delete-subgroup" data-id="${subgroup.id}">Удалить</button>
</td>
</tr>
`).join('');
}
async function handleSubgroupTableClick(event) {
const editButton = event.target.closest('.btn-edit-subgroup');
const deleteButton = event.target.closest('.btn-delete-subgroup');
const groupId = manageSubgroupGroupIdInput.value;
if (editButton) {
const subgroup = subgroups.find(item => String(item.id) === String(editButton.dataset.id));
const subgroup = subgroups.find(item => item.id == editButton.dataset.id);
if (!subgroup) return;
subgroupIdInput.value = subgroup.id;
subgroupGroupSelect.value = subgroup.groupId;
subgroupGroupSelect.disabled = true;
subgroupNameInput.value = subgroup.name || '';
subgroupCapacityInput.value = subgroup.studentCapacity ?? '';
subgroupSubmitButton.textContent = 'Сохранить подгруппу';
syncSelects(subgroupGroupSelect);
hideAlert('subgroup-alert');
manageSubgroupIdInput.value = subgroup.id;
manageSubgroupNameInput.value = subgroup.name || '';
manageSubgroupCapacityInput.value = subgroup.studentCapacity ?? '';
subgroupFormTitle.textContent = 'Редактирование подгруппы';
btnSaveSubgroup.textContent = 'Сохранить';
btnCancelSubgroupEdit.style.display = 'inline-block';
hideAlert('manage-subgroup-alert');
return;
}
if (deleteButton) {
const subgroup = subgroups.find(item => String(item.id) === String(deleteButton.dataset.id));
if (!subgroup || !confirm('Удалить подгруппу?')) return;
if (!confirm('Удалить подгруппу?')) return;
try {
await api.delete(`/api/groups/${subgroup.groupId}/subgroups/${subgroup.id}`);
showAlert('subgroup-alert', 'Подгруппа удалена', 'success');
await api.delete(`/api/groups/${groupId}/subgroups/${deleteButton.dataset.id}`);
showAlert('manage-subgroup-alert', 'Подгруппа удалена', 'success');
resetSubgroupForm();
await loadSubgroups();
renderSubgroupsList(groupId);
} catch (error) {
showAlert('subgroup-alert', error.message || 'Ошибка удаления подгруппы', 'error');
showAlert('manage-subgroup-alert', error.message || 'Ошибка удаления подгруппы', 'error');
}
}
}
function resetSubgroupForm() {
subgroupIdInput.value = '';
subgroupNameInput.value = '';
subgroupCapacityInput.value = '';
subgroupGroupSelect.disabled = false;
subgroupSubmitButton.textContent = 'Создать подгруппу';
syncSelects(subgroupGroupSelect);
hideAlert('subgroup-alert');
// --- Управление календарными графиками ---
async function openCalendarModal(group) {
calendarGroupContext.textContent = `Группа: ${group.name} (${specialityLabel(group.specialtyId || group.specialityCode)})`;
manageCalendarGroupIdInput.value = group.id;
hideAlert('manage-calendar-alert');
populateCalendarSelect();
modalManageCalendar.classList.add('open');
await loadAssignmentsList(group.id);
}
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;
}
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;
}
async function loadAssignmentsList(groupId) {
manageCalendarTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Загрузка...</td></tr>';
try {
currentAssignments = await api.get(`/api/groups/${groupId}/calendar-assignments`);
renderAssignments();
renderAssignmentsList();
} catch (error) {
calendarAssignmentsTbody.innerHTML = `<tr><td colspan="3" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
manageCalendarTbody.innerHTML = `<tr><td colspan="3" class="loading-row">Ошибка: ${escapeHtml(error.message)}</td></tr>`;
}
}
function renderAssignments() {
function renderAssignmentsList() {
if (!currentAssignments.length) {
calendarAssignmentsTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Графики не назначены</td></tr>';
manageCalendarTbody.innerHTML = '<tr><td colspan="3" class="loading-row">Календарные графики не назначены</td></tr>';
return;
}
calendarAssignmentsTbody.innerHTML = currentAssignments.map(assignment => `
manageCalendarTbody.innerHTML = currentAssignments.map(assignment => `
<tr>
<td>${escapeHtml(assignment.academicYearTitle)}</td>
<td>${escapeHtml(assignment.calendarTitle)}</td>
@@ -545,35 +545,53 @@ export async function initGroups() {
`).join('');
}
async function handleAssignmentTableClick(event) {
const deleteButton = event.target.closest('.btn-delete-assignment');
if (!deleteButton) return;
if (!confirm('Удалить назначение графика?')) return;
async function saveAssignment(event) {
event.preventDefault();
hideAlert('manage-calendar-alert');
const groupId = manageCalendarGroupIdInput.value;
const academicYearId = manageCalendarYearSelect.value;
const calendarId = manageCalendarSelect.value;
if (!academicYearId || !calendarId) {
showAlert('manage-calendar-alert', 'Выберите учебный год и график', 'error');
return;
}
try {
await api.delete(`/api/groups/${calendarGroupSelect.value}/calendar-assignments/${deleteButton.dataset.id}`);
showAlert('group-calendar-alert', 'Назначение удалено', 'success');
await loadAssignments();
await api.put(`/api/groups/${groupId}/calendar-assignments`, {
academicYearId: Number(academicYearId),
calendarId: Number(calendarId)
});
showAlert('manage-calendar-alert', 'Календарный график назначен', 'success');
await loadAssignmentsList(groupId);
} catch (error) {
showAlert('group-calendar-alert', error.message || 'Ошибка удаления назначения', 'error');
showAlert('manage-calendar-alert', error.message || 'Ошибка назначения графика', 'error');
}
}
function selectedCalendarGroup() {
return allGroups.find(group => String(group.id) === String(calendarGroupSelect.value));
}
manageCalendarTbody.addEventListener('click', async (event) => {
const deleteButton = event.target.closest('.btn-delete-assignment');
if (!deleteButton) return;
if (!confirm('Удалить назначение графика?')) return;
const groupId = manageCalendarGroupIdInput.value;
try {
await api.delete(`/api/groups/${groupId}/calendar-assignments/${deleteButton.dataset.id}`);
showAlert('manage-calendar-alert', 'Назначение удалено', 'success');
await loadAssignmentsList(groupId);
} catch (error) {
showAlert('manage-calendar-alert', error.message || 'Ошибка удаления назначения', 'error');
}
});
// --- Вспомогательные функции ---
function departmentLabel(departmentId) {
const department = departments.find(item => item.id == departmentId);
if (!department) return departmentId || '-';
return department.departmentName || department.name || departmentId;
}
function groupName(groupId) {
const group = allGroups.find(item => String(item.id) === String(groupId));
return group ? group.name : groupId || '-';
}
function specialityLabel(specialityId) {
const speciality = specialties.find(item => item.id == specialityId);
if (!speciality) return specialityId || '-';