diff --git a/tradein-mvp/frontend/src/app/v2/page.tsx b/tradein-mvp/frontend/src/app/v2/page.tsx index 813ed0c0..e398c129 100644 --- a/tradein-mvp/frontend/src/app/v2/page.tsx +++ b/tradein-mvp/frontend/src/app/v2/page.tsx @@ -599,8 +599,9 @@ export default function TradeInV2Page() { [locationIndexData], ); const resultPanelData = useMemo( - () => (estimate ? mapResultPanel(estimate, streetDealsData) : null), - [estimate, streetDealsData], + () => + estimate ? mapResultPanel(estimate, streetDealsData, analyticsData) : null, + [estimate, streetDealsData, analyticsData], ); const summaryData = useMemo( () => diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/ResultPanel.tsx b/tradein-mvp/frontend/src/components/trade-in/v2/ResultPanel.tsx index 088fc12a..6bb5bc15 100644 --- a/tradein-mvp/frontend/src/components/trade-in/v2/ResultPanel.tsx +++ b/tradein-mvp/frontend/src/components/trade-in/v2/ResultPanel.tsx @@ -484,6 +484,7 @@ export default function ResultPanel({ // M2 — calm delta pill (was a 51px circular gauge that read like a // tech "занижение" indicator). Full text «−18% к цене объявления», // neutral accentDeep on a soft-blue chip, tabular-nums. + <>
+ {card.exposureNote && ( +
+ {card.exposureNote} +
+ )} + )} ); diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/mappers.ts b/tradein-mvp/frontend/src/components/trade-in/v2/mappers.ts index 3c965409..79fd9f1e 100644 --- a/tradein-mvp/frontend/src/components/trade-in/v2/mappers.ts +++ b/tradein-mvp/frontend/src/components/trade-in/v2/mappers.ts @@ -1038,6 +1038,7 @@ export function mapMarkers(e: AggregatedEstimate | null): MapMarker[] { export function mapResultPanel( e: AggregatedEstimate, streetDeals?: StreetDealsResponse | null, + analytics?: HouseAnalyticsResponse | null, ): ResultPanelData { const dealTier = resolveDealTier(e, streetDeals); @@ -1105,6 +1106,13 @@ export function mapResultPanel( emptyNote: hasExpected ? undefined : "Недостаточно данных для прогноза цены сделки — ориентируйтесь на рекомендованную цену в объявлении", + // Median exposure days (house/district), from house_placement_history — + // gated on hasExpected: when the card has no forecast it renders only + // emptyNote (see above), the exposure aside has nowhere honest to sit. + exposureNote: + hasExpected && analytics?.kpi?.median_exposure_days != null + ? `срок продажи ~${analytics.kpi.median_exposure_days} дн` + : undefined, note: hasExpected && dealTier != null && diff --git a/tradein-mvp/frontend/src/components/trade-in/v2/types.ts b/tradein-mvp/frontend/src/components/trade-in/v2/types.ts index 32021e07..81ca7422 100644 --- a/tradein-mvp/frontend/src/components/trade-in/v2/types.ts +++ b/tradein-mvp/frontend/src/components/trade-in/v2/types.ts @@ -55,6 +55,8 @@ export interface ResultCard { deltaLabel?: string; /** Reconciliation note under the value (card 2: expected price vs ДКП median). */ note?: string; + /** Медиана срока экспозиции по дому/району (house_placement_history), подстрока карточки 2. */ + exposureNote?: string; /** H1 empty-state message: when set (card 2, no expected-sold prediction) the * card renders this instead of value/range/ppm/delta, and the panel moves the * accent frame onto the «рекомендованная цена» card. */