209 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Финальный редизайн обзора брокерского счёта по 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`.