feat: add breadcrumbs
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-08-14
|
||||
@@ -0,0 +1,57 @@
|
||||
## Context
|
||||
|
||||
См. `proposal.md` для мотивации и `specs/app-breadcrumb-navigation/spec.md` для контрактов поведения. `AppShell` является общим layout защищённых маршрутов и уже располагает содержимое над `<Outlet />`, но находится в shared-слое. Название Brokerage Account доступно через существующий query списка счетов и может отсутствовать при прямом переходе на маршрут счёта.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Показывать единый маршрутный trail над защищённым page content без изменения API.
|
||||
- Использовать настоящее название Brokerage Account, когда оно доступно, и безопасный fallback в противном случае.
|
||||
- Сохранить понятную навигацию на узких экранах.
|
||||
|
||||
**Non-Goals:**
|
||||
- Не добавлять крошки на public и not-found маршруты.
|
||||
- Не создавать endpoint отдельного Brokerage Account и не менять cache policy списка счетов.
|
||||
- Не заменять существующую боковую или нижнюю навигацию.
|
||||
|
||||
## Decisions
|
||||
|
||||
### Route-aware компонент остаётся в app-слое
|
||||
|
||||
Добавить app-level компонент, который строит trail из текущего маршрута, его параметра `accountId` и локализованных строк. `AppRoutes` передаёт его в shared `AppShell` как content slot перед `<Outlet />`.
|
||||
|
||||
Это сохраняет FSD-направление зависимостей: shared layout не импортирует entity или feature. Альтернатива с определением маршрутов и загрузкой счета внутри `AppShell` смешала бы shared-слой с доменными данными.
|
||||
|
||||
### Название счёта берётся из существующего query списка
|
||||
|
||||
Подкомпонент для маршрутов `/accounts/:accountId` и `/accounts/:accountId/operations` использует существующий query Brokerage Account и находит запись по `accountId`. Пока query не вернул соответствующее название, trail показывает «Счёт».
|
||||
|
||||
Это поддерживает прямые переходы и повторно использует query cache списка. Альтернатива с передачей имени из каждой страницы дублировала бы route-логику и не покрывала бы loading/error state до отображения page content. Новый API endpoint не нужен: список уже содержит стабильный id и непустое name.
|
||||
|
||||
### Trail строится из фиксированной карты защищённых маршрутов
|
||||
|
||||
Карта определяет `Брокерские счета`, `Настройки`, Portfolio счёта и Account Ledger, а также destination URL для предшествующих элементов. Текущий элемент рендерится текстом, остальные - router links. Неизвестные и public routes возвращают отсутствие trail.
|
||||
|
||||
Фиксированная карта соответствует текущему небольшому набору маршрутов и исключает разбор URL по сегментам. Автоматическое построение из pathname было бы хрупким для динамического `accountId` и не предоставляло бы локализованного названия счёта.
|
||||
|
||||
### Крошки заменяют локальный возврат из Account Ledger
|
||||
|
||||
Удалить действие «К счёту» из заголовка Account Ledger: trail уже предоставляет ссылку на Portfolio текущего Brokerage Account.
|
||||
|
||||
Это устраняет дублирующий navigation control. Боковая и нижняя навигация остаются без изменений, поскольку ведут к другим разделам приложения.
|
||||
|
||||
### Адаптация сохраняет последние два элемента
|
||||
|
||||
На узком экране компонент передаёт в presentation только ближайшего родителя и текущий элемент; на широком экране показывает полный trail. Стили layout отвечают за перенос и отступ от page content.
|
||||
|
||||
Это прямо реализует согласованное поведение для трёхуровневой цепочки. Горизонтальная прокрутка или отдельное меню скрытых элементов добавили бы управление без новых маршрутов и не нужны для текущей глубины.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [Прямой переход временно показывает обобщённое имя] → отображать «Счёт» до результата существующего query и не раскрывать `accountId`.
|
||||
- [Запрос списка счётов ради названия] → монтировать account-aware подкомпонент только на маршрутах счёта; React Query повторно использует его 60-секундный cache.
|
||||
- [Рост route map при новых страницах] → добавлять маршрут и его expected trail в той же app-level карте и соответствующем тесте.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Изменение не требует миграции данных или API. При rollback удалить slot и app-level компонент; существующие маршруты и page content продолжат работать без крошек.
|
||||
@@ -0,0 +1,25 @@
|
||||
## Why
|
||||
|
||||
Пользователь теряет быстрый путь к списку Brokerage Account и к Portfolio при переходе к Account Ledger. Нужна единая навигационная цепочка в защищённой части приложения.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Добавить хлебные крошки над содержимым каждого защищённого маршрута.
|
||||
- Отразить иерархию `Брокерские счета → <название счёта> → Операции` и отдельный маршрут «Настройки».
|
||||
- Сделать только предшествующие крошки ссылками; текущую страницу показывать текстом.
|
||||
- Сохранять текущую страницу и ближайшего родителя на узких экранах; при недоступном названии Brokerage Account показывать «Счёт».
|
||||
- Удалить из Account Ledger избыточное локальное действие возврата к счёту.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `app-breadcrumb-navigation`: единая и адаптивная навигация по защищённым маршрутам приложения.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
_Нет._
|
||||
|
||||
## Impact
|
||||
|
||||
- Затрагивает shared layout и локализованные UI-строки frontend.
|
||||
- Не изменяет backend API, данные Brokerage Account или зависимости.
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
## Purpose
|
||||
|
||||
Позволяет User быстро переходить по иерархии защищённых инвестиционных маршрутов через хлебные крошки над содержимым страницы.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Хлебные крошки доступны на защищённых маршрутах
|
||||
Клиент SHALL показывать хлебные крошки над содержимым на защищённых маршрутах `/accounts`, `/accounts/:accountId`, `/accounts/:accountId/operations` и `/settings`. Клиент MUST NOT показывать хлебные крошки на public-маршрутах и странице несуществующего маршрута.
|
||||
|
||||
#### Scenario: User открывает список Brokerage Account
|
||||
- **WHEN** авторизованный User открывает `/accounts`
|
||||
- **THEN** клиент показывает единственную текущую крошку «Брокерские счета» над содержимым списка
|
||||
|
||||
#### Scenario: User открывает Settings
|
||||
- **WHEN** авторизованный User открывает `/settings`
|
||||
- **THEN** клиент показывает единственную текущую крошку «Настройки» над содержимым страницы
|
||||
|
||||
### Requirement: Цепочка крошек отражает иерархию Brokerage Account
|
||||
На `/accounts/:accountId` клиент SHALL показывать «Брокерские счета → <название счёта>». На `/accounts/:accountId/operations` клиент SHALL показывать «Брокерские счета → <название счёта> → Операции». Если название Brokerage Account недоступно, клиент MUST показывать «Счёт» вместо технического идентификатора.
|
||||
|
||||
#### Scenario: User открывает Portfolio счёта с доступным названием
|
||||
- **WHEN** авторизованный User открывает `/accounts/:accountId` и название Brokerage Account доступно
|
||||
- **THEN** клиент показывает его название как текущую крошку после ссылки «Брокерские счета»
|
||||
|
||||
#### Scenario: User открывает Account Ledger
|
||||
- **WHEN** авторизованный User открывает `/accounts/:accountId/operations` и название Brokerage Account доступно
|
||||
- **THEN** клиент показывает ссылку на список, ссылку на Portfolio этого счёта и текущую крошку «Операции» в указанном порядке без отдельного локального действия возврата к счёту
|
||||
|
||||
#### Scenario: Название Brokerage Account недоступно
|
||||
- **WHEN** клиент не получил название Brokerage Account для защищённого маршрута счёта
|
||||
- **THEN** клиент показывает «Счёт» и не показывает `accountId`
|
||||
|
||||
### Requirement: Крошки предоставляют доступную адаптивную навигацию
|
||||
Клиент SHALL делать ссылками только крошки, предшествующие текущей странице; текущая крошка MUST быть обычным текстом. На узком экране клиент MUST скрывать промежуточные крошки, сохраняя ближайшую родительскую крошку и текущую страницу.
|
||||
|
||||
#### Scenario: User выбирает предшествующую крошку
|
||||
- **WHEN** User выбирает крошку, предшествующую текущей странице
|
||||
- **THEN** клиент переходит по маршруту этой крошки
|
||||
|
||||
#### Scenario: Крошки показаны на узком экране
|
||||
- **WHEN** цепочка содержит более двух элементов и User открывает её на узком экране
|
||||
- **THEN** клиент сохраняет видимыми ближайшую родительскую крошку и текущую страницу, скрывая более дальние промежуточные крошки
|
||||
@@ -0,0 +1,16 @@
|
||||
## 1. Навигация по маршрутам
|
||||
|
||||
- [x] 1.1 Добавить app-level компонент хлебных крошек, зависящий от маршрута: он строит локализованную цепочку для каждого protected route и не отображается на public или неизвестных маршрутах.
|
||||
- [x] 1.2 Получать название Brokerage Account через существующий query списка счетов на маршрутах счёта, используя локализованный fallback `Счёт`.
|
||||
- [x] 1.3 Добавить content slot в `AppShell` и отрисовать компонент крошек над outlet защищённого маршрута, не добавляя entity-зависимости в shared.
|
||||
- [x] 1.4 Удалить из Account Ledger локальное действие «К счету», которое дублирует ссылку крошек на Portfolio.
|
||||
|
||||
## 2. Отображение и тесты
|
||||
|
||||
- [x] 2.1 Добавить локализованные строки крошек и адаптивные стили, сохраняющие ближайшую родительскую и текущую крошку на узком экране.
|
||||
- [x] 2.2 Добавить component или route-тесты для цепочек protected route, назначений ссылок, текста текущей крошки, fallback названия счёта и отсутствия крошек на public и неизвестных маршрутах.
|
||||
|
||||
## 3. Проверка
|
||||
|
||||
- [x] 3.1 Выполнить `npm run verify` в `frontend`.
|
||||
- [x] 3.2 Выполнить `openspec validate add-app-breadcrumbs --type change --store wealth-ledger-specs` в `specs`.
|
||||
@@ -0,0 +1,44 @@
|
||||
# app-breadcrumb-navigation Specification
|
||||
|
||||
## Purpose
|
||||
|
||||
Позволяет User быстро переходить по иерархии защищённых инвестиционных маршрутов через хлебные крошки над содержимым страницы.
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: Хлебные крошки доступны на защищённых маршрутах
|
||||
Клиент SHALL показывать хлебные крошки над содержимым на защищённых маршрутах `/accounts`, `/accounts/:accountId`, `/accounts/:accountId/operations` и `/settings`. Клиент MUST NOT показывать хлебные крошки на public-маршрутах и странице несуществующего маршрута.
|
||||
|
||||
#### Scenario: User открывает список Brokerage Account
|
||||
- **WHEN** авторизованный User открывает `/accounts`
|
||||
- **THEN** клиент показывает единственную текущую крошку «Брокерские счета» над содержимым списка
|
||||
|
||||
#### Scenario: User открывает Settings
|
||||
- **WHEN** авторизованный User открывает `/settings`
|
||||
- **THEN** клиент показывает единственную текущую крошку «Настройки» над содержимым страницы
|
||||
|
||||
### Requirement: Цепочка крошек отражает иерархию Brokerage Account
|
||||
На `/accounts/:accountId` клиент SHALL показывать «Брокерские счета → <название счёта>». На `/accounts/:accountId/operations` клиент SHALL показывать «Брокерские счета → <название счёта> → Операции». Если название Brokerage Account недоступно, клиент MUST показывать «Счёт» вместо технического идентификатора.
|
||||
|
||||
#### Scenario: User открывает Portfolio счёта с доступным названием
|
||||
- **WHEN** авторизованный User открывает `/accounts/:accountId` и название Brokerage Account доступно
|
||||
- **THEN** клиент показывает его название как текущую крошку после ссылки «Брокерские счета»
|
||||
|
||||
#### Scenario: User открывает Account Ledger
|
||||
- **WHEN** авторизованный User открывает `/accounts/:accountId/operations` и название Brokerage Account доступно
|
||||
- **THEN** клиент показывает ссылку на список, ссылку на Portfolio этого счёта и текущую крошку «Операции» в указанном порядке без отдельного локального действия возврата к счёту
|
||||
|
||||
#### Scenario: Название Brokerage Account недоступно
|
||||
- **WHEN** клиент не получил название Brokerage Account для защищённого маршрута счёта
|
||||
- **THEN** клиент показывает «Счёт» и не показывает `accountId`
|
||||
|
||||
### Requirement: Крошки предоставляют доступную адаптивную навигацию
|
||||
Клиент SHALL делать ссылками только крошки, предшествующие текущей странице; текущая крошка MUST быть обычным текстом. На узком экране клиент MUST скрывать промежуточные крошки, сохраняя ближайшую родительскую крошку и текущую страницу.
|
||||
|
||||
#### Scenario: User выбирает предшествующую крошку
|
||||
- **WHEN** User выбирает крошку, предшествующую текущей странице
|
||||
- **THEN** клиент переходит по маршруту этой крошки
|
||||
|
||||
#### Scenario: Крошки показаны на узком экране
|
||||
- **WHEN** цепочка содержит более двух элементов и User открывает её на узком экране
|
||||
- **THEN** клиент сохраняет видимыми ближайшую родительскую крошку и текущую страницу, скрывая более дальние промежуточные крошки
|
||||
Reference in New Issue
Block a user