feat(tradein): HeroSummary empty-state при insufficient_data
При пустой оценке (median<=0, backend #697) headline показывал «0,00 млн ₽». Теперь рендерим явный empty-state «Недостаточно данных для оценки» вместо нулевого числа. Добавлено поле insufficient_data в AggregatedEstimate. Refs #740
This commit is contained in:
parent
34a13d71c3
commit
9af88ebaa5
3 changed files with 51 additions and 27 deletions
|
|
@ -95,6 +95,9 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
|
|||
const m = estimate.median_price_rub;
|
||||
const lo = estimate.range_low_rub;
|
||||
const hi = estimate.range_high_rub;
|
||||
// Пустая оценка (backend #697): нет аналогов/сделок → median<=0. Вместо
|
||||
// «0,00 млн ₽» в headline показываем явный empty-state.
|
||||
const insufficientData = estimate.insufficient_data || m <= 0;
|
||||
|
||||
// ── Ожидаемая цена продажи (S3). Рисуем второй блок только при валидном числе:
|
||||
// null/undefined/0 (старые оценки или пустая ratio-таблица) → одиночный layout. ──
|
||||
|
|
@ -294,33 +297,47 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
|
|||
запроса, с −X%). Без него (старые оценки / пустая ratio) — headline =
|
||||
asking-медиана, чтобы блок не оставался без главного числа. */}
|
||||
<div className="hero-headline">
|
||||
<div className="headline-label">
|
||||
{hasSold ? "Ожидаемая цена сделки" : "Рекомендованная цена"}
|
||||
{showDiscount && (
|
||||
<span
|
||||
className="headline-delta"
|
||||
aria-label={`на ${discountPct}% ниже рекомендованной цены в объявлении`}
|
||||
>
|
||||
−{discountPct}% к объявлению
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="headline-value mono">
|
||||
{formatMln(hasSold ? (sold as number) : m)} ₽
|
||||
</div>
|
||||
{hasSold && typeof soldLo === "number" && typeof soldHi === "number" && (
|
||||
<div className="headline-range mono">
|
||||
ожидаемый диапазон {formatMln(soldLo)} – {formatMln(soldHi)} ₽
|
||||
{typeof soldPerM2 === "number"
|
||||
? ` · ${soldPerM2.toLocaleString("ru-RU")} ₽/м²`
|
||||
: ""}
|
||||
</div>
|
||||
)}
|
||||
{hasSold && (
|
||||
<p className="headline-note">
|
||||
Реальные сделки проходят на <b>5–12% ниже</b> цен в объявлениях — это
|
||||
ожидаемая цена сделки по данным ДКП Росреестра, а не цена запроса.
|
||||
</p>
|
||||
{insufficientData ? (
|
||||
<>
|
||||
<div className="headline-label">Оценка недоступна</div>
|
||||
<div className="headline-value-empty">Недостаточно данных для оценки</div>
|
||||
<p className="headline-note">
|
||||
Рядом не нашлось достаточно сопоставимых объявлений и сделок, чтобы
|
||||
рассчитать цену. Уточните адрес или параметры объекта и попробуйте
|
||||
снова.
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="headline-label">
|
||||
{hasSold ? "Ожидаемая цена сделки" : "Рекомендованная цена"}
|
||||
{showDiscount && (
|
||||
<span
|
||||
className="headline-delta"
|
||||
aria-label={`на ${discountPct}% ниже рекомендованной цены в объявлении`}
|
||||
>
|
||||
−{discountPct}% к объявлению
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="headline-value mono">
|
||||
{formatMln(hasSold ? (sold as number) : m)} ₽
|
||||
</div>
|
||||
{hasSold && typeof soldLo === "number" && typeof soldHi === "number" && (
|
||||
<div className="headline-range mono">
|
||||
ожидаемый диапазон {formatMln(soldLo)} – {formatMln(soldHi)} ₽
|
||||
{typeof soldPerM2 === "number"
|
||||
? ` · ${soldPerM2.toLocaleString("ru-RU")} ₽/м²`
|
||||
: ""}
|
||||
</div>
|
||||
)}
|
||||
{hasSold && (
|
||||
<p className="headline-note">
|
||||
Реальные сделки проходят на <b>5–12% ниже</b> цен в объявлениях — это
|
||||
ожидаемая цена сделки по данным ДКП Росреестра, а не цена запроса.
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -705,6 +705,12 @@
|
|||
letter-spacing: -0.025em;
|
||||
color: var(--fg);
|
||||
}
|
||||
.headline-value-empty {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
line-height: 1.27;
|
||||
color: var(--fg);
|
||||
}
|
||||
.headline-range {
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ export interface AggregatedEstimate {
|
|||
confidence: ConfidenceLevel;
|
||||
confidence_explanation: string | null;
|
||||
n_analogs: number;
|
||||
insufficient_data: boolean; // backend #697: true когда median_price_rub <= 0 (нет данных)
|
||||
period_months: number; // 24
|
||||
analogs: AnalogLot[]; // top 5-10
|
||||
actual_deals: AnalogLot[]; // last 12 mo
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue