Cài đặt tính năng người quản lí có thể khai báo số lượng người tham gia
This commit is contained in:
Vendored
+3
@@ -332,6 +332,9 @@ let TourController = class TourController {
|
|||||||
title: body.title,
|
title: body.title,
|
||||||
startDate: body.startDate ? new Date(body.startDate) : undefined,
|
startDate: body.startDate ? new Date(body.startDate) : undefined,
|
||||||
endDate: body.endDate ? new Date(body.endDate) : undefined,
|
endDate: body.endDate ? new Date(body.endDate) : undefined,
|
||||||
|
adultCount: body.adultCount !== undefined ? Number(body.adultCount) : undefined,
|
||||||
|
childCount: body.childCount !== undefined ? Number(body.childCount) : undefined,
|
||||||
|
childDiscount: body.childDiscount !== undefined ? Number(body.childDiscount) : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -326,6 +326,9 @@ class TourController {
|
|||||||
title: body.title,
|
title: body.title,
|
||||||
startDate: body.startDate ? new Date(body.startDate) : undefined,
|
startDate: body.startDate ? new Date(body.startDate) : undefined,
|
||||||
endDate: body.endDate ? new Date(body.endDate) : undefined,
|
endDate: body.endDate ? new Date(body.endDate) : undefined,
|
||||||
|
adultCount: body.adultCount !== undefined ? Number(body.adultCount) : undefined,
|
||||||
|
childCount: body.childCount !== undefined ? Number(body.childCount) : undefined,
|
||||||
|
childDiscount: body.childDiscount !== undefined ? Number(body.childDiscount) : undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -162,6 +162,13 @@ const MapContextMenu = ({ onAction }: { onAction: (action: string, latlng: L.Lat
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
|
export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
|
||||||
|
// Tách biệt các state và actions để tối ưu performance - Chuyển lên đầu để tránh lỗi initialization
|
||||||
|
const currentTour = useTourStore(state => state.currentTour);
|
||||||
|
const legs = useTourStore(state => state.legs);
|
||||||
|
const publicTours = useTourStore(state => state.publicTours);
|
||||||
|
const userRole = useTourStore(state => state.userRole);
|
||||||
|
const mapCenter = useTourStore(state => state.mapCenter);
|
||||||
|
|
||||||
const [activeTab, setActiveTab] = useState<'plan' | 'expense' | 'photo' | 'settings'>('plan');
|
const [activeTab, setActiveTab] = useState<'plan' | 'expense' | 'photo' | 'settings'>('plan');
|
||||||
const [viewMode, setViewMode] = useState<'map' | 'timeline'>('timeline');
|
const [viewMode, setViewMode] = useState<'map' | 'timeline'>('timeline');
|
||||||
const [isAddLocationOpen, setIsAddLocationOpen] = useState(false);
|
const [isAddLocationOpen, setIsAddLocationOpen] = useState(false);
|
||||||
@@ -171,21 +178,19 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
|
|||||||
const [selectedMember, setSelectedMember] = useState<any>(null);
|
const [selectedMember, setSelectedMember] = useState<any>(null);
|
||||||
const [isMemberDetailOpen, setIsMemberDetailOpen] = useState(false);
|
const [isMemberDetailOpen, setIsMemberDetailOpen] = useState(false);
|
||||||
const [joinRequests, setJoinRequests] = useState<any[]>([]);
|
const [joinRequests, setJoinRequests] = useState<any[]>([]);
|
||||||
|
// State cho input số lượng người tham gia
|
||||||
|
const [adultCountInput, setAdultCountInput] = useState(currentTour?.adultCount ?? 0);
|
||||||
|
const [childCountInput, setChildCountInput] = useState(currentTour?.childCount ?? 0);
|
||||||
|
const [childDiscountInput, setChildDiscountInput] = useState(currentTour?.childDiscount ?? 0);
|
||||||
const [joinRequestActionId, setJoinRequestActionId] = useState<string | null>(null);
|
const [joinRequestActionId, setJoinRequestActionId] = useState<string | null>(null);
|
||||||
const [confirmState, setConfirmState] = useState<{ open: boolean; title?: string; message?: string; onConfirm?: () => void }>({ open: false });
|
const [confirmState, setConfirmState] = useState<{ open: boolean; title?: string; message?: string; onConfirm?: () => void }>({ open: false });
|
||||||
|
|
||||||
// Tách biệt các state và actions để tối ưu performance
|
|
||||||
const currentTour = useTourStore(state => state.currentTour);
|
|
||||||
const legs = useTourStore(state => state.legs);
|
|
||||||
const publicTours = useTourStore(state => state.publicTours);
|
|
||||||
const userRole = useTourStore(state => state.userRole);
|
|
||||||
const mapCenter = useTourStore(state => state.mapCenter);
|
|
||||||
|
|
||||||
const fetchTour = useTourStore(state => state.fetchTour);
|
const fetchTour = useTourStore(state => state.fetchTour);
|
||||||
const fetchPublicTours = useTourStore(state => state.fetchPublicTours);
|
const fetchPublicTours = useTourStore(state => state.fetchPublicTours);
|
||||||
const setMapCenter = useTourStore(state => state.setMapCenter);
|
const setMapCenter = useTourStore(state => state.setMapCenter);
|
||||||
const updateTourStartPoint = useTourStore(state => state.updateTourStartPoint);
|
const updateTourStartPoint = useTourStore(state => state.updateTourStartPoint);
|
||||||
const updateTourEndPoint = useTourStore(state => state.updateTourEndPoint);
|
const updateTourEndPoint = useTourStore(state => state.updateTourEndPoint);
|
||||||
|
const updateTourDetails = useTourStore(state => state.updateTourDetails); // Thêm action này
|
||||||
const initializeLegs = useTourStore(state => state.initializeLegs);
|
const initializeLegs = useTourStore(state => state.initializeLegs);
|
||||||
const addLocation = useTourStore(state => state.addLocation);
|
const addLocation = useTourStore(state => state.addLocation);
|
||||||
const removeMember = useTourStore(state => state.removeMember);
|
const removeMember = useTourStore(state => state.removeMember);
|
||||||
@@ -334,6 +339,21 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hàm xử lý cập nhật số lượng người tham gia
|
||||||
|
const handleUpdateParticipantCounts = async () => {
|
||||||
|
if (!currentTour) return;
|
||||||
|
try {
|
||||||
|
await updateTourDetails(currentTour.id, {
|
||||||
|
adultCount: adultCountInput,
|
||||||
|
childCount: childCountInput,
|
||||||
|
childDiscount: childDiscountInput,
|
||||||
|
});
|
||||||
|
notificationModal.openModal('Thành công', 'Đã cập nhật số lượng người tham gia.', 'success');
|
||||||
|
fetchTour(currentTour.id); // Re-fetch tour để cập nhật lại các tính toán liên quan
|
||||||
|
} catch (error: any) {
|
||||||
|
notificationModal.openModal('Lỗi', error.message || 'Không thể cập nhật số lượng người tham gia.', 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
// Xác định Điểm xuất phát và Điểm kết thúc hiển thị dưới widget tài chính
|
// Xác định Điểm xuất phát và Điểm kết thúc hiển thị dưới widget tài chính
|
||||||
const startPoint = legs[0]?.locations[0];
|
const startPoint = legs[0]?.locations[0];
|
||||||
const lastLeg = legs[legs.length - 1];
|
const lastLeg = legs[legs.length - 1];
|
||||||
@@ -798,6 +818,58 @@ export const TourDetailPage = ({ onBack }: { onBack: () => void }) => {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{canEdit && ( // Chỉ OWNER và MANAGER mới có thể chỉnh sửa phần này
|
||||||
|
<div className="p-6 bg-white rounded-3xl border border-gray-100 shadow-sm animate-in zoom-in-95">
|
||||||
|
<div className="flex items-center gap-3 mb-4">
|
||||||
|
<Users className="w-6 h-6 text-purple-500" />
|
||||||
|
<h3 className="text-lg font-bold text-gray-900">Quản lý số lượng người tham gia</h3>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<label htmlFor="adultCount" className="block text-sm font-medium text-gray-700 mb-1">Số lượng người lớn</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="adultCount"
|
||||||
|
value={adultCountInput}
|
||||||
|
onChange={(e) => setAdultCountInput(Number(e.target.value))}
|
||||||
|
min="0"
|
||||||
|
className="w-full px-4 py-2 border border-gray-200 rounded-xl focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="childCount" className="block text-sm font-medium text-gray-700 mb-1">Số lượng trẻ em</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="childCount"
|
||||||
|
value={childCountInput}
|
||||||
|
onChange={(e) => setChildCountInput(Number(e.target.value))}
|
||||||
|
min="0"
|
||||||
|
className="w-full px-4 py-2 border border-gray-200 rounded-xl focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="childDiscount" className="block text-sm font-medium text-gray-700 mb-1">Giảm giá trẻ em (%)</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
id="childDiscount"
|
||||||
|
value={childDiscountInput}
|
||||||
|
onChange={(e) => setChildDiscountInput(Number(e.target.value))}
|
||||||
|
min="0"
|
||||||
|
max="100"
|
||||||
|
className="w-full px-4 py-2 border border-gray-200 rounded-xl focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={handleUpdateParticipantCounts}
|
||||||
|
className="w-full px-4 py-3 bg-blue-600 hover:bg-blue-700 text-white font-bold rounded-xl transition-all shadow-md"
|
||||||
|
>
|
||||||
|
Lưu thay đổi
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="p-8 text-center bg-gray-50 rounded-3xl border border-dashed border-gray-200">
|
<div className="p-8 text-center bg-gray-50 rounded-3xl border border-dashed border-gray-200">
|
||||||
<Settings className="w-10 h-10 text-gray-300 mx-auto mb-3" />
|
<Settings className="w-10 h-10 text-gray-300 mx-auto mb-3" />
|
||||||
<p className="text-gray-500 font-medium">Tính năng cài đặt khác đang được cập nhật...</p>
|
<p className="text-gray-500 font-medium">Tính năng cài đặt khác đang được cập nhật...</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user