fix: tính năng tìm đường ngắn nhất và chỉ đường cho người dùng

This commit is contained in:
2026-06-24 22:05:41 +07:00
parent cfdcb573de
commit a135196cb5
12 changed files with 210 additions and 204 deletions
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from 'react';
import React, { useState, useEffect, useRef, useMemo } from 'react';
import { MapContainer, TileLayer, Marker, Polyline, Popup, useMap } from 'react-leaflet';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
@@ -34,26 +34,33 @@ const FitBounds = ({ coords }: { coords: [number, number][] }) => {
export const LocationNavigationModal: React.FC<NavModalProps> = ({ isOpen, onClose, routeData }) => {
const [routeGeometry, setRouteGeometry] = useState<[number, number][] | null>(null);
const [loading, setLoading] = useState(false);
const [isSearchingRoute, setIsSearchingRoute] = useState(false);
const [error, setError] = useState<string | null>(null);
const [routeInfo, setRouteInfo] = useState<{ distance: string; duration: string } | null>(null);
const fetchLockRef = useRef(false);
const originLat = routeData?.origin?.lat;
const originLng = routeData?.origin?.lng;
const destLat = routeData?.destination?.lat;
const destLng = routeData?.destination?.lng;
useEffect(() => {
if (!isOpen || !routeData.origin || !routeData.destination) {
if (!isOpen || !originLat || !originLng || !destLat || !destLng) {
setRouteGeometry(null);
setError(null);
setRouteInfo(null);
return;
}
const origin = routeData.origin;
const destination = routeData.destination;
if (fetchLockRef.current) return;
const fetchRoute = async () => {
setLoading(true);
setError(null);
const calculateOptimalRoute = async () => {
try {
const url = `https://router.project-osrm.org/route/v1/driving/${origin.lng},${origin.lat};${destination.lng},${destination.lat}?overview=full&geometries=geojson`;
setIsSearchingRoute(true);
fetchLockRef.current = true;
const url = `https://router.project-osrm.org/route/v1/driving/${originLng},${originLat};${destLng},${destLat}?overview=full&geometries=geojson`;
const res = await fetch(url);
if (!res.ok) throw new Error(`OSRM error: ${res.status}`);
const data: { code: string; routes: OSRMRoute[] } = await res.json();
@@ -73,11 +80,18 @@ export const LocationNavigationModal: React.FC<NavModalProps> = ({ isOpen, onClo
} catch (err: any) {
setError(err.message);
} finally {
setLoading(false);
setIsSearchingRoute(false);
fetchLockRef.current = false;
}
};
fetchRoute();
}, [isOpen, routeData.origin, routeData.destination]);
calculateOptimalRoute();
return () => {
fetchLockRef.current = false;
setIsSearchingRoute(false);
};
}, [isOpen, originLat, originLng, destLat, destLng]);
const userIcon = useMemo(() => L.divIcon({
className: '!bg-transparent !border-none',
@@ -126,7 +140,7 @@ export const LocationNavigationModal: React.FC<NavModalProps> = ({ isOpen, onClo
</div>
{/* Map Container */}
<div className="flex-1 relative bg-slate-950">
<div className="relative flex-1 bg-slate-950">
<MapContainer
center={center}
zoom={14}
@@ -155,11 +169,8 @@ export const LocationNavigationModal: React.FC<NavModalProps> = ({ isOpen, onClo
)}
</MapContainer>
{loading && (
<div className="absolute top-4 left-1/2 -translate-x-1/2 bg-slate-800 text-white text-xs px-3 py-1.5 rounded-lg shadow-lg z-[1000]">
Đang tải lộ trình...
</div>
)}
{error && (
<div className="absolute top-4 left-1/2 -translate-x-1/2 bg-red-900 text-white text-xs px-3 py-1.5 rounded-lg shadow-lg z-[1000]">
{error}
@@ -170,4 +181,3 @@ export const LocationNavigationModal: React.FC<NavModalProps> = ({ isOpen, onClo
</div>
);
};
+1 -7
View File
@@ -2585,13 +2585,7 @@ export const TourDetailPage = ({
</div>
)}
{/* Chỉ báo đang tìm đường */}
{isRoutingLoading && (
<div className="bg-white/90 backdrop-blur-md px-3 py-2 rounded-xl shadow-lg border border-white flex items-center gap-2 animate-pulse animate-in slide-in-from-left-2">
<Loader2 className="w-3.5 h-3.5 animate-spin text-blue-600" />
<span className="text-[10px] font-black text-gray-500 uppercase tracking-tighter">Đang tìm đưng tối ưu...</span>
</div>
)}
</div>
</div>