feat(tradein/v2): показать срок экспозиции в карточке «ожидаемая цена сделки»
All checks were successful
CI / changes (pull_request) Successful in 8s
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m4s

Подстрока «срок продажи ~N дн» под пилюлей дельты во второй карточке
02-РЕЗУЛЬТАТ, источник — analytics.kpi.median_exposure_days (реальные
данные house_placement_history). Не используем est_days_on_market —
считается по deals.days_on_market, который 0 в 96974/96974 строк.
This commit is contained in:
bot-backend 2026-08-02 19:01:59 +03:00
parent 8c0511e0bd
commit be5771931e
4 changed files with 28 additions and 2 deletions

View file

@ -599,8 +599,9 @@ export default function TradeInV2Page() {
[locationIndexData], [locationIndexData],
); );
const resultPanelData = useMemo( const resultPanelData = useMemo(
() => (estimate ? mapResultPanel(estimate, streetDealsData) : null), () =>
[estimate, streetDealsData], estimate ? mapResultPanel(estimate, streetDealsData, analyticsData) : null,
[estimate, streetDealsData, analyticsData],
); );
const summaryData = useMemo( const summaryData = useMemo(
() => () =>

View file

@ -484,6 +484,7 @@ export default function ResultPanel({
// M2 — calm delta pill (was a 51px circular gauge that read like a // M2 — calm delta pill (was a 51px circular gauge that read like a
// tech "занижение" indicator). Full text «18% к цене объявления», // tech "занижение" indicator). Full text «18% к цене объявления»,
// neutral accentDeep on a soft-blue chip, tabular-nums. // neutral accentDeep on a soft-blue chip, tabular-nums.
<>
<div <div
style={{ style={{
display: "flex", display: "flex",
@ -549,6 +550,20 @@ export default function ResultPanel({
Подробнее <span></span> Подробнее <span></span>
</button> </button>
</div> </div>
{card.exposureNote && (
<div
style={{
fontSize: 9,
color: muted2,
marginTop: 4,
lineHeight: 1.4,
maxWidth: "82%",
}}
>
{card.exposureNote}
</div>
)}
</>
)} )}
</div> </div>
); );

View file

@ -1038,6 +1038,7 @@ export function mapMarkers(e: AggregatedEstimate | null): MapMarker[] {
export function mapResultPanel( export function mapResultPanel(
e: AggregatedEstimate, e: AggregatedEstimate,
streetDeals?: StreetDealsResponse | null, streetDeals?: StreetDealsResponse | null,
analytics?: HouseAnalyticsResponse | null,
): ResultPanelData { ): ResultPanelData {
const dealTier = resolveDealTier(e, streetDeals); const dealTier = resolveDealTier(e, streetDeals);
@ -1105,6 +1106,13 @@ export function mapResultPanel(
emptyNote: hasExpected emptyNote: hasExpected
? undefined ? 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: note:
hasExpected && hasExpected &&
dealTier != null && dealTier != null &&

View file

@ -55,6 +55,8 @@ export interface ResultCard {
deltaLabel?: string; deltaLabel?: string;
/** Reconciliation note under the value (card 2: expected price vs ДКП median). */ /** Reconciliation note under the value (card 2: expected price vs ДКП median). */
note?: string; note?: string;
/** Медиана срока экспозиции по дому/району (house_placement_history), подстрока карточки 2. */
exposureNote?: string;
/** H1 empty-state message: when set (card 2, no expected-sold prediction) the /** 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 * card renders this instead of value/range/ppm/delta, and the panel moves the
* accent frame onto the «рекомендованная цена» card. */ * accent frame onto the «рекомендованная цена» card. */