Merge pull request 'fix(trade-in/ui): расшифровка ДКП/IMV/CV + графики на --viz токены + убрать emoji (#1871)' (#1922) from fix/tradein-redesign-pack into main
Some checks failed
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / changes (push) Successful in 9s
Deploy Trade-In / test (push) Failing after 1m29s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m2s
Deploy Trade-In / deploy (push) Has been skipped

Reviewed-on: #1922
This commit is contained in:
lekss361 2026-06-26 16:19:23 +00:00
commit d111573a4b
8 changed files with 51 additions and 27 deletions

View file

@ -191,7 +191,19 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
lineHeight: 1.4,
}}
>
Адрес распознан неточно оценка может быть приближённой.
<span
aria-hidden="true"
style={{
display: "inline-block",
width: 8,
height: 8,
borderRadius: "50%",
background: "var(--warn)",
marginRight: 8,
verticalAlign: "middle",
}}
/>
Адрес распознан неточно оценка может быть приближённой.
Уточните адрес и пересчитайте для более точного результата.
</div>
)}
@ -208,7 +220,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
</b>
</div>
<div style={{ marginTop: 4 }}>
Достоверность · <b style={{ color: conf.color }}>{conf.txt}</b> · CV {cv.toFixed(1)}%
Достоверность · <b style={{ color: conf.color }}>{conf.txt}</b> · <abbr title="Коэффициент вариации (разброс цен аналогов)">CV</abbr> {cv.toFixed(1)}%
{precisionBadge && (
<span className={precisionBadge.className} title="Точность определения адреса">
{precisionBadge.text}
@ -376,7 +388,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
<div className="hero-benchmarks">
{imvPrice !== null && (
<div className="bench-chip">
<span className="bench-chip__src">Avito IMV</span>
<span className="bench-chip__src">Avito <abbr title="Information Market Value — оценка рыночной стоимости от Avito">IMV</abbr></span>
<span className="bench-chip__val mono">{formatMln(imvPrice)} </span>
</div>
)}
@ -395,7 +407,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
)}
{showDkp && dkp && (
<div className="bench-chip">
<span className="bench-chip__src">ДКП · Росреестр</span>
<span className="bench-chip__src"><abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> · Росреестр</span>
<span className="bench-chip__val mono">
{formatMln(dkp.median_ppm2 * dkpArea)}
</span>
@ -438,7 +450,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
title="Индекс Market Value Avito по этому дому — реальный рыночный якорь"
style={{ marginTop: 6, fontSize: 12, color: "var(--muted)" }}
>
Avito IMV: {formatMln(imvPrice)}
Avito <abbr title="Information Market Value — оценка рыночной стоимости от Avito">IMV</abbr>: {formatMln(imvPrice)}
</div>
)}
</div>
@ -446,7 +458,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
{showDkp && dkp && (
<div className="price-figure" data-kind="dkp">
<div className="price-figure-label">
Цены по фактическим сделкам (ДКП · Росреестр)
Цены по фактическим сделкам (<abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> · Росреестр)
</div>
<div className="price-figure-value mono">
{formatMln(dkp.median_ppm2 * dkpArea)}
@ -455,7 +467,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
{formatMln(dkp.low_ppm2 * dkpArea)} {formatMln(dkp.high_ppm2 * dkpArea)}
</div>
<div className="price-figure-perm2 mono">
{dkp.median_ppm2.toLocaleString("ru-RU")} /м² · {dkp.count} ДКП за{" "}
{dkp.median_ppm2.toLocaleString("ru-RU")} /м² · {dkp.count} <abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> за{" "}
{dkp.period_months} мес
</div>
</div>
@ -641,7 +653,7 @@ export function HeroSummary({ estimate, input, onResubmit, isResubmitting = fals
</span>
<div>
<b>Цены в объявлениях это ожидания собственников.</b> Реальные сделки
проходят на <b>512% ниже</b> по данным ДКП Росреестра.
проходят на <b>512% ниже</b> по данным <abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> Росреестра.
</div>
</div>
<div className="warn-row">

View file

@ -253,9 +253,20 @@ export function HeroTransparency({ estimate, brandSlug, brandName }: Props) {
{showPrecisionWarning && (
<span
aria-label="Предупреждение: адрес определён приблизительно при низкой достоверности"
style={{ marginLeft: 6 }}
style={{ marginLeft: 6, display: "inline-flex", alignItems: "center", gap: 4 }}
>
<span
aria-hidden="true"
style={{
display: "inline-block",
width: 7,
height: 7,
borderRadius: "50%",
background: "var(--warn)",
verticalAlign: "middle",
}}
/>
<span style={{ fontSize: 11, color: "var(--warn)", fontWeight: 600 }}>Приблизительно</span>
</span>
)}
</span>

View file

@ -48,7 +48,7 @@ export function IMVBenchmark({ benchmark, isLoading }: Props) {
return (
<section className="imv-benchmark" aria-label="Avito IMV benchmark">
<header className="imv-benchmark__head">
<span className="imv-benchmark__title">Avito IMV</span>
<span className="imv-benchmark__title">Avito <abbr title="Information Market Value — оценка рыночной стоимости от Avito">IMV</abbr></span>
<a
href="https://www.avito.ru/evaluation/realty"
target="_blank"

View file

@ -16,10 +16,11 @@ const LEAFLET_JS = `https://unpkg.com/leaflet@${LEAFLET_VER}/dist/leaflet.js`;
const LEAFLET_CSS_SRI = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=";
const LEAFLET_JS_SRI = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=";
// Цвета пинов: asking (синий) / ДКП (зелёный) / target (оранжевый акцент).
const COLOR_ANALOG = "#2563eb";
const COLOR_DEAL = "#16a34a";
const COLOR_TARGET = "#ea580c";
// Цвета пинов: asking (синий --viz-1) / ДКП (бирюза --viz-3) / target (янтарь --viz-4).
// Leaflet принимает строку-цвет напрямую (не CSS vars) — используем hex-значения токенов.
const COLOR_ANALOG = "#1d4ed8"; // --viz-1
const COLOR_DEAL = "#14b8a6"; // --viz-3
const COLOR_TARGET = "#f59e0b"; // --viz-4
/** Подгружает Leaflet с CDN один раз, резолвит window.L. (mirror MapPicker) */
function loadLeaflet(): Promise<any> {

View file

@ -130,9 +130,9 @@ export function MapPicker({ onPick, onClose }: Props) {
else
marker = L.circleMarker([lat, lon], {
radius: 8,
color: "#1d4ed8",
color: "#1d4ed8", // --viz-1 hex (Leaflet не принимает CSS vars)
weight: 2,
fillColor: "#1d4ed8",
fillColor: "#1d4ed8", // --viz-1 hex
fillOpacity: 0.45,
}).addTo(map);
setAddress(null);

View file

@ -79,7 +79,7 @@ export function PriceHistoryChart({ points }: Props) {
<Line
type="monotone"
dataKey="avito_imv"
stroke="#2563eb"
stroke="var(--viz-1)"
strokeWidth={2}
dot={{ r: 3 }}
name="Avito"
@ -88,7 +88,7 @@ export function PriceHistoryChart({ points }: Props) {
<Line
type="monotone"
dataKey="yandex_valuation"
stroke="#ea580c"
stroke="var(--viz-4)"
strokeWidth={2}
dot={{ r: 3 }}
name="Яндекс"

View file

@ -30,7 +30,7 @@ export function PriceRangeBar({ rangeLow, rangeHigh, median }: Props) {
position: "relative",
height: 10,
borderRadius: 999,
background: "linear-gradient(to right, #bfdbfe, #1d4ed8)",
background: "linear-gradient(to right, color-mix(in srgb, var(--viz-1) 30%, white), var(--viz-1))",
}}
>
{/* Median marker */}
@ -44,7 +44,7 @@ export function PriceRangeBar({ rangeLow, rangeHigh, median }: Props) {
width: 14,
height: 14,
borderRadius: "50%",
background: "#1d4ed8",
background: "var(--viz-1)",
border: "2px solid #fff",
boxShadow: "0 1px 4px rgba(0,0,0,0.25)",
}}
@ -65,7 +65,7 @@ export function PriceRangeBar({ rangeLow, rangeHigh, median }: Props) {
<span
style={{
fontWeight: 700,
color: "#1d4ed8",
color: "var(--viz-1)",
fontSize: 13,
}}
>

View file

@ -12,10 +12,10 @@ const BUCKET_LABELS: Record<string, string> = {
};
const BUCKET_COLORS: Record<string, string> = {
cheap: "#dcfce7", // light green
median: "#dbeafe", // light blue
plus5: "#fef3c7", // light yellow
plus10: "#fee2e2", // light red
cheap: "var(--success-soft)", // light green
median: "var(--accent-soft)", // light blue
plus5: "var(--warn-soft)", // light yellow
plus10: "var(--danger-soft)", // light red
};
function BucketCard({ bucket }: { bucket: SellTimeBucket }) {
@ -30,7 +30,7 @@ function BucketCard({ bucket }: { bucket: SellTimeBucket }) {
background: BUCKET_COLORS[bucket.price_premium_label] ?? "#f9fafb",
border:
bucket.price_premium_label === "median"
? "2px solid #2563eb"
? "2px solid var(--viz-1)"
: "1px solid var(--border, #e5e7eb)",
}}
>