From cdc5ae89951e2639ef5a635f785068bfa8d9f316 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Fri, 14 Aug 2026 11:32:32 +0300 Subject: [PATCH] feat: little frontend structure refactor --- .../.openspec.yaml | 2 + .../design.md | 66 +++++++++++++++++++ .../proposal.md | 26 ++++++++ .../specs/frontend-ui-resilience/spec.md | 34 ++++++++++ .../tasks.md | 16 +++++ openspec/specs/frontend-ui-resilience/spec.md | 36 ++++++++++ 6 files changed, 180 insertions(+) create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/.openspec.yaml create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/design.md create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/proposal.md create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/specs/frontend-ui-resilience/spec.md create mode 100644 openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/tasks.md create mode 100644 openspec/specs/frontend-ui-resilience/spec.md diff --git a/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/.openspec.yaml b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/.openspec.yaml new file mode 100644 index 0000000..4af8641 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-14 diff --git a/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/design.md b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/design.md new file mode 100644 index 0000000..9136b47 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/design.md @@ -0,0 +1,66 @@ +## Контекст + +После `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 не затрагиваются. diff --git a/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/proposal.md b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/proposal.md new file mode 100644 index 0000000..1470118 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/proposal.md @@ -0,0 +1,26 @@ +## Why + +Существующие finance-экраны содержат смешанные container/view/state responsibilities и не имеют общего recovery для непойманных runtime-ошибок. После foundation-выравнивания нужно закрепить проверяемые UI-паттерны template, не меняя бизнес-сценарии счетов, портфеля, auth и T-Bank connection. + +## What Changes + +- Добавляется глобальный route-keyed ErrorBoundary с понятным fallback и восстановлением после навигации. +- Асинхронные finance-сценарии разделяются на container, view-model и Loading/Error/Empty/Loaded views с colocated tests. +- Сохраняются существующие состояния ошибок T-Bank, stale portfolio, пустых данных, auth redirects и logout/refresh поведения. +- Удаляются фиктивные loading/error/empty components статичных страниц; opaque `accountId` не получает неиспользуемую валидацию параметра. +- Playwright и Storybook не входят в это изменение. + +## Capabilities + +### New Capabilities + +- `frontend-ui-resilience`: frontend безопасно отображает fallback и восстанавливается при непойманной runtime-ошибке маршрута. + +### Modified Capabilities + + + +## Impact + +- Затрагивает app shell, маршрутизацию, страницы счетов и портфеля, auth и T-Bank feature, colocated unit tests и frontend documentation. +- Зависит от завершённого `align-frontend-foundation`, который предоставляет общий API и test tooling. diff --git a/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/specs/frontend-ui-resilience/spec.md b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/specs/frontend-ui-resilience/spec.md new file mode 100644 index 0000000..a1c636d --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/specs/frontend-ui-resilience/spec.md @@ -0,0 +1,34 @@ +## Purpose + +Обеспечивает безопасное восстановление frontend после непойманной ошибки маршрута, не смешивая runtime failures с ожидаемыми состояниями инвестиционных сценариев. + +## ADDED Requirements + +### Requirement: Frontend безопасно обрабатывает непойманные ошибки маршрута +Frontend SHALL показывать понятный fallback вместо аварийно завершившегося route content, когда на public или protected route возникает непойманная runtime-ошибка. Fallback MUST предоставлять пользователю безопасный путь навигации и MUST NOT показывать технические детали ошибки. + +#### Scenario: Ошибка на protected route +- **WHEN** при отображении protected route возникает непойманная runtime-ошибка +- **THEN** frontend показывает fallback с навигацией к безопасному route для текущего auth state + +#### Scenario: Ошибка на public route +- **WHEN** при отображении public route возникает непойманная runtime-ошибка +- **THEN** frontend показывает fallback с навигацией к public route без технических деталей ошибки + +### Requirement: Frontend восстанавливается после навигации +Frontend SHALL сбрасывать состояние route error после перехода пользователя на другой route и MUST отображать новый route, если он не выбрасывает runtime-ошибку. + +#### Scenario: Навигация после fallback +- **WHEN** пользователь переходит с fallback на другой route +- **THEN** frontend сбрасывает предыдущую runtime-ошибку и отображает целевой route + +### Requirement: Ожидаемые finance states не становятся route failures +Frontend SHALL сохранять локальное отображение и recovery actions известных состояний Brokerage Account, Portfolio и T-Bank connection. Эти состояния MUST NOT заменяться глобальным fallback. + +#### Scenario: Portfolio Snapshot устарел +- **WHEN** frontend получает устаревший Portfolio Snapshot +- **THEN** frontend отображает данные Portfolio и существующее предупреждение об устаревании вместо глобального fallback + +#### Scenario: Ошибка T-Bank connection +- **WHEN** запрос T-Bank connection завершается известной ошибкой +- **THEN** frontend показывает существующее локальное состояние ошибки и доступное для него recovery action вместо глобального fallback diff --git a/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/tasks.md b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/tasks.md new file mode 100644 index 0000000..31c7784 --- /dev/null +++ b/openspec/changes/archive/2026-08-14-align-frontend-ui-patterns/tasks.md @@ -0,0 +1,16 @@ +## 1. Route resilience + +- [x] 1.1 Добавить native route-keyed ErrorBoundary вокруг `AppRoutes` с fallback без технических деталей и безопасной навигацией для authenticated и unauthenticated user. +- [x] 1.2 Добавить colocated tests для public/protected route errors и восстановления после navigation на исправный route. + +## 2. Finance UI patterns + +- [x] 2.1 Разделить Brokerage Account list на container, view-model и Loading/Error/Empty/Loaded views; сохранить query policy и recovery actions, добавить colocated tests. +- [x] 2.2 Разделить Portfolio page на container, view-model и Loading/Error/Empty/Loaded views; сохранить opaque `accountId`, local API errors, stale Portfolio Snapshot и portfolio presentation, добавить colocated tests. +- [x] 2.3 Разделить T-Bank connection status на container, view-model и state views; оставить form, modal и mutation behavior в Loaded view, добавить colocated tests известных errors и recovery actions. + +## 3. Cleanup, documentation и verification + +- [x] 3.1 Удалить `NotFoundPageEmpty` и оставить существующее 404 presentation в static page без artificial state components. +- [x] 3.2 Обновить frontend `AGENTS.md`, `docs/UI-PATTERN.md` и `docs/TESTING.md`; добавить в `docs/features/` фактическое описание route fallback и recovery behavior. +- [x] 3.3 Выполнить `npm run verify` и убедиться, что UI refactor не меняет auth redirects, logout/refresh и известные finance states. diff --git a/openspec/specs/frontend-ui-resilience/spec.md b/openspec/specs/frontend-ui-resilience/spec.md new file mode 100644 index 0000000..a64433c --- /dev/null +++ b/openspec/specs/frontend-ui-resilience/spec.md @@ -0,0 +1,36 @@ +# frontend-ui-resilience Specification + +## Purpose + +Обеспечивает безопасное восстановление frontend после непойманной ошибки маршрута, не смешивая runtime failures с ожидаемыми состояниями инвестиционных сценариев. + +## Requirements + +### Requirement: Frontend безопасно обрабатывает непойманные ошибки маршрута +Frontend SHALL показывать понятный fallback вместо аварийно завершившегося route content, когда на public или protected route возникает непойманная runtime-ошибка. Fallback MUST предоставлять пользователю безопасный путь навигации и MUST NOT показывать технические детали ошибки. + +#### Scenario: Ошибка на protected route +- **WHEN** при отображении protected route возникает непойманная runtime-ошибка +- **THEN** frontend показывает fallback с навигацией к безопасному route для текущего auth state + +#### Scenario: Ошибка на public route +- **WHEN** при отображении public route возникает непойманная runtime-ошибка +- **THEN** frontend показывает fallback с навигацией к public route без технических деталей ошибки + +### Requirement: Frontend восстанавливается после навигации +Frontend SHALL сбрасывать состояние route error после перехода пользователя на другой route и MUST отображать новый route, если он не выбрасывает runtime-ошибку. + +#### Scenario: Навигация после fallback +- **WHEN** пользователь переходит с fallback на другой route +- **THEN** frontend сбрасывает предыдущую runtime-ошибку и отображает целевой route + +### Requirement: Ожидаемые finance states не становятся route failures +Frontend SHALL сохранять локальное отображение и recovery actions известных состояний Brokerage Account, Portfolio и T-Bank connection. Эти состояния MUST NOT заменяться глобальным fallback. + +#### Scenario: Portfolio Snapshot устарел +- **WHEN** frontend получает устаревший Portfolio Snapshot +- **THEN** frontend отображает данные Portfolio и существующее предупреждение об устаревании вместо глобального fallback + +#### Scenario: Ошибка T-Bank connection +- **WHEN** запрос T-Bank connection завершается известной ошибкой +- **THEN** frontend показывает существующее локальное состояние ошибки и доступное для него recovery action вместо глобального fallback