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 { useStoreActions, 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 datepicker 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:
The code for this lesson is available at https://github.com/flaviocopes/airbnb-clone-react-nextjs-2020/tree/6-3