Sergey Krylov d4d1dd980e feat: replace native date inputs with single DateCalendar field in broker dashboard
- Replace two MUI DatePicker components with a single visual range field
- Click opens Popover with community DateCalendar (no DateRangePicker/pro)
- First click sets start, second sets end; if end < start, range resets
- Remove isOpen/onToggle props — Popover managed locally
- Add BrokerDashboardTableSkeleton for events/income loading
- Set default weekly range (today-7d / today) for both events and income
- Remove 'Фильтр' label, rename 'Показать' → 'Применить период'
- Install @mui/x-date-pickers@7
- Update tests for new UI and skeleton loading states
- Align spec.md, plan.md, tasks.md with implementation
2026-06-27 11:48:10 +03:00

242 lines
22 KiB
Markdown
Raw 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.

# Редизайн обзора брокерского счёта в инвестиционный дашборд
Дата: 2026-06-26 (обновлено 2026-06-27)
Статус: согласовано к планированию
Эпик: [Портфель брокера](../../epics/BrokerPortfolio.md)
## Контекст
Текущий маршрут `/broker/:accountId` показывает корректный overview выбранного брокерского счёта, но
визуально остаётся вертикальным набором отдельных блоков: сводка, аллокация, карточки активов,
ближайшие события и последние операции. Пользователь подготовил прототип `temp.html`, где тот же домен
представлен как более плотный инвестиционный дашборд с hero KPI, компактными таблицами и аналитическими
карточками.
После обсуждения выбран вариант A: страница `/broker/:accountId` должна стать единым дашбордом,
используя структуру и плотность прототипа, но сохраняя текущую светлую тему и компоненты
`@moex-vibe/design-system`. Тёмная тема из прототипа не переносится в первую версию. При этом
двухколоночная desktop-композиция прототипа сознательно не переносится: и на desktop, и на mobile блоки
dashboard идут по одному на строке в фиксированном порядке.
## Цель
Сделать overview брокерского счёта быстрым обзором состояния портфеля, будущих/прошедших событий,
полученных доходов, аналитики доходности и аллокации без перехода по вкладкам.
## Пользовательский результат
Пользователь может на `/broker/:accountId`:
- сразу увидеть стоимость портфеля, доходность и сумму полученных доходов;
- увидеть ближайшие события по счёту в компактной таблице;
- увидеть последние доходные операции по дивидендам и купонам и итог по ним;
- оценить вложения, полученные выплаты и доходность по существующей аналитике;
- увидеть структуру портфеля через горизонтальные полосы аллокации и подписи к ним;
- перейти в существующие подробные вкладки `Акции`, `Облигации`, `Операции`, `События` и `Аналитика`
для drill-down сценариев.
## Область изменений
Фича относится только к frontend-маршруту `/broker/:accountId` и связанным frontend-компонентам
брокерского overview.
В область входят:
- новая dashboard-композиция для `BrokerAccountOverviewPage`;
- переиспользуемые frontend-паттерны для dashboard card, KPI, compact table и filter chips;
- адаптация существующих брокерских widgets под новую компоновку, если это нужно для читаемости;
- точечные доработки `@moex-vibe/design-system`, если существующие компоненты блокируют корректное
использование текущей светлой DS-темы;
- тесты новой композиции и ключевых представлений.
## Требования
### 1. Общая композиция
- `/broker/:accountId` остаётся overview выбранного брокерского счёта.
- Overview визуально становится dashboard-страницей, а не вертикальным списком независимых секций.
- Существующая навигация счёта отображается горизонтальными вкладками над контентом, чтобы не занимать
левую колонку и оставить больше пространства для таблиц dashboard.
- На desktop и mobile блоки dashboard отображаются по одному блоку на строке: hero KPI, `События`,
`Доходы`, `Аналитика доходности`, `Аллокация`.
- Существующая навигация счёта сохраняет ссылки на `Обзор`, `Акции`, `Облигации`, `Операции`,
`События`, `Аналитика`.
- На мобильном viewport сохраняется тот же порядок блоков в одну колонку.
### 2. Визуальный стиль
- Используется текущая светлая DS-тема MoexVibe.
- Не добавляется dark mode и не переносится тёмная палитра `temp.html`.
- Визуальная плотность, структура карточек, KPI-иерархия и компактность таблиц ориентируются на
`temp.html`.
- Дизайн использует компоненты и токены `@moex-vibe/design-system` там, где они применимы.
- Если DS-компонент слишком ограничен, допускается точечно расширить DS API или создать локальный
dashboard-pattern в frontend, но не добавлять доменную брокерскую логику в DS.
### 3. Hero KPI
Hero показывает:
- название счёта или fallback `Брокерский счёт`;
- стоимость портфеля из `portfolio.totals.portfolio`;
- доходность из существующих данных: приоритет `broker analytics.totalReturnPercent`, если загружена,
иначе `portfolio.yields.expectedPercent`, если доступна;
- дневное изменение из `portfolio.yields.daily` и `portfolio.yields.dailyPercent`, если доступно;
- всего полученных доходов из `broker analytics.totalReceived`, если аналитика загружена;
- спокойный fallback `—` для недоступных значений.
- Все Metric-блоки имеют одинаковую высоту в grid-ряду, даже если у некоторых есть supportingText. Поддерживающий текст остаётся внутри Metric, но все Metrics растягиваются на полную высоту grid-ячейки с выравниванием от верхнего края.
### 4. Блок `События`
- Блок использует существующий источник `useBrokerEvents(accountId, query)`.
- По умолчанию применяется период `сегодня - 7 дней` / `сегодня` и типы
`dividend,coupon,maturity,offer`, как в существующей вкладке событий.
- Блок содержит кликабельные chip-фильтры типов событий: `Дивиденды`, `Купоны`, `Погашения`, `Оферты`.
- Пользователь может выбрать несколько типов событий.
- Если пользователь снимает все типы событий, запрос не выполняется, а блок показывает
валидационное сообщение.
- Изменение chip-фильтров типов событий применяется сразу и возвращает локальную пагинацию на первую
страницу.
- Блок содержит фильтр периода `from` / `to`.
- По умолчанию применяется недельный период `сегодня - 7 дней` / `сегодня`.
- Изменение черновых фильтров не запускает запрос до применения периода пользователем.
- В пользовательском тексте шапки периода не используется слово `Фильтр`; UI должен считываться как
управление периодом за счёт иконки календаря, применённого диапазона, пресетов и affordance раскрытия.
- Блок содержит быстрые пресеты периода `7д`, `30д`, `90д`, `1г`, `Всё`, действие `Сбросить` и
основное действие применения периода с более понятным текстом, чем `Показать`.
- Период отображается как одно визуальное поле/кнопка с диапазоном, например `19 июн 26 июн`.
- По клику на поле периода открывается popover с community-компонентом MUI `DateCalendar` из
`@mui/x-date-pickers` и ручной логикой выбора начала/конца периода.
- `DateRangePicker` и пакет `@mui/x-date-pickers-pro` не используются.
- Native `<input type="date">` не используется.
- Применённые фильтры dashboard не обязаны синхронизироваться с URL; URL-синхронизация остаётся
обязанностью подробной вкладки `События`.
- В dashboard отображается не более 10 событий на странице.
- Если в выбранном диапазоне больше 10 событий, блок показывает локальную пагинацию по страницам.
- Смена применённых фильтров возвращает пагинацию блока на первую страницу.
- Таблица показывает дату, инструмент, тип, сумму и статус.
- Сумма для `actual` берётся из `actualAmount`, сумма для `forecast` берётся из `estimatedAmount`.
- Фактические поступления визуально отмечаются как `Поступило`.
- Прогнозные суммы помечаются как оценочные.
- Блок содержит ссылку на подробную вкладку `/broker/:accountId/events`.
- Ошибка загрузки событий не ломает остальной dashboard.
### 5. Блок `Доходы`
- Блок показывает последние доходные операции по дивидендам и купонам из существующего endpoint
операций.
- В первую версию входят операции с типами дивидендов и купонов, которые уже используются в backend
analytics: `OPERATION_TYPE_DIVIDEND`, `OPERATION_TYPE_DIV_EXT`, `OPERATION_TYPE_COUPON`.
- Блок содержит кликабельные chip-фильтры типов доходов: `Дивиденды`, `Купоны`.
- Пользователь может выбрать один или оба типа доходов.
- Если пользователь снимает все типы доходов, запрос не выполняется, а блок показывает
валидационное сообщение.
- Изменение chip-фильтров типов доходов применяется сразу и сбрасывает cursor-пагинацию.
- Блок содержит фильтр периода `from` / `to`.
- По умолчанию применяется недельный период `сегодня - 7 дней` / `сегодня`, чтобы dashboard не загружал
слишком много операций при первом открытии.
- Изменение черновых фильтров не запускает запрос до применения периода пользователем.
- В пользовательском тексте шапки периода не используется слово `Фильтр`; UI должен считываться как
управление периодом за счёт иконки календаря, применённого диапазона, пресетов и affordance раскрытия.
- Блок содержит быстрые пресеты периода `7д`, `30д`, `90д`, `1г`, `Всё`, действие `Сбросить` и
основное действие применения периода с более понятным текстом, чем `Показать`.
- Период отображается как одно визуальное поле/кнопка с диапазоном, например `19 июн 26 июн`.
- По клику на поле периода открывается popover с community-компонентом MUI `DateCalendar` из
`@mui/x-date-pickers` и ручной логикой выбора начала/конца периода.
- `DateRangePicker` и пакет `@mui/x-date-pickers-pro` не используются.
- Native `<input type="date">` не используется.
- Применённые фильтры dashboard не обязаны синхронизироваться с URL; URL-синхронизация остаётся
обязанностью подробных разделов.
- Для блока используется cursor-пагинация existing operations endpoint с размером страницы 10.
- Смена применённых фильтров сбрасывает cursor-пагинацию блока на первую страницу.
- Таблица показывает дату, инструмент, тип и сумму.
- Блок показывает итог по отображаемым доходным операциям.
- Блок содержит ссылку на подробную вкладку `/broker/:accountId/operations`.
- Если текущий endpoint операций не позволяет корректно получить доходные операции без изменения
backend-контракта, первая реализация должна явно зафиксировать это в `plan.md` перед изменением API.
### 6. Блок `Аналитика доходности`
- Блок использует существующий endpoint `/api/v1/broker/accounts/:accountId/analytics`.
- Отображаются: пополнения, выводы, нетто вложено, дивиденды, купоны, всего получено, доходность.
- При отсутствии analytics data блок показывает спокойное пустое состояние.
- Ошибка analytics не ломает остальные блоки.
### 7. Блок `Аллокация`
- Используется существующий расчёт `buildBrokerAllocation`.
- Вместо donut-диаграммы используется горизонтальный bar chart: каждый сектор — полоса с процентом,
подписью и суммой.
- Сверху блока показывается итоговая стоимость портфеля.
- Каждая полоса содержит: название сектора, долю в процентах, сумму в валюте.
- Цвета полос соответствуют существующей палитре аллокации (акции, облигации, ETF, деньги, прочие).
- Отрицательные значения отображаются текстом без полосы.
- Текст подписей контрастный и читаемый на всех цветах фона.
- Информация остаётся понятной без различения цветов.
### 8. Загрузка, ошибки и пустые состояния
- Первичная загрузка portfolio показывает dashboard skeleton соответствующей формы.
- Ошибка portfolio показывает ошибку overview, потому что без portfolio dashboard не имеет основного
контекста.
- Загрузка событий и доходов внутри карточек показывает skeleton таблицы соответствующей структуры, а не
только текстовую строку загрузки.
- Ошибка событий, доходов или analytics отображается внутри соответствующей карточки.
- Пустые события, пустые доходы и пустая analytics имеют отдельные понятные сообщения.
- Недоступные отдельные значения отображаются как `—`, не подменяются нулём.
## Ограничения
- Backend остаётся единственным клиентом T-Bank и MOEX.
- В первой версии не добавляется график истории стоимости портфеля.
- В первой версии не добавляется backend storage/API для снапшотов стоимости портфеля.
- Тёмная тема и переключатель темы не входят в область фичи.
- Не меняются правила расчёта доходности, событий, операций и аллокации.
- Не удаляются существующие detailed вкладки счёта.
- Не изменяется URL-структура `/broker/:accountId/*`.
## Backlog
Идея `Broker portfolio value history` вынесена в `docs/inbox.md` и `docs/roadmap.md`: хранить снапшоты
стоимости брокерского счёта и позже заменить отсутствие графика полноценным блоком `Стоимость портфеля`.
## Acceptance Criteria
- `/broker/:accountId` показывает dashboard-композицию: hero KPI, `События`, `Доходы`,
`Аналитика доходности`, `Аллокация`.
- Навигация счёта отображается горизонтальными вкладками над dashboard-контентом.
- На desktop и mobile блоки `События`, `Доходы`, `Аналитика доходности`, `Аллокация` расположены по
одному блоку на строке.
- На мобильном viewport dashboard читаемо перестраивается в одну колонку.
- Hero показывает стоимость портфеля, доходность или fallback, дневное изменение или fallback, всего
доходов или fallback.
- Все Metric-блоки hero имеют одинаковую высоту; supportingText не создаёт перекоса.
- Блок `События` использует существующие events data и показывает дату, инструмент, тип, сумму и статус.
- Блок `События` поддерживает multi-select chip-фильтр типов и локальную пагинацию по 10 событий.
- Блок `События` по умолчанию запрашивает период `сегодня - 7 дней` / `сегодня` и использует одно поле
периода с popover-календарём на базе community `DateCalendar`.
- Блок `Доходы` показывает доходные операции дивидендов и купонов и итог по отображаемым строкам.
- Блок `Доходы` поддерживает multi-select chip-фильтр типов и cursor-пагинацию по 10 операций.
- Блок `Доходы` по умолчанию запрашивает период `сегодня - 7 дней` / `сегодня` и использует одно поле
периода с popover-календарём на базе community `DateCalendar`.
- В шапке управления периодом не отображается слово `Фильтр`, а действие применения периода не называется
`Показать`.
- Загрузка событий и доходов отображается skeleton-таблицей.
- Блок `Аналитика доходности` показывает данные существующего analytics endpoint.
- Блок `Аллокация` показывает горизонтальные бары секторов с названием, долей и суммой, а также
итоговую стоимость портфеля.
- Ошибка одного вторичного блока не скрывает остальные блоки dashboard.
- Существующие detailed вкладки остаются доступны из навигации счёта.
- Первая версия не содержит график истории стоимости портфеля и не добавляет API для него.
- Дизайн использует светлую DS-тему, а не тёмную тему из `temp.html`.
## Вне области фичи
- график стоимости портфеля по датам;
- новые исторические снапшоты стоимости;
- dark mode;
- изменение backend-расчётов доходности;
- налоговая аналитика;
- экспорт dashboard;
- объединение нескольких брокерских счетов в один dashboard.