107 lines
5.1 KiB
Markdown
107 lines
5.1 KiB
Markdown
# To AI Agent: Implement Smart Date-Based Accordion Auto-Expansion on Itinerary Navigation
|
|
|
|
## 1. Context & Feature Objective
|
|
We are introducing a smart UX routing feature between the `MemberDashboard.tsx` (or Tour Card grid) and the `ItineraryTimeline.tsx` detailed view as shown in `image_c1bcff.png` and `image_c1bdb7.png`.
|
|
|
|
- **The Goal:** When a user clicks the "Chi tiết hành trình" (Itinerary Details) button on a tour card, the system must evaluate the user's **current real-time system date**. It will match this date against the date ranges allocated to each Stage/Leg within that specific tour.
|
|
- **Action:** Upon navigating to the timeline page, the accordion wrapper corresponding to today's matching Leg must be **expanded by default** (`expandedStageId === leg.id`). If today's date falls outside the entire tour timeline (before start or after end), fallback to expanding the very first Leg (`legs[0].id`).
|
|
|
|
---
|
|
|
|
## 2. Technical Implementation Architecture
|
|
|
|
We will use `react-router-dom`'s navigation state mechanism to transfer the target calculated ID across routing boundaries without polluting the URL structure with complex query strings.
|
|
|
|
### Execution Blueprint:
|
|
1. **In Dashboard / Card Component:** Intercept the click event on "Chi tiết hành trình". Calculate the matching `leg.id` using a safe date-range comparison algorithm. Pass it inside the `Maps()` state payload.
|
|
2. **In Timeline Component:** Check for the passed state parameter inside a mount-level `useEffect` hook. If detected, override the initial `expandedStageId` state with this smart calculated ID.
|
|
|
|
---
|
|
|
|
## 3. Detailed Code Specifications
|
|
|
|
### Step 1: Implement Date-Matching Engine in Dashboard
|
|
Locate the "Chi tiết hành trình" action button inside the dashboard module. Refactor its click handler to compute the current timezone-safe operational timeline match:
|
|
|
|
```typescript
|
|
import { useNavigate } from 'react-router-dom';
|
|
|
|
// Inside your Tour Card / MemberDashboard component:
|
|
const navigate = useNavigate();
|
|
|
|
const handleNavigateToItinerary = (tour: any) => {
|
|
let targetLegId = null;
|
|
|
|
if (tour?.legs && tour.legs.length > 0) {
|
|
// 1. Get current system local time stripped of hours/minutes for pure date comparison
|
|
const today = new Date();
|
|
today.setHours(0, 0, 0, 0);
|
|
|
|
// 2. Loop and evaluate leg date bounds
|
|
for (const leg of tour.legs) {
|
|
// Resolve flexible date parameter keys from schema model
|
|
const rawStart = leg.startDate || leg.plannedStart || leg.date;
|
|
const rawEnd = leg.endDate || leg.plannedEnd || leg.date;
|
|
|
|
if (rawStart) {
|
|
const startBound = new Date(rawStart);
|
|
startBound.setHours(0, 0, 0, 0);
|
|
|
|
const endBound = rawEnd ? new Date(rawEnd) : new Date(rawStart);
|
|
endBound.setHours(23, 59, 59, 999);
|
|
|
|
// Check if today falls cleanly inside this specific leg's timeline envelope
|
|
if (today >= startBound && today <= endBound) {
|
|
targetLegId = leg.id;
|
|
break; // Match found, terminate loop early
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Robust Fallback: If today doesn't match any leg, default to the first leg
|
|
if (!targetLegId) {
|
|
targetLegId = tour.legs[0].id;
|
|
}
|
|
}
|
|
|
|
// 4. Route with state package injection
|
|
navigate(`/tour/${tour.id}`, {
|
|
state: { defaultExpandedLegId: targetLegId }
|
|
});
|
|
};
|
|
|
|
Update your JSX button connector to trigger this method:
|
|
|
|
<button
|
|
onClick={() => handleNavigateToItinerary(currentTour)}
|
|
className="flex-1 bg-yotrip-surface ... flex items-center justify-center"
|
|
>
|
|
Chi tiết hành trình
|
|
</button>
|
|
|
|
### Step 2: Consume State Payload inside the Itinerary Timeline Page
|
|
Locate the state hooks where expandedStageId is managed inside your timeline component. Update the initialization lifecycle engine to look for the navigation bundle parameters:
|
|
|
|
import { useLocation } from 'react-router-dom';
|
|
|
|
// Inside your main Itinerary Timeline rendering component:
|
|
const location = useLocation();
|
|
const [expandedStageId, setExpandedStageId] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
// Check if a specific leg ID was passed down via smart routing engine
|
|
if (location.state?.defaultExpandedLegId) {
|
|
setExpandedStageId(location.state.defaultExpandedLegId);
|
|
} else if (currentTour?.legs && currentTour.legs.length > 0) {
|
|
// Standard basic fallback behavior if user accessed URL directly without dashboard state
|
|
setExpandedStageId(currentTour.legs[0].id);
|
|
}
|
|
}, [location.state, currentTour]);
|
|
|
|
## 4. Verification & Acceptance Criteria
|
|
[ ] Mid-Tour Testing: Set a tour leg's duration date range to cover the current real-world date. Click "Chi tiết hành trình" from the dashboard. The timeline page must load with that specific leg opened automatically.
|
|
|
|
[ ] Out-of-Bounds Fallback Testing: Access a historical tour (e.g., a trip from 2024). Click the details button. The system must gracefully recognize no dates match today, and auto-expand Chặng 1 (legs[0]) instead of breaking or leaving all accordions closed.
|
|
|
|
[ ] Direct URL Access Resilience: Manually type the tour URL directly into the browser address bar (without clicking the dashboard button). The timeline state must recover gracefully and default open the first leg seamlessly.
|