diff --git a/AddLocationModal.tsx b/AddLocationModal.tsx index 06b199b..c3e16e1 100644 --- a/AddLocationModal.tsx +++ b/AddLocationModal.tsx @@ -59,7 +59,7 @@ const MapPicker = ({ onPick, center }: { onPick: (latlng: L.LatLng) => void, cen ); }; -export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId }: { isOpen: boolean, onClose: () => void, tourId: string, initialLegId?: string }) => { +export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editingLocation }: { isOpen: boolean, onClose: () => void, tourId: string, initialLegId?: string, editingLocation?: any }) => { const [formData, setFormData] = useState({ name: '', address: '', @@ -73,15 +73,32 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId }: { is const [isLoading, setIsLoading] = useState(false); // 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, mapCenter } = useTourStore(); + const { legs, addLocation, updateLocation, mapCenter } = useTourStore(); // 1. Luôn khai báo Hook ở cấp cao nhất, không đặt code logic/return giữa các Hook useEffect(() => { - // Chỉ cập nhật nếu giá trị thực sự thay đổi để tránh vòng lặp re-render - if (initialLegId && formData.legId !== initialLegId) { - setFormData(prev => ({ ...prev, legId: initialLegId })); + if (isOpen) { + if (editingLocation) { + setFormData({ + name: editingLocation.name || '', + address: editingLocation.address || '', + latitude: editingLocation.latitude, + longitude: editingLocation.longitude, + type: editingLocation.type || 'VISIT', + legId: editingLocation.legId || '', + plannedStart: editingLocation.plannedStart ? editingLocation.plannedStart.slice(0, 16) : '', + plannedEnd: editingLocation.plannedEnd ? editingLocation.plannedEnd.slice(0, 16) : '' + }); + } else { + setFormData(prev => ({ + ...prev, + name: '', address: '', + legId: initialLegId || (legs.length > 0 ? legs[0].id : ''), + plannedStart: '', plannedEnd: '' + })); + } } - }, [initialLegId, isOpen]); + }, [initialLegId, editingLocation, isOpen]); useEffect(() => { if (isOpen && !formData.name) { @@ -92,8 +109,8 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId }: { is // 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 = initialLegId ? `Thêm địa điểm cho ${targetLeg?.note || `Chặng ${targetLeg?.sequence}`}` : 'Thêm địa điểm mới'; - const buttonText = initialLegId ? 'Xác nhận thêm vào chặng' : 'Thêm địa điểm'; + 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 handlePickLocation = async (latlng: L.LatLng) => { setFormData(prev => ({ ...prev, latitude: latlng.lat, longitude: latlng.lng })); @@ -119,15 +136,24 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId }: { is e.preventDefault(); setIsLoading(true); try { - await addLocation(tourId, { - ...formData, - legId: currentLegId, - latitude: parseFloat(formData.latitude as any), - longitude: parseFloat(formData.longitude as any), - }); + if (editingLocation) { + await updateLocation(editingLocation.id, { + ...formData, + legId: currentLegId, + latitude: parseFloat(formData.latitude as any), + longitude: parseFloat(formData.longitude as any), + }); + } else { + await addLocation(tourId, { + ...formData, + legId: currentLegId, + latitude: parseFloat(formData.latitude as any), + longitude: parseFloat(formData.longitude as any), + }); + } onClose(); } catch (error) { - alert('Lỗi khi thêm địa điểm'); + alert('Lỗi khi lưu địa điểm'); } finally { setIsLoading(false); } diff --git a/ItineraryTimeline.tsx b/ItineraryTimeline.tsx index 547daa2..08f5f45 100644 --- a/ItineraryTimeline.tsx +++ b/ItineraryTimeline.tsx @@ -35,8 +35,11 @@ const formatTravelTime = (minutes: number) => { return mins > 0 ? `${hours} giờ ${mins} phút` : `${hours} giờ`; }; -export const ItineraryTimeline = ({ onAddLocation }: { onAddLocation?: (legId: string) => void }) => { - const { currentTour, legs, optimizeRouting, userRole, addLeg, updateLeg, deleteLeg, initializeLegs } = useTourStore(); +export const ItineraryTimeline = ({ + onAddLocation, + onEditLocation +}: { onAddLocation?: (legId: string) => void, onEditLocation?: (location: any) => void }) => { + const { currentTour, legs, optimizeRouting, userRole, addLeg, updateLeg, deleteLeg, initializeLegs, deleteLocation } = useTourStore(); const toggleComplete = async (locationId: string) => { // Gọi API PATCH /api/v1/locations/:id để cập nhật trạng thái @@ -75,6 +78,14 @@ export const ItineraryTimeline = ({ onAddLocation }: { onAddLocation?: (legId: s } }; + const handleDeleteLocation = async (id: string) => { + if (window.confirm("Bạn có chắc chắn muốn xóa địa điểm này?")) { + try { + await deleteLocation(id); + } catch (err: any) { alert(err.message); } + } + }; + return (
@@ -241,6 +252,16 @@ export const ItineraryTimeline = ({ onAddLocation }: { onAddLocation?: (legId: s Thực tế: {format(parseISO(location.actualStart), 'HH:mm')}
)} + {['OWNER', 'MANAGER'].includes(userRole || '') && !isStartPoint && !isEndPoint && ( +
+ + +
+ )}
diff --git a/TourDetailPage.tsx b/TourDetailPage.tsx index 26dc0aa..9937ed4 100644 --- a/TourDetailPage.tsx +++ b/TourDetailPage.tsx @@ -161,6 +161,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => { const [viewMode, setViewMode] = useState<'map' | 'timeline'>('timeline'); const [isAddLocationOpen, setIsAddLocationOpen] = useState(false); const [targetLegId, setTargetLegId] = useState(null); + const [editingLocation, setEditingLocation] = useState(null); // Khôi phục vị trí và mức zoom từ localStorage const [initialViewState] = useState(() => { @@ -499,6 +500,12 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => { {viewMode === 'timeline' ? ( { setTargetLegId(legId); + setEditingLocation(null); + setIsAddLocationOpen(true); + }} onEditLocation={(loc) => { + setEditingLocation(loc); + setTargetLegId(loc.legId); + setMapCenter([loc.latitude, loc.longitude]); setIsAddLocationOpen(true); }} /> ) : ( @@ -587,6 +594,7 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {