Files
Maxim Dolgolyov be4d43105e LearnSpace: full-stack educational whiteboard platform
Node.js/Express backend + vanilla JS frontend.
Features: real-time collaborative whiteboard (SSE), multi-page support,
LaTeX formulas, shapes/connectors, coordinate systems, number lines,
compass, zoom/pan, Catmull-Rom pencil smoothing, ruler/protractor with
rotation & resize controls, minimap navigation overlay, auto-measurements,
multi-page thumbnails sidebar, PNG export, page templates.
Student/teacher workflows: classes, assignments, library, dashboard.
Mobile responsive. SQLite (better-sqlite3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 10:10:37 +03:00

494 lines
35 KiB
Markdown
Raw Permalink 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.
# LearnSpace — Полный UX-аудит фронтенда
**Дата проверки:** 2026-03-16
**Проверено файлов:** 40
**Методология:** ручной просмотр каждого HTML-файла, оценка по 10 категориям
---
## Сводная таблица критических проблем
| # | Файл | Проблема | Серьёзность |
|---|------|----------|-------------|
| 1 | board.html | Кнопка «Перейти» ведёт на `/dashboard`, а не запускает тест | HIGH |
| 2 | login.html | После регистрации всегда редирект на `/dashboard` — учителя попадают не туда | HIGH |
| 3 | homework.html | `catch {}` — пустой обработчик ошибок, студент не знает о сбое | HIGH |
| 4 | test-result.html | Обе кнопки «К тестам» и «В кабинет» ведут на `/dashboard` — дублирование без смысла | HIGH |
| 5 | live-quiz.html | Кнопка «Завершить сессию» без подтверждения — деструктивное действие | HIGH |
| 6 | admin.html | Смена роли пользователя без подтверждения (inline select) | HIGH |
| 7 | board.html | Ссылка `href="/red-book.html"`с расширением `.html`, все остальные роуты без расширения | MEDIUM |
| 8 | Все страницы | Analytics, Gradebook, Question Bank, Live Quiz, Lesson Editor — нет в сайдбаре | MEDIUM |
| 9 | profile.html | `overflow: hidden` на `body` — может сломаться при виртуальной клавиатуре на мобиле | MEDIUM |
| 10 | profile.html | Форма смены пароля без поля «текущий пароль» | MEDIUM |
---
## Детальный разбор по файлам
---
### login.html
#### [HIGH] Flow: редирект после регистрации всегда на `/dashboard`
Учителя и администраторы после создания аккаунта попадают на страницу студента. Им нужны `/classes` или `/admin`.
**Фикс:** после успешной регистрации получать роль из ответа API и редиректить по роли: `student → /dashboard`, `teacher → /classes`, `admin → /admin`.
#### [MEDIUM] Flow: нет выбора роли при регистрации
Непонятно, как учитель или администратор создаёт аккаунт. Нет поля «роль» в форме регистрации.
**Фикс:** добавить `<select>` с вариантами «Студент» / «Учитель». Роль «Администратор» создаётся только через admin-панель.
#### [MEDIUM] Accessibility: нет ссылки «Забыли пароль?»
Стандартный элемент auth-страниц полностью отсутствует.
**Фикс:** добавить ссылку под полем пароля на форме входа.
#### [LOW] Consistency: `showBtnSuccess` вставляет строку `'✓ Сохранено'` через конкатенацию текста
Использование символа `✓` в коде (эмодзи-подобный символ) противоречит правилу проекта «только SVG-иконки».
**Фикс:** заменить `'✓ '` на inline SVG с классом `.ic`.
---
### dashboard.html
#### [MEDIUM] Loading: у виджетов есть скелетоны, но нет fallback при сетевой ошибке
Если API возвращает 500 — скелетон не убирается и данные не появляются. Пользователь не понимает, что произошло.
**Фикс:** в каждом `catch` блоке заменять скелетон на rich-empty с текстом ошибки и кнопкой «Повторить».
#### [MEDIUM] Navigation: кнопки быстрых действий (`adm-actions`) видны для teacher/admin, но не имеют состояния загрузки
После нажатия на «Создать тест» или «Добавить класс» кнопка не блокируется — возможно двойное нажатие.
**Фикс:** `btn.disabled = true` сразу при клике.
#### [LOW] Feedback: таймер дедлайна предупреждает только за 2 минуты (`secondsLeft <= 120`)
Для длинных тестов (1 час+) единственное предупреждение придёт слишком поздно, если пользователь отвлёкся.
**Фикс:** добавить промежуточное предупреждение при 25% оставшегося времени и за 5 минут.
---
### board.html
#### [HIGH] Flow: кнопка «Перейти» на карточке задания ведёт на `/dashboard`, а не на тест
```html
<a href="/dashboard" class="btn-action">Перейти</a>
```
Студент не может начать задание прямо с борда — он попадает на главную страницу.
**Фикс:** ссылка должна вести на `/test-run?session=...&assignment=...` (с нужными параметрами задания).
#### [MEDIUM] Consistency: ссылка на Red Book в сайдбаре содержит `.html`-расширение
```html
<a href="/red-book.html" ...>
```
Все остальные ссылки: `/dashboard`, `/board`, `/classes` — без расширения.
**Фикс:** добавить Express-роут `/red-book` → отдавать `red-book.html`, изменить ссылку на `/red-book`. Это же исправление нужно во **всех** файлах с сайдбаром.
#### [MEDIUM] Navigation: студент без класса видит пустой бord без способа вступить по инвайт-коду
Пустое состояние есть, но нет поля ввода инвайт-кода.
**Фикс:** добавить в empty-state форму «Введи код класса» с полем и кнопкой.
#### [MEDIUM] Feedback: бейдж «LIVE» рендерится всегда, независимо от статуса
```html
<span class="live-badge">LIVE</span>
```
JS должен скрывать/показывать его по реальному статусу сессии.
**Фикс:** `liveBadge.style.display = session.is_live ? '' : 'none'`.
#### [LOW] Data integrity: реакции сохраняются только в `localStorage`
Данные `ls_reactions` пропадут на другом браузере/устройстве и не видны другим пользователям.
**Фикс:** сохранять реакцию через POST `/api/posts/:id/react`, localStorage использовать только как оптимистичный кэш.
---
### classes.html
#### [MEDIUM] Forms: кнопка «Создать класс» видна до выполнения JS role-check
В HTML `btn-new-cl` отрисовывается сразу. Студент, быстро открыв страницу, видит её до скрытия.
**Фикс:** добавить `style="display:none"` в HTML, показывать через JS только для teacher/admin.
#### [MEDIUM] Navigation: ссылка на Gradebook скрыта в маленькой иконке в заголовке сайдбара
Новый пользователь никогда не найдёт журнал оценок.
**Фикс:** вынести «Журнал» как отдельный пункт сайдбара (или добавить явную кнопку в шапку детали класса).
#### [MEDIUM] Confirmation: кнопка `btn-delete-class` — неясно, есть ли подтверждение
В HTML диалог подтверждения не виден. Если он реализован только в JS-коде в виде `confirm()` браузера — это слабое решение.
**Фикс:** использовать кастомный modal с явным текстом «Удалить класс X? Это действие необратимо», кнопками «Отмена» / «Удалить».
#### [LOW] Accessibility: все элементы управления классами доступны только после выбора класса из списка
Состояние «ничего не выбрано» показывает заглушку «Выберите класс» без инструкции о том, что нужно сделать слева.
**Фикс:** добавить стрелку-подсказку или анимацию, указывающую на список классов.
---
### admin.html
#### [HIGH] Confirmation: смена роли пользователя через inline `<select>` без подтверждения
```html
<select class="role-select">...</select>
```
Неосторожный клик меняет роль немедленно. Это деструктивное действие: учитель превращается в студента без возможности отмены.
**Фикс:** при `change` события показывать confirmation modal «Изменить роль пользователя X с Y на Z?», только после подтверждения отправлять PATCH-запрос.
#### [HIGH] Confirmation: кнопка `btn-del-q` (удалить вопрос) без видимого диалога подтверждения
Удаление вопроса из банка — необратимое действие.
**Фикс:** confirmation modal с предупреждением об необратимости.
#### [MEDIUM] Error handling: нет отображения ошибок при провале admin-операций
Если PATCH /api/users/:id/role вернул 500, пользователь не узнает об этом.
**Фикс:** показывать toast/alert с текстом ошибки из ответа API.
#### [LOW] UX: `max-height: 0` → `max-height: 4000px` в session drawer
Анимация открытия непредсказуема по скорости — CSS считает переход от 0 до 4000px, хотя реальная высота 200px.
**Фикс:** использовать JavaScript для установки точной `max-height: element.scrollHeight + 'px'`.
---
### test-run.html
#### [MEDIUM] Feedback: кнопка «Завершить тест» доступна с первого вопроса без минимального порога
Студент может нечаянно нажать и завершить тест, ответив на 0 вопросов.
**Фикс:** показывать предупреждение в confirmation modal: «Ты ответил на X из Y вопросов. Уверен, что хочешь завершить?»
#### [MEDIUM] Accessibility: `<img src="..." alt="">` — пустой атрибут alt у изображений в вопросах
Студенты с нарушениями зрения не получат описание изображения.
**Фикс:** заполнять `alt` из поля описания вопроса в БД (например, `alt="Изображение к вопросу: ..."`).
#### [MEDIUM] Loading: нет серверного сохранения прогресса теста в процессе
SessionStorage работает только в рамках одной вкладки. При сбое браузера прогресс теряется.
**Фикс:** периодически (каждые 30 сек) отправлять автосохранение через PATCH `/api/sessions/:id/autosave` с текущими ответами.
#### [LOW] Feedback: предупреждение таймера только за 120 секунд — мало для коротких тестов
**Фикс:** добавить предупреждение при достижении 25% оставшегося времени (независимо от абсолютного значения).
---
### test-result.html
#### [HIGH] Flow: обе кнопки «К тестам» и «В кабинет» ведут на `/dashboard`
```html
<a href="/dashboard" class="btn-primary">К тестам</a>
<a href="/dashboard" class="btn-ghost">В кабинет</a>
```
Дублирование кнопок с одинаковым назначением сбивает с толку.
**Фикс:** «К тестам» → `/board` (список заданий), «В кабинет» → `/dashboard`, добавить третью кнопку «Пройти ещё раз» → `/test-run?...` с теми же параметрами.
#### [MEDIUM] Navigation: нет возврата к заданию на борде, если тест был из assignment
Если студент пришёл с `/board?class=5`, после теста нет ссылки «Вернуться к классу».
**Фикс:** передавать `returnUrl` параметром и использовать его в кнопке «Назад».
---
### homework.html
#### [HIGH] Error handling: пустой `catch {}` при загрузке данных класса в `initStudent()`
```js
catch {}
```
Студент видит пустую форму без объяснения причины.
**Фикс:** заменить на `catch(e) { showError('Не удалось загрузить задания. ' + e.message); }`.
#### [HIGH] Forms: при нескольких классах у студента задание всегда подаётся в `classes[0].id`
Студент в 2+ классах может случайно отправить работу не в тот класс.
**Фикс:** добавить `<select>` для выбора класса, если `classes.length > 1`.
#### [MEDIUM] Forms: нет клиентской валидации размера файла перед отправкой
Подсказка «до 50 МБ» есть, но проверки нет — пользователь узнаёт об ошибке только после долгой загрузки.
**Фикс:**
```js
if (file.size > 50 * 1024 * 1024) { showError('Файл слишком большой (максимум 50 МБ)'); return; }
```
#### [MEDIUM] Error handling: при ошибке загрузки списка работ нет кнопки «Повторить»
Показывается текст «Ошибка загрузки», но нет способа повторить запрос.
**Фикс:** добавить `<button onclick="loadHomeworks()">Повторить</button>` в блок с ошибкой.
---
### profile.html
#### [MEDIUM] Mobile: `html, body { height: 100%; overflow: hidden; }` сломается при виртуальной клавиатуре
На iOS при фокусе на input виртуальная клавиатура поднимает область просмотра, overflow hidden блокирует прокрутку к полю.
**Фикс:** убрать `overflow: hidden` с `body`, использовать `height: 100dvh` на layout-контейнере или flex с `min-height: 100svh`.
#### [MEDIUM] Security/Forms: форма смены пароля не требует ввода текущего пароля
Любой получивший доступ к разблокированному устройству может сменить пароль.
**Фикс:** добавить поле «Текущий пароль» (`type="password"`) перед полями нового пароля.
#### [LOW] Feedback: `form-msg` появляется, но нет auto-hide через N секунд
Сообщение «Сохранено» остаётся на экране indefinitely.
**Фикс:** `setTimeout(() => formMsg.classList.remove('ok','err'), 4000)`.
---
### library.html
#### [MEDIUM] Accessibility/Mobile: кнопки редактирования/удаления папок появляются только при hover
```css
.folder-card-acts { opacity: 0; }
.folder-card:hover .folder-card-acts { opacity: 1; }
```
На touch-устройствах hover недоступен — кнопки принципиально не видны.
**Фикс:** на мобиле заменить hover на long-press или показывать кнопки через иконку «⋮» (три точки) при тапе на карточку.
#### [MEDIUM] Confirmation: `btn-del` (удалить файл/папку) без видимого confirmation modal в HTML
**Фикс:** добавить modal «Удалить файл/папку X? Это действие необратимо».
#### [LOW] Navigation: breadcrumb `.lib-bc` показывает путь, но нет клавиатурной поддержки
Ссылки в breadcrumb должны быть `<a href="...">`, а не `<span onclick="...">`.
**Фикс:** использовать семантические `<a>` элементы для каждого уровня пути.
---
### analytics.html
#### [MEDIUM] Loading: нет состояния загрузки для диаграмм Chart.js
Пока данные грузятся — область диаграммы пустая/белая без индикации.
**Фикс:** показывать skeleton-placeholder над canvas до получения данных.
#### [LOW] Navigation: кнопка «Назад» (`an-header-back`) ведёт `history.back()` — может вернуть в сломанное состояние
**Фикс:** задать конкретный href (`/classes` или `/dashboard`) вместо `history.back()`.
---
### gradebook.html
#### [MEDIUM] Accessibility: при большом числе студентов таблица горизонтально прокручивается, но нет подсказки
Пользователь не знает, что таблица шире экрана.
**Фикс:** добавить fade-gradient на правом краю таблицы, пока есть горизонтальный скролл.
#### [LOW] Navigation: нет ссылки на профиль студента при клике на имя в таблице
**Фикс:** сделать имена студентов в первом столбце кликабельными ссылками на `/profile?id=...`.
---
### question-bank.html
#### [MEDIUM] Confirmation: удаление вопроса (если есть кнопка) без confirmation modal
**Фикс:** modal с предупреждением «Вопрос может быть использован в активных тестах».
#### [LOW] Forms: фильтры не сохраняются при переходе к редактированию и обратно
Пользователь теряет выбранные фильтры при навигации.
**Фикс:** хранить состояние фильтров в URL query params (`?subject=bio&type=mc`).
---
### live-quiz.html
#### [HIGH] Confirmation: кнопка `btn-end` (завершить сессию) без подтверждения
Нажатие заканчивает живую викторину для всех участников мгновенно.
**Фикс:** modal «Завершить сессию? Все участники будут автоматически переведены на экран результатов».
#### [MEDIUM] Feedback: нет отображения числа подключённых участников в реальном времени
Учитель не видит, сколько студентов уже подключились перед стартом.
**Фикс:** показывать счётчик «X участников подключились» с обновлением через polling/WebSocket.
---
### course.html
#### [LOW] Navigation: кнопка «Записаться» / «Продолжить» не показывает состояние записи до загрузки
Кнопка мерцает при загрузке (меняет текст после JS-инициализации).
**Фикс:** рендерить кнопку в скрытом/skeleton-состоянии до получения данных о статусе записи.
---
### lesson.html
#### [MEDIUM] Content: inline quiz не имеет кнопки «Попробовать снова»
После неверного ответа опция подсвечивается красным, но повторить нельзя.
**Фикс:** добавить кнопку «Ещё раз» после показа правильного ответа.
#### [MEDIUM] Layout: `max-height: 600px` для accordion-блоков — длинный контент обрежется
```css
.accordion-body { max-height: 600px; overflow: hidden; }
```
**Фикс:** убрать `max-height`, использовать JS `element.style.maxHeight = element.scrollHeight + 'px'` для анимации.
#### [LOW] Navigation: оглавление (TOC) скрыто при ширине < 1100px — нет альтернативного доступа
На планшетах нет способа перейти к нужному разделу без ручной прокрутки.
**Фикс:** добавить кнопку «Оглавление» в topbar, открывающую TOC как drawer/dropdown на мобиле.
---
### pet.html
#### [LOW] Accessibility: интерактивная анимация дыхания без возможности приостановки
Постоянно движущийся элемент может вызывать дискомфорт у пользователей с вестибулярными нарушениями.
**Фикс:** соблюдать `prefers-reduced-motion`: `@media (prefers-reduced-motion: reduce) { .breath-anim { animation: none; } }`.
---
### red-book.html
#### [MEDIUM] Consistency: доступ через `href="/red-book.html"` с расширением
Все остальные страницы доступны без `.html`. Эта единственная исключение.
**Фикс:** добавить роут `GET /red-book` → serve `red-book.html`. Исправить ссылку в сайдбарах всех страниц.
#### [LOW] Navigation: пункты подменю Red Book (`red-book-biomes`, `red-book-ecosystem`, `red-book-games`) видны в сайдбаре только на страницах Red Book
Пользователь не знает о подразделах, пока не зайдёт в Red Book.
**Фикс:** допустимо (паттерн контекстного сайдбара), но нужен `<title>` в виде «Красная книга — Биомы» для ориентации (уже есть).
---
### theory.html
#### [MEDIUM] Navigation/Roles: кнопка «Создать курс» (`btn-new-course`) видна в заголовке для всех
Студент видит кнопку действия, недоступного ему по роли.
**Фикс:** скрывать кнопку до JS role-check (`style="display:none"` в HTML, показывать только для teacher/admin).
---
### 404.html
#### [LOW] Navigation: кнопка «Назад» использует `history.back()`
Если 404 открыт по прямой ссылке или из внешнего источника — history.back() закроет вкладку или уведёт на внешний сайт.
**Фикс:** добавить `if (history.length > 1) history.back(); else location.href = '/dashboard';`.
---
### 403.html
#### [LOW] UX: три кнопки подряд — «На главную», «Назад», «Войти» — могут запутать незалогиненного пользователя
Если пользователь не залогинен, «На главную» тоже приведёт к редиректу на login.
**Фикс:** проверять авторизацию через `LS.isLoggedIn()` и показывать только релевантные кнопки: для залогиненных — «На главную» + «Назад», для незалогиненных — только «Войти».
---
### 500.html
#### [LOW] Feedback: «Мы уже в курсе» — вводящий в заблуждение текст
На статических страницах ошибок нет механизма автоматической отправки отчёта. Фраза ложная.
**Фикс:** заменить на «Попробуй обновить страницу или вернись позже».
---
### biochem.html и семейство (biochem-library, biochem-pathways, biochem-reactions, biochem-properties)
#### [MEDIUM] Mobile: `body { overflow: hidden; }` + canvas-интерфейс непригоден для мобиле
Молекулярный конструктор и аналогичные страницы требуют мышь для взаимодействия с canvas.
**Фикс:** добавить предупреждение «Для работы рекомендуется компьютер» при ширине < 768px, или реализовать touch-управление (pinch-to-zoom, tap-to-place).
#### [MEDIUM] Navigation: нет кнопки «Сохранить молекулу» с очевидным расположением
Действия в правой панели (`bp-btn`) мелкие и не имеют подписей при сжатии панели.
**Фикс:** добавить явную primary-кнопку «Сохранить в библиотеку» в тулбар.
#### [LOW] Accessibility: canvas-элементы недоступны для скринридеров
`<canvas id="mol-canvas">` без `aria-label` и без fallback-контента.
**Фикс:** `<canvas aria-label="Молекулярный конструктор" role="img">Интерактивный редактор молекул</canvas>`.
---
### lesson-editor.html
#### [MEDIUM] Forms: нет автосохранения (видна статусная строка `.etb-status`, но нет таймера автосохранения)
Потеря введённого контента при случайном закрытии вкладки.
**Фикс:** `setInterval(() => saveDraft(), 30000)` + обработчик `beforeunload` при несохранённых изменениях.
#### [MEDIUM] Navigation/Confirmation: кнопка «Назад» (`etb-back`) ведёт без проверки несохранённых изменений
**Фикс:** перехватить клик, если `isDirty`, показать: «Есть несохранённые изменения. Уйти?».
#### [LOW] Accessibility: редактор использует `contenteditable` (предположительно) без ARIA-ролей
**Фикс:** `role="textbox" aria-multiline="true" aria-label="Содержимое урока"`.
---
### collection.html / collection-rb.html
#### [LOW] Empty state: нет состояния пустой коллекции
Если у пользователя нет элементов в коллекции, нет поясняющего empty-state.
**Фикс:** добавить `.rich-empty` с иллюстрацией и текстом «Коллекция пуста — открывай карточки видов, чтобы пополнять её».
---
### red-book-biomes.html / red-book-ecosystem.html / red-book-games.html
#### [LOW] Mobile: canvas-страницы с `height: 100vh; overflow: hidden` некорректно ведут себя на мобиле
**Фикс:** использовать `height: 100dvh` для корректного учёта браузерной панели на мобиле.
#### [LOW] Navigation: кнопка «Назад» присутствует в topbar как `eco-back` — хорошо, но нет breadcrumb
Пользователь не видит, где он в иерархии Red Book.
**Фикс:** добавить breadcrumb «Красная книга > Экосистемы».
---
## Кросс-страничные (системные) проблемы
### [HIGH] Navigation: ссылка `/red-book.html` с расширением во всех сайдбарах
Затрагивает: board.html, classes.html, homework.html, library.html, profile.html, dashboard.html и все остальные страницы с общим сайдбаром.
**Фикс:** добавить роут в Express: `app.get('/red-book', (req,res) => res.sendFile('red-book.html', {root: 'frontend'}))`, изменить все ссылки.
### [MEDIUM] Navigation: «скрытые» инструментальные страницы недостижимы из навигации
Следующие страницы **не имеют ссылок в основном сайдбаре**:
- `/analytics` — аналитика успеваемости
- `/gradebook` — журнал оценок
- `/question-bank` — банк вопросов
- `/live-quiz` — живая викторина
- `/lesson-editor` — редактор уроков
- `/flashcards` — карточки SRS
- `/crossword`, `/hangman` — игры
- `/knowledge-map` — карта знаний
- `/lab` — лаборатория симуляций
- `/pet` — виртуальный питомец
Пользователи находят их только если знают прямой URL.
**Фикс:** сгруппировать в сайдбаре или добавить «хаб» страницу с плитками всех инструментов (например, `/tools`).
### [MEDIUM] Consistency: CDN Lucide загружается с разных источников
- Большинство файлов: `https://cdn.jsdelivr.net/npm/lucide@0.469.0/dist/umd/lucide.min.js`
- `biochem-pathways.html`, `biochem-reactions.html`: `https://unpkg.com/lucide@0.469.0/...`
**Фикс:** унифицировать на один CDN (jsDelivr предпочтительнее по производительности).
### [MEDIUM] Accessibility: нет `aria-current="page"` на активных пунктах сайдбара
`.sb-link.active` ставится через JS, но без ARIA-атрибута скринридеры не объявляют текущую страницу.
**Фикс:** при активации добавлять `link.setAttribute('aria-current', 'page')`.
### [LOW] Mobile: `js/mobile.js` не подключён к инструментальным страницам
Страницы analytics, gradebook, question-bank, live-quiz и др. не имеют моб-бара, их нельзя нормально открыть на мобиле.
**Фикс:** добавить `<script src="/js/mobile.js"></script>` перед `</body>` во всех страницах с `.app-layout`.
### [LOW] Consistency: страницы biochem-семейства используют `data-lucide` без вызова `lucide.createIcons()`
Если JS lucide загружен defer, иконки не инициализируются до вызова функции.
**Фикс:** убедиться, что `lucide.createIcons()` вызывается после загрузки DOM во всех таких страницах.
### [LOW] Accessibility: большинство модальных окон (`confirm-overlay`, `modal`) не управляют фокусом
При открытии modal фокус остаётся на кнопке открытия, а не перемещается внутрь. Нет `aria-modal="true"`, нет `role="dialog"`, нет `aria-labelledby`.
**Фикс:** при открытии modal: `modal.setAttribute('aria-modal', 'true'); modal.setAttribute('role', 'dialog'); firstFocusable.focus();`. Trap focus внутри пока открыт.
---
## Статистика проблем
| Серьёзность | Количество |
|-------------|------------|
| HIGH | 10 |
| MEDIUM | 32 |
| LOW | 22 |
| **Итого** | **64** |
---
## Приоритетный план исправлений
### Спринт 1 (критические, 1–2 дня)
1. `board.html`: исправить href кнопки «Перейти» → `/test-run?...`
2. `login.html`: редирект после регистрации по роли
3. `homework.html`: убрать пустой `catch {}`, добавить обработку ошибок
4. `test-result.html`: разделить кнопки на разные destination
5. `admin.html`: добавить confirmation modal на смену роли и удаление вопроса
6. `live-quiz.html`: добавить confirmation на завершение сессии
### Спринт 2 (важные, 3–5 дней)
7. Express-роут `/red-book` + исправить ссылки во всех сайдбарах
8. Добавить `/js/mobile.js` на все инструментальные страницы
9. `profile.html`: поле «текущий пароль» + убрать `overflow:hidden` с body
10. `homework.html`: select класса при нескольких классах + валидация размера файла
11. `lesson-editor.html`: автосохранение + beforeunload guard
12. Скрыть учительские кнопки (`btn-new-cl`, `btn-new-course`) до JS role-check
### Спринт 3 (улучшения, 1–2 недели)
13. Добавить `aria-current="page"` в сайдбаре
14. ARIA-атрибуты на всех modal/dialog
15. `prefers-reduced-motion` на анимации
16. Хаб-страница `/tools` или группировка инструментов в сайдбаре
17. Унификация CDN Lucide на jsDelivr
18. Сохранение реакций на сервере (board.html)
19. Автосохранение прогресса теста на сервере (test-run.html)
20. Контекстные empty-state для всех ошибок загрузки с кнопкой «Повторить»