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 }) => {