From e89fd66639f092342c367b39c6ecea735e3947ce Mon Sep 17 00:00:00 2001 From: Light1YT Date: Thu, 18 Jun 2026 12:48:23 +0500 Subject: [PATCH] =?UTF-8?q?feat(site-finder):=20=D0=BA=D0=B0=D1=80=D1=82?= =?UTF-8?q?=D0=B0=20=D1=82=D0=BE=D1=87=D0=B5=D0=BA=20=D0=BF=D0=BE=D0=B4?= =?UTF-8?q?=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8=D1=8F=20(=D0=9D?= =?UTF-8?q?=D0=A1=D0=9F=D0=94)=20=D0=B2=20=D1=81=D0=B5=D0=BA=D1=86=D0=B8?= =?UTF-8?q?=D0=B8=20=C2=AB=D0=A1=D0=B5=D1=82=D0=B8=C2=BB=20(#1746,=20?= =?UTF-8?q?=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BD=D0=B0=D1=8F=20=D1=87?= =?UTF-8?q?=D0=B0=D1=81=D1=82=D1=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Section2 «Сети и точки подключения» был только таблицей расстояний без карты — пользователь не видел ГДЕ точка. Добавлена карта (dynamic SiteMap ssr:false + useConnectionPoints + ConnectionPointsLayer/CpLayerControlPanel), label «Точки подключения и охранные зоны сетей (НСПД)», empty-state при отсутствии дампа квартала. Таблица расстояний не тронута. Frontend-only. Полное обогащение 191-ФЗ (владелец/диаметр/мощность, единый источник истины) — остаётся за датасетом Минстроя (#1746 не закрыт целиком). --- .../analysis/Section2NetworksUtilities.tsx | 177 +++++++++++++++++- 1 file changed, 176 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/site-finder/analysis/Section2NetworksUtilities.tsx b/frontend/src/components/site-finder/analysis/Section2NetworksUtilities.tsx index 84662cd6..5c04a136 100644 --- a/frontend/src/components/site-finder/analysis/Section2NetworksUtilities.tsx +++ b/frontend/src/components/site-finder/analysis/Section2NetworksUtilities.tsx @@ -14,6 +14,7 @@ */ import type { ReactNode } from "react"; +import dynamic from "next/dynamic"; import { Zap, Flame, @@ -21,11 +22,53 @@ import { Pipette, AlertTriangle, Info, + MapPin, } from "lucide-react"; +import type { Geometry } from "geojson"; import { HeadlineBar } from "@/components/ui/HeadlineBar"; import { NspdVerifyLink } from "./NspdVerifyLink"; import { useParcelAnalyzeQuery } from "@/lib/site-finder-api"; -import type { UtilitySummaryItem } from "@/lib/site-finder-api"; +import type { + ParcelAnalyzeResponse, + UtilitySummaryItem, +} from "@/lib/site-finder-api"; +import type { ParcelAnalysis } from "@/types/site-finder"; +import { useConnectionPoints } from "@/hooks/useConnectionPoints"; + +// ── Connection-points map (#1746) ───────────────────────────────────────────── +// Высота тайла карты для drill-in уровня раздела. Легенда + CpLayerControlPanel +// рендерятся flow-потоком ПОД картой внутри SiteMap, поэтому обёртка НЕ задаёт +// height/overflow (иначе клипнёт контролы — см. урок MiniMap.tsx #1218). +const CP_MAP_HEIGHT = 340; + +// Leaflet использует window → монтируем только на клиенте (ssr:false), как в +// MiniMap.tsx. НЕ импортировать SiteMap в server component. +const SiteMap = dynamic( + () => + import("@/components/site-finder/SiteMap").then((m) => ({ + default: m.SiteMap, + })), + { + ssr: false, + loading: () => ( +
+ Загрузка карты... +
+ ), + }, +); // ── Subtype display config ──────────────────────────────────────────────────── @@ -525,6 +568,135 @@ function UtilitiesTable({ items }: UtilitiesTableProps) { ); } +// ── Connection-points map block (#1746) ─────────────────────────────────────── + +/** + * Адаптирует /analyze-ответ в минимальный ParcelAnalysis, который SiteMap + * требует обязательным prop `data`. Для контекста карты точек подключения нужен + * только геом участка (geom_geojson) + cad_num + source — остальные required- + * поля заполняем безопасными дефолтами (без рыночных слоёв: тут показываем + * только точки подключения + охранные зоны сетей). Зеркалит toSiteMapData из + * MiniMap.tsx, но без проброса competitors/pipeline/opportunity (не нужны). + */ +function toCpMapData(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: data.score, + score_breakdown: {}, + poi_count: 0, + competitors: [], + noise: null, + air_quality: null, + wind: null, + district: data.district ?? null, + // #255 — ЗОУИТ overlaps (охранные зоны сетей) для слоя ZouitLayer. + nspd_zouit_overlaps: data.nspd_zouit_overlaps ?? undefined, + }; +} + +interface ConnectionPointsMapProps { + data: ParcelAnalyzeResponse; +} + +function ConnectionPointsMap({ data }: ConnectionPointsMapProps) { + // Точки подключения ресурсов (электро/газ/вода/тепло) по кварталу — тянем по + // cad_num и пробрасываем в SiteMap, который рисует ConnectionPointsLayer + + // CpLayerControlPanel при truthy prop `connectionPoints` (паттерн MiniMap.tsx). + const { data: connectionPoints, isLoading } = useConnectionPoints( + data.cad_num, + ); + + const hasPoints = + !!connectionPoints && + connectionPoints.dump_available && + connectionPoints.engineering_structures.length > 0; + + return ( +
+ {/* Label над картой */} +
+ +

+ Точки подключения и охранные зоны сетей (НСПД) +

+
+

+ Инженерные объекты и зоны с особыми условиями из снимка кадастрового + квартала (НСПД). Показывает, где находятся точки подключения, а не только + расстояние до них. +

+ + {isLoading ? ( +
+ Загрузка точек подключения... +
+ ) : hasPoints ? ( + + ) : ( +
+ + Точки подключения по кварталу не загружены. Снимок инженерной + инфраструктуры НСПД для этого кадастрового квартала отсутствует. +
+ )} +
+ ); +} + // ── Main component ──────────────────────────────────────────────────────────── interface Props { @@ -640,6 +812,9 @@ export function Section2NetworksUtilities({ cad }: Props) { {/* Utilities table */} + {/* Карта точек подключения + охранных зон сетей (#1746) */} + + {/* Caveat note */} {note && (