diff --git a/client/src/pages/Booking.js b/client/src/pages/Booking.js
index be896ba..ec05fe9 100644
--- a/client/src/pages/Booking.js
+++ b/client/src/pages/Booking.js
@@ -15,7 +15,7 @@ import api from '../utils/api';
const Booking = () => {
const navigate = useNavigate();
- const [rooms, setRooms] = useState([]);
+ const [rooms, setRooms] = useState([]);
const [loadingRooms, setLoadingRooms] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [message, setMessage] = useState(null);
@@ -33,26 +33,128 @@ const Booking = () => {
specialRequests: ''
});
+ const extractArray = (res, keys = []) => {
+ const d = res?.data;
+ const dd = d?.data;
+
+ // direct arrays
+ if (Array.isArray(d)) return d;
+ if (Array.isArray(dd)) return dd;
+
+ // common shapes
+ for (const k of keys) {
+ if (Array.isArray(dd?.[k])) return dd[k];
+ if (Array.isArray(d?.[k])) return d[k];
+ }
+
+ // fallback scan first array found
+ if (dd && typeof dd === 'object') {
+ for (const v of Object.values(dd)) {
+ if (Array.isArray(v)) return v;
+ }
+ }
+ if (d && typeof d === 'object') {
+ for (const v of Object.values(d)) {
+ if (Array.isArray(v)) return v;
+ }
+ }
+
+ return [];
+ };
+
+ const normalizeRoomOptions = (items = []) => {
+ return items
+ .filter(Boolean)
+ .map((r) => {
+ const id = r?._id || r?.id || '';
+ const name =
+ r?.name ||
+ r?.title ||
+ r?.roomName ||
+ r?.roomCategory?.name ||
+ 'Room';
+
+ const roomNumber = r?.roomNumber || r?.number || '';
+ const price =
+ r?.basePrice ??
+ r?.price ??
+ r?.pricing?.basePrice ??
+ r?.roomCategory?.basePrice;
+
+ const statusRaw = String(r?.status || '').toLowerCase();
+ const isInactive =
+ statusRaw === 'inactive' ||
+ statusRaw === 'disabled' ||
+ statusRaw === 'draft';
+
+ return {
+ _id: id,
+ name,
+ roomNumber,
+ basePrice: price,
+ status: r?.status,
+ kind: 'room',
+ label:
+ roomNumber && price != null
+ ? `${name} (#${roomNumber}) - $${price}/night`
+ : roomNumber
+ ? `${name} (#${roomNumber})`
+ : price != null
+ ? `${name} - $${price}/night`
+ : name,
+ _isInactive: isInactive
+ };
+ })
+ .filter((x) => x._id)
+ .filter((x) => !x._isInactive);
+ };
+
+ const normalizeCategoryOptions = (items = []) => {
+ return items
+ .filter(Boolean)
+ .map((c) => {
+ const id = c?._id || c?.id || '';
+ const name = c?.name || c?.title || c?.categoryName || 'Room Type';
+ const price = c?.basePrice ?? c?.price ?? c?.startingPrice;
+ return {
+ _id: id,
+ name,
+ basePrice: price,
+ kind: 'category',
+ label: price != null ? `${name} - from $${price}/night` : name
+ };
+ })
+ .filter((x) => x._id);
+ };
+
useEffect(() => {
let mounted = true;
const loadRooms = async () => {
try {
- const res = await api.get('/api/rooms?limit=100');
+ // 1) Try actual rooms first
+ const roomsRes = await api.get('/api/rooms?limit=100');
+ const rawRooms = extractArray(roomsRes, ['rooms', 'items', 'data']);
+ const normalizedRooms = normalizeRoomOptions(rawRooms);
- const rawRooms =
- res?.data?.data?.rooms ||
- res?.data?.data ||
- res?.data?.rooms ||
- [];
+ if (normalizedRooms.length > 0) {
+ if (mounted) setRooms(normalizedRooms);
+ return;
+ }
- const fetched = (Array.isArray(rawRooms) ? rawRooms : []).filter((r) => {
- const status = String(r?.status || '').toLowerCase();
- return !status || status === 'active';
- });
+ // 2) Fallback to room categories (the types shown on public rooms page)
+ const categoriesRes = await api.get('/api/room-categories');
+ const rawCategories = extractArray(categoriesRes, ['categories', 'roomCategories', 'items', 'data']);
+ const normalizedCategories = normalizeCategoryOptions(rawCategories);
- if (mounted) setRooms(fetched);
+ if (mounted) {
+ setRooms(normalizedCategories);
+ if (normalizedCategories.length === 0) {
+ setMessage({ type: 'warning', text: 'No rooms available' });
+ }
+ }
} catch (e) {
+ console.error('Failed to load rooms/categories:', e);
if (mounted) setMessage({ type: 'error', text: 'Failed to load rooms' });
} finally {
if (mounted) setLoadingRooms(false);
@@ -65,11 +167,10 @@ const Booking = () => {
}, []);
const handleChange = (key) => (e) => {
- setForm(prev => ({ ...prev, [key]: e.target.value }));
+ setForm((prev) => ({ ...prev, [key]: e.target.value }));
};
const validateBeforeSubmit = () => {
- // check dates
if (form.checkInDate && form.checkOutDate) {
const inD = new Date(form.checkInDate);
const outD = new Date(form.checkOutDate);
@@ -81,10 +182,12 @@ const Booking = () => {
const adults = Number(form.adults);
const children = Number(form.children || 0);
+
if (!Number.isFinite(adults) || adults < 1) {
setMessage({ type: 'error', text: 'Adults must be at least 1' });
return false;
}
+
if (!Number.isFinite(children) || children < 0) {
setMessage({ type: 'error', text: 'Children cannot be negative' });
return false;
@@ -102,6 +205,9 @@ const Booking = () => {
setSubmitting(true);
try {
+ const selectedOption = rooms.find((r) => r._id === form.roomId);
+
+ // Keep original payload structure, but add category fallback fields safely
const payload = {
guestInfo: {
firstName: form.firstName.trim(),
@@ -109,7 +215,10 @@ const Booking = () => {
email: form.email.trim(),
phone: form.phone.trim()
},
- roomId: form.roomId,
+ roomId: selectedOption?.kind === 'room' ? form.roomId : undefined,
+ roomCategoryId: selectedOption?.kind === 'category' ? form.roomId : undefined,
+ roomCategory: selectedOption?.name || undefined,
+ requestedRoomType: selectedOption?.name || undefined,
checkInDate: form.checkInDate,
checkOutDate: form.checkOutDate,
numberOfGuests: {
@@ -119,13 +228,14 @@ const Booking = () => {
specialRequests: form.specialRequests
};
+ // remove undefined keys
+ Object.keys(payload).forEach((k) => payload[k] === undefined && delete payload[k]);
+
const res = await api.post('/api/bookings/request', payload);
const booking = res?.data?.data?.booking || null;
const bookingNumber = booking?.bookingNumber || res?.data?.data?.bookingNumber || null;
- const selectedRoom = rooms.find(r => String(r._id || r.id) === String(form.roomId));
-
const requestForConfirmation = {
fullName: `${form.firstName} ${form.lastName}`.trim(),
phone: form.phone,
@@ -134,15 +244,15 @@ const Booking = () => {
checkOutDate: form.checkOutDate,
adults: Number(form.adults),
children: Number(form.children || 0),
- roomCategory: selectedRoom?.name || selectedRoom?.title || '',
+ roomCategory: selectedOption?.name || '',
message: form.specialRequests || '',
bookingNumber: bookingNumber || '',
- autoOpenWhatsApp: true
+ autoOpenWhatsApp: true
};
navigate('/booking/confirmation', { state: { request: requestForConfirmation } });
-
} catch (error) {
+ console.error('Booking submit error:', error);
setMessage({
type: 'error',
text: error.response?.data?.message || 'Failed to submit booking request'
@@ -190,18 +300,16 @@ const Booking = () => {
onChange={handleChange('roomId')}
required
>
- {rooms.length === 0 ? (
-
- ) : (
+ {rooms.length > 0 ? (
rooms.map((r) => (
-