fix(tradein): фото аналога в Сводке + честный статус источников #414
2 changed files with 25 additions and 5 deletions
|
|
@ -1,5 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
/* eslint-disable @next/next/no-img-element -- фото аналога с внешнего CDN, next/image не нужен */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HeroSummary — Секция 1 «Сводка» из mockup tradein.html.
|
* HeroSummary — Секция 1 «Сводка» из mockup tradein.html.
|
||||||
* Показывает медиану + достоверность CV + параметры объекта + 2 ценовых бара (объявления / сделки).
|
* Показывает медиану + достоверность CV + параметры объекта + 2 ценовых бара (объявления / сделки).
|
||||||
|
|
@ -49,6 +51,8 @@ export function HeroSummary({ estimate, input }: Props) {
|
||||||
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;
|
||||||
|
// Фото первого аналога с картинкой — вместо пустого серого плейсхолдера.
|
||||||
|
const heroPhoto = estimate.analogs.find((a) => a.photo_url)?.photo_url ?? null;
|
||||||
// Расчёт ширины для price bar (50% = середина): медиана внутри min/max
|
// Расчёт ширины для price bar (50% = середина): медиана внутри min/max
|
||||||
const span = hi - lo;
|
const span = hi - lo;
|
||||||
const medianPctRaw = span > 0 ? ((m - lo) / span) * 100 : 50;
|
const medianPctRaw = span > 0 ? ((m - lo) / span) * 100 : 50;
|
||||||
|
|
@ -75,11 +79,23 @@ export function HeroSummary({ estimate, input }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hero-top">
|
<div className="hero-top">
|
||||||
<div className="hero-photo" role="img" aria-label="Фасад дома">
|
<div className="hero-photo" role="img" aria-label="Фото квартиры-аналога">
|
||||||
|
{heroPhoto && (
|
||||||
|
<img
|
||||||
|
src={heroPhoto}
|
||||||
|
alt="Фото похожего объекта"
|
||||||
|
onError={(e) => {
|
||||||
|
e.currentTarget.style.display = "none";
|
||||||
|
}}
|
||||||
|
style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<div className="photo-meta">
|
<div className="photo-meta">
|
||||||
{estimate.sources_used.length > 0
|
{heroPhoto
|
||||||
? `${estimate.sources_used[0]} · ${estimate.n_analogs} аналогов`
|
? `фото аналога${estimate.sources_used[0] ? ` · ${estimate.sources_used[0]}` : ""}`
|
||||||
: "Нет фото"}
|
: estimate.sources_used.length > 0
|
||||||
|
? `${estimate.sources_used[0]} · ${estimate.n_analogs} аналогов`
|
||||||
|
: "Нет фото"}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="hero-meta">
|
<div className="hero-meta">
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,11 @@ export function SourcesProgress({ estimate, isPending }: Props) {
|
||||||
{r.status === "done" && r.count === undefined && <b>готово</b>}
|
{r.status === "done" && r.count === undefined && <b>готово</b>}
|
||||||
{r.status === "loading" && "сбор..."}
|
{r.status === "loading" && "сбор..."}
|
||||||
{r.status === "error" && "timeout — нет ответа"}
|
{r.status === "error" && "timeout — нет ответа"}
|
||||||
{r.status === "idle" && <span style={{ color: "var(--muted-2)" }}>ожидает запрос</span>}
|
{r.status === "idle" && (
|
||||||
|
<span style={{ color: "var(--muted-2)" }}>
|
||||||
|
{isDone ? "нет данных" : "ожидает запрос"}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue