16 KiB
Raw Permalink Blame History

Финальный редизайн обзора брокерского счёта по HTML-эталону

Дата: 2026-06-27 Статус: спецификация подготовлена Эпик: Портфель брокера

Контекст

Маршрут /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.