# План реализации редизайна обзора брокерского счёта > **Для агентных исполнителей:** ОБЯЗАТЕЛЬНЫЙ SUB-SKILL: использовать `superpowers:subagent-driven-development` (предпочтительно) или `superpowers:executing-plans` для пошагового выполнения. Шаги ведутся чекбоксами `- [ ]`. **Цель:** превратить `/broker/:accountId` в компактный инвестиционный дашборд на текущей светлой теме без изменения backend-контрактов и URL-структуры. **Архитектура:** маршрут и FSD-границы остаются прежними. Композиция собирается в `widgets/broker-dashboard`, данные продолжают приходить из существующих `entities/*` hooks. Навигация счёта остаётся в `BrokerAccountLayout`, а dashboard использует только локальные presentation/helpers без выноса брокерской логики в design system. **Технологии:** React 18, TanStack Router, TanStack Query, MUI через `@moex-vibe/design-system`, MUI X DateCalendar community (`@mui/x-date-pickers`) с Day.js, Vitest, Testing Library. **HTML parity update:** согласованный визуальный эталон находится в `docs/research/2026-06-27-broker-account-redesign.html`. Следующая итерация переносит его детали в реальную страницу без изменения backend-контрактов. --- ## Область реализации ### Файлы - Modify: `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` — горизонтальные вкладки над контентом обзора и подробных разделов. - Modify: `apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx` — точка входа обзора через dashboard. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/index.ts` — публичный API dashboard-виджета. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` — верхнеуровневая композиция и управление filter state. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardHero.tsx` — hero KPI с fallback-значениями и выравниванием `Metric`. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx` — локальный паттерн карточки. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx` — карточка событий. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx` — переиспользуемое управление периодом с draft/apply поведением. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx` — карточка доходов. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx` — карточка аналитики доходности. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx` — карточка аллокации с горизонтальными барами. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardSkeleton.tsx` — skeleton-форма dashboard. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` — компонентные тесты композиции и фильтров. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/lib/dashboardIncome.ts` — чистые helpers доходных операций. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/lib/dashboardIncome.test.ts` — unit-тесты income helpers. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts` — пресеты дат, validate и mapping income types. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/lib/dashboardFormatters.ts` — локальные formatter/helpers для fallback и event labels. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts` — локальные helpers визуальной семантики dashboard: tone сумм, tone типов, отображение инструмента, символ валюты. - Create/Modify: `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts` — unit-тесты визуальных helpers. - Modify: `apps/frontend/package.json` — добавить community-пакет `@mui/x-date-pickers`, если зависимость ещё не подключена; `@mui/x-date-pickers-pro` не добавлять. - Modify: `docs/features/broker-dashboard-redesign/tasks.md` — фиксация статусов выполнения. ### Источники данных - Портфель: `useBrokerAccountContext().portfolio`. - События: `useBrokerEvents(accountId, { from, to, types })`. - Операции: `useBrokerOperations(accountId, { from, to, operationTypes, cursor, limit: 10 })`. - Аналитика: `useBrokerAnalytics(accountId)`. - Аллокация: `buildBrokerAllocation(portfolio)`. ## Технические решения ### 1. Композиция страницы - Dashboard на desktop и mobile строится в одну колонку: `Hero`, `События`, `Доходы`, `Аналитика доходности`, `Аллокация`. - Двухколоночный layout из прототипа не переносится. - Навигация счёта находится над контентом в `BrokerAccountLayout` и не дублируется внутри dashboard. ### 2. Hero KPI - Hero показывает название счёта, стоимость портфеля, доходность, дневное изменение и всего полученных доходов. - Приоритет доходности: `analytics.totalReturnPercent`, затем `portfolio.yields.expectedPercent`, иначе `—`. - Дневное изменение берётся из `portfolio.yields.daily` и `portfolio.yields.dailyPercent`, при недоступности показывается `—`. - Все `Metric` должны иметь одинаковую высоту; supporting text не должен ломать вертикальный ритм. ### 3. События - Типы событий переключаются chip-фильтрами с немедленным применением. - Диапазон дат по умолчанию: `сегодня - 7 дней` / `сегодня + 7 дней`, чтобы обзор сразу показывал ближайшие будущие события. - Диапазон дат редактируется отдельно от применённого состояния: draft state меняется локально, запрос уходит только по действию применения периода. - В шапке управления периодом не используется слово `Фильтр`; роль управления считывается через иконку календаря, применённый диапазон, раскрытие панели и пресеты. - Native `` не используется; период выбирается через одно визуальное поле и popover с community `DateCalendar` из `@mui/x-date-pickers`. - `DateRangePicker` и `@mui/x-date-pickers-pro` не используются; выбор начала/конца периода реализуется локальной логикой dashboard. - При пустом выборе типов запрос отключается, карточка показывает валидационное сообщение. - Пагинация локальная, по 10 событий на страницу, сбрасывается при смене применённых фильтров. - При загрузке карточка показывает skeleton таблицы событий с колонками дата, инструмент, тип, сумма, статус. - В toolbar карточки используются count badge, label `Тип`, кнопка `Обновить` и footer-summary по паттерну HTML-эталона. ### 4. Доходы - Доходы строятся на существующем endpoint операций только для `OPERATION_TYPE_DIVIDEND`, `OPERATION_TYPE_DIV_EXT`, `OPERATION_TYPE_COUPON`. - Типы доходов переключаются chip-фильтрами с немедленным применением. - Диапазон дат по умолчанию: `сегодня - 7 дней` / `сегодня`, чтобы не загружать большой объём операций на первом открытии. - Диапазон дат использует тот же draft/apply паттерн, что и события. - В шапке управления периодом не используется слово `Фильтр`; действие применения периода не называется `Показать`. - Native `` не используется; период выбирается через одно визуальное поле и popover с community `DateCalendar` из `@mui/x-date-pickers`. - `DateRangePicker` и `@mui/x-date-pickers-pro` не используются; выбор начала/конца периода реализуется локальной логикой dashboard. - Пагинация cursor-based, размер страницы 10, сбрасывается при смене применённых фильтров. - При загрузке карточка показывает skeleton таблицы доходов с колонками дата, инструмент, тип, сумма. ### 5. Аналитика и аллокация - Карточка аналитики использует существующий analytics endpoint и показывает спокойное empty state при отсутствии данных. - Карточка аллокации не использует donut chart. Она строит список горизонтальных bar rows по `buildBrokerAllocation`. - Отрицательные значения показываются текстом без полосы. ### 6. Ошибки и пустые состояния - Ошибка `portfolio` роняет весь обзор. - Ошибки `events`, `income`, `analytics` локальны соответствующим карточкам. - Пустые данные показываются отдельными сообщениями, а не нулевыми значениями. ### 7. HTML parity visual layer - Реальная страница `/broker/:accountId` должна визуально соответствовать `docs/research/2026-06-27-broker-account-redesign.html`, но использовать существующие React-компоненты и FSD-границы. - Не менять backend и OpenAPI: блок `Доходы` остаётся на текущем endpoint операций и текущем наборе income-типов. Отрицательный tone должен поддерживаться для строк, которые уже отображаются или будут отображаться без расширения контракта. - Ввести локальные helpers в `widgets/broker-dashboard/lib/dashboardVisual.ts`: `moneyTone(value, source?) -> 'positive' | 'negative' | 'planned' | 'neutral'`, `eventTypeTone(type)`, `incomeTypeTone(typeLabel)`, `formatDashboardCurrency(moneyOrValue)`, `instrumentDisplay({ ticker, name, description })`. - `formatDashboardCurrency` для RUB должен выводить `₽`. Для неизвестных валют использовать код валюты. - `instrumentDisplay` должен возвращать основную строку и опциональную подпись: для событий приоритет `ticker/isin` как main и `name` как subtitle; для операций приоритет `ticker` как main и `name/description` как subtitle. Если ticker отсутствует, main берётся из name/description, subtitle не дублируется. - `BrokerDashboardCard` должен поддержать компактный заголовок карточки уровня HTML-прототипа, не используя крупный `Heading size="title"`. - `BrokerDashboardDateFilter` должен использовать иконку раскрытия вместо текстового символа и сохранять единый toolbar-паттерн для `События` и `Доходы`. - Для `События` period presets должны поддерживать будущую часть диапазона, а не обрезаться текущим днём. - Таблицы `События` и `Доходы` должны иметь `thead`, type badges, двухстрочный инструмент при наличии названия и semantic amount colors. - `BrokerDashboardAnalyticsCard` должен окрашивать KPI-карточки по смыслу и показывать RUB через `₽`. - Skeleton таблиц событий и доходов должен использовать один компонент/паттерн и различаться только числом колонок. ## Задачи ### Задача 1: Базовые helpers и локальные dashboard-patterns **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardIncome.ts` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardIncome.test.ts` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardFormatters.ts` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx` - [ ] Убедиться, что income helpers покрывают только допустимые типы доходов и умеют считать итог отображаемых строк. - [ ] Убедиться, что filter helpers содержат default state, date presets, validate и mapping income type -> operation types. - [ ] Убедиться, что formatters покрывают fallback-значения, label типов событий и label статусов. - [ ] Использовать локальный `BrokerDashboardCard` как основной контейнер карточек; design system расширять только если без этого нельзя реализовать требования спецификации. ### Задача 2: Hero и layout overview **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardHero.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardSkeleton.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` - `apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx` - `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` - [ ] Собрать обзор через `BrokerDashboard` и `BrokerDashboardSkeleton`. - [ ] Оставить навигацию счёта в `BrokerAccountLayout` как горизонтальные вкладки над контентом. - [ ] Исправить hero так, чтобы все `Metric` были одной высоты и поддерживающий текст не поднимал одну ячейку выше остальных. - [ ] Проверить, что dashboard остаётся одноколоночным и на desktop, и на mobile. ### Задача 3: Карточка событий **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts` - `apps/frontend/package.json` - [ ] Добавить или подтвердить зависимость community-пакета `@mui/x-date-pickers` и использовать Day.js adapter. - [ ] Не добавлять `@mui/x-date-pickers-pro` и не использовать `DateRangePicker`. - [ ] Обновить `BrokerDashboardDateFilter`: убрать слово `Фильтр` из пользовательского текста, заменить `Показать` на понятное действие применения периода и визуально собрать chips, диапазон, сброс и применение в аккуратную шапку. - [ ] Заменить native date inputs на одно визуальное поле периода, которое открывает MUI `Popover` с community `DateCalendar`. - [ ] Реализовать локальную логику выбора диапазона: первый клик задаёт начало, второй — конец; если конец раньше начала, диапазон пересобирается от выбранной даты. - [ ] Настроить default range событий на `сегодня - 7 дней` / `сегодня + 7 дней`. - [ ] Подключить для событий draft/applied state: типы применяются сразу, даты только по действию применения периода. - [ ] Сохранять локальную пагинацию по 10 событий и сбрасывать её при смене применённых фильтров. - [ ] Заменить текстовую загрузку событий на skeleton таблицы. - [ ] Оставить локальные error/empty states внутри карточки. ### Задача 4: Карточка доходов **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardFilters.ts` - [ ] Подключить тот же `BrokerDashboardDateFilter` для доходов с draft/applied state. - [ ] Настроить default range доходов на `сегодня - 7 дней` / `сегодня` вместо периода с начала года. - [ ] Оставить chip-фильтры типов доходов с немедленным применением. - [ ] Сохранить cursor pagination по 10 операций и сбрасывать её при смене применённых фильтров. - [ ] Заменить текстовую загрузку доходов на skeleton таблицы. - [ ] Если endpoint операций даёт недостаточно релевантных строк для dashboard, зафиксировать ограничение в заметках по реализации, а не расширять backend в рамках этой фичи. ### Задача 5: Карточки аналитики и аллокации **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx` - [ ] Довести карточку аналитики до соответствия спецификации по полям и состояниям. - [ ] Заменить donut chart на горизонтальные бары, построенные из `buildBrokerAllocation`. - [ ] Отрицательные значения аллокации выводить отдельно текстом без bar. ### Задача 6: Тесты и верификация **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` - `docs/features/broker-dashboard-redesign/tasks.md` - [ ] Обновить компонентные тесты dashboard так, чтобы они покрывали текущую композицию, фильтры и основные empty/error states. - [ ] Обновить тесты, которые ищут `Фильтр дат` или `Показать`, под новые тексты и accessible labels единого поля периода. - [ ] Добавить/обновить тесты default weekly range для событий и доходов. - [ ] Добавить/обновить тесты skeleton-таблиц для loading state событий и доходов. - [ ] Прогнать `rtk npm run test:frontend -- --run src/widgets/broker-dashboard`. - [ ] Прогнать `rtk npm run test:frontend`. - [ ] Прогнать `rtk npm run test:design-system && rtk npm run lint -w apps/frontend && rtk npm run build:frontend`. - [ ] Проверить вручную desktop layout `/broker/2084014113`. - [ ] Проверить вручную mobile layout `/broker/2084014113` на viewport `390x844`. - [ ] После завершения обновить `docs/features/broker-dashboard-redesign/tasks.md` и выполнить `graphify update .`. ### Задача 7: Visual helpers для HTML parity **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.test.ts` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardIncome.ts` - `apps/frontend/src/widgets/broker-dashboard/lib/dashboardIncome.test.ts` - [ ] Добавить `moneyTone`, который возвращает `negative` для отрицательных значений, `positive` для положительных фактических значений, `planned` для прогнозных/нефактических значений и `neutral` для нуля/недоступного значения. - [ ] Добавить `formatDashboardCurrency`, который для `RUB` выводит `₽`, а для неизвестной валюты оставляет код валюты. - [ ] Добавить `instrumentDisplay` с приоритетами main/subtitle из технического решения 7. - [ ] Добавить type tone helpers для event types и income labels. - [ ] Расширить `DashboardIncomeRow`: хранить `instrumentMain` и `instrumentSubtitle`, сохранив совместимость через существующий `instrument` только если это нужно текущим тестам. - [ ] Покрыть helpers unit-тестами: RUB symbol, unknown currency fallback, negative/positive/planned tones, event/income type tones, отсутствие дублирования subtitle. ### Задача 8: Hero, карточка и toolbar parity **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardHero.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardDateFilter.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` - [ ] Применить semantic color к hero: отрицательная доходность красная, положительная зелёная, дневное изменение окрашивается по знаку, `Всего доходов` зелёный при значении больше нуля. - [ ] Сделать заголовки dashboard-card компактными, соответствующими HTML-прототипу. - [ ] Собрать filters area карточек в единый toolbar: слева типы, справа поле периода и действие. - [ ] Заменить текстовую стрелку раскрытия периода на иконку: использовать уже подключённые `CalendarTodayRounded` и `ExpandMoreRounded` из `@mui/icons-material`, без добавления новой icon dependency. - [ ] Обновить component tests: проверять отсутствие текста `RUB` для RUB-значений, отсутствие символа `v` в period button и наличие accessible name у управления периодом. ### Задача 9: Таблицы событий и доходов как в HTML **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardIncomeCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardTableSkeleton.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` - [ ] Добавить `thead` в обе dashboard-таблицы с колонками из спецификации. - [ ] В колонке инструмента выводить main и subtitle из `instrumentDisplay`. - [ ] Заменить текстовые типы на компактные бейджи с tone из visual helpers. - [ ] Окрашивать суммы через `moneyTone`: поступления зелёным, списания красным, прогноз/ожидание серым. - [ ] Окрашивать статусные бейджи: `Поступило` зелёный, прогноз/ожидание серый. - [ ] Сохранить горизонтальный scroll только внутри таблицы на mobile, без общего page overflow. - [ ] Обновить skeleton так, чтобы `События` и `Доходы` использовали один визуальный паттерн строк. - [ ] Обновить tests на наличие type badges, subtitle инструмента, semantic amount classes и skeleton. ### Задача 10: Analytics parity и визуальная проверка **Файлы:** - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx` - `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` - `docs/features/broker-dashboard-redesign/tasks.md` - [ ] Отображать RUB как `₽` во всех analytics KPI. - [ ] Окрашивать analytics cards: positive для пополнений/дивидендов/купонов/всего получено, negative для выводов и отрицательного нетто, neutral для нулевых/недоступных значений. - [ ] Обновить component tests для analytics: `₽` вместо `RUB`, positive/negative tone cards. - [ ] Прогнать `rtk npm run test:frontend -- --run src/widgets/broker-dashboard`. - [ ] Прогнать `rtk npm run test:frontend`. - [ ] Прогнать `rtk npm run lint -w apps/frontend && rtk npm run build:frontend`. - [ ] Проверить `/broker/2084014113` вручную на desktop и mobile `390x844` против `docs/research/2026-06-27-broker-account-redesign.html`. - [ ] После проверки обновить `docs/features/broker-dashboard-redesign/tasks.md`. ## Проверка покрытия спецификации - Общая композиция и горизонтальная навигация: задачи 2 и 6. - Hero KPI и equal-height `Metric`: задача 2. - События с chip filters, date filters и локальной пагинацией: задача 3. - Доходы с chip filters, date filters и cursor pagination: задача 4. - Community DateCalendar range control, weekly default range и skeleton loading tables: задачи 3, 4 и 6. - Аналитика и аллокация с горизонтальными барами: задача 5. - Ошибки, empty states, тесты и ручная верификация: задача 6. - HTML parity: visual helpers — задача 7, hero/card/toolbar — задача 8, dashboard tables — задача 9, analytics — задача 10.