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