-
FILTERS
-
- setOpenFilters(!openFilters)}> {openFilters ? "HIDE" : "SHOW"}
- CLEAR
-
+
+ {/* Loading State */}
+ {loading &&
Loading available rooms...
}
+
+ {/* Error State */}
+ {error &&
Error: {error}
}
+
+ {/* Empty State */}
+ {!loading && !error && rooms.length === 0 &&
No rooms available at the moment.
}
+
+ {/* Rooms List */}
+ {!loading && !error && rooms.length > 0 &&
+ rooms.map((room) => (
+
+
{ navigate(`/rooms/${room._id}`); scrollTo(0, 0); }}
+ src={room.images[0]}
+ alt="hotel-img"
+ title="View Room Details"
+ className="max-h-65 md:w-1/2 rounded-xl shadow-lg object-cover cursor-pointer"
+ />
+
+
{room.hotel.city}
+
{ navigate(`/rooms/${room._id}`); scrollTo(0, 0); }}
+ >
+ {room.hotel.name}
+
+
-
-
-
Popular Filters
- {roomTypes.map((room,index)=>(
-
- ))}
-
-
-
Price Range
- {priceRange.map((range,index)=>(
-
- ))}
-
-
-
Sort By
- {sortOptions.map((option,index)=>(
-
- ))}
+
+
+
{room.hotel.address}
+
+
+ {room.amenities.map((item, index) => (
+
+
+
{item}
-
+ ))}
+ ${room.pricePerNight}/night
+
+ ))
+ }
+
+
+ {/* Filters */}
+
+
+
FILTERS
+
+ setOpenFilters(!openFilters)}>
+ {openFilters ? "HIDE" : "SHOW"}
+
+ CLEAR
+
+
+
+
+
Popular Filters
+ {roomTypes.map((room, index) => (
+
+ ))}
+
+
+
Price Range
+ {priceRange.map((range, index) => (
+
+ ))}
+
+
+
Sort By
+ {sortOptions.map((option, index) => (
+
+ ))}
+
+
+
- )
-}
+ );
+};
-export default AllRooms
\ No newline at end of file
+export default AllRooms;
diff --git a/client/src/pages/MyBookings.jsx b/client/src/pages/MyBookings.jsx
index 995f08a..d3b0fb7 100644
--- a/client/src/pages/MyBookings.jsx
+++ b/client/src/pages/MyBookings.jsx
@@ -1,67 +1,135 @@
-import React, { useState } from 'react'
-import Title from '../components/Title'
-import { assets, userBookingsDummyData } from '../assets/assets'
+import React, { useState, useEffect } from 'react';
+import Title from '../components/Title';
+import { assets } from '../assets/assets';
+
const MyBookings = () => {
- const [bookings, setBookings] = useState(userBookingsDummyData);
+ const [bookings, setBookings] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchBookings = async () => {
+ try {
+ const response = await fetch('/api/bookings');
+ if (!response.ok) {
+ throw new Error('Failed to fetch bookings');
+ }
+ const data = await response.json();
+ setBookings(data);
+ } catch (err) {
+ setError(err.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchBookings();
+ }, []);
+
return (
-
-
-
-
Hotels
-
Date & Timings
-
Payment
-
- {bookings.map((booking)=>(
-
+
+
+
+ {/* Loading State */}
+ {loading &&
Loading your bookings...
}
+
+ {/* Error State */}
+ {error &&
Error: {error}
}
+
+ {/* Empty State */}
+ {!loading && !error && bookings.length === 0 && (
+
You have no bookings yet.
+ )}
+
+ {/* Table Header */}
+ {!loading && !error && bookings.length > 0 && (
+ <>
+
+
Hotels
+
Date & Timings
+
Payment
+
+
+ {/* Booking List */}
+ {bookings.map((booking) => (
+
{/* Hotel Details */}
-
-
-
-
{booking.hotel.name}
- ({booking.room.roomType})
-
-
-
-
{booking.hotel.address}
-
-
-
-
Guests: {booking.guests}
-
-
Total: ${booking.totalPrice}
+
+
+
+
+ {booking.hotel.name}
+ ({booking.room.roomType})
+
+
+
+
{booking.hotel.address}
+
+
+
Guests: {booking.guests}
+
+
Total: ${booking.totalPrice}
+
+
{/* Date & Timings */}
-
+
Check-In:
-
{new Date(booking.checkInDate).toDateString()}
+
+ {new Date(booking.checkInDate).toDateString()}
+
Check-Out:
-
{new Date(booking.checkOutDate).toDateString()}
+
+ {new Date(booking.checkOutDate).toDateString()}
+
-
- {/* Payment Status */}
-
-
-
-
-
-
{booking.isPaid ? "Paid" : "Unpaid"}
+ {/* Payment Status */}
+
+
+
+
+ {booking.isPaid ? 'Paid' : 'Unpaid'}
+
{!booking.isPaid && (
-
Pay Now
+
+ Pay Now
+
)}
-
- ))}
+
+ ))}
+ >
+ )}
);
};
-export default MyBookings
\ No newline at end of file
+export default MyBookings;
diff --git a/client/src/pages/RoomDetails.jsx b/client/src/pages/RoomDetails.jsx
index ba1f7bf..178807b 100644
--- a/client/src/pages/RoomDetails.jsx
+++ b/client/src/pages/RoomDetails.jsx
@@ -1,122 +1,188 @@
import React, { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
-import { roomsDummyData, assets, facilityIcons, roomCommonData } from '../assets/assets';
+import { assets, facilityIcons, roomCommonData } from '../assets/assets';
import StarRating from '../components/StarRating';
const RoomDetails = () => {
- const {id} = useParams();
- const [room, setRoom] = useState(null);
- const [mainImage, setMainImage] = useState(null);
- useEffect(()=>{
- const room = roomsDummyData.find(room => room._id === id);
- room && setRoom(room);
- room && setMainImage(room.images[0]);
- },[])
- return room &&(
-
- {/* {Room Details} */}
-
-
{room.hotel.name} ({room.roomType})
-
20% OFF
-
- {/* {Room Rating} */}
-
- {/* Room Address */}
-
-
-
{room.hotel.address}
-
- {/* Room Images */}
-
-
-
-
-
- {room?.images.length > 1 && room.images.map((image,index)=>(
-
setMainImage(image)} key={index} src={image} alt="room-img" className={`w-full rounded-xl shadow-md object-cover cursor-pointer ${mainImage === image && 'outline-3 outline-orange-500'}`}/>
- ))}
-
+ const { id } = useParams();
+ const [room, setRoom] = useState(null);
+ const [mainImage, setMainImage] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchRoomDetails = async () => {
+ try {
+ const response = await fetch(`/api/rooms/${id}`);
+ if (!response.ok) {
+ throw new Error('Failed to fetch room details');
+ }
+ const data = await response.json();
+ setRoom(data);
+ setMainImage(data.images?.[0]);
+ } catch (err) {
+ setError(err.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchRoomDetails();
+ }, [id]);
+
+ // Loading State
+ if (loading) {
+ return
Loading room details...
;
+ }
+
+ // Error State
+ if (error) {
+ return
Error: {error}
;
+ }
+
+ // Empty State (if room not found)
+ if (!room) {
+ return
Room details not available.
;
+ }
+
+ return (
+
+ {/* Room Details Header */}
+
+
+ {room.hotel.name}{' '}
+ ({room.roomType})
+
+
+ 20% OFF
+
+
+
+ {/* Rating */}
+
+
+ {/* Address */}
+
+
+
{room.hotel.address}
+
+
+ {/* Room Images */}
+
+
+
- {/* Room Highlights */}
-
-
-
Experience Luxury Like Never Before
-
- {room.amenities.map((item,index)=>(
-
-
-
{item}
-
- ))}
-
-
- {/* Room Price */}
-
${room.pricePerNight}/night
+
+ {room?.images?.length > 1 &&
+ room.images.map((image, index) => (
+
setMainImage(image)}
+ className={`w-full rounded-xl shadow-md object-cover cursor-pointer ${
+ mainImage === image && 'outline-3 outline-orange-500'
+ }`}
+ />
+ ))}
- {/* Checkin Checkout form */}
-
-
Check Availability
-
-
- {/* Common Specifiications */}
-
- {roomCommonData.map((spec,index)=>(
-
-
-
-
{spec.title}
-
{spec.description}
-
-
-
+ {/* Highlights */}
+
+
+
+ Experience Luxury Like Never Before
+
+
+ {room.amenities.map((item, index) => (
+
+
+
{item}
+
))}
+
-
-
Guests will be allocated on the ground floor according to availability. You get a comfortable Two bedroom apartment has a true city feeling.
- The price quoted is for two guest, at the guest slot please mark the number of guests to get the exact price for groups. The Guests will be allocated
- ground floor according to availability. You get the comfortable two-bedroom apartment that has a true city feeling.
+ {/* Price */}
+
+ ${room.pricePerNight}/night
+
+
+
+ {/* Checkin Checkout Form */}
+