100 lines
3.8 KiB
JavaScript
100 lines
3.8 KiB
JavaScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect, useRef } from 'react';
|
||
|
|
import L from 'leaflet';
|
||
|
|
import 'leaflet/dist/leaflet.css';
|
||
|
|
|
||
|
|
delete L.Icon.Default.prototype._getIconUrl;
|
||
|
|
L.Icon.Default.mergeOptions({
|
||
|
|
iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png',
|
||
|
|
iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png',
|
||
|
|
shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png',
|
||
|
|
});
|
||
|
|
|
||
|
|
export default function PropertyMapWithMarkers({ properties = [], onPropertyClick }) {
|
||
|
|
const mapRef = useRef(null);
|
||
|
|
const mapInstanceRef = useRef(null);
|
||
|
|
const markersRef = useRef([]);
|
||
|
|
const [mapLoaded, setMapLoaded] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!mapRef.current || mapInstanceRef.current) return;
|
||
|
|
|
||
|
|
const map = L.map(mapRef.current).setView([33.5138, 38.9968], 7);
|
||
|
|
|
||
|
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||
|
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
||
|
|
maxZoom: 19,
|
||
|
|
}).addTo(map);
|
||
|
|
|
||
|
|
mapInstanceRef.current = map;
|
||
|
|
setMapLoaded(true);
|
||
|
|
|
||
|
|
return () => {
|
||
|
|
if (mapInstanceRef.current) {
|
||
|
|
mapInstanceRef.current.remove();
|
||
|
|
mapInstanceRef.current = null;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (!mapInstanceRef.current || !mapLoaded) return;
|
||
|
|
|
||
|
|
markersRef.current.forEach(marker => marker.remove());
|
||
|
|
markersRef.current = [];
|
||
|
|
|
||
|
|
properties.forEach(property => {
|
||
|
|
if (property.lat && property.lng) {
|
||
|
|
const marker = L.marker([property.lat, property.lng]).addTo(mapInstanceRef.current);
|
||
|
|
|
||
|
|
const popupContent = `
|
||
|
|
<div dir="rtl" style="text-align: right; padding: 12px; max-width: 250px;">
|
||
|
|
<h3 style="font-weight: bold; font-size: 16px; margin-bottom: 8px; color: #111;">${property.title || 'عقار'}</h3>
|
||
|
|
<p style="font-size: 14px; color: #666; margin-bottom: 8px;">${property.address || property.location?.address || ''}</p>
|
||
|
|
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">
|
||
|
|
<span style="font-weight: bold; font-size: 18px; color: #d97706;">${formatPrice(property)}</span>
|
||
|
|
</div>
|
||
|
|
${property.images && property.images.length > 0 ? `<img src="${property.images[0]}" alt="${property.title}" style="width: 100%; height: 96px; object-fit: cover; border-radius: 8px; margin-bottom: 8px;" onerror="this.src='/property-placeholder.jpg'" />` : ''}
|
||
|
|
<div style="font-size: 12px; color: #888;">
|
||
|
|
${property.type ? `<p>النوع: ${property.type}</p>` : ''}
|
||
|
|
${property.bedrooms > 0 ? `<p>غرف نوم: ${property.bedrooms}</p>` : ''}
|
||
|
|
${property.bathrooms > 0 ? `<p>حمامات: ${property.bathrooms}</p>` : ''}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
`;
|
||
|
|
|
||
|
|
marker.bindPopup(popupContent);
|
||
|
|
|
||
|
|
marker.on('click', () => {
|
||
|
|
if (onPropertyClick) {
|
||
|
|
onPropertyClick(property);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
markersRef.current.push(marker);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
if (markersRef.current.length > 0) {
|
||
|
|
const group = L.featureGroup(markersRef.current);
|
||
|
|
mapInstanceRef.current.fitBounds(group.getBounds(), { padding: [50, 100] });
|
||
|
|
}
|
||
|
|
}, [properties, mapLoaded]);
|
||
|
|
|
||
|
|
const formatPrice = (property) => {
|
||
|
|
if (property.priceUnit === 'monthly') {
|
||
|
|
return `${property.price?.toLocaleString() || 0} ل.س/شهر`;
|
||
|
|
} else if (property.priceUnit === 'daily') {
|
||
|
|
return `${property.price?.toLocaleString() || 0} ل.س/يوم`;
|
||
|
|
} else {
|
||
|
|
return `${property.price?.toLocaleString() || 0} ل.س`;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="relative w-full h-[600px] rounded-xl overflow-hidden border-2 border-gray-200">
|
||
|
|
<div ref={mapRef} className="w-full h-full z-0" />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|