"use client"; import type { ParcelAnalysis } from "@/types/site-finder"; import { SectionLabel } from "@/components/ui/SectionLabel"; import { EmptyState } from "@/components/ui/EmptyState"; import { MarketTrendBlock } from "./MarketTrendBlock"; import { BestLayoutsBlock } from "./BestLayoutsBlock"; import { CompetitorTable } from "./CompetitorTable"; import { Pipeline24moBlock } from "./Pipeline24moBlock"; import { SuccessRecommendationBlock } from "./SuccessRecommendationBlock"; import { VelocityBlock } from "./VelocityBlock"; interface Props { data: ParcelAnalysis; } /** Derive radius_km from available nested fields. */ function getRadiusKm(data: ParcelAnalysis): number { return data.market_trend?.radius_km ?? data.pipeline_24mo?.radius_km ?? 3; } /** * Max success score (%) from success_recommendation ranking. * Returns null when recommendation is absent or ranking is empty. */ function getMaxMixPct(data: ParcelAnalysis): number | null { const ranking = data.success_recommendation?.ranking; if (!ranking || ranking.length === 0) return null; const maxScore = Math.max(...ranking.map((r) => r.success_score)); return Math.round(maxScore * 100); } /** * Estimated sales period in months (supply / flats_per_month). * Uses pipeline_24mo.flats_total as supply proxy, velocity for rate. * Assumes ~50 m² avg flat for unit conversion. */ function getSalesPeriodMonths(data: ParcelAnalysis): number | null { const supply = data.pipeline_24mo?.flats_total; const velocitySqm = data.velocity?.monthly_velocity_sqm; if (!supply || !velocitySqm || velocitySqm <= 0) return null; const flatsPerMonth = velocitySqm / 50; return Math.round(supply / flatsPerMonth); } export function MarketTab({ data }: Props) { const hasTrend = "market_trend" in data; const hasRecommendation = "success_recommendation" in data; const hasPipeline = data.pipeline_24mo !== undefined; const hasVelocity = "velocity" in data; const hasAny = hasTrend || hasRecommendation || hasPipeline || hasVelocity || data.competitors.length > 0; // ── Above-the-fold values ──────────────────────────────────────────────── // Maxim's spec: «ЖК старше года → не конкурент». Filter active competitors: // - site_status === 'Строящиеся' → активен // - либо ready_dt в будущем → активен (готовность ещё не наступила) // - либо ready_dt в пределах последнего года → активен (свежие продажи) // - иначе (Сданные >1y назад) → не конкурент, скрыт const REL_MS = 365 * 24 * 60 * 60 * 1000; const yearAgoTs = Date.now() - REL_MS; const relevantCompetitors = data.competitors.filter((c) => { if (c.site_status === "Строящиеся") return true; if (!c.ready_dt) return false; const ts = new Date(c.ready_dt).getTime(); if (Number.isNaN(ts)) return false; return ts >= yearAgoTs; }); const activeCount = relevantCompetitors.length; const radiusKm = getRadiusKm(data); // Treat zero velocity as "no data" — backend returns 0 when competitors // are unmapped to Objective ground truth (OBJ-3). Showing "0.0 м²/мес" // is misleading; "—" makes it clear data is unavailable. const velocityRaw = data.velocity?.monthly_velocity_sqm; const velocityPerMonth = velocityRaw != null && velocityRaw > 0 ? velocityRaw : null; const maxMixPct = getMaxMixPct(data); const salesPeriodMonths = getSalesPeriodMonths(data); const showHeadlineBar = activeCount > 0 || velocityPerMonth !== null; return (