CzyBnB is a full-stack web application inspired by Airbnb. It allows users to create spots, add reviews, offering a diverse range of accommodations for exploration.
- 🌟 MVP Feature List
- 💡 Database Schema
- 📚 User Stories
- 🔗 API Docs
- 🛠️ Technologies Used
- 🌅 Landing Page
- 🏞️ One Spot Page and Reviews
- 💻 Code I'm Proud Of
- 🚀 Getting Started
- ✨ Features
- 📞 Contact
Explore the minimum viable product feature list required for CzyBnB.
Discover the schema of the PostgreSQL database powering CzyBnB.
Immerse yourself in captivating user stories, detailing each feature's journey and acceptance criteria.
Browse the API documentation for JSON data interaction between frontend and backend routes.
// GET all spots with query filters
/* This route retrieves spots with optional query filters
such as latitude, longitude, price range, etc. It performs a query based
on the provided filters and returns paginated results along with average
ratings and preview images for each spot. */
router.get('/', validateQueryParams, async (req, res, next) => {
try {
// Get the query params
let {
page = 1,
size = 20,
minLat,
maxLat,
minLng,
maxLng,
minPrice,
maxPrice,
} = req.query;
page = parseInt(page, 10);
size = parseInt(size, 10);
// Create the where clause
const whereClause = {
...(minLat && { lat: { [Sequelize.Op.gte]: parseFloat(minLat) } }),
...(maxLat && { lat: { [Sequelize.Op.lte]: parseFloat(maxLat) } }),
...(minLng && { lng: { [Sequelize.Op.gte]: parseFloat(minLng) } }),
...(maxLng && { lng: { [Sequelize.Op.lte]: parseFloat(maxLng) } }),
...(minPrice && { price: { [Sequelize.Op.gte]: parseFloat(minPrice) } }),
...(maxPrice && { price: { [Sequelize.Op.lte]: parseFloat(maxPrice) } }),
};
// Perform the query
const spotsData = await Spot.findAndCountAll({
where: whereClause,
attributes: {
include: [
[Sequelize.fn('AVG', Sequelize.col('Reviews.stars')), 'avgRating'],
],
},
include: [
{
model: Review,
attributes: [],
},
{
model: SpotImage,
as: 'SpotImages',
attributes: ['url'],
where: { preview: true },
required: false,
},
],
// Group by the primary keys of the Spot and SpotImages tables
group: ['Spot.id', 'SpotImages.id'],
limit: size,
offset: (page - 1) * size,
subQuery: false,
distinct: true,
});
// Format the spots to be returned to the client
const spots = spotsData.rows.map((spot) => {
const previewImage =
spot.SpotImages && spot.SpotImages.length > 0
? spot.SpotImages[0].url
: null;
// Cast the avgRating to a float and round to a single decimal point
const avgRating = spot.dataValues.avgRating
? parseFloat(spot.dataValues.avgRating).toFixed(1)
: null;
return {
id: spot.id,
ownerId: spot.ownerId,
address: spot.address,
city: spot.city,
state: spot.state,
country: spot.country,
lat: parseFloat(spot.lat),
lng: parseFloat(spot.lng),
name: spot.name,
description: spot.description,
price: parseFloat(spot.price),
createdAt: spot.createdAt.toISOString(), // Add createdAt field in ISO string format
updatedAt: spot.updatedAt.toISOString(), // Add updatedAt field in ISO string format
avgRating: avgRating !== null ? parseFloat(avgRating) : null, // Ensure avgRating is not returned as a string
previewImage: previewImage,
};
});
res.status(200).json({
Spots: spots,
page,
size,
});
} catch (error) {
next(error);
}
});
- Clone this repository: CzyBnB Repository
- Install dependencies for the backend and frontend by navigating to each directory in separate terminals and running
npm install
. - Create a
.env
file using the provided.envexample
. - Set up your database with information from your
.env
file and then run the following commands:npx dotenv sequelize db:create
npx dotenv sequelize db:migrate
npx dotenv sequelize db:seed:all
- Start the app for both backend and frontend using
npm start
. - Now you can use the Demo User or Create an account.
- Users can create a Spot.
- Users can read/view other Spots.
- Users can update their Spot.
- Users can delete their Spot.
- Users can create Reviews on Spots.
- Users can read/view all of the Reviews on a Spot.
- Users can delete their Review(s) on a Spot.