fix: sửa tính năng hiển thị quãng đường tại điểm đến kế tiếp

This commit is contained in:
2026-06-17 15:37:18 +07:00
parent 7ad785fed9
commit c5474f1ff6
7 changed files with 450 additions and 107 deletions
+55 -33
View File
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { format, differenceInMinutes, parseISO } from 'date-fns';
import { CheckCircle2, Circle, Clock, MapPin, AlertCircle, Zap, Navigation, Edit2, Trash2, Plus, List, X, Calendar as CalendarIcon, AlignLeft, MessageSquare } from 'lucide-react';
import { CheckCircle2, Circle, Clock, MapPin, AlertCircle, Zap, Navigation, Edit2, Trash2, Plus, List, X, Calendar as CalendarIcon, AlignLeft, MessageSquare, FileText } from 'lucide-react';
import { useTourStore } from '@/store/useTourStore';
import { useConfirm } from '@/hooks/useConfirm';
import { useNotification } from '@/hooks/useNotification';
@@ -41,8 +41,16 @@ const formatTravelTime = (minutes: number) => {
export const ItineraryTimeline = ({
onAddLocation,
onEditLocation,
onQuickNote,
onSuccess,
isPublicView = false
}: { onAddLocation?: (legId: string) => void, onEditLocation?: (location: any) => void, isPublicView?: boolean }) => {
}: {
onAddLocation?: (legId: string) => void,
onEditLocation?: (location: any) => void,
onQuickNote?: (name: string) => void,
onSuccess?: () => void,
isPublicView?: boolean
}) => {
// Tối ưu hóa selectors để chỉ lắng nghe những thay đổi cần thiết
const currentTour = useTourStore(state => state.currentTour);
const legs = useTourStore(state => state.legs);
@@ -160,6 +168,7 @@ export const ItineraryTimeline = ({
if (isConfirmed) {
try {
await deleteLeg(legId);
onSuccess?.();
} catch (err: any) {
notify({ title: 'Lỗi', message: err.message, type: 'error' });
}
@@ -174,12 +183,20 @@ export const ItineraryTimeline = ({
if (isConfirmed) {
try {
await deleteLocation(id);
onSuccess?.();
} catch (err: any) {
notify({ title: 'Lỗi', message: err.message, type: 'error' });
}
}
};
// Tạo mảng phẳng tất cả địa điểm để tính toán quãng đường liên tục giữa các chặng
const allLocations = useMemo(() => legs.flatMap(l => l.locations), [legs]);
useEffect(() => {
console.log("ItineraryTimeline: Legs updated", legs);
}, [legs]);
return (
<div className="max-w-2xl mx-auto p-0 bg-gray-50 min-h-screen">
<div className="px-2 pt-4">
@@ -285,15 +302,14 @@ export const ItineraryTimeline = ({
<div className="ml-2">
{leg.locations.map((location, idx) => {
// Logic quan trọng: Gán điểm cuối chặng này nối với điểm đầu chặng sau
const nextLocation = leg.locations[idx + 1] || legs[legIdx + 1]?.locations[0];
const distanceToNext = nextLocation
? calculateDistance(location.latitude, location.longitude, nextLocation.latitude, nextLocation.longitude)
// Tìm vị trí của điểm này trong toàn bộ hành trình
const globalIdx = allLocations.findIndex(loc => loc.id === location.id);
const prevLocation = globalIdx > 0 ? allLocations[globalIdx - 1] : null;
const distanceFromPrev = prevLocation
? calculateDistance(prevLocation.latitude, prevLocation.longitude, location.latitude, location.longitude)
: null;
const averageSpeed = 35; // km/h
const travelTimeMinutes = distanceToNext ? Math.round((distanceToNext / averageSpeed) * 60) : null;
const dwellMinutes = (location.plannedStart && location.plannedEnd)
? differenceInMinutes(parseISO(location.plannedEnd), parseISO(location.plannedStart))
: null;
@@ -370,17 +386,28 @@ export const ItineraryTimeline = ({
</div>
<div className="text-right flex flex-col items-end">
<button
onClick={() => {
setCommentLocationId(location.id);
setCommentLocationName(location.name);
setIsCommentModalOpen(true);
}}
className="flex items-center gap-1 px-2 py-1 bg-gray-50 hover:bg-blue-50 text-gray-400 hover:text-blue-600 rounded-lg text-[10px] font-bold transition-all border border-gray-100 hover:border-blue-100 mb-2"
>
<MessageSquare className="w-3 h-3" />
BÌNH LUẬN {location._count?.comments > 0 && `(${location._count.comments})`}
</button>
<div className="flex gap-1 mb-2">
{onQuickNote && !isPublicView && (
<button
onClick={() => onQuickNote(location.name)}
className="flex items-center gap-1 px-2 py-1 bg-amber-50 hover:bg-amber-100 text-amber-600 rounded-lg text-[10px] font-bold transition-all border border-amber-100"
title="Ghi chú nhanh"
>
<FileText className="w-3 h-3" />
</button>
)}
<button
onClick={() => {
setCommentLocationId(location.id);
setCommentLocationName(location.name);
setIsCommentModalOpen(true);
}}
className="flex items-center gap-1 px-2 py-1 bg-gray-50 hover:bg-blue-50 text-gray-400 hover:text-blue-600 rounded-lg text-[10px] font-bold transition-all border border-gray-100 hover:border-blue-100"
>
<MessageSquare className="w-3 h-3" />
{location._count?.comments > 0 && `(${location._count.comments})`}
</button>
</div>
<div className="flex items-center text-sm font-black text-blue-600">
<Clock className="w-3 h-3 mr-1" />
{location.plannedStart ? format(parseISO(location.plannedStart), 'HH:mm') : '--:--'}
@@ -408,18 +435,13 @@ export const ItineraryTimeline = ({
</div>
</div>
{distanceToNext !== null && travelTimeMinutes !== null && (
<div className="ml-4 -mt-4 mb-2 flex items-center gap-3">
<div className="w-8 flex justify-center">
<Navigation className="w-3 h-3 text-blue-400 rotate-180" />
</div>
<div className="flex items-center gap-2">
<span className="text-[10px] font-bold text-blue-500 bg-blue-50 px-2 py-0.5 rounded-full border border-blue-100">
{distanceToNext.toFixed(2)} km
</span>
<span className="text-[10px] font-bold text-gray-500 bg-gray-50 px-2 py-0.5 rounded-full border border-gray-100 flex items-center gap-1">
<Clock className="w-2.5 h-2.5" />
~ {formatTravelTime(travelTimeMinutes)}
{/* Hiển thị quãng đường di chuyển từ điểm trước ĐẾN điểm hiện tại */}
{distanceFromPrev !== null && distanceFromPrev > 0 && (
<div className="ml-14 -mt-4 mb-6 flex items-center gap-2 animate-in fade-in slide-in-from-left-2 duration-500">
<div className="flex items-center gap-1.5 px-3 py-1.5 bg-blue-50 text-blue-600 rounded-xl border border-blue-100 shadow-sm">
<Navigation className="w-3 h-3 rotate-45" />
<span className="text-[10px] font-black uppercase tracking-tighter">
+{distanceFromPrev.toFixed(1)} km từ {prevLocation?.name.split(',')[0]}
</span>
</div>
</div>