feat(sf-fe-a6): Section 2 «Сети и точки подключения» — real B5 utilities data #354
4 changed files with 616 additions and 6 deletions
|
|
@ -7,6 +7,7 @@ import { AnalysisSidebar } from "@/components/site-finder/analysis/AnalysisSideb
|
||||||
import { AnalysisBreadcrumb } from "@/components/site-finder/analysis/AnalysisBreadcrumb";
|
import { AnalysisBreadcrumb } from "@/components/site-finder/analysis/AnalysisBreadcrumb";
|
||||||
import { UserAvatar } from "@/components/site-finder/analysis/UserAvatar";
|
import { UserAvatar } from "@/components/site-finder/analysis/UserAvatar";
|
||||||
import { Section1ParcelInfo } from "@/components/site-finder/analysis/Section1ParcelInfo";
|
import { Section1ParcelInfo } from "@/components/site-finder/analysis/Section1ParcelInfo";
|
||||||
|
import { Section2NetworksUtilities } from "@/components/site-finder/analysis/Section2NetworksUtilities";
|
||||||
|
|
||||||
// ── Section placeholder (Wave 3–4 refactor will replace these) ─────────────────
|
// ── Section placeholder (Wave 3–4 refactor will replace these) ─────────────────
|
||||||
|
|
||||||
|
|
@ -133,12 +134,8 @@ export default function AnalysisPage({ params }: PageProps) {
|
||||||
{/* Section 1 — A5: Инфо об участке */}
|
{/* Section 1 — A5: Инфо об участке */}
|
||||||
<Section1ParcelInfo cad={cad} />
|
<Section1ParcelInfo cad={cad} />
|
||||||
|
|
||||||
{/* Section 2 — TODO A6 */}
|
{/* Section 2 — A6: Сети и точки подключения */}
|
||||||
<SectionPlaceholder
|
<Section2NetworksUtilities cad={cad} />
|
||||||
id="section-2"
|
|
||||||
title="2. Земля и риски"
|
|
||||||
note="TODO A6: reuse NspdEngineeringNearbyBlock в новом layout headline+map"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Section 3 — TODO A7/A8/A9 */}
|
{/* Section 3 — TODO A7/A8/A9 */}
|
||||||
<SectionPlaceholder
|
<SectionPlaceholder
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,564 @@
|
||||||
|
"use client";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section2NetworksUtilities — "2. Сети и точки подключения"
|
||||||
|
*
|
||||||
|
* Layout:
|
||||||
|
* HeadlineBar (title «Сети» + subtitle с расстояниями)
|
||||||
|
* Utilities table: 4 rows × [Иконка / Тип / Расстояние / Количество / Статус]
|
||||||
|
* Warning banner if power_line_охранная_зона_flag === true
|
||||||
|
* Note (caveat text)
|
||||||
|
*
|
||||||
|
* Data: useParcelAnalyzeQuery (B5) — field `utilities`.
|
||||||
|
* If utilities absent from response — shows "нет данных" state gracefully.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
import {
|
||||||
|
Zap,
|
||||||
|
Flame,
|
||||||
|
Droplet,
|
||||||
|
Pipette,
|
||||||
|
AlertTriangle,
|
||||||
|
Info,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { HeadlineBar } from "@/components/ui/HeadlineBar";
|
||||||
|
import { useParcelAnalyzeQuery } from "@/lib/site-finder-api";
|
||||||
|
import type { UtilitySummaryItem } from "@/lib/site-finder-api";
|
||||||
|
|
||||||
|
// ── Subtype display config ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface UtilityDisplayConfig {
|
||||||
|
label: string;
|
||||||
|
icon: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUtilityConfig(subtype: string): UtilityDisplayConfig {
|
||||||
|
switch (subtype) {
|
||||||
|
case "substation":
|
||||||
|
return {
|
||||||
|
label: "Электричество (подстанция)",
|
||||||
|
icon: (
|
||||||
|
<Zap size={16} strokeWidth={1.5} style={{ color: "var(--viz-4)" }} />
|
||||||
|
),
|
||||||
|
};
|
||||||
|
case "power_line":
|
||||||
|
return {
|
||||||
|
label: "Электричество (ЛЭП)",
|
||||||
|
icon: (
|
||||||
|
<Zap size={16} strokeWidth={1.5} style={{ color: "var(--viz-4)" }} />
|
||||||
|
),
|
||||||
|
};
|
||||||
|
case "pipeline":
|
||||||
|
return {
|
||||||
|
label: "Газ (газопровод)",
|
||||||
|
icon: (
|
||||||
|
<Flame
|
||||||
|
size={16}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
style={{ color: "var(--viz-4)" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
case "water_intake":
|
||||||
|
return {
|
||||||
|
label: "Водопровод",
|
||||||
|
icon: (
|
||||||
|
<Droplet
|
||||||
|
size={16}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
style={{ color: "var(--viz-2)" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
case "pumping_station":
|
||||||
|
return {
|
||||||
|
label: "Канализация (насосная)",
|
||||||
|
icon: (
|
||||||
|
<Pipette
|
||||||
|
size={16}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
style={{ color: "var(--fg-secondary)" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
label: subtype,
|
||||||
|
icon: (
|
||||||
|
<Info
|
||||||
|
size={16}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
style={{ color: "var(--fg-tertiary)" }}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Distance → presence status ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
type PresenceStatus = "close" | "reachable" | "far" | "absent";
|
||||||
|
|
||||||
|
function getPresenceStatus(nearestM: number): PresenceStatus {
|
||||||
|
if (nearestM <= 200) return "close";
|
||||||
|
if (nearestM <= 500) return "reachable";
|
||||||
|
if (nearestM <= 1000) return "far";
|
||||||
|
return "absent";
|
||||||
|
}
|
||||||
|
|
||||||
|
const PRESENCE_LABEL: Record<PresenceStatus, string> = {
|
||||||
|
close: "В радиусе 200 м",
|
||||||
|
reachable: "В радиусе 500 м",
|
||||||
|
far: "В радиусе 1 км",
|
||||||
|
absent: "Далее 1 км",
|
||||||
|
};
|
||||||
|
|
||||||
|
const PRESENCE_COLOR: Record<PresenceStatus, string> = {
|
||||||
|
close: "var(--success)",
|
||||||
|
reachable: "var(--accent)",
|
||||||
|
far: "var(--warn)",
|
||||||
|
absent: "var(--danger)",
|
||||||
|
};
|
||||||
|
|
||||||
|
const PRESENCE_BG: Record<PresenceStatus, string> = {
|
||||||
|
close: "var(--success-soft)",
|
||||||
|
reachable: "var(--accent-soft)",
|
||||||
|
far: "var(--warn-soft)",
|
||||||
|
absent: "var(--danger-soft)",
|
||||||
|
};
|
||||||
|
|
||||||
|
// ── HeadlineBar subtitle helper ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
function buildSubtitle(items: UtilitySummaryItem[]): string {
|
||||||
|
const parts: string[] = [];
|
||||||
|
|
||||||
|
const electric = items.find(
|
||||||
|
(i) => i.subtype === "substation" || i.subtype === "power_line",
|
||||||
|
);
|
||||||
|
const gas = items.find((i) => i.subtype === "pipeline");
|
||||||
|
const water = items.find((i) => i.subtype === "water_intake");
|
||||||
|
|
||||||
|
if (electric) parts.push(`электричество ${electric.nearest_m} м`);
|
||||||
|
if (gas) parts.push(`газ ${gas.nearest_m} м`);
|
||||||
|
if (water) parts.push(`водопровод ${water.nearest_m} м`);
|
||||||
|
|
||||||
|
return parts.length > 0
|
||||||
|
? parts.join(" · ")
|
||||||
|
: "данные из НСПД об инженерной инфраструктуре";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Loading skeleton ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Section2Skeleton() {
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="section-2"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-card)",
|
||||||
|
border: "1px solid var(--border-card)",
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: 56,
|
||||||
|
background: "var(--bg-card-alt)",
|
||||||
|
borderRadius: 12,
|
||||||
|
marginBottom: 16,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: 180,
|
||||||
|
background: "var(--bg-card-alt)",
|
||||||
|
borderRadius: 10,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── No-data state ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Section2NoData() {
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="section-2"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-card)",
|
||||||
|
border: "1px solid var(--border-card)",
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
margin: "0 0 12px",
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-primary)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
2. Сети и точки подключения
|
||||||
|
</h2>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
padding: "20px 16px",
|
||||||
|
background: "var(--bg-card-alt)",
|
||||||
|
border: "1px dashed var(--border-strong)",
|
||||||
|
borderRadius: 10,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 8,
|
||||||
|
color: "var(--fg-tertiary)",
|
||||||
|
fontSize: 13,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Info size={16} strokeWidth={1.5} style={{ flexShrink: 0 }} />
|
||||||
|
Данные НСПД об инженерной инфраструктуре не получены для этого участка.
|
||||||
|
Проверьте наличие NSPD-снимка в базе.
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Error state ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function Section2Error({ message }: { message: string }) {
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="section-2"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-card)",
|
||||||
|
border: "1px solid var(--border-card)",
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
margin: "0 0 8px",
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-primary)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
2. Сети и точки подключения
|
||||||
|
</h2>
|
||||||
|
<p
|
||||||
|
style={{ margin: 0, fontSize: 13, color: "var(--danger)" }}
|
||||||
|
role="alert"
|
||||||
|
>
|
||||||
|
{message}
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utilities table ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface UtilitiesTableProps {
|
||||||
|
items: UtilitySummaryItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function UtilitiesTable({ items }: UtilitiesTableProps) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: "1px solid var(--border-card)",
|
||||||
|
borderRadius: 10,
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Table header */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "32px 1fr 120px 110px 140px",
|
||||||
|
gap: 0,
|
||||||
|
background: "var(--bg-card-alt)",
|
||||||
|
borderBottom: "1px solid var(--border-soft)",
|
||||||
|
padding: "8px 16px",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div />
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-secondary)",
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: "0.04em",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Тип объекта
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-secondary)",
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: "0.04em",
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Ближайший
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-secondary)",
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: "0.04em",
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
В 2 км
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 11,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-secondary)",
|
||||||
|
textTransform: "uppercase",
|
||||||
|
letterSpacing: "0.04em",
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Статус
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Table rows */}
|
||||||
|
{items.map((item, idx) => {
|
||||||
|
const config = getUtilityConfig(item.subtype);
|
||||||
|
const status = getPresenceStatus(item.nearest_m);
|
||||||
|
const isLast = idx === items.length - 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={`${item.subtype}-${idx}`}
|
||||||
|
style={{
|
||||||
|
display: "grid",
|
||||||
|
gridTemplateColumns: "32px 1fr 120px 110px 140px",
|
||||||
|
gap: 0,
|
||||||
|
padding: "10px 16px",
|
||||||
|
alignItems: "center",
|
||||||
|
borderBottom: isLast ? "none" : "1px solid var(--border-soft)",
|
||||||
|
background: "var(--bg-card)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Icon */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{config.icon}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Label */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 14,
|
||||||
|
color: "var(--fg-primary)",
|
||||||
|
fontWeight: 400,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{config.label}
|
||||||
|
{item.name && (
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: "block",
|
||||||
|
fontSize: 12,
|
||||||
|
color: "var(--fg-tertiary)",
|
||||||
|
marginTop: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.name}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Nearest distance */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 14,
|
||||||
|
fontVariantNumeric: "tabular-nums",
|
||||||
|
color: "var(--fg-primary)",
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.nearest_m.toLocaleString("ru")} м
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Count within 2km */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: 14,
|
||||||
|
fontVariantNumeric: "tabular-nums",
|
||||||
|
color: "var(--fg-secondary)",
|
||||||
|
textAlign: "right",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item.count_within_2km} шт.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status badge */}
|
||||||
|
<div style={{ textAlign: "right" }}>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
display: "inline-block",
|
||||||
|
padding: "2px 8px",
|
||||||
|
borderRadius: 6,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: PRESENCE_COLOR[status],
|
||||||
|
background: PRESENCE_BG[status],
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{PRESENCE_LABEL[status]}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Main component ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
cad: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Section2NetworksUtilities({ cad }: Props) {
|
||||||
|
const { isLoading, isError, error, data } = useParcelAnalyzeQuery(cad);
|
||||||
|
|
||||||
|
if (isLoading) return <Section2Skeleton />;
|
||||||
|
|
||||||
|
if (isError || !data) {
|
||||||
|
return (
|
||||||
|
<Section2Error
|
||||||
|
message={
|
||||||
|
error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: "Не удалось загрузить данные участка"
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const utilities = data.utilities;
|
||||||
|
|
||||||
|
// No utilities data in response (NSPD snapshot absent)
|
||||||
|
if (!utilities || utilities.summary.length === 0) {
|
||||||
|
return <Section2NoData />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
summary,
|
||||||
|
power_line_охранная_зона_flag: powerLineZone,
|
||||||
|
note,
|
||||||
|
} = utilities;
|
||||||
|
|
||||||
|
// HeadlineBar subtitle: list distances for key utility types
|
||||||
|
const headlineSubtitle = buildSubtitle(summary);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
id="section-2"
|
||||||
|
style={{
|
||||||
|
background: "var(--bg-card)",
|
||||||
|
border: "1px solid var(--border-card)",
|
||||||
|
borderRadius: 12,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 24,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Section title */}
|
||||||
|
<h2
|
||||||
|
style={{
|
||||||
|
margin: "0 0 12px",
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "var(--fg-primary)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
2. Сети и точки подключения
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* HeadlineBar */}
|
||||||
|
<div style={{ marginBottom: 16 }}>
|
||||||
|
<HeadlineBar title="Сети" subtitle={headlineSubtitle} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Power line охранная зона warning */}
|
||||||
|
{powerLineZone && (
|
||||||
|
<div
|
||||||
|
role="alert"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
gap: 10,
|
||||||
|
padding: "12px 16px",
|
||||||
|
background: "var(--danger-soft)",
|
||||||
|
border: "1px solid var(--danger)",
|
||||||
|
borderRadius: 10,
|
||||||
|
marginBottom: 16,
|
||||||
|
fontSize: 13,
|
||||||
|
color: "var(--danger)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<AlertTriangle
|
||||||
|
size={16}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
style={{ flexShrink: 0, marginTop: 1 }}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
Участок находится в охранной зоне ЛЭП ≥35 кВ. Капитальное
|
||||||
|
строительство в ОЗ запрещено (СП 36.13330, ЗОУИТ тип 5).
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Utilities table */}
|
||||||
|
<UtilitiesTable items={summary} />
|
||||||
|
|
||||||
|
{/* Caveat note */}
|
||||||
|
{note && (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
gap: 8,
|
||||||
|
marginTop: 12,
|
||||||
|
padding: "10px 12px",
|
||||||
|
background: "var(--bg-card-alt)",
|
||||||
|
borderRadius: 8,
|
||||||
|
fontSize: 12,
|
||||||
|
color: "var(--fg-secondary)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Info
|
||||||
|
size={14}
|
||||||
|
strokeWidth={1.5}
|
||||||
|
style={{ flexShrink: 0, marginTop: 1, color: "var(--fg-tertiary)" }}
|
||||||
|
/>
|
||||||
|
<span>{note}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -112,5 +112,29 @@
|
||||||
"encumbrance": "Нет",
|
"encumbrance": "Нет",
|
||||||
"status": "Учтённый",
|
"status": "Учтённый",
|
||||||
"last_updated": "2025-10-01"
|
"last_updated": "2025-10-01"
|
||||||
|
},
|
||||||
|
"utilities": {
|
||||||
|
"summary": [
|
||||||
|
{
|
||||||
|
"subtype": "substation",
|
||||||
|
"nearest_m": 59,
|
||||||
|
"name": null,
|
||||||
|
"count_within_2km": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subtype": "pipeline",
|
||||||
|
"nearest_m": 320,
|
||||||
|
"name": null,
|
||||||
|
"count_within_2km": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"subtype": "power_line",
|
||||||
|
"nearest_m": 630,
|
||||||
|
"name": null,
|
||||||
|
"count_within_2km": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"power_line_охранная_зона_flag": false,
|
||||||
|
"note": "Охранная зона ЛЭП ≥35 кВ — 15-40м по обе стороны (СП 36.13330). В зоне ОЗ нельзя строить капитальные объекты."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -254,6 +254,29 @@ export interface ParcelEgrn {
|
||||||
last_updated: string | null;
|
last_updated: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Types: utilities (engineering nearby) ────────────────────────────────────
|
||||||
|
|
||||||
|
export type UtilitySubtype =
|
||||||
|
| "substation"
|
||||||
|
| "pipeline"
|
||||||
|
| "power_line"
|
||||||
|
| "water_intake"
|
||||||
|
| "pumping_station"
|
||||||
|
| string; // open for future subtypes
|
||||||
|
|
||||||
|
export interface UtilitySummaryItem {
|
||||||
|
subtype: UtilitySubtype;
|
||||||
|
nearest_m: number;
|
||||||
|
name: string | null;
|
||||||
|
count_within_2km: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UtilitiesData {
|
||||||
|
summary: UtilitySummaryItem[];
|
||||||
|
power_line_охранная_зона_flag: boolean;
|
||||||
|
note: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ParcelAnalyzeResponse {
|
export interface ParcelAnalyzeResponse {
|
||||||
cad_num: string;
|
cad_num: string;
|
||||||
source: string;
|
source: string;
|
||||||
|
|
@ -279,6 +302,8 @@ export interface ParcelAnalyzeResponse {
|
||||||
poi_count: number;
|
poi_count: number;
|
||||||
/** EGRN data — may be null if B5 extended not yet deployed */
|
/** EGRN data — may be null if B5 extended not yet deployed */
|
||||||
egrn?: ParcelEgrn | null;
|
egrn?: ParcelEgrn | null;
|
||||||
|
/** Engineering / utilities nearby — present when NSPD data available */
|
||||||
|
utilities?: UtilitiesData | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Types: B6 poi-score response ─────────────────────────────────────────────
|
// ── Types: B6 poi-score response ─────────────────────────────────────────────
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue