195 lines
12 KiB
Markdown
Raw Permalink 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.

# 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
## Целевая структура
```text
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
```text
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
```text
Route /bonds/:secid
→ pages/bond/ui/BondPage.tsx
→ useBond(secid) + useBondCandles(secid, interval, from, till)
→ widgets/bond-details + widgets/price-chart
```
### Search flow
```text
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 синхронизированы с новой структурой.