feat(site-finder): карта точек подключения (НСПД) в секции «Сети» (#1746, доступная часть)
Section2 «Сети и точки подключения» был только таблицей расстояний без карты — пользователь не видел ГДЕ точка. Добавлена карта (dynamic SiteMap ssr:false + useConnectionPoints + ConnectionPointsLayer/CpLayerControlPanel), label «Точки подключения и охранные зоны сетей (НСПД)», empty-state при отсутствии дампа квартала. Таблица расстояний не тронута. Frontend-only. Полное обогащение 191-ФЗ (владелец/диаметр/мощность, единый источник истины) — остаётся за датасетом Минстроя (#1746 не закрыт целиком).
This commit is contained in:
parent
6214baf0c9
commit
e89fd66639
1 changed files with 176 additions and 1 deletions
|
|
@ -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: () => (
|
||||
<div
|
||||
style={{
|
||||
height: CP_MAP_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>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
// ── 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 (
|
||||
<div style={{ marginTop: 16 }}>
|
||||
{/* Label над картой */}
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
marginBottom: 4,
|
||||
}}
|
||||
>
|
||||
<MapPin
|
||||
size={16}
|
||||
strokeWidth={1.5}
|
||||
style={{ color: "var(--accent)", flexShrink: 0 }}
|
||||
/>
|
||||
<h3
|
||||
style={{
|
||||
margin: 0,
|
||||
fontSize: 14,
|
||||
fontWeight: 600,
|
||||
color: "var(--fg-primary)",
|
||||
}}
|
||||
>
|
||||
Точки подключения и охранные зоны сетей (НСПД)
|
||||
</h3>
|
||||
</div>
|
||||
<p
|
||||
style={{
|
||||
margin: "0 0 10px",
|
||||
fontSize: 12,
|
||||
color: "var(--fg-tertiary)",
|
||||
}}
|
||||
>
|
||||
Инженерные объекты и зоны с особыми условиями из снимка кадастрового
|
||||
квартала (НСПД). Показывает, где находятся точки подключения, а не только
|
||||
расстояние до них.
|
||||
</p>
|
||||
|
||||
{isLoading ? (
|
||||
<div
|
||||
style={{
|
||||
height: CP_MAP_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>
|
||||
) : hasPoints ? (
|
||||
<SiteMap
|
||||
data={toCpMapData(data)}
|
||||
connectionPoints={connectionPoints}
|
||||
mapHeight={CP_MAP_HEIGHT}
|
||||
/>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 8,
|
||||
padding: "20px 16px",
|
||||
background: "var(--bg-card-alt)",
|
||||
border: "1px dashed var(--border-strong)",
|
||||
borderRadius: 10,
|
||||
color: "var(--fg-tertiary)",
|
||||
fontSize: 13,
|
||||
}}
|
||||
>
|
||||
<Info size={16} strokeWidth={1.5} style={{ flexShrink: 0 }} />
|
||||
Точки подключения по кварталу не загружены. Снимок инженерной
|
||||
инфраструктуры НСПД для этого кадастрового квартала отсутствует.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Main component ────────────────────────────────────────────────────────────
|
||||
|
||||
interface Props {
|
||||
|
|
@ -640,6 +812,9 @@ export function Section2NetworksUtilities({ cad }: Props) {
|
|||
{/* Utilities table */}
|
||||
<UtilitiesTable items={summary} />
|
||||
|
||||
{/* Карта точек подключения + охранных зон сетей (#1746) */}
|
||||
<ConnectionPointsMap data={data} />
|
||||
|
||||
{/* Caveat note */}
|
||||
{note && (
|
||||
<div
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue