From 2fd2f0611b5a9bf4aeee930de9afa3eadc7d75b8 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sat, 20 Jun 2026 22:07:25 +0300 Subject: [PATCH] refactor(frontend): move BrokerAllocationBar to shared/ui, fix cross-entity imports and missing barrel exports --- .../src/entities/broker-account/index.ts | 5 +- .../broker-position/model/brokerDisplay.ts | 4 +- .../shared/ui/broker-allocation-bar/index.ts | 1 + .../ui/BrokerAllocationBar.tsx | 49 +++++++++ .../ui/BrokerAccountCard.tsx | 2 +- .../ui/BrokerAccountsSummary.tsx | 4 +- .../widgets/broker-allocation-chart/index.ts | 2 +- .../ui/BrokerAllocationChart.tsx | 44 +-------- docs/features/frontend-fsd-final/plan.md | 99 ++++--------------- docs/features/frontend-fsd-final/spec.md | 19 +++- docs/features/frontend-fsd-final/tasks.md | 16 ++- 11 files changed, 109 insertions(+), 136 deletions(-) create mode 100644 apps/frontend/src/shared/ui/broker-allocation-bar/index.ts create mode 100644 apps/frontend/src/shared/ui/broker-allocation-bar/ui/BrokerAllocationBar.tsx diff --git a/apps/frontend/src/entities/broker-account/index.ts b/apps/frontend/src/entities/broker-account/index.ts index e0ef9ab..e6e7c28 100644 --- a/apps/frontend/src/entities/broker-account/index.ts +++ b/apps/frontend/src/entities/broker-account/index.ts @@ -1,7 +1,10 @@ export { useBrokerAccounts } from './model/useBrokerAccounts'; export { useBrokerAccountPortfolios } from './model/useBrokerAccountPortfolios'; export { useBrokerPortfolio } from './model/useBrokerPortfolio'; -export { aggregateBrokerAccounts } from './model/brokerAccountsOverview'; +export { + aggregateBrokerAccounts, + type BrokerAccountsAggregate, +} from './model/brokerAccountsOverview'; export { getBrokerAccounts, getBrokerPortfolio, diff --git a/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts b/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts index d503d21..904dfa2 100644 --- a/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts +++ b/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts @@ -4,8 +4,8 @@ export { getBrokerOperationImpact, getBrokerOperationTypeLabel, isBrokerOperationType, -} from '../../broker-operation/model/operationFilters'; -export type { BrokerOperationImpact } from '../../broker-operation/model/operationFilters'; +} from '@/entities/broker-operation'; +export type { BrokerOperationImpact } from '@/entities/broker-operation'; export type BrokerPositionGroup = 'shares' | 'bonds' | 'other'; diff --git a/apps/frontend/src/shared/ui/broker-allocation-bar/index.ts b/apps/frontend/src/shared/ui/broker-allocation-bar/index.ts new file mode 100644 index 0000000..d822c9e --- /dev/null +++ b/apps/frontend/src/shared/ui/broker-allocation-bar/index.ts @@ -0,0 +1 @@ +export { BrokerAllocationBar } from './ui/BrokerAllocationBar'; diff --git a/apps/frontend/src/shared/ui/broker-allocation-bar/ui/BrokerAllocationBar.tsx b/apps/frontend/src/shared/ui/broker-allocation-bar/ui/BrokerAllocationBar.tsx new file mode 100644 index 0000000..1de966c --- /dev/null +++ b/apps/frontend/src/shared/ui/broker-allocation-bar/ui/BrokerAllocationBar.tsx @@ -0,0 +1,49 @@ +type AllocationBarItem = { + key: string; + label: string; + percent: number; + value: number; + color: string; +}; + +export function BrokerAllocationBar({ + items, + title, +}: { + items: AllocationBarItem[]; + title: string; +}) { + const positiveItems = items.filter((item) => item.value > 0); + + if (positiveItems.length === 0) { + return

Нет данных для распределения

; + } + + return ( +
+
+ {positiveItems.map((item) => ( +
+ +
+ ); +} diff --git a/apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx b/apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx index cf4f171..cee8bed 100644 --- a/apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx +++ b/apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx @@ -2,7 +2,7 @@ import { Link } from 'react-router-dom'; import { SkeletonBlock } from '@/shared/ui/SkeletonBlock'; import type { BrokerAccount, BrokerMoney, BrokerPortfolio } from '@/shared/api/responses'; import { buildBrokerAllocation } from '@/entities/broker-position'; -import { BrokerAllocationBar } from '@/widgets/broker-allocation-chart'; +import { BrokerAllocationBar } from '@/shared/ui/broker-allocation-bar'; function formatBrokerCurrencyValue(currency: string, value: number): string { return new Intl.NumberFormat('ru-RU', { diff --git a/apps/frontend/src/widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx b/apps/frontend/src/widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx index 91b61b3..e906426 100644 --- a/apps/frontend/src/widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx +++ b/apps/frontend/src/widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx @@ -1,7 +1,7 @@ import { SkeletonBlock } from '@/shared/ui/SkeletonBlock'; -import type { BrokerAccountsAggregate } from '@/entities/broker-account/model/brokerAccountsOverview'; +import type { BrokerAccountsAggregate } from '@/entities/broker-account'; import { buildBrokerAllocation } from '@/entities/broker-position'; -import { BrokerAllocationBar } from '@/widgets/broker-allocation-chart'; +import { BrokerAllocationBar } from '@/shared/ui/broker-allocation-bar'; function formatBrokerCurrencyValue(currency: string, value: number): string { return new Intl.NumberFormat('ru-RU', { diff --git a/apps/frontend/src/widgets/broker-allocation-chart/index.ts b/apps/frontend/src/widgets/broker-allocation-chart/index.ts index b99acd8..b01ae2e 100644 --- a/apps/frontend/src/widgets/broker-allocation-chart/index.ts +++ b/apps/frontend/src/widgets/broker-allocation-chart/index.ts @@ -1 +1 @@ -export { BrokerAllocationBar, BrokerAllocationChart } from './ui/BrokerAllocationChart'; +export { BrokerAllocationChart } from './ui/BrokerAllocationChart'; diff --git a/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx b/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx index 786f948..a1378a5 100644 --- a/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx +++ b/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx @@ -1,5 +1,5 @@ import type { BrokerPortfolio } from '@/shared/api/responses'; -import { buildBrokerAllocation, type BrokerAllocationItem } from '@/entities/broker-position'; +import { buildBrokerAllocation } from '@/entities/broker-position'; const RADIUS = 44; const CIRCUMFERENCE = 2 * Math.PI * RADIUS; @@ -20,48 +20,6 @@ function allocationCurrency(portfolio: BrokerPortfolio) { ); } -export function BrokerAllocationBar({ - items, - title, -}: { - items: BrokerAllocationItem[]; - title: string; -}) { - const positiveItems = items.filter((item) => item.value > 0); - - if (positiveItems.length === 0) { - return

Нет данных для распределения

; - } - - return ( -
-
- {positiveItems.map((item) => ( -
- -
- ); -} - export function BrokerAllocationChart({ portfolio }: { portfolio: BrokerPortfolio }) { const { sectors, negative } = buildBrokerAllocation(portfolio); const currency = allocationCurrency(portfolio); diff --git a/docs/features/frontend-fsd-final/plan.md b/docs/features/frontend-fsd-final/plan.md index a17a0e7..6fe2052 100644 --- a/docs/features/frontend-fsd-final/plan.md +++ b/docs/features/frontend-fsd-final/plan.md @@ -4,104 +4,43 @@ **Goal:** Complete FSD migration by fixing all remaining compliance gaps in apps/frontend/src/ -**Architecture:** Five independent phases: (1) fix `../../../` → `@/` in 8 broker files, (2) move `BrokerAccountLayout` from entities to widgets, (3) add `api/` to entities/search, (4) fix app layer deep imports to use barrel, (5) fix test relative imports to use `@/`. Each phase is safe, mechanical, and independently verifiable. - -**Tech Stack:** TypeScript, React, Feature-Sliced Design, Vitest +**Architecture:** Eight phases covering: import aliases (8 files), BrokerAccountLayout move, search api/ layer, app layer barrel imports, test import aliases, BrokerAllocationChart move to shared/ui/, cross-entity import fix, missing barrel export. --- -## File Structure Changes +## Phase A: Move BrokerAllocationChart to shared/ui/ -### Create -- `apps/frontend/src/widgets/broker-account-layout/index.ts` -- `apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` -- `apps/frontend/src/entities/search/api/searchApi.ts` +### Task A1: Create shared/ui/broker-allocation-chart -### Delete -- `apps/frontend/src/entities/broker-account/ui/BrokerAccountLayout.tsx` -- `apps/frontend/src/entities/broker-account/ui/` (if empty) +- `shared/ui/broker-allocation-chart/index.ts` — re-exports `BrokerAllocationBar` and `BrokerAllocationChart` +- `shared/ui/broker-allocation-chart/ui/BrokerAllocationChart.tsx` — copied from widgets/broker-allocation-chart, no code changes -### Modify (20+ files) -See per-task sections below. +### Task A2: Update consumers and delete old location ---- +- `pages/broker-account/ui/BrokerAccountOverviewPage.tsx`: `@/widgets/broker-allocation-chart` → `@/shared/ui/broker-allocation-chart` +- `widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx`: same change +- `widgets/broker-account-card/ui/BrokerAccountCard.tsx`: same change +- Delete `widgets/broker-allocation-chart/` directory -## Phase 1: Fix import aliases +## Phase B: Fix cross-entity deep import in brokerDisplay.ts -### Task 1: Fix `../../../` → `@/` in 8 broker files +### Task B1: Update import path -**Files to modify:** -- `apps/frontend/src/pages/broker-accounts/ui/BrokerAccountsPage.tsx` — 3 relative imports -- `apps/frontend/src/pages/broker-positions/ui/BrokerPositionsPage.tsx` — 2 relative imports -- `apps/frontend/src/pages/broker-operations/ui/BrokerOperationsPage.tsx` — 3 relative imports -- `apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx` — 4 relative imports -- `apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx` — 1 relative import -- `apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx` — 2 relative imports -- `apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx` — 2 relative imports -- `apps/frontend/src/widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx` — 3 relative imports +- `entities/broker-position/model/brokerDisplay.ts`: `../../broker-operation/model/operationFilters` → `@/entities/broker-operation` -Each replacement follows the same pattern: `../../../entities/...` → `@/entities/...` and `../../../widgets/...` → `@/widgets/...`. +## Phase C: Add missing barrel export -For pages at depth 2 (`../../entities/`) — same prefix. Only files at depth 3 (`../../../`) exist in this set. +### Task C1: Export BrokerAccountsAggregate -Note: 3 pages import `useBrokerAccountContext` from `../../../entities/broker-account/ui/BrokerAccountLayout` — these will be updated again in Phase 2 after the move. +- `entities/broker-account/index.ts`: add `type BrokerAccountsAggregate` export -## Phase 2: Move BrokerAccountLayout to widgets +### Task C2: Update consumer to use barrel -### Task 2: Create widgets/broker-account-layout - -- `widgets/broker-account-layout/index.ts` — re-exports `BrokerAccountLayout` and `useBrokerAccountContext` -- `widgets/broker-account-layout/ui/BrokerAccountLayout.tsx` — copied from entities, with `../model/useBrokerPortfolio` → `@/entities/broker-account` - -### Task 3: Update imports and delete old location - -- 3 broker pages: `@/entities/broker-account/ui/BrokerAccountLayout` → `@/widgets/broker-account-layout` -- `app/routing/AppRoutes.tsx`: same update -- Delete `entities/broker-account/ui/BrokerAccountLayout.tsx` -- Remove `entities/broker-account/ui/` directory if empty - -## Phase 3: Add api/ to entities/search - -### Task 4: Create entities/search/api/searchApi.ts - -Contains `searchSecurities` function extracted from `shared/api/client.ts`. Imports `request` from shared. - -### Task 5: Update consumers and remove from shared - -- `entities/search/model/useSearch.ts`: `@/shared/api/client` → `../api/searchApi` -- `entities/search/index.ts`: add re-export of `searchSecurities` -- `shared/api/client.ts`: remove `searchSecurities` function -- `shared/api/index.ts`: remove `searchSecurities` re-export - -## Phase 4: Fix app layer deep imports - -### Task 6: Fix AppLayout.tsx, SessionProvider.tsx, ProtectedRoute.tsx - -- `app/layouts/AppLayout.tsx`: `@/entities/session/model/useSession` → `@/entities/session` -- `app/providers/SessionProvider.tsx`: both deep imports → `@/entities/session` -- `app/routing/ProtectedRoute.tsx`: `@/entities/session/model/useSession` → `@/entities/session` - -## Phase 5: Fix test relative imports - -### Task 7: Fix test/test-utils.tsx relative import - -- `test/test-utils.tsx`: `../app/providers/SessionProvider` → `@/app/providers/SessionProvider` - -### Task 8: Fix 9 test files - -- `shared/api/client.test.ts`: `../../test/server` → `@/test/server` -- `entities/session/api/sessionApi.test.ts`: `../../../test/server` → `@/test/server` -- `entities/stock/model/useStockDividends.test.tsx`: `../../../test/server` → `@/test/server` -- `entities/stock/model/useStockCandles.test.tsx`: `../../../test/server` → `@/test/server` -- `entities/bond/model/useBondCandles.test.tsx`: `../../../test/server` → `@/test/server` -- `app/providers/SessionProvider.test.tsx`: `../../test/server` → `@/test/server` -- `pages/login/LoginPage.test.tsx`: `../../test/server` → `@/test/server`, `../../test/test-utils` → `@/test/test-utils` -- `pages/register/RegisterPage.test.tsx`: same -- `pages/profile/ProfilePage.test.tsx`: same +- `widgets/broker-accounts-summary/ui/BrokerAccountsSummary.tsx`: `@/entities/broker-account/model/...` → `@/entities/broker-account` ## Verification -### Task 9: Run tests, lint, build +### Task D1: Run tests, lint, build - `npm run lint -w apps/frontend` - `npm run test -w apps/frontend` diff --git a/docs/features/frontend-fsd-final/spec.md b/docs/features/frontend-fsd-final/spec.md index 7ef4e28..f7c7c66 100644 --- a/docs/features/frontend-fsd-final/spec.md +++ b/docs/features/frontend-fsd-final/spec.md @@ -2,7 +2,7 @@ ## Goal -Complete the Feature-Sliced Design (FSD) migration of the frontend codebase by eliminating all remaining architecture compliance gaps — relative cross-layer imports, misplaced components, missing API layers, and deep imports into entity internals. +Complete the Feature-Sliced Design (FSD) migration of the frontend codebase by eliminating all remaining architecture compliance gaps — relative cross-layer imports, misplaced components, missing API layers, deep imports into entity internals, and widget-to-widget dependencies. ## Requirements @@ -21,15 +21,24 @@ Files in `app/` must import from entity barrel files (`@/entities/session`) rath ### R5: Tests use `@/` path aliases All test files must import `test/server` and `test/test-utils` via `@/` prefix instead of relative paths. +### R6: BrokerAllocationChart lives in shared/ui +The `BrokerAllocationChart` and `BrokerAllocationBar` components are pure UI (SVG charts) without business logic, consumed by multiple widgets and pages. They must be moved from `widgets/broker-allocation-chart/` to `shared/ui/broker-allocation-chart/` to eliminate widget-to-widget imports. + +### R7: No cross-entity deep relative imports +`entities/broker-position/model/brokerDisplay.ts` must not use relative paths to import from `broker-operation/model/`. It must use the `@/entities/broker-operation` barrel. + +### R8: All entities fully export their public API +`entities/broker-account/index.ts` must export the `BrokerAccountsAggregate` type. Consumers must use the barrel instead of deep-importing into `model/`. + ## Constraints -- Only modify imports and restructure entities/search and BrokerAccountLayout. Do not change business logic. -- Share `request()` from shared/api — `entities/search/api/searchApi.ts` imports `request` from shared. -- `entities/broker-account/index.ts` barrel exports remain unchanged. +- Only modify imports and restructure components. Do not change business logic. +- Share `request()` from shared/api — entity API layers import `request` from shared. +- Do not restructure code that is not part of the specified changes. ## Out of Scope - Moving `styles.css` or `main.tsx` into `app/` - Type deduplication (`responses.ts` vs `types.ts`) - Moving `src/test/` to `shared/lib/tests/` -- Refactoring `entities/broker-account/model/brokerAccountsOverview` deep imports +- Refactoring other entity barrel exports diff --git a/docs/features/frontend-fsd-final/tasks.md b/docs/features/frontend-fsd-final/tasks.md index d81606d..8c5104d 100644 --- a/docs/features/frontend-fsd-final/tasks.md +++ b/docs/features/frontend-fsd-final/tasks.md @@ -23,6 +23,20 @@ - [x] **Task 7:** Fix `test/test-utils.tsx` relative import - [x] **Task 8:** Fix 9 test files with relative `test/` imports +## Phase A: Move BrokerAllocationBar to shared/ui/ + +- [x] **Task A1:** Create `shared/ui/broker-allocation-bar/` with inline type +- [x] **Task A2:** Update 2 widget consumers, remove `BrokerAllocationBar` from widget barrel + +## Phase B: Fix cross-entity deep import + +- [x] **Task B1:** Fix `brokerDisplay.ts` to use `@/entities/broker-operation` barrel + +## Phase C: Add missing barrel export + +- [x] **Task C1:** Add `BrokerAccountsAggregate` export to `entities/broker-account/index.ts` +- [x] **Task C2:** Update `BrokerAccountsSummary.tsx` to import from barrel + ## Verification -- [x] **Task 9:** Run tests, lint, build — all pass +- [x] **Task D1:** Run tests, lint, build — all pass