Files
specs/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/design.md
T

7.0 KiB
Raw Blame History

Контекст

После align-frontend-foundation frontend имеет типизированный API layer, aliases и изолированную test infrastructure. Асинхронные страницы всё ещё одновременно получают данные, выбирают состояние и формируют разметку. AppRoutes не имеет boundary для непойманных ошибок, а AppShell существует только для protected routes.

Причина изменения описана в proposal.md; наблюдаемое resilience behavior определено в specs/frontend-ui-resilience/spec.md.

Цели и нецели

Цели:

  • Изолировать непойманную ошибку любого route в безопасном fallback с восстановлением после навигации.
  • Разделить выбор данных и состояния от представления для Brokerage Account list, Portfolio и T-Bank connection.
  • Сохранить существующее поведение finance states, auth и navigation.
  • Зафиксировать новый UI pattern и resilience behavior тестами и документацией.

Нецели:

  • Не менять API contract, query keys, cache policies, T-Bank connection semantics и domain terminology.
  • Не вводить validation для opaque accountId.
  • Не переносить auth forms в view-model pattern и не добавлять Storybook или Playwright.

Решения

Один native route ErrorBoundary

Boundary реализуется средствами React без новой зависимости и оборачивает AppRoutes внутри уже существующих router, auth и query providers. Wrapper получает key текущего route из location и сбрасывает captured error при его изменении. Так один boundary покрывает public и protected routes; boundary внутри AppShell отклонён, поскольку не защищает login, register и 404 routes.

Fallback не показывает error details. Он определяет безопасный destination по текущему auth state: authenticated user получает переход к accounts, unauthenticated user — к login. При ошибке protected route AppShell не сохраняется, так как иначе понадобятся разные boundaries или перестройка route layout вне scope.

Container, view-model и state views для finance data

Accounts page, account portfolio page и T-Bank connection section получают container, который вызывает hooks и рендерит выбранное state view. View-model hook преобразует query and mutation results в props и callbacks конкретного состояния, включая error code and retry action. Loading, Error, Empty и Loaded views получают только presentation props и не выполняют HTTP requests.

Portfolio Loaded view сохраняет stale warning, totals, positions и cash balances. Его Empty view сохраняет heading Brokerage Account и текущее сообщение. Account API-404 остаётся локальным error state; cache miss не получает отдельной validation ветки для accountId.

T-Bank connection Loading/Error относятся к status query. Loaded view продолжает владеть формой, modal удаления, pending state и mutation error, чтобы submit/replacement/deletion flow не менялся. Auth forms остаются в текущей структуре, поскольку proposal не включает их в finance UI migration.

Граница известных ошибок

Known API and domain states выбираются view-model до рендера Loaded view и не throw в route boundary. Это включает отсутствие T-Bank connection, invalid T-Bank token, temporary T-Bank unavailability, empty data и stale Portfolio Snapshot. Existing recovery actions, redirects, logout and refresh behavior сохраняются.

Превращать known failures в глобальный fallback отклонено: оно уничтожит полезные действия recovery и смешает service error с defect рендера.

Статичные pages, тесты и документация

NotFoundPageEmpty удаляется как лишняя wrapper-component; существующий 404 presentation остаётся прямо в static page. Login, register и settings не получают artificial state components.

Tests лежат рядом с container and views. Они проверяют selection Loading/Error/Empty/Loaded, retry/navigation callbacks, stale Portfolio Snapshot, T-Bank error actions и route error recovery. Для route error используются test-only throwing components; production routes не получают тестовых веток.

frontend/docs/UI-PATTERN.md, TESTING.md и AGENTS.md описывают pattern и границы его применения. После реализации docs/features/ получает краткое фактическое описание fallback и recovery behavior.

Риски и компромиссы

  • [Boundary может поймать ошибку fallback] → Fallback остаётся минимальным, использует только уже существующие auth/navigation providers и не рендерит finance data.
  • [Refactor может изменить порядок query states] → Tests фиксируют текущий порядок и known recovery actions до удаления старых компонентов.
  • [VM может стать прослойкой без смысла] → VM содержит только state selection and presentation mapping; простая static page не получает VM.
  • [Route change не гарантирует исправность нового route] → Boundary сбрасывается на навигации, а новый route снова изолируется, если выбросит ошибку.

План миграции

  1. Добавить route ErrorBoundary и тесты public/protected fallback и navigation recovery.
  2. Разделить accounts и portfolio pages на container, VM и colocated state views, сохраняя их tests.
  3. Разделить T-Bank connection status presentation от Loaded form and modal, затем проверить mutation behavior.
  4. Удалить static state wrapper, обновить documentation и запустить npm run verify.

Изменение frontend-local и откатывается возвратом frontend и documentation commits. Backend и persisted data не затрагиваются.