Перейти к содержанию

SvelteKit: ошибки кабинета и доступный интерфейс

У «Маршрута» появилась серверная сессия и история отдельного учебного читателя. Теперь рассмотрим ситуации, когда данных нет, запрос отклонён или сервер не смог выполнить операцию. Одновременно проверим смысл ссылок, кнопок и сообщений для человека, который использует клавиатуру или вспомогательную технологию.

Результат урока — понятные состояния кабинета без ложного успеха. Мы не проводим аудит действующего приложения и не утверждаем соответствие какому-либо стандарту по итогам испытаний. Пример не запускался. Разберём ожидаемое поведение и подготовим код, который позже можно проверить в выбранной среде.

Пустая история и отказ чтения

Новая сессия действительно может иметь ноль закладок. Тогда кабинет успешно загружен и показывает пояснение: «Сохранённых уроков пока нет». Сбой репозитория — другое событие. Если заменить его пустым массивом, человек решит, что сохранённая история потеряна или очищена, хотя сервер просто не смог её прочитать.

Нельзя писать общий catch вокруг загрузки, который возвращает пустую историю при любой ошибке. Ожидаемое отсутствие записей выражается обычным результатом репозитория. Неожиданная ошибка должна попасть в обработчик, который отдаёт безопасное сообщение и сохраняет возможность повторить запрос. При этом техническая диагностика остаётся на сервере и не содержит паролей или токенов.

Полный src/routes/+error.svelte задаёт общий экран ошибки. $app/state используется для текущего состояния страницы в SvelteKit 2 с Svelte 5; выбранный API проверен по официальному руководству $app/state.

<script lang="ts">
  import { page } from '$app/state';
</script>
<svelte:head><title>Ошибка — Маршрут</title></svelte:head>
<h1>{page.status === 404 ? 'Страница не найдена' : 'Не удалось открыть страницу'}</h1>
<p>{page.error?.message ?? 'Повторите попытку позже.'}</p>
<a href="/">Перейти к урокам</a>

Сервер обязан формировать message безопасно. Для неизвестного урока допустим предметный текст «Урок не найден». Для неожиданной ошибки не следует автоматически копировать error.message из драйвера базы: там могут быть детали внутреннего соединения. Выбор экрана и выбор допустимой информации связаны, но остаются отдельными решениями.

Ожидаемый отказ формы

Короткое имя или неизвестный идентификатор закладки уже обрабатываются через fail. Человек остаётся возле операции и может исправить запрос. HTTP 400 говорит о недопустимом входе, 401 — об отсутствии действующей сессии. Общая страница ошибки для этих случаев сделала бы обычную работу с формой неудобной.

Если токен истёк в момент отправки, данные нельзя записывать для общего пользователя или последнего вошедшего читателя. Нужно отказать и объяснить необходимость входа. Введённое имя можно сохранить в ответе ожидаемого отказа, однако пароль или токен не следует отражать обратно. После нового входа человек должен понимать, нужно ли повторить действие.

Ожидаемое сообщение «Имя обновлено» появляется только после записи. Если сервер вернул ошибку, прежнее имя остаётся источником истины. Оптимистический интерфейс мог бы показать будущее значение заранее, но тогда потребовались бы откат и разрешение конфликта. В серии используем подтверждённое обновление, потому что оно легче объясняет границу сохранения.

Название действия и состояние кнопки

Кнопка закладки должна говорить, что произойдёт при нажатии, а её состояние — установлен ли признак сейчас. В предыдущих формах использованы «Сохранить урок» и «Убрать закладку» вместе с aria-pressed. Это помогает различать команду и текущую отметку. Цвет служит дополнительным признаком и не заменяет текст.

Нативные button, a, input и label предоставляют базовую семантику и клавиатурное управление. Не заменяйте их декоративными контейнерами без необходимости. Фокус должен быть видим на светлом и тёмном фоне, а контуры не следует удалять ради аккуратной рамки. Компактный режим уменьшает отступы, однако не должен превращать кнопки в слишком маленькие цели.

Для прогресса можно использовать нативный элемент и видимый текст. Фрагмент применяется в кабинете, где числа вычислены по полному каталогу курса.

<label for="course-progress">Прогресс курса</label>
<progress id="course-progress" value={data.completed.length} max={data.lessons.length}>
  {data.completed.length} из {data.lessons.length}
</progress>
<p>Завершено {data.completed.length} из {data.lessons.length} уроков.</p>

Если каталог пуст, нужно отдельно показать отсутствие курса, а не делить на ноль. Набор завершённых идентификаторов также должен быть пересечён с известными уроками. Иначе удалённый материал способен сделать прогресс больше общего количества. Это предметная целостность, которая поддерживает доступное описание числа.

Исчезновение карточки и фокус

В подборке закладок снятие отметки удаляет карточку. Если активная кнопка находилась внутри неё, фокус может исчезнуть вместе с DOM-элементом. Человеку приходится заново искать место. Один подход — после успешного обновления направить фокус на соседнюю карточку, а при пустом наборе на заголовок подборки. Другой — временно оставить сообщение с возможностью вернуть закладку.

В конечном учебном кабинете показывается весь каталог, поэтому карточка после снятия закладки остаётся на месте. Такое решение уменьшает сложность управления фокусом и явно названо как граница текущего интерфейса. Если позже появится отдельный список только сохранённых материалов, это будет новым UX-сценарием, который нельзя считать автоматически решённым существующим enhance.

Не объявляйте всему документу постоянный aria-live. Обновление списка и числа может создавать слишком много сообщений. Выделенный статус формы сообщает именно результат операции, а остальные данные остаются доступными при обычном чтении страницы.

Что передать для проверки

Будущая ручная проверка должна включать неизвестный урок, новую пустую историю, истёкшую сессию, недопустимое имя и отказ репозитория. Для каждого случая заранее известно, какое сообщение и какой статус ожидаются. Отдельно пройдите интерфейс клавиатурой и убедитесь, что названия действий и видимость фокуса сохраняются после обновления.

Отображение удалённого материала

Если завершённый идентификатор отсутствует в публичном каталоге, кабинет не должен падать при обращении к его названию. Можно показать архивную запись, исключить её из текущего процента или объяснить, что материал больше не входит в курс. Конкретное решение выбирает редактор, а серверная проекция должна выражать его явно.

В нашей неизменной фикстуре такого состояния нет, но контракт проверки идентификаторов уже показывает место для будущей политики. Неизвестная запись в старой истории и неизвестное новое действие требуют разного отношения: первое могло быть законным прошлым состоянием, второе ещё можно отклонить до записи.

Такой разбор дополняет технический 404. Ошибка адреса, пустая подборка и архивный прогресс могут выглядеть похожими визуально, однако означают разные результаты для человека. Хороший текст интерфейса следует этому различию.

Это план проверки, а не выполненный отчёт. Наличие кода ошибки не подтверждает, что все среды, адаптеры и браузеры показали одинаковый результат. Последняя глава соберёт исходники и договор передачи: какие состояния реализованы, что осталось учебным и какие действия нужны до использования кабинета внешними читателями.

Оглавление серии