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:
Vendored
+1
-1
File diff suppressed because one or more lines are too long
-2
File diff suppressed because one or more lines are too long
+3
-3
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+2
-2
@@ -21,8 +21,8 @@
|
||||
<meta name="twitter:title" content="Travel Planner - Lên kế hoạch chuyến đi của bạn" />
|
||||
<meta name="twitter:description" content="Khám phá những hành trình tuyệt vời và chia sẻ khoảnh khắc đẹp với cộng đồng du lịch." />
|
||||
<meta name="twitter:image" content="https://yotrip.labz.io.vn/background.avif" />
|
||||
<script type="module" crossorigin src="/assets/index-ULMuD9f4.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BU0L_D9H.css">
|
||||
<script type="module" crossorigin src="/assets/index-D6jMbgMg.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-nLng8wU9.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user