fix: lỗi bong bóng Tour vẫn xuất hiện và điểm kết thúc
This commit is contained in:
@@ -61,7 +61,7 @@ const MapPicker = ({ onPick, center }: { onPick: (latlng: L.LatLng) => void, cen
|
||||
);
|
||||
};
|
||||
|
||||
export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editingLocation, isStartPoint = false, isPublicView = false, onSuccess }: { isOpen: boolean, onClose: () => void, tourId: string, initialLegId?: string, editingLocation?: any, isStartPoint?: boolean, isPublicView?: boolean, onSuccess?: () => void }) => {
|
||||
export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editingLocation, isStartPoint = false, isEndPoint = false, isPublicView = false, onSuccess }: { isOpen: boolean, onClose: () => void, tourId: string, initialLegId?: string, editingLocation?: any, isStartPoint?: boolean, isEndPoint?: boolean, isPublicView?: boolean, onSuccess?: () => void }) => {
|
||||
const [formData, setFormData] = useState({
|
||||
name: '',
|
||||
address: '',
|
||||
@@ -85,7 +85,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
const searchTimeout = useRef<any>(null);
|
||||
|
||||
// Gom các selector lại để giảm số lượng Hook gọi nội bộ và tăng hiệu năng
|
||||
const { legs, addLocation, updateLocation, updateTourStartPoint, mapCenter, currentTour, userRole } = useTourStore();
|
||||
const { legs, addLocation, updateLocation, updateTourStartPoint, updateTourEndPoint, mapCenter, currentTour, userRole } = useTourStore();
|
||||
const notify = useNotification();
|
||||
|
||||
// 1. Luôn khai báo Hook ở cấp cao nhất, không đặt code logic/return giữa các Hook
|
||||
@@ -154,9 +154,12 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
const currentLegId = formData.legId || (legs.length > 0 ? legs[0].id : '');
|
||||
const targetLeg = legs.find(l => l.id === currentLegId);
|
||||
const titleText = isStartPoint ? 'Thiết lập Điểm xuất phát' :
|
||||
isEndPoint ? 'Thiết lập Điểm kết thúc' :
|
||||
editingLocation ? `Sửa địa điểm: ${editingLocation.name}` :
|
||||
(initialLegId ? `Thêm địa điểm cho ${targetLeg?.note || `Chặng ${targetLeg?.sequence}`}` : 'Thêm địa điểm mới');
|
||||
const buttonText = isStartPoint ? 'Xác nhận Điểm xuất phát' : editingLocation ? 'Cập nhật thay đổi' : (initialLegId ? 'Xác nhận thêm vào chặng' : 'Thêm địa điểm');
|
||||
const buttonText = isStartPoint ? 'Xác nhận Điểm xuất phát' :
|
||||
isEndPoint ? 'Xác nhận Điểm kết thúc' :
|
||||
editingLocation ? 'Cập nhật thay đổi' : (initialLegId ? 'Xác nhận thêm vào chặng' : 'Thêm địa điểm');
|
||||
|
||||
const handleSearchLocation = (query: string) => {
|
||||
setFormData(prev => ({ ...prev, name: query }));
|
||||
@@ -289,6 +292,12 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
latitude: parseFloat(formData.latitude as any),
|
||||
longitude: parseFloat(formData.longitude as any),
|
||||
});
|
||||
} else if (isEndPoint) {
|
||||
await updateTourEndPoint(tourId, {
|
||||
name: formData.name || "Điểm kết thúc",
|
||||
latitude: parseFloat(formData.latitude as any),
|
||||
longitude: parseFloat(formData.longitude as any),
|
||||
});
|
||||
} else {
|
||||
const payload: any = {
|
||||
...formData,
|
||||
@@ -304,7 +313,11 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
await addLocation(tourId, payload);
|
||||
}
|
||||
}
|
||||
notify({ title: 'Thành công', message: isStartPoint ? 'Đã thiết lập điểm xuất phát.' : editingLocation ? 'Đã cập nhật địa điểm.' : 'Đã thêm địa điểm mới.', type: 'success' });
|
||||
notify({
|
||||
title: 'Thành công',
|
||||
message: isStartPoint ? 'Đã thiết lập điểm xuất phát.' : isEndPoint ? 'Đã thiết lập điểm kết thúc.' : editingLocation ? 'Đã cập nhật địa điểm.' : 'Đã thêm địa điểm mới.',
|
||||
type: 'success'
|
||||
});
|
||||
onSuccess?.(); // Gọi callback onSuccess sau khi thành công
|
||||
onClose();
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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, FileText } from 'lucide-react';
|
||||
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';
|
||||
import { useConfirm } from '@/hooks/useConfirm';
|
||||
import { useNotification } from '@/hooks/useNotification';
|
||||
@@ -45,7 +45,7 @@ export const ItineraryTimeline = ({
|
||||
onSuccess,
|
||||
isPublicView = false
|
||||
}: {
|
||||
onAddLocation?: (legId: string, isStart?: boolean) => void,
|
||||
onAddLocation?: (legId: string, isStart?: boolean, isEnd?: boolean) => void,
|
||||
onEditLocation?: (location: any) => void,
|
||||
onQuickNote?: (name: string) => void,
|
||||
onSuccess?: () => void,
|
||||
@@ -322,6 +322,27 @@ export const ItineraryTimeline = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 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)) && (
|
||||
<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">
|
||||
<Flag className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onAddLocation?.(leg.id, false, true)}
|
||||
className="flex-1 bg-red-50/20 p-4 rounded-xl border border-dashed border-red-100 hover:border-red-400 hover:bg-red-50 transition-all flex items-center justify-between group"
|
||||
>
|
||||
<div className="text-left">
|
||||
<span className="inline-block px-2 py-0.5 bg-red-50 text-red-600 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider">Điểm kết thúc</span>
|
||||
<h3 className="font-bold text-gray-400 text-sm italic">Nhấn để ghim điểm kết thúc cho Tour...</h3>
|
||||
</div>
|
||||
<Plus className="w-5 h-5 text-red-500 group-hover:scale-110 transition-transform" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{leg.locations.map((location, idx) => {
|
||||
// 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);
|
||||
|
||||
@@ -208,6 +208,7 @@ export const TourDetailPage = ({
|
||||
const [isAddMemberOpen, setIsAddMemberOpen] = useState(false);
|
||||
const [isAddPhotoOpen, setIsAddPhotoOpen] = useState(false);
|
||||
const [isStartPointAction, setIsStartPointAction] = useState(false);
|
||||
const [isEndPointAction, setIsEndPointAction] = useState(false);
|
||||
const [targetLegId, setTargetLegId] = useState<string | null>(null);
|
||||
const [editingLocation, setEditingLocation] = useState<any>(null);
|
||||
const [selectedMember, setSelectedMember] = useState<any>(null);
|
||||
@@ -1015,16 +1016,18 @@ export const TourDetailPage = ({
|
||||
</div>
|
||||
|
||||
{viewMode === 'timeline' ? (
|
||||
<ItineraryTimeline onAddLocation={(legId, isStart) => {
|
||||
<ItineraryTimeline onAddLocation={(legId, isStart, isEnd) => {
|
||||
setTargetLegId(legId);
|
||||
setEditingLocation(null);
|
||||
setIsStartPointAction(!!isStart);
|
||||
setIsEndPointAction(!!isEnd);
|
||||
setIsAddLocationOpen(true);
|
||||
}} onEditLocation={(loc) => {
|
||||
setEditingLocation(loc);
|
||||
setTargetLegId(loc.legId);
|
||||
setMapCenter([loc.latitude, loc.longitude]);
|
||||
setIsStartPointAction(false);
|
||||
setIsEndPointAction(false);
|
||||
setIsAddLocationOpen(true);
|
||||
}}
|
||||
onQuickNote={(locName: string) => handleQuickNote(locName)}
|
||||
@@ -1509,6 +1512,7 @@ export const TourDetailPage = ({
|
||||
setTargetLegId(null); // Reset khi nhấn nút floating tổng quát
|
||||
setEditingLocation(null);
|
||||
setIsStartPointAction(false);
|
||||
setIsEndPointAction(false);
|
||||
if (activeTab === 'plan') setIsAddLocationOpen(true);
|
||||
if (activeTab === 'photo' && canUploadPhoto) setIsAddPhotoOpen(true);
|
||||
}}
|
||||
@@ -1540,6 +1544,7 @@ export const TourDetailPage = ({
|
||||
onClose={() => setIsAddLocationOpen(false)}
|
||||
initialLegId={targetLegId || undefined}
|
||||
isStartPoint={isStartPointAction}
|
||||
isEndPoint={isEndPointAction}
|
||||
editingLocation={editingLocation}
|
||||
tourId={currentTour.id}
|
||||
isPublicView={isPublicView} // Pass isPublicView
|
||||
|
||||
Reference in New Issue
Block a user