fix: lỗi hiển thị ở frontend

This commit is contained in:
2026-06-21 21:53:14 +07:00
parent 403c169ddd
commit b1a539235b
40 changed files with 5094 additions and 668 deletions
+40 -19
View File
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useMemo } from 'react';
import { 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, FileText, Flag } from 'lucide-react';
import { useTourStore } from '@/store/useTourStore';
@@ -63,7 +63,6 @@ export const ItineraryTimeline = ({
// Removed: const { isPublicView } = useTourStore(state => state); // isPublicView is passed as a prop
const deleteLeg = useTourStore(state => state.deleteLeg);
const initializeLegs = useTourStore(state => state.initializeLegs);
const fetchTour = useTourStore(state => state.fetchTour);
const deleteLocation = useTourStore(state => state.deleteLocation);
// Khai báo logic canEdit để sử dụng trong toàn bộ component
@@ -80,9 +79,9 @@ export const ItineraryTimeline = ({
// Tối ưu hóa: Cập nhật UI ngay lập tức bằng cách can thiệp vào State của Store
const handleCommentIncrement = (locationId: string) => {
const currentLegs = useTourStore.getState().legs;
const updatedLegs = currentLegs.map(leg => ({
const updatedLegs = currentLegs.map((leg: any) => ({
...leg,
locations: leg.locations.map(loc =>
locations: leg.locations.map((loc: any) =>
loc.id === locationId
? { ...loc, _count: { ...loc._count, comments: (loc._count?.comments || 0) + 1 } }
: loc
@@ -94,9 +93,9 @@ export const ItineraryTimeline = ({
const handleCommentDecrement = (locationId: string) => {
const currentLegs = useTourStore.getState().legs;
const updatedLegs = currentLegs.map(leg => ({
const updatedLegs = currentLegs.map((leg: any) => ({
...leg,
locations: leg.locations.map(loc =>
locations: leg.locations.map((loc: any) =>
loc.id === locationId
? { ...loc, _count: { ...loc._count, comments: Math.max(0, (loc._count?.comments || 1) - 1) } }
: loc
@@ -305,7 +304,7 @@ export const ItineraryTimeline = ({
<div className="ml-2">
{/* Nút thêm nhanh "Điểm xuất phát" cho Chặng 1 nếu chưa có */}
{legIdx === 0 && !leg.locations.some(loc => loc.plannedStart && new Date(loc.plannedStart).getTime() === 0) && (
{legIdx === 0 && !leg.locations.some((loc: any) => loc.plannedStart && new Date(loc.plannedStart).getTime() === 0) && (
<div className="relative flex group mb-6 opacity-80 hover:opacity-100 transition-opacity">
<div className="z-10 mt-1.5 mr-4">
<div className="w-8 h-8 bg-white rounded-full border-2 border-dashed border-blue-200 flex items-center justify-center text-blue-400">
@@ -326,7 +325,7 @@ export const ItineraryTimeline = ({
)}
{/* Nút thêm nhanh "Điểm kết thúc" cho Chặng cuối nếu chưa có */}
{legIdx === legs.length - 1 && !legs.some(l => l.locations.some(loc => loc.plannedEnd && new Date(loc.plannedEnd).getTime() === 0)) && (
{legIdx === legs.length - 1 && !legs.some((l: any) => l.locations.some((loc: any) => loc.plannedEnd && new Date(loc.plannedEnd).getTime() === 0)) && (
<div className="relative flex group mb-6 opacity-80 hover:opacity-100 transition-opacity">
<div className="z-10 mt-1.5 mr-4">
<div className="w-8 h-8 bg-white rounded-full border-2 border-dashed border-red-200 flex items-center justify-center text-red-400">
@@ -346,9 +345,9 @@ export const ItineraryTimeline = ({
</div>
)}
{leg.locations.map((location, idx) => {
{leg.locations.map((location: any) => {
// 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 globalIdx = allLocations.findIndex((loc: any) => loc.id === location.id);
const prevLocation = globalIdx > 0 ? allLocations[globalIdx - 1] : null;
const distanceFromPrev = prevLocation
@@ -386,9 +385,12 @@ export const ItineraryTimeline = ({
</div>
{/* Card Content */}
<div className={`flex-1 bg-white p-4 rounded-xl border transition-all duration-200 ${
location.status === 'COMPLETED' ? 'border-green-100 bg-green-50/30' : 'border-gray-100 shadow-sm hover:shadow-md'
}`}>
<div
onClick={() => onNavigate?.(location)}
className={`flex-1 bg-white p-4 rounded-xl border transition-all duration-200 cursor-pointer ${
location.status === 'COMPLETED' ? 'border-green-100 bg-green-50/30' : 'border-gray-100 shadow-sm hover:shadow-md'
}`}
>
<div className="flex justify-between items-start">
<div>
{isStartPoint && (
@@ -398,7 +400,10 @@ export const ItineraryTimeline = ({
<span className="inline-block px-2 py-0.5 bg-red-100 text-red-700 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider">Điểm kết thúc</span>
)}
<h3
onClick={() => onNavigate?.(location)}
onClick={(e) => {
e.stopPropagation();
onNavigate?.(location);
}}
className={`font-semibold text-lg cursor-pointer hover:text-blue-600 transition-colors select-none ${location.status === 'COMPLETED' ? 'text-gray-500 line-through' : 'text-gray-800'}`}
>
{location.name}
@@ -437,11 +442,14 @@ export const ItineraryTimeline = ({
)}
</div>
<div className="text-right flex flex-col items-end">
<div className="text-right flex flex-col items-end" onClick={(e) => e.stopPropagation()}>
<div className="flex gap-1 mb-2">
{onQuickNote && !isPublicView && (
<button
onClick={() => onQuickNote(location.name)}
onClick={(e) => {
e.stopPropagation();
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"
>
@@ -449,7 +457,8 @@ export const ItineraryTimeline = ({
</button>
)}
<button
onClick={() => {
onClick={(e) => {
e.stopPropagation();
setCommentLocationId(location.id);
setCommentLocationName(location.name);
setIsCommentModalOpen(true);
@@ -471,10 +480,22 @@ export const ItineraryTimeline = ({
)}
{canEdit && ( // Allow editing and deleting of all locations if user has edit permissions
<div className="flex gap-1 mt-2">
<button onClick={() => onEditLocation?.(location)} className="p-1 text-gray-400 hover:text-blue-600 transition-colors">
<button
onClick={(e) => {
e.stopPropagation();
onEditLocation?.(location);
}}
className="p-1 text-gray-400 hover:text-blue-600 transition-colors"
>
<Edit2 className="w-3.5 h-3.5" />
</button>
<button onClick={() => handleDeleteLocation(location.id)} className="p-1 text-gray-400 hover:text-red-600 transition-colors">
<button
onClick={(e) => {
e.stopPropagation();
handleDeleteLocation(location.id);
}}
className="p-1 text-gray-400 hover:text-red-600 transition-colors"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>