209 lines
16 KiB
Markdown
209 lines
16 KiB
Markdown
# Финальный редизайн обзора брокерского счёта по HTML-эталону
|
||
|
||
Дата: 2026-06-27
|
||
Статус: спецификация подготовлена
|
||
Эпик: [Портфель брокера](../../epics/BrokerPortfolio.md)
|
||
|
||
## Контекст
|
||
|
||
Маршрут `/broker/:accountId` уже был переведён на dashboard-композицию в рамках
|
||
`docs/features/broker-dashboard-redesign`, но эта итерация отражает промежуточный вариант:
|
||
`Hero → События → Доходы → Аналитика доходности → Аллокация`.
|
||
|
||
После интерактивной дизайн-итерации пользователь согласовал финальный HTML-эталон в
|
||
`docs/research/frontend-overview-redesign/example.html`. Именно этот файл является canonical visual
|
||
reference для текущей фичи. Файл `docs/research/2026-06-27-broker-account-redesign.html` содержит более
|
||
раннюю версию и не должен использоваться как источник истины, пока не будет синхронизирован.
|
||
|
||
Финальный экран должен выглядеть как HTML-эталон, но работать на реальных данных приложения,
|
||
существующих FSD-границах, backend API и текущей светлой теме MoexVibe.
|
||
|
||
## Цель
|
||
|
||
Переделать обзор брокерского счёта `/broker/:accountId` так, чтобы production React-страница визуально
|
||
и поведенчески соответствовала финальному HTML-эталону:
|
||
|
||
`Заголовок счёта → Стоимость портфеля за 6 месяцев → Аналитика доходности → Структура → Последние события`.
|
||
|
||
## Пользовательский результат
|
||
|
||
Пользователь на `/broker/:accountId` может:
|
||
|
||
- сразу увидеть название счёта, текущую доходность и дневное изменение;
|
||
- увидеть график стоимости портфеля за последние 6 месяцев;
|
||
- увидеть ключевые показатели доходности, включая комиссии и уплаченные налоги;
|
||
- увидеть структуру портфеля в компактном bar-chart виде;
|
||
- увидеть последние уже произошедшие события/денежные операции по счёту;
|
||
- перейти в подробные вкладки `Акции`, `Облигации`, `Операции`, `События`, `Аналитика`.
|
||
|
||
## Область изменений
|
||
|
||
Входит в scope:
|
||
|
||
- frontend-маршрут `/broker/:accountId` и виджеты `widgets/broker-dashboard`;
|
||
- layout заголовка счёта в `widgets/broker-account-layout`;
|
||
- backend read endpoints брокерского домена, необходимые для честного отображения HTML parity;
|
||
- OpenAPI/codegen для новых или расширенных DTO;
|
||
- тесты backend/frontend и визуальная проверка desktop/mobile.
|
||
|
||
Не входит в scope:
|
||
|
||
- редизайн подробных вкладок `Акции`, `Облигации`, `Операции`, `События`, `Аналитика`;
|
||
- dark mode;
|
||
- demo-переключатель `Данные / Загрузка` из HTML-эталона;
|
||
- полноценное хранилище исторических снапшотов портфеля;
|
||
- изменение URL-структуры `/broker/:accountId/*`.
|
||
|
||
## Требования
|
||
|
||
### 1. Общая композиция
|
||
|
||
- `/broker/:accountId` остаётся overview выбранного брокерского счёта.
|
||
- Страница использует порядок блоков из HTML-эталона:
|
||
`Заголовок счёта`, `Стоимость портфеля за 6 месяцев`, `Аналитика доходности`, `Структура`,
|
||
`Последние события`.
|
||
- Отдельный hero KPI-блок удаляется из overview-композиции.
|
||
- Блок `Доходы` удаляется из overview-композиции. Подробные доходные операции остаются доступны через
|
||
вкладку `Операции`.
|
||
- Существующая навигация счёта остаётся горизонтальными вкладками над dashboard-контентом.
|
||
- На desktop и mobile блоки идут одной колонкой в одинаковом порядке.
|
||
- Production UI не содержит demo-toggle `Данные / Загрузка`.
|
||
|
||
### 2. Заголовок счёта
|
||
|
||
- Заголовок показывает название счёта или fallback `Брокерский счёт`.
|
||
- Справа от заголовка, визуально меньшим блоком, показывается доходность счёта:
|
||
`Доходность`, значение процента и supporting text `За день: ...`.
|
||
- Доходность берётся из `analytics.totalReturnPercent`, если доступна, иначе из
|
||
`portfolio.yields.expectedPercent`, иначе отображается `—`.
|
||
- Дневное изменение берётся из `portfolio.yields.daily`, иначе отображается `—`.
|
||
- Отрицательная доходность окрашивается красным, положительная зелёным, недоступная нейтральным.
|
||
- В loading-состоянии заголовок не показывает skeleton-полосы. Layout должен сохранять стабильную
|
||
высоту и не прыгать после загрузки.
|
||
|
||
### 3. Стоимость портфеля за 6 месяцев
|
||
|
||
- Первый dashboard-блок называется `Стоимость портфеля за 6 месяцев`.
|
||
- Карточка использует зелёный градиентный фон и тонкую зелёную рамку как в HTML-эталоне.
|
||
- В карточке отображаются:
|
||
- label `Текущая стоимость`;
|
||
- текущая стоимость портфеля из `portfolio.totals.portfolio`;
|
||
- плавный line/area chart по 6 месячным точкам.
|
||
- График показывает ровно 6 подписей месяцев и 6 значений, соответствующих этим месяцам.
|
||
- Первая точка линии начинается у левого края области графика, последняя — у правого края.
|
||
- Visible point markers не отображаются; линия плавная.
|
||
- Подписи месяцев не выходят за границы карточки.
|
||
- Loading-состояние графика использует chart-like indicator без skeleton-подписей месяцев.
|
||
- Высота карточки в loading и loaded состояниях должна совпадать.
|
||
|
||
### 4. Аналитика доходности
|
||
|
||
- Блок называется `Аналитика доходности`.
|
||
- Верхний summary-row содержит только:
|
||
- `Стоимость портфеля`;
|
||
- `Всего доходов`.
|
||
- Detail-grid содержит ровно:
|
||
- `Пополнения`;
|
||
- `Выводы`;
|
||
- `Дивиденды`;
|
||
- `Купоны`;
|
||
- `Комиссия`;
|
||
- `Уплаченные налоги`.
|
||
- Поля `Нетто` и `Всего получено` не отображаются в overview-карточке.
|
||
- RUB отображается как `₽`, а не `RUB`.
|
||
- Положительные финансовые значения окрашиваются зелёным, отрицательные/уменьшающие баланс —
|
||
красным.
|
||
- `Комиссия` и `Уплаченные налоги` отображаются как отрицательные UI-суммы, даже если backend хранит
|
||
агрегат абсолютным положительным числом.
|
||
- Loading-состояние использует skeleton-карточки той же геометрии, чтобы интерфейс не прыгал.
|
||
|
||
### 5. Структура
|
||
|
||
- Блок называется `Структура`.
|
||
- Под заголовком показывается итоговая стоимость портфеля.
|
||
- Под итогом отображаются горизонтальные бары:
|
||
- `Акции`;
|
||
- `Облигации`;
|
||
- `Деньги`.
|
||
- Каждая строка показывает название сектора, сумму и процент.
|
||
- Цвета соответствуют HTML-эталону: акции — синий, облигации — янтарный, деньги — фиолетовый.
|
||
- Если значение сектора отсутствует или равно нулю, строка остаётся читаемой и не ломает layout.
|
||
- Общая карточка не содержит subtitle `Структура портфеля`.
|
||
|
||
### 6. Последние события
|
||
|
||
- Блок называется `Последние события`.
|
||
- Блок показывает только уже произошедшие записи, которые меняют или отражают денежный поток счёта.
|
||
- Источник данных — executed broker operations с категориями `income`, `tax`, `fee`.
|
||
- Записи сортируются от новых к старым.
|
||
- В overview показывается не более 7 последних записей.
|
||
- Таблица содержит колонки:
|
||
- `Дата`;
|
||
- `Инструмент`;
|
||
- `Тип`;
|
||
- `Сумма`.
|
||
- Колонка `Статус` не отображается.
|
||
- Toolbar фильтров, count badge и footer summary не отображаются в overview-карточке.
|
||
- В колонке `Инструмент` название отображается сверху жирным, ticker/ISIN снизу серым.
|
||
- Для операций без названия основная строка берётся из ticker/figi/description без дублирования в subtitle.
|
||
- Тип операции отображается бейджем: `Дивиденд`, `Купон`, `Погашение`, `Налог`, `Комиссия` или
|
||
безопасный fallback.
|
||
- Всё, что уменьшает баланс, отображается красным: сумма, типовой бейдж и tone строки/значения.
|
||
- Для налоговых операций в колонке `Тип` должен быть бейдж `Налог`, а не исходный income/coupon label.
|
||
- Блок содержит ссылку `Все события` на подробную вкладку `/broker/:accountId/events` или согласованный
|
||
detailed cashflow route, если реализация выделит его отдельно.
|
||
- Ошибка загрузки последних событий не ломает остальные блоки.
|
||
|
||
### 7. Backend API
|
||
|
||
- `BrokerAnalyticsDto` расширяется полями:
|
||
- `totalFees: number`;
|
||
- `totalTaxesPaid: number`.
|
||
- `totalFees` считается из executed broker operations категории `fee`.
|
||
- `totalTaxesPaid` считается из executed broker operations категории `tax`.
|
||
- Оба агрегата возвращаются абсолютными положительными числами; frontend отвечает за знак отображения.
|
||
- Добавляется endpoint:
|
||
`GET /api/v1/broker/accounts/:accountId/portfolio/history?months=6`.
|
||
- Endpoint возвращает envelope с данными:
|
||
- `accountId: string`;
|
||
- `points: Array<{ month: string; label: string; value: BrokerMoneyDto }>`;
|
||
- `asOf: string`.
|
||
- `month` имеет формат `YYYY-MM`.
|
||
- `label` содержит короткое русское имя месяца для оси.
|
||
- `points.length` равен запрошенному `months`, по умолчанию 6.
|
||
- Для v1 допускается estimated read-model из текущей стоимости портфеля и executed operations. Shape
|
||
контракта должен позволять позже заменить расчёт на реальные snapshots без изменения frontend.
|
||
- `BrokerOperationQueryDto` расширяется фильтром `categories?: string`.
|
||
- `categories` принимает comma-separated категории из существующего набора:
|
||
`trade,income,tax,fee,transfer,other`.
|
||
- `GET /broker/accounts/:accountId/operations` применяет `categories` до пагинации/лимита, чтобы overview
|
||
не фильтровал неполную страницу на клиенте.
|
||
|
||
### 8. Загрузка, ошибки и пустые состояния
|
||
|
||
- Ошибка portfolio остаётся page-level ошибкой.
|
||
- Ошибка analytics, history или latest events отображается только внутри соответствующей карточки.
|
||
- Недоступные значения отображаются как `—`, не подменяются нулём.
|
||
- Loading-состояния должны сохранять высоты карточек и не вызывать layout shift.
|
||
- На mobile не должно быть общего горизонтального overflow. Горизонтальный scroll допустим только внутри
|
||
таблицы, если без него невозможно сохранить читаемость.
|
||
|
||
## Acceptance Criteria
|
||
|
||
- `/broker/:accountId` отображает блоки в порядке:
|
||
`Стоимость портфеля за 6 месяцев`, `Аналитика доходности`, `Структура`, `Последние события`.
|
||
- Верхняя строка страницы показывает `Брокерский счёт` и доходность рядом с ним, без отдельного hero.
|
||
- Production UI не показывает demo-toggle `Данные / Загрузка`.
|
||
- График стоимости имеет 6 подписей месяцев и 6 точек данных; линия начинается слева и заканчивается
|
||
справа.
|
||
- Loading графика не содержит skeleton-подписей месяцев и имеет ту же высоту, что loaded состояние.
|
||
- Analytics overview показывает `Комиссия` и `Уплаченные налоги`, не показывает `Нетто` и
|
||
`Всего получено`.
|
||
- `Структура` показывает итоговую стоимость и бары `Акции`, `Облигации`, `Деньги`.
|
||
- `Последние события` показывает только произошедшие записи, отсортированные новые → старые.
|
||
- Таблица `Последние события` не содержит toolbar, count badge, footer summary и колонку `Статус`.
|
||
- Операции налогов/комиссий и другие списания отображаются красным.
|
||
- Backend Swagger содержит новые analytics fields, portfolio history endpoint и `categories` query.
|
||
- Frontend generated types обновлены через codegen.
|
||
- Desktop и mobile визуально соответствуют `docs/research/frontend-overview-redesign/example.html`.
|