gendesign/frontend/src/components/site-finder/CustomPoiLayer.tsx
bot-backend 02b5f0b5d1
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-backend (push) Successful in 2m26s
Deploy / build-worker (push) Successful in 3m41s
Deploy / deploy (push) Successful in 1m30s
Deploy / build-frontend (push) Successful in 4m2s
feat(site-finder): max-info попапы — конкурент / §3-таблица / польз.точка / соседи (#2111) (#2113)
2026-06-30 13:45:43 +00:00

156 lines
5.2 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 { CircleMarker, LayerGroup, Popup } from "react-leaflet";
import type { CustomPoi } from "@/types/customPoi";
// ---------------------------------------------------------------------------
// Props
// ---------------------------------------------------------------------------
interface Props {
pois: CustomPoi[];
onEdit: (poi: CustomPoi) => void;
onDelete: (id: number) => void;
}
// ---------------------------------------------------------------------------
// Color helpers
// ---------------------------------------------------------------------------
function markerColor(weight: number): string {
if (weight > 0) return "#16a34a"; // green — positive
if (weight < 0) return "#dc2626"; // red — negative
return "#6b7280"; // gray — neutral
}
function weightLabel(weight: number): string {
if (weight > 0) return `+${weight}`;
return String(weight);
}
// Человекочитаемая дата (ru) с guard против null / невалидного ISO.
// Repo-convention: inline new Date(...).toLocaleDateString("ru-RU") + NaN-fallback
// (см. EgrnPropertyTable.formatDate).
function formatDate(raw: string | null | undefined): string | null {
if (!raw) return null;
const d = new Date(raw);
if (Number.isNaN(d.getTime())) return null;
return d.toLocaleDateString("ru-RU");
}
// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------
export function CustomPoiLayer({ pois, onEdit, onDelete }: Props) {
if (!pois.length) return null;
return (
<LayerGroup>
{pois.map((poi) => {
const color = markerColor(poi.weight);
const isGlobal = !poi.parcel_cad;
const createdStr = formatDate(poi.created_at);
const updatedStr = formatDate(poi.updated_at);
// «изменена» показываем только если updated_at отличается от created_at.
const wasEdited =
!!poi.updated_at &&
!!poi.created_at &&
poi.updated_at !== poi.created_at;
return (
<CircleMarker
key={poi.id}
center={[poi.lat, poi.lon]}
radius={9}
pathOptions={{
color,
fillColor: color,
fillOpacity: 0.85,
weight: 2.5,
dashArray: "4 2",
}}
>
<Popup>
<div style={{ fontSize: 13, lineHeight: 1.6, minWidth: 160 }}>
<div
style={{ fontWeight: 700, marginBottom: 2, color: "#111827" }}
>
{poi.name}
</div>
{poi.category && (
<div
style={{ fontSize: 11, color: "#6b7280", marginBottom: 4 }}
>
{poi.category}
</div>
)}
<div
style={{ fontSize: 11, color: "#6b7280", marginBottom: 4 }}
>
{isGlobal
? "Глобальная точка"
: `Привязана к участку ${poi.parcel_cad}`}
</div>
<div
style={{
fontWeight: 700,
color,
marginBottom: 6,
fontSize: 14,
}}
>
Вес: {weightLabel(poi.weight)}
</div>
{poi.notes && (
<div
style={{ fontSize: 12, color: "#374151", marginBottom: 8 }}
>
{poi.notes}
</div>
)}
<div style={{ display: "flex", gap: 8 }}>
<button
type="button"
onClick={() => onEdit(poi)}
style={{
padding: "4px 10px",
fontSize: 12,
borderRadius: 5,
border: "1px solid #d1d5db",
background: "#f9fafb",
cursor: "pointer",
color: "#374151",
}}
>
Изменить
</button>
<button
type="button"
onClick={() => onDelete(poi.id)}
style={{
padding: "4px 10px",
fontSize: 12,
borderRadius: 5,
border: "none",
background: "#fef2f2",
color: "#dc2626",
cursor: "pointer",
}}
>
Удалить
</button>
</div>
{createdStr && (
<div style={{ marginTop: 8, fontSize: 10, color: "#9ca3af" }}>
Добавлена {createdStr}
{wasEdited && updatedStr ? ` · изменена ${updatedStr}` : ""}
</div>
)}
</div>
</Popup>
</CircleMarker>
);
})}
</LayerGroup>
);
}