fix: sửa lỗi hiển thị logo trên trang pdf
This commit is contained in:
@@ -132,22 +132,36 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
[formData.latitude, formData.longitude]
|
||||
);
|
||||
|
||||
// Tự động cập nhật vị trí bản đồ theo chặng được chọn (Lấy điểm cuối của chặng đó làm tham chiếu)
|
||||
// Tự động cập nhật vị trí bản đồ theo chặng được chọn (Lấy điểm cuối của chặng trước/hiện tại làm tham chiếu tiếp nối)
|
||||
useEffect(() => {
|
||||
if (isOpen && !editingLocation && formData.legId && !formData.name) {
|
||||
const selectedLeg = legs.find(l => l.id === formData.legId);
|
||||
|
||||
if (selectedLeg && selectedLeg.locations && selectedLeg.locations.length > 0) {
|
||||
// Di chuyển đến địa điểm cuối cùng của chặng để người dùng thấy điểm nối tiếp
|
||||
const lastLoc = selectedLeg.locations[selectedLeg.locations.length - 1];
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
latitude: lastLoc.latitude,
|
||||
longitude: lastLoc.longitude
|
||||
}));
|
||||
} else {
|
||||
// Nếu chặng chưa có điểm nào, mặc định dùng vị trí trung tâm hiện tại của tour
|
||||
setFormData(prev => ({ ...prev, latitude: mapCenter[0], longitude: mapCenter[1] }));
|
||||
if (selectedLeg) {
|
||||
if (selectedLeg.locations && selectedLeg.locations.length > 0) {
|
||||
// Di chuyển đến địa điểm cuối cùng của chặng hiện tại để người dùng thấy điểm nối tiếp
|
||||
const lastLoc = selectedLeg.locations[selectedLeg.locations.length - 1];
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
latitude: lastLoc.latitude,
|
||||
longitude: lastLoc.longitude
|
||||
}));
|
||||
} else {
|
||||
// Chặng trống -> Lấy địa điểm cuối của chặng trước làm tọa độ tiếp nối
|
||||
const currentLegIdx = legs.findIndex(l => l.id === formData.legId);
|
||||
const prevLeg = currentLegIdx > 0 ? legs[currentLegIdx - 1] : null;
|
||||
if (prevLeg && prevLeg.locations && prevLeg.locations.length > 0) {
|
||||
const lastLoc = prevLeg.locations[prevLeg.locations.length - 1];
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
latitude: lastLoc.latitude,
|
||||
longitude: lastLoc.longitude
|
||||
}));
|
||||
} else {
|
||||
// Nếu không có chặng trước hoặc chặng trước trống, mặc định dùng vị trí trung tâm hiện tại của tour
|
||||
setFormData(prev => ({ ...prev, latitude: mapCenter[0], longitude: mapCenter[1] }));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [formData.legId, isOpen, editingLocation, legs, mapCenter]);
|
||||
@@ -484,7 +498,7 @@ export const AddLocationModal = ({ isOpen, onClose, tourId, initialLegId, editin
|
||||
<select className="w-full px-3 py-2 bg-white border border-gray-200 rounded-xl outline-none text-sm"
|
||||
value={formData.paidById} onChange={e => setFormData({...formData, paidById: e.target.value})}>
|
||||
<option value="">-- Chọn người thanh toán --</option>
|
||||
{currentTour?.participants?.map((p: any) => {
|
||||
{currentTour?.participants?.filter((p: any) => p.user)?.map((p: any) => {
|
||||
const name = p.user?.name;
|
||||
const email = p.user?.email;
|
||||
const label = [name, email && name ? `(${email})` : email].filter(Boolean).join(' ');
|
||||
|
||||
@@ -15,7 +15,7 @@ interface AddMemberModalProps {
|
||||
isPublicView?: boolean; // New prop to indicate public view
|
||||
}
|
||||
|
||||
export const AddMemberModal: React.FC<AddMemberModalProps> = ({ isOpen, onClose, tourId, participants = [], joinRequests = [], onRemoveMember, onMemberAdded, userRole }) => {
|
||||
export const AddMemberModal: React.FC<AddMemberModalProps> = ({ isOpen, onClose, tourId, participants = [], joinRequests = [], onRemoveMember, onMemberAdded, userRole, isPublicView }) => {
|
||||
const [query, setQuery] = useState('');
|
||||
const [users, setUsers] = useState<any[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -54,8 +54,12 @@ export const AddMemberModal: React.FC<AddMemberModalProps> = ({ isOpen, onClose,
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) return;
|
||||
fetchUsers();
|
||||
}, [isOpen]);
|
||||
const delayDebounceFn = setTimeout(() => {
|
||||
fetchUsers();
|
||||
}, 300);
|
||||
|
||||
return () => clearTimeout(delayDebounceFn);
|
||||
}, [query, isOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isOpen) {
|
||||
@@ -158,9 +162,9 @@ export const AddMemberModal: React.FC<AddMemberModalProps> = ({ isOpen, onClose,
|
||||
|
||||
<div className="p-5 space-y-4">
|
||||
<div>
|
||||
<p className="text-[11px] font-bold text-gray-500 mb-2">Thành viên của tour ({participants.length})</p>
|
||||
<p className="text-[11px] font-bold text-gray-500 mb-2">Thành viên của tour ({participants.filter(p => p.user).length})</p>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
{participants.map((p) => {
|
||||
{participants.filter(p => p.user).map((p) => {
|
||||
const rawToken = localStorage.getItem('token');
|
||||
let currentUserId: string | null = null;
|
||||
try {
|
||||
@@ -255,6 +259,26 @@ export const AddMemberModal: React.FC<AddMemberModalProps> = ({ isOpen, onClose,
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="relative mb-2">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Tìm kiếm theo tên hoặc địa chỉ email..."
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
className="w-full pl-10 pr-10 py-3 bg-gray-50 border border-gray-100 rounded-2xl focus:ring-2 focus:ring-blue-500 outline-none transition-all text-sm font-bold text-gray-800"
|
||||
/>
|
||||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-blue-500" />
|
||||
{query && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setQuery('')}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 p-1.5 hover:bg-gray-200 rounded-full text-gray-400 transition-colors"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{fetchError && (
|
||||
<div className="p-3 bg-red-50 text-red-600 rounded-xl text-xs font-bold border border-red-100">
|
||||
{fetchError}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -250,7 +250,7 @@ export const ItineraryTimeline = ({
|
||||
{canEdit && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => onAddLocation?.(leg.id)}
|
||||
onClick={() => onAddLocation?.(leg.id, legIdx === 0 && leg.locations.length === 0)}
|
||||
className="p-2 text-gray-400 hover:text-blue-600 hover:bg-blue-50 rounded-xl transition-all"
|
||||
title="Thêm địa điểm vào chặng này"
|
||||
>
|
||||
|
||||
@@ -402,7 +402,7 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
|
||||
<a
|
||||
href={`${window.location.origin}/api/v1/public-photos/${photo.id}/share`}
|
||||
className="w-full h-auto max-h-[85vh] object-contain cursor-zoom-in md:h-full md:max-h-none flex items-center justify-center"
|
||||
className="w-full h-auto max-h-[85vh] object-contain cursor-zoom-in md:h-full md:max-h-none flex items-center justify-center relative"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsFullscreen(true);
|
||||
@@ -411,8 +411,13 @@ export const PublicPhotoModal: React.FC<PublicPhotoModalProps> = ({
|
||||
<img
|
||||
src={photo.imageUrl}
|
||||
alt="Public Map Upload"
|
||||
className="w-full h-auto max-h-[85vh] object-contain md:h-full md:max-h-none"
|
||||
className="w-full h-auto max-h-[85vh] object-contain md:h-full md:max-h-none select-none"
|
||||
draggable={false}
|
||||
/>
|
||||
{/* Shield overlay to prevent Save Image As on right-click / long press for non-owners */}
|
||||
{!isAuthorized && (
|
||||
<div className="absolute inset-0 bg-transparent select-none z-10" />
|
||||
)}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user