Честная полировка живого /trade-in/v2 без правок архитектуры (FE-only). - Провенанс из data, без хардкодов: footer «ПОСТРОЕНО ПО N АНАЛОГАМ И M СДЕЛКАМ» из meta.builtOn; единый счётчик источников (activeSourceCount = залитые плитки) в шапке 02, СВОДКЕ и оверлее; убраны «6/10», «2/7», «Avito·10», «из 2 источников». - Единый ru-форматтер: запятая везде (CV/площадь/%), NBSP число+единица, один en-dash и одна точность для денежных диапазонов. - CTA «ОЦЕНИТЬ КВАРТИРУ» → solid brand-blue + белый текст; :focus-visible HUD-glow на инпутах и кнопке. - Лендмарки <nav>/<main>/<footer> (display:contents, 0 визуальных изменений); scale-fit артборда — на 1366×768 / 1440×900 ужимается, не режется. - Мини-карта: честный зум (min=1, без пустых краёв при отдалении), убран мёртвый crosshair-двойник, расклеены налезающие метки цен. - CRM-дропдаун явно задизейблен («скоро», без ▼); убран «ШАГ 1/1». - Карточка 02 → «ОЖИДАЕМАЯ ЦЕНА СДЕЛКИ · с учётом торга» + строка-примирение с медианой ДКП; КОЭФ.ЛОКАЦИИ → muted-пилюля «скоро». - Бейдж СВОДКИ «В РАСЧЁТЕ» → «АКТУАЛЬНО»; скрыты целиком-прочерк колонки (ИСТОЧНИКОВ в 07, ОБЪЯВЛЕНИЕ ДО СДЕЛКИ в 04) data-driven.
312 lines
8.7 KiB
TypeScript
312 lines
8.7 KiB
TypeScript
// 03 СВОДКА ОБЪЕКТА — right-rail summary card for the /trade-in/v2 "МЕРА Оценка"
|
||
// design port. Faithful markup port of МЕРА Оценка.dc.html lines 397-422:
|
||
// address + "В РАСЧЁТЕ" badge, four clickable per-section totals, and a
|
||
// "КАЧЕСТВО ДАННЫХ" block with a donut. Data from fixtures; clicks fire
|
||
// onNavigate(row.nav) only — no API, no fetching.
|
||
|
||
"use client";
|
||
|
||
import { tokens } from "./tokens";
|
||
import { object, summary } from "./fixtures";
|
||
import type { ObjectSummaryData } from "./mappers";
|
||
|
||
// Default presentation data (unwired usage): the existing design fixtures.
|
||
const OBJECT_SUMMARY_FIXTURE: ObjectSummaryData = { object, summary };
|
||
|
||
interface ObjectSummaryProps {
|
||
data?: ObjectSummaryData;
|
||
onNavigate: (i: number) => void;
|
||
}
|
||
|
||
export function ObjectSummary({
|
||
data = OBJECT_SUMMARY_FIXTURE,
|
||
onNavigate,
|
||
}: ObjectSummaryProps) {
|
||
return (
|
||
<div
|
||
style={{
|
||
position: "relative",
|
||
background: tokens.surface.w50,
|
||
backdropFilter: "blur(6px)",
|
||
border: `1px solid ${tokens.line2}`,
|
||
borderRadius: 8,
|
||
padding: "15px 16px",
|
||
flex: 1,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
color: tokens.ink,
|
||
fontFamily: tokens.font.sans,
|
||
}}
|
||
>
|
||
<style>{`
|
||
.os-row { transition: background .12s; }
|
||
.os-row:hover { background: rgba(46,139,255,.07); }
|
||
`}</style>
|
||
|
||
{/* HUD corner bracket */}
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
right: -1,
|
||
top: -1,
|
||
width: 12,
|
||
height: 12,
|
||
borderRight: `1.5px solid ${tokens.bracket}`,
|
||
borderTop: `1.5px solid ${tokens.bracket}`,
|
||
}}
|
||
/>
|
||
|
||
{/* Header: 03 СВОДКА ОБЪЕКТА + В РАСЧЁТЕ badge */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
marginBottom: 5,
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", alignItems: "baseline", gap: 9 }}>
|
||
<span
|
||
style={{
|
||
fontFamily: tokens.font.mono,
|
||
fontSize: 14,
|
||
color: tokens.accent,
|
||
}}
|
||
>
|
||
03
|
||
</span>
|
||
<span style={{ fontSize: 12, fontWeight: 600, letterSpacing: 1.5 }}>
|
||
СВОДКА ОБЪЕКТА
|
||
</span>
|
||
</div>
|
||
<span
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 4,
|
||
fontSize: 8,
|
||
letterSpacing: 0.8,
|
||
color: tokens.success,
|
||
border: `1px solid ${tokens.successBorder}`,
|
||
borderRadius: 20,
|
||
padding: "2px 7px",
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
width: 5,
|
||
height: 5,
|
||
borderRadius: "50%",
|
||
background: tokens.success,
|
||
}}
|
||
/>
|
||
АКТУАЛЬНО
|
||
</span>
|
||
</div>
|
||
|
||
<div
|
||
style={{
|
||
fontSize: 9,
|
||
letterSpacing: 1.2,
|
||
color: tokens.muted3,
|
||
marginBottom: 13,
|
||
}}
|
||
>
|
||
ИТОГИ ПО РАЗДЕЛАМ
|
||
</div>
|
||
|
||
{/* Address */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "flex-start",
|
||
gap: 9,
|
||
marginBottom: 14,
|
||
}}
|
||
>
|
||
<svg
|
||
width="15"
|
||
height="18"
|
||
viewBox="0 0 15 18"
|
||
fill="none"
|
||
style={{ marginTop: 1 }}
|
||
>
|
||
<path
|
||
d="M7.5 17C7.5 17 14 11 14 6.5A6.5 6.5 0 1 0 1 6.5C1 11 7.5 17 7.5 17Z"
|
||
stroke={tokens.accent}
|
||
strokeWidth="1.2"
|
||
/>
|
||
<circle cx="7.5" cy="6.5" r="2.3" fill={tokens.accent} />
|
||
</svg>
|
||
<div style={{ flex: 1 }}>
|
||
<div style={{ fontSize: 13, fontWeight: 600 }}>
|
||
{data.object.address}
|
||
</div>
|
||
<div style={{ fontSize: 11, color: tokens.muted, marginTop: 2 }}>
|
||
{data.object.city}
|
||
</div>
|
||
</div>
|
||
<svg width="13" height="13" viewBox="0 0 13 13" fill="none">
|
||
<path
|
||
d="M5 1h7v7M12 1L6 7M9 8v4H1V4h4"
|
||
stroke={tokens.muted2}
|
||
strokeWidth="1"
|
||
/>
|
||
</svg>
|
||
</div>
|
||
|
||
{/* Per-section totals (clickable) */}
|
||
<div style={{ display: "flex", flexDirection: "column", gap: 0 }}>
|
||
{data.summary.rows.map((row, i) => (
|
||
<div
|
||
key={row.label}
|
||
className="os-row"
|
||
onClick={() => onNavigate(row.nav)}
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "center",
|
||
padding: "9px 6px",
|
||
borderBottom:
|
||
i < data.summary.rows.length - 1
|
||
? `1px dotted ${tokens.lineGradient}`
|
||
: undefined,
|
||
cursor: "pointer",
|
||
borderRadius: 5,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 10.5,
|
||
color: tokens.body,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 6,
|
||
whiteSpace: "nowrap",
|
||
paddingRight: 12,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
width: 5,
|
||
height: 5,
|
||
borderRadius: "50%",
|
||
background:
|
||
row.dot === "accent" ? tokens.accent : tokens.scatterDeal,
|
||
}}
|
||
/>
|
||
{row.label}
|
||
</span>
|
||
<span style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
||
<span
|
||
style={{
|
||
fontFamily: tokens.font.mono,
|
||
fontSize: 11,
|
||
color: tokens.ink2,
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
{row.value}
|
||
</span>
|
||
<span style={{ color: tokens.accent, fontSize: 11 }}>↗</span>
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* КАЧЕСТВО ДАННЫХ */}
|
||
<div
|
||
style={{
|
||
marginTop: 14,
|
||
background: tokens.infoSoftBg,
|
||
border: `1px solid ${tokens.infoSoftBorder}`,
|
||
borderRadius: 6,
|
||
padding: 12,
|
||
display: "flex",
|
||
gap: 12,
|
||
alignItems: "center",
|
||
}}
|
||
>
|
||
<svg width="50" height="50" viewBox="0 0 50 50" fill="none">
|
||
<circle
|
||
cx="25"
|
||
cy="25"
|
||
r="20"
|
||
stroke={tokens.track}
|
||
strokeWidth="3"
|
||
/>
|
||
<circle
|
||
cx="25"
|
||
cy="25"
|
||
r="20"
|
||
stroke={tokens.accent}
|
||
strokeWidth="3"
|
||
strokeLinecap="round"
|
||
strokeDasharray="126"
|
||
strokeDashoffset="72"
|
||
transform="rotate(-90 25 25)"
|
||
/>
|
||
<circle cx="25" cy="25" r="11" stroke="#cdddec" strokeWidth="1" />
|
||
<circle cx="25" cy="25" r="1.6" fill={tokens.accent} />
|
||
</svg>
|
||
<div style={{ flex: 1 }}>
|
||
<div
|
||
style={{
|
||
fontSize: 9.5,
|
||
letterSpacing: 1.5,
|
||
color: tokens.muted2,
|
||
marginBottom: 7,
|
||
}}
|
||
>
|
||
КАЧЕСТВО ДАННЫХ
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
fontSize: 10,
|
||
color: tokens.muted,
|
||
lineHeight: 1.7,
|
||
}}
|
||
>
|
||
<span>Источников</span>
|
||
<span style={{ fontFamily: tokens.font.mono, color: tokens.ink }}>
|
||
{data.summary.quality.sources}
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
fontSize: 10,
|
||
color: tokens.muted,
|
||
lineHeight: 1.7,
|
||
}}
|
||
>
|
||
<span>Достоверность</span>
|
||
<span style={{ color: tokens.ink }}>
|
||
{data.summary.quality.confidence}
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
fontSize: 10,
|
||
color: tokens.muted,
|
||
lineHeight: 1.7,
|
||
}}
|
||
>
|
||
<span>CV</span>
|
||
<span
|
||
style={{ fontFamily: tokens.font.mono, color: tokens.accent }}
|
||
>
|
||
{data.summary.quality.cv}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|