feat(notifications+a11y): центр уведомлений /notifications, skip-link, axe smoke

Шаг 2.5 плана production-готовности:
- страница уведомлений (фильтр, догрузка, прочтение) + ссылки из dropdown/мобильного меню
- общие хелперы notification-utils (дедупликация dropdown)
- skip-link + id=content на main
- axe smoke-тесты (0 critical/serious), исправлены 2 нарушения в новой странице
- 3 новых теста (107/107), i18n отложен по решению пользователя
This commit is contained in:
2026-09-08 09:32:36 +03:00
parent 6e7270d2ee
commit 42e5ace655
12 changed files with 1249 additions and 127 deletions

View File

@@ -389,3 +389,19 @@
- Как проверялось: построчная программная сверка — 13/15 блоков байт-в-байт, расхождения объяснены (export-префиксы, перенос типов/констант, фрагменты вместо leading-комментариев JSX, удаление 24 неиспользуемых импортов оригинала). Чеклист: bulkSaveFormFields, правила согласующих, пресеты, QR-toggle, условия полей — дословно. check/lint/build чисто (warnings 566→542); vitest 104/104.
- Влияние на поиск/UX: нет.
- Итог 2.2: Chat 2696→639, TaskDetail 3278→804, Bots 3928→163, FormEditor 4975→337.
---
## [2.5] Продуктовая середина: центр уведомлений + a11y
- Статус: ✅ done (i18n-задел — ⏸ отклонено/отложено по решению пользователя: не-русские рынки не подтверждены)
- Зачем: центр уведомлений страницей, skip-link и базовый axe-проход (Фаза 2).
- Что изменено:
- `client/src/pages/NotificationsPage.tsx` — страница /notifications: фильтр все/непрочитанные (кнопки с aria-pressed), клиентская «Загрузить ещё» по 30, отметка одного/всех, переход к задаче через общий хелпер `getNotificationTarget`. Маршрут в App.tsx.
- `client/src/lib/notification-utils.tsx` — общие хелперы/хуки уведомлений (дедупликация: SystemNotificationsDropdown отрефакторен на них); ссылка «Все уведомления» в обоих dropdown + пункт в MobileBottomNav (на мобильном dropdown недоступен).
- Skip-link «Перейти к содержимому» в Sidebar.tsx (первым фокусируемым элементом, sr-only до фокуса, оба main получили id="content") — размещён в Sidebar, т.к. Layout рендерит children внутрь него.
- `tests/a11y.test.tsx` — axe smoke (jsdom) на Login/OnboardingChecklist/NotificationsPage: 0 critical/serious; инфра: axe-core, jsdom, @testing-library/react, alias @ в vitest.config + oxc jsx runtime (vitest 4/Vite 8).
- Исправлены найденные axe-нарушения: aria-valid-attr-value (Tabs без панелей → кнопки), nested-interactive (строка уведомления перестроена).
- Как проверялось: vitest 107/107; check/lint/build чисто.
- Влияние на поиск/UX: нет.
- Подводные камни: GET /api/notifications без серверной пагинации — при очень больших историях «Загрузить ещё» работает на клиенте; серверная пагинация — отдельная задача. Полный axe-проход по всему приложению — задел, не входил.