7.3 KiB
7.3 KiB
To AI Agent: Implement Real-Time Point-to-Point Navigation Map from Timeline Nodes
1. Context & Feature Objective
We are introducing a real-time routing feature inside ItineraryTimeline.tsx as indicated by pointers [1] and [2] in image.png.
Target Requirements:
- Interactive Node Trigger: Convert the static white circular timeline node (Pointer [1]) next to each location card into an active, clickable interactive button.
- Real-Time Geolocation Acquisition: Clicking the button must invoke the HTML5 Geolocation API to fetch the user's exact current device coordinates (
userLat,userLng). - Point-to-Point Shortest Route: Open a dedicated Map Modal (
LocationNavigationModal.tsx). This map must calculate and draw the shortest transit route connecting ONLY TWO POINTS: the user's current location (Origin) and the selected timeline node's coordinates (Destination). No other intermediate tour locations should be displayed on this map layer.
2. Technical Architecture Blueprint
We will implement this using a localized React component state wrapper combined with the device's native GPS API, loading the route into a clean Map Modal interface:
[Timeline Circular Button] ➔ [Request Browser GPS] ➔ [Capture Current Coordinates] ➔ [Open Modal with 2-Point Route Engine]
3. Detailed Code Refactoring Specifications
Step 1: Upgrade the Timeline Node Checkpoint into a Button
Locate the white circular element layout inside the leg.locations.map rendering loop. Convert it into a semantic <button> equipped with micro-interactions:
{/* ❌ OLD STATIC NODE */}
<div className="absolute left-[13px] top-[26px] w-6 h-6 bg-white ... " />
{/* ✅ NEW INTERACTIVE NAVIGATION BUTTON */}
<button
onClick={() => handleTriggerNavigation(location)}
className="absolute left-[13px] top-[26px] w-6 h-6 bg-white rounded-full border-2 border-gray-300 z-20 flex items-center justify-center shadow-sm hover:scale-110 hover:border-blue-500 hover:shadow-md transition-all group"
title="Bấm để xem chỉ đường từ vị trí của bạn"
>
{/* Inner center dot changes color on hover to signify link action */}
<div className="w-2 h-2 bg-blue-500 rounded-full group-hover:bg-red-500 transition-colors" />
</button>
### Step 2: Implement Geolocation Extraction Handler
Inside ItineraryTimeline.tsx, initialize state controllers to manage the modal visibility and add the coordinate-acquisition engine:
// 1. Initialize State Vectors for the Navigation Pipeline
const [navRouteData, setNavRouteData] = useState<{
origin: { lat: number; lng: number } | null;
destination: { lat: number; lng: number; name: string } | null;
}>({ origin: null, destination: null });
const [isNavModalOpen, setIsNavModalOpen] = useState(false);
// 2. Core Handler Engine
const handleTriggerNavigation = (location: any) => {
if (!location.latitude || !location.longitude) {
alert("Địa điểm này chưa được cấu hình tọa độ GPS chính xác.");
return;
}
if (!navigator.geolocation) {
alert("Trình duyệt hoặc thiết bị của bạn không hỗ trợ định vị toàn cầu GPS.");
return;
}
// Request high-accuracy real-time user positioning data
navigator.geolocation.getCurrentPosition(
(position) => {
setNavRouteData({
origin: {
lat: position.coords.latitude,
lng: position.coords.longitude
},
destination: {
lat: parseFloat(location.latitude),
lng: parseFloat(location.longitude),
name: location.name || "Điểm đến chọn sẵn"
}
});
setIsNavModalOpen(true);
},
(error) => {
console.error("Error fetching native geolocation metrics:", error);
alert("Không thể truy cập vị trí hiện tại của bạn. Vui lòng bật định vị GPS của thiết bị.");
},
{ enableHighAccuracy: true, timeout: 8000 }
);
};
### Step 3: Create the Dedicated 2-Point Route Modal Component
Create a new file components/LocationNavigationModal.tsx. Configure your mapping stack (e.g., Google Maps JavaScript API, Leaflet Routing Machine, or Mapbox Directions) ensuring zero waypoints are injected, displaying strictly the point-to-point path:
import React from 'react';
// Note: Adapt the map layer imports below to match your active mapping stack (e.g., Mapbox, Google, or Leaflet)
interface NavModalProps {
isOpen: boolean;
onClose: () => void;
routeData: {
origin: { lat: number; lng: number } | null;
destination: { lat: number; lng: number; name: string } | null;
};
}
export const LocationNavigationModal: React.FC<NavModalProps> = ({ isOpen, onClose, routeData }) => {
if (!isOpen || !routeData.origin || !routeData.destination) return null;
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm p-4">
<div className="bg-yotripDark-surface border border-yotripDark-border w-full max-w-4xl h-[80vh] rounded-2xl overflow-hidden flex flex-col shadow-2xl animate-fade-in">
{/* Modal Header */}
<div className="p-4 bg-slate-900 border-b border-yotripDark-border flex items-center justify-between">
<div>
<h3 className="text-md font-bold text-white flex items-center gap-2">
📍 Chỉ đường đến: <span className="text-blue-400">{routeData.destination.name}</span>
</h3>
<p className="text-xs text-gray-400 mt-0.5">Tuyến đường ngắn nhất tối ưu từ vị trí hiện tại của bạn</p>
</div>
<button onClick={onClose} className="text-gray-400 hover:text-white text-sm font-bold px-3 py-1.5 rounded-lg bg-gray-800">
Đóng [X]
</button>
</div>
{/* Map Container Viewport */}
<div className="flex-1 relative bg-slate-950">
{/* AI AGENT IMPLEMENTATION TASK: Load Map View Container here.
- Inject Marker A at [routeData.origin.lat, routeData.origin.lng] (Label: "Vị trí của bạn")
- Inject Marker B at [routeData.destination.lat, routeData.destination.lng] (Label: Destination Name)
- Invoke Directions Service with alternative route options disabled to enforce rendering ONLY the absolute shortest route line connecting these two coordinates.
*/}
<div className="absolute inset-0 flex items-center justify-center text-xs text-gray-500">
[Bản đồ Mapbox/Google/Leaflet định tuyến A ➔ B hiển thị tại đây]
</div>
</div>
</div>
</div>
);
};
Mount this modal at the bottom of your main ItineraryTimeline.tsx file return layer:
<LocationNavigationModal
isOpen={isNavModalOpen}
onClose={() => setIsNavModalOpen(false)}
routeData={navRouteData}
/>
## 4. Verification & Acceptance Criteria for AI Agent
[ ] Interaction Verification: Hovering over the circular white node checkpoints must convert the mouse cursor into a pointer hand and slightly scale up the element (scale-110).
[ ] Security Validation: Triggering the handler must properly request the standard native browser/OS location authorization dialog popup.
[ ] Clean Routing Strictness: The generated modal map must display precisely 2 custom markers (Current Device Spot & Selected Location Point). Ensure no other intermediate destination waypoints from other stages crawl onto the map workspace canvas.