12 KiB
Raw Blame History

Frontend FSD Market Pages Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Перевести market pages (home, stock, bond) и связанные market widgets в FSD-структуру без изменения пользовательского поведения и с сохранением совместимости через shim-файлы.

Architecture: Route params и orchestration загрузки данных остаются в pages/*, а UI-композиция переносится в widgets/* с явным public API. Search query-hook выделяется в отдельный entities/search slice, старые entrypoints в components/, hooks/ и плоских pages/ превращаются в re-export shims, чтобы маршруты и тесты можно было переключать постепенно.

Tech Stack: React 18, TypeScript, React Router v6, TanStack Query v5, lightweight-charts v4, Vitest, Testing Library, ESLint, Vite, Docusaurus.


Базовое состояние

  • Feature branch: codex/frontend-fsd-market-pages
  • Baseline verification:
    • npm test -w apps/frontend — PASS (36 test files, 198 tests)
    • npm run lint -w apps/frontend — PASS
    • npm run build -w apps/frontend — PASS

Карта файлов

Create

  • apps/frontend/src/entities/search/index.ts — public API search slice
  • apps/frontend/src/entities/search/model/useSearch.ts — query hook для поиска бумаг
  • apps/frontend/src/entities/search/model/useSearch.test.tsx — тесты query hook после переноса
  • apps/frontend/src/widgets/search-bar/index.ts — public API search widget
  • apps/frontend/src/widgets/search-bar/ui/SearchBar.tsx — UI поиска с локальным state/dropdown
  • apps/frontend/src/widgets/search-bar/ui/SearchBar.test.tsx — widget tests после переноса
  • apps/frontend/src/widgets/price-chart/index.ts — public API chart widget
  • apps/frontend/src/widgets/price-chart/ui/PriceChart.tsx — chart widget implementation
  • apps/frontend/src/widgets/price-chart/ui/PriceChart.test.tsx — chart tests после переноса
  • apps/frontend/src/widgets/stock-details/index.ts — public API stock details widget
  • apps/frontend/src/widgets/stock-details/ui/StockDetails.tsx — stock details widget
  • apps/frontend/src/widgets/stock-details/ui/StockDetails.test.tsx — widget tests после переноса
  • apps/frontend/src/widgets/bond-details/index.ts — public API bond details widget
  • apps/frontend/src/widgets/bond-details/ui/BondDetails.tsx — bond details widget
  • apps/frontend/src/widgets/bond-details/ui/BondDetails.test.tsx — widget tests после переноса
  • apps/frontend/src/widgets/dividends-table/index.ts — public API dividends widget
  • apps/frontend/src/widgets/dividends-table/ui/DividendsTable.tsx — dividends table extracted from StockPage
  • apps/frontend/src/pages/home/index.ts — public API home page
  • apps/frontend/src/pages/home/ui/HomePage.tsx — home page entrypoint
  • apps/frontend/src/pages/stock/index.ts — public API stock page
  • apps/frontend/src/pages/stock/ui/StockPage.tsx — stock page entrypoint/orchestration
  • apps/frontend/src/pages/bond/index.ts — public API bond page
  • apps/frontend/src/pages/bond/ui/BondPage.tsx — bond page entrypoint/orchestration

Modify

  • apps/frontend/src/app/routing/AppRoutes.tsx — переключить route imports на @/pages/home, @/pages/stock, @/pages/bond
  • apps/frontend/src/app/layouts/AppLayout.tsx — переключить импорт SearchBar на @/widgets/search-bar
  • apps/frontend/src/components/SearchBar.tsx — re-export shim
  • apps/frontend/src/components/SearchBar.test.tsx — перенести/обновить импорт под новый widget path
  • apps/frontend/src/components/PriceChart.tsx — re-export shim
  • apps/frontend/src/components/PriceChart.test.tsx — перенести/обновить импорт под новый widget path
  • apps/frontend/src/components/StockDetails.tsx — re-export shim
  • apps/frontend/src/components/StockDetails.test.tsx — перенести/обновить импорт под новый widget path
  • apps/frontend/src/components/BondDetails.tsx — re-export shim
  • apps/frontend/src/components/BondDetails.test.tsx — перенести/обновить импорт под новый widget path
  • apps/frontend/src/hooks/useSearch.ts — re-export shim
  • apps/frontend/src/hooks/useSearch.test.tsx — перенести/обновить импорт под новый entity path
  • apps/frontend/src/pages/HomePage.tsx — re-export shim
  • apps/frontend/src/pages/HomePage.test.tsx — перенести/обновить импорт под новый page path
  • apps/frontend/src/pages/StockPage.tsx — re-export shim
  • apps/frontend/src/pages/StockPage.test.tsx — перенести/обновить импорт под новый page path
  • apps/frontend/src/pages/BondPage.tsx — re-export shim
  • apps/frontend/src/pages/BondPage.test.tsx — перенести/обновить импорт под новый page path
  • apps/docs/docs/frontend/overview.md — отразить FSD migration market pages
  • apps/docs/docs/frontend/components.md — обновить описание widget/component boundaries
  • apps/docs/docs/frontend/hooks.md — убрать market search из legacy hooks как primary location
  • apps/docs/docs/adr/ADR-014-frontend-fsd-market-pages.md — зафиксировать стратегию второй market-итерации FSD

Целевая структура

apps/frontend/src/
├── entities/
│   └── search/
│       ├── index.ts
│       └── model/
│           ├── useSearch.ts
│           └── useSearch.test.tsx
├── widgets/
│   ├── search-bar/
│   │   ├── index.ts
│   │   └── ui/
│   │       ├── SearchBar.tsx
│   │       └── SearchBar.test.tsx
│   ├── price-chart/
│   ├── stock-details/
│   ├── bond-details/
│   └── dividends-table/
└── pages/
    ├── home/
    │   ├── index.ts
    │   └── ui/HomePage.tsx
    ├── stock/
    │   ├── index.ts
    │   └── ui/StockPage.tsx
    └── bond/
        ├── index.ts
        └── ui/BondPage.tsx

Потоки данных

Stock page

Route /stocks/:secid
→ pages/stock/ui/StockPage.tsx
→ useStock(secid) + useStockCandles(secid, interval, from, till) + useStockDividends(secid)
→ widgets/stock-details + widgets/price-chart + widgets/dividends-table

Bond page

Route /bonds/:secid
→ pages/bond/ui/BondPage.tsx
→ useBond(secid) + useBondCandles(secid, interval, from, till)
→ widgets/bond-details + widgets/price-chart

Search flow

AppLayout
→ widgets/search-bar/ui/SearchBar.tsx
→ entities/search/model/useSearch(debouncedQuery)
→ shared/api/client.searchSecurities(query)
→ navigate('/stocks/:secid' | '/bonds/:secid')

Этапы реализации

Phase 1: Search entity и SearchBar widget

  1. Создать entities/search/model/useSearch.ts, перенеся текущий hook из src/hooks/useSearch.ts без изменения queryKey, enabled, staleTime и response mapping.
  2. Создать entities/search/index.ts и экспортировать useSearch только через public API.
  3. Перенести SearchBar.tsx в widgets/search-bar/ui/SearchBar.tsx, сохранив локальное состояние query, debounced, open, обработчик клика вне dropdown и navigate-логику.
  4. Добавить widgets/search-bar/index.ts.
  5. Старые src/hooks/useSearch.ts и src/components/SearchBar.tsx превратить в re-export shims.
  6. Переместить и адаптировать тесты useSearch.test.tsx и SearchBar.test.tsx так, чтобы они проверяли новые public entrypoints.
  7. Переключить app/layouts/AppLayout.tsx на импорт @/widgets/search-bar.

Phase 2: Market widgets

  1. Перенести PriceChart.tsx в widgets/price-chart/ui/PriceChart.tsx без изменения работы с lightweight-charts.
  2. Перенести StockDetails.tsx и BondDetails.tsx в widgets/stock-details/ui/StockDetails.tsx и widgets/bond-details/ui/BondDetails.tsx.
  3. Создать widgets/dividends-table/ui/DividendsTable.tsx, выделив таблицу дивидендов из StockPage.tsx без изменения разметки, заголовков и форматирования значений.
  4. Добавить index.ts для каждого widget slice.
  5. Старые src/components/PriceChart.tsx, StockDetails.tsx, BondDetails.tsx превратить в re-export shims.
  6. Перенести widget tests рядом с новыми реализациями и оставить старые тестовые файлы либо как shim-import consumers, либо обновить их на новые public entrypoints без дублирования покрытия.

Phase 3: Market pages и route imports

  1. Создать pages/home/ui/HomePage.tsx и pages/home/index.ts, сохранив текущее статическое содержимое главной страницы.
  2. Создать pages/stock/ui/StockPage.tsx и pages/stock/index.ts, оставив в page orchestration:
    • чтение secid из useParams
    • расчёт from/till
    • вызовы useStock, useStockCandles, useStockDividends
    • состояния loading/not-found
    • передачу готовых props в widgets
  3. Создать pages/bond/ui/BondPage.tsx и pages/bond/index.ts по той же схеме для useBond и useBondCandles.
  4. Переключить app/routing/AppRoutes.tsx на новые page public entrypoints.
  5. Старые src/pages/HomePage.tsx, StockPage.tsx, BondPage.tsx превратить в re-export shims.
  6. Обновить page tests, чтобы они импортировали новые page entrypoints и подтверждали эквивалентность поведения.

Phase 4: Documentation, ADR и финальная verification

  1. Создать apps/docs/docs/adr/ADR-014-frontend-fsd-market-pages.md с секциями Context / Options / Decision / Consequences.
  2. Обновить apps/docs/docs/frontend/overview.md, components.md, hooks.md, чтобы опубликованная документация отражала market FSD slices и shim-стратегию coexistence.
  3. Проверить, что новые public API не требуют deep imports из model/ или ui/ во внешнем коде.
  4. Выполнить финальную verification:
    • npm test -w apps/frontend
    • npm run lint -w apps/frontend
    • npm run build -w apps/frontend
    • npm run build -w apps/docs

Риски и контрольные точки

  • PriceChart чувствителен к ref/effect lifecycle. При переносе важно не менять cleanup и порядок инициализации chart series.
  • SearchBar используется в AppLayout, поэтому ошибки в импорт-пути сразу затронут почти все route-level tests.
  • StockPage/BondPage вычисляют даты inline. В этой фиче это остаётся в page-layer и не выносится в shared util, чтобы не расширять scope.
  • Legacy shims должны оставаться тонкими re-export файлами без дополнительной логики.

Критерии завершения плана

  • Все acceptance criteria из spec.md сопоставлены задачам реализации.
  • Новая market-структура использует только public API между pages, widgets и entities.
  • Frontend tests, lint и build проходят после миграции.
  • ADR и frontend docs синхронизированы с новой структурой.