diff --git a/ExploreMap.tsx b/ExploreMap.tsx index 8f93573..8f34447 100644 --- a/ExploreMap.tsx +++ b/ExploreMap.tsx @@ -3,7 +3,7 @@ import { MapContainer, TileLayer, Marker, Popup, useMap } 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 } from 'lucide-react'; +import { X, Navigation, Image as ImageIcon, LogOut, Settings, Edit2, Trash2 } from 'lucide-react'; import { UserManagementModal } from './UserManagementModal.js'; import { CreateTourModal } from './CreateTourModal.js'; @@ -26,7 +26,7 @@ function RecenterMap({ position }: { position: [number, number] }) { } export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: () => void, onLogout?: () => void, user?: any, onViewTour: (id: string) => void }) => { - const { publicTours, fetchPublicTours, fetchTour } = useTourStore(); + const { publicTours, fetchPublicTours, fetchTour, updateTour, deleteTour } = useTourStore(); const [userPos, setUserPos] = useState<[number, number]>([10.7769, 106.7009]); // Mặc định TP.HCM const [isAdminModalOpen, setIsAdminModalOpen] = useState(false); const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); @@ -39,6 +39,27 @@ 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 (
{/* Nút quay lại */} @@ -133,6 +154,21 @@ export const ExploreMap = ({ onBack, onLogout, user, onViewTour }: { onBack: () className="text-xs font-bold text-blue-600 flex items-center gap-1"> Xem chi tiết hình ảnh + + {user && ( +
+ + +
+ )}
diff --git a/ItineraryTimeline.tsx b/ItineraryTimeline.tsx index a194f3a..ec40592 100644 --- a/ItineraryTimeline.tsx +++ b/ItineraryTimeline.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { format, differenceInMinutes, parseISO } from 'date-fns'; -import { CheckCircle2, Circle, Clock, MapPin, AlertCircle, Zap, Navigation } from 'lucide-react'; +import { CheckCircle2, Circle, Clock, MapPin, AlertCircle, Zap, Navigation, Edit2, Trash2, Plus } from 'lucide-react'; import { useTourStore } from './useTourStore.js'; const TimeVariance = ({ planned, actual }: { planned: string, actual: string | null }) => { @@ -36,12 +36,37 @@ const formatTravelTime = (minutes: number) => { }; export const ItineraryTimeline = () => { - const { legs, optimizeRouting, userRole, activeLegId, setActiveLegId } = useTourStore(); + const { currentTour, legs, optimizeRouting, userRole, activeLegId, setActiveLegId, addLeg, updateLeg, deleteLeg } = useTourStore(); const toggleComplete = async (locationId: string) => { // Gọi API PATCH /api/v1/locations/:id để cập nhật trạng thái console.log("Toggle status for location:", locationId); }; + + const handleAddLeg = async () => { + const note = window.prompt("Nhập ghi chú cho chặng mới:", `Chặng ${legs.length + 1}`); + if (note && currentTour) { + await addLeg(currentTour.id, { note }); + } + }; + + const handleEditLeg = async (leg: any) => { + const note = window.prompt("Sửa ghi chú chặng:", leg.note || ""); + if (note !== null) { + await updateLeg(leg.id, { note }); + } + }; + + const handleDeleteLeg = async (legId: string) => { + if (window.confirm("Bạn có chắc chắn muốn xóa chặng này?")) { + try { + await deleteLeg(legId); + } catch (err: any) { + alert(err.message); + } + } + }; + const activeLeg = legs.find(l => l.id === activeLegId) || legs[0]; return (
@@ -61,6 +86,14 @@ export const ItineraryTimeline = () => { Chặng {leg.sequence} ))} + {['OWNER', 'MANAGER'].includes(userRole || '') && ( + + )}
@@ -81,11 +114,28 @@ export const ItineraryTimeline = () => { <> {/* Leg Header */}
-
+
{leg.note || `Chi tiết Chặng ${leg.sequence}`}
- {leg.totalDistance !== undefined && ( -
+
+ {['OWNER', 'MANAGER'].includes(userRole || '') && ( + <> + + + + )} + {leg.totalDistance !== undefined && ( +
{leg.totalDistance} km
@@ -93,15 +143,16 @@ export const ItineraryTimeline = () => { ~ {formatTravelTime(Math.round((leg.totalDistance / 35) * 60))}
- {totalDwellMinutes > 0 && ( -
- - Dừng: {formatTravelTime(totalDwellMinutes)} -
- )}
)} - {['OWNER', 'MANAGER'].includes(userRole || '') && ( + {totalDwellMinutes > 0 && ( +
+ + Dừng: {formatTravelTime(totalDwellMinutes)} +
+ )} +
+ {['OWNER', 'MANAGER'].includes(userRole || '') && legs.length > 0 && (