refactor(frontend): bring FSD architecture into compliance #30
@ -38,74 +38,6 @@ function moneyValue(money: BrokerMoney | null | undefined): number {
|
||||
return money?.value ?? 0;
|
||||
}
|
||||
|
||||
export function formatBrokerMoney(value: BrokerMoney | null | undefined): string {
|
||||
if (!value) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return formatBrokerCurrencyValue(value.currency, value.value);
|
||||
}
|
||||
|
||||
export function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
export function formatBrokerSignedCurrencyValue(currency: string, value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = formatBrokerCurrencyValue(currency, Math.abs(value));
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `−${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
export function formatBrokerPercent(value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return `${new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(value)}%`;
|
||||
}
|
||||
|
||||
export function formatBrokerSignedPercent(value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = formatBrokerPercent(Math.abs(value));
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `−${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
export function formatBrokerDate(value: string | null | undefined): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Date(value).toLocaleDateString('ru-RU');
|
||||
}
|
||||
|
||||
export function brokerAccountTypeLabel(type: 'brokerage' | 'iis'): string {
|
||||
return type === 'iis' ? 'ИИС' : 'Брокерский счёт';
|
||||
}
|
||||
|
||||
@ -5,13 +5,8 @@ export {
|
||||
type BrokerAllocationKey,
|
||||
} from './model/brokerAllocation';
|
||||
export {
|
||||
BROKER_OPERATION_TYPE_OPTIONS,
|
||||
getBrokerInstrumentPath,
|
||||
getBrokerOperationImpact,
|
||||
getBrokerOperationTypeLabel,
|
||||
getBrokerPositionGroup,
|
||||
isBrokerOperationType,
|
||||
type BrokerOperationImpact,
|
||||
type BrokerPositionGroup,
|
||||
} from './model/brokerDisplay';
|
||||
export { useBrokerPositions } from './model/useBrokerPositions';
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import type { BrokerOperation, BrokerPosition } from '@/shared/api/responses';
|
||||
import { getBrokerInstrumentPath, getBrokerPositionGroup } from './brokerDisplay';
|
||||
import {
|
||||
BROKER_OPERATION_TYPE_OPTIONS,
|
||||
getBrokerInstrumentPath,
|
||||
getBrokerOperationImpact,
|
||||
getBrokerOperationTypeLabel,
|
||||
getBrokerPositionGroup,
|
||||
isBrokerOperationType,
|
||||
} from './brokerDisplay';
|
||||
} from '@/entities/broker-operation';
|
||||
|
||||
function position(input: Partial<BrokerPosition>): BrokerPosition {
|
||||
return {
|
||||
|
||||
@ -1,11 +1,4 @@
|
||||
import type { BrokerPosition } from '@/shared/api/responses';
|
||||
export {
|
||||
BROKER_OPERATION_TYPE_OPTIONS,
|
||||
getBrokerOperationImpact,
|
||||
getBrokerOperationTypeLabel,
|
||||
isBrokerOperationType,
|
||||
} from '@/entities/broker-operation';
|
||||
export type { BrokerOperationImpact } from '@/entities/broker-operation';
|
||||
|
||||
export type BrokerPositionGroup = 'shares' | 'bonds' | 'other';
|
||||
|
||||
|
||||
@ -1,10 +1,3 @@
|
||||
export { useStock } from './model/useStock';
|
||||
export { useStockCandles } from './model/useStockCandles';
|
||||
export { useStockDividends } from './model/useStockDividends';
|
||||
export {
|
||||
getShare,
|
||||
getShareMarketData,
|
||||
getShareDividends,
|
||||
getShareHistory,
|
||||
getShareCandles,
|
||||
} from './api/stockApi';
|
||||
|
||||
@ -5,29 +5,11 @@ 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 '—';
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: value.currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value.value);
|
||||
}
|
||||
|
||||
function formatPercent(value: number | null) {
|
||||
if (value === null) return '—';
|
||||
return `${new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(value)}%`;
|
||||
}
|
||||
|
||||
function pluralize(count: number, one: string, few: string, many: string) {
|
||||
const modulo100 = Math.abs(count) % 100;
|
||||
const modulo10 = modulo100 % 10;
|
||||
if (modulo100 > 10 && modulo100 < 20) return many;
|
||||
if (modulo10 === 1) return one;
|
||||
if (modulo10 >= 2 && modulo10 <= 4) return few;
|
||||
return many;
|
||||
}
|
||||
import {
|
||||
formatBrokerMoney as formatMoney,
|
||||
formatBrokerPercent as formatPercent,
|
||||
pluralize,
|
||||
} from '@/shared/lib/formatters';
|
||||
|
||||
function BrokerSummary({ portfolio }: { portfolio: BrokerPortfolio }) {
|
||||
return (
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type {
|
||||
BrokerMoney,
|
||||
BrokerPosition,
|
||||
BrokerPositionsPage as BrokerPositionsPageData,
|
||||
} from '@/shared/api/responses';
|
||||
import { TableSkeleton } from '@/shared/ui/TableSkeleton';
|
||||
import { getBrokerInstrumentPath, useBrokerPositions } from '@/entities/broker-position';
|
||||
import { useBrokerAccountContext } from '@/widgets/broker-account-layout';
|
||||
import { formatBrokerMoney as formatMoney } from '@/shared/lib/formatters';
|
||||
|
||||
const tableStyle = {
|
||||
width: '100%',
|
||||
@ -46,15 +46,6 @@ const pagButtonDisabledStyle = {
|
||||
cursor: 'not-allowed',
|
||||
} satisfies React.CSSProperties;
|
||||
|
||||
function formatMoney(value: BrokerMoney | null | undefined) {
|
||||
if (!value) return '-';
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: value.currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value.value);
|
||||
}
|
||||
|
||||
function formatQuantity(value: number | null | undefined) {
|
||||
return value == null ? '-' : value.toLocaleString('ru-RU');
|
||||
}
|
||||
|
||||
89
apps/frontend/src/shared/lib/formatters.ts
Normal file
89
apps/frontend/src/shared/lib/formatters.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import type { BrokerMoney } from '@/shared/api/responses';
|
||||
|
||||
export function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
export function formatBrokerMoney(value: BrokerMoney | null | undefined): string {
|
||||
if (!value) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return formatBrokerCurrencyValue(value.currency, value.value);
|
||||
}
|
||||
|
||||
export function formatBrokerSignedCurrencyValue(currency: string, value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = formatBrokerCurrencyValue(currency, Math.abs(value));
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `\u2212${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
export function formatBrokerSignedMoney(value: BrokerMoney | null | undefined): string {
|
||||
if (!value) {
|
||||
return '\u2014';
|
||||
}
|
||||
|
||||
return formatBrokerSignedCurrencyValue(value.currency, value.value);
|
||||
}
|
||||
|
||||
export function formatBrokerPercent(value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return `${new Intl.NumberFormat('ru-RU', { maximumFractionDigits: 2 }).format(value)}%`;
|
||||
}
|
||||
|
||||
export function formatBrokerSignedPercent(value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = formatBrokerPercent(Math.abs(value));
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `\u2212${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
export function formatBrokerDate(value: string | null | undefined): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Date(value).toLocaleDateString('ru-RU');
|
||||
}
|
||||
|
||||
export function pluralize(n: number, one: string, few: string, many: string): string {
|
||||
const abs = Math.abs(n);
|
||||
const modulo100 = abs % 100;
|
||||
const modulo10 = modulo100 % 10;
|
||||
|
||||
if (modulo100 > 10 && modulo100 < 20) return many;
|
||||
if (modulo10 === 1) return one;
|
||||
if (modulo10 >= 2 && modulo10 <= 4) return few;
|
||||
|
||||
return many;
|
||||
}
|
||||
@ -1 +1 @@
|
||||
export { BrokerAllocationBar } from './ui/BrokerAllocationBar';
|
||||
export { BrokerAllocationBar } from './BrokerAllocationBar';
|
||||
|
||||
@ -1,70 +1,14 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
||||
import type { BrokerAccount, BrokerMoney, BrokerPortfolio } from '@/shared/api/responses';
|
||||
import type { BrokerAccount, BrokerPortfolio } from '@/shared/api/responses';
|
||||
import { buildBrokerAllocation } from '@/entities/broker-position';
|
||||
import { BrokerAllocationBar } from '@/shared/ui/broker-allocation-bar';
|
||||
|
||||
function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
function formatBrokerMoney(value: BrokerMoney | null | undefined): string {
|
||||
if (!value) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
return formatBrokerCurrencyValue(value.currency, value.value);
|
||||
}
|
||||
|
||||
function formatBrokerSignedCurrencyValue(currency: string, value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = formatBrokerCurrencyValue(currency, Math.abs(value));
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `−${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
function formatBrokerSignedPercent(value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = `${new Intl.NumberFormat('ru-RU', {
|
||||
maximumFractionDigits: 2,
|
||||
}).format(Math.abs(value))}%`;
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `−${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
function formatBrokerDate(value: string | null | undefined): string | null {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Date(value).toLocaleDateString('ru-RU');
|
||||
}
|
||||
import {
|
||||
formatBrokerMoney,
|
||||
formatBrokerSignedCurrencyValue,
|
||||
formatBrokerSignedPercent,
|
||||
formatBrokerDate,
|
||||
} from '@/shared/lib/formatters';
|
||||
|
||||
function brokerAccountTypeLabel(type: 'brokerage' | 'iis'): string {
|
||||
return type === 'iis' ? 'ИИС' : 'Брокерский счёт';
|
||||
|
||||
@ -2,52 +2,11 @@ import { SkeletonBlock } from '@/shared/ui/SkeletonBlock';
|
||||
import type { BrokerAccountsAggregate } from '@/entities/broker-account';
|
||||
import { buildBrokerAllocation } from '@/entities/broker-position';
|
||||
import { BrokerAllocationBar } from '@/shared/ui/broker-allocation-bar';
|
||||
|
||||
function formatBrokerCurrencyValue(currency: string, value: number): string {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
function formatBrokerSignedCurrencyValue(currency: string, value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = formatBrokerCurrencyValue(currency, Math.abs(value));
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `−${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
function formatBrokerSignedPercent(value: number | null): string {
|
||||
if (value === null) {
|
||||
return '—';
|
||||
}
|
||||
|
||||
const formatted = `${new Intl.NumberFormat('ru-RU', {
|
||||
maximumFractionDigits: 2,
|
||||
}).format(Math.abs(value))}%`;
|
||||
|
||||
if (value > 0) {
|
||||
return `+${formatted}`;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return `−${formatted}`;
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
import {
|
||||
formatBrokerCurrencyValue,
|
||||
formatBrokerSignedCurrencyValue,
|
||||
formatBrokerSignedPercent,
|
||||
} from '@/shared/lib/formatters';
|
||||
|
||||
export function BrokerAccountsSummary({
|
||||
aggregate,
|
||||
|
||||
@ -1,17 +1,10 @@
|
||||
import type { BrokerPortfolio } from '@/shared/api/responses';
|
||||
import { buildBrokerAllocation } from '@/entities/broker-position';
|
||||
import { formatBrokerCurrencyValue } from '@/shared/lib/formatters';
|
||||
|
||||
const RADIUS = 44;
|
||||
const CIRCUMFERENCE = 2 * Math.PI * RADIUS;
|
||||
|
||||
function formatMoneyValue(value: number, currency: string) {
|
||||
return new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
function allocationCurrency(portfolio: BrokerPortfolio) {
|
||||
return (
|
||||
portfolio.totals.portfolio?.currency ||
|
||||
@ -64,7 +57,7 @@ export function BrokerAllocationChart({ portfolio }: { portfolio: BrokerPortfoli
|
||||
style={{ background: sector.color }}
|
||||
/>
|
||||
<span>
|
||||
{sector.label}: {formatMoneyValue(sector.value, currency)} ·{' '}
|
||||
{sector.label}: {formatBrokerCurrencyValue(currency, sector.value)} ·{' '}
|
||||
{sector.percent.toFixed(1)}%
|
||||
</span>
|
||||
</li>
|
||||
@ -78,7 +71,8 @@ export function BrokerAllocationChart({ portfolio }: { portfolio: BrokerPortfoli
|
||||
>
|
||||
{negative.map((item) => (
|
||||
<li key={item.key}>
|
||||
{item.label}: отрицательное значение {formatMoneyValue(item.value, currency)}
|
||||
{item.label}: отрицательное значение{' '}
|
||||
{formatBrokerCurrencyValue(currency, item.value)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { BrokerMoney, BrokerOperation, BrokerOperationsPage } from '@/shared/api/responses';
|
||||
import type { BrokerOperation, BrokerOperationsPage } from '@/shared/api/responses';
|
||||
import { TableSkeleton } from '@/shared/ui/TableSkeleton';
|
||||
import {
|
||||
getBrokerOperationImpact,
|
||||
@ -8,6 +8,7 @@ import {
|
||||
type BrokerOperationImpact,
|
||||
} from '@/entities/broker-operation';
|
||||
import { getBrokerInstrumentPath } from '@/entities/broker-position';
|
||||
import { formatBrokerSignedMoney } from '@/shared/lib/formatters';
|
||||
|
||||
const tableStyle = {
|
||||
width: '100%',
|
||||
@ -46,16 +47,6 @@ const pagButtonDisabledStyle = {
|
||||
cursor: 'not-allowed',
|
||||
} satisfies React.CSSProperties;
|
||||
|
||||
function formatMoney(value: BrokerMoney | null | undefined) {
|
||||
if (!value) return '-';
|
||||
const formatted = new Intl.NumberFormat('ru-RU', {
|
||||
style: 'currency',
|
||||
currency: value.currency || 'RUB',
|
||||
maximumFractionDigits: 2,
|
||||
}).format(value.value);
|
||||
return value.value > 0 ? `+${formatted}` : formatted;
|
||||
}
|
||||
|
||||
function formatDate(value: string | null) {
|
||||
if (!value) return '-';
|
||||
|
||||
@ -235,7 +226,7 @@ export function BrokerOperationsTable({
|
||||
align="right"
|
||||
style={{ ...tdStyle, color: moneyColor(impact), fontWeight: 700 }}
|
||||
>
|
||||
{formatMoney(operation.payment)}
|
||||
{formatBrokerSignedMoney(operation.payment)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { Portfolio } from '@/shared/api/responses';
|
||||
import { pluralize } from '@/shared/lib/formatters';
|
||||
|
||||
export function PortfolioCard({ portfolio }: { portfolio: Portfolio }) {
|
||||
const chipStyle = (bg: string): React.CSSProperties => ({
|
||||
@ -79,9 +80,3 @@ export function PortfolioCard({ portfolio }: { portfolio: Portfolio }) {
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function pluralize(n: number, one: string, few: string, many: string): string {
|
||||
if (n % 10 === 1 && n % 100 !== 11) return one;
|
||||
if (n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 10 || n % 100 >= 20)) return few;
|
||||
return many;
|
||||
}
|
||||
|
||||
32
docs/features/fsd-quick-fix/plan.md
Normal file
32
docs/features/fsd-quick-fix/plan.md
Normal file
@ -0,0 +1,32 @@
|
||||
# FSD Quick Fix — Plan
|
||||
|
||||
## Архитектурные решения
|
||||
|
||||
1. **Все форматтеры** переносятся в `shared/lib/formatters.ts`. Каноничные реализации берутся из `entities/broker-account/model/brokerAccountsOverview.ts`. Исходные функции в brokerAccountsOverview становятся тонкими врапперами (re-export), чтобы не менять контракт entity.
|
||||
2. **Cross-entity импорт** удаляется полностью — символы broker-operation никто не импортирует через broker-position, реэкспорты мёртвые.
|
||||
3. **Stock API-функции** убираются из barrel — используются только внутри entity хуками.
|
||||
4. **shared/ui/broker-allocation-bar** уплощается: компонент поднимается на уровень slice.
|
||||
|
||||
## Порядок выполнения
|
||||
|
||||
### Task 1: Убрать cross-entity import
|
||||
- broker-position/model/brokerDisplay.ts — удалить re-export строки
|
||||
- broker-position/index.ts — удалить соответствующие экспорты
|
||||
|
||||
### Task 2: Дедуплицировать форматтеры
|
||||
- Создать shared/lib/formatters.ts
|
||||
- Переделать brokerAccountsOverview.ts на импорт из shared
|
||||
- Заменить дубликаты во всех консьюмерах
|
||||
|
||||
### Task 3: Уплостить shared/ui/broker-allocation-bar
|
||||
- Переместить BrokerAllocationBar.tsx на уровень выше
|
||||
- Обновить index.ts
|
||||
- Удалить ui/
|
||||
|
||||
### Task 4: Очистить entities/stock/index.ts
|
||||
- Убрать getShare, getShareMarketData и т.д. из barrel
|
||||
|
||||
### Проверка
|
||||
- npm run lint (frontend)
|
||||
- npm run build (frontend)
|
||||
- npm run test (frontend)
|
||||
22
docs/features/fsd-quick-fix/spec.md
Normal file
22
docs/features/fsd-quick-fix/spec.md
Normal file
@ -0,0 +1,22 @@
|
||||
# FSD Quick Fix
|
||||
|
||||
## Цель
|
||||
|
||||
Привести фронтенд к соответствию Feature-Sliced Design (FSD) путём исправления выявленных нарушений и дублирования кода. Никакой новой функциональности — только архитектурный рефакторинг.
|
||||
|
||||
## Требования
|
||||
|
||||
1. Устранить cross-entity импорт (entities → entities).
|
||||
2. Устранить дублирование форматтеров денежных величин и pluralize.
|
||||
3. Устранить избыточную вложенность shared/ui-компонента.
|
||||
4. Убрать из публичного API entity сырые API-функции, не используемые снаружи.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `entities/broker-position` не импортирует символы из `entities/broker-operation`
|
||||
- [ ] `shared/lib/formatters.ts` содержит каноничные реализации всех форматтеров
|
||||
- [ ] Все консьюмеры используют форматтеры из `shared/lib/formatters.ts` вместо локальных копий
|
||||
- [ ] `shared/ui/broker-allocation-bar` не имеет лишней вложенности `ui/`
|
||||
- [ ] `entities/stock/index.ts` не экспортирует неиспользуемые извне API-функции
|
||||
- [ ] Lint проходит, build проходит, тесты проходят
|
||||
- [ ] Нет изменений в поведении системы
|
||||
28
docs/features/fsd-quick-fix/tasks.md
Normal file
28
docs/features/fsd-quick-fix/tasks.md
Normal file
@ -0,0 +1,28 @@
|
||||
# FSD Quick Fix — Tasks
|
||||
|
||||
- [x] Создать feature branch `codex/fsd-quick-fix`
|
||||
- [x] Написать spec.md
|
||||
- [x] Написать plan.md
|
||||
- [x] **Task 1**: Убрать cross-entity import broker-position → broker-operation
|
||||
- [x] `entities/broker-position/model/brokerDisplay.ts` — удалить re-export строк 2-8
|
||||
- [x] `entities/broker-position/index.ts` — удалить BROKER_OPERATION_TYPE_OPTIONS, getBrokerOperationImpact, getBrokerOperationTypeLabel, isBrokerOperationType, BrokerOperationImpact
|
||||
- [x] `entities/broker-position/model/brokerDisplay.test.ts` — перенести импорты broker-operation напрямую
|
||||
- [x] **Task 2**: Дедуплицировать форматтеры
|
||||
- [x] Создать `shared/lib/formatters.ts` с каноничными реализациями
|
||||
- [x] `entities/broker-account/model/brokerAccountsOverview.ts` — перейти на импорт из shared
|
||||
- [x] `widgets/broker-account-card/BrokerAccountCard.tsx` — убрать дубликаты
|
||||
- [x] `widgets/broker-accounts-summary/BrokerAccountsSummary.tsx` — убрать дубликаты
|
||||
- [x] `widgets/broker-allocation-chart/BrokerAllocationChart.tsx` — убрать дубликаты
|
||||
- [x] `widgets/broker-operations-table/BrokerOperationsTable.tsx` — убрать дубликаты
|
||||
- [x] `widgets/portfolio-card/PortfolioCard.tsx` — убрать дубликаты
|
||||
- [x] `pages/broker-account/BrokerAccountOverviewPage.tsx` — убрать дубликаты
|
||||
- [x] `pages/broker-positions/BrokerPositionsPage.tsx` — убрать дубликаты
|
||||
- [x] **Task 3**: Уплостить shared/ui/broker-allocation-bar
|
||||
- [x] Переместить BrokerAllocationBar.tsx из `ui/` на уровень slice
|
||||
- [x] Обновить `index.ts`
|
||||
- [x] Удалить пустую `ui/`
|
||||
- [x] **Task 4**: Очистить entities/stock/index.ts
|
||||
- [x] Убрать getShare, getShareMarketData, getShareDividends, getShareHistory, getShareCandles
|
||||
- [x] **Проверка**: lint + build + тесты
|
||||
- [x] Build — OK
|
||||
- [x] Tests — 23/23 files, 111/111 passed
|
||||
Loading…
x
Reference in New Issue
Block a user