Files
travelplanning/DATE_OF_TOUR.md
T

5.1 KiB

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:

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.