"use client"; import { AlertTriangle } from "lucide-react"; import type { NspdZouitOverlap } from "@/types/nspd"; interface Props { overlaps: NspdZouitOverlap[]; /** * #1957 — кадастровый номер анализируемого участка. Нужен, чтобы подсветить * «СЗЗ вашего участка» (когда описание/рег-номер охранной зоны ссылается на * сам участок). Optional: вызовы без cad просто не делают подсветку. */ parcelCad?: string; } // Group key → human-readable label (fallback, когда type_zone отсутствует). const GROUP_LABELS: Record = { engineering: "Инженерные коммуникации", okn: "Объекты культурного наследия", natural: "Природные территории", protected: "Охраняемые зоны", other: "Прочие ЗОУИТ", }; // #1957 — СЗЗ (санитарно-защитная зона): размещение жилья внутри ограничено/ // запрещено (СанПиН 2.2.1/2.1.1.1200-03), это материальный инвест-риск. function isSzz(typeZone: string | null | undefined): boolean { return (typeZone ?? "").toLowerCase().includes("санитарно-защитн"); } // Severity → токенные цвета (раньше тут был сырой hex, вне ui-tokens). function getSeverityStyle( groupKey: string, szz: boolean, ): { bg: string; color: string; label: string } { // СЗЗ — всегда danger вне зависимости от group_key (материальный риск). if (szz) return { bg: "var(--danger-soft)", color: "var(--danger)", label: "СЗЗ" }; switch (groupKey) { case "engineering": return { bg: "var(--warn-soft)", color: "var(--warn)", label: "warn" }; case "okn": case "protected": return { bg: "var(--danger-soft)", color: "var(--danger)", label: "block", }; case "natural": return { bg: "var(--accent-soft)", color: "var(--accent)", label: "low" }; default: return { bg: "var(--bg-card-alt)", color: "var(--fg-secondary)", label: "info", }; } } /** * #1957 — ссылается ли охранная зона на сам участок? Сверяем cad участка с * descr / name_by_doc / reg_numb_border / name. (СЗЗ собственного предприятия * участка — «СЗЗ вашего участка», а не чужая зона по соседству.) */ function referencesOwnParcel( overlap: NspdZouitOverlap, parcelCad: string | undefined, ): boolean { if (!parcelCad) return false; const raw = overlap.raw_props ?? {}; const haystacks = [ overlap.reg_numb_border, overlap.name, typeof raw.descr === "string" ? raw.descr : null, typeof raw.name_by_doc === "string" ? raw.name_by_doc : null, ]; return haystacks.some((h) => typeof h === "string" && h.includes(parcelCad)); } export function NspdZouitOverlapsBlock({ overlaps, parcelCad }: Props) { if (overlaps.length === 0) { return (
Нет пересечений Охранных зон ЗОУИТ не обнаружено
); } // #1957 — СЗЗ-caveat: показываем, если хоть один overlap — санитарно-защитная // зона. Если эта СЗЗ ссылается на сам участок — формулировка строже. const hasSzz = overlaps.some((o) => isSzz(o.type_zone) || isSzz(o.name)); const ownSzz = overlaps.some( (o) => (isSzz(o.type_zone) || isSzz(o.name)) && referencesOwnParcel(o, parcelCad), ); return (
{hasSzz && (
{ownSzz ? "Участок попадает в санитарно-защитную зону (СЗЗ вашего участка). " : "Участок пересекает санитарно-защитную зону (СЗЗ). "} Размещение жилой застройки в границах СЗЗ ограничено и, как правило, не допускается (СанПиН 2.2.1/2.1.1.1200-03). Это материальный инвест-риск: до проектирования нужно подтвердить возможность строительства жилья на участке.
)} {overlaps.map((overlap, idx) => { const szz = isSzz(overlap.type_zone) || isSzz(overlap.name); const style = getSeverityStyle(overlap.group_key, szz); const own = szz && referencesOwnParcel(overlap, parcelCad); // #1957 — заголовок строки: человекочитаемая категория зоны (type_zone), // иначе name, иначе fallback на group-label. type_zone приоритетнее // общей метки группы («Охраняемые зоны»). const title = overlap.type_zone || overlap.name || GROUP_LABELS[overlap.group_key] || overlap.group_key; const reg = overlap.reg_numb_border; return (
{style.label}
{title} {own && ( СЗЗ вашего участка )}
{reg && (
Рег. номер границы: {reg}
)}
); })}
); }