gendesign/frontend/src/components/site-finder/analysis/MiniMap.tsx
Light1YT 7d81b1b65f
All checks were successful
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (push) Successful in 54s
CI / frontend-tests (pull_request) Successful in 54s
feat(site-finder): ЗОУИТ visual layer на Leaflet карте (#255)
ZouitLayer (GeoJSON-полигоны из geom_geojson, severity blocker/warning/info по keyword,
popup тип+имя) + ZouitLayerControlPanel (collapsible toggle per-severity, счётчик mappable)
+ SiteMap integration (default blocker+warning ON, info OFF, фон под POI). Graceful: нет
geom_geojson → слой/панель скрыты, текстовый NspdZouitOverlapsBlock остаётся. 10 vitest.

Closes #255
2026-06-13 22:31:59 +05:00

113 lines
4.4 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";
// 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);
// #1218: НЕ задаём height + overflow:hidden на обёртке. SiteMap сам
// ограничивает высоту карты-тайла (mapHeight), а легенда + layer-controls
// (Будущие проекты / Зоны риска / Перспективные ЗУ / Красные линии) лежат
// под картой потоком и должны быть видны/кликабельны.
return (
<div
style={{
width: "100%",
maxWidth: 400,
flexShrink: 0,
}}
>
<SiteMap
data={adapted}
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>
);
}