This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useMemo } from "react";
|
import { useState, useEffect, useMemo, useRef } from "react";
|
||||||
import { motion, AnimatePresence } from "framer-motion";
|
import { motion, AnimatePresence } from "framer-motion";
|
||||||
import toast, { Toaster } from "react-hot-toast";
|
import toast, { Toaster } from "react-hot-toast";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@ -69,23 +69,70 @@ import { useFavorites } from "@/app/contexts/FavoritesContext";
|
|||||||
import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from "../../enums";
|
import { BuildingTypeKeys, PropertyStatusKeys, extractCity } from "../../enums";
|
||||||
import PropertyRatingList from "@/app/components/ratings/PropertyRatingList";
|
import PropertyRatingList from "@/app/components/ratings/PropertyRatingList";
|
||||||
import { getPropertyAverageRating } from "../../utils/ratings";
|
import { getPropertyAverageRating } from "../../utils/ratings";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import "leaflet/dist/leaflet.css";
|
import "leaflet/dist/leaflet.css";
|
||||||
|
|
||||||
const MapContainer = dynamic(
|
function PropertyDetailMap({ lat, lng, title }) {
|
||||||
() => import("react-leaflet").then((m) => m.MapContainer),
|
const mapRef = useRef(null);
|
||||||
{ ssr: false },
|
const mapInstanceRef = useRef(null);
|
||||||
);
|
const markerRef = useRef(null);
|
||||||
const TileLayer = dynamic(
|
|
||||||
() => import("react-leaflet").then((m) => m.TileLayer),
|
useEffect(() => {
|
||||||
{ ssr: false },
|
if (!mapRef.current || mapInstanceRef.current) return;
|
||||||
);
|
|
||||||
const Marker = dynamic(() => import("react-leaflet").then((m) => m.Marker), {
|
if (mapRef.current._leaflet_id && !mapInstanceRef.current) {
|
||||||
ssr: false,
|
delete mapRef.current._leaflet_id;
|
||||||
});
|
}
|
||||||
const Popup = dynamic(() => import("react-leaflet").then((m) => m.Popup), {
|
|
||||||
ssr: false,
|
const L = require("leaflet");
|
||||||
});
|
|
||||||
|
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",
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = L.map(mapRef.current, {
|
||||||
|
center: [lat, lng],
|
||||||
|
zoom: 14,
|
||||||
|
scrollWheelZoom: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
|
||||||
|
attribution:
|
||||||
|
'© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
||||||
|
maxZoom: 19,
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
const marker = L.marker([lat, lng]).addTo(map).bindPopup(title);
|
||||||
|
mapInstanceRef.current = map;
|
||||||
|
markerRef.current = marker;
|
||||||
|
map.invalidateSize();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
markerRef.current?.remove();
|
||||||
|
markerRef.current = null;
|
||||||
|
if (mapInstanceRef.current) {
|
||||||
|
mapInstanceRef.current.remove();
|
||||||
|
mapInstanceRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [lat, lng, title]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (mapInstanceRef.current) {
|
||||||
|
mapInstanceRef.current.setView([lat, lng], 14);
|
||||||
|
markerRef.current?.setLatLng([lat, lng]);
|
||||||
|
markerRef.current?.setPopupContent(title);
|
||||||
|
mapInstanceRef.current.invalidateSize();
|
||||||
|
}
|
||||||
|
}, [lat, lng, title]);
|
||||||
|
|
||||||
|
return <div ref={mapRef} className="h-full w-full" />;
|
||||||
|
}
|
||||||
|
|
||||||
function formatCurrency(amount) {
|
function formatCurrency(amount) {
|
||||||
if (!amount || isNaN(amount)) return "0";
|
if (!amount || isNaN(amount)) return "0";
|
||||||
@ -1243,19 +1290,11 @@ export default function PropertyDetailsPage() {
|
|||||||
className="bg-white rounded-2xl overflow-hidden shadow-sm border border-gray-200"
|
className="bg-white rounded-2xl overflow-hidden shadow-sm border border-gray-200"
|
||||||
>
|
>
|
||||||
<div className="h-64">
|
<div className="h-64">
|
||||||
<MapContainer
|
<PropertyDetailMap
|
||||||
center={[property.location.lat, property.location.lng]}
|
lat={property.location.lat}
|
||||||
zoom={14}
|
lng={property.location.lng}
|
||||||
className="h-full w-full"
|
title={property.title}
|
||||||
scrollWheelZoom={false}
|
/>
|
||||||
>
|
|
||||||
<TileLayer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
|
|
||||||
<Marker
|
|
||||||
position={[property.location.lat, property.location.lng]}
|
|
||||||
>
|
|
||||||
<Popup>{property.title}</Popup>
|
|
||||||
</Marker>
|
|
||||||
</MapContainer>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="p-3 bg-amber-50 text-center text-sm text-amber-700 flex items-center justify-center gap-2">
|
<div className="p-3 bg-amber-50 text-center text-sm text-amber-700 flex items-center justify-center gap-2">
|
||||||
<Info className="w-4 h-4" />
|
<Info className="w-4 h-4" />
|
||||||
|
|||||||
Reference in New Issue
Block a user