From c4b93f9f0c869ce049996988235cdce483262a50 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sat, 27 Jun 2026 19:22:54 +0300 Subject: [PATCH] docs: add broker overview html parity spec --- .../plan.md | 329 ++++++++++++++++++ .../spec.md | 208 +++++++++++ .../tasks.md | 117 +++++++ 3 files changed, 654 insertions(+) create mode 100644 docs/features/broker-account-overview-html-parity/plan.md create mode 100644 docs/features/broker-account-overview-html-parity/spec.md create mode 100644 docs/features/broker-account-overview-html-parity/tasks.md diff --git a/docs/features/broker-account-overview-html-parity/plan.md b/docs/features/broker-account-overview-html-parity/plan.md new file mode 100644 index 0000000..2426d00 --- /dev/null +++ b/docs/features/broker-account-overview-html-parity/plan.md @@ -0,0 +1,329 @@ +# Финальный HTML Parity брокерского overview — план реализации + +> **Для агентных исполнителей:** ОБЯЗАТЕЛЬНЫЙ SUB-SKILL: использовать +> `superpowers:subagent-driven-development` (предпочтительно) или `superpowers:executing-plans` для +> пошагового выполнения. Шаги ведутся чекбоксами `- [ ]`. + +**Цель:** привести `/broker/:accountId` к финальному HTML-эталону +`docs/research/frontend-overview-redesign/example.html` без изменения URL-структуры счёта. + +**Архитектура:** backend расширяет существующий broker read API минимальными агрегатами и историей +стоимости. Frontend остаётся в FSD-границах `entities/broker-*`, `widgets/broker-dashboard`, +`widgets/broker-account-layout`; dashboard-композиция меняется с промежуточной версии на финальную +HTML parity структуру. + +**Технологии:** NestJS, Prisma/T-Bank broker operations read path, Swagger/OpenAPI codegen, React 18, +TanStack Query, TanStack Router, MUI + `@moex-vibe/design-system`, Vitest, Testing Library. + +--- + +## Canonical Research Source + +- Использовать как visual source of truth: + `docs/research/frontend-overview-redesign/example.html`. +- Не использовать как source of truth: + `docs/research/2026-06-27-broker-account-redesign.html`, пока файл не синхронизирован с финальным + макетом. +- Production UI не переносит demo-control `Данные / Загрузка`; этот control нужен только HTML-макету. + +## Файлы + +### Backend + +- Modify: `apps/backend/src/modules/tbank/dto/broker-analytics-response.dto.ts` — добавить + `totalFees`, `totalTaxesPaid`. +- Create: `apps/backend/src/modules/tbank/dto/broker-portfolio-history-response.dto.ts` — DTO для + 6-месячной истории стоимости. +- Modify: `apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts` — envelope для history endpoint. +- Modify: `apps/backend/src/modules/tbank/dto/broker-operation-query.dto.ts` — query `categories`. +- Modify: `apps/backend/src/modules/tbank/services/broker-analytics.service.ts` — агрегировать fee/tax. +- Create: `apps/backend/src/modules/tbank/services/broker-portfolio-history.service.ts` — read-model + истории стоимости. +- Modify: `apps/backend/src/modules/tbank/services/broker-operations.service.ts` — применять + category-фильтр до ответа. +- Modify: `apps/backend/src/modules/tbank/tbank.controller.ts` — endpoint portfolio history. +- Modify: backend tests рядом с изменёнными сервисами/controller. + +### Frontend data + +- Modify: `apps/frontend/src/shared/api/index.ts` — экспорт нового `BrokerPortfolioHistory`. +- Create: `apps/frontend/src/entities/broker-account/api/brokerPortfolioHistoryApi.ts`. +- Create: `apps/frontend/src/entities/broker-account/model/useBrokerPortfolioHistory.ts`. +- Modify: `apps/frontend/src/entities/broker-account/index.ts` — экспорт hook/API. +- Modify: `apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts` — query `categories`. + +### Frontend UI + +- Modify: `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` — title yield + рядом с заголовком счёта, без page-header skeleton. +- Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` — финальный порядок блоков + и источники данных. +- Replace/Remove: `BrokerDashboardHero.tsx` из overview-композиции; файл можно оставить только если + больше используется в тестах/экспортах, но в `/broker/:accountId` он не рендерится. +- Create: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerPortfolioHistoryCard.tsx`. +- Modify: `BrokerDashboardAnalyticsCard.tsx`, `BrokerDashboardAllocationCard.tsx`, + `BrokerDashboardEventsCard.tsx`, `BrokerDashboardSkeleton.tsx`. +- Modify/Create helpers в `apps/frontend/src/widgets/broker-dashboard/lib/` для chart points, latest + event rows, analytics display и visual tones. +- Modify: `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` и helper unit tests. + +### Docs + +- Modify: `docs/features/broker-account-overview-html-parity/tasks.md` по факту выполнения. +- Не переписывать `docs/features/broker-dashboard-redesign/*`; старая фича остаётся историей + промежуточной итерации. + +## Data Contracts + +### Analytics + +`BrokerAnalyticsDto` расширяется: + +```ts +totalFees: number +totalTaxesPaid: number +``` + +Расчёт: + +- `totalFees` = сумма absolute payment values executed операций категории `fee`; +- `totalTaxesPaid` = сумма absolute payment values executed операций категории `tax`; +- значения возвращаются как положительные агрегаты; +- frontend отображает их со знаком минус и negative tone. + +### Portfolio History + +Endpoint: + +```text +GET /api/v1/broker/accounts/:accountId/portfolio/history?months=6 +``` + +Response data: + +```ts +type BrokerPortfolioHistoryData = { + accountId: string + points: Array<{ + month: string + label: string + value: BrokerMoneyDto + }> + asOf: string +} +``` + +Правила v1: + +- `months` по умолчанию 6, допустимый диапазон 2-12; +- `points.length === months`; +- `month` в формате `YYYY-MM`; +- `label` — короткое русское имя месяца; +- последняя точка равна текущей `portfolio.totals.portfolio`; +- предыдущие точки могут быть estimated read-model из текущей стоимости и executed операций за период; +- контракт должен позволять позже заменить estimated calculation на persisted snapshots. + +### Operation Categories + +`BrokerOperationQueryDto` получает: + +```ts +categories?: string +``` + +Правила: + +- comma-separated значения из `trade,income,tax,fee,transfer,other`; +- неизвестные категории игнорировать или валидировать с `400`; выбрать один вариант и покрыть тестом; +- filtering выполняется до формирования page response; +- overview `Последние события` запрашивает `categories=income,tax,fee`, `state=OPERATION_STATE_EXECUTED`, + `limit=7`. + +## Implementation Tasks + +### Task 1: Docs and research alignment + +**Files:** +- `docs/features/broker-account-overview-html-parity/spec.md` +- `docs/features/broker-account-overview-html-parity/plan.md` +- `docs/features/broker-account-overview-html-parity/tasks.md` + +- Проверить, что docs ссылаются на `docs/research/frontend-overview-redesign/example.html`. +- Проверить, что docs явно запрещают использовать старый dated HTML как canonical reference. +- Зафиксировать финальный порядок блоков и отсутствие production demo-toggle. + +### Task 2: Backend analytics contract + +**Files:** +- `apps/backend/src/modules/tbank/dto/broker-analytics-response.dto.ts` +- `apps/backend/src/modules/tbank/services/broker-analytics.service.ts` +- `apps/backend/src/modules/tbank/services/broker-analytics.service.spec.ts` +- `apps/backend/src/modules/tbank/tbank.controller.spec.ts` + +- Добавить `totalFees`, `totalTaxesPaid` в DTO и тестовый response. +- Расширить analytics service наборами fee/tax типов через существующую категоризацию операций. +- Считать fee/tax только по executed операциям с `payment`. +- Возвращать округление до копеек аналогично текущим analytics агрегатам. + +### Task 3: Backend portfolio history endpoint + +**Files:** +- `apps/backend/src/modules/tbank/dto/broker-portfolio-history-response.dto.ts` +- `apps/backend/src/modules/tbank/dto/broker-envelope.dto.ts` +- `apps/backend/src/modules/tbank/services/broker-portfolio-history.service.ts` +- `apps/backend/src/modules/tbank/tbank.controller.ts` +- `apps/backend/src/modules/tbank/tbank.controller.spec.ts` + +- Добавить DTO для history point и envelope. +- Добавить service, который проверяет account access через `BrokerAccountsService`. +- Получить текущую стоимость через existing portfolio service или общий read path без дублирования T-Bank + вызовов сверх необходимого. +- Вернуть 6 monthly points для default query. +- Покрыть default months, invalid account и shape response тестами. + +### Task 4: Backend operation category filtering + +**Files:** +- `apps/backend/src/modules/tbank/dto/broker-operation-query.dto.ts` +- `apps/backend/src/modules/tbank/services/broker-operations.service.ts` +- `apps/backend/src/modules/tbank/services/broker-operations.service.spec.ts` + +- Добавить `categories` query. +- Применить category filtering к mapped operations page перед отдачей response. +- Если T-Bank page может содержать меньше 7 подходящих операций после фильтрации, documented v1 поведение: + endpoint возвращает подходящие операции из текущей fetched page; full backfill pagination не требуется. +- Покрыть `categories=income,tax,fee` и неизвестную категорию тестом. + +### Task 5: OpenAPI and frontend generated types + +**Files:** +- `apps/frontend/src/shared/api/types.ts` +- `apps/frontend/src/shared/api/index.ts` + +- Запустить backend dev server. +- Выполнить `npm run codegen -w apps/frontend`. +- Не редактировать generated `types.ts` вручную. +- Экспортировать новые frontend aliases из `shared/api/index.ts`. +- Проверить, что generated schemas содержат `totalFees`, `totalTaxesPaid`, + `BrokerPortfolioHistoryDataDto`. + +### Task 6: Frontend data hooks + +**Files:** +- `apps/frontend/src/entities/broker-account/api/brokerPortfolioHistoryApi.ts` +- `apps/frontend/src/entities/broker-account/model/useBrokerPortfolioHistory.ts` +- `apps/frontend/src/entities/broker-account/index.ts` +- `apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts` + +- Добавить `getBrokerPortfolioHistory(accountId, { months })`. +- Добавить `useBrokerPortfolioHistory(accountId, { months: 6 })` с query key + `['broker', 'portfolio-history', accountId, months]`. +- Добавить `categories` в `BrokerOperationQuery`. +- Не менять существующие hooks detailed вкладок. + +### Task 7: Page title yield + +**Files:** +- `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` +- `apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx` +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` + +- Перенести compact yield UI рядом с `Брокерский счёт`. +- Убрать отдельный hero KPI из overview. +- Не показывать page-title skeleton в loading state. +- Сохранить доступность: доходность имеет `aria-label="Доходность счёта"` или эквивалент. + +### Task 8: Portfolio history card + +**Files:** +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerPortfolioHistoryCard.tsx` +- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardPortfolioHistory.ts` +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardSkeleton.tsx` + +- Создать карточку `Стоимость портфеля за 6 месяцев`. +- Нарисовать SVG line/area chart без visible point markers. +- Использовать 6 backend points и подписи месяцев из response. +- Сделать loading chart indicator без skeleton месяцев. +- Обеспечить одинаковую min-height loaded/loading. +- Первая и последняя точки графика должны совпадать с краями области. + +### Task 9: Analytics card parity + +**Files:** +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAnalyticsCard.tsx` +- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts` +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` + +- Summary row: `Стоимость портфеля`, `Всего доходов`. +- Detail grid: `Пополнения`, `Выводы`, `Дивиденды`, `Купоны`, `Комиссия`, + `Уплаченные налоги`. +- Удалить `Нетто` и `Всего получено` из overview-card. +- Отображать `totalFees` и `totalTaxesPaid` как negative UI amounts. +- Сохранить skeleton геометрию 2 + 6 карточек. + +### Task 10: Allocation card parity + +**Files:** +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardAllocationCard.tsx` +- `apps/frontend/src/entities/broker-position/model/brokerAllocation.ts` + +- Переименовать карточку в `Структура`. +- Убрать subtitle `Структура портфеля`. +- Показать итоговую стоимость под заголовком. +- Отобразить только строки `Акции`, `Облигации`, `Деньги` для overview parity. +- Сохранить корректное поведение для отсутствующих/нулевых значений. + +### Task 11: Latest events card parity + +**Files:** +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboardEventsCard.tsx` +- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardEvents.ts` +- `apps/frontend/src/widgets/broker-dashboard/lib/dashboardVisual.ts` +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.tsx` + +- Переключить overview source с `useBrokerEvents` на `useBrokerOperations` с + `categories=income,tax,fee`, `limit=7`. +- Убрать filters toolbar, count badge, footer summary и колонку `Статус`. +- Переименовать карточку в `Последние события`. +- Отсортировать rows новые → старые. +- Инструмент: название сверху жирным, ticker/ISIN снизу серым. +- Тип: бейдж `Дивиденд`, `Купон`, `Погашение`, `Налог`, `Комиссия`. +- Налоговые/комиссионные/отрицательные операции отображать красным. + +### Task 12: Tests, build, visual QA + +**Files:** +- `apps/frontend/src/widgets/broker-dashboard/ui/BrokerDashboard.test.tsx` +- backend specs из предыдущих задач +- `docs/features/broker-account-overview-html-parity/tasks.md` + +- Backend targeted tests: + `npm run test -w apps/backend -- src/modules/tbank/services/broker-analytics.service.spec.ts src/modules/tbank/services/broker-operations.service.spec.ts src/modules/tbank/tbank.controller.spec.ts` +- Frontend targeted tests: + `npm run test -w apps/frontend -- --run src/widgets/broker-dashboard` +- Full checks: + `npm run test:frontend` + `npm run lint -w apps/frontend` + `npm run build:frontend` +- Visual QA: + desktop `/broker/:accountId`; + mobile viewport `390x844`; + compare against `docs/research/frontend-overview-redesign/example.html`. +- Update `tasks.md` statuses and notes after verification. + +## Risks and Decisions + +- History chart v1 uses estimated read-model; exact historical market value requires future snapshots. +- Category filtering after one fetched T-Bank page may underfill latest events; acceptable for v1 unless + testing shows too many empty overview rows. +- The HTML mock uses static values. React must match structure and visual behavior, not literal amounts. +- `Последние события` is intentionally based on executed operations, not calendar events, because the + final HTML shows already happened cashflow rows and removes status/forecast semantics. + +## Verification Matrix + +- Spec requirements map to tasks 2-11. +- Backend API requirements map to tasks 2-5. +- Frontend order and visual parity map to tasks 7-11. +- Loading stability and mobile overflow are verified in task 12. diff --git a/docs/features/broker-account-overview-html-parity/spec.md b/docs/features/broker-account-overview-html-parity/spec.md new file mode 100644 index 0000000..2dce3d3 --- /dev/null +++ b/docs/features/broker-account-overview-html-parity/spec.md @@ -0,0 +1,208 @@ +# Финальный редизайн обзора брокерского счёта по 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`. diff --git a/docs/features/broker-account-overview-html-parity/tasks.md b/docs/features/broker-account-overview-html-parity/tasks.md new file mode 100644 index 0000000..ae6cf45 --- /dev/null +++ b/docs/features/broker-account-overview-html-parity/tasks.md @@ -0,0 +1,117 @@ +# Финальный редизайн брокерского overview по HTML — задачи + +Дата: 2026-06-27 +Статус: документация подготовлена, реализация не начата + +## Документация и pre-flight + +- [x] Создать `docs/features/broker-account-overview-html-parity/spec.md`. +- [x] Создать `docs/features/broker-account-overview-html-parity/plan.md`. +- [x] Создать `docs/features/broker-account-overview-html-parity/tasks.md`. +- [x] Зафиксировать canonical visual reference: + `docs/research/frontend-overview-redesign/example.html`. +- [x] Зафиксировать, что `docs/research/2026-06-27-broker-account-redesign.html` не является + source of truth для этой итерации. +- [x] Зафиксировать финальный порядок блоков: + `Заголовок счёта → Стоимость портфеля за 6 месяцев → Аналитика доходности → Структура → Последние события`. +- [x] Зафиксировать, что production UI не переносит demo-toggle `Данные / Загрузка`. +- [ ] Перед началом реализации убедиться, что работа идёт в feature branch. +- [ ] Перед началом реализации запустить baseline checks текущей ветки. + +## Backend contract + +- [ ] Расширить `BrokerAnalyticsDto` полями `totalFees` и `totalTaxesPaid`. +- [ ] Обновить `BrokerAnalyticsService`: считать комиссии из executed операций категории `fee`. +- [ ] Обновить `BrokerAnalyticsService`: считать уплаченные налоги из executed операций категории `tax`. +- [ ] Обновить `broker-analytics.service.spec.ts` для новых агрегатов и округления. +- [ ] Добавить DTO для `BrokerPortfolioHistoryData`. +- [ ] Добавить envelope DTO для portfolio history endpoint. +- [ ] Добавить `BrokerPortfolioHistoryService`. +- [ ] Добавить endpoint `GET /api/v1/broker/accounts/:accountId/portfolio/history?months=6`. +- [ ] Покрыть portfolio history default months и response shape тестами. +- [ ] Добавить `categories?: string` в `BrokerOperationQueryDto`. +- [ ] Обновить `BrokerOperationsService`: применять category filtering для operations response. +- [ ] Покрыть `categories=income,tax,fee` и неизвестные категории тестами. +- [ ] Обновить `TBankController` и `tbank.controller.spec.ts` под новый endpoint/DTO. + +## OpenAPI и frontend data layer + +- [ ] Запустить backend dev server для Swagger JSON. +- [ ] Выполнить `npm run codegen -w apps/frontend`. +- [ ] Проверить, что generated types содержат `totalFees`, `totalTaxesPaid` и portfolio history schemas. +- [ ] Экспортировать новый `BrokerPortfolioHistory` alias из `apps/frontend/src/shared/api/index.ts`. +- [ ] Добавить `getBrokerPortfolioHistory`. +- [ ] Добавить `useBrokerPortfolioHistory`. +- [ ] Добавить `categories` в frontend `BrokerOperationQuery`. +- [ ] Не редактировать `apps/frontend/src/shared/api/types.ts` вручную. + +## Frontend composition + +- [ ] Перестроить `BrokerDashboard` на финальный порядок блоков. +- [ ] Убрать `BrokerDashboardHero` из overview-render path. +- [ ] Перенести compact yield UI в заголовок счёта рядом с `Брокерский счёт`. +- [ ] Убедиться, что page title loading state не показывает skeleton-полосы. +- [ ] Создать `BrokerPortfolioHistoryCard`. +- [ ] Подключить `useBrokerPortfolioHistory(accountId, { months: 6 })`. +- [ ] Заменить overview `BrokerDashboardIncomeCard` на `BrokerPortfolioHistoryCard`. +- [ ] Обновить `BrokerDashboardSkeleton` под финальный порядок и стабильные высоты. + +## Frontend visual parity + +- [ ] Карточка `Стоимость портфеля за 6 месяцев`: зелёный градиентный фон и зелёная рамка. +- [ ] График стоимости: 6 месячных значений, 6 подписей месяцев, плавная линия без visible markers. +- [ ] График стоимости: первая точка у левого края, последняя у правого края. +- [ ] Loading графика: chart-like indicator без skeleton месяцев. +- [ ] Analytics summary: только `Стоимость портфеля` и `Всего доходов`. +- [ ] Analytics detail grid: `Пополнения`, `Выводы`, `Дивиденды`, `Купоны`, `Комиссия`, + `Уплаченные налоги`. +- [ ] Analytics overview не показывает `Нетто` и `Всего получено`. +- [ ] `Комиссия` и `Уплаченные налоги` отображаются как отрицательные UI-суммы. +- [ ] Карточка структуры называется `Структура`. +- [ ] Карточка структуры не показывает subtitle `Структура портфеля`. +- [ ] Карточка структуры показывает итоговую стоимость под заголовком. +- [ ] Карточка структуры показывает бары `Акции`, `Облигации`, `Деньги`. +- [ ] Карточка последних событий называется `Последние события`. +- [ ] Последние события используют executed operations, а не calendar events. +- [ ] Последние события отсортированы новые → старые. +- [ ] Последние события не показывают toolbar, count badge, footer summary и колонку `Статус`. +- [ ] Инструмент в последних событиях: название сверху жирным, ticker/ISIN снизу серым. +- [ ] Налоги/комиссии/списания отображаются красным и с корректным бейджем типа. +- [ ] На mobile нет page-level horizontal overflow. + +## Tests + +- [ ] Backend targeted: + `npm run test -w apps/backend -- src/modules/tbank/services/broker-analytics.service.spec.ts src/modules/tbank/services/broker-operations.service.spec.ts src/modules/tbank/tbank.controller.spec.ts` +- [ ] Frontend targeted: + `npm run test -w apps/frontend -- --run src/widgets/broker-dashboard` +- [ ] Full frontend: + `npm run test:frontend` +- [ ] Frontend lint: + `npm run lint -w apps/frontend` +- [ ] Frontend build: + `npm run build:frontend` +- [ ] Проверить OpenAPI/codegen после backend изменений. + +## Visual QA + +- [ ] Проверить `/broker/:accountId` на desktop против + `docs/research/frontend-overview-redesign/example.html`. +- [ ] Проверить `/broker/:accountId` на viewport `390x844`. +- [ ] Проверить, что loading/loaded высоты карточек не вызывают layout shift. +- [ ] Проверить, что подписи месяцев графика не выходят за границы карточки. +- [ ] Проверить, что таблица последних событий читаема на mobile. + +## Definition of Done + +- [ ] Все acceptance criteria из `spec.md` выполнены. +- [ ] Backend tests проходят. +- [ ] Frontend targeted tests проходят. +- [ ] `npm run test:frontend` проходит. +- [ ] `npm run lint -w apps/frontend` проходит. +- [ ] `npm run build:frontend` проходит. +- [ ] Generated OpenAPI types обновлены через codegen. +- [ ] Visual QA desktop/mobile выполнена. +- [ ] Существующие detailed вкладки `Акции`, `Облигации`, `Операции`, `События`, `Аналитика` + остаются доступны. +- [ ] `tasks.md` обновлён по факту выполнения.