fix: sửa lỗi hiển thị logo trên trang pdf
This commit is contained in:
@@ -701,6 +701,22 @@ export const TourDetailPage = ({
|
||||
const [commentLocationName, setCommentLocationName] = useState('');
|
||||
const [joinRequestActionId, setJoinRequestActionId] = useState<string | null>(null);
|
||||
|
||||
// State cho Modal ghi chú nhanh
|
||||
const [quickNoteLocName, setQuickNoteLocName] = useState<string | null>(null);
|
||||
const [quickNoteInput, setQuickNoteInput] = useState('');
|
||||
|
||||
// Đồng bộ hóa các ô nhập dữ liệu cài đặt với currentTour khi tour tải/cập nhật
|
||||
useEffect(() => {
|
||||
if (currentTour) {
|
||||
setTitleInput(currentTour.title ?? '');
|
||||
setDescriptionInput(currentTour.description ?? '');
|
||||
setAdultCountInput(currentTour.adultCount ?? 0);
|
||||
setChildCountInput(currentTour.childCount ?? 0);
|
||||
setChildDiscountInput(currentTour.childDiscount ?? 0);
|
||||
setTagsInput(currentTour.tags ?? []);
|
||||
}
|
||||
}, [currentTour]);
|
||||
|
||||
// State tìm kiếm cho chế độ Bản đồ trong Tab Lộ trình
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<any[]>([]);
|
||||
@@ -1201,6 +1217,35 @@ export const TourDetailPage = ({
|
||||
}
|
||||
};
|
||||
|
||||
// Đồng bộ tiêu đề ghi chú khi đổi tên chuyến đi
|
||||
const syncTourNoteTitle = (tourId: string, newTitle: string) => {
|
||||
const savedNotes = localStorage.getItem('my_journey_notes');
|
||||
if (!savedNotes) return;
|
||||
try {
|
||||
let notes = JSON.parse(savedNotes);
|
||||
if (Array.isArray(notes)) {
|
||||
let updated = false;
|
||||
const oldTitle = currentTour?.title || '';
|
||||
notes = notes.map((n: any) => {
|
||||
if (n.tourId === tourId || n.title === `Ghi chú của hành trình: ${oldTitle}`) {
|
||||
n.tourId = tourId;
|
||||
n.title = `Ghi chú của hành trình: ${newTitle}`;
|
||||
if (oldTitle && n.content) {
|
||||
n.content = n.content.split(`<strong>${oldTitle}</strong>`).join(`<strong>${newTitle}</strong>`);
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
return n;
|
||||
});
|
||||
if (updated) {
|
||||
localStorage.setItem('my_journey_notes', JSON.stringify(notes));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Error syncing tour note title:", e);
|
||||
}
|
||||
};
|
||||
|
||||
// Hàm xử lý cập nhật số lượng người tham gia
|
||||
const handleUpdateTourInfo = async () => {
|
||||
if (!currentTour) return;
|
||||
@@ -1218,31 +1263,40 @@ export const TourDetailPage = ({
|
||||
message: 'Đã cập nhật thông tin chuyến đi.',
|
||||
type: 'success'
|
||||
});
|
||||
if (titleInput !== currentTour.title) {
|
||||
syncTourNoteTitle(currentTour.id, titleInput);
|
||||
}
|
||||
fetchTour(currentTour.id); // Re-fetch tour để cập nhật lại các tính toán liên quan
|
||||
} catch (error: any) {
|
||||
notify({ title: 'Lỗi', message: error.message || 'Không thể cập nhật thông tin.', type: 'error' });
|
||||
}
|
||||
};
|
||||
// Hàm ghi chú nhanh cho từng địa điểm, tự động append vào note chung của Tour
|
||||
|
||||
// Hàm ghi chú nhanh cho từng địa điểm, tự động append vào note chung của Tour
|
||||
const handleQuickNote = (locationName: string) => {
|
||||
if (isPublicView) return;
|
||||
setQuickNoteLocName(locationName);
|
||||
setQuickNoteInput('');
|
||||
};
|
||||
|
||||
const content = window.prompt(`Ghi chú nhanh cho địa điểm: ${locationName}`);
|
||||
if (!content || !content.trim()) return;
|
||||
// Hàm xử lý submit ghi chú nhanh từ modal
|
||||
const submitQuickNote = () => {
|
||||
if (!quickNoteLocName || !quickNoteInput.trim() || !currentTour) return;
|
||||
const content = quickNoteInput;
|
||||
|
||||
const storedUser = localStorage.getItem('user');
|
||||
const user = storedUser ? JSON.parse(storedUser) : { name: 'Thành viên' };
|
||||
const userName = user.name || 'Thành viên';
|
||||
const now = new Date().toLocaleString('vi-VN');
|
||||
|
||||
const noteTitle = `Ghi chú của hành trình: ${currentTour?.title}`;
|
||||
const noteTitle = `Ghi chú của hành trình: ${currentTour.title}`;
|
||||
const savedNotes = localStorage.getItem('my_journey_notes');
|
||||
let notes = [];
|
||||
try {
|
||||
notes = savedNotes ? JSON.parse(savedNotes) : [];
|
||||
} catch (e) { notes = []; }
|
||||
|
||||
let targetNote = notes.find((n: any) => n.title === noteTitle);
|
||||
let targetNote = notes.find((n: any) => n.tourId === currentTour.id || n.title === noteTitle);
|
||||
|
||||
// Tạo khối nội dung dạng "Textbox" chuyên nghiệp
|
||||
const newContentLine = `
|
||||
@@ -1250,18 +1304,21 @@ export const TourDetailPage = ({
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
|
||||
<span style="font-size: 10px; color: #b45309; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em;">🕒 ${now} • 👤 ${userName}</span>
|
||||
</div>
|
||||
<p style="margin: 0; color: #4b5563; font-size: 14px; line-height: 1.5;"><strong>📍 ${locationName}:</strong> ${content}</p>
|
||||
<p style="margin: 0; color: #4b5563; font-size: 14px; line-height: 1.5;"><strong>📍 ${quickNoteLocName}:</strong> ${content}</p>
|
||||
</div>
|
||||
<p></p>
|
||||
`;
|
||||
|
||||
if (targetNote) {
|
||||
targetNote.tourId = currentTour.id;
|
||||
targetNote.title = noteTitle;
|
||||
targetNote.content += newContentLine;
|
||||
} else {
|
||||
const newNote = {
|
||||
id: Date.now().toString(),
|
||||
tourId: currentTour.id,
|
||||
title: noteTitle,
|
||||
content: `<p>Bắt đầu lập kế hoạch cho chuyến đi <strong>${currentTour?.title}</strong> của bạn tại đây...</p>` + newContentLine,
|
||||
content: `<p>Bắt đầu lập kế hoạch cho chuyến đi <strong>${currentTour.title}</strong> của bạn tại đây...</p>` + newContentLine,
|
||||
createdAt: new Date().toISOString()
|
||||
};
|
||||
notes.unshift(newNote);
|
||||
@@ -1269,6 +1326,8 @@ export const TourDetailPage = ({
|
||||
|
||||
localStorage.setItem('my_journey_notes', JSON.stringify(notes));
|
||||
notify({ title: 'Thành công', message: 'Đã lưu vào ghi chú chung!', type: 'success' });
|
||||
setQuickNoteLocName(null);
|
||||
setQuickNoteInput('');
|
||||
};
|
||||
|
||||
// Hàm xử lý xóa Tour vĩnh viễn
|
||||
@@ -1352,7 +1411,7 @@ export const TourDetailPage = ({
|
||||
const tourInfo = {
|
||||
title: currentTour?.title || "Hành trình khám phá TP.HCM",
|
||||
date: tourDateDisplay,
|
||||
membersCount: currentTour?.participants?.length || 0,
|
||||
membersCount: currentTour?.participants?.filter((p: any) => p.user)?.length || 0,
|
||||
budget: currentTour?.totalCost ? `${Number(currentTour.totalCost).toLocaleString()} VND` : "0 VND",
|
||||
coverImage: getMostLikedPhoto(currentTour?.photos || [])?.imageUrl || "https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?q=80&w=2070&auto=format&fit=crop"
|
||||
};
|
||||
@@ -1448,7 +1507,7 @@ export const TourDetailPage = ({
|
||||
{/* Member Avatars Stack */}
|
||||
<div className="flex items-center gap-2 mt-4">
|
||||
<div className="flex flex-wrap gap-2"> {/* Always show participants */}
|
||||
{currentTour?.participants?.slice(0, 5).map((p: any, i: number) => (
|
||||
{currentTour?.participants?.filter((p: any) => p.user)?.slice(0, 5).map((p: any, i: number) => (
|
||||
<button
|
||||
key={p.userId || i}
|
||||
onClick={() => {
|
||||
@@ -2682,6 +2741,54 @@ export const TourDetailPage = ({
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* Quick Note Modal */}
|
||||
{quickNoteLocName && (
|
||||
<div className="fixed inset-0 z-[4000] flex items-center justify-center p-4">
|
||||
<div className="absolute inset-0 bg-gray-900/60 backdrop-blur-sm" onClick={() => setQuickNoteLocName(null)} />
|
||||
<div className="relative w-full max-w-md bg-white rounded-[32px] shadow-2xl p-8 animate-in zoom-in-95 duration-200">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
<h3 className="text-xl font-black text-gray-900">Ghi chú nhanh</h3>
|
||||
<button onClick={() => setQuickNoteLocName(null)} className="p-2 hover:bg-gray-100 rounded-full transition-colors">
|
||||
<X className="w-5 h-5 text-gray-400" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm font-bold text-gray-700">
|
||||
📍 Địa điểm: <span className="text-blue-600">{quickNoteLocName}</span>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-black text-gray-400 uppercase tracking-widest mb-2 ml-1">Nội dung ghi chú</label>
|
||||
<textarea
|
||||
value={quickNoteInput}
|
||||
onChange={(e) => setQuickNoteInput(e.target.value)}
|
||||
placeholder="Nhập nội dung ghi chú nhanh..."
|
||||
className="w-full px-5 py-4 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 outline-none transition-all text-sm min-h-[120px] resize-none"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-3 mt-8">
|
||||
<button
|
||||
onClick={() => setQuickNoteLocName(null)}
|
||||
className="py-4 bg-gray-100 hover:bg-gray-200 text-gray-600 font-bold rounded-2xl transition-all active:scale-95"
|
||||
>
|
||||
Hủy
|
||||
</button>
|
||||
<button
|
||||
onClick={submitQuickNote}
|
||||
disabled={!quickNoteInput.trim()}
|
||||
className="py-4 bg-blue-600 hover:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none text-white font-bold rounded-2xl shadow-lg shadow-blue-100 transition-all active:scale-95"
|
||||
>
|
||||
Lưu
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Member Detail Popover */}
|
||||
{isMemberDetailOpen && selectedMember && (
|
||||
<div className="fixed inset-0 z-[2100] flex items-center justify-center p-4">
|
||||
|
||||
Reference in New Issue
Block a user