fix booking page

This commit is contained in:
yotakii
2026-02-23 14:56:25 +03:00
parent 724bcc8f10
commit 921c587656

View File

@@ -33,26 +33,128 @@ const Booking = () => {
specialRequests: '' 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(() => { useEffect(() => {
let mounted = true; let mounted = true;
const loadRooms = async () => { const loadRooms = async () => {
try { 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 = if (normalizedRooms.length > 0) {
res?.data?.data?.rooms || if (mounted) setRooms(normalizedRooms);
res?.data?.data || return;
res?.data?.rooms || }
[];
const fetched = (Array.isArray(rawRooms) ? rawRooms : []).filter((r) => { // 2) Fallback to room categories (the types shown on public rooms page)
const status = String(r?.status || '').toLowerCase(); const categoriesRes = await api.get('/api/room-categories');
return !status || status === 'active'; 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) { } catch (e) {
console.error('Failed to load rooms/categories:', e);
if (mounted) setMessage({ type: 'error', text: 'Failed to load rooms' }); if (mounted) setMessage({ type: 'error', text: 'Failed to load rooms' });
} finally { } finally {
if (mounted) setLoadingRooms(false); if (mounted) setLoadingRooms(false);
@@ -65,11 +167,10 @@ const Booking = () => {
}, []); }, []);
const handleChange = (key) => (e) => { const handleChange = (key) => (e) => {
setForm(prev => ({ ...prev, [key]: e.target.value })); setForm((prev) => ({ ...prev, [key]: e.target.value }));
}; };
const validateBeforeSubmit = () => { const validateBeforeSubmit = () => {
// check dates
if (form.checkInDate && form.checkOutDate) { if (form.checkInDate && form.checkOutDate) {
const inD = new Date(form.checkInDate); const inD = new Date(form.checkInDate);
const outD = new Date(form.checkOutDate); const outD = new Date(form.checkOutDate);
@@ -81,10 +182,12 @@ const Booking = () => {
const adults = Number(form.adults); const adults = Number(form.adults);
const children = Number(form.children || 0); const children = Number(form.children || 0);
if (!Number.isFinite(adults) || adults < 1) { if (!Number.isFinite(adults) || adults < 1) {
setMessage({ type: 'error', text: 'Adults must be at least 1' }); setMessage({ type: 'error', text: 'Adults must be at least 1' });
return false; return false;
} }
if (!Number.isFinite(children) || children < 0) { if (!Number.isFinite(children) || children < 0) {
setMessage({ type: 'error', text: 'Children cannot be negative' }); setMessage({ type: 'error', text: 'Children cannot be negative' });
return false; return false;
@@ -102,6 +205,9 @@ const Booking = () => {
setSubmitting(true); setSubmitting(true);
try { try {
const selectedOption = rooms.find((r) => r._id === form.roomId);
// Keep original payload structure, but add category fallback fields safely
const payload = { const payload = {
guestInfo: { guestInfo: {
firstName: form.firstName.trim(), firstName: form.firstName.trim(),
@@ -109,7 +215,10 @@ const Booking = () => {
email: form.email.trim(), email: form.email.trim(),
phone: form.phone.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, checkInDate: form.checkInDate,
checkOutDate: form.checkOutDate, checkOutDate: form.checkOutDate,
numberOfGuests: { numberOfGuests: {
@@ -119,13 +228,14 @@ const Booking = () => {
specialRequests: form.specialRequests 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 res = await api.post('/api/bookings/request', payload);
const booking = res?.data?.data?.booking || null; const booking = res?.data?.data?.booking || null;
const bookingNumber = booking?.bookingNumber || res?.data?.data?.bookingNumber || 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 = { const requestForConfirmation = {
fullName: `${form.firstName} ${form.lastName}`.trim(), fullName: `${form.firstName} ${form.lastName}`.trim(),
phone: form.phone, phone: form.phone,
@@ -134,15 +244,15 @@ const Booking = () => {
checkOutDate: form.checkOutDate, checkOutDate: form.checkOutDate,
adults: Number(form.adults), adults: Number(form.adults),
children: Number(form.children || 0), children: Number(form.children || 0),
roomCategory: selectedRoom?.name || selectedRoom?.title || '', roomCategory: selectedOption?.name || '',
message: form.specialRequests || '', message: form.specialRequests || '',
bookingNumber: bookingNumber || '', bookingNumber: bookingNumber || '',
autoOpenWhatsApp: true autoOpenWhatsApp: true
}; };
navigate('/booking/confirmation', { state: { request: requestForConfirmation } }); navigate('/booking/confirmation', { state: { request: requestForConfirmation } });
} catch (error) { } catch (error) {
console.error('Booking submit error:', error);
setMessage({ setMessage({
type: 'error', type: 'error',
text: error.response?.data?.message || 'Failed to submit booking request' text: error.response?.data?.message || 'Failed to submit booking request'
@@ -190,18 +300,16 @@ const Booking = () => {
onChange={handleChange('roomId')} onChange={handleChange('roomId')}
required required
> >
{rooms.length === 0 ? ( {rooms.length > 0 ? (
<MenuItem disabled value="">
No rooms available
</MenuItem>
) : (
rooms.map((r) => ( rooms.map((r) => (
<MenuItem key={r._id || r.id} value={r._id || r.id}> <MenuItem key={r._id} value={r._id}>
{(r.name || r.title || 'Room')} {r.label || r.name}
{r.roomNumber ? ` (#${r.roomNumber})` : ''}
{(r.basePrice ?? r.price) != null ? ` - $${r.basePrice ?? r.price}/night` : ''}
</MenuItem> </MenuItem>
)) ))
) : (
<MenuItem value="" disabled>
No rooms available
</MenuItem>
)} )}
</TextField> </TextField>
@@ -248,7 +356,7 @@ const Booking = () => {
rows={3} rows={3}
/> />
<Button type="submit" variant="contained" size="large" disabled={submitting}> <Button type="submit" variant="contained" size="large" disabled={submitting || rooms.length === 0}>
{submitting ? 'Submitting...' : 'Submit Booking Request'} {submitting ? 'Submitting...' : 'Submit Booking Request'}
</Button> </Button>
</Box> </Box>