From 9932278b640bc5f8cf9b0a2d347163b4d12d627e Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Tue, 23 Jun 2026 20:24:16 +0300 Subject: [PATCH] feat: complete Phase 3 API type unification, delete stale test file - Delete shared/api/responses.ts, move type re-exports to index.ts - Update all 55+ imports from shared/api/responses to shared/api - Delete stale client.test.ts (tested deleted client.ts) - Run biome checks and fix import ordering - Update tasks.md and plan.md to reflect actual approach --- .../src/app/providers/SessionProvider.tsx | 2 +- .../frontend/src/entities/bond/api/bondApi.ts | 4 +- .../src/entities/bond/model/useBond.ts | 2 +- .../src/entities/bond/model/useBondCandles.ts | 2 +- .../broker-account/api/brokerAccountApi.ts | 2 +- .../model/brokerAccountsOverview.test.ts | 2 +- .../model/brokerAccountsOverview.ts | 2 +- .../model/useBrokerAccountPortfolios.ts | 2 +- .../broker-account/model/useBrokerAccounts.ts | 2 +- .../model/useBrokerPortfolio.ts | 2 +- .../broker-event/api/brokerEventApi.ts | 2 +- .../broker-event/model/useBrokerEvents.ts | 2 +- .../api/brokerOperationApi.ts | 2 +- .../model/operationFilters.ts | 2 +- .../model/useBrokerOperations.ts | 2 +- .../broker-position/api/brokerPositionApi.ts | 2 +- .../model/brokerAllocation.test.ts | 2 +- .../broker-position/model/brokerAllocation.ts | 2 +- .../model/brokerDisplay.test.ts | 2 +- .../broker-position/model/brokerDisplay.ts | 2 +- .../model/useBrokerPositions.ts | 2 +- .../entities/portfolio/api/portfolioApi.ts | 7 +- .../entities/portfolio/model/usePortfolio.ts | 2 +- .../portfolio/model/usePortfolioAnalytics.ts | 2 +- .../entities/portfolio/model/usePortfolios.ts | 2 +- .../portfolio/model/usePositionMutations.ts | 2 +- .../src/entities/search/api/searchApi.ts | 2 +- .../src/entities/search/model/useSearch.ts | 2 +- .../src/entities/session/api/sessionApi.ts | 2 +- .../src/entities/session/api/tokenManager.ts | 2 +- .../entities/session/model/useSessionStore.ts | 2 +- .../src/entities/stock/api/stockApi.ts | 4 +- .../src/entities/stock/model/useStock.ts | 2 +- .../entities/stock/model/useStockCandles.ts | 2 +- .../entities/stock/model/useStockDividends.ts | 2 +- .../src/features/screener/api/screenerApi.ts | 2 +- .../features/screener/ui/ScreenerTable.tsx | 2 +- apps/frontend/src/shared/api/client.test.ts | 163 ------------------ apps/frontend/src/shared/api/index.ts | 96 +++++++---- apps/frontend/src/shared/api/responses.ts | 62 ------- apps/frontend/src/shared/lib/formatters.ts | 2 +- .../src/shared/lib/session-context.ts | 2 +- .../frontend/src/shared/lib/test/factories.ts | 2 +- apps/frontend/src/shared/lib/test/handlers.ts | 7 +- .../widgets/bond-details/ui/BondDetails.tsx | 2 +- .../ui/BondPositionRow.tsx | 2 +- .../ui/BondPositionTable.tsx | 2 +- .../ui/BrokerAccountCard.tsx | 2 +- .../ui/BrokerAllocationChart.tsx | 2 +- .../ui/BrokerOperationsTable.tsx | 2 +- .../broker-overview/ui/BrokerAssetCards.tsx | 2 +- .../broker-overview/ui/BrokerSummary.tsx | 2 +- .../ui/BrokerPositionTable.tsx | 2 +- .../ui/PositionTicker.tsx | 2 +- .../dividends-table/ui/DividendsTable.tsx | 2 +- .../ui/AnalyticsSummary.tsx | 2 +- .../portfolio-card/ui/PortfolioCard.tsx | 2 +- .../portfolio-form/ui/PortfolioForm.tsx | 2 +- .../portfolio-summary/ui/AllocationChart.tsx | 2 +- .../portfolio-summary/ui/PortfolioSummary.tsx | 2 +- .../ui/SharePositionRow.tsx | 2 +- .../ui/SharePositionTable.tsx | 2 +- .../widgets/stock-details/ui/StockDetails.tsx | 2 +- .../frontend-infrastructure-tooling/plan.md | 10 +- .../frontend-infrastructure-tooling/tasks.md | 12 +- 65 files changed, 136 insertions(+), 341 deletions(-) delete mode 100644 apps/frontend/src/shared/api/client.test.ts delete mode 100644 apps/frontend/src/shared/api/responses.ts diff --git a/apps/frontend/src/app/providers/SessionProvider.tsx b/apps/frontend/src/app/providers/SessionProvider.tsx index dc68d31..a4cbd2c 100644 --- a/apps/frontend/src/app/providers/SessionProvider.tsx +++ b/apps/frontend/src/app/providers/SessionProvider.tsx @@ -6,8 +6,8 @@ import { handleUnauthorized, setOnUnauthorized, } from '@/entities/session/api/tokenManager' +import type { UserResponse } from '@/shared/api' import { configureKyAuth } from '@/shared/api/kyClient' -import type { UserResponse } from '@/shared/api/responses' export function SessionProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null) diff --git a/apps/frontend/src/entities/bond/api/bondApi.ts b/apps/frontend/src/entities/bond/api/bondApi.ts index 2357a1f..4d505ce 100644 --- a/apps/frontend/src/entities/bond/api/bondApi.ts +++ b/apps/frontend/src/entities/bond/api/bondApi.ts @@ -1,11 +1,11 @@ -import { request } from '@/shared/api/kyClient' import type { ApiResponseMeta, BondHistoryItem, BondMarketData, BondResponse, CandleItem, -} from '@/shared/api/responses' +} from '@/shared/api' +import { request } from '@/shared/api/kyClient' export function getBond(secid: string): Promise<{ data: BondResponse; meta: ApiResponseMeta }> { return request(`/api/v1/securities/bonds/${encodeURIComponent(secid)}`) diff --git a/apps/frontend/src/entities/bond/model/useBond.ts b/apps/frontend/src/entities/bond/model/useBond.ts index 1ca08ee..60ead24 100644 --- a/apps/frontend/src/entities/bond/model/useBond.ts +++ b/apps/frontend/src/entities/bond/model/useBond.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { BondResponse } from '@/shared/api/responses' +import type { BondResponse } from '@/shared/api' import { getBond } from '../api/bondApi' export function useBond(secid: string) { diff --git a/apps/frontend/src/entities/bond/model/useBondCandles.ts b/apps/frontend/src/entities/bond/model/useBondCandles.ts index ebec92e..931b636 100644 --- a/apps/frontend/src/entities/bond/model/useBondCandles.ts +++ b/apps/frontend/src/entities/bond/model/useBondCandles.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { CandleItem } from '@/shared/api/responses' +import type { CandleItem } from '@/shared/api' import { getBondCandles } from '../api/bondApi' export function useBondCandles(secid: string, interval: '1h' | '24h', from: string, till: string) { diff --git a/apps/frontend/src/entities/broker-account/api/brokerAccountApi.ts b/apps/frontend/src/entities/broker-account/api/brokerAccountApi.ts index 814c017..67b9f4b 100644 --- a/apps/frontend/src/entities/broker-account/api/brokerAccountApi.ts +++ b/apps/frontend/src/entities/broker-account/api/brokerAccountApi.ts @@ -1,5 +1,5 @@ +import type { ApiResponseMeta, BrokerAccount, BrokerPortfolio } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { ApiResponseMeta, BrokerAccount, BrokerPortfolio } from '@/shared/api/responses' export type BrokerOperationQuery = { from?: string diff --git a/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.test.ts b/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.test.ts index 8cebcee..44c658e 100644 --- a/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.test.ts +++ b/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import type { BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerPortfolio } from '@/shared/api' import { aggregateBrokerAccounts } from '../model/brokerAccountsOverview' function portfolio( diff --git a/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.ts b/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.ts index 2990d89..209246b 100644 --- a/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.ts +++ b/apps/frontend/src/entities/broker-account/model/brokerAccountsOverview.ts @@ -1,4 +1,4 @@ -import type { BrokerMoney, BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerMoney, BrokerPortfolio } from '@/shared/api' export interface BrokerCurrencyAllocationSummary { shares: number diff --git a/apps/frontend/src/entities/broker-account/model/useBrokerAccountPortfolios.ts b/apps/frontend/src/entities/broker-account/model/useBrokerAccountPortfolios.ts index 1caad5f..bfca33a 100644 --- a/apps/frontend/src/entities/broker-account/model/useBrokerAccountPortfolios.ts +++ b/apps/frontend/src/entities/broker-account/model/useBrokerAccountPortfolios.ts @@ -1,5 +1,5 @@ import { useQueries } from '@tanstack/react-query' -import type { BrokerAccount, BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerAccount, BrokerPortfolio } from '@/shared/api' import { getBrokerPortfolio } from '../api/brokerAccountApi' export function useBrokerAccountPortfolios(accounts: BrokerAccount[]) { diff --git a/apps/frontend/src/entities/broker-account/model/useBrokerAccounts.ts b/apps/frontend/src/entities/broker-account/model/useBrokerAccounts.ts index ff10939..ff4a3b6 100644 --- a/apps/frontend/src/entities/broker-account/model/useBrokerAccounts.ts +++ b/apps/frontend/src/entities/broker-account/model/useBrokerAccounts.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { BrokerAccount } from '@/shared/api/responses' +import type { BrokerAccount } from '@/shared/api' import { getBrokerAccounts } from '../api/brokerAccountApi' export function useBrokerAccounts() { diff --git a/apps/frontend/src/entities/broker-account/model/useBrokerPortfolio.ts b/apps/frontend/src/entities/broker-account/model/useBrokerPortfolio.ts index b86ac98..f7a9a5b 100644 --- a/apps/frontend/src/entities/broker-account/model/useBrokerPortfolio.ts +++ b/apps/frontend/src/entities/broker-account/model/useBrokerPortfolio.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerPortfolio } from '@/shared/api' import { getBrokerPortfolio } from '../api/brokerAccountApi' export function useBrokerPortfolio(accountId: string | undefined) { diff --git a/apps/frontend/src/entities/broker-event/api/brokerEventApi.ts b/apps/frontend/src/entities/broker-event/api/brokerEventApi.ts index cba3d1e..6180d85 100644 --- a/apps/frontend/src/entities/broker-event/api/brokerEventApi.ts +++ b/apps/frontend/src/entities/broker-event/api/brokerEventApi.ts @@ -1,5 +1,5 @@ +import type { ApiResponseMeta, BrokerEventsData } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { ApiResponseMeta, BrokerEventsData } from '@/shared/api/responses' export type BrokerEventsQuery = { from: string diff --git a/apps/frontend/src/entities/broker-event/model/useBrokerEvents.ts b/apps/frontend/src/entities/broker-event/model/useBrokerEvents.ts index 7f1c74d..5efa7fe 100644 --- a/apps/frontend/src/entities/broker-event/model/useBrokerEvents.ts +++ b/apps/frontend/src/entities/broker-event/model/useBrokerEvents.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { BrokerEventsData } from '@/shared/api/responses' +import type { BrokerEventsData } from '@/shared/api' import { type BrokerEventsQuery, getBrokerEvents } from '../api/brokerEventApi' export function useBrokerEvents(accountId: string | undefined, query: BrokerEventsQuery) { diff --git a/apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts b/apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts index f0f1606..e5d5b2f 100644 --- a/apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts +++ b/apps/frontend/src/entities/broker-operation/api/brokerOperationApi.ts @@ -1,5 +1,5 @@ +import type { ApiResponseMeta, BrokerOperationsPage } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { ApiResponseMeta, BrokerOperationsPage } from '@/shared/api/responses' export type BrokerOperationQuery = { from?: string diff --git a/apps/frontend/src/entities/broker-operation/model/operationFilters.ts b/apps/frontend/src/entities/broker-operation/model/operationFilters.ts index b2c2d09..7510ed2 100644 --- a/apps/frontend/src/entities/broker-operation/model/operationFilters.ts +++ b/apps/frontend/src/entities/broker-operation/model/operationFilters.ts @@ -1,4 +1,4 @@ -import type { BrokerOperation } from '@/shared/api/responses' +import type { BrokerOperation } from '@/shared/api' export type BrokerOperationImpact = 'adds' | 'reduces' | 'neutral' | 'unknown' diff --git a/apps/frontend/src/entities/broker-operation/model/useBrokerOperations.ts b/apps/frontend/src/entities/broker-operation/model/useBrokerOperations.ts index a02ba05..20335c4 100644 --- a/apps/frontend/src/entities/broker-operation/model/useBrokerOperations.ts +++ b/apps/frontend/src/entities/broker-operation/model/useBrokerOperations.ts @@ -1,5 +1,5 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query' -import type { BrokerOperationsPage } from '@/shared/api/responses' +import type { BrokerOperationsPage } from '@/shared/api' import { type BrokerOperationQuery, getBrokerOperations } from '../api/brokerOperationApi' export function useBrokerOperations( diff --git a/apps/frontend/src/entities/broker-position/api/brokerPositionApi.ts b/apps/frontend/src/entities/broker-position/api/brokerPositionApi.ts index 858f6a0..0cd0fe8 100644 --- a/apps/frontend/src/entities/broker-position/api/brokerPositionApi.ts +++ b/apps/frontend/src/entities/broker-position/api/brokerPositionApi.ts @@ -1,5 +1,5 @@ +import type { ApiResponseMeta, BrokerPositionsPage } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { ApiResponseMeta, BrokerPositionsPage } from '@/shared/api/responses' export function getBrokerPositions( accountId: string, diff --git a/apps/frontend/src/entities/broker-position/model/brokerAllocation.test.ts b/apps/frontend/src/entities/broker-position/model/brokerAllocation.test.ts index 7715884..5ea8fd3 100644 --- a/apps/frontend/src/entities/broker-position/model/brokerAllocation.test.ts +++ b/apps/frontend/src/entities/broker-position/model/brokerAllocation.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import type { BrokerMoney, BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerMoney, BrokerPortfolio } from '@/shared/api' import { buildBrokerAllocation } from './brokerAllocation' function money(value: number): BrokerMoney { diff --git a/apps/frontend/src/entities/broker-position/model/brokerAllocation.ts b/apps/frontend/src/entities/broker-position/model/brokerAllocation.ts index 51f670c..2e20a2a 100644 --- a/apps/frontend/src/entities/broker-position/model/brokerAllocation.ts +++ b/apps/frontend/src/entities/broker-position/model/brokerAllocation.ts @@ -1,4 +1,4 @@ -import type { BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerPortfolio } from '@/shared/api' export type BrokerAllocationKey = 'shares' | 'bonds' | 'etf' | 'cash' | 'other' diff --git a/apps/frontend/src/entities/broker-position/model/brokerDisplay.test.ts b/apps/frontend/src/entities/broker-position/model/brokerDisplay.test.ts index 3c978a3..59480f2 100644 --- a/apps/frontend/src/entities/broker-position/model/brokerDisplay.test.ts +++ b/apps/frontend/src/entities/broker-position/model/brokerDisplay.test.ts @@ -5,7 +5,7 @@ import { getBrokerOperationTypeLabel, isBrokerOperationType, } from '@/entities/broker-operation' -import type { BrokerOperation, BrokerPosition } from '@/shared/api/responses' +import type { BrokerOperation, BrokerPosition } from '@/shared/api' import { getBrokerInstrumentPath, getBrokerPositionGroup } from './brokerDisplay' function position(input: Partial): BrokerPosition { diff --git a/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts b/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts index 7618b23..174fad4 100644 --- a/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts +++ b/apps/frontend/src/entities/broker-position/model/brokerDisplay.ts @@ -1,4 +1,4 @@ -import type { BrokerPosition } from '@/shared/api/responses' +import type { BrokerPosition } from '@/shared/api' export type BrokerPositionGroup = 'shares' | 'bonds' | 'other' diff --git a/apps/frontend/src/entities/broker-position/model/useBrokerPositions.ts b/apps/frontend/src/entities/broker-position/model/useBrokerPositions.ts index 6ad870b..1911d63 100644 --- a/apps/frontend/src/entities/broker-position/model/useBrokerPositions.ts +++ b/apps/frontend/src/entities/broker-position/model/useBrokerPositions.ts @@ -1,5 +1,5 @@ import { keepPreviousData, useQuery } from '@tanstack/react-query' -import type { BrokerPositionsPage } from '@/shared/api/responses' +import type { BrokerPositionsPage } from '@/shared/api' import { getBrokerPositions } from '../api/brokerPositionApi' export function useBrokerPositions( diff --git a/apps/frontend/src/entities/portfolio/api/portfolioApi.ts b/apps/frontend/src/entities/portfolio/api/portfolioApi.ts index 4ddea5c..26634c9 100644 --- a/apps/frontend/src/entities/portfolio/api/portfolioApi.ts +++ b/apps/frontend/src/entities/portfolio/api/portfolioApi.ts @@ -1,10 +1,5 @@ +import type { AnalyticsResponse, Portfolio, PortfolioDetail, Position } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { - AnalyticsResponse, - Portfolio, - PortfolioDetail, - Position, -} from '@/shared/api/responses' export function getPortfolios(): Promise<{ data: Portfolio[] diff --git a/apps/frontend/src/entities/portfolio/model/usePortfolio.ts b/apps/frontend/src/entities/portfolio/model/usePortfolio.ts index 3ceb9ed..547f4ac 100644 --- a/apps/frontend/src/entities/portfolio/model/usePortfolio.ts +++ b/apps/frontend/src/entities/portfolio/model/usePortfolio.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { PortfolioDetail } from '@/shared/api/responses' +import type { PortfolioDetail } from '@/shared/api' import { getPortfolio } from '../api/portfolioApi' export function usePortfolio(id: number) { diff --git a/apps/frontend/src/entities/portfolio/model/usePortfolioAnalytics.ts b/apps/frontend/src/entities/portfolio/model/usePortfolioAnalytics.ts index 9560ac2..3d0b8e1 100644 --- a/apps/frontend/src/entities/portfolio/model/usePortfolioAnalytics.ts +++ b/apps/frontend/src/entities/portfolio/model/usePortfolioAnalytics.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { AnalyticsResponse } from '@/shared/api/responses' +import type { AnalyticsResponse } from '@/shared/api' import { getPortfolioAnalytics } from '../api/portfolioApi' export function usePortfolioAnalytics(portfolioId: number) { diff --git a/apps/frontend/src/entities/portfolio/model/usePortfolios.ts b/apps/frontend/src/entities/portfolio/model/usePortfolios.ts index 3bd8cc6..d5c7050 100644 --- a/apps/frontend/src/entities/portfolio/model/usePortfolios.ts +++ b/apps/frontend/src/entities/portfolio/model/usePortfolios.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { Portfolio } from '@/shared/api/responses' +import type { Portfolio } from '@/shared/api' import { getPortfolios } from '../api/portfolioApi' export function usePortfolios() { diff --git a/apps/frontend/src/entities/portfolio/model/usePositionMutations.ts b/apps/frontend/src/entities/portfolio/model/usePositionMutations.ts index 639e121..25bea73 100644 --- a/apps/frontend/src/entities/portfolio/model/usePositionMutations.ts +++ b/apps/frontend/src/entities/portfolio/model/usePositionMutations.ts @@ -1,5 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query' -import type { PortfolioDetail } from '@/shared/api/responses' +import type { PortfolioDetail } from '@/shared/api' import { addPosition, removePosition, updatePosition } from '../api/portfolioApi' export function usePositionMutations(portfolioId: number) { diff --git a/apps/frontend/src/entities/search/api/searchApi.ts b/apps/frontend/src/entities/search/api/searchApi.ts index b901d41..327e891 100644 --- a/apps/frontend/src/entities/search/api/searchApi.ts +++ b/apps/frontend/src/entities/search/api/searchApi.ts @@ -1,5 +1,5 @@ +import type { SearchResultItem } from '@/shared/api' import { request } from '@/shared/api/kyClient' -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', { diff --git a/apps/frontend/src/entities/search/model/useSearch.ts b/apps/frontend/src/entities/search/model/useSearch.ts index 62b9e2d..b6c57a9 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 type { SearchResultItem } from '@/shared/api/responses' +import type { SearchResultItem } from '@/shared/api' import { searchSecurities } from '../api/searchApi' export function useSearch(query: string) { diff --git a/apps/frontend/src/entities/session/api/sessionApi.ts b/apps/frontend/src/entities/session/api/sessionApi.ts index 1a86cbc..07fdd97 100644 --- a/apps/frontend/src/entities/session/api/sessionApi.ts +++ b/apps/frontend/src/entities/session/api/sessionApi.ts @@ -1,5 +1,5 @@ +import type { AuthResponse, UserResponse } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { AuthResponse, UserResponse } from '@/shared/api/responses' import { setAccessToken } from './tokenManager' export async function login(email: string, password: string) { diff --git a/apps/frontend/src/entities/session/api/tokenManager.ts b/apps/frontend/src/entities/session/api/tokenManager.ts index c671f36..a77df65 100644 --- a/apps/frontend/src/entities/session/api/tokenManager.ts +++ b/apps/frontend/src/entities/session/api/tokenManager.ts @@ -1,5 +1,5 @@ +import type { AuthResponse } from '@/shared/api' import { normalizeEnvelope } from '@/shared/api/kyClient' -import type { AuthResponse } from '@/shared/api/responses' let accessToken: string | null = null let onUnauthorized: (() => void) | null = null diff --git a/apps/frontend/src/entities/session/model/useSessionStore.ts b/apps/frontend/src/entities/session/model/useSessionStore.ts index 9593101..d2f6c5c 100644 --- a/apps/frontend/src/entities/session/model/useSessionStore.ts +++ b/apps/frontend/src/entities/session/model/useSessionStore.ts @@ -1,5 +1,5 @@ import { create } from 'zustand' -import type { UserResponse } from '@/shared/api/responses' +import type { UserResponse } from '@/shared/api' interface SessionState { user: UserResponse | null diff --git a/apps/frontend/src/entities/stock/api/stockApi.ts b/apps/frontend/src/entities/stock/api/stockApi.ts index b0ee9a5..8debd80 100644 --- a/apps/frontend/src/entities/stock/api/stockApi.ts +++ b/apps/frontend/src/entities/stock/api/stockApi.ts @@ -1,4 +1,3 @@ -import { request } from '@/shared/api/kyClient' import type { ApiResponseMeta, CandleItem, @@ -6,7 +5,8 @@ import type { ShareHistoryItem, ShareResponse, StockMarketData, -} from '@/shared/api/responses' +} from '@/shared/api' +import { request } from '@/shared/api/kyClient' export function getShare(secid: string): Promise<{ data: ShareResponse; meta: ApiResponseMeta }> { return request(`/api/v1/securities/shares/${encodeURIComponent(secid)}`) diff --git a/apps/frontend/src/entities/stock/model/useStock.ts b/apps/frontend/src/entities/stock/model/useStock.ts index a3bca2d..7d88e61 100644 --- a/apps/frontend/src/entities/stock/model/useStock.ts +++ b/apps/frontend/src/entities/stock/model/useStock.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { ShareResponse } from '@/shared/api/responses' +import type { ShareResponse } from '@/shared/api' import { getShare } from '../api/stockApi' export function useStock(secid: string) { diff --git a/apps/frontend/src/entities/stock/model/useStockCandles.ts b/apps/frontend/src/entities/stock/model/useStockCandles.ts index 66912b4..d2887b5 100644 --- a/apps/frontend/src/entities/stock/model/useStockCandles.ts +++ b/apps/frontend/src/entities/stock/model/useStockCandles.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { CandleItem } from '@/shared/api/responses' +import type { CandleItem } from '@/shared/api' import { getShareCandles } from '../api/stockApi' export function useStockCandles(secid: string, interval: '1h' | '24h', from: string, till: string) { diff --git a/apps/frontend/src/entities/stock/model/useStockDividends.ts b/apps/frontend/src/entities/stock/model/useStockDividends.ts index 0b0a5b8..2bb4f92 100644 --- a/apps/frontend/src/entities/stock/model/useStockDividends.ts +++ b/apps/frontend/src/entities/stock/model/useStockDividends.ts @@ -1,5 +1,5 @@ import { useQuery } from '@tanstack/react-query' -import type { DividendItem } from '@/shared/api/responses' +import type { DividendItem } from '@/shared/api' import { getShareDividends } from '../api/stockApi' export function useStockDividends(secid: string) { diff --git a/apps/frontend/src/features/screener/api/screenerApi.ts b/apps/frontend/src/features/screener/api/screenerApi.ts index 76faed9..e0dd3d0 100644 --- a/apps/frontend/src/features/screener/api/screenerApi.ts +++ b/apps/frontend/src/features/screener/api/screenerApi.ts @@ -1,5 +1,5 @@ +import type { ScreenerResult } from '@/shared/api' import { request } from '@/shared/api/kyClient' -import type { ScreenerResult } from '@/shared/api/responses' export interface ScreenerQuery { type: 'share' | 'bond' diff --git a/apps/frontend/src/features/screener/ui/ScreenerTable.tsx b/apps/frontend/src/features/screener/ui/ScreenerTable.tsx index 280b2ed..d5394ab 100644 --- a/apps/frontend/src/features/screener/ui/ScreenerTable.tsx +++ b/apps/frontend/src/features/screener/ui/ScreenerTable.tsx @@ -1,5 +1,5 @@ import { Link } from '@tanstack/react-router' -import type { ScreenerResult } from '@/shared/api/responses' +import type { ScreenerResult } from '@/shared/api' interface Props { result: ScreenerResult diff --git a/apps/frontend/src/shared/api/client.test.ts b/apps/frontend/src/shared/api/client.test.ts deleted file mode 100644 index 67c411c..0000000 --- a/apps/frontend/src/shared/api/client.test.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { HttpResponse, http } from 'msw' -import { beforeEach, describe, expect, it } from 'vitest' -import { - getAccessToken, - handleUnauthorized, - setAccessToken, - setOnUnauthorized, -} from '@/entities/session/api/tokenManager' -import { server } from '@/shared/lib/test/server' -import { configureKyAuth, request } from './kyClient' - -const API = '/api/v1' - -beforeEach(() => { - setAccessToken(null) - configureKyAuth({ - getAccessToken, - handleUnauthorized, - }) -}) - -describe('request', () => { - it('makes GET request and returns data', async () => { - const result = await request<{ status: string; timestamp: string; uptime: number }>( - '/api/v1/health', - ) - expect(result.data.status).toBe('ok') - }) - - it('supports the single API envelope shape documented by Swagger', async () => { - server.use( - http.get(`${API}/test-single-envelope`, () => - HttpResponse.json({ - data: { ok: true }, - meta: { fromCache: true, cachedAt: '2026-06-17T00:00:00.000Z' }, - }), - ), - ) - - const result = await request<{ ok: boolean }>('/api/v1/test-single-envelope') - - expect(result).toEqual({ - data: { ok: true }, - meta: { fromCache: true, cachedAt: '2026-06-17T00:00:00.000Z' }, - }) - }) - - it('includes Authorization header when token is set', async () => { - setAccessToken('test-token') - let capturedAuth: string | null = null - server.use( - http.get(`${API}/test-auth`, ({ request }) => { - capturedAuth = request.headers.get('Authorization') - return HttpResponse.json({ - data: { data: { ok: true }, meta: { fromCache: false, cachedAt: null } }, - }) - }), - ) - await request('/api/v1/test-auth') - expect(capturedAuth).toBe('Bearer test-token') - }) - - it('retries on 401 and succeeds after refresh', async () => { - setAccessToken('expired-token') - let attempts = 0 - server.use( - http.get(`${API}/test-retry`, ({ request }) => { - attempts++ - const auth = request.headers.get('Authorization') - if (auth === 'Bearer expired-token') { - return new HttpResponse(null, { status: 401 }) - } - return HttpResponse.json({ - data: { data: { ok: true }, meta: { fromCache: false, cachedAt: null } }, - }) - }), - http.post(`${API}/auth/refresh`, () => - HttpResponse.json({ - data: { - data: { - user: { id: 1, email: 'user@test.com', name: null, role: 'user' }, - accessToken: 'new-token', - }, - meta: { fromCache: false, cachedAt: null }, - }, - }), - ), - ) - const result = await request<{ ok: boolean }>('/api/v1/test-retry') - expect(attempts).toBe(2) - expect(result.data).toEqual({ ok: true }) - expect(getAccessToken()).toBe('new-token') - }) - - it('throws on persistent 401 and clears token', async () => { - setAccessToken('expired-token') - let unauthorizedCalled = false - setOnUnauthorized(() => { - unauthorizedCalled = true - }) - server.use( - http.get(`${API}/test-fail`, () => new HttpResponse(null, { status: 401 })), - http.post(`${API}/auth/refresh`, () => new HttpResponse(null, { status: 401 })), - ) - await expect(request('/api/v1/test-fail')).rejects.toThrow('Сессия истекла') - expect(getAccessToken()).toBeNull() - expect(unauthorizedCalled).toBe(true) - }) - - it('throws on non-ok response with status text', async () => { - server.use( - http.get( - `${API}/test-error`, - () => new HttpResponse('Not found', { status: 404, statusText: 'Not Found' }), - ), - ) - await expect(request('/api/v1/test-error')).rejects.toThrow('Ошибка API: 404') - }) - - it('sends JSON body for POST requests', async () => { - let capturedBody: string | null = null - server.use( - http.post(`${API}/test-post`, async ({ request }) => { - capturedBody = await request.text() - return HttpResponse.json({ - data: { data: { ok: true }, meta: { fromCache: false, cachedAt: null } }, - }) - }), - ) - await request('/api/v1/test-post', undefined, { method: 'POST', body: { foo: 'bar' } }) - expect(capturedBody).toBe(JSON.stringify({ foo: 'bar' })) - }) - - it('does not send auth header when skipAuth is true', async () => { - setAccessToken('test-token') - let capturedAuth: string | null = null - server.use( - http.get(`${API}/test-skip`, ({ request }) => { - capturedAuth = request.headers.get('Authorization') - return HttpResponse.json({ - data: { data: { ok: true }, meta: { fromCache: false, cachedAt: null } }, - }) - }), - ) - await request('/api/v1/test-skip', undefined, { skipAuth: true }) - expect(capturedAuth).toBeNull() - }) - - it('sets query params correctly', async () => { - let capturedUrl = '' - server.use( - http.get(`${API}/test-params`, ({ request }) => { - capturedUrl = request.url - return HttpResponse.json({ - data: { data: { ok: true }, meta: { fromCache: false, cachedAt: null } }, - }) - }), - ) - await request('/api/v1/test-params', { q: 'sber', type: 'share' }) - expect(capturedUrl).toContain('q=sber') - expect(capturedUrl).toContain('type=share') - }) -}) diff --git a/apps/frontend/src/shared/api/index.ts b/apps/frontend/src/shared/api/index.ts index f1c7620..566da60 100644 --- a/apps/frontend/src/shared/api/index.ts +++ b/apps/frontend/src/shared/api/index.ts @@ -1,36 +1,62 @@ +import type { components } from './types' + export { configureKyAuth, getHealth, request } from './kyClient' -export type { - AnalyticsResponse, - ApiEnvelope, - ApiResponseMeta, - AuthResponse, - BondHistoryItem, - BondMarketData, - BondResponse, - BrokerAccount, - BrokerEventItem, - BrokerEventsData, - BrokerEventsSummary, - BrokerMoney, - BrokerOperation, - BrokerOperationCategory, - BrokerOperationsPage, - BrokerPortfolio, - BrokerPosition, - BrokerPositionsPage, - CandleItem, - DividendItem, - HealthResponse, - Portfolio, - PortfolioDetail, - PortfolioSummary, - Position, - PositionWithPrice, - ScreenerItem, - ScreenerResult, - SearchResultItem, - ShareHistoryItem, - ShareResponse, - StockMarketData, - UserResponse, -} from './responses' + +export type ApiResponseMeta = components['schemas']['ApiResponseMeta'] + +export interface ApiEnvelope { + data: T + meta: ApiResponseMeta +} + +// Auth +export type AuthResponse = components['schemas']['AuthTokenDataDto'] +export type UserResponse = components['schemas']['AuthUserDto'] + +// Shares +export type ShareResponse = components['schemas']['ShareResponseDto'] +export type StockMarketData = components['schemas']['StockMarketDataDto'] +export type DividendItem = components['schemas']['DividendItemDto'] +export type ShareHistoryItem = components['schemas']['HistoryItemDto'] + +// Bonds +export type BondResponse = components['schemas']['BondResponseDto'] +export type BondMarketData = components['schemas']['BondMarketDataDto'] +export type BondHistoryItem = components['schemas']['BondHistoryItemDto'] + +// Candles +export type CandleItem = components['schemas']['CandleItemDto'] + +// Search +export type SearchResultItem = components['schemas']['SearchResultItemDto'] + +// Screener +export type ScreenerResult = components['schemas']['ScreenerResultDto'] +export type ScreenerItem = components['schemas']['ScreenerItemDto'] + +// Portfolio +export type Portfolio = components['schemas']['PortfolioListResponseDto'] +export type PortfolioDetail = components['schemas']['PortfolioDetailResponseDto'] +export type Position = components['schemas']['PositionResponseDto'] +export type PositionWithPrice = components['schemas']['PositionWithPriceDto'] +export type PortfolioSummary = components['schemas']['PortfolioSummaryDto'] +export type AnalyticsResponse = components['schemas']['AnalyticsResponseDto'] + +// Health +export type HealthResponse = components['schemas']['HealthResponseDto'] + +// Broker +export type BrokerAccount = components['schemas']['BrokerAccountResponseDto'] +export type BrokerPortfolio = components['schemas']['BrokerPortfolioResponseDto'] +export type BrokerMoney = components['schemas']['BrokerMoneyDto'] +export type BrokerPosition = components['schemas']['BrokerPositionResponseDto'] +export type BrokerOperation = components['schemas']['BrokerOperationResponseDto'] +export type BrokerOperationsPage = components['schemas']['BrokerOperationsPageResponseDto'] +export type BrokerPositionsPage = components['schemas']['BrokerPositionsPageResponseDto'] +export type BrokerOperationCategory = + components['schemas']['BrokerOperationResponseDto']['category'] + +// Broker events +export type BrokerEventItem = components['schemas']['BrokerEventItemDto'] +export type BrokerEventsData = components['schemas']['BrokerEventsDataDto'] +export type BrokerEventsSummary = components['schemas']['BrokerEventsSummaryDto'] diff --git a/apps/frontend/src/shared/api/responses.ts b/apps/frontend/src/shared/api/responses.ts deleted file mode 100644 index 7841ed0..0000000 --- a/apps/frontend/src/shared/api/responses.ts +++ /dev/null @@ -1,62 +0,0 @@ -import type { components } from './types' - -// Re-exported from openapi-typescript codegen with friendly names - -export type ApiResponseMeta = components['schemas']['ApiResponseMeta'] - -export interface ApiEnvelope { - data: T - meta: ApiResponseMeta -} - -// Auth -export type AuthResponse = components['schemas']['AuthTokenDataDto'] -export type UserResponse = components['schemas']['AuthUserDto'] - -// Shares -export type ShareResponse = components['schemas']['ShareResponseDto'] -export type StockMarketData = components['schemas']['StockMarketDataDto'] -export type DividendItem = components['schemas']['DividendItemDto'] -export type ShareHistoryItem = components['schemas']['HistoryItemDto'] - -// Bonds -export type BondResponse = components['schemas']['BondResponseDto'] -export type BondMarketData = components['schemas']['BondMarketDataDto'] -export type BondHistoryItem = components['schemas']['BondHistoryItemDto'] - -// Candles -export type CandleItem = components['schemas']['CandleItemDto'] - -// Search -export type SearchResultItem = components['schemas']['SearchResultItemDto'] - -// Screener -export type ScreenerResult = components['schemas']['ScreenerResultDto'] -export type ScreenerItem = components['schemas']['ScreenerItemDto'] - -// Portfolio -export type Portfolio = components['schemas']['PortfolioListResponseDto'] -export type PortfolioDetail = components['schemas']['PortfolioDetailResponseDto'] -export type Position = components['schemas']['PositionResponseDto'] -export type PositionWithPrice = components['schemas']['PositionWithPriceDto'] -export type PortfolioSummary = components['schemas']['PortfolioSummaryDto'] -export type AnalyticsResponse = components['schemas']['AnalyticsResponseDto'] - -// Health -export type HealthResponse = components['schemas']['HealthResponseDto'] - -// Broker — some nullable fields still have Record from backend -export type BrokerAccount = components['schemas']['BrokerAccountResponseDto'] -export type BrokerPortfolio = components['schemas']['BrokerPortfolioResponseDto'] -export type BrokerMoney = components['schemas']['BrokerMoneyDto'] -export type BrokerPosition = components['schemas']['BrokerPositionResponseDto'] -export type BrokerOperation = components['schemas']['BrokerOperationResponseDto'] -export type BrokerOperationsPage = components['schemas']['BrokerOperationsPageResponseDto'] -export type BrokerPositionsPage = components['schemas']['BrokerPositionsPageResponseDto'] -export type BrokerOperationCategory = - components['schemas']['BrokerOperationResponseDto']['category'] - -// Broker events -export type BrokerEventItem = components['schemas']['BrokerEventItemDto'] -export type BrokerEventsData = components['schemas']['BrokerEventsDataDto'] -export type BrokerEventsSummary = components['schemas']['BrokerEventsSummaryDto'] diff --git a/apps/frontend/src/shared/lib/formatters.ts b/apps/frontend/src/shared/lib/formatters.ts index 6fff43d..f528fcf 100644 --- a/apps/frontend/src/shared/lib/formatters.ts +++ b/apps/frontend/src/shared/lib/formatters.ts @@ -1,4 +1,4 @@ -import type { BrokerMoney } from '@/shared/api/responses' +import type { BrokerMoney } from '@/shared/api' export function formatBrokerCurrencyValue(currency: string, value: number): string { return new Intl.NumberFormat('ru-RU', { diff --git a/apps/frontend/src/shared/lib/session-context.ts b/apps/frontend/src/shared/lib/session-context.ts index 450e824..f9a5179 100644 --- a/apps/frontend/src/shared/lib/session-context.ts +++ b/apps/frontend/src/shared/lib/session-context.ts @@ -1,5 +1,5 @@ import { createContext } from 'react' -import type { UserResponse } from '@/shared/api/responses' +import type { UserResponse } from '@/shared/api' export interface SessionContextValue { user: UserResponse | null diff --git a/apps/frontend/src/shared/lib/test/factories.ts b/apps/frontend/src/shared/lib/test/factories.ts index e95c281..82e474d 100644 --- a/apps/frontend/src/shared/lib/test/factories.ts +++ b/apps/frontend/src/shared/lib/test/factories.ts @@ -8,7 +8,7 @@ import type { ShareResponse, StockMarketData, UserResponse, -} from '@/shared/api/responses' +} from '@/shared/api' export function createMockMarketData(overrides: Partial = {}): StockMarketData { return { diff --git a/apps/frontend/src/shared/lib/test/handlers.ts b/apps/frontend/src/shared/lib/test/handlers.ts index b938b87..029a4d4 100644 --- a/apps/frontend/src/shared/lib/test/handlers.ts +++ b/apps/frontend/src/shared/lib/test/handlers.ts @@ -1,10 +1,5 @@ import { HttpResponse, http } from 'msw' -import type { - BondResponse, - CandleItem, - SearchResultItem, - ShareResponse, -} from '@/shared/api/responses' +import type { BondResponse, CandleItem, SearchResultItem, ShareResponse } from '@/shared/api' const API = '/api/v1' diff --git a/apps/frontend/src/widgets/bond-details/ui/BondDetails.tsx b/apps/frontend/src/widgets/bond-details/ui/BondDetails.tsx index ebed043..9a7b19d 100644 --- a/apps/frontend/src/widgets/bond-details/ui/BondDetails.tsx +++ b/apps/frontend/src/widgets/bond-details/ui/BondDetails.tsx @@ -1,4 +1,4 @@ -import type { BondResponse } from '@/shared/api/responses' +import type { BondResponse } from '@/shared/api' interface BondDetailsProps { bond: BondResponse diff --git a/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionRow.tsx b/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionRow.tsx index d7c973a..53a2c26 100644 --- a/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionRow.tsx +++ b/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionRow.tsx @@ -1,6 +1,6 @@ import { Link } from '@tanstack/react-router' import { useState } from 'react' -import type { PositionWithPrice } from '@/shared/api/responses' +import type { PositionWithPrice } from '@/shared/api' interface Props { position: PositionWithPrice diff --git a/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionTable.tsx b/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionTable.tsx index 5cf7c68..e6592a6 100644 --- a/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionTable.tsx +++ b/apps/frontend/src/widgets/bond-positions-table/ui/BondPositionTable.tsx @@ -1,4 +1,4 @@ -import type { PositionWithPrice } from '@/shared/api/responses' +import type { PositionWithPrice } from '@/shared/api' import { BondPositionRow } from './BondPositionRow' interface Props { 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 552bb30..efb58a2 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 { Alert, Button, Heading, Skeleton, Text } from '@moex-vibe/design-system import { Box } from '@mui/material' import { Link } from '@tanstack/react-router' import { buildBrokerAllocation } from '@/entities/broker-position' -import type { BrokerAccount, BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerAccount, BrokerPortfolio } from '@/shared/api' import { formatBrokerDate, formatBrokerMoney, 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 71add71..25183da 100644 --- a/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx +++ b/apps/frontend/src/widgets/broker-allocation-chart/ui/BrokerAllocationChart.tsx @@ -1,7 +1,7 @@ import { Text } from '@moex-vibe/design-system' import { Box } from '@mui/material' import { buildBrokerAllocation } from '@/entities/broker-position' -import type { BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerPortfolio } from '@/shared/api' import { formatBrokerCurrencyValue } from '@/shared/lib/formatters' const RADIUS = 44 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 c0ae185..50aa765 100644 --- a/apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx +++ b/apps/frontend/src/widgets/broker-operations-table/ui/BrokerOperationsTable.tsx @@ -8,7 +8,7 @@ import { getBrokerOperationTypeLabel, } from '@/entities/broker-operation' import { getBrokerInstrumentPath } from '@/entities/broker-position' -import type { BrokerOperation, BrokerOperationsPage } from '@/shared/api/responses' +import type { BrokerOperation, BrokerOperationsPage } from '@/shared/api' import { formatBrokerSignedMoney } from '@/shared/lib/formatters' import { TableSkeleton } from '@/shared/ui/TableSkeleton' diff --git a/apps/frontend/src/widgets/broker-overview/ui/BrokerAssetCards.tsx b/apps/frontend/src/widgets/broker-overview/ui/BrokerAssetCards.tsx index fa93d3a..101a91f 100644 --- a/apps/frontend/src/widgets/broker-overview/ui/BrokerAssetCards.tsx +++ b/apps/frontend/src/widgets/broker-overview/ui/BrokerAssetCards.tsx @@ -1,7 +1,7 @@ import { Text } from '@moex-vibe/design-system' import { Box } from '@mui/material' import { Link } from '@tanstack/react-router' -import type { BrokerMoney, BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerMoney, BrokerPortfolio } from '@/shared/api' import { formatBrokerMoney, pluralize } from '@/shared/lib/formatters' function allocationPercent(value: BrokerMoney | null, total: BrokerMoney | null) { diff --git a/apps/frontend/src/widgets/broker-overview/ui/BrokerSummary.tsx b/apps/frontend/src/widgets/broker-overview/ui/BrokerSummary.tsx index 8cfbbee..abfe56d 100644 --- a/apps/frontend/src/widgets/broker-overview/ui/BrokerSummary.tsx +++ b/apps/frontend/src/widgets/broker-overview/ui/BrokerSummary.tsx @@ -1,6 +1,6 @@ import { Text } from '@moex-vibe/design-system' import { Box } from '@mui/material' -import type { BrokerPortfolio } from '@/shared/api/responses' +import type { BrokerPortfolio } from '@/shared/api' import { formatBrokerMoney as formatMoney, formatBrokerPercent as formatPercent, diff --git a/apps/frontend/src/widgets/broker-positions-table/ui/BrokerPositionTable.tsx b/apps/frontend/src/widgets/broker-positions-table/ui/BrokerPositionTable.tsx index e2220e1..30c3aa8 100644 --- a/apps/frontend/src/widgets/broker-positions-table/ui/BrokerPositionTable.tsx +++ b/apps/frontend/src/widgets/broker-positions-table/ui/BrokerPositionTable.tsx @@ -1,6 +1,6 @@ import { Button, Heading, Skeleton, Text } from '@moex-vibe/design-system' import { Box } from '@mui/material' -import type { BrokerPositionsPage as BrokerPositionsPageData } from '@/shared/api/responses' +import type { BrokerPositionsPage as BrokerPositionsPageData } from '@/shared/api' import { formatBrokerMoney as formatMoney } from '@/shared/lib/formatters' import { PositionTicker } from './PositionTicker' diff --git a/apps/frontend/src/widgets/broker-positions-table/ui/PositionTicker.tsx b/apps/frontend/src/widgets/broker-positions-table/ui/PositionTicker.tsx index af3dd0c..0b2fb81 100644 --- a/apps/frontend/src/widgets/broker-positions-table/ui/PositionTicker.tsx +++ b/apps/frontend/src/widgets/broker-positions-table/ui/PositionTicker.tsx @@ -1,7 +1,7 @@ import { Box } from '@mui/material' import { Link } from '@tanstack/react-router' import { getBrokerInstrumentPath } from '@/entities/broker-position' -import type { BrokerPosition } from '@/shared/api/responses' +import type { BrokerPosition } from '@/shared/api' export function PositionTicker({ position }: { position: BrokerPosition }) { const label = position.ticker || position.figi || '-' diff --git a/apps/frontend/src/widgets/dividends-table/ui/DividendsTable.tsx b/apps/frontend/src/widgets/dividends-table/ui/DividendsTable.tsx index 581754b..bfb4194 100644 --- a/apps/frontend/src/widgets/dividends-table/ui/DividendsTable.tsx +++ b/apps/frontend/src/widgets/dividends-table/ui/DividendsTable.tsx @@ -1,4 +1,4 @@ -import type { DividendItem } from '@/shared/api/responses' +import type { DividendItem } from '@/shared/api' interface DividendsTableProps { dividends: DividendItem[] diff --git a/apps/frontend/src/widgets/portfolio-analytics/ui/AnalyticsSummary.tsx b/apps/frontend/src/widgets/portfolio-analytics/ui/AnalyticsSummary.tsx index 50f9fc2..e6d1358 100644 --- a/apps/frontend/src/widgets/portfolio-analytics/ui/AnalyticsSummary.tsx +++ b/apps/frontend/src/widgets/portfolio-analytics/ui/AnalyticsSummary.tsx @@ -1,4 +1,4 @@ -import type { PortfolioSummary } from '@/shared/api/responses' +import type { PortfolioSummary } from '@/shared/api' export function AnalyticsSummary({ summary }: { summary: PortfolioSummary }) { const formatRub = (val: number | null) => diff --git a/apps/frontend/src/widgets/portfolio-card/ui/PortfolioCard.tsx b/apps/frontend/src/widgets/portfolio-card/ui/PortfolioCard.tsx index 380a4f1..fdae5c1 100644 --- a/apps/frontend/src/widgets/portfolio-card/ui/PortfolioCard.tsx +++ b/apps/frontend/src/widgets/portfolio-card/ui/PortfolioCard.tsx @@ -1,5 +1,5 @@ import { Link } from '@tanstack/react-router' -import type { Portfolio } from '@/shared/api/responses' +import type { Portfolio } from '@/shared/api' import { pluralize } from '@/shared/lib/formatters' export function PortfolioCard({ portfolio }: { portfolio: Portfolio }) { diff --git a/apps/frontend/src/widgets/portfolio-form/ui/PortfolioForm.tsx b/apps/frontend/src/widgets/portfolio-form/ui/PortfolioForm.tsx index d960283..73724eb 100644 --- a/apps/frontend/src/widgets/portfolio-form/ui/PortfolioForm.tsx +++ b/apps/frontend/src/widgets/portfolio-form/ui/PortfolioForm.tsx @@ -1,5 +1,5 @@ import { useState } from 'react' -import type { Portfolio } from '@/shared/api/responses' +import type { Portfolio } from '@/shared/api' interface Props { initial?: Portfolio diff --git a/apps/frontend/src/widgets/portfolio-summary/ui/AllocationChart.tsx b/apps/frontend/src/widgets/portfolio-summary/ui/AllocationChart.tsx index f3e166b..c614455 100644 --- a/apps/frontend/src/widgets/portfolio-summary/ui/AllocationChart.tsx +++ b/apps/frontend/src/widgets/portfolio-summary/ui/AllocationChart.tsx @@ -1,4 +1,4 @@ -import type { PositionWithPrice } from '@/shared/api/responses' +import type { PositionWithPrice } from '@/shared/api' interface AllocationChartProps { positions: PositionWithPrice[] diff --git a/apps/frontend/src/widgets/portfolio-summary/ui/PortfolioSummary.tsx b/apps/frontend/src/widgets/portfolio-summary/ui/PortfolioSummary.tsx index 609bb36..dfda404 100644 --- a/apps/frontend/src/widgets/portfolio-summary/ui/PortfolioSummary.tsx +++ b/apps/frontend/src/widgets/portfolio-summary/ui/PortfolioSummary.tsx @@ -1,4 +1,4 @@ -import type { PortfolioDetail } from '@/shared/api/responses' +import type { PortfolioDetail } from '@/shared/api' import { AllocationChart } from './AllocationChart' export function PortfolioSummary({ portfolio }: { portfolio: PortfolioDetail }) { diff --git a/apps/frontend/src/widgets/share-positions-table/ui/SharePositionRow.tsx b/apps/frontend/src/widgets/share-positions-table/ui/SharePositionRow.tsx index 60b703f..9d01d80 100644 --- a/apps/frontend/src/widgets/share-positions-table/ui/SharePositionRow.tsx +++ b/apps/frontend/src/widgets/share-positions-table/ui/SharePositionRow.tsx @@ -1,6 +1,6 @@ import { Link } from '@tanstack/react-router' import { useState } from 'react' -import type { PositionWithPrice } from '@/shared/api/responses' +import type { PositionWithPrice } from '@/shared/api' interface Props { position: PositionWithPrice diff --git a/apps/frontend/src/widgets/share-positions-table/ui/SharePositionTable.tsx b/apps/frontend/src/widgets/share-positions-table/ui/SharePositionTable.tsx index 291429c..51281e0 100644 --- a/apps/frontend/src/widgets/share-positions-table/ui/SharePositionTable.tsx +++ b/apps/frontend/src/widgets/share-positions-table/ui/SharePositionTable.tsx @@ -1,4 +1,4 @@ -import type { PositionWithPrice } from '@/shared/api/responses' +import type { PositionWithPrice } from '@/shared/api' import { SharePositionRow } from './SharePositionRow' interface Props { diff --git a/apps/frontend/src/widgets/stock-details/ui/StockDetails.tsx b/apps/frontend/src/widgets/stock-details/ui/StockDetails.tsx index 0c956c0..75efe52 100644 --- a/apps/frontend/src/widgets/stock-details/ui/StockDetails.tsx +++ b/apps/frontend/src/widgets/stock-details/ui/StockDetails.tsx @@ -1,4 +1,4 @@ -import type { ShareResponse } from '@/shared/api/responses' +import type { ShareResponse } from '@/shared/api' interface StockDetailsProps { stock: ShareResponse diff --git a/docs/features/frontend-infrastructure-tooling/plan.md b/docs/features/frontend-infrastructure-tooling/plan.md index f9fc584..f903639 100644 --- a/docs/features/frontend-infrastructure-tooling/plan.md +++ b/docs/features/frontend-infrastructure-tooling/plan.md @@ -47,10 +47,14 @@ ### Phase 3 — Unify API Types *Низкий риск* -1. Убедиться, что все entity API импортируют из `types.ts` (codegen) +> **Фактический подход: ре-экспорты в index.ts вместо types.ts.** +> В процессе реализации выяснилось, что direct-импорты из `types.ts` ведут к многословным `components['schemas']['XxxDto']` в 60+ файлах. Принято решение перенести friendly-name ре-экспорты из `responses.ts` в `index.ts`. Таким образом `responses.ts` удалён, а единая точка входа — `@/shared/api`. + +1. Перенести friendly-name type-алиасы из `responses.ts` в `shared/api/index.ts` 2. Удалить `shared/api/responses.ts` -3. Перенести normalizeEnvelope (ky-версию) в `shared/api/kyClient.ts` -4. Прогнать `npm run build` +3. Переключить все импорты с `@/shared/api/responses` на `@/shared/api` +4. Перенести normalizeEnvelope (ky-версию) в `shared/api/kyClient.ts` +5. Прогнать `npm run build` ### Phase 4 — MSW Browser *Низкий риск, handlers готовы* diff --git a/docs/features/frontend-infrastructure-tooling/tasks.md b/docs/features/frontend-infrastructure-tooling/tasks.md index 7ba9b03..3b9d748 100644 --- a/docs/features/frontend-infrastructure-tooling/tasks.md +++ b/docs/features/frontend-infrastructure-tooling/tasks.md @@ -21,9 +21,9 @@ ## Phase 2: Biome Migration -- [ ] Research: проверить Biome plugin system на поддержку FSD/import-no-restricted-paths +- [x] Research: проверить Biome plugin system на поддержку FSD/import-no-restricted-paths — не поддерживается, ESLint оставлен для FSD - [x] Установить `@biomejs/biome` (devDependency) -- [ ] Запустить `npx @biomejs/biome migrate eslint --write` +- [x] Запустить `npx @biomejs/biome migrate eslint --write` — 0 правил мигрировано, FSD-правила несовместимы, конфиг откачен - [x] Создать и настроить `biome.json` под проект - [x] ESLint оставлен только для FSD-правил (`.eslintrc.cjs`) - [x] Удалить зависимость prettier (ESLint core, @typescript-eslint/*, eslint-plugin-* оставлены для FSD-правил) @@ -37,11 +37,11 @@ ## Phase 3: Unify API Types -- [ ] Аудит импортов: entity API используют `types.ts` (codegen) или `responses.ts`? -- [ ] Переключить все импорты с `responses.ts` на `types.ts` (если типы есть в codegen) -- [ ] Удалить `shared/api/responses.ts` (после переключения) +- [x] Аудит импортов: entity API используют `types.ts` (codegen) или `responses.ts`? +- [x] Переключить все импорты с `responses.ts` на `@/shared/api` (типы перенесены в `index.ts` вместо рукописного файла) +- [x] Удалить `shared/api/responses.ts` (после переключения) - [x] normalizeEnvelope перенесён в `shared/api/kyClient.ts` -- [ ] `npm run build` — сборка проходит +- [x] `npm run build` — сборка проходит ## Phase 4: MSW Browser