137 lines
3.5 KiB
TypeScript
137 lines
3.5 KiB
TypeScript
"use client";
|
||
|
||
import type { MarketTrend } from "@/types/site-finder";
|
||
|
||
interface Props {
|
||
trend?: MarketTrend | null;
|
||
}
|
||
|
||
const LABEL_COLORS: Record<string, { bg: string; color: string }> = {
|
||
"Сильный рост": { bg: "#dcfce7", color: "#15803d" },
|
||
"Умеренный рост": { bg: "#dbeafe", color: "#1d4ed8" },
|
||
Стагнация: { bg: "#fef3c7", color: "#b45309" },
|
||
Падение: { bg: "#fecaca", color: "#b91c1c" },
|
||
};
|
||
|
||
function trendArrow(delta: number): string {
|
||
if (delta > 1) return "↑";
|
||
if (delta < -1) return "↓";
|
||
return "→";
|
||
}
|
||
|
||
function deltaColor(delta: number): string {
|
||
if (delta > 1) return "#15803d";
|
||
if (delta < -1) return "#b91c1c";
|
||
return "#6b7280";
|
||
}
|
||
|
||
function fmtPrice(v: number): string {
|
||
return v.toLocaleString("ru-RU", { maximumFractionDigits: 0 });
|
||
}
|
||
|
||
export function MarketTrendBlock({ trend }: Props) {
|
||
if (!trend) {
|
||
return (
|
||
<div
|
||
style={{
|
||
borderRadius: 10,
|
||
padding: "14px 16px",
|
||
background: "#f3f4f6",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 6,
|
||
}}
|
||
>
|
||
<div style={{ fontSize: 12, fontWeight: 700, color: "#374151" }}>
|
||
Тренд рынка
|
||
</div>
|
||
<div style={{ fontSize: 13, color: "#9ca3af" }}>
|
||
Недостаточно сделок ДДУ в радиусе для тренда
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
const arrow = trendArrow(trend.delta_6m_pct);
|
||
const arrowColor = deltaColor(trend.delta_6m_pct);
|
||
const labelStyle = LABEL_COLORS[trend.label] ?? {
|
||
bg: "#f3f4f6",
|
||
color: "#374151",
|
||
};
|
||
|
||
return (
|
||
<div
|
||
style={{
|
||
borderRadius: 10,
|
||
padding: "14px 16px",
|
||
background: "#fafafa",
|
||
border: "1px solid #e5e7eb",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: 8,
|
||
}}
|
||
>
|
||
<div style={{ fontSize: 12, fontWeight: 700, color: "#374151" }}>
|
||
Тренд рынка в радиусе {trend.radius_km} км
|
||
</div>
|
||
|
||
{/* Main price */}
|
||
<div style={{ display: "flex", alignItems: "baseline", gap: 6 }}>
|
||
<span
|
||
style={{
|
||
fontSize: 24,
|
||
fontWeight: 800,
|
||
color: "#111827",
|
||
lineHeight: 1,
|
||
}}
|
||
>
|
||
{fmtPrice(trend.recent_avg_price_per_m2)} ₽/м²
|
||
</span>
|
||
</div>
|
||
<div style={{ fontSize: 12, color: "#6b7280", marginTop: -4 }}>
|
||
за последние 6 мес · {trend.recent_deals_count} сделок
|
||
</div>
|
||
|
||
{/* Delta */}
|
||
<div
|
||
style={{
|
||
display: "flex",
|
||
alignItems: "center",
|
||
gap: 4,
|
||
fontSize: 16,
|
||
fontWeight: 700,
|
||
color: arrowColor,
|
||
}}
|
||
>
|
||
<span>{arrow}</span>
|
||
<span>
|
||
{trend.delta_6m_pct > 0 ? "+" : ""}
|
||
{trend.delta_6m_pct.toFixed(1)}%
|
||
</span>
|
||
</div>
|
||
|
||
{/* Prior comparison */}
|
||
<div style={{ fontSize: 11, color: "#9ca3af" }}>
|
||
vs {fmtPrice(trend.prior_avg_price_per_m2)} ₽/м² за предыдущие 6 мес (
|
||
{trend.prior_deals_count}→{trend.recent_deals_count} сделок)
|
||
</div>
|
||
|
||
{/* Label badge */}
|
||
<div
|
||
style={{
|
||
display: "inline-block",
|
||
borderRadius: 6,
|
||
padding: "3px 10px",
|
||
background: labelStyle.bg,
|
||
color: labelStyle.color,
|
||
fontSize: 12,
|
||
fontWeight: 600,
|
||
alignSelf: "flex-start",
|
||
marginTop: 2,
|
||
}}
|
||
>
|
||
{trend.label}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|