From 1eed9b00de244860f39ae0cf1aeafcca30db89fe Mon Sep 17 00:00:00 2001 From: 3dtours Date: Tue, 16 Jun 2026 20:38:40 +0700 Subject: [PATCH] =?UTF-8?q?feat:=20xem=20=E1=BA=A3nh=20theo=20c=E1=BA=A5u?= =?UTF-8?q?=20tr=C3=BAc=20c=E1=BB=A7a=20lightroom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/pages/TourDetailPage.tsx | 169 +++++++++++++++++++++----- 1 file changed, 138 insertions(+), 31 deletions(-) diff --git a/frontend/src/pages/TourDetailPage.tsx b/frontend/src/pages/TourDetailPage.tsx index 492366f..947b8e0 100644 --- a/frontend/src/pages/TourDetailPage.tsx +++ b/frontend/src/pages/TourDetailPage.tsx @@ -188,7 +188,7 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac const [editingLocation, setEditingLocation] = useState(null); const [selectedMember, setSelectedMember] = useState(null); const [isMemberDetailOpen, setIsMemberDetailOpen] = useState(false); - const [selectedPhoto, setSelectedPhoto] = useState(null); + const [selectedPhotoForDisplay, setSelectedPhotoForDisplay] = useState(null); // New state for large photo display const currentUserId = useMemo(() => { const token = localStorage.getItem('token'); if (!token) return null; @@ -208,7 +208,34 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac const [tagsInput, setTagsInput] = useState(currentTour?.tags ?? []); const [customTag, setCustomTag] = useState(''); const availableTags = ['Văn hóa', 'Mạo hiểm', 'Nghỉ dưỡng', 'Thư giãn', 'Ẩm thực', 'Khám phá', 'Gia đình']; + const [selectedLegIdForPhoto, setSelectedLegIdForPhoto] = useState('all'); // Keep this state + // Memoized filtered photos based on selectedLegIdForPhoto + const filteredPhotos = useMemo(() => { + if (!currentTour?.photos) return []; + let photosToFilter = currentTour.photos; + + if (selectedLegIdForPhoto !== 'all') { + const targetLeg = legs.find(l => l.id === selectedLegIdForPhoto); + if (targetLeg) { + const locationIdsInLeg = targetLeg.locations.map((loc: any) => loc.id); + // Filter photos that have a locationId and that locationId is in the current leg + photosToFilter = photosToFilter.filter((p: any) => p.locationId && locationIdsInLeg.includes(p.locationId)); + } else { + photosToFilter = []; // If leg not found, no photos + } + } + return photosToFilter; + }, [currentTour?.photos, selectedLegIdForPhoto, legs]); + + // Effect to set initial selected photo for display or reset if current one is no longer in filtered list + useEffect(() => { + if (filteredPhotos.length > 0 && !selectedPhotoForDisplay) { + setSelectedPhotoForDisplay(filteredPhotos[0]); + } else if (selectedPhotoForDisplay && !filteredPhotos.some(p => p.id === selectedPhotoForDisplay.id)) { + setSelectedPhotoForDisplay(filteredPhotos.length > 0 ? filteredPhotos[0] : null); + } + }, [filteredPhotos, selectedPhotoForDisplay]); const [isCommentModalOpen, setIsCommentModalOpen] = useState(false); const [commentLocationId, setCommentLocationId] = useState(''); const [commentLocationName, setCommentLocationName] = useState(''); @@ -256,8 +283,8 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac if (!response.ok) throw new Error('Không thể xóa ảnh'); notify({ title: 'Thành công', message: 'Đã xóa ảnh.', type: 'success' }); - fetchTour(tourId); - setSelectedPhoto(null); + fetchTour(tourId); // Re-fetch tour to update photo list + setSelectedPhotoForDisplay(null); // Reset selected photo after deletion } catch (error) { notify({ title: 'Lỗi', message: 'Không thể xóa ảnh. Vui lòng thử lại.', type: 'error' }); } @@ -341,6 +368,28 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac // Fetch tour details when tourId changes or public view status changes if (tourId) { isPublicView ? fetchPublicTourDetails(tourId) : fetchTour(tourId); } }, [tourId, isPublicView, userRole]); // Add tourId to dependencies + + // New useEffect to manage initial photo display when currentTour or legs change + useEffect(() => { + if (currentTour && currentTour.photos && currentTour.photos.length > 0) { + // If there are photos, and no leg is selected, default to 'all' and first photo + if (selectedLegIdForPhoto === 'all' && !selectedPhotoForDisplay) { + setSelectedPhotoForDisplay(currentTour.photos[0]); + } else if (selectedLegIdForPhoto !== 'all') { + // If a specific leg is selected, try to find a photo for that leg + const targetLeg = legs.find(l => l.id === selectedLegIdForPhoto); + if (targetLeg) { + const locationIdsInLeg = targetLeg.locations.map((loc: any) => loc.id); + const photosInLeg = currentTour.photos.filter((p: any) => p.locationId && locationIdsInLeg.includes(p.locationId)); + if (photosInLeg.length > 0 && !selectedPhotoForDisplay) { + setSelectedPhotoForDisplay(photosInLeg[0]); + } else if (selectedPhotoForDisplay && !photosInLeg.some(p => p.id === selectedPhotoForDisplay.id)) { + setSelectedPhotoForDisplay(photosInLeg.length > 0 ? photosInLeg[0] : null); + } + } + } + } + }, [currentTour, legs, selectedLegIdForPhoto, selectedPhotoForDisplay]); const [mapZoom] = useState(initialViewState?.zoom || 13); // Memoize danh sách tọa độ để MapTourBounds không bị trigger thừa @@ -985,45 +1034,103 @@ export const TourDetailPage = ({ onBack, tourId, isPublicView = false }: { onBac )} {activeTab === 'photo' && ( -
- {currentTour?.photos && currentTour.photos.length > 0 ? ( - currentTour.photos.map((photo: any) => { - const isUploader = currentUserId === photo.uploaderId; - return ( -
setSelectedPhoto(photo)} - className="aspect-square bg-gray-200 rounded-2xl overflow-hidden relative group border border-white shadow-sm cursor-pointer" +
+ {/* Main container for the new layout */} +
+ {/* Left Column: Leg List */} +
+

Chặng của Tour

+
+ + ))} +
+
+ + {/* Right Column: Large Photo Display */} +
+ {selectedPhotoForDisplay ? ( +
+ Selected Tour Photo - {isUploader && !isPublicView && ( + {/* Optional: Add delete button for the large photo */} + {currentUserId === selectedPhotoForDisplay.uploaderId && !isPublicView && ( )}
- ); - }) - ) : ( -
-
- -
-

Khoảnh khắc trống

-

Hãy là người đầu tiên chia sẻ những hình ảnh tuyệt vời của chuyến đi này!

+ ) : ( +
+ +

Chọn một ảnh để xem chi tiết

+
+ )}
- )} +
+ + {/* Bottom Row: Thumbnails */} +
+

+ {selectedLegIdForPhoto === 'all' ? 'Tất cả ảnh' : `Ảnh của Chặng ${legs.find(l => l.id === selectedLegIdForPhoto)?.sequence || ''}`} +

+ {filteredPhotos.length > 0 ? ( +
+ {filteredPhotos.map((photo: any) => ( +
setSelectedPhotoForDisplay(photo)} + className={`aspect-square bg-gray-200 rounded-xl overflow-hidden relative group border-2 ${ + selectedPhotoForDisplay?.id === photo.id ? 'border-blue-500' : 'border-transparent' + } hover:border-blue-300 transition-all cursor-pointer`} + > + Thumbnail +
+ ))} +
+ ) : ( +
+ +

Chưa có ảnh nào cho chặng này.

+
+ )} +
)}