Vào thằng Tour Dashboard khi click vào Tour callout
This commit is contained in:
+18
-80
@@ -1,5 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { MapContainer, TileLayer, Marker, Popup, useMap, useMapEvents } from 'react-leaflet';
|
||||
import _MarkerClusterGroup from 'react-leaflet-cluster';
|
||||
const MarkerClusterGroup = (_MarkerClusterGroup as any).default || _MarkerClusterGroup;
|
||||
import L from 'leaflet';
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import { useTourStore } from './useTourStore.js';
|
||||
@@ -44,7 +46,7 @@ function MapTracker() {
|
||||
}
|
||||
|
||||
export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: () => void, onLogout?: () => void, user?: any, onViewTour: (id: string) => void }) => {
|
||||
const { publicTours, fetchPublicTours, fetchTour, updateTour, deleteTour, mapCenter, setMapCenter } = useTourStore();
|
||||
const { publicTours, fetchPublicTours, setMapCenter } = useTourStore();
|
||||
|
||||
// Khởi tạo vị trí từ localStorage nếu có, nếu không dùng mặc định (TP.HCM)
|
||||
const [initialViewState] = useState(() => {
|
||||
@@ -79,27 +81,6 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: ()
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleEditTour = async (tour: any) => {
|
||||
const newTitle = window.prompt("Nhập tên mới cho Tour:", tour.title);
|
||||
if (newTitle && newTitle !== tour.title) {
|
||||
try {
|
||||
await updateTour(tour.id, { title: newTitle });
|
||||
} catch (err: any) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteTour = async (id: string) => {
|
||||
if (window.confirm("Bạn có chắc chắn muốn xóa Tour này và toàn bộ dữ liệu liên quan?")) {
|
||||
try {
|
||||
await deleteTour(id);
|
||||
} catch (err: any) {
|
||||
alert(err.message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full relative">
|
||||
{/* Nút quay lại */}
|
||||
@@ -151,7 +132,12 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: ()
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MapContainer center={userPos} zoom={mapZoom} className="h-full w-full">
|
||||
<MapContainer
|
||||
center={userPos}
|
||||
zoom={mapZoom}
|
||||
className="h-full w-full"
|
||||
preferCanvas={true}
|
||||
>
|
||||
<TileLayer
|
||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
attribution='© OpenStreetMap contributors'
|
||||
@@ -163,49 +149,21 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: ()
|
||||
{/* Tự động di chuyển bản đồ đến vị trí người dùng khi tìm thấy tọa độ */}
|
||||
<RecenterMap position={userPos} />
|
||||
|
||||
{publicTours.map((tour) => {
|
||||
<MarkerClusterGroup chunkedLoading>
|
||||
{publicTours.map((tour) => {
|
||||
const startLoc = tour.legs?.[0]?.locations?.[0];
|
||||
if (!startLoc) return null;
|
||||
|
||||
const lastLeg = tour.legs?.[tour.legs.length - 1];
|
||||
const endLoc = lastLeg?.locations?.[lastLeg.locations.length - 1];
|
||||
|
||||
const tourImage = tour.photos?.[0]?.imageUrl || `https://picsum.photos/seed/${tour.id}/200/200`;
|
||||
|
||||
const commonPopup = (
|
||||
<Popup className="custom-popup">
|
||||
<div className="p-1 max-w-[200px]">
|
||||
<img src={tourImage} className="w-full h-32 object-cover rounded-lg mb-2" />
|
||||
<h4 className="font-bold text-gray-900 leading-tight mb-1">{tour.title}</h4>
|
||||
<button
|
||||
onClick={() => onViewTour(tour.id)}
|
||||
className="text-xs font-bold text-blue-600 flex items-center gap-1">
|
||||
Xem chi tiết hình ảnh <ImageIcon className="w-3 h-3" />
|
||||
</button>
|
||||
|
||||
{user && (
|
||||
<div className="flex gap-2 mt-3 pt-3 border-t border-gray-100">
|
||||
<button
|
||||
onClick={() => handleEditTour(tour)}
|
||||
className="flex-1 flex items-center justify-center gap-1 py-1.5 bg-blue-50 text-blue-600 rounded-lg text-[10px] font-bold hover:bg-blue-100 transition-colors">
|
||||
<Edit2 className="w-3 h-3" /> Sửa
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDeleteTour(tour.id)}
|
||||
className="flex-1 flex items-center justify-center gap-1 py-1.5 bg-red-50 text-red-600 rounded-lg text-[10px] font-bold hover:bg-red-100 transition-colors">
|
||||
<Trash2 className="w-3 h-3" /> Xóa
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Popup>
|
||||
);
|
||||
|
||||
return (
|
||||
<React.Fragment key={tour.id}>
|
||||
{/* Start Marker - Blue with 'S' Badge */}
|
||||
{/* Tour Marker - Bong bóng chứa thumbnail. Click chuyển vào Dashboard */}
|
||||
<Marker
|
||||
position={[startLoc.latitude, startLoc.longitude]}
|
||||
eventHandlers={{
|
||||
click: () => onViewTour(tour.id)
|
||||
}}
|
||||
icon={L.divIcon({
|
||||
className: 'custom-bubble',
|
||||
html: `
|
||||
@@ -221,31 +179,11 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: ()
|
||||
iconSize: [48, 48],
|
||||
iconAnchor: [24, 24]
|
||||
})}
|
||||
>
|
||||
{commonPopup}
|
||||
</Marker>
|
||||
|
||||
{/* End Marker - Green with 'E' (Only if different from start) */}
|
||||
{endLoc && (endLoc.latitude !== startLoc.latitude || endLoc.longitude !== startLoc.longitude) && (
|
||||
<Marker
|
||||
position={[endLoc.latitude, endLoc.longitude]}
|
||||
icon={L.divIcon({
|
||||
className: 'custom-end-marker',
|
||||
html: `
|
||||
<div class="w-8 h-8 bg-green-600 rounded-full border-2 border-white shadow-lg flex items-center justify-center text-xs font-black text-white hover:scale-110 transition-transform">
|
||||
E
|
||||
</div>
|
||||
`,
|
||||
iconSize: [32, 32],
|
||||
iconAnchor: [16, 16]
|
||||
})}
|
||||
>
|
||||
{commonPopup}
|
||||
</Marker>
|
||||
)}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
})}
|
||||
</MarkerClusterGroup>
|
||||
</MapContainer>
|
||||
|
||||
{/* Admin Modal */}
|
||||
|
||||
Reference in New Issue
Block a user