import { api } from '../api.js'; import { escapeHtml, hideAlert, renderTableMessage, showAlert } from '../utils.js'; import { promptAction } from '../dialog.js'; import { renderPagination } from '../pagination.js'; import { debounce, readUrlState, writeUrlState } from '../url-state.js'; import { markTableReady, renderTableEmpty, renderTableError, renderTableLoading } from '../view-state.js'; import { trackUxEvent } from '../../../telemetry.js'; export async function initTeacherRequests() { const requestsTbody = document.getElementById('teacher-requests-tbody'); const refreshButton = document.getElementById('teacher-requests-refresh'); const requestsCount = document.getElementById('teacher-requests-count'); const searchInput = document.getElementById('teacher-requests-search'); const statusSelect = document.getElementById('teacher-requests-status'); const pagination = document.getElementById('teacher-requests-pagination'); let departments = []; const restored = readUrlState({ requestQuery: '', requestStatus: 'PENDING', requestPage: '0', requestSize: '25' }); const state = { query: restored.requestQuery, status: restored.requestStatus, page: Math.max(Number(restored.requestPage) || 0, 0), size: [25, 50, 100].includes(Number(restored.requestSize)) ? Number(restored.requestSize) : 25 }; searchInput.value = state.query; statusSelect.value = state.status; async function loadDepartments() { departments = await api.get('/api/departments'); } async function loadRequests() { renderTableLoading(requestsTbody, 7, 'Загрузка заявок...'); hideAlert('teacher-requests-alert'); try { const params = new URLSearchParams({ query: state.query, page: String(state.page), size: String(state.size), sort: 'createdAt', direction: 'desc' }); if (state.status) params.set('status', state.status); const page = await api.get(`/api/teacher-requests/page?${params}`); if (!(page.items || []).length && page.totalItems > 0 && state.page > 0) { state.page = Math.max(0, page.totalPages - 1); return loadRequests(); } const normalizedRequests = page.items || []; renderRequests(normalizedRequests); updateCount(Number(page.totalItems || 0)); renderPagination(pagination, page, { label: 'заявок', onPage: nextPage => { state.page = nextPage; loadRequests(); }, onSize: size => { state.size = size; state.page = 0; loadRequests(); } }); persistState(); document.dispatchEvent(new CustomEvent('teacherRequestsChanged', { detail: { count: state.status === 'PENDING' && !state.query ? Number(page.totalItems || 0) : undefined } })); trackUxEvent('реестр_заявок_загружен', { total: Number(page.totalItems || 0), page: state.page }); } catch (error) { renderTableError(requestsTbody, 7, 'Не удалось загрузить заявки преподавателей', loadRequests); updateCount(0); } } function renderRequests(requests) { if (!requests.length) { renderTableEmpty(requestsTbody, 7, state.query ? 'Заявки не найдены' : 'Заявок с выбранным статусом нет'); return; } requestsTbody.innerHTML = requests.map(request => ` ${escapeHtml(request.comment || '-')}
${request.status === 'PENDING' ? ` ` : `${escapeHtml(request.status === 'APPROVED' ? 'Одобрена' : 'Отклонена')}`}
`).join(''); markTableReady(requestsTbody); } function updateCount(count) { if (!requestsCount) return; requestsCount.textContent = requestCountLabel(count); } function requestCountLabel(count) { const mod10 = count % 10; const mod100 = count % 100; if (mod10 === 1 && mod100 !== 11) return `${count} заявка`; if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return `${count} заявки`; return `${count} заявок`; } async function approveRequest(row, requestId) { const payload = { departmentId: Number(row.querySelector('.teacher-request-department')?.value), username: row.querySelector('.teacher-request-username')?.value.trim(), fullName: row.querySelector('.teacher-request-fullname')?.value.trim(), jobTitle: row.querySelector('.teacher-request-jobtitle')?.value.trim(), password: row.querySelector('.teacher-request-password')?.value }; if (!payload.departmentId || !payload.username || !payload.fullName || !payload.password) { showAlert('teacher-requests-alert', 'Заполните кафедру, логин, ФИО и пароль', 'error'); return; } if (payload.password.length < 8) { showAlert('teacher-requests-alert', 'Пароль должен быть не короче 8 символов', 'error'); return; } try { await api.post(`/api/teacher-requests/${encodeURIComponent(requestId)}/approve`, payload); showAlert('teacher-requests-alert', 'Преподаватель создан, заявка одобрена', 'success'); await loadRequests(); } catch (error) { showAlert('teacher-requests-alert', error.message || 'Ошибка одобрения заявки', 'error'); } } async function rejectRequest(requestId) { const reviewComment = await promptAction({ title: 'Отклонить заявку?', message: 'Комментарий увидит кафедра, отправившая заявку.', confirmLabel: 'Отклонить', danger: true, input: { label: 'Причина отклонения', placeholder: 'Укажите, что нужно исправить', required: true, requiredMessage: 'Укажите причину отклонения' } }); if (reviewComment === null) return; try { await api.post(`/api/teacher-requests/${encodeURIComponent(requestId)}/reject`, { reviewComment }); showAlert('teacher-requests-alert', 'Заявка отклонена', 'success'); await loadRequests(); } catch (error) { showAlert('teacher-requests-alert', error.message || 'Ошибка отклонения заявки', 'error'); } } refreshButton?.addEventListener('click', loadRequests); searchInput.addEventListener('input', debounce(() => { state.query = searchInput.value.trim(); state.page = 0; loadRequests(); })); statusSelect.addEventListener('change', () => { state.status = statusSelect.value; state.page = 0; loadRequests(); }); requestsTbody.addEventListener('click', async (event) => { const approveButton = event.target.closest('.teacher-request-approve'); const rejectButton = event.target.closest('.teacher-request-reject'); if (!approveButton && !rejectButton) return; const row = event.target.closest('tr[data-request-id]'); const requestId = row?.dataset.requestId; if (!requestId) return; if (approveButton) { await approveRequest(row, requestId); return; } await rejectRequest(requestId); }); try { await loadDepartments(); await loadRequests(); } catch (error) { renderTableMessage(requestsTbody, 7, 'Не удалось загрузить заявки преподавателей'); } function persistState() { writeUrlState({ requestQuery: state.query, requestStatus: state.status === 'PENDING' ? '' : state.status, requestPage: state.page || '', requestSize: state.size === 25 ? '' : state.size }); } }