import React, { useEffect, useState } from 'react'; import { MapContainer, TileLayer, Marker, Popup, useMap, useMapEvents } from 'react-leaflet'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; import { useTourStore } from './useTourStore.js'; import { X, Navigation, Image as ImageIcon, LogOut, Settings, Edit2, Trash2 } from 'lucide-react'; import { UserManagementModal } from './UserManagementModal.js'; import { CreateTourModal } from './CreateTourModal.js'; // Fix lỗi icon mặc định của Leaflet const DefaultIcon = L.icon({ iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png', shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png', iconSize: [25, 41], iconAnchor: [12, 41], }); L.Marker.prototype.options.icon = DefaultIcon; // Component Helper để cập nhật tâm bản đồ khi vị trí người dùng thay đổi function RecenterMap({ position }: { position: [number, number] }) { const map = useMap(); useEffect(() => { map.setView(position, map.getZoom()); }, [position, map]); return null; } // Component Helper để theo dõi sự di chuyển của người dùng trên bản đồ function MapTracker() { const setMapCenter = useTourStore(state => state.setMapCenter); useMapEvents({ moveend: (e) => { const map = e.target; const center = map.getCenter(); const zoom = map.getZoom(); const coords: [number, number] = [center.lat, center.lng]; setMapCenter(coords); // Lưu vị trí và mức zoom vào localStorage để sử dụng cho lần sau localStorage.setItem('map_view_state', JSON.stringify({ center: coords, zoom })); }, }); return null; } 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(); // 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(() => { const saved = localStorage.getItem('map_view_state'); if (saved) { try { return JSON.parse(saved); } catch (e) { return null; } } return null; }); const [userPos, setUserPos] = useState<[number, number]>(initialViewState?.center || [10.7769, 106.7009]); const [mapZoom] = useState(initialViewState?.zoom || 13); const [isAdminModalOpen, setIsAdminModalOpen] = useState(false); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); useEffect(() => { fetchPublicTours(); // Nếu không có vị trí lưu từ trước, mới yêu cầu lấy vị trí hiện tại của thiết bị if (!initialViewState) { navigator.geolocation.getCurrentPosition( (pos) => { const posArray: [number, number] = [pos.coords.latitude, pos.coords.longitude]; setUserPos(posArray); setMapCenter(posArray); }, () => console.log("Không thể lấy vị trí người dùng") ); } else { // Cập nhật store để đồng bộ với vị trí khởi tạo từ cache setMapCenter(initialViewState.center); } }, []); 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 (
{/* Nút quay lại */} {/* Nút đăng xuất - Chỉ hiển thị khi có user login */} {onLogout && ( )} {/* Nút quản lý người dùng cho Admin */} {user?.isAdmin && ( )} {/* Nút tạo Tour mới */} {user && ( )} {/* Header Overlay */}
Đang khám phá khu vực của bạn
{/* Theo dõi di chuyển bản đồ */} {/* Tự động di chuyển bản đồ đến vị trí người dùng khi tìm thấy tọa độ */} {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 = (

{tour.title}

{user && (
)}
); return ( {/* Start Marker - Blue with 'S' Badge */}
S
`, iconSize: [48, 48], iconAnchor: [24, 24] })} > {commonPopup} {/* End Marker - Green with 'E' (Only if different from start) */} {endLoc && (endLoc.latitude !== startLoc.latitude || endLoc.longitude !== startLoc.longitude) && ( E `, iconSize: [32, 32], iconAnchor: [16, 16] })} > {commonPopup} )} ); })} {/* Admin Modal */} setIsAdminModalOpen(false)} /> {/* Create Tour Modal */} setIsCreateModalOpen(false)} onSuccess={(tour) => { fetchTour(tour.id); onViewTour(tour.id); }} /> ); };