gendesign/frontend/src/components/site-finder/NspdEngineeringNearbyBlock.tsx
Light1YT 86e9ea2937 fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1»)
Многоагентный аудит + имплементация: один воркер на файл, точечные правки.
Верификация: py_compile (47/47 .py) + tsc --noEmit (0 ошибок). Unit-тесты
не прогонялись (окружение не поднято: rollup native dep / нет pytest-venv).

Полностью исправлено (169): #1336, #1337, #1339, #1340, #1341, #1342, #1343, #1345, #1346, #1348, #1349, #1350, #1351, #1354, #1356, #1358, #1359, #1360, #1362, #1364, #1365, #1366, #1367, #1368, #1369, #1370, #1371, #1372, #1373, #1374, #1375, #1376, #1377, #1378, #1379, #1380, #1381, #1382, #1384, #1385, #1386, #1387, #1388, #1389, #1390, #1391, #1392, #1394, #1395, #1396, #1397, #1399, #1400, #1401, #1402, #1403, #1404, #1408, #1409, #1410, #1411, #1412, #1413, #1414, #1415, #1416, #1417, #1418, #1420, #1423, #1425, #1426, #1427, #1428, #1429, #1430, #1431, #1432, #1433, #1434, #1435, #1437, #1438, #1439, #1440, #1441, #1442, #1443, #1444, #1445, #1446, #1447, #1448, #1449, #1450, #1451, #1452, #1453, #1454, #1455, #1456, #1457, #1458, #1459, #1460, #1461, #1462, #1463, #1464, #1465, #1466, #1467, #1468, #1469, #1471, #1472, #1473, #1474, #1476, #1478, #1479, #1481, #1482, #1483, #1484, #1485, #1487, #1488, #1489, #1490, #1491, #1492, #1493, #1494, #1495, #1496, #1497, #1499, #1500, #1501, #1502, #1504, #1505, #1506, #1507, #1510, #1514, #1515, #1516, #1517, #1518, #1519, #1521, #1522, #1523, #1524, #1525, #1526, #1527, #1528, #1529, #1531, #1532, #1533, #1534, #1535, #1536, #1537, #1538

Частично (9, in-file часть, остаток cross-file): #1361, #1419, #1422, #1424, #1470, #1475, #1477, #1480, #1498
Требуют cross-file (3, не тронуты): #1338, #1363, #1421
Пропущено (1): #1539

Не входило в партию: 22 needs-Leha issue (нужны решения владельца).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:21:11 +05:00

223 lines
6.5 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";
import type { NspdEngineeringNearby } from "@/types/nspd";
import { useConnectionPoints } from "@/hooks/useConnectionPoints";
interface Props {
nearby: NspdEngineeringNearby[];
cadNum: string | null | undefined;
}
interface MergedRow {
label: string;
type: string | null;
distanceM: number;
source: "analyze" | "connection-points";
// Stable structure identifier (НСПД cad_num) when available — используется для
// дедупа между источниками, т.к. distanceM меряется до разных опорных точек
// (analyze — до центроида, connection-points — до границы участка).
cadNum: string | null;
}
export function NspdEngineeringNearbyBlock({ nearby, cadNum }: Props) {
const { data: cpData, isPending: cpLoading } = useConnectionPoints(cadNum);
// Merge and sort by distance
const rows: MergedRow[] = [];
for (const item of nearby) {
if (item.distance_m !== null) {
rows.push({
label: item.name ?? item.type ?? "Объект",
type: item.type,
distanceM: item.distance_m,
source: "analyze",
cadNum: null,
});
}
}
if (cpData) {
for (const s of cpData.engineering_structures) {
rows.push({
label: s.name ?? s.type ?? "Объект",
type: s.type,
distanceM: s.distance_to_boundary_m,
source: "connection-points",
cadNum: s.cad_num,
});
}
}
// Dedup. distanceM меряется до разных опорных точек (analyze — до центроида,
// connection-points — до границы), поэтому label+round(distance) пропускает
// дубли одного физического сооружения. Дедупим по стабильному cad_num, когда он
// есть; иначе (analyze без cad_num) — по label + округлённому расстоянию.
const seen = new Set<string>();
const deduped = rows.filter((r) => {
const key =
r.cadNum !== null
? `cad:${r.cadNum}`
: `lbl:${r.label}|${Math.round(r.distanceM)}`;
if (seen.has(key)) return false;
seen.add(key);
return true;
});
deduped.sort((a, b) => a.distanceM - b.distanceM);
if (deduped.length === 0 && !cpLoading) {
return (
<div style={{ fontSize: 13, color: "#6b7280", fontStyle: "italic" }}>
Инженерных объектов поблизости не найдено
</div>
);
}
return (
<div>
{deduped.length > 0 && (
<table
style={{
width: "100%",
borderCollapse: "collapse",
fontSize: 13,
}}
>
<thead>
<tr>
<th
style={{
textAlign: "left",
padding: "4px 8px",
color: "#6b7280",
fontWeight: 600,
borderBottom: "1px solid #e5e7eb",
fontSize: 12,
}}
>
Объект
</th>
<th
style={{
textAlign: "left",
padding: "4px 8px",
color: "#6b7280",
fontWeight: 600,
borderBottom: "1px solid #e5e7eb",
fontSize: 12,
}}
>
Тип
</th>
<th
style={{
textAlign: "right",
padding: "4px 8px",
color: "#6b7280",
fontWeight: 600,
borderBottom: "1px solid #e5e7eb",
fontSize: 12,
}}
>
Расстояние
</th>
</tr>
</thead>
<tbody>
{deduped.map((row, idx) => (
<tr
key={idx}
style={{
borderBottom:
idx < deduped.length - 1 ? "1px solid #f3f4f6" : undefined,
}}
>
<td
style={{
padding: "5px 8px",
color: "#1f2937",
maxWidth: 180,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{row.label}
</td>
<td
style={{
padding: "5px 8px",
color: "#6b7280",
maxWidth: 140,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}
>
{row.type ?? "—"}
</td>
<td
style={{
padding: "5px 8px",
color: "#374151",
textAlign: "right",
whiteSpace: "nowrap",
}}
>
{Math.round(row.distanceM)} м
</td>
</tr>
))}
</tbody>
</table>
)}
{cpLoading && (
<div style={{ marginTop: 8, fontSize: 12, color: "#9ca3af" }}>
Загружаем данные точек подключения
</div>
)}
{cpData?.summary && (
<div
style={{
marginTop: 10,
display: "flex",
flexWrap: "wrap",
gap: 8,
}}
>
{cpData.summary.in_protection_zone && (
<span
style={{
background: "#fee2e2",
color: "#991b1b",
borderRadius: 6,
padding: "2px 10px",
fontSize: 12,
fontWeight: 600,
}}
>
В охранной зоне
</span>
)}
{cpData.summary.nearest_structure_distance_m !== null && (
<span
style={{
background: "#f3f4f6",
color: "#374151",
borderRadius: 6,
padding: "2px 10px",
fontSize: 12,
}}
>
Ближайший:{" "}
{Math.round(cpData.summary.nearest_structure_distance_m)} м
</span>
)}
</div>
)}
</div>
);
}