fix: lỗi bong bóng Tour vẫn xuất hiện và điểm bắt đầu là điểm xuất phát
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, isPublicView = false, onSuccess }: { isOpen: boolean, onClose: () => void, tourId: string, initialLegId?: string, editingLocation?: any, isPublicView?: boolean, onSuccess?: () => void }) => {
|
||||
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 }) => {
|
||||
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, mapCenter, currentTour, userRole } = useTourStore();
|
||||
const { legs, addLocation, updateLocation, updateTourStartPoint, 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
|
||||
@@ -153,8 +153,10 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
// 2. Thực hiện các tính toán và hàm xử lý
|
||||
const currentLegId = formData.legId || (legs.length > 0 ? legs[0].id : '');
|
||||
const targetLeg = legs.find(l => l.id === currentLegId);
|
||||
const titleText = 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 = editingLocation ? 'Cập nhật thay đổi' : (initialLegId ? 'Xác nhận thêm vào chặng' : 'Thêm địa điểm');
|
||||
const titleText = isStartPoint ? 'Thiết lập Điểm xuất phát' :
|
||||
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 handleSearchLocation = (query: string) => {
|
||||
setFormData(prev => ({ ...prev, name: query }));
|
||||
@@ -281,20 +283,28 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const payload: any = {
|
||||
...formData,
|
||||
expenseAmount: formData.expenseAmount.replace(/\./g, ''), // Loại bỏ dấu chấm trước khi gửi
|
||||
legId: currentLegId,
|
||||
latitude: parseFloat(formData.latitude as any),
|
||||
longitude: parseFloat(formData.longitude as any),
|
||||
};
|
||||
|
||||
if (editingLocation) {
|
||||
await updateLocation(editingLocation.id, payload);
|
||||
if (isStartPoint) {
|
||||
await updateTourStartPoint(tourId, {
|
||||
name: formData.name || "Điểm xuất phát",
|
||||
latitude: parseFloat(formData.latitude as any),
|
||||
longitude: parseFloat(formData.longitude as any),
|
||||
});
|
||||
} else {
|
||||
await addLocation(tourId, payload);
|
||||
const payload: any = {
|
||||
...formData,
|
||||
expenseAmount: formData.expenseAmount.replace(/\./g, ''), // Loại bỏ dấu chấm trước khi gửi
|
||||
legId: currentLegId,
|
||||
latitude: parseFloat(formData.latitude as any),
|
||||
longitude: parseFloat(formData.longitude as any),
|
||||
};
|
||||
|
||||
if (editingLocation) {
|
||||
await updateLocation(editingLocation.id, payload);
|
||||
} else {
|
||||
await addLocation(tourId, payload);
|
||||
}
|
||||
}
|
||||
notify({ title: 'Thành công', message: 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.' : 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) {
|
||||
|
||||
@@ -45,7 +45,7 @@ export const ItineraryTimeline = ({
|
||||
onSuccess,
|
||||
isPublicView = false
|
||||
}: {
|
||||
onAddLocation?: (legId: string) => void,
|
||||
onAddLocation?: (legId: string, isStart?: boolean) => void,
|
||||
onEditLocation?: (location: any) => void,
|
||||
onQuickNote?: (name: string) => void,
|
||||
onSuccess?: () => void,
|
||||
@@ -301,6 +301,27 @@ export const ItineraryTimeline = ({
|
||||
<div className={`absolute left-6 top-16 ${legIdx === legs.length - 1 ? 'bottom-0' : 'bottom-[-3.5rem]'} w-0.5 bg-blue-100 -z-0`} />
|
||||
|
||||
<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) && (
|
||||
<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">
|
||||
<MapPin className="w-4 h-4" />
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onAddLocation?.(leg.id, true)}
|
||||
className="flex-1 bg-blue-50/20 p-4 rounded-xl border border-dashed border-blue-100 hover:border-blue-400 hover:bg-blue-50 transition-all flex items-center justify-between group"
|
||||
>
|
||||
<div className="text-left">
|
||||
<span className="inline-block px-2 py-0.5 bg-blue-50 text-blue-600 text-[10px] font-bold rounded-md mb-1 uppercase tracking-wider">Điểm xuất phát</span>
|
||||
<h3 className="font-bold text-gray-400 text-sm italic">Nhấn để ghim điểm bắt đầu cho Tour...</h3>
|
||||
</div>
|
||||
<Plus className="w-5 h-5 text-blue-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);
|
||||
@@ -316,8 +337,9 @@ export const ItineraryTimeline = ({
|
||||
|
||||
const locationExpense = leg.expenses?.find((e: any) => e.locationId === location.id);
|
||||
|
||||
const isStartPoint = legs[0]?.id === leg.id && idx === 0;
|
||||
const isEndPoint = legs[legs.length - 1]?.id === leg.id && idx === leg.locations.length - 1;
|
||||
// Nhận diện điểm mốc dựa trên timestamp đặc biệt (0) thay vì chỉ số mảng
|
||||
const isStartPoint = location.plannedStart && new Date(location.plannedStart).getTime() === 0;
|
||||
const isEndPoint = location.plannedEnd && new Date(location.plannedEnd).getTime() === 0;
|
||||
|
||||
return (
|
||||
<div key={location.id}>
|
||||
|
||||
@@ -207,6 +207,7 @@ export const TourDetailPage = ({
|
||||
const [isAddLocationOpen, setIsAddLocationOpen] = useState(false);
|
||||
const [isAddMemberOpen, setIsAddMemberOpen] = useState(false);
|
||||
const [isAddPhotoOpen, setIsAddPhotoOpen] = useState(false);
|
||||
const [isStartPointAction, setIsStartPointAction] = useState(false);
|
||||
const [targetLegId, setTargetLegId] = useState<string | null>(null);
|
||||
const [editingLocation, setEditingLocation] = useState<any>(null);
|
||||
const [selectedMember, setSelectedMember] = useState<any>(null);
|
||||
@@ -1014,14 +1015,16 @@ export const TourDetailPage = ({
|
||||
</div>
|
||||
|
||||
{viewMode === 'timeline' ? (
|
||||
<ItineraryTimeline onAddLocation={(legId) => {
|
||||
<ItineraryTimeline onAddLocation={(legId, isStart) => {
|
||||
setTargetLegId(legId);
|
||||
setEditingLocation(null);
|
||||
setIsStartPointAction(!!isStart);
|
||||
setIsAddLocationOpen(true);
|
||||
}} onEditLocation={(loc) => {
|
||||
setEditingLocation(loc);
|
||||
setTargetLegId(loc.legId);
|
||||
setMapCenter([loc.latitude, loc.longitude]);
|
||||
setIsStartPointAction(false);
|
||||
setIsAddLocationOpen(true);
|
||||
}}
|
||||
onQuickNote={(locName: string) => handleQuickNote(locName)}
|
||||
@@ -1505,6 +1508,7 @@ export const TourDetailPage = ({
|
||||
onClick={() => {
|
||||
setTargetLegId(null); // Reset khi nhấn nút floating tổng quát
|
||||
setEditingLocation(null);
|
||||
setIsStartPointAction(false);
|
||||
if (activeTab === 'plan') setIsAddLocationOpen(true);
|
||||
if (activeTab === 'photo' && canUploadPhoto) setIsAddPhotoOpen(true);
|
||||
}}
|
||||
@@ -1535,6 +1539,7 @@ export const TourDetailPage = ({
|
||||
isOpen={isAddLocationOpen}
|
||||
onClose={() => setIsAddLocationOpen(false)}
|
||||
initialLegId={targetLegId || undefined}
|
||||
isStartPoint={isStartPointAction}
|
||||
editingLocation={editingLocation}
|
||||
tourId={currentTour.id}
|
||||
isPublicView={isPublicView} // Pass isPublicView
|
||||
|
||||
Reference in New Issue
Block a user