FE-1 foundation for #2036. app/v2/page.tsx now owns data: ParamsPanel form -> useEstimateMutation, restore-by-id via window.location.search (mirrors v1, avoids useSearchParams Suspense build break), sub-hooks useStreetDeals/useEstimateHouseAnalytics; loading/empty/insufficient/error states (honest placeholders, no fixtures-as-real). New components/trade-in/v2/mappers.ts: pure AggregatedEstimate(+street-deals/analytics) -> fixture-shape transforms — 3 price tiers (asking/expected_sold/DKP), ranges, scatter, 7 source slots, summary. CV + per-source counts are FE-approx pending BE-1 (#2043). ParamsPanel -> controlled form (onSubmit/isPending/error/initialValues). ResultPanel/ ObjectSummary/HeroBar/Footer accept a composite data prop defaulting to the existing fixture (pixel-perfect markup unchanged). next build green (/v2 34 kB).
894 lines
24 KiB
TypeScript
894 lines
24 KiB
TypeScript
"use client";
|
||
|
||
import { Fragment } from "react";
|
||
import { tokens } from "./tokens";
|
||
import {
|
||
ranges,
|
||
resultCards,
|
||
resultMeta,
|
||
scatterMini,
|
||
sources,
|
||
} from "./fixtures";
|
||
import type { ResultPanelData } from "./mappers";
|
||
|
||
const {
|
||
accent,
|
||
onAccent,
|
||
accentGlow,
|
||
ink,
|
||
muted,
|
||
muted2,
|
||
muted3,
|
||
line,
|
||
line2,
|
||
line3,
|
||
bracket,
|
||
barLo,
|
||
barMid,
|
||
track,
|
||
success,
|
||
hint,
|
||
scatterDeal,
|
||
disabledBorder,
|
||
disabledValue,
|
||
disabledLabel,
|
||
surface,
|
||
font,
|
||
} = tokens;
|
||
|
||
// Default presentation data (unwired usage): the existing design fixtures.
|
||
const RESULT_FIXTURE: ResultPanelData = {
|
||
cards: resultCards,
|
||
meta: resultMeta,
|
||
ranges,
|
||
scatterMini,
|
||
sources,
|
||
};
|
||
|
||
interface ResultPanelProps {
|
||
data?: ResultPanelData;
|
||
onNavigate: (i: number) => void;
|
||
}
|
||
|
||
// Mini-histogram column tint: peak column = accent, its neighbours = mid, rest = lo.
|
||
function barColor(i: number, bars: number[]): string {
|
||
const peak = bars.indexOf(Math.max(...bars));
|
||
if (i === peak) return accent;
|
||
if (i === peak - 1 || i === peak + 1) return barMid;
|
||
return barLo;
|
||
}
|
||
|
||
// Render a string[] with <br/> between lines (faithful to the design line breaks).
|
||
function lines(parts: string[]) {
|
||
return parts.map((part, i) => (
|
||
<Fragment key={i}>
|
||
{i > 0 && <br />}
|
||
{part}
|
||
</Fragment>
|
||
));
|
||
}
|
||
|
||
// Split a source name on its parenthetical so the design's forced
|
||
// "РОСРЕЕСТР / (ВНУТР.)" two-line break (line-height 1.3) is reproduced; plain
|
||
// names without " (" stay on a single line.
|
||
function sourceName(name: string) {
|
||
const idx = name.indexOf(" (");
|
||
if (idx === -1) return name;
|
||
return (
|
||
<>
|
||
{name.slice(0, idx)}
|
||
<br />
|
||
{name.slice(idx + 1)}
|
||
</>
|
||
);
|
||
}
|
||
|
||
export default function ResultPanel({
|
||
data = RESULT_FIXTURE,
|
||
onNavigate,
|
||
}: ResultPanelProps) {
|
||
return (
|
||
<div
|
||
style={{ display: "flex", flexDirection: "column", gap: 14, minWidth: 0 }}
|
||
>
|
||
<style>{`
|
||
.rp-more { transition: opacity 120ms ease; }
|
||
.rp-more:hover { opacity: .6; }
|
||
`}</style>
|
||
|
||
{/* header */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "baseline",
|
||
justifyContent: "space-between",
|
||
}}
|
||
>
|
||
<div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
|
||
<span style={{ fontFamily: font.mono, fontSize: 15, color: accent }}>
|
||
02
|
||
</span>
|
||
<span style={{ fontSize: 13, fontWeight: 600, letterSpacing: 2 }}>
|
||
РЕЗУЛЬТАТ ОЦЕНКИ
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 22,
|
||
fontSize: 10,
|
||
letterSpacing: 1,
|
||
color: muted,
|
||
}}
|
||
>
|
||
<span>
|
||
ИСТОЧНИКОВ:{" "}
|
||
<b style={{ color: ink, fontFamily: font.mono }}>
|
||
{data.meta.sources}
|
||
</b>
|
||
</span>
|
||
<span>
|
||
ДОСТОВЕРНОСТЬ: <b style={{ color: ink }}>{data.meta.confidence}</b>
|
||
</span>
|
||
<span>
|
||
CV{" "}
|
||
<b style={{ color: accent, fontFamily: font.mono }}>
|
||
{data.meta.cv}
|
||
</b>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* result cards */}
|
||
<div
|
||
style={{
|
||
display: "grid",
|
||
gridTemplateColumns: "1fr 1fr 1fr",
|
||
gap: 14,
|
||
flex: "0 0 auto",
|
||
}}
|
||
>
|
||
{data.cards.map((card, ci) => (
|
||
<div
|
||
key={ci}
|
||
style={{
|
||
position: "relative",
|
||
background: surface.w55,
|
||
backdropFilter: "blur(6px)",
|
||
border: `1px solid ${line2}`,
|
||
borderRadius: 8,
|
||
padding: "15px 16px 13px",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: -1,
|
||
top: -1,
|
||
width: 12,
|
||
height: 12,
|
||
borderLeft: `1.5px solid ${bracket}`,
|
||
borderTop: `1.5px solid ${bracket}`,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
fontSize: 9.5,
|
||
letterSpacing: 1.5,
|
||
color: muted2,
|
||
lineHeight: 1.6,
|
||
}}
|
||
>
|
||
{lines(card.title)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
marginTop: 14,
|
||
display: "flex",
|
||
alignItems: "baseline",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 38,
|
||
fontWeight: 300,
|
||
letterSpacing: -1,
|
||
}}
|
||
>
|
||
{card.value}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 13,
|
||
color: muted,
|
||
letterSpacing: 1,
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
{card.unit}
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 11,
|
||
color: muted,
|
||
marginTop: 10,
|
||
}}
|
||
>
|
||
{card.range}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 10,
|
||
color: muted2,
|
||
marginTop: 4,
|
||
}}
|
||
>
|
||
{card.ppm}
|
||
</div>
|
||
|
||
{card.bars ? (
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "flex-end",
|
||
justifyContent: "space-between",
|
||
gap: 12,
|
||
marginTop: 12,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "flex-end",
|
||
gap: 2,
|
||
height: 20,
|
||
flex: 1,
|
||
}}
|
||
>
|
||
{card.bars.map((h, bi) => (
|
||
<span
|
||
key={bi}
|
||
style={{
|
||
flex: 1,
|
||
height: `${h}%`,
|
||
background: barColor(bi, card.bars as number[]),
|
||
}}
|
||
/>
|
||
))}
|
||
</div>
|
||
<div
|
||
className="rp-more"
|
||
onClick={() => onNavigate(card.nav)}
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 4,
|
||
cursor: "pointer",
|
||
fontSize: 9,
|
||
letterSpacing: 0.3,
|
||
color: accent,
|
||
whiteSpace: "nowrap",
|
||
}}
|
||
>
|
||
Подробнее <span>→</span>
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<>
|
||
<div
|
||
className="rp-more"
|
||
onClick={() => onNavigate(card.nav)}
|
||
style={{
|
||
marginTop: 16,
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "flex-end",
|
||
gap: 4,
|
||
cursor: "pointer",
|
||
fontSize: 9,
|
||
letterSpacing: 0.3,
|
||
color: accent,
|
||
}}
|
||
>
|
||
Подробнее <span>→</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
right: 14,
|
||
top: 13,
|
||
width: 58,
|
||
height: 58,
|
||
}}
|
||
>
|
||
<svg width="58" height="58" viewBox="0 0 58 58">
|
||
<circle
|
||
cx="29"
|
||
cy="29"
|
||
r="25"
|
||
fill="none"
|
||
stroke={track}
|
||
strokeWidth="3"
|
||
/>
|
||
<circle
|
||
cx="29"
|
||
cy="29"
|
||
r="25"
|
||
fill="none"
|
||
stroke={accent}
|
||
strokeWidth="3"
|
||
strokeLinecap="round"
|
||
strokeDasharray="157"
|
||
strokeDashoffset="140"
|
||
transform="rotate(-90 29 29)"
|
||
/>
|
||
</svg>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
inset: 0,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 13,
|
||
fontWeight: 500,
|
||
color: accent,
|
||
}}
|
||
>
|
||
{card.delta}
|
||
</span>
|
||
<span
|
||
style={{
|
||
fontSize: 6.5,
|
||
letterSpacing: 1,
|
||
color: muted2,
|
||
marginTop: 1,
|
||
}}
|
||
>
|
||
{card.deltaLabel}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)}
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* ranges + radar */}
|
||
<div
|
||
style={{
|
||
position: "relative",
|
||
background: surface.w50,
|
||
backdropFilter: "blur(6px)",
|
||
border: `1px solid ${line2}`,
|
||
borderRadius: 8,
|
||
padding: "18px 20px",
|
||
display: "grid",
|
||
gridTemplateColumns: "1fr 168px 1fr",
|
||
gap: 24,
|
||
alignItems: "center",
|
||
flex: "0 0 auto",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
right: -1,
|
||
bottom: -1,
|
||
width: 13,
|
||
height: 13,
|
||
borderRight: `1.5px solid ${bracket}`,
|
||
borderBottom: `1.5px solid ${bracket}`,
|
||
}}
|
||
/>
|
||
|
||
{/* left range — ads */}
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontSize: 9,
|
||
letterSpacing: 1.3,
|
||
color: muted2,
|
||
lineHeight: 1.5,
|
||
}}
|
||
>
|
||
{lines(data.ranges.ads.label)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 11,
|
||
color: muted,
|
||
margin: "12px 0 16px",
|
||
}}
|
||
>
|
||
медиана · <b style={{ color: ink }}>{data.ranges.ads.median}</b>
|
||
</div>
|
||
<div
|
||
style={{
|
||
position: "relative",
|
||
height: 3,
|
||
background: track,
|
||
borderRadius: 2,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: data.ranges.ads.marker.fillLeft,
|
||
right: data.ranges.ads.marker.fillRight,
|
||
top: 0,
|
||
bottom: 0,
|
||
background: `linear-gradient(90deg,${barMid},${accent})`,
|
||
borderRadius: 2,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: data.ranges.ads.marker.dotLeft,
|
||
top: "50%",
|
||
transform: "translate(-50%,-50%)",
|
||
width: 13,
|
||
height: 13,
|
||
borderRadius: "50%",
|
||
background: onAccent,
|
||
border: `3px solid ${accent}`,
|
||
boxShadow: `0 0 8px ${accentGlow}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
fontFamily: font.mono,
|
||
fontSize: 10,
|
||
color: muted2,
|
||
marginTop: 10,
|
||
}}
|
||
>
|
||
<span>{data.ranges.ads.lo}</span>
|
||
<span style={{ color: accent }}>{data.ranges.ads.median}</span>
|
||
<span>{data.ranges.ads.hi}</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* radar — price × sell-time scatter */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<div style={{ fontSize: 8, letterSpacing: 1.5, color: muted2 }}>
|
||
ЦЕНА × СРОК ПРОДАЖИ
|
||
</div>
|
||
<svg width="168" height="140" viewBox="0 0 168 140" fill="none">
|
||
<g stroke="#e3ecf4" strokeWidth="1">
|
||
<line x1="34" y1="38" x2="158" y2="38" />
|
||
<line x1="34" y1="74" x2="158" y2="74" />
|
||
<line x1="96" y1="10" x2="96" y2="110" />
|
||
</g>
|
||
<line
|
||
x1="34"
|
||
y1="10"
|
||
x2="34"
|
||
y2="110"
|
||
stroke={line3}
|
||
strokeWidth="1"
|
||
/>
|
||
<line
|
||
x1="34"
|
||
y1="110"
|
||
x2="158"
|
||
y2="110"
|
||
stroke={line3}
|
||
strokeWidth="1"
|
||
/>
|
||
<line
|
||
x1="40"
|
||
y1="100"
|
||
x2="150"
|
||
y2="30"
|
||
stroke={accent}
|
||
strokeWidth="1"
|
||
strokeDasharray="3 4"
|
||
opacity="0.45"
|
||
/>
|
||
<g fill={scatterDeal}>
|
||
{data.scatterMini.deals.map((p, i) => (
|
||
<circle key={i} cx={p.x} cy={p.y} r={p.r} />
|
||
))}
|
||
</g>
|
||
<g fill={accent}>
|
||
{data.scatterMini.analogs.map((p, i) => (
|
||
<circle key={i} cx={p.x} cy={p.y} r={p.r} />
|
||
))}
|
||
</g>
|
||
<circle
|
||
cx={data.scatterMini.subject.x}
|
||
cy={data.scatterMini.subject.y}
|
||
r={data.scatterMini.subject.r}
|
||
fill={ink}
|
||
/>
|
||
<circle
|
||
cx={data.scatterMini.subject.x}
|
||
cy={data.scatterMini.subject.y}
|
||
r="6.5"
|
||
fill="none"
|
||
stroke={ink}
|
||
strokeWidth="1"
|
||
opacity="0.4"
|
||
/>
|
||
<g
|
||
fontFamily={font.mono}
|
||
fontSize="6.5"
|
||
fill={muted2}
|
||
textAnchor="end"
|
||
>
|
||
{data.scatterMini.yTicks.map((t, i) => (
|
||
<text key={i} x={30} y={t.y}>
|
||
{t.label}
|
||
</text>
|
||
))}
|
||
</g>
|
||
<g
|
||
fontFamily={font.mono}
|
||
fontSize="6.5"
|
||
fill={muted2}
|
||
textAnchor="middle"
|
||
>
|
||
{data.scatterMini.xTicks.map((t, i) => (
|
||
<text key={i} x={t.x} y={120}>
|
||
{t.label}
|
||
</text>
|
||
))}
|
||
</g>
|
||
<text
|
||
x="96"
|
||
y="134"
|
||
fontSize="6.5"
|
||
letterSpacing="1"
|
||
fill={muted2}
|
||
textAnchor="middle"
|
||
>
|
||
СРОК ПРОДАЖИ, ДН
|
||
</text>
|
||
<text
|
||
x="11"
|
||
y="60"
|
||
fontSize="6.5"
|
||
letterSpacing="1"
|
||
fill={muted2}
|
||
textAnchor="middle"
|
||
transform="rotate(-90 11 60)"
|
||
>
|
||
ЦЕНА ПРОДАЖИ
|
||
</text>
|
||
</svg>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
gap: 12,
|
||
fontSize: 7.5,
|
||
letterSpacing: 0.5,
|
||
color: muted2,
|
||
}}
|
||
>
|
||
<span style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||
<span
|
||
style={{
|
||
width: 6,
|
||
height: 6,
|
||
borderRadius: "50%",
|
||
background: accent,
|
||
}}
|
||
/>
|
||
аналоги
|
||
</span>
|
||
<span style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||
<span
|
||
style={{
|
||
width: 6,
|
||
height: 6,
|
||
borderRadius: "50%",
|
||
background: scatterDeal,
|
||
}}
|
||
/>
|
||
сделки
|
||
</span>
|
||
<span style={{ display: "flex", alignItems: "center", gap: 4 }}>
|
||
<span
|
||
style={{
|
||
width: 6,
|
||
height: 6,
|
||
borderRadius: "50%",
|
||
background: ink,
|
||
}}
|
||
/>
|
||
объект
|
||
</span>
|
||
</div>
|
||
<div
|
||
className="rp-more"
|
||
onClick={() => onNavigate(3)}
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 4,
|
||
cursor: "pointer",
|
||
fontSize: 9,
|
||
letterSpacing: 0.3,
|
||
color: accent,
|
||
marginTop: 2,
|
||
}}
|
||
>
|
||
Подробнее <span>→</span>
|
||
</div>
|
||
</div>
|
||
|
||
{/* right range — deals */}
|
||
<div>
|
||
<div
|
||
style={{
|
||
fontSize: 9,
|
||
letterSpacing: 1.3,
|
||
color: muted2,
|
||
lineHeight: 1.5,
|
||
}}
|
||
>
|
||
{lines(data.ranges.deals.label)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 11,
|
||
color: muted,
|
||
margin: "12px 0 16px",
|
||
}}
|
||
>
|
||
медиана · <b style={{ color: ink }}>{data.ranges.deals.median}</b>
|
||
</div>
|
||
<div
|
||
style={{
|
||
position: "relative",
|
||
height: 3,
|
||
background: track,
|
||
borderRadius: 2,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: data.ranges.deals.marker.fillLeft,
|
||
right: data.ranges.deals.marker.fillRight,
|
||
top: 0,
|
||
bottom: 0,
|
||
background: `linear-gradient(90deg,${barMid},${accent})`,
|
||
borderRadius: 2,
|
||
}}
|
||
/>
|
||
<div
|
||
style={{
|
||
position: "absolute",
|
||
left: data.ranges.deals.marker.dotLeft,
|
||
top: "50%",
|
||
transform: "translate(-50%,-50%)",
|
||
width: 13,
|
||
height: 13,
|
||
borderRadius: "50%",
|
||
background: onAccent,
|
||
border: `3px solid ${accent}`,
|
||
boxShadow: `0 0 8px ${accentGlow}`,
|
||
}}
|
||
/>
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
fontFamily: font.mono,
|
||
fontSize: 10,
|
||
color: muted2,
|
||
marginTop: 10,
|
||
}}
|
||
>
|
||
<span>{data.ranges.deals.lo}</span>
|
||
<span style={{ color: accent }}>{data.ranges.deals.median}</span>
|
||
<span>{data.ranges.deals.hi}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* sources */}
|
||
<div
|
||
style={{
|
||
background: surface.w50,
|
||
backdropFilter: "blur(6px)",
|
||
border: `1px solid ${line2}`,
|
||
borderRadius: 8,
|
||
padding: "14px 18px 13px",
|
||
flex: 1,
|
||
minHeight: 0,
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
justifyContent: "space-between",
|
||
marginBottom: 11,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
fontSize: 11,
|
||
fontWeight: 600,
|
||
letterSpacing: 2,
|
||
color: ink,
|
||
}}
|
||
>
|
||
ИСТОЧНИКИ ДАННЫХ
|
||
</span>
|
||
<span
|
||
className="rp-more"
|
||
onClick={() => onNavigate(2)}
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 4,
|
||
cursor: "pointer",
|
||
fontSize: 9.5,
|
||
letterSpacing: 0.3,
|
||
color: accent,
|
||
}}
|
||
>
|
||
Подробнее <span>→</span>
|
||
</span>
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "grid",
|
||
gridTemplateColumns: "repeat(7,1fr)",
|
||
gap: 9,
|
||
flex: 1,
|
||
minHeight: 0,
|
||
alignItems: "stretch",
|
||
alignContent: "stretch",
|
||
}}
|
||
>
|
||
{data.sources.map((s, si) =>
|
||
s.active ? (
|
||
<div
|
||
key={si}
|
||
style={{
|
||
border: `1px solid ${line}`,
|
||
borderRadius: 6,
|
||
padding: "10px 9px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
background: surface.w50,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 9,
|
||
letterSpacing: 0.5,
|
||
color: ink,
|
||
fontWeight: 600,
|
||
...(s.name.includes(" (") ? { lineHeight: 1.3 } : {}),
|
||
}}
|
||
>
|
||
{sourceName(s.name)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 26,
|
||
fontWeight: 300,
|
||
marginTop: "auto",
|
||
}}
|
||
>
|
||
{s.count}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 5,
|
||
fontSize: 9,
|
||
color: muted,
|
||
marginTop: 3,
|
||
}}
|
||
>
|
||
<span
|
||
style={{
|
||
width: 6,
|
||
height: 6,
|
||
borderRadius: "50%",
|
||
background: success,
|
||
}}
|
||
/>
|
||
{s.label}
|
||
</div>
|
||
</div>
|
||
) : (
|
||
<div
|
||
key={si}
|
||
style={{
|
||
border: `1px solid ${disabledBorder}`,
|
||
borderRadius: 6,
|
||
padding: "10px 9px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
background: surface.w25,
|
||
}}
|
||
>
|
||
<div
|
||
style={{
|
||
fontSize: 9,
|
||
letterSpacing: 0.5,
|
||
color: muted3,
|
||
fontWeight: 600,
|
||
}}
|
||
>
|
||
{s.name}
|
||
</div>
|
||
<div
|
||
style={{
|
||
fontFamily: font.mono,
|
||
fontSize: 22,
|
||
fontWeight: 300,
|
||
marginTop: "auto",
|
||
color: disabledValue,
|
||
}}
|
||
>
|
||
{s.count}
|
||
</div>
|
||
<div
|
||
style={{ fontSize: 9, color: disabledLabel, marginTop: 3 }}
|
||
>
|
||
{s.label}
|
||
</div>
|
||
</div>
|
||
),
|
||
)}
|
||
</div>
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
justifyContent: "space-between",
|
||
alignItems: "center",
|
||
marginTop: 11,
|
||
}}
|
||
>
|
||
<span style={{ fontSize: 10, color: hint }}>
|
||
При недоступности источника частичный результат не блокируется
|
||
</span>
|
||
<span style={{ fontSize: 9.5, letterSpacing: 1.5, color: muted2 }}>
|
||
ПОСТРОЕНО ПО 6 АНАЛОГАМ И 10 СДЕЛКАМ ЗА 6 МЕСЯЦЕВ
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|