gendesign/frontend/src/components/site-finder/analysis/MiniMap.tsx
Light1YT b1c0ea1268
Some checks failed
CI / changes (push) Successful in 9s
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (push) Failing after 1m50s
CI / frontend-tests (pull_request) Failing after 1m48s
CI / openapi-codegen-check (pull_request) Failing after 2m39s
CI / openapi-codegen-check (push) Failing after 2m46s
CI / backend-tests (pull_request) Successful in 9m25s
CI / backend-tests (push) Successful in 9m30s
fix(site-finder): 5 дефектов из боевого фидбека analyze (#1736 #1737 #1738 #1739 #1740)
- #1736 MiniMap: проброс useConnectionPoints → точки подключения на карте analyze (были только в /legacy)
- #1737 confidence: пронесено имя сервиса → RU-ярлык (Рынок/Будущее предложение/…) вместо «Компонент вкладывающий сервис»
- #1738 pipeline: self-exclusion субъекта (ST_DWithin 80м) — проект не считает сам себя будущим конкурентом
- #1739 PDF: snapshot_pdf обёрнут в try/except+logger.exception (причина 500 видна) + format=pdf в forecast export + font_url fallback
- #1740 gate↔recommendation: при can_build_mkd=False — gate_caveat на обоих рекомендаторах (противоречие явное, не молчит)

Verify: py_compile 5/5, tsc 0, ruff clean, pytest confidence/forecast 95 passed.

Closes #1736
Closes #1737
Closes #1738
Closes #1739
Closes #1740
2026-06-18 11:47:21 +05:00

123 lines
5.1 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";
/**
* MiniMap — compact Leaflet map (≈400px wide) showing parcel polygon + top POI.
* Dynamic import of react-leaflet (ssr:false) to avoid SSR breakage.
* Wraps SiteMap with constrained width for Section 1 layout.
*
* #1218: ранее обёртка имела `height: 320 + overflow: hidden` — это клипало
* legend + CpLayerControlPanel, рендерящиеся flow-div ПОД картой в SiteMap,
* и делало недостижимыми переключатели рыночных слоёв (Будущие проекты,
* Зоны риска, Перспективные ЗУ, Красные линии). Фикс: задаём только ширину,
* высота тайла карты — через prop `mapHeight` SiteMap; контент течёт вниз.
*/
import dynamic from "next/dynamic";
import type { ParcelAnalyzeResponse } from "@/lib/site-finder-api";
import type { ParcelAnalysis } from "@/types/site-finder";
import type { Geometry } from "geojson";
import { useConnectionPoints } from "@/hooks/useConnectionPoints";
// MiniMap высота тайла карты — компромисс между плотностью Section 1 и
// читаемостью. Layer-controls/легенда вне этого размера (flow ниже).
const MINIMAP_HEIGHT = 280;
// Lazy-mount to avoid SSR breakage (Leaflet uses window)
const SiteMap = dynamic(
() =>
import("@/components/site-finder/SiteMap").then((m) => ({
default: m.SiteMap,
})),
{
ssr: false,
loading: () => (
<div
style={{
height: MINIMAP_HEIGHT,
background: "var(--bg-card-alt)",
borderRadius: 12,
border: "1px solid var(--border-card)",
display: "flex",
alignItems: "center",
justifyContent: "center",
color: "var(--fg-tertiary)",
fontSize: 13,
}}
>
Загрузка карты...
</div>
),
},
);
interface Props {
data: ParcelAnalyzeResponse;
}
/**
* Adapts ParcelAnalyzeResponse → ParcelAnalysis shape expected by SiteMap.
* SiteMap reads: cad_num, geom_geojson, score_breakdown +
* #999 (958-B4): competitors, pipeline_24mo, nspd_risk_zones +
* §12.1-13 (#958): nspd_opportunity_parcels, nspd_red_lines (рыночные слои).
* Остальные required-поля заполняются безопасными дефолтами.
*/
function toSiteMapData(data: ParcelAnalyzeResponse): ParcelAnalysis {
return {
cad_num: data.cad_num,
source: data.source === "cad_building" ? "cad_building" : "cad_quarter",
geom_geojson: (data.geom_geojson as Geometry) ?? null,
score_breakdown: data.score_breakdown,
score: data.score,
poi_count: data.poi_count,
// #999 — пробрасываем рыночные данные в SiteMap-слои.
competitors: data.competitors ?? [],
pipeline_24mo: data.pipeline_24mo ?? undefined,
nspd_risk_zones: data.nspd_risk_zones ?? undefined,
// §12.1-13 (#958) — opportunity-ЗУ + красные линии застройки.
nspd_opportunity_parcels: data.nspd_opportunity_parcels ?? undefined,
nspd_red_lines: data.nspd_red_lines ?? undefined,
// #255 — ЗОУИТ overlaps (geom_geojson) для map-слоя охранных зон.
nspd_zouit_overlaps: data.nspd_zouit_overlaps ?? undefined,
noise: null,
air_quality: null,
wind: null,
district: data.district ?? null,
};
}
export function MiniMap({ data }: Props) {
const adapted = toSiteMapData(data);
// #1736: точки подключения ресурсов (электро/газ/вода/тепло). Зеркалим
// обвязку legacy /site-finder (page.tsx:109,568) — тянем CP-данные по
// data.cad_num и пробрасываем prop `connectionPoints` в <SiteMap>, который
// рисует ConnectionPointsLayer + CpLayerControlPanel при truthy prop.
const { data: connectionPoints } = useConnectionPoints(data.cad_num);
// #1218: НЕ задаём height + overflow:hidden на обёртке. SiteMap сам
// ограничивает высоту карты-тайла (mapHeight), а легенда + layer-controls
// (Будущие проекты / Зоны риска / Перспективные ЗУ / Красные линии) + панель
// точек подключения (CpLayerControlPanel) лежат под картой потоком и должны
// быть видны/кликабельны. MINIMAP_HEIGHT ограничивает ТОЛЬКО тайл карты, не
// клипает CP-панель.
return (
<div
style={{
width: "100%",
maxWidth: 400,
flexShrink: 0,
}}
>
<SiteMap
data={adapted}
connectionPoints={connectionPoints}
competitors={data.competitors ?? []}
pipelineObjects={data.pipeline_24mo?.top_objects ?? []}
riskZones={data.nspd_risk_zones ?? []}
opportunityParcels={data.nspd_opportunity_parcels ?? []}
redLines={data.nspd_red_lines ?? []}
mapHeight={MINIMAP_HEIGHT}
/>
</div>
);
}