fix: lỗi hiển thị ở frontend
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user