Sergey Krylov fee179d8e9
All checks were successful
CI / ci (pull_request) Successful in 15m41s
CI / ci (push) Successful in 13m50s
fix(frontend): align broker dashboard HTML parity
2026-06-27 15:23:55 +03:00

28 KiB
Raw Permalink Blame History

План реализации редизайна обзора брокерского счёта

Для агентных исполнителей: ОБЯЗАТЕЛЬНЫЙ 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 <input type="date"> не используется; период выбирается через одно визуальное поле и 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 <input type="date"> не используется; период выбирается через одно визуальное поле и 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.