fix(tradein): gate «0,00 млн ₽» в не-headline секциях при insufficient_data

QA side-finding #748 из #740: headline empty-state починен, но median=0
рендерил «0,00 млн ₽» ещё в 8 местах. Гейтим на insufficient_data/median>0:
- HeroSummary: hero-prices + hero-bars блоки скрыты при insufficientData
- ListingsCard: count-cell медиана + pricebar title → «—» при m<=0
- OfferCard: «Оценка по рынку» → «—»
- WhatIfPanel: «исходная оценка» → «—» при basePrice<=0
- SourcesProgress: «медиана:» → «—»
Happy-path (median>0) не тронут.

Refs #748
This commit is contained in:
bot-frontend 2026-05-30 18:55:47 +03:00
parent a74591d7cc
commit fe2f79792f
5 changed files with 25 additions and 8 deletions

View file

@ -352,7 +352,10 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
{/* Два сопоставимых ценовых ориентира рынка (РАЗНЫЕ источники)
asking (объявления) vs реальные сделки ДКП. Оба рыночный контекст
под headline-итогом, поэтому одинакового веса. */}
под headline-итогом, поэтому одинакового веса.
При insufficient_data (median=0) оба блока скрыты иначе «0,00 млн » (#748). */}
{!insufficientData && (
<>
<div className={`hero-prices${showDkp ? "" : " single"}`}>
<div className="price-figure" data-kind="asking">
<div className="price-figure-label">Рекомендованная цена в объявлении</div>
@ -466,6 +469,8 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
);
})()}
</div>
</>
)}
{/* ── Источники данных (#648 IA, task #5) — какие источники участвовали ── */}
<div className="filters-row" style={{ padding: "14px 22px 0" }}>

View file

@ -136,10 +136,18 @@ export function ListingsCard({ estimate, estimateId }: Props) {
<div className="count-cell">
<div className="label">Медиана</div>
<div className="value">
<span data-tnum>{(m / 1_000_000).toFixed(2)}</span>
<span className="unit">млн </span>
{m > 0 ? (
<>
<span data-tnum>{(m / 1_000_000).toFixed(2)}</span>
<span className="unit">млн </span>
</>
) : (
<span data-tnum></span>
)}
</div>
<div className="sub">
{m > 0 ? `${estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м²` : "нет данных"}
</div>
<div className="sub">{estimate.median_price_per_m2.toLocaleString("ru-RU")} /м²</div>
</div>
<div className="count-cell">
<div className="label">Диапазон P25P75</div>
@ -201,7 +209,8 @@ export function ListingsCard({ estimate, estimateId }: Props) {
className="bar-title"
style={{ color: "var(--fg-2)", fontSize: 12, letterSpacing: "0.04em" }}
>
ДИАПАЗОН ЦЕН В ОБЪЯВЛЕНИЯХ · {(m / 1_000_000).toFixed(2)} МЛН медиана
ДИАПАЗОН ЦЕН В ОБЪЯВЛЕНИЯХ
{m > 0 ? ` · ${(m / 1_000_000).toFixed(2)} МЛН ₽ медиана` : ""}
</span>
</div>
<div className="pricebar">

View file

@ -84,7 +84,10 @@ export function OfferCard({ estimate, brandSlug }: Props) {
</div>
<div className="card-meta">
<div>
Оценка по рынку: <b className="mono">{formatMln(estimate.median_price_rub)} </b>
Оценка по рынку:{" "}
<b className="mono">
{estimate.insufficient_data ? "—" : `${formatMln(estimate.median_price_rub)}`}
</b>
</div>
<div style={{ marginTop: 4 }}>медиана цен в объявлениях аналогов</div>
</div>

View file

@ -118,7 +118,7 @@ export function SourcesProgress({ estimate, isPending }: Props) {
<div style={{ marginTop: 4 }}>
медиана:{" "}
<b className="mono" style={{ color: "var(--fg)" }}>
{formatMln(estimate.median_price_rub)}
{estimate.insufficient_data ? "—" : `${formatMln(estimate.median_price_rub)}`}
</b>
</div>
)}

View file

@ -328,7 +328,7 @@ export function WhatIfPanel({ baseEstimate, baseInput }: Props) {
</>
) : (
<span className="whatif-baseline mono">
исходная оценка {formatMln(basePrice)}
{basePrice > 0 ? `исходная оценка ${formatMln(basePrice)}` : "исходная оценка —"}
</span>
)}
</div>