fix(tradein/v2): аналитика — 2 графика в ряд, чтобы Цена×срок влезала на экран
All checks were successful
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
All checks were successful
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 7s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 58s
"История цен в этом доме" и "Цена × срок продажи" стояли друг под другом и переполняли фиксированную высоту v2-панели (scrollHeight 1041px vs ~847px видимой области) — scatter-график обрезался, требовался скролл. Ставим оба блока в один ряд (grid 1fr 1fr, gap 16, align-items start) — освобождает ровно ~194px по вертикали, весь оверлей помещается без скролла. Высоты и viewBox самих SVG не трогали.
This commit is contained in:
parent
4f205bd7da
commit
a575f59378
1 changed files with 335 additions and 323 deletions
|
|
@ -327,204 +327,196 @@ export default function AnalyticsView({
|
|||
)}
|
||||
</div>
|
||||
|
||||
{/* история цен */}
|
||||
<div style={{ position: "relative", ...cardStyle }}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
|
||||
История цен в этом доме
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: tokens.muted2, marginTop: 4 }}>
|
||||
{historyNote}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 16, fontSize: 10 }}>
|
||||
{avitoPts.length > 0 ? (
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{ width: 14, height: 2, background: tokens.accent }}
|
||||
/>
|
||||
Avito
|
||||
</span>
|
||||
) : null}
|
||||
{yandexPts.length > 0 ? (
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{ width: 14, height: 2, background: tokens.gold }}
|
||||
/>
|
||||
Яндекс
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
viewBox="0 0 900 220"
|
||||
style={{ width: "100%", height: 200, marginTop: 12 }}
|
||||
preserveAspectRatio="none"
|
||||
role="img"
|
||||
aria-label="График истории цен в этом доме: медиана ₽/м² по годам, серии Avito и Яндекс"
|
||||
>
|
||||
<title>История цен в этом доме — медиана ₽/м² по годам</title>
|
||||
<g stroke={tokens.lineSoft2} strokeWidth={1}>
|
||||
{PH_GRID_Y.map((y) => (
|
||||
<line key={y} x1={40} y1={y} x2={900} y2={y} />
|
||||
))}
|
||||
</g>
|
||||
<g fontFamily={tokens.font.mono} fontSize={11} fill={tokens.muted2}>
|
||||
{data.priceHistory.yTicks.map((t) => (
|
||||
<text key={t.label} x={0} y={t.y + 4}>
|
||||
{t.label}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fontFamily={tokens.font.mono}
|
||||
fontSize={11}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{data.priceHistory.years.map((yr, i) => (
|
||||
<text
|
||||
key={yr}
|
||||
x={histYearX(i, data.priceHistory.years.length)}
|
||||
y={214}
|
||||
>
|
||||
{yr}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<polyline
|
||||
points={data.priceHistory.avito}
|
||||
fill="none"
|
||||
stroke={tokens.accent}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<polyline
|
||||
points={data.priceHistory.yandex}
|
||||
fill="none"
|
||||
stroke={tokens.gold}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
{/* white dot fill on an accent ring — documented one-off, no token (design l.568) */}
|
||||
<g
|
||||
fill="#fff"
|
||||
stroke={tokens.accent}
|
||||
strokeWidth={2}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<title>Архивные объявления Avito · нажмите, чтобы открыть</title>
|
||||
{avitoPts.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={3.5} />
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fill={tokens.gold}
|
||||
stroke={tokens.gold}
|
||||
strokeWidth={2}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{yandexDots.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={3.5} />
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fill="transparent"
|
||||
style={{ cursor: "pointer" }}
|
||||
onMouseEnter={() => setHoverHist(true)}
|
||||
onMouseLeave={() => setHoverHist(false)}
|
||||
onClick={() => onNavigate?.(2)}
|
||||
>
|
||||
{hoverPts.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={13} />
|
||||
))}
|
||||
</g>
|
||||
</svg>
|
||||
{hoverHist ? (
|
||||
// Dark navy tooltip surface + white text — documented one-off literals
|
||||
// matching the design (l.572); no token equivalent.
|
||||
{/* история цен + цена × срок продажи — side by side so both fit the
|
||||
fixed-height v2 artboard panel without scrolling (reclaims ~194px
|
||||
of vertical space vs. stacking; chart heights unchanged). */}
|
||||
<div
|
||||
style={{
|
||||
display: "grid",
|
||||
gridTemplateColumns: "1fr 1fr",
|
||||
gap: 16,
|
||||
alignItems: "start",
|
||||
}}
|
||||
>
|
||||
{/* история цен */}
|
||||
<div style={{ position: "relative", ...cardStyle }}>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: 64,
|
||||
top: 54,
|
||||
background: "rgba(23,38,58,.96)",
|
||||
color: "#fff",
|
||||
borderRadius: 7,
|
||||
padding: "9px 13px",
|
||||
fontSize: 11,
|
||||
lineHeight: 1.5,
|
||||
zIndex: 6,
|
||||
pointerEvents: "none",
|
||||
boxShadow: "0 10px 26px rgba(20,40,70,.28)",
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600 }}>
|
||||
Архивные объявления этого дома
|
||||
<div>
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
|
||||
История цен в этом доме
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: tokens.muted2, marginTop: 4 }}>
|
||||
{historyNote}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ color: tokens.barMid, fontSize: 10, marginTop: 2 }}>
|
||||
Нажмите, чтобы открыть список →
|
||||
<div style={{ display: "flex", gap: 16, fontSize: 10 }}>
|
||||
{avitoPts.length > 0 ? (
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{ width: 14, height: 2, background: tokens.accent }}
|
||||
/>
|
||||
Avito
|
||||
</span>
|
||||
) : null}
|
||||
{yandexPts.length > 0 ? (
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{ width: 14, height: 2, background: tokens.gold }}
|
||||
/>
|
||||
Яндекс
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* цена × срок продажи (детально) */}
|
||||
<div style={cardStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
|
||||
Цена × срок продажи
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: tokens.muted2, marginTop: 4 }}>
|
||||
Зависимость цены от срока экспозиции · {data.scatterDetail.note}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 14, fontSize: 10 }}>
|
||||
<span
|
||||
<svg
|
||||
viewBox="0 0 900 220"
|
||||
style={{ width: "100%", height: 200, marginTop: 12 }}
|
||||
preserveAspectRatio="none"
|
||||
role="img"
|
||||
aria-label="График истории цен в этом доме: медиана ₽/м² по годам, серии Avito и Яндекс"
|
||||
>
|
||||
<title>История цен в этом доме — медиана ₽/м² по годам</title>
|
||||
<g stroke={tokens.lineSoft2} strokeWidth={1}>
|
||||
{PH_GRID_Y.map((y) => (
|
||||
<line key={y} x1={40} y1={y} x2={900} y2={y} />
|
||||
))}
|
||||
</g>
|
||||
<g fontFamily={tokens.font.mono} fontSize={11} fill={tokens.muted2}>
|
||||
{data.priceHistory.yTicks.map((t) => (
|
||||
<text key={t.label} x={0} y={t.y + 4}>
|
||||
{t.label}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fontFamily={tokens.font.mono}
|
||||
fontSize={11}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{data.priceHistory.years.map((yr, i) => (
|
||||
<text
|
||||
key={yr}
|
||||
x={histYearX(i, data.priceHistory.years.length)}
|
||||
y={214}
|
||||
>
|
||||
{yr}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<polyline
|
||||
points={data.priceHistory.avito}
|
||||
fill="none"
|
||||
stroke={tokens.accent}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
<polyline
|
||||
points={data.priceHistory.yandex}
|
||||
fill="none"
|
||||
stroke={tokens.gold}
|
||||
strokeWidth={2.5}
|
||||
/>
|
||||
{/* white dot fill on an accent ring — documented one-off, no token (design l.568) */}
|
||||
<g
|
||||
fill="#fff"
|
||||
stroke={tokens.accent}
|
||||
strokeWidth={2}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
<title>Архивные объявления Avito · нажмите, чтобы открыть</title>
|
||||
{avitoPts.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={3.5} />
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fill={tokens.gold}
|
||||
stroke={tokens.gold}
|
||||
strokeWidth={2}
|
||||
style={{ cursor: "pointer" }}
|
||||
>
|
||||
{yandexDots.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={3.5} />
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fill="transparent"
|
||||
style={{ cursor: "pointer" }}
|
||||
onMouseEnter={() => setHoverHist(true)}
|
||||
onMouseLeave={() => setHoverHist(false)}
|
||||
onClick={() => onNavigate?.(2)}
|
||||
>
|
||||
{hoverPts.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={13} />
|
||||
))}
|
||||
</g>
|
||||
</svg>
|
||||
{hoverHist ? (
|
||||
// Dark navy tooltip surface + white text — documented one-off literals
|
||||
// matching the design (l.572); no token equivalent.
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
position: "absolute",
|
||||
left: 64,
|
||||
top: 54,
|
||||
background: "rgba(23,38,58,.96)",
|
||||
color: "#fff",
|
||||
borderRadius: 7,
|
||||
padding: "9px 13px",
|
||||
fontSize: 11,
|
||||
lineHeight: 1.5,
|
||||
zIndex: 6,
|
||||
pointerEvents: "none",
|
||||
boxShadow: "0 10px 26px rgba(20,40,70,.28)",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
background: tokens.accent,
|
||||
}}
|
||||
/>
|
||||
аналоги
|
||||
</span>
|
||||
{data.scatterDetail.deals.length > 0 ? (
|
||||
<div style={{ fontWeight: 600 }}>
|
||||
Архивные объявления этого дома
|
||||
</div>
|
||||
<div style={{ color: tokens.barMid, fontSize: 10, marginTop: 2 }}>
|
||||
Нажмите, чтобы открыть список →
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{/* цена × срок продажи (детально) */}
|
||||
<div style={cardStyle}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
|
||||
Цена × срок продажи
|
||||
</div>
|
||||
<div style={{ fontSize: 10, color: tokens.muted2, marginTop: 4 }}>
|
||||
Зависимость цены от срока экспозиции · {data.scatterDetail.note}
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ display: "flex", gap: 14, fontSize: 10 }}>
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
|
|
@ -538,149 +530,169 @@ export default function AnalyticsView({
|
|||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
background: tokens.scatterDeal,
|
||||
background: tokens.accent,
|
||||
}}
|
||||
/>
|
||||
сделки
|
||||
аналоги
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
>
|
||||
{data.scatterDetail.deals.length > 0 ? (
|
||||
<span
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
background: tokens.scatterDeal,
|
||||
}}
|
||||
/>
|
||||
сделки
|
||||
</span>
|
||||
) : null}
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
background: tokens.ink,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 6,
|
||||
color: tokens.muted,
|
||||
}}
|
||||
/>
|
||||
объект
|
||||
</span>
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: "50%",
|
||||
background: tokens.ink,
|
||||
}}
|
||||
/>
|
||||
объект
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<svg
|
||||
viewBox="0 0 900 300"
|
||||
style={{ width: "100%", height: 280, marginTop: 12 }}
|
||||
preserveAspectRatio="none"
|
||||
role="img"
|
||||
aria-label="Точечный график зависимости цены продажи от срока экспозиции: аналоги, сделки и ваш объект"
|
||||
>
|
||||
<title>Цена × срок продажи</title>
|
||||
<g stroke={tokens.lineSoft2} strokeWidth={1}>
|
||||
<line x1={60} y1={20} x2={880} y2={20} />
|
||||
<line x1={60} y1={77} x2={880} y2={77} />
|
||||
<line x1={60} y1={134} x2={880} y2={134} />
|
||||
<line x1={60} y1={191} x2={880} y2={191} />
|
||||
<line x1={60} y1={250} x2={880} y2={250} />
|
||||
<line x1={265} y1={20} x2={265} y2={250} />
|
||||
<line x1={470} y1={20} x2={470} y2={250} />
|
||||
<line x1={675} y1={20} x2={675} y2={250} />
|
||||
</g>
|
||||
<line
|
||||
x1={60}
|
||||
y1={20}
|
||||
x2={60}
|
||||
y2={250}
|
||||
stroke={tokens.line3}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<line
|
||||
x1={60}
|
||||
y1={250}
|
||||
x2={880}
|
||||
y2={250}
|
||||
stroke={tokens.line3}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<g fill={tokens.scatterDeal}>
|
||||
{data.scatterDetail.deals.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={p.r} />
|
||||
))}
|
||||
</g>
|
||||
<g fill={tokens.accent}>
|
||||
{data.scatterDetail.analogs.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={p.r} />
|
||||
))}
|
||||
</g>
|
||||
<g opacity={data.scatterDetail.subjectApprox ? 0.55 : 1}>
|
||||
{/* V4: subjectApprox marks a fallback x (est_days_on_market unknown) —
|
||||
dashed ring + title flag it as an estimate, not a measurement. */}
|
||||
{data.scatterDetail.subjectApprox ? (
|
||||
<title>
|
||||
Срок продажи — оценка (~90 дн.), точных данных нет
|
||||
</title>
|
||||
) : null}
|
||||
<circle
|
||||
cx={data.scatterDetail.subject.x}
|
||||
cy={data.scatterDetail.subject.y}
|
||||
r={data.scatterDetail.subject.r}
|
||||
fill={tokens.ink}
|
||||
/>
|
||||
<circle
|
||||
cx={data.scatterDetail.subject.x}
|
||||
cy={data.scatterDetail.subject.y}
|
||||
r={11}
|
||||
fill="none"
|
||||
stroke={tokens.ink}
|
||||
strokeWidth={1.2}
|
||||
strokeDasharray={
|
||||
data.scatterDetail.subjectApprox ? "3 3" : undefined
|
||||
}
|
||||
opacity={0.4}
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
fontFamily={tokens.font.mono}
|
||||
fontSize={11}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="end"
|
||||
>
|
||||
{data.scatterDetail.yTicks.map((t) => (
|
||||
<text key={t.label} x={52} y={t.y}>
|
||||
{t.label}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fontFamily={tokens.font.mono}
|
||||
fontSize={11}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{data.scatterDetail.xTicks.map((t) => (
|
||||
<text key={t.label} x={t.x} y={268}>
|
||||
{t.label}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<text
|
||||
x={470}
|
||||
y={288}
|
||||
fontSize={10}
|
||||
letterSpacing={1}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
СРОК ПРОДАЖИ, ДН
|
||||
</text>
|
||||
<text
|
||||
x={18}
|
||||
y={135}
|
||||
fontSize={10}
|
||||
letterSpacing={1}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
transform="rotate(-90 18 135)"
|
||||
>
|
||||
ЦЕНА ПРОДАЖИ, ₽
|
||||
</text>
|
||||
</svg>
|
||||
</div>
|
||||
<svg
|
||||
viewBox="0 0 900 300"
|
||||
style={{ width: "100%", height: 280, marginTop: 12 }}
|
||||
preserveAspectRatio="none"
|
||||
role="img"
|
||||
aria-label="Точечный график зависимости цены продажи от срока экспозиции: аналоги, сделки и ваш объект"
|
||||
>
|
||||
<title>Цена × срок продажи</title>
|
||||
<g stroke={tokens.lineSoft2} strokeWidth={1}>
|
||||
<line x1={60} y1={20} x2={880} y2={20} />
|
||||
<line x1={60} y1={77} x2={880} y2={77} />
|
||||
<line x1={60} y1={134} x2={880} y2={134} />
|
||||
<line x1={60} y1={191} x2={880} y2={191} />
|
||||
<line x1={60} y1={250} x2={880} y2={250} />
|
||||
<line x1={265} y1={20} x2={265} y2={250} />
|
||||
<line x1={470} y1={20} x2={470} y2={250} />
|
||||
<line x1={675} y1={20} x2={675} y2={250} />
|
||||
</g>
|
||||
<line
|
||||
x1={60}
|
||||
y1={20}
|
||||
x2={60}
|
||||
y2={250}
|
||||
stroke={tokens.line3}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<line
|
||||
x1={60}
|
||||
y1={250}
|
||||
x2={880}
|
||||
y2={250}
|
||||
stroke={tokens.line3}
|
||||
strokeWidth={1.5}
|
||||
/>
|
||||
<g fill={tokens.scatterDeal}>
|
||||
{data.scatterDetail.deals.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={p.r} />
|
||||
))}
|
||||
</g>
|
||||
<g fill={tokens.accent}>
|
||||
{data.scatterDetail.analogs.map((p, i) => (
|
||||
<circle key={i} cx={p.x} cy={p.y} r={p.r} />
|
||||
))}
|
||||
</g>
|
||||
<g opacity={data.scatterDetail.subjectApprox ? 0.55 : 1}>
|
||||
{/* V4: subjectApprox marks a fallback x (est_days_on_market unknown) —
|
||||
dashed ring + title flag it as an estimate, not a measurement. */}
|
||||
{data.scatterDetail.subjectApprox ? (
|
||||
<title>
|
||||
Срок продажи — оценка (~90 дн.), точных данных нет
|
||||
</title>
|
||||
) : null}
|
||||
<circle
|
||||
cx={data.scatterDetail.subject.x}
|
||||
cy={data.scatterDetail.subject.y}
|
||||
r={data.scatterDetail.subject.r}
|
||||
fill={tokens.ink}
|
||||
/>
|
||||
<circle
|
||||
cx={data.scatterDetail.subject.x}
|
||||
cy={data.scatterDetail.subject.y}
|
||||
r={11}
|
||||
fill="none"
|
||||
stroke={tokens.ink}
|
||||
strokeWidth={1.2}
|
||||
strokeDasharray={
|
||||
data.scatterDetail.subjectApprox ? "3 3" : undefined
|
||||
}
|
||||
opacity={0.4}
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
fontFamily={tokens.font.mono}
|
||||
fontSize={11}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="end"
|
||||
>
|
||||
{data.scatterDetail.yTicks.map((t) => (
|
||||
<text key={t.label} x={52} y={t.y}>
|
||||
{t.label}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<g
|
||||
fontFamily={tokens.font.mono}
|
||||
fontSize={11}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
{data.scatterDetail.xTicks.map((t) => (
|
||||
<text key={t.label} x={t.x} y={268}>
|
||||
{t.label}
|
||||
</text>
|
||||
))}
|
||||
</g>
|
||||
<text
|
||||
x={470}
|
||||
y={288}
|
||||
fontSize={10}
|
||||
letterSpacing={1}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
>
|
||||
СРОК ПРОДАЖИ, ДН
|
||||
</text>
|
||||
<text
|
||||
x={18}
|
||||
y={135}
|
||||
fontSize={10}
|
||||
letterSpacing={1}
|
||||
fill={tokens.muted2}
|
||||
textAnchor="middle"
|
||||
transform="rotate(-90 18 135)"
|
||||
>
|
||||
ЦЕНА ПРОДАЖИ, ₽
|
||||
</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue