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 type { ReactNode } from "react";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
import {
|
import {
|
||||||
Zap,
|
Zap,
|
||||||
Flame,
|
Flame,
|
||||||
|
|
@ -21,11 +22,53 @@ import {
|
||||||
Pipette,
|
Pipette,
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Info,
|
Info,
|
||||||
|
MapPin,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
import type { Geometry } from "geojson";
|
||||||
import { HeadlineBar } from "@/components/ui/HeadlineBar";
|
import { HeadlineBar } from "@/components/ui/HeadlineBar";
|
||||||
import { NspdVerifyLink } from "./NspdVerifyLink";
|
import { NspdVerifyLink } from "./NspdVerifyLink";
|
||||||
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
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 ────────────────────────────────────────────────────
|
// ── 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 ────────────────────────────────────────────────────────────
|
// ── Main component ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -640,6 +812,9 @@ export function Section2NetworksUtilities({ cad }: Props) {
|
||||||
{/* Utilities table */}
|
{/* Utilities table */}
|
||||||
<UtilitiesTable items={summary} />
|
<UtilitiesTable items={summary} />
|
||||||
|
|
||||||
|
{/* Карта точек подключения + охранных зон сетей (#1746) */}
|
||||||
|
<ConnectionPointsMap data={data} />
|
||||||
|
|
||||||
{/* Caveat note */}
|
{/* Caveat note */}
|
||||||
{note && (
|
{note && (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue