codex/frontend-fsd-final #29
@ -1,6 +1,6 @@
|
||||
import { Outlet, Link, useNavigate } from 'react-router-dom';
|
||||
import { SearchBar } from '@/widgets/search-bar';
|
||||
import { useSession } from '@/entities/session/model/useSession';
|
||||
import { useSession } from '@/entities/session';
|
||||
|
||||
export function AppLayout() {
|
||||
const { isAuthenticated, user, logout } = useSession();
|
||||
|
||||
@ -3,8 +3,8 @@ import { useContext } from 'react';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../test/server';
|
||||
import { SessionContext } from '@/entities/session/model/sessionContext';
|
||||
import { server } from '@/test/server';
|
||||
import { SessionContext } from '@/entities/session';
|
||||
import { SessionProvider } from './SessionProvider';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useState, useEffect, useCallback, type ReactNode } from 'react';
|
||||
import * as sessionApi from '@/entities/session/api/sessionApi';
|
||||
import { SessionContext, type SessionContextValue } from '@/entities/session/model/sessionContext';
|
||||
import * as sessionApi from '@/entities/session';
|
||||
import { SessionContext, type SessionContextValue } from '@/entities/session';
|
||||
import { setOnUnauthorized } from '@/shared/api/client';
|
||||
import type { UserResponse } from '@/shared/api/responses';
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import { ProfilePage } from '@/pages/profile';
|
||||
import { PortfoliosListPage, PortfolioDetailPage } from '@/pages/portfolios';
|
||||
import { ScreenerPage } from '@/pages/screener';
|
||||
import { BrokerAccountsPage } from '@/pages/broker-accounts';
|
||||
import { BrokerAccountLayout } from '@/entities/broker-account/ui/BrokerAccountLayout';
|
||||
import { BrokerAccountLayout } from '@/widgets/broker-account-layout';
|
||||
import { BrokerAccountOverviewPage } from '@/pages/broker-account';
|
||||
import { BrokerPositionsPage } from '@/pages/broker-positions';
|
||||
import { BrokerOperationsPage } from '@/pages/broker-operations';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Navigate, useLocation } from 'react-router-dom';
|
||||
import { useSession } from '@/entities/session/model/useSession';
|
||||
import { useSession } from '@/entities/session';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export function ProtectedRoute({ children }: { children: ReactNode }) {
|
||||
|
||||
@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { useBondCandles } from './useBondCandles';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
|
||||
10
apps/frontend/src/entities/search/api/searchApi.ts
Normal file
10
apps/frontend/src/entities/search/api/searchApi.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { request } from '@/shared/api/client';
|
||||
import type { SearchResultItem } from '@/shared/api/responses';
|
||||
|
||||
export function searchSecurities(q: string, type: 'all' | 'share' | 'bond' = 'all', limit = 20) {
|
||||
return request<SearchResultItem[]>('/api/v1/securities/search', {
|
||||
q,
|
||||
type,
|
||||
limit: String(limit),
|
||||
});
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
export { useSearch } from './model/useSearch';
|
||||
export { searchSecurities } from './api/searchApi';
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { searchSecurities } from '@/shared/api/client';
|
||||
import { searchSecurities } from '../api/searchApi';
|
||||
import type { SearchResultItem } from '@/shared/api/responses';
|
||||
|
||||
export function useSearch(query: string) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { setAccessToken, getAccessToken } from '@/shared/api/client';
|
||||
import { login, register, refresh, logout, getMe, updateProfile } from './sessionApi';
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { useStockCandles } from './useStockCandles';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { useStockDividends } from './useStockDividends';
|
||||
import { type ReactNode } from 'react';
|
||||
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { BrokerMoney, BrokerPortfolio } from '@/shared/api/responses';
|
||||
import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
||||
import { useBrokerOperations } from '../../../entities/broker-operation';
|
||||
import { useBrokerAccountContext } from '../../../entities/broker-account/ui/BrokerAccountLayout';
|
||||
import { BrokerAllocationChart } from '../../../widgets/broker-allocation-chart';
|
||||
import { BrokerOperationsTable } from '../../../widgets/broker-operations-table';
|
||||
import { useBrokerOperations } from '@/entities/broker-operation';
|
||||
import { useBrokerAccountContext } from '@/widgets/broker-account-layout';
|
||||
import { BrokerAllocationChart } from '@/widgets/broker-allocation-chart';
|
||||
import { BrokerOperationsTable } from '@/widgets/broker-operations-table';
|
||||
|
||||
function formatMoney(value: BrokerMoney | null | undefined) {
|
||||
if (!value) return '—';
|
||||
|
||||
@ -2,9 +2,9 @@ import {
|
||||
aggregateBrokerAccounts,
|
||||
useBrokerAccounts,
|
||||
useBrokerAccountPortfolios,
|
||||
} from '../../../entities/broker-account';
|
||||
import { BrokerAccountCard } from '../../../widgets/broker-account-card';
|
||||
import { BrokerAccountsSummary } from '../../../widgets/broker-accounts-summary';
|
||||
} from '@/entities/broker-account';
|
||||
import { BrokerAccountCard } from '@/widgets/broker-account-card';
|
||||
import { BrokerAccountsSummary } from '@/widgets/broker-accounts-summary';
|
||||
|
||||
function BrokerAccountsPageSkeleton() {
|
||||
return (
|
||||
|
||||
@ -4,9 +4,9 @@ import {
|
||||
BROKER_OPERATION_TYPE_OPTIONS,
|
||||
isBrokerOperationType,
|
||||
useBrokerOperations,
|
||||
} from '../../../entities/broker-operation';
|
||||
import { useBrokerAccountContext } from '../../../entities/broker-account/ui/BrokerAccountLayout';
|
||||
import { BrokerOperationsTable } from '../../../widgets/broker-operations-table';
|
||||
} from '@/entities/broker-operation';
|
||||
import { useBrokerAccountContext } from '@/widgets/broker-account-layout';
|
||||
import { BrokerOperationsTable } from '@/widgets/broker-operations-table';
|
||||
|
||||
export function BrokerOperationsPage() {
|
||||
const { accountId } = useBrokerAccountContext();
|
||||
|
||||
@ -6,8 +6,8 @@ import type {
|
||||
BrokerPositionsPage as BrokerPositionsPageData,
|
||||
} from '@/shared/api/responses';
|
||||
import { TableSkeleton } from '@/shared/ui/TableSkeleton';
|
||||
import { getBrokerInstrumentPath, useBrokerPositions } from '../../../entities/broker-position';
|
||||
import { useBrokerAccountContext } from '../../../entities/broker-account/ui/BrokerAccountLayout';
|
||||
import { getBrokerInstrumentPath, useBrokerPositions } from '@/entities/broker-position';
|
||||
import { useBrokerAccountContext } from '@/widgets/broker-account-layout';
|
||||
|
||||
const tableStyle = {
|
||||
width: '100%',
|
||||
|
||||
@ -3,9 +3,9 @@ import { screen } from '@testing-library/react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { LoginPage } from './ui/LoginPage';
|
||||
import { renderWithProviders } from '../../test/test-utils';
|
||||
import { renderWithProviders } from '@/test/test-utils';
|
||||
|
||||
const API = '/api/v1';
|
||||
|
||||
|
||||
@ -2,9 +2,9 @@ import { describe, it, expect } from 'vitest';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { ProfilePage } from './ui/ProfilePage';
|
||||
import { renderWithProviders } from '../../test/test-utils';
|
||||
import { renderWithProviders } from '@/test/test-utils';
|
||||
|
||||
const API = '/api/v1';
|
||||
|
||||
|
||||
@ -3,9 +3,9 @@ import { screen } from '@testing-library/react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { RegisterPage } from './ui/RegisterPage';
|
||||
import { renderWithProviders } from '../../test/test-utils';
|
||||
import { renderWithProviders } from '@/test/test-utils';
|
||||
|
||||
const API = '/api/v1';
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { http, HttpResponse } from 'msw';
|
||||
import { server } from '../../test/server';
|
||||
import { server } from '@/test/server';
|
||||
import { request, setAccessToken, getAccessToken, setOnUnauthorized } from './client';
|
||||
|
||||
const API = '/api/v1';
|
||||
|
||||
@ -1,10 +1,4 @@
|
||||
import type {
|
||||
ApiEnvelope,
|
||||
ApiResponseMeta,
|
||||
AuthResponse,
|
||||
SearchResultItem,
|
||||
HealthResponse,
|
||||
} from './responses';
|
||||
import type { ApiEnvelope, ApiResponseMeta, AuthResponse, HealthResponse } from './responses';
|
||||
|
||||
const BASE = '';
|
||||
|
||||
@ -130,15 +124,3 @@ export async function request<T>(
|
||||
export function getHealth(): Promise<{ data: HealthResponse; meta: ApiResponseMeta }> {
|
||||
return request<HealthResponse>('/api/v1/health');
|
||||
}
|
||||
|
||||
export function searchSecurities(
|
||||
q: string,
|
||||
type: 'all' | 'share' | 'bond' = 'all',
|
||||
limit = 20,
|
||||
): Promise<{ data: SearchResultItem[]; meta: ApiResponseMeta }> {
|
||||
return request<SearchResultItem[]>('/api/v1/securities/search', {
|
||||
q,
|
||||
type,
|
||||
limit: String(limit),
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
export {
|
||||
request,
|
||||
setAccessToken,
|
||||
getAccessToken,
|
||||
setOnUnauthorized,
|
||||
getHealth,
|
||||
searchSecurities,
|
||||
} from './client';
|
||||
export { request, setAccessToken, getAccessToken, setOnUnauthorized, getHealth } from './client';
|
||||
export type {
|
||||
ApiResponseMeta,
|
||||
ApiEnvelope,
|
||||
|
||||
@ -2,7 +2,7 @@ import { type ReactElement } from 'react';
|
||||
import { render, type RenderOptions } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { SessionProvider } from '../app/providers/SessionProvider';
|
||||
import { SessionProvider } from '@/app/providers/SessionProvider';
|
||||
|
||||
interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
|
||||
queryClient?: QueryClient;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
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 { buildBrokerAllocation } from '@/entities/broker-position';
|
||||
import { BrokerAllocationBar } from '@/widgets/broker-allocation-chart';
|
||||
|
||||
function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
|
||||
2
apps/frontend/src/widgets/broker-account-layout/index.ts
Normal file
2
apps/frontend/src/widgets/broker-account-layout/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { BrokerAccountLayout, useBrokerAccountContext } from './ui/BrokerAccountLayout';
|
||||
export type { BrokerAccountContext } from './ui/BrokerAccountLayout';
|
||||
@ -1,5 +1,5 @@
|
||||
import { NavLink, Outlet, useOutletContext, useParams } from 'react-router-dom';
|
||||
import { useBrokerPortfolio } from '../model/useBrokerPortfolio';
|
||||
import { useBrokerPortfolio } from '@/entities/broker-account';
|
||||
|
||||
export type BrokerAccountContext = {
|
||||
accountId: string;
|
||||
@ -1,7 +1,7 @@
|
||||
import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
||||
import type { BrokerAccountsAggregate } from '../../../entities/broker-account/model/brokerAccountsOverview';
|
||||
import { buildBrokerAllocation } from '../../../entities/broker-position';
|
||||
import { BrokerAllocationBar } from '../../../widgets/broker-allocation-chart';
|
||||
import type { BrokerAccountsAggregate } from '@/entities/broker-account/model/brokerAccountsOverview';
|
||||
import { buildBrokerAllocation } from '@/entities/broker-position';
|
||||
import { BrokerAllocationBar } from '@/widgets/broker-allocation-chart';
|
||||
|
||||
function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
import type { BrokerPortfolio } from '@/shared/api/responses';
|
||||
import {
|
||||
buildBrokerAllocation,
|
||||
type BrokerAllocationItem,
|
||||
} from '../../../entities/broker-position';
|
||||
import { buildBrokerAllocation, type BrokerAllocationItem } from '@/entities/broker-position';
|
||||
|
||||
const RADIUS = 44;
|
||||
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
|
||||
|
||||
@ -6,8 +6,8 @@ import {
|
||||
getBrokerOperationImpact,
|
||||
getBrokerOperationTypeLabel,
|
||||
type BrokerOperationImpact,
|
||||
} from '../../../entities/broker-operation';
|
||||
import { getBrokerInstrumentPath } from '../../../entities/broker-position';
|
||||
} from '@/entities/broker-operation';
|
||||
import { getBrokerInstrumentPath } from '@/entities/broker-position';
|
||||
|
||||
const tableStyle = {
|
||||
width: '100%',
|
||||
|
||||
108
docs/features/frontend-fsd-final/plan.md
Normal file
108
docs/features/frontend-fsd-final/plan.md
Normal file
@ -0,0 +1,108 @@
|
||||
# FSD Final Cleanup Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development to implement task-by-task.
|
||||
|
||||
**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
|
||||
|
||||
---
|
||||
|
||||
## File Structure Changes
|
||||
|
||||
### 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`
|
||||
|
||||
### Delete
|
||||
- `apps/frontend/src/entities/broker-account/ui/BrokerAccountLayout.tsx`
|
||||
- `apps/frontend/src/entities/broker-account/ui/` (if empty)
|
||||
|
||||
### Modify (20+ files)
|
||||
See per-task sections below.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Fix import aliases
|
||||
|
||||
### Task 1: Fix `../../../` → `@/` in 8 broker files
|
||||
|
||||
**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
|
||||
|
||||
Each replacement follows the same pattern: `../../../entities/...` → `@/entities/...` and `../../../widgets/...` → `@/widgets/...`.
|
||||
|
||||
For pages at depth 2 (`../../entities/`) — same prefix. Only files at depth 3 (`../../../`) exist in this set.
|
||||
|
||||
Note: 3 pages import `useBrokerAccountContext` from `../../../entities/broker-account/ui/BrokerAccountLayout` — these will be updated again in Phase 2 after the move.
|
||||
|
||||
## Phase 2: Move BrokerAccountLayout to widgets
|
||||
|
||||
### 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
|
||||
|
||||
## Verification
|
||||
|
||||
### Task 9: Run tests, lint, build
|
||||
|
||||
- `npm run lint -w apps/frontend`
|
||||
- `npm run test -w apps/frontend`
|
||||
- `npm run build -w apps/frontend`
|
||||
35
docs/features/frontend-fsd-final/spec.md
Normal file
35
docs/features/frontend-fsd-final/spec.md
Normal file
@ -0,0 +1,35 @@
|
||||
# FSD Final Cleanup
|
||||
|
||||
## 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.
|
||||
|
||||
## Requirements
|
||||
|
||||
### R1: All imports use `@/` path aliases
|
||||
No `../../../` or `../../` relative imports across the entire `src/` tree. Every import must use the `@/` alias pointing to `src/`.
|
||||
|
||||
### R2: BrokerAccountLayout lives in widgets layer
|
||||
The `BrokerAccountLayout` component (layout with routing + `useOutletContext`) currently lives in `entities/broker-account/ui/`. It must be moved to `widgets/broker-account-layout/` since it is a page layout, not a business entity.
|
||||
|
||||
### R3: entities/search has an `api/` layer
|
||||
The `searchSecurities` function — a domain API call — currently lives in `shared/api/client.ts`. It must be moved to `entities/search/api/searchApi.ts` so that each entity owns its domain calls.
|
||||
|
||||
### R4: App layer uses barrel imports
|
||||
Files in `app/` must import from entity barrel files (`@/entities/session`) rather than deep-importing into `model/` or `api/` subdirectories.
|
||||
|
||||
### R5: Tests use `@/` path aliases
|
||||
All test files must import `test/server` and `test/test-utils` via `@/` prefix instead of relative paths.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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
|
||||
28
docs/features/frontend-fsd-final/tasks.md
Normal file
28
docs/features/frontend-fsd-final/tasks.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Tasks: FSD Final Cleanup
|
||||
|
||||
## Phase 1: Fix import aliases
|
||||
|
||||
- [x] **Task 1:** Fix `../../../` → `@/` in 8 broker files (pages + widgets)
|
||||
|
||||
## Phase 2: Move BrokerAccountLayout to widgets
|
||||
|
||||
- [x] **Task 2:** Create `widgets/broker-account-layout/index.ts` and `ui/BrokerAccountLayout.tsx`
|
||||
- [x] **Task 3:** Update all imports referencing old path, delete `entities/broker-account/ui/BrokerAccountLayout.tsx`
|
||||
|
||||
## Phase 3: Add api/ to entities/search
|
||||
|
||||
- [x] **Task 4:** Create `entities/search/api/searchApi.ts` with `searchSecurities`
|
||||
- [x] **Task 5:** Update consumers, remove `searchSecurities` from shared
|
||||
|
||||
## Phase 4: Fix app layer deep imports
|
||||
|
||||
- [x] **Task 6:** Fix `AppLayout.tsx`, `SessionProvider.tsx`, `ProtectedRoute.tsx` to use `@/entities/session` barrel
|
||||
|
||||
## Phase 5: Fix test relative imports
|
||||
|
||||
- [x] **Task 7:** Fix `test/test-utils.tsx` relative import
|
||||
- [x] **Task 8:** Fix 9 test files with relative `test/` imports
|
||||
|
||||
## Verification
|
||||
|
||||
- [x] **Task 9:** Run tests, lint, build — all pass
|
||||
Loading…
x
Reference in New Issue
Block a user