This content originally appeared on flaviocopes.com and was authored by flaviocopes.com
This post is part of a new series where we build a clone of Airbnb with Next.js. See the first post here.
- Part 1: Let’s start by installing Next.js
- Part 2: Build the list of houses
- Part 3: Build the house detail view
- Part 4: CSS and navigation bar
- Part 5: Start with the date picker
- Part 6: Add the sidebar
- Part 7: Add react-day-picker
- Part 8: Add the calendar to the page
- Part 9: Configure the DayPickerInput component
- Part 10: Sync the start and end dates
- Part 11: Show the price for the chosen dates
- Part 12: Login and signup forms
- Part 13: Activate the modal
- Part 14: Send registration data to the server
- Part 15: Add postgres
- Part 16: Implement model and DB connection
- Part 17: Create a session token
- Part 18: Implement login
- Part 19: Determine if we are logged in
- Part 20: Change state after we login
- Part 21: Log in after registration
- Part 22: Create the models and move data to the db
- Part 23: Use the database instead of the file
We’re now ready to implement booking a house!
Let’s keep things simple now and let’s defer payments to later.
A person can book if the dates chosen do not overlap with another booking for that house.
First, instead of allowing to reserve before being logged in, like we have now:
pages/houses/[id].js
<button
className='reserve'
onClick={() => {
setShowLoginModal()
}}>
Reserve
</button>
we do a check on the user
property from the easy-peasy
store, and if we’re not logged in, we’ll show a “Log in to Reserve” button instead.
First we need to import useStoreState
:
pages/houses/[id].js
import { useStoreState } from 'easy-peasy'
then we declare, inside the component function body the loggedIn
value:
pages/houses/[id].js
const loggedIn = useStoreState((state) => state.login.loggedIn)
Now we can use this inside our JSX:
pages/houses/[id].js
{
loggedIn ? (
<button
className="reserve"
onClick={() => {
//todo: add code to reserve
}}
>
Reserve
</button>
) : (
<button
className="reserve"
onClick={() => {
setShowLoginModal()
}}
>
Log in to Reserve
</button>
)
}
Now when the Reserve now button is clicked I want to trigger a function that calls an endpoint on the server to book the place.
Server side I’m going to add the booking to a new table in the database, which we’ll call bookings
.
Once I’m done with that, I’ll create a way to check if a house is booked in a particular period, to avoid people booking days already booked.
We’ll integrate that with the calendar, so people can’t book places booked, and we’ll check if the dates are still valid when we actually go to book.
Let’s do it.
Let’s start with the model, in the model.js
file:
model.js
class Booking extends Sequelize.Model {}
Booking.init(
{
id: {
type: Sequelize.DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
houseId: { type: Sequelize.DataTypes.INTEGER, allowNull: false },
userId: { type: Sequelize.DataTypes.INTEGER, allowNull: false },
startDate: { type: Sequelize.DataTypes.DATEONLY, allowNull: false },
endDate: { type: Sequelize.DataTypes.DATEONLY, allowNull: false }
},
{
sequelize,
modelName: 'booking',
timestamps: true
}
)
And export it:
export { sequelize, User, House, Booking }
It’s very similar to the models we already have.
Now like we did before, we can call Booking.sync() to create the table in the database.
You can add this line at the end of the model.js
file.
Booking.sync({ alter: true })
Ok, now that we have the model ready for our data, we can go and implement the booking functionality in the frontend.
In pages/houses/[id].js
I first import axios
:
import axios from 'axios'
In the component code, I declare 2 new state variables:
const [startDate, setStartDate] = useState()
const [endDate, setEndDate] = useState()
and when the date picker tells us new dates have been set, we update them.
Change:
<DateRangePicker
datesChanged={(startDate, endDate) => {
setNumberOfNightsBetweenDates(
calcNumberOfNightsBetweenDates(startDate, endDate)
)
setDateChosen(true)
}}
/>
to:
<DateRangePicker
datesChanged={(startDate, endDate) => {
setNumberOfNightsBetweenDates(
calcNumberOfNightsBetweenDates(startDate, endDate)
)
setDateChosen(true)
setStartDate(startDate)
setEndDate(endDate)
}}
/>
Now when clicking the “Reserve” button I now invoke a new function, that POSTS data to the /api/reserve
endpoint:
<button
className='reserve'
onClick={async () => {
try {
const response = await axios.post('/api/reserve', {
houseId: house.id,
startDate,
endDate,
})
if (response.data.status === 'error') {
alert(response.data.message)
return
}
console.log(response.data)
} catch (error) {
console.log(error)
return
}
}}
>
Reserve
</button>
In this function I hit the /api/reserve
API endpoint with Axios, passing the house id, the start date, and the end date. The user is figured out in the server side thanks to seessions.
I added some simple console.log()
calls to figure out what the server responds.
The endpoint implementation
Here’s the endpoint implementation, which we create into pages/api/reserve.js
:
import { User, Booking } from '../../model.js'
export default async (req, res) => {
if (req.method !== 'POST') {
res.status(405).end() //Method Not Allowed
return
}
const user_session_token = req.cookies.nextbnb_session
if (!user_session_token) {
res.status(401).end()
return
}
User.findOne({ where: { session_token: user_session_token } }).then(
(user) => {
Booking.create({
houseId: req.body.houseId,
userId: user.id,
startDate: req.body.startDate,
endDate: req.body.endDate
}).then(() => {
res.writeHead(200, {
'Content-Type': 'application/json'
})
res.end(JSON.stringify({ status: 'success', message: 'ok' }))
})
}
)
}
Great! Now the booking data is stored successfully:
This content originally appeared on flaviocopes.com and was authored by flaviocopes.com
flaviocopes.com | Sciencx (2021-12-24T05:00:00+00:00) Airbnb clone, handle bookings. Retrieved from https://www.scien.cx/2021/12/24/airbnb-clone-handle-bookings/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.