fix(trade-in/ui): расшифровка ДКП/IMV/CV + графики на --viz токены + убрать emoji из вердиктов (#1871)
- HeroSummary: все вхождения ДКП/IMV/CV обёрнуты в <abbr title="..."> с расшифровкой
- IMVBenchmark: «Avito IMV» → abbr с title
- HeroSummary/HeroTransparency: emoji ⚠️ заменены на span-точки var(--warn) + текст
- PriceHistoryChart: stroke="#2563eb"/"#ea580c" → var(--viz-1)/var(--viz-4)
- PriceRangeBar: "#bfdbfe"/"#1d4ed8" → color-mix(--viz-1)/var(--viz-1)
- SellTimeSensitivity: hardcoded bg-цвета → --success-soft/--accent-soft/--warn-soft/--danger-soft; border → var(--viz-1)
- MapCard: пины обновлены на hex-значения --viz-1/--viz-3/--viz-4 (Leaflet JS, не CSS-контекст)
- MapPicker: комментарий -- viz-1 hex (Leaflet constraint)
This commit is contained in:
parent
2fe34ac785
commit
27766acf38
8 changed files with 51 additions and 27 deletions
|
|
@ -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>5–12% ниже</b> по данным ДКП Росреестра.
|
||||
проходят на <b>5–12% ниже</b> по данным <abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> Росреестра.
|
||||
</div>
|
||||
</div>
|
||||
<div className="warn-row">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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="Яндекс"
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -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)",
|
||||
}}
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue