Files
travelplanning/IMPLEMENT_NAVI.md
T

151 lines
7.3 KiB
Markdown

# 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:
1. **Interactive Node Trigger:** Convert the static white circular timeline node (Pointer [1]) next to each location card into an active, clickable interactive button.
2. **Real-Time Geolocation Acquisition:** Clicking the button must invoke the HTML5 Geolocation API to fetch the user's exact current device coordinates (`userLat`, `userLng`).
3. **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:
```jsx
{/* ❌ 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.