доработал отображение учебного графика

This commit is contained in:
Zuev
2026-05-01 14:02:01 +03:00
parent 89c822a073
commit bf02efb8b8
28 changed files with 1644 additions and 1145 deletions

View File

@@ -0,0 +1,192 @@
import { api } from '../api.js';
import { escapeHtml, showAlert, hideAlert } from '../utils.js';
export async function initProfiles() {
const createProfileForm = document.getElementById('create-profile-form');
const profileSpecialtySelect = document.getElementById('profile-specialty');
const profileNameInput = document.getElementById('profile-name');
const profileDescriptionInput = document.getElementById('profile-description');
const profilesTbody = document.getElementById('profiles-tbody');
const profilesContext = document.getElementById('profiles-context');
const refreshButton = document.getElementById('profiles-refresh');
const editProfileForm = document.getElementById('edit-profile-form');
const editProfileModal = document.getElementById('modal-edit-profile');
const editProfileClose = document.getElementById('modal-edit-profile-close');
let specialties = [];
let profiles = [];
await loadSpecialties();
profileSpecialtySelect.addEventListener('change', loadProfiles);
refreshButton.addEventListener('click', loadProfiles);
createProfileForm.addEventListener('submit', async (event) => {
event.preventDefault();
hideAlert('create-profile-alert');
const specialtyId = profileSpecialtySelect.value;
const name = profileNameInput.value.trim();
const description = profileDescriptionInput.value.trim();
if (!specialtyId || !name) {
showAlert('create-profile-alert', 'Выберите специальность и заполните название профиля', 'error');
return;
}
try {
await api.post(`/api/specialties/${specialtyId}/profiles`, { name, description });
showAlert('create-profile-alert', `Профиль "${name}" создан`, 'success');
createProfileForm.reset();
profileSpecialtySelect.value = specialtyId;
syncSelect(profileSpecialtySelect);
await loadProfiles();
} catch (error) {
showAlert('create-profile-alert', error.message || 'Ошибка создания профиля', 'error');
}
});
profilesTbody.addEventListener('click', async (event) => {
const editButton = event.target.closest('.btn-edit-profile');
const deleteButton = event.target.closest('.btn-delete-profile');
if (editButton) {
const profile = profiles.find(item => String(item.id) === String(editButton.dataset.id));
if (!profile) return;
document.getElementById('edit-profile-id').value = profile.id;
document.getElementById('edit-profile-name').value = profile.name || '';
document.getElementById('edit-profile-description').value = profile.description || '';
hideAlert('edit-profile-alert');
editProfileModal.classList.add('open');
return;
}
if (deleteButton) {
if (!confirm('Удалить профиль обучения?')) return;
try {
await api.delete(`/api/specialties/${profileSpecialtySelect.value}/profiles/${deleteButton.dataset.id}`);
showAlert('create-profile-alert', 'Профиль удалён', 'success');
await loadProfiles();
} catch (error) {
showAlert('create-profile-alert', error.message || 'Ошибка удаления профиля', 'error');
}
}
});
editProfileForm.addEventListener('submit', async (event) => {
event.preventDefault();
hideAlert('edit-profile-alert');
const specialtyId = profileSpecialtySelect.value;
const id = document.getElementById('edit-profile-id').value;
const name = document.getElementById('edit-profile-name').value.trim();
const description = document.getElementById('edit-profile-description').value.trim();
if (!specialtyId || !id || !name) {
showAlert('edit-profile-alert', 'Заполните название профиля', 'error');
return;
}
try {
await api.put(`/api/specialties/${specialtyId}/profiles/${id}`, { id: Number(id), name, description });
editProfileModal.classList.remove('open');
showAlert('create-profile-alert', `Профиль "${name}" обновлён`, 'success');
await loadProfiles();
} catch (error) {
showAlert('edit-profile-alert', error.message || 'Ошибка обновления профиля', 'error');
}
});
editProfileClose.addEventListener('click', () => editProfileModal.classList.remove('open'));
editProfileModal.addEventListener('click', (event) => {
if (event.target === editProfileModal) {
editProfileModal.classList.remove('open');
}
});
async function loadSpecialties() {
profilesTbody.innerHTML = '<tr><td colspan="4" class="loading-row">Загрузка...</td></tr>';
try {
specialties = await api.get('/api/specialties');
renderSpecialtySelect();
await loadProfiles();
} catch (error) {
profileSpecialtySelect.innerHTML = '<option value="">Не удалось загрузить специальности</option>';
profilesTbody.innerHTML = `<tr><td colspan="4" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
syncSelect(profileSpecialtySelect);
}
}
function renderSpecialtySelect() {
if (!specialties.length) {
profileSpecialtySelect.innerHTML = '<option value="">Сначала создайте специальность</option>';
syncSelect(profileSpecialtySelect);
return;
}
const current = profileSpecialtySelect.value;
profileSpecialtySelect.innerHTML = '<option value="">Выберите специальность</option>' +
specialties.map(specialty => {
const code = specialty.specialityCode || specialty.specialtyCode || specialty.specialty_code || specialty.id;
const name = specialty.specialityName || specialty.name || 'Без названия';
return `<option value="${specialty.id}">${escapeHtml(code)}${escapeHtml(name)}</option>`;
}).join('');
if (current && specialties.some(specialty => String(specialty.id) === String(current))) {
profileSpecialtySelect.value = current;
} else {
profileSpecialtySelect.value = String(specialties[0].id);
}
syncSelect(profileSpecialtySelect);
}
async function loadProfiles() {
const specialtyId = profileSpecialtySelect.value;
const specialty = specialties.find(item => String(item.id) === String(specialtyId));
profilesContext.textContent = specialty ? specialtyLabel(specialty) : '';
if (!specialtyId) {
profiles = [];
profilesTbody.innerHTML = '<tr><td colspan="4" class="loading-row">Выберите специальность</td></tr>';
return;
}
profilesTbody.innerHTML = '<tr><td colspan="4" class="loading-row">Загрузка...</td></tr>';
try {
profiles = await api.get(`/api/specialties/${specialtyId}/profiles`);
renderProfiles();
} catch (error) {
profilesTbody.innerHTML = `<tr><td colspan="4" class="loading-row">Ошибка загрузки: ${escapeHtml(error.message)}</td></tr>`;
}
}
function renderProfiles() {
if (!profiles.length) {
profilesTbody.innerHTML = '<tr><td colspan="4" class="loading-row">Профили не созданы</td></tr>';
return;
}
profilesTbody.innerHTML = profiles.map(profile => `
<tr>
<td>${profile.id}</td>
<td>${escapeHtml(profile.name)}</td>
<td>${escapeHtml(profile.description || '-')}</td>
<td>
<button class="btn-edit-classroom btn-edit-profile" data-id="${profile.id}">Изменить</button>
<button class="btn-delete btn-delete-profile" data-id="${profile.id}">Удалить</button>
</td>
</tr>
`).join('');
}
function specialtyLabel(specialty) {
const code = specialty.specialityCode || specialty.specialtyCode || specialty.specialty_code || specialty.id;
const name = specialty.specialityName || specialty.name || 'Без названия';
return `${code} · ${name}`;
}
function syncSelect(select) {
select.dispatchEvent(new Event('change', { bubbles: true }));
}
}