Frontend surfacing in the DEPLOYED trade-in UI (tradein-mvp/frontend): - types/trade-in.ts: add AvitoImvSummary + DkpCorridor, optional avito_imv / dkp_corridor on AggregatedEstimate (mirror backend snake_case, no any) - HeroSummary.tsx: 'Avito IMV: X млн ₽' caption near price + 'Коридор реальных сделок: X–Y млн · N ДКП (Росреестр)' line. No new fetch, no-op when null. tsc --noEmit clean. Root legacy frontend/ untouched.
This commit is contained in:
parent
70ee7b282c
commit
6a81e91a83
2 changed files with 62 additions and 0 deletions
|
|
@ -92,6 +92,18 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
|
||||||
|
|
||||||
const showMockDisclaimer = isMockAddress(estimate);
|
const showMockDisclaimer = isMockAddress(estimate);
|
||||||
|
|
||||||
|
// ── #651: Avito IMV anchor — реальная рыночная оценка дома. Маркер у цены. ──
|
||||||
|
const imv = estimate.avito_imv;
|
||||||
|
const imvPrice =
|
||||||
|
imv && typeof imv.recommended_price === "number" && imv.recommended_price > 0
|
||||||
|
? imv.recommended_price
|
||||||
|
: null;
|
||||||
|
// ── #652: коридор реальных ДКП-сделок (advisory). ₽/м² → млн через площадь. ──
|
||||||
|
const dkp = estimate.dkp_corridor;
|
||||||
|
const dkpArea = estimate.area_m2 ?? input.area_m2 ?? 0;
|
||||||
|
const showDkp =
|
||||||
|
!!dkp && dkp.count > 0 && dkpArea > 0 && dkp.low_ppm2 > 0 && dkp.high_ppm2 > 0;
|
||||||
|
|
||||||
// Progressive enrichment state
|
// Progressive enrichment state
|
||||||
const needsHouseType = estimate.house_type === null;
|
const needsHouseType = estimate.house_type === null;
|
||||||
const needsRepairState = estimate.repair_state === null;
|
const needsRepairState = estimate.repair_state === null;
|
||||||
|
|
@ -264,6 +276,15 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
|
||||||
<div className="price-figure-perm2 mono">
|
<div className="price-figure-perm2 mono">
|
||||||
{estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м²
|
{estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м²
|
||||||
</div>
|
</div>
|
||||||
|
{imvPrice !== null && (
|
||||||
|
<div
|
||||||
|
className="price-figure-imv mono"
|
||||||
|
title="Индекс Market Value Avito по этому дому — реальный рыночный якорь"
|
||||||
|
style={{ marginTop: 6, fontSize: 12, color: "var(--muted)" }}
|
||||||
|
>
|
||||||
|
Avito IMV: {formatMln(imvPrice)} ₽
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{hasSold && (
|
{hasSold && (
|
||||||
|
|
@ -299,6 +320,20 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showDkp && dkp && (
|
||||||
|
<p
|
||||||
|
className="hero-prices-note"
|
||||||
|
title="Коридор реальных ₽/м² по ДКП-сделкам Росреестра (справочно)"
|
||||||
|
>
|
||||||
|
<b>Коридор реальных сделок:</b>{" "}
|
||||||
|
<span className="mono">
|
||||||
|
{formatMln(dkp.low_ppm2 * dkpArea)} – {formatMln(dkp.high_ppm2 * dkpArea)} ₽
|
||||||
|
</span>{" "}
|
||||||
|
· <span className="mono">{dkp.median_ppm2.toLocaleString("ru-RU")} ₽/м²</span> ·{" "}
|
||||||
|
{dkp.count} ДКП (Росреестр)
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="hero-bars">
|
<div className="hero-bars">
|
||||||
<div className="bar-block">
|
<div className="bar-block">
|
||||||
<div className="bar-head">
|
<div className="bar-head">
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,27 @@ export interface CianValuationSummary {
|
||||||
// Базис коэффициента asking→sold (S3): по группе комнат либо городской fallback.
|
// Базис коэффициента asking→sold (S3): по группе комнат либо городской fallback.
|
||||||
export type RatioBasis = "per_rooms" | "global_fallback";
|
export type RatioBasis = "per_rooms" | "global_fallback";
|
||||||
|
|
||||||
|
// ── #651: Avito IMV (Индекс Market Value) якорь для target-дома ──
|
||||||
|
// Реальная рыночная оценка Avito по дому (house_imv_evaluations) — anchor для
|
||||||
|
// blend'а медианы. Сурфейсится как референсный маркер. null при отсутствии записи.
|
||||||
|
export interface AvitoImvSummary {
|
||||||
|
recommended_price: number | null; // рекомендованная цена Avito, ₽
|
||||||
|
lower_price: number | null; // нижняя граница IMV-коридора, ₽
|
||||||
|
higher_price: number | null; // верхняя граница IMV-коридора, ₽
|
||||||
|
market_count: number | null; // объём рынка, на котором построена оценка
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── #652: коридор реальных ДКП-сделок Росреестра (advisory, не клампит) ──
|
||||||
|
// Агрегаты ₽/м² по сопоставимым сделкам за период. null / count=0 если по улице
|
||||||
|
// нет сделок. count..period_months — required (бэкенд не отдаёт частичный объект).
|
||||||
|
export interface DkpCorridor {
|
||||||
|
count: number; // число ДКП-сделок в выборке
|
||||||
|
low_ppm2: number; // min ₽/м² по сделкам
|
||||||
|
median_ppm2: number; // медиана ₽/м²
|
||||||
|
high_ppm2: number; // max ₽/м²
|
||||||
|
period_months: number; // окно поиска сделок
|
||||||
|
}
|
||||||
|
|
||||||
export interface AggregatedEstimate {
|
export interface AggregatedEstimate {
|
||||||
estimate_id: string; // UUID
|
estimate_id: string; // UUID
|
||||||
median_price_rub: number; // ASKING-ориентир: по чему выставлены аналоги
|
median_price_rub: number; // ASKING-ориентир: по чему выставлены аналоги
|
||||||
|
|
@ -117,6 +138,12 @@ export interface AggregatedEstimate {
|
||||||
has_balcony: boolean | null;
|
has_balcony: boolean | null;
|
||||||
// ── Cian valuation surface ──
|
// ── Cian valuation surface ──
|
||||||
cian_valuation?: CianValuationSummary | null;
|
cian_valuation?: CianValuationSummary | null;
|
||||||
|
// ── #651/#652: внешние якоря ──
|
||||||
|
// avito_imv — реальная Avito IMV-оценка дома, anchor для blend'а медианы.
|
||||||
|
// dkp_corridor — коридор ₽/м² по ДКП-сделкам Росреестра (advisory). Оба nullable:
|
||||||
|
// null при отсутствии данных (graceful, common case без регрессий).
|
||||||
|
avito_imv?: AvitoImvSummary | null;
|
||||||
|
dkp_corridor?: DkpCorridor | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Stage 4a/4b response types ──
|
// ── Stage 4a/4b response types ──
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue