refactor(frontend): move BrokerAllocationBar to shared/ui, fix cross-entity imports and missing barrel exports
This commit is contained in:
parent
b9d7a6ad69
commit
2fd2f0611b
@ -1,7 +1,10 @@
|
|||||||
export { useBrokerAccounts } from './model/useBrokerAccounts';
|
export { useBrokerAccounts } from './model/useBrokerAccounts';
|
||||||
export { useBrokerAccountPortfolios } from './model/useBrokerAccountPortfolios';
|
export { useBrokerAccountPortfolios } from './model/useBrokerAccountPortfolios';
|
||||||
export { useBrokerPortfolio } from './model/useBrokerPortfolio';
|
export { useBrokerPortfolio } from './model/useBrokerPortfolio';
|
||||||
export { aggregateBrokerAccounts } from './model/brokerAccountsOverview';
|
export {
|
||||||
|
aggregateBrokerAccounts,
|
||||||
|
type BrokerAccountsAggregate,
|
||||||
|
} from './model/brokerAccountsOverview';
|
||||||
export {
|
export {
|
||||||
getBrokerAccounts,
|
getBrokerAccounts,
|
||||||
getBrokerPortfolio,
|
getBrokerPortfolio,
|
||||||
|
|||||||
@ -4,8 +4,8 @@ export {
|
|||||||
getBrokerOperationImpact,
|
getBrokerOperationImpact,
|
||||||
getBrokerOperationTypeLabel,
|
getBrokerOperationTypeLabel,
|
||||||
isBrokerOperationType,
|
isBrokerOperationType,
|
||||||
} from '../../broker-operation/model/operationFilters';
|
} from '@/entities/broker-operation';
|
||||||
export type { BrokerOperationImpact } from '../../broker-operation/model/operationFilters';
|
export type { BrokerOperationImpact } from '@/entities/broker-operation';
|
||||||
|
|
||||||
export type BrokerPositionGroup = 'shares' | 'bonds' | 'other';
|
export type BrokerPositionGroup = 'shares' | 'bonds' | 'other';
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
export { BrokerAllocationBar } from './ui/BrokerAllocationBar';
|
||||||
@ -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 <p className="broker-allocation-bar__empty">Нет данных для распределения</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="broker-allocation-bar">
|
||||||
|
<div className="broker-allocation-bar__track" role="img" aria-label={title}>
|
||||||
|
{positiveItems.map((item) => (
|
||||||
|
<span
|
||||||
|
key={item.key}
|
||||||
|
className="broker-allocation-bar__segment"
|
||||||
|
style={{ width: `${item.percent}%`, background: item.color }}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<ul className="broker-allocation-bar__legend" aria-label={`${title}: легенда`}>
|
||||||
|
{positiveItems.map((item) => (
|
||||||
|
<li className="broker-allocation-bar__legend-item" key={item.key}>
|
||||||
|
<span
|
||||||
|
className="broker-allocation-bar__swatch"
|
||||||
|
style={{ background: item.color }}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
<strong>{item.percent.toFixed(0)}%</strong>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@ import { Link } from 'react-router-dom';
|
|||||||
import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
||||||
import type { BrokerAccount, BrokerMoney, BrokerPortfolio } from '@/shared/api/responses';
|
import type { BrokerAccount, BrokerMoney, BrokerPortfolio } from '@/shared/api/responses';
|
||||||
import { buildBrokerAllocation } from '@/entities/broker-position';
|
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 {
|
function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||||
return new Intl.NumberFormat('ru-RU', {
|
return new Intl.NumberFormat('ru-RU', {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
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 { 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 {
|
function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||||
return new Intl.NumberFormat('ru-RU', {
|
return new Intl.NumberFormat('ru-RU', {
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export { BrokerAllocationBar, BrokerAllocationChart } from './ui/BrokerAllocationChart';
|
export { BrokerAllocationChart } from './ui/BrokerAllocationChart';
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { BrokerPortfolio } from '@/shared/api/responses';
|
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 RADIUS = 44;
|
||||||
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
|
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 <p className="broker-allocation-bar__empty">Нет данных для распределения</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="broker-allocation-bar">
|
|
||||||
<div className="broker-allocation-bar__track" role="img" aria-label={title}>
|
|
||||||
{positiveItems.map((item) => (
|
|
||||||
<span
|
|
||||||
key={item.key}
|
|
||||||
className="broker-allocation-bar__segment"
|
|
||||||
style={{ width: `${item.percent}%`, background: item.color }}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<ul className="broker-allocation-bar__legend" aria-label={`${title}: легенда`}>
|
|
||||||
{positiveItems.map((item) => (
|
|
||||||
<li className="broker-allocation-bar__legend-item" key={item.key}>
|
|
||||||
<span
|
|
||||||
className="broker-allocation-bar__swatch"
|
|
||||||
style={{ background: item.color }}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span>{item.label}</span>
|
|
||||||
<strong>{item.percent.toFixed(0)}%</strong>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BrokerAllocationChart({ portfolio }: { portfolio: BrokerPortfolio }) {
|
export function BrokerAllocationChart({ portfolio }: { portfolio: BrokerPortfolio }) {
|
||||||
const { sectors, negative } = buildBrokerAllocation(portfolio);
|
const { sectors, negative } = buildBrokerAllocation(portfolio);
|
||||||
const currency = allocationCurrency(portfolio);
|
const currency = allocationCurrency(portfolio);
|
||||||
|
|||||||
@ -4,104 +4,43 @@
|
|||||||
|
|
||||||
**Goal:** Complete FSD migration by fixing all remaining compliance gaps in apps/frontend/src/
|
**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.
|
**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.
|
||||||
|
|
||||||
**Tech Stack:** TypeScript, React, Feature-Sliced Design, Vitest
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## File Structure Changes
|
## Phase A: Move BrokerAllocationChart to shared/ui/
|
||||||
|
|
||||||
### Create
|
### Task A1: Create shared/ui/broker-allocation-chart
|
||||||
- `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`
|
|
||||||
|
|
||||||
### Delete
|
- `shared/ui/broker-allocation-chart/index.ts` — re-exports `BrokerAllocationBar` and `BrokerAllocationChart`
|
||||||
- `apps/frontend/src/entities/broker-account/ui/BrokerAccountLayout.tsx`
|
- `shared/ui/broker-allocation-chart/ui/BrokerAllocationChart.tsx` — copied from widgets/broker-allocation-chart, no code changes
|
||||||
- `apps/frontend/src/entities/broker-account/ui/` (if empty)
|
|
||||||
|
|
||||||
### Modify (20+ files)
|
### Task A2: Update consumers and delete old location
|
||||||
See per-task sections below.
|
|
||||||
|
|
||||||
---
|
- `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:**
|
- `entities/broker-position/model/brokerDisplay.ts`: `../../broker-operation/model/operationFilters` → `@/entities/broker-operation`
|
||||||
- `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
|
|
||||||
|
|
||||||
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-accounts-summary/ui/BrokerAccountsSummary.tsx`: `@/entities/broker-account/model/...` → `@/entities/broker-account`
|
||||||
|
|
||||||
- `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
|
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
### Task 9: Run tests, lint, build
|
### Task D1: Run tests, lint, build
|
||||||
|
|
||||||
- `npm run lint -w apps/frontend`
|
- `npm run lint -w apps/frontend`
|
||||||
- `npm run test -w apps/frontend`
|
- `npm run test -w apps/frontend`
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## Goal
|
## 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
|
## Requirements
|
||||||
|
|
||||||
@ -21,15 +21,24 @@ Files in `app/` must import from entity barrel files (`@/entities/session`) rath
|
|||||||
### R5: Tests use `@/` path aliases
|
### R5: Tests use `@/` path aliases
|
||||||
All test files must import `test/server` and `test/test-utils` via `@/` prefix instead of relative paths.
|
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
|
## Constraints
|
||||||
|
|
||||||
- Only modify imports and restructure entities/search and BrokerAccountLayout. Do not change business logic.
|
- Only modify imports and restructure components. Do not change business logic.
|
||||||
- Share `request()` from shared/api — `entities/search/api/searchApi.ts` imports `request` from shared.
|
- Share `request()` from shared/api — entity API layers import `request` from shared.
|
||||||
- `entities/broker-account/index.ts` barrel exports remain unchanged.
|
- Do not restructure code that is not part of the specified changes.
|
||||||
|
|
||||||
## Out of Scope
|
## Out of Scope
|
||||||
|
|
||||||
- Moving `styles.css` or `main.tsx` into `app/`
|
- Moving `styles.css` or `main.tsx` into `app/`
|
||||||
- Type deduplication (`responses.ts` vs `types.ts`)
|
- Type deduplication (`responses.ts` vs `types.ts`)
|
||||||
- Moving `src/test/` to `shared/lib/tests/`
|
- Moving `src/test/` to `shared/lib/tests/`
|
||||||
- Refactoring `entities/broker-account/model/brokerAccountsOverview` deep imports
|
- Refactoring other entity barrel exports
|
||||||
|
|||||||
@ -23,6 +23,20 @@
|
|||||||
- [x] **Task 7:** Fix `test/test-utils.tsx` relative import
|
- [x] **Task 7:** Fix `test/test-utils.tsx` relative import
|
||||||
- [x] **Task 8:** Fix 9 test files with relative `test/` imports
|
- [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
|
## Verification
|
||||||
|
|
||||||
- [x] **Task 9:** Run tests, lint, build — all pass
|
- [x] **Task D1:** Run tests, lint, build — all pass
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user