// DetailModal + MapView (Leaflet) const { useState: _useState, useEffect: _useEffect, useRef: _useRef, useMemo: _useMemo } = React; // -------------------------------------------------------------- // DETAIL MODAL // -------------------------------------------------------------- function DetailModal({ listing, onClose }) { const [idx, setIdx] = _useState(0); const history = _useMemo(() => parsePriceHistory(listing.price_history), [listing.price_history]); _useEffect(() => { const onKey = (e) => { if (e.key === "Escape") onClose(); if (e.key === "ArrowLeft") setIdx((i) => (i - 1 + listing.photos.length) % listing.photos.length); if (e.key === "ArrowRight") setIdx((i) => (i + 1) % listing.photos.length); }; window.addEventListener("keydown", onKey); document.body.style.overflow = "hidden"; return () => { window.removeEventListener("keydown", onKey); document.body.style.overflow = ""; }; }, [listing.photos.length, onClose]); // mini-map render on listing change const hasGeo = listing.latitude != null && listing.longitude != null; const mapRef = _useRef(null); _useEffect(() => { if (!mapRef.current || !window.L || !hasGeo) return; const map = window.L.map(mapRef.current, { zoomControl: false, attributionControl: false, scrollWheelZoom: false, dragging: false }).setView([listing.latitude, listing.longitude], 13); window.L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, crossOrigin: true, }).addTo(map); // divIcon dot (no marker-image asset dependency) window.L.marker([listing.latitude, listing.longitude], { icon: window.L.divIcon({ className: "geo-dot", html: "", iconSize: [18, 18] }) }).addTo(map); return () => map.remove(); }, [listing.id, hasGeo]); return (
e.stopPropagation()}>
REF{listing.ref_id} · ID {listing.id}
{listing.title}/ {listing.photos.length > 1 && ( <>
{listing.photos.map((_, i) => ( setIdx(i)}/> ))}
)}
{listing.photos.map((p, i) => ( ))}

{listing.title}

{listing.location}

{listing.description}

Amenities
{listing.amenities.map((a) => (
{a}
))}
{fmtPrice(listing.price)}/yr
{(listing.price / 12).toLocaleString("en-US", { maximumFractionDigits: 0 })} AED/month · {" "}{(listing.price / listing.size_sqft).toFixed(1)} AED/sqft
Bedrooms {listing.bedrooms}
Bathrooms {listing.bathrooms}
Size {listing.size_sqft.toLocaleString()} sqft
Type {listing.property_type}
Furnishing {listing.furnished}
Balcony {listing.balcony}
Water view {listing.water_view}
Status {listing.status}
{history.length > 0 ? (
Price history
{history.map((h, i) => { const up = h.to > h.from; return (
{fmtDate(h.date)} {fmtPriceShort(h.from)} {fmtPriceShort(h.to)} {up ? : } {Math.round(Math.abs((h.to - h.from) / h.from) * 100)}%
); })}
) : (
Price history
No price changes since {fmtDate(listing.first_seen)}.
)}
Location
{hasGeo ? ( <>
{listing.latitude.toFixed(4)}, {listing.longitude.toFixed(4)}
) : (
No map coordinates for this listing.
)}
View on Dubizzle
); } // -------------------------------------------------------------- // MAP VIEW // -------------------------------------------------------------- function MapView({ listings, onOpen, activeId, setActiveId }) { const ref = _useRef(null); const mapRef = _useRef(null); const markersRef = _useRef({}); // init map once _useEffect(() => { if (!ref.current || !window.L) return; const map = window.L.map(ref.current, { zoomControl: true, attributionControl: true }).setView([24.466, 54.49], 11); window.L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, attribution: '© OpenStreetMap' }).addTo(map); mapRef.current = map; return () => { map.remove(); mapRef.current = null; }; }, []); // rebuild markers when listings change _useEffect(() => { const map = mapRef.current; if (!map || !window.L) return; Object.values(markersRef.current).forEach((m) => map.removeLayer(m)); markersRef.current = {}; const geoListings = listings.filter((l) => l.latitude != null && l.longitude != null); if (!geoListings.length) return; const bounds = window.L.latLngBounds([]); geoListings.forEach((l) => { const isDelisted = l.status === "delisted"; const isActive = l.id === activeId; const icon = window.L.divIcon({ className: "price-marker-wrap", html: `
${fmtPriceShort(l.price)}
`, iconSize: [0, 0], iconAnchor: [0, 0] }); const m = window.L.marker([l.latitude, l.longitude], { icon, riseOnHover: true }); m.on("click", () => setActiveId(l.id)); m.bindPopup(buildPopupHtml(l), { offset: [0, -8], closeButton: false, autoClose: true }); m.on("popupopen", () => { const node = m.getPopup().getElement(); node?.querySelector(".minicard__cta")?.addEventListener("click", () => onOpen(l)); node?.querySelector(".minicard")?.addEventListener("click", () => onOpen(l)); }); m.addTo(map); markersRef.current[l.id] = m; bounds.extend([l.latitude, l.longitude]); }); if (bounds.isValid()) { map.fitBounds(bounds, { padding: [60, 60], maxZoom: 13 }); } }, [listings]); // open popup of active marker _useEffect(() => { if (!activeId) return; const m = markersRef.current[activeId]; if (m) m.openPopup(); }, [activeId, listings]); function buildPopupHtml(l) { return `
${fmtPrice(l.price)} /yr
${l.title}
${l.bedrooms} bd · ${l.bathrooms} ba · ${l.size_sqft.toLocaleString()} sqft
Open details →
`; } return
; } Object.assign(window, { DetailModal, MapView });