feat(tradein): HeroSummary empty-state при insufficient_data (#740) (#743)
All checks were successful
Deploy Trade-In / changes (push) Successful in 4s
Deploy Trade-In / build-frontend (push) Successful in 1m34s
Deploy Trade-In / deploy (push) Successful in 34s
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-backend (push) Has been skipped

Co-authored-by: bot-frontend <bot-frontend@gendsgn.local>
Co-committed-by: bot-frontend <bot-frontend@gendsgn.local>
This commit is contained in:
bot-frontend 2026-05-30 15:37:13 +00:00 committed by bot-reviewer
parent 34a13d71c3
commit 798fb12b4c
3 changed files with 51 additions and 27 deletions

View file

@ -95,6 +95,9 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
const m = estimate.median_price_rub; const m = estimate.median_price_rub;
const lo = estimate.range_low_rub; const lo = estimate.range_low_rub;
const hi = estimate.range_high_rub; const hi = estimate.range_high_rub;
// Пустая оценка (backend #697): нет аналогов/сделок → median<=0. Вместо
// «0,00 млн ₽» в headline показываем явный empty-state.
const insufficientData = estimate.insufficient_data || m <= 0;
// ── Ожидаемая цена продажи (S3). Рисуем второй блок только при валидном числе: // ── Ожидаемая цена продажи (S3). Рисуем второй блок только при валидном числе:
// null/undefined/0 (старые оценки или пустая ratio-таблица) → одиночный layout. ── // null/undefined/0 (старые оценки или пустая ratio-таблица) → одиночный layout. ──
@ -294,6 +297,18 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
запроса, с X%). Без него (старые оценки / пустая ratio) headline = запроса, с X%). Без него (старые оценки / пустая ratio) headline =
asking-медиана, чтобы блок не оставался без главного числа. */} asking-медиана, чтобы блок не оставался без главного числа. */}
<div className="hero-headline"> <div className="hero-headline">
{insufficientData ? (
<>
<div className="headline-label">Оценка недоступна</div>
<div className="headline-value-empty">Недостаточно данных для оценки</div>
<p className="headline-note">
Рядом не нашлось достаточно сопоставимых объявлений и сделок, чтобы
рассчитать цену. Уточните адрес или параметры объекта и попробуйте
снова.
</p>
</>
) : (
<>
<div className="headline-label"> <div className="headline-label">
{hasSold ? "Ожидаемая цена сделки" : "Рекомендованная цена"} {hasSold ? "Ожидаемая цена сделки" : "Рекомендованная цена"}
{showDiscount && ( {showDiscount && (
@ -322,6 +337,8 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
ожидаемая цена сделки по данным ДКП Росреестра, а не цена запроса. ожидаемая цена сделки по данным ДКП Росреестра, а не цена запроса.
</p> </p>
)} )}
</>
)}
</div> </div>
{/* Доверие + действия (web-преимущества над PDF): свежесть данных, {/* Доверие + действия (web-преимущества над PDF): свежесть данных,

View file

@ -705,6 +705,12 @@
letter-spacing: -0.025em; letter-spacing: -0.025em;
color: var(--fg); color: var(--fg);
} }
.headline-value-empty {
font-size: 22px;
font-weight: 600;
line-height: 1.27;
color: var(--fg);
}
.headline-range { .headline-range {
font-size: 12px; font-size: 12px;
color: var(--muted); color: var(--muted);

View file

@ -123,6 +123,7 @@ export interface AggregatedEstimate {
confidence: ConfidenceLevel; confidence: ConfidenceLevel;
confidence_explanation: string | null; confidence_explanation: string | null;
n_analogs: number; n_analogs: number;
insufficient_data: boolean; // backend #697: true когда median_price_rub <= 0 (нет данных)
period_months: number; // 24 period_months: number; // 24
analogs: AnalogLot[]; // top 5-10 analogs: AnalogLot[]; // top 5-10
actual_deals: AnalogLot[]; // last 12 mo actual_deals: AnalogLot[]; // last 12 mo