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
- #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
123 lines
5.1 KiB
TypeScript
123 lines
5.1 KiB
TypeScript
"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>
|
||
);
|
||
}
|