Files
magistr/frontend/react/admin/teacher-requests-service.js
dipatrik10 97bf3cc50a feat: завершить миграцию фронтенда на React (этапы 1-5)
Админка, настройки, логин, кабинеты преподавателя и студента
полностью переведены на React 19 (модель strangler, docs/REACT_MIGRATION.md).

- 15 вкладок админки: lazy-загрузка, общий error boundary, сервисный слой
- Удалены legacy-views и переходный слой LegacyTabView (45 файлов)
- Сборка esbuild (frontend/scripts/build-react.mjs) с бюджетами gzip на чанки
- CSP-хэши, кэширование: chunk'и immutable, CSS/HTML no-cache (react-cache.conf)
- Фиксы: lazy-импорт именованных экспортов (краш после авторизации),
  растяжение #admin-root/#settings-root на всю ширину,
  возврат subjects/lessonTypes из справочников (краш просмотра расписаний),
  отказ от заведомо широкого поиска расписания без выбранной цели,
  центрирование карточки логина
- Тесты: 237/237 (npm run check), регрессионные на lazy-вкладки и ширину
- Документация: REACT_MIGRATION.md, REACT_MIGRATION_RESULT.md, FRONTEND.md
2026-09-28 23:15:04 +03:00

119 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { apiClient } from '../shared/api-client.js';
export const REQUEST_TABLE_COLUMNS = 7;
export const REQUEST_PAGE_SIZES = [25, 50, 100];
export const REQUEST_STATUS_OPTIONS = Object.freeze([
{ value: 'PENDING', label: 'Ожидают рассмотрения' },
{ value: 'APPROVED', label: 'Одобрены' },
{ value: 'REJECTED', label: 'Отклонены' },
{ value: '', label: 'Все статусы' }
]);
export function normalizeTeacherRequestsState(restored = {}) {
const size = Number(restored.requestSize);
return {
query: restored.requestQuery || '',
status: restored.requestStatus || 'PENDING',
page: Math.max(Number(restored.requestPage) || 0, 0),
size: REQUEST_PAGE_SIZES.includes(size) ? size : 25
};
}
export function initialTeacherRequestsState(restored = {}) {
return normalizeTeacherRequestsState(restored);
}
export function teacherRequestsQueryParams(state) {
const params = new URLSearchParams({
query: state.query || '',
page: String(state.page || 0),
size: String(state.size || 25),
sort: 'createdAt',
direction: 'desc'
});
if (state.status) params.set('status', state.status);
return `/api/teacher-requests/page?${params}`;
}
export function teacherRequestsUrlState(state) {
return {
requestQuery: state.query || '',
requestStatus: state.status === 'PENDING' ? '' : state.status,
requestPage: state.page || '',
requestSize: state.size === 25 ? '' : state.size
};
}
// --- Справочники ---
export function fetchDepartments(client = apiClient) {
return client.get('/api/departments', { errorMessage: 'Не удалось загрузить список кафедр' });
}
// Кафедра в строке заявки подписывается только названием — как в legacy-реестре.
export function requestDepartmentLabel(department) {
return department.departmentName || department.name || department.id;
}
// --- Реестр заявок ---
export function fetchTeacherRequestsPage(state, client = apiClient) {
return client.get(teacherRequestsQueryParams(state), {
errorMessage: 'Не удалось загрузить заявки преподавателей'
});
}
export function teacherRequestsEmptyMessage(state) {
return state.query ? 'Заявки не найдены' : 'Заявок с выбранным статусом нет';
}
export function reviewedStatusLabel(status) {
return status === 'APPROVED' ? 'Одобрена' : 'Отклонена';
}
// Счётчик в sidebar обновляется событием teacherRequestsChanged: он актуален
// только когда реестр показывает нефильтрованный список ожидающих заявок.
export function pendingCountDetail(state, totalItems) {
return {
count: state.status === 'PENDING' && !state.query
? Number(totalItems || 0)
: undefined
};
}
// --- Одобрение и отклонение ---
export function approvePayloadFromFields(fields) {
const payload = {
departmentId: Number(fields.departmentId),
username: (fields.username || '').trim(),
fullName: (fields.fullName || '').trim(),
jobTitle: (fields.jobTitle || '').trim(),
password: fields.password
};
if (!payload.departmentId || !payload.username || !payload.fullName || !payload.password) {
return { error: 'Заполните кафедру, логин, ФИО и пароль' };
}
if (payload.password.length < 8) {
return { error: 'Пароль должен быть не короче 8 символов' };
}
return { payload };
}
export function approveRequest(id, payload, client = apiClient) {
return client.post(
`/api/teacher-requests/${encodeURIComponent(id)}/approve`,
payload,
{ errorMessage: 'Ошибка одобрения заявки' }
);
}
export function rejectRequest(id, reviewComment, client = apiClient) {
return client.post(
`/api/teacher-requests/${encodeURIComponent(id)}/reject`,
{ reviewComment },
{ errorMessage: 'Ошибка отклонения заявки' }
);
}