From b9d7a6ad69a4b5a524cc5dae3ff771d0d3a99224 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Sat, 20 Jun 2026 21:57:04 +0300 Subject: [PATCH] refactor(frontend): complete FSD migration - import aliases, BrokerAccountLayout move, search api/ layer --- apps/frontend/src/app/layouts/AppLayout.tsx | 2 +- .../app/providers/SessionProvider.test.tsx | 4 +- .../src/app/providers/SessionProvider.tsx | 4 +- apps/frontend/src/app/routing/AppRoutes.tsx | 2 +- .../src/app/routing/ProtectedRoute.tsx | 2 +- .../bond/model/useBondCandles.test.tsx | 2 +- .../src/entities/search/api/searchApi.ts | 10 ++ apps/frontend/src/entities/search/index.ts | 1 + .../src/entities/search/model/useSearch.ts | 2 +- .../entities/session/api/sessionApi.test.ts | 2 +- .../stock/model/useStockCandles.test.tsx | 2 +- .../stock/model/useStockDividends.test.tsx | 2 +- .../ui/BrokerAccountOverviewPage.tsx | 8 +- .../broker-accounts/ui/BrokerAccountsPage.tsx | 6 +- .../ui/BrokerOperationsPage.tsx | 6 +- .../ui/BrokerPositionsPage.tsx | 4 +- .../src/pages/login/LoginPage.test.tsx | 4 +- .../src/pages/profile/ProfilePage.test.tsx | 4 +- .../src/pages/register/RegisterPage.test.tsx | 4 +- apps/frontend/src/shared/api/client.test.ts | 2 +- apps/frontend/src/shared/api/client.ts | 20 +--- apps/frontend/src/shared/api/index.ts | 9 +- apps/frontend/src/test/test-utils.tsx | 2 +- .../ui/BrokerAccountCard.tsx | 4 +- .../widgets/broker-account-layout/index.ts | 2 + .../ui/BrokerAccountLayout.tsx | 2 +- .../ui/BrokerAccountsSummary.tsx | 6 +- .../ui/BrokerAllocationChart.tsx | 5 +- .../ui/BrokerOperationsTable.tsx | 4 +- docs/features/frontend-fsd-final/plan.md | 108 ++++++++++++++++++ docs/features/frontend-fsd-final/spec.md | 35 ++++++ docs/features/frontend-fsd-final/tasks.md | 28 +++++ 32 files changed, 227 insertions(+), 71 deletions(-) create mode 100644 apps/frontend/src/entities/search/api/searchApi.ts create mode 100644 apps/frontend/src/widgets/broker-account-layout/index.ts rename apps/frontend/src/{entities/broker-account => widgets/broker-account-layout}/ui/BrokerAccountLayout.tsx (96%) create mode 100644 docs/features/frontend-fsd-final/plan.md create mode 100644 docs/features/frontend-fsd-final/spec.md create mode 100644 docs/features/frontend-fsd-final/tasks.md diff --git a/apps/frontend/src/app/layouts/AppLayout.tsx b/apps/frontend/src/app/layouts/AppLayout.tsx index 46f4408..fcdd1ff 100644 --- a/apps/frontend/src/app/layouts/AppLayout.tsx +++ b/apps/frontend/src/app/layouts/AppLayout.tsx @@ -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(); diff --git a/apps/frontend/src/app/providers/SessionProvider.test.tsx b/apps/frontend/src/app/providers/SessionProvider.test.tsx index f7750ce..3f13cf1 100644 --- a/apps/frontend/src/app/providers/SessionProvider.test.tsx +++ b/apps/frontend/src/app/providers/SessionProvider.test.tsx @@ -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'; diff --git a/apps/frontend/src/app/providers/SessionProvider.tsx b/apps/frontend/src/app/providers/SessionProvider.tsx index 871930d..39a305b 100644 --- a/apps/frontend/src/app/providers/SessionProvider.tsx +++ b/apps/frontend/src/app/providers/SessionProvider.tsx @@ -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'; diff --git a/apps/frontend/src/app/routing/AppRoutes.tsx b/apps/frontend/src/app/routing/AppRoutes.tsx index 3fdd335..71efb7d 100644 --- a/apps/frontend/src/app/routing/AppRoutes.tsx +++ b/apps/frontend/src/app/routing/AppRoutes.tsx @@ -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'; diff --git a/apps/frontend/src/app/routing/ProtectedRoute.tsx b/apps/frontend/src/app/routing/ProtectedRoute.tsx index d5fccb5..5232231 100644 --- a/apps/frontend/src/app/routing/ProtectedRoute.tsx +++ b/apps/frontend/src/app/routing/ProtectedRoute.tsx @@ -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 }) { diff --git a/apps/frontend/src/entities/bond/model/useBondCandles.test.tsx b/apps/frontend/src/entities/bond/model/useBondCandles.test.tsx index 08a159d..66807df 100644 --- a/apps/frontend/src/entities/bond/model/useBondCandles.test.tsx +++ b/apps/frontend/src/entities/bond/model/useBondCandles.test.tsx @@ -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'; diff --git a/apps/frontend/src/entities/search/api/searchApi.ts b/apps/frontend/src/entities/search/api/searchApi.ts new file mode 100644 index 0000000..5b7388e --- /dev/null +++ b/apps/frontend/src/entities/search/api/searchApi.ts @@ -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('/api/v1/securities/search', { + q, + type, + limit: String(limit), + }); +} diff --git a/apps/frontend/src/entities/search/index.ts b/apps/frontend/src/entities/search/index.ts index 06cdb77..f152f29 100644 --- a/apps/frontend/src/entities/search/index.ts +++ b/apps/frontend/src/entities/search/index.ts @@ -1 +1,2 @@ export { useSearch } from './model/useSearch'; +export { searchSecurities } from './api/searchApi'; diff --git a/apps/frontend/src/entities/search/model/useSearch.ts b/apps/frontend/src/entities/search/model/useSearch.ts index fb5de32..eafdc21 100644 --- a/apps/frontend/src/entities/search/model/useSearch.ts +++ b/apps/frontend/src/entities/search/model/useSearch.ts @@ -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) { diff --git a/apps/frontend/src/entities/session/api/sessionApi.test.ts b/apps/frontend/src/entities/session/api/sessionApi.test.ts index af29bfd..af9f582 100644 --- a/apps/frontend/src/entities/session/api/sessionApi.test.ts +++ b/apps/frontend/src/entities/session/api/sessionApi.test.ts @@ -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'; diff --git a/apps/frontend/src/entities/stock/model/useStockCandles.test.tsx b/apps/frontend/src/entities/stock/model/useStockCandles.test.tsx index be437f4..d106040 100644 --- a/apps/frontend/src/entities/stock/model/useStockCandles.test.tsx +++ b/apps/frontend/src/entities/stock/model/useStockCandles.test.tsx @@ -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'; diff --git a/apps/frontend/src/entities/stock/model/useStockDividends.test.tsx b/apps/frontend/src/entities/stock/model/useStockDividends.test.tsx index 9e899ae..5ca5b6c 100644 --- a/apps/frontend/src/entities/stock/model/useStockDividends.test.tsx +++ b/apps/frontend/src/entities/stock/model/useStockDividends.test.tsx @@ -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'; diff --git a/apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx b/apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx index 9c965af..3656712 100644 --- a/apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx +++ b/apps/frontend/src/pages/broker-account/ui/BrokerAccountOverviewPage.tsx @@ -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 '—'; diff --git a/apps/frontend/src/pages/broker-accounts/ui/BrokerAccountsPage.tsx b/apps/frontend/src/pages/broker-accounts/ui/BrokerAccountsPage.tsx index aaa0e05..ba3bff0 100644 --- a/apps/frontend/src/pages/broker-accounts/ui/BrokerAccountsPage.tsx +++ b/apps/frontend/src/pages/broker-accounts/ui/BrokerAccountsPage.tsx @@ -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 ( diff --git a/apps/frontend/src/pages/broker-operations/ui/BrokerOperationsPage.tsx b/apps/frontend/src/pages/broker-operations/ui/BrokerOperationsPage.tsx index 892eff7..a71ade0 100644 --- a/apps/frontend/src/pages/broker-operations/ui/BrokerOperationsPage.tsx +++ b/apps/frontend/src/pages/broker-operations/ui/BrokerOperationsPage.tsx @@ -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(); diff --git a/apps/frontend/src/pages/broker-positions/ui/BrokerPositionsPage.tsx b/apps/frontend/src/pages/broker-positions/ui/BrokerPositionsPage.tsx index 63f6cd1..041d298 100644 --- a/apps/frontend/src/pages/broker-positions/ui/BrokerPositionsPage.tsx +++ b/apps/frontend/src/pages/broker-positions/ui/BrokerPositionsPage.tsx @@ -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%', diff --git a/apps/frontend/src/pages/login/LoginPage.test.tsx b/apps/frontend/src/pages/login/LoginPage.test.tsx index 4932b74..ec78ee3 100644 --- a/apps/frontend/src/pages/login/LoginPage.test.tsx +++ b/apps/frontend/src/pages/login/LoginPage.test.tsx @@ -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'; diff --git a/apps/frontend/src/pages/profile/ProfilePage.test.tsx b/apps/frontend/src/pages/profile/ProfilePage.test.tsx index e4bc956..321d6b6 100644 --- a/apps/frontend/src/pages/profile/ProfilePage.test.tsx +++ b/apps/frontend/src/pages/profile/ProfilePage.test.tsx @@ -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'; diff --git a/apps/frontend/src/pages/register/RegisterPage.test.tsx b/apps/frontend/src/pages/register/RegisterPage.test.tsx index 832cb29..71f0eab 100644 --- a/apps/frontend/src/pages/register/RegisterPage.test.tsx +++ b/apps/frontend/src/pages/register/RegisterPage.test.tsx @@ -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'; diff --git a/apps/frontend/src/shared/api/client.test.ts b/apps/frontend/src/shared/api/client.test.ts index 1272a13..f1a6fc3 100644 --- a/apps/frontend/src/shared/api/client.test.ts +++ b/apps/frontend/src/shared/api/client.test.ts @@ -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'; diff --git a/apps/frontend/src/shared/api/client.ts b/apps/frontend/src/shared/api/client.ts index d5e60ba..38ec072 100644 --- a/apps/frontend/src/shared/api/client.ts +++ b/apps/frontend/src/shared/api/client.ts @@ -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( export function getHealth(): Promise<{ data: HealthResponse; meta: ApiResponseMeta }> { return request('/api/v1/health'); } - -export function searchSecurities( - q: string, - type: 'all' | 'share' | 'bond' = 'all', - limit = 20, -): Promise<{ data: SearchResultItem[]; meta: ApiResponseMeta }> { - return request('/api/v1/securities/search', { - q, - type, - limit: String(limit), - }); -} diff --git a/apps/frontend/src/shared/api/index.ts b/apps/frontend/src/shared/api/index.ts index 5d16891..f86b529 100644 --- a/apps/frontend/src/shared/api/index.ts +++ b/apps/frontend/src/shared/api/index.ts @@ -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, diff --git a/apps/frontend/src/test/test-utils.tsx b/apps/frontend/src/test/test-utils.tsx index 971b7da..7a765c8 100644 --- a/apps/frontend/src/test/test-utils.tsx +++ b/apps/frontend/src/test/test-utils.tsx @@ -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 { queryClient?: QueryClient; 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 c45a3f3..cf4f171 100644 --- a/apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx +++ b/apps/frontend/src/widgets/broker-account-card/ui/BrokerAccountCard.tsx @@ -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', { diff --git a/apps/frontend/src/widgets/broker-account-layout/index.ts b/apps/frontend/src/widgets/broker-account-layout/index.ts new file mode 100644 index 0000000..d78b2fc --- /dev/null +++ b/apps/frontend/src/widgets/broker-account-layout/index.ts @@ -0,0 +1,2 @@ +export { BrokerAccountLayout, useBrokerAccountContext } from './ui/BrokerAccountLayout'; +export type { BrokerAccountContext } from './ui/BrokerAccountLayout'; diff --git a/apps/frontend/src/entities/broker-account/ui/BrokerAccountLayout.tsx b/apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx similarity index 96% rename from apps/frontend/src/entities/broker-account/ui/BrokerAccountLayout.tsx rename to apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx index 6d784c6..5d34f26 100644 --- a/apps/frontend/src/entities/broker-account/ui/BrokerAccountLayout.tsx +++ b/apps/frontend/src/widgets/broker-account-layout/ui/BrokerAccountLayout.tsx @@ -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; 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 e5e4b7a..91b61b3 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 { 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', { 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 683bf22..786f948 100644 --- a/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx +++ b/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx @@ -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; diff --git a/apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx b/apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx index 3169d76..8b60681 100644 --- a/apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx +++ b/apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx @@ -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%', diff --git a/docs/features/frontend-fsd-final/plan.md b/docs/features/frontend-fsd-final/plan.md new file mode 100644 index 0000000..a17a0e7 --- /dev/null +++ b/docs/features/frontend-fsd-final/plan.md @@ -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` diff --git a/docs/features/frontend-fsd-final/spec.md b/docs/features/frontend-fsd-final/spec.md new file mode 100644 index 0000000..7ef4e28 --- /dev/null +++ b/docs/features/frontend-fsd-final/spec.md @@ -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 diff --git a/docs/features/frontend-fsd-final/tasks.md b/docs/features/frontend-fsd-final/tasks.md new file mode 100644 index 0000000..d81606d --- /dev/null +++ b/docs/features/frontend-fsd-final/tasks.md @@ -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