"use client"; import { useState } from "react"; import type { ConnectionPointsResponse, EngineeringStructure, } from "@/types/nspd"; import { CP_ALL_CATEGORIES, CP_CATEGORY_STYLES, type CpCategory, } from "@/components/site-finder/ConnectionPointsLayer"; import { MARKET_COLORS } from "@/components/site-finder/MarketLayers"; import type { MarketLayerKey } from "@/components/site-finder/SiteMap"; // #999 (958-B4) — конфиг рыночных слоёв для тумблеров. count = число объектов // с координатами (без них точка не рисуется), поэтому показываем именно его. export interface MarketLayerToggle { key: MarketLayerKey; label: string; color: string; count: number; } interface Props { data: ConnectionPointsResponse | null; grouped: Map; visibleCategories: Set; onToggleCategory: (cat: CpCategory) => void; onToggleAll: () => void; // #999 — рыночные слои (опциональны: панель работает и без них). marketLayers?: MarketLayerToggle[]; visibleMarketLayers?: Set; onToggleMarketLayer?: (key: MarketLayerKey) => void; } export function CpLayerControlPanel({ data, grouped, visibleCategories, onToggleCategory, onToggleAll, marketLayers, visibleMarketLayers, onToggleMarketLayer, }: Props) { const [collapsed, setCollapsed] = useState(false); const totalCount = data?.engineering_structures.length ?? 0; // #1459 — чекбоксы рендерятся только для непустых категорий, поэтому // «Показать все» должен отражать состояние именно доступных пользователю // (непустых) категорий, а не всех CP_ALL_CATEGORIES (включая пустые). const togglableCategories = CP_ALL_CATEGORIES.filter( (cat) => (grouped.get(cat)?.length ?? 0) > 0, ); const allVisible = togglableCategories.length > 0 && togglableCategories.every((cat) => visibleCategories.has(cat)); const hasMarketLayers = (marketLayers?.length ?? 0) > 0; return (
{/* Header */}
setCollapsed((v) => !v)} > {data ? "Слои карты" : "Слои рынка"} {data ? `${totalCount} тчк ` : ""} {collapsed ? "▲" : "▼"}
{!collapsed && (
{/* ── Точки подключения (CP) ─────────────────────────────── */} {data && ( <> {/* No dump */} {!data.dump_available && (
Дамп квартала не загружен — 0 точек подключения
)} {/* Empty state */} {data.dump_available && totalCount === 0 && (
0 точек подключения в этом квартале
)} {/* Toggle-all */} {totalCount > 0 && ( )} {/* Per-category */}
{CP_ALL_CATEGORIES.map((cat) => { const structs = grouped.get(cat) ?? []; const style = CP_CATEGORY_STYLES[cat]; if (structs.length === 0) return null; const active = visibleCategories.has(cat); return ( ); })}
{/* Summary */} {data.dump_available && totalCount > 0 && (
{data.summary.nearest_structure_distance_m !== null && ( Ближайший:{" "} {Math.round(data.summary.nearest_structure_distance_m)} м )} {data.summary.in_protection_zone && ( В охранной зоне )} {data.summary.protection_zones_intersecting > 0 && !data.summary.in_protection_zone && ( Охранных зон: {data.summary.protection_zones_intersecting} )}
)} )} {/* ── Рыночные слои (#999, 958-B4) ───────────────────────── */} {hasMarketLayers && visibleMarketLayers && onToggleMarketLayer && (
Рынок
{marketLayers!.map((ml) => { const active = visibleMarketLayers.has(ml.key); return ( ); })}
)}
)}
); }