be4d43105e
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>
494 lines
35 KiB
Markdown
494 lines
35 KiB
Markdown
# 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 для всех ошибок загрузки с кнопкой «Повторить»
|