import { useState, useEffect, useCallback, useRef } from "react"; import { useParams, useNavigate } from "react-router-dom"; import QRCode from "qrcode"; import { MapContainer, TileLayer, Marker } from "react-leaflet"; import "leaflet/dist/leaflet.css"; import L from "leaflet"; import api from "../api/client"; import { useAuth } from "../auth/AuthContext"; import ConfirmDialog from "../components/ConfirmDialog"; import NotesPanel from "../equipment/NotesPanel"; import useMqttWebSocket from "../mqtt/useMqttWebSocket"; // Fix default Leaflet marker icon delete L.Icon.Default.prototype._getIconUrl; L.Icon.Default.mergeOptions({ iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png", iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png", shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png", }); // --- Helpers --- function formatSecondsAgo(seconds) { if (seconds == null) return null; if (seconds < 60) return `${seconds}s ago`; if (seconds < 3600) { const m = Math.round(seconds / 60); return `${m}m ago`; } if (seconds < 86400) { const h = Math.round(seconds / 3600); return `${h}h ago`; } const d = Math.round(seconds / 86400); return `${d}d ago`; } // --- Helper components --- function Field({ label, children }) { const isUnavailableText = typeof children === "string" && (children.toLowerCase() === "unavailable info" || children.toLowerCase() === "info unavailable"); return (
{label}
{children == null ? ( Unavailable info ) : isUnavailableText ? ( {children} ) : ( children )}
); } function BoolBadge({ value, yesLabel = "Yes", noLabel = "No" }) { const isMissing = typeof value !== "boolean"; const text = isMissing ? "Unavailable info" : value ? yesLabel : noLabel; const tone = isMissing ? "muted" : value ? "positive" : "negative"; return ( {text} ); } function SectionCard({ title, children, onEdit }) { return (

{title}

{onEdit && ( )}
{children}
); } function Subsection({ title, children, isFirst = false }) { return (

{title}

{children}
); } /** A grid row of fields — Nth items align across rows within a subsection */ function FieldRow({ children, columns }) { const childArray = Array.isArray(children) ? children.filter(Boolean) : [children]; const count = columns || childArray.length; return (
{children}
); } function EmptyCell() { return