gendesign/frontend/src/components/site-finder/MarketTrendBlock.tsx

137 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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}&rarr;{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>
);
}