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
This commit is contained in:
dipatrik10
2026-09-28 23:15:04 +03:00
parent c6083b35e0
commit 97bf3cc50a
167 changed files with 31283 additions and 19033 deletions

View File

@@ -0,0 +1,101 @@
# Итоги миграции frontend на React
Дата завершения: 28.09.2026. Полный журнал процесса — в [`REACT_MIGRATION.md`](REACT_MIGRATION.md),
архитектура итогового решения — в [`FRONTEND.md`](FRONTEND.md).
---
## Что сделано
По модели strangler (этапы 1–5, все изменения не закоммичены — коммит по решению владельца):
| Этап | Содержимое | Статус |
|------|-----------|--------|
| 1. Точка входа и вход в систему | `index.html`, страница логина | Завершён |
| 2. Интерфейс студента | SPA `student/` | Завершён |
| 3. Интерфейс преподавателя | SPA `teacher/` (пожелания, заявки) | Завершён |
| 4. Страница настроек | Отдельный SPA `admin/settings/` (4 вкладки) | Завершён |
| 5. Основная админ-панель | Shell + hash-router + **15 вкладок** в 6 волнах | Завершён |
Волны этапа 5: `dashboard`, `classrooms`, `subjects` → `users`, `groups`, `university-structure` →
`teacher-requests`, `department-workspace` → `auditorium-workload`, `academic-calendar` →
`schedule-view`, `schedule-versions`, `schedule-quality`, `teacher-absences` → `schedule`
(конструктор правил + абсорбция панели разовых правок).
**Удалено 45 legacy-файлов** (~19 000 строк vanilla-кода): все `admin/js/views/*.js`
и `admin/views/*.html`, `admin/js/main.js`, переходный слой `LegacyTabView.jsx` + `legacy-views.js`,
дублирующие утилиты `async-combobox.js` и `pagination.js` (заменены общими React-компонентами
`AsyncCombobox.jsx` и `Pagination.jsx`).
**Добавлено 75 React-файлов** (~23 200 строк): оболочки четырёх SPA, 15 вкладок админки,
19 сервисов с чистой логикой, общая дизайн-система (`react/shared/ui/`), render-хелперы для тестов.
---
## Насколько стало лучше
### Скорость загрузки и переключения вкладок
| Метрика (gzip) | До (legacy) | После (React) |
|----------------|-------------|---------------|
| Переключение вкладки админки | 2 запроса: `fetch` HTML-фрагмента + lazy JS | **1 запрос**: один lazy-chunk |
| Средний размер вкладки | HTML + JS отдельно | ~5,1 КБ (128 571 байт / 25 chunk'ов) |
| Общие chunk'и (кэшируются между страницами) | — | 79 217 байт (лимит 85 000) |
| Холодная загрузка админки | — | 84 857 байт (лимит 90 000) |
- HTML-фрагменты больше **не загружаются через `fetch`** — вкладка рендерится сразу из
одного кэшируемого JS-chunk'а, исчезли двойные сетевые задержки и парсинг HTML на каждое
переключение.
- Runtime, дизайн-система и утилиты вынесены в общие chunk'и: повторные визиты и переходы
между страницами (логин → студент → админка) переиспользуют кэш браузера.
- Каждая вкладка загружается отдельно (`React.lazy` + `Suspense`) — пользователь качает
только то, что открывает.
### Качество и надёжность
- **237 тестов** проходят (`npm run check`), против ~170 на момент начала этапа 5:
контракты API, payload-валидация, DOM-рендер каждой вкладки, бюджеты bundle, CSP,
разрешение всех lazy-вкладок (регрессия на React error #306 поймана и закрыта тестом).
- Логика вынесена в **чистые сервисы** (`*-service.js`) — тестируются в Node без браузера
и DOM-заглушек.
- Единая дизайн-система вместо копипасты виджетов: кастомные селекты, `AsyncCombobox`,
`ConfirmDialog`, `Pagination`, `MaskedDateInput` — одинаковое поведение и a11y везде.
- Устранён класс ошибок ручного DOM: React-state вместо `innerHTML`/`classList`,
модалки — условный рендер вместо `hidden`-флагов, позиционирование меню — Web Animations API.
### Безопасность (не ослаблена)
- CSP-хэши: `[]` — ни одного элемента вне политики.
- JWT по-прежнему только в памяти, без inline-скриптов и inline-стилей
(проверяется тестами `security-policy.test.mjs`).
- Сообщения об ошибках не вставляют текст исключений через `innerHTML`.
### Сопровождаемость
- Один стек вместо двух: не нужно поддерживать параллельно vanilla-модули и React.
- −19 000 строк legacy-кода, +4 965 строк тестов.
- URL/REST-контракты и русский UI сохранены — для пользователей миграция незаметна.
---
## Бюджеты bundle (итог, лимиты не поднимались с волны 1)
| Entry point | Размер gzip | Холодная загрузка | Лимиты |
|-------------|-------------|-------------------|--------|
| Логин | 2 493 | 81 710 | 10 000 / 85 000 |
| Студент | 3 431 | 82 648 | 20 000 / 90 000 |
| Преподаватель | 10 145 | 89 362 | 20 000 / 90 000 |
| Настройки | 8 466 | 87 683 | 20 000 / 90 000 |
| Админ-панель | 5 640 | 84 857 | 20 000 / 90 000 |
| Lazy-вкладки админки | 128 571 (25 chunk'ов, по одной за раз) | — | 150 000 |
---
## Проверка
```bash
cd frontend
npm ci
npm run build
npm run check # 237/237 тестов, бюджеты, CSP
```