fix: in đậm cho tiêu đề của cột khi xuất pdf
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user