37 KiB
Agenda
We will try to cover most of these topics in today's sessions and the remaining in the next.
- Build the shows components
- Build the seating arrangements
- Book tickets
- Make Payments
- Deploy It is going to be a bit challenging, advanced, but very interesting session covering topics that are asked very frequently in interviews.
So let's start.
Build the Home Page
As till now we have completed the home page, where all the movies and upcoming movies are shown as below:
As we click on the movies then the related information about the movies theaters and the shows timings should be visible in the below blank region.
Shows
Now we will be implementing those related functionalities as follows: If we get this below data in the theaterforMovie then we can show the related shows and the timings and all.
import { message } from "antd";
import { useEffect, useState } from "react";
import {useNavigate, useParams} from "react-router-dom"
import { GetMovieById } from "../apicalls/movies";
import { useDispatch } from "react-redux";
import { HideLoading, ShowLoading } from "../redux/loadersSlice";
import {GetTheatresByMovie} from "../apicalls/theatres"
import moment from "moment";
export default function TheatreForMovie(){
let [movie,setMovie] = useState()
let [theatres,setTheatres] = useState();
let [date,setDate] = useState(moment().format("YYYY-MM-DD"));
const [isHovering, setIsHovering] = useState(false);
const params = useParams();
const dispatch = useDispatch();
const navigate = useNavigate();
const getData = async() => {
try{
dispatch(ShowLoading());
const response = await GetMovieById(params.id)
if(response.success){
setMovie(response.data);
message.success(response.message)
}else{
message.error(response.message);
}
dispatch(HideLoading());
}catch(err){
dispatch(HideLoading());
message.error(err.message)
}
}
const getTheatres = async () => {
try{
dispatch(ShowLoading());
const response = await GetTheatresByMovie({date,movie:params.id})
console.log(response.data);
if(response.success){
setTheatres(response.data);
message.success(response.message)
}else{
message.error(response.message);
}
dispatch(HideLoading());
}catch(err){
dispatch(HideLoading());
message.error(err.message);
}
}
const handleMouseEnter = (id) => {
setIsHovering(true);
};
const handleMouseLeave = (id) => {
setIsHovering(false);
};
useEffect(() => {
getData();
},[])
useEffect(() => {
getTheatres()
},[date])
return (
movie && (
<div>
{/* movie information */}
<div className = "flex justify-between items-center mb-2">
<div>
<h1 className = "text-2xl uppercase">
{movie.title} ({movie.language})
</h1>
<h1 className = "text-md">Duration : {movie.duration} mins</h1>
<h1 className = "text-md">
Release Date : {moment(movie.releaseDate).format("MMM Do yyyy")}
</h1>
<h1 className = "text-md">Genre : {movie.genre}</h1>
</div>
<div className = "mr-3">
<h1 className = "text-md ">Select Date</h1>
<input
type = "date"
min = {moment().format("YYYY-MM-DD")}
value = {date}
onChange = {(e) => {
setDate(e.target.value);
navigate(`/movie/${params.id}?date=${e.target.value}`);
}}
/>
</div>
</div>
<hr />
{/* movie theatres */}
<div className = "mt - 1">
<h1 className = "text - xl uppercase">Theatres</h1>
</div>
<div className = "mt - 1 flex flex-col gap-1">
{theatres.map((theatre) => (
<div className = "card p-2">
<h1 className = "text-md uppercase">{theatre.name}</h1>
<h1 className = "text-sm">Address : {theatre.address}</h1>
<div className = "divider"></div>
<div className = "flex gap-2">
{theatre.shows
.sort(
(a, b) => moment(a.time, "HH:mm") - moment(b.time, "HH:mm")
)
.map((show) => (
<div key = {show._id} style = {{
backgroundColor: isHovering ? '#DF1827' : 'white',
color: isHovering ? 'white' : '#DF1827',
}}
onMouseEnter = {handleMouseEnter}
onMouseLeave = {handleMouseLeave}
className = "card p-1 cursor-pointer border-primary"
onClick = {() => {
navigate(`/book-show/${show._id}`);
}}
>
<h1 className = "text-sm">
{moment(show.time, "HH:mm").format("hh:mm A")}
</h1>
</div>
))}
</div>
</div>
))}
</div>
</div>
)
);
}
The code is a React component named TheatreForMovie
that is responsible for displaying information about a movie and its associated theaters. It utilizes various React hooks and asynchronous API calls. Here's a breakdown of its functionality:
-
State Management:
- The component uses
useState
to manage state variables formovie
,theatres
, anddate
. - It also manages the
isHovering
state to handle mouse hover events.
- The component uses
-
Initialization:
- The component extracts route parameters using
useParams()
and obtains a navigation function usinguseNavigate()
. - It also obtains a Redux dispatch function using
useDispatch()
.
- The component extracts route parameters using
-
Data Retrieval:
- Two asynchronous functions,
getData()
andgetTheatres()
, are defined to fetch movie details and theater information, respectively. - These functions use API calls (e.g.,
GetMovieById
andGetTheatresByMovie
) to retrieve data. - Loading states are managed using Redux actions (
ShowLoading
andHideLoading
).
- Two asynchronous functions,
-
Mouse Hover Effects:
handleMouseEnter
andhandleMouseLeave
functions are used to toggle theisHovering
state when hovering over theater showtimes.- This is used to change the background and text color of showtime cards.
-
Effect Hooks:
- The
useEffect
hook is utilized to fetch movie data (getData()
) when the component mounts. It runs once because it has an empty dependency array. - Another
useEffect
hook is used to fetch theater data (getTheatres()
) whenever thedate
state changes.
- The
-
Rendering:
- The component conditionally renders the movie and theater information if the
movie
state exists. - Movie information, including title, language, duration, release date, and genre, is displayed.
- A date input allows users to select a date, triggering a navigation change.
- Theater information is displayed, including theater name, address, and showtimes.
- Showtimes are sorted by time and displayed in cards that respond to hover events.
- The component conditionally renders the movie and theater information if the
-
Sorting of Theater Shows:
- Within the rendering section of the component, theater shows are sorted based on their showtimes.
- This sorting is achieved through the
.sort()
method applied to thetheatre.shows
array. - The sorting logic compares the showtimes, ensuring that shows are displayed in chronological order.
- The
moment
library is used to parse and format showtime values for comparison. - As a result, theater shows are presented to users in ascending order of showtime, providing a clear and organized view of available showtimes for the selected date.
Seating Component
Now lets start with the seating component, after selecting the show of the theater as follows.
onClick = {() => {
navigate(`/book-show/${show._id}`);
}}
When a user clicks an element (like a button), the onClick
function runs. It uses the navigate
function to take the user to a new page, typically based on the show's ID.
Get show by id
//get show by id
router.post('/get-show-by-id',authMiddleware,async(req,res) => {
try{
const show = await Show.findById(req.body.showId)
.populate("movie")
.populate("theatre")
res.send({
success:true,
message:"Show fetched",
data:show,
})
}catch(err){
res.send({
success:false,
message:err.message
})
}
})
This specific route handles a POST request to retrieve information about a show by its ID. Let's break down what this code does:
-
Route Definition:
- This route is defined to listen for POST requests on a specific path, which is "/get-show-by-id".
-
Middleware:
- It includes an authentication middleware function called
authMiddleware
. This function is likely responsible for verifying the user's identity and ensuring that only authorized users can access this route.
- It includes an authentication middleware function called
-
Request Handling:
- When a POST request is made to this route, the server executes the code inside the
try
block.
- When a POST request is made to this route, the server executes the code inside the
-
Show Retrieval:
- Inside the
try
block, the code uses theShow
model (presumably a MongoDB model) to find a show document in the database based on the providedshowId
. This is done usingShow.findById(req.body.showId)
. - The
populate
method is used to retrieve additional information related to the show, specifically details about the associated movie and theater. This populates the "movie" and "theatre" fields in theshow
document with their respective data.
- Inside the
-
Response:
- If the show is successfully found and populated, a response is sent back to the client with a
success
status set totrue
. - The response includes a message indicating that the show has been fetched and the
show
data itself.
- If the show is successfully found and populated, a response is sent back to the client with a
-
Error Handling:
- If any errors occur during the process (e.g., the show with the provided ID doesn't exist or there's an issue with the database), the code inside the
catch
block is executed. - In case of an error, a response is sent back to the client with a
success
status set tofalse
, and an error message is provided.
- If any errors occur during the process (e.g., the show with the provided ID doesn't exist or there's an issue with the database), the code inside the
In summary, this route is designed to handle POST requests to fetch details about a show by its ID. It uses authentication middleware to secure the route, retrieves the show data from a database, populates related movie and theater information, and sends a response with the retrieved data or an error message, depending on the outcome of the database operation.
export const GetShowById = async (payload) => {
try{
const response = await axiosInstance.post('/api/theatres/get-show-by-id',payload)
return response.data
}catch(err){
return err.message
}
}
The GetShowById
function serves as a client-side utility for making HTTP POST requests to a server's /api/theatres/get-show-by-id
endpoint. It is designed to retrieve specific show information based on a provided payload
, which typically includes data necessary for the request, such as a showId
.
The function operates asynchronously, utilizing Axios to handle the HTTP request. Within a try...catch
block, it awaits the completion of the request, ensuring that the program doesn't proceed until a response is received.
On success, the function extracts and returns the response data from the server using return response.data
. This allows the caller to access and utilize the fetched show data.
However, in the event of an error—whether due to network issues, server problems, or other reasons—the catch
block captures and handles the error.
How Many Seats Are There for That Show
Different shows have different number of the shows as below:
import { message } from "antd";
import { useEffect, useState } from "react"
import { useParams, useNavigate } from "react-router-dom";
import { GetShowById } from "../apicalls/theatres";
import {BookShowTickets, MakePayment } from "../apicalls/bookings";
import Button from "../components/Button";
import { HideLoading, ShowLoading } from "../redux/loadersSlice";
import StripeCheckout from "react-stripe-checkout";
import moment from "moment"
import { useDispatch, useSelector } from "react-redux";
export default function BookShow(){
const { user } = useSelector((state) => state.users);
const [selectedSeats, setSelectedSeats] = useState([]);
let [show,setShow] = useState();
const params = useParams();
const dispatch = useDispatch();
const navigate = useNavigate()
const getData = async() => {
try{
const response = await GetShowById({showId:params.id})
if(response.success){
setShow(response.data);
}else{
message.error(response.message)
}
}catch(err){
message.error(err.message)
}
}
const getSeats = () => {
const columns = 12;
const totalSeats = show.totalSeats; // 120
const rows = Math.ceil(totalSeats / columns); // 10
return (
<div>
<p className = "m - 4">Screen This Side</p>
<hr/>
<div className = "flex gap-1 flex-col p-2 card">
<hr/>
{Array.from(Array(rows).keys()).map((seat, index) => {
return (
<div className = "flex gap-1 justify-center">
{/* 0, 1 ,2, ,3, .. 11
0 [ [ 0, 1, 2, 3, 4, 5,6,7.. ,11],
1 [0, 1, 2, 3, .. ,11],
2 .
.
9 [0,1,2 , .... 11]
] */}
{Array.from(Array(columns).keys()).map((column, index) => {
const seatNumber = seat * columns + column + 1;
// 12*1 + 3+ 1 = 16
let seatClass = "seat";
// seat = 0 // coloumns = 12
//0 + 1 + 1 = 2
if (selectedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " selected-seat";
}
if (show.bookedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " booked-seat";
}
return (
seat * columns + column + 1 <= totalSeats && (
<div
className = {seatClass}
onClick = {() => {
if (selectedSeats.includes(seatNumber)) {
setSelectedSeats(
selectedSeats.filter((item) => item !== seatNumber)
);
} else {
setSelectedSeats([...selectedSeats, seatNumber]);
}
}}
>
<h1 className = "text-sm">{seat * columns + column + 1}</h1>
</div>
)
);
})}
</div>
);
})}
</div>
</div>
);
};
const book = async (transactionId) => {
try {
dispatch(ShowLoading());
const response = await BookShowTickets({
show: params.id,
seats: selectedSeats,
transactionId,
user: user._id,
});
if (response.success) {
message.success(response.message);
navigate("/profile");
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
const onToken = async (token) => {
// console.log(token)
try {
dispatch(ShowLoading());
const response = await MakePayment(
token,
selectedSeats.length * show.ticketPrice * 100
);
if (response.success) {
message.success(response.message);
// console.log(response.data);
book(response.data);
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
useEffect(() => {
getData();
},[])
return (
show && (
<div>
{/* show infomation */}
<div className = "flex justify-between card p-2 items-center">
<div>
<h1 className = "text-sm">{show.theatre.name}</h1>
<h1 className = "text-sm">{show.theatre.address}</h1>
</div>
<div>
<h1 className = "text-2xl uppercase">
{show.movie.title} ({show.movie.language})
</h1>
</div>
<div>
<h1 className = "text-sm">
{moment(show.date).format("MMM Do yyyy")} - {" "}
{moment(show.time, "HH:mm").format("hh:mm A")}
</h1>
</div>
</div>
{/* seats */}
<div className = "flex justify-center mt-2">{getSeats()}</div>
{selectedSeats.length > 0 && (
<div className = "mt-2 flex justify-center gap-2 items-center flex-col">
<div className = "flex justify-center">
<div className = "flex uppercase card p-2 gap-3">
<h1 className = "text-sm"><b>Selected Seats</b> : {selectedSeats.join(" , ")}</h1>
<h1 className = "text-sm">
<b>Total Price</b> : {selectedSeats.length * show.ticketPrice}
</h1>
</div>
</div>
<StripeCheckout
token = {onToken}
amount = {selectedSeats.length * show.ticketPrice * 100}
billingAddress
stripeKey = "pk_test_eTH82XLklCU1LJBkr2cSDiGL001Bew71X8"
>
<Button title = "Book Now" />
</StripeCheckout>
</div>
)}
</div>
)
);
}
Let's dive into the BookShow
component and focus on the details, particularly the part that handles rendering the seats with the for
loops:
-
Component Structure:
TheBookShow
component is a React functional component responsible for booking show tickets. It receives data about a show and allows users to select seats for booking. -
Fetching Show Data:
ThegetData
function is called when the component mounts (useEffect
). It fetches details about the show specified by theparams.id
using theGetShowById
API call and sets the show data in the component's state. -
Rendering Seats:
- The
getSeats
function is responsible for rendering the seats grid. It calculates the number of rows and columns based on thetotalSeats
of the show. - It returns a JSX structure that represents the seats grid. The grid is divided into rows and columns using nested
map
functions.
{Array.from(Array(rows).keys()).map((seat, index) => { return ( <div className="flex gap-1 justify-center"> {Array.from(Array(columns).keys()).map((column, index) => { // ... Seat rendering logic })} </div> ); })}
- The outer
map
iterates over the rows, and the innermap
iterates over the columns. For each seat, it calculates a uniqueseatNumber
based on the row and column indices. - The seat's appearance (e.g., styling) depends on its characteristics, such as whether it's selected or booked. This is determined by the
seatClass
. - Seats that are already booked (
show.bookedSeats
) are given a "booked-seat" class. - Seats that are currently selected by the user are given a "selected-seat" class.
- Seats that are neither booked nor selected are rendered without any additional class.
- The
-
Selecting and Deselecting Seats:
- Seats can be selected or deselected by clicking on them. This interaction is handled by the
onClick
event attached to each seat<div>
. - When a seat is clicked, the
setSelectedSeats
function is used to update theselectedSeats
state based on whether the clicked seat is already in the selected seats array or not.
- Seats can be selected or deselected by clicking on them. This interaction is handled by the
-
Displaying Selected Seats and Total Price:
- Below the seat grid, there's a section that displays the selected seats and the total price. This section is conditionally rendered only when there are selected seats (
selectedSeats.length > 0
). - It shows the selected seat numbers joined by a comma and calculates the total price based on the number of selected seats and the ticket price for the show.
- Below the seat grid, there's a section that displays the selected seats and the total price. This section is conditionally rendered only when there are selected seats (
-
Booking Seats and Making Payments:
- The component includes functions (
book
andonToken
) for booking seats and handling payment. When the user confirms the booking, these functions are called to initiate the booking process and make a payment.
- The component includes functions (
Seat Selection/Deselection Functionaities
if (selectedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " selected-seat";
}
if (show.bookedSeats.includes(seat * columns + column + 1)) {
seatClass = seatClass + " booked-seat";
}
return (
seat * columns + column + 1 <= totalSeats && (
<div
className = {seatClass}
onClick = {() => {
if (selectedSeats.includes(seatNumber)) {
setSelectedSeats(
selectedSeats.filter((item) => item !== seatNumber)
);
} else {
setSelectedSeats([...selectedSeats, seatNumber]);
}
}}
>
<h1 className = "text-sm">{seat * columns + column + 1}</h1>
</div>
)
);
-
Class Assignment:
- The
seatClass
variable is initially set to an empty string for each seat. - The first
if
statement checks if the current seat number (calculated asseat * columns + column + 1
) is included in theselectedSeats
array. If it is, the"selected-seat"
class is appended toseatClass
. - The second
if
statement checks if the seat number is included in theshow.bookedSeats
array. If it is, the"booked-seat"
class is appended toseatClass
.
- The
-
Rendering Seats:
- The code then returns a
div
element representing the seat. - The condition
seat * columns + column + 1 <= totalSeats
ensures that only valid seats are rendered based on the total number of seats available. - The
className
attribute is set toseatClass
, which may include"selected-seat"
and/or"booked-seat"
classes based on the conditions. - An
onClick
event handler is attached to the seatdiv
. When a user clicks on a seat, it triggers this function. - Inside the
onClick
function:- If the clicked seat number is already in the
selectedSeats
array, it's removed from the array usingselectedSeats.filter()
. This means the user is deselecting the seat. - If the clicked seat number is not in
selectedSeats
, it's added to the array, indicating the user's selection.
- If the clicked seat number is already in the
- The code then returns a
/* seats */
.seat {
height: 30px;
width: 50px;
display: flex;
justify-content: center;
align-items: center;
border : 1px solid #b1b1b1;
cursor: pointer;
}
.selected-seat {
background-color: #118838;
}
.selected-seat h1 {
color: white !important;
}
.booked-seat {
background-color: #b1b1b1;
cursor: not-allowed;
pointer-events: none;
}
The provided CSS code defines styles for rendering seats in a grid. Each seat (.seat
) has a height and width, is centered both horizontally and vertically, and has a border to separate seats. The cursor
property changes to a pointer to indicate interactivity. Selected seats (.selected-seat
) have a green background and white text for visibility.
Booked seats (.booked-seat
) have a gray background and a disabled cursor, preventing further interaction. These styles effectively visualize seat availability and user selections, enhancing the user experience in a seat selection interface.
Add the Payment Options
"React Stripe Checkout" refers to a React library or component that allows developers to easily integrate Stripe's payment processing capabilities into their React applications. This library streamlines the process of creating a checkout experience, handling payments, and managing user interactions with the Stripe API.
Key features and steps typically involved in using "React Stripe Checkout" include:
- Installation:
Developers can install the library in their React project using npm or yarn. - Configuration:
Setting up Stripe credentials and configuring payment options, such as currency and amount. - Integration:
Adding the "React Stripe Checkout" component to the application's payment page or modal. - User Interaction:
Users can enter their payment details, including card information. - Tokenization:
The library securely tokenizes the payment information, ensuring sensitive data is not exposed to the application server. - Payment Processing:
Stripe processes the payment using the token and completes the transaction.
<StripeCheckout
token = {onToken}
amount = {selectedSeats.length * show.ticketPrice * 100}
billingAddress
stripeKey = "pk_test_eTH82XLklCU1LJBkr2cSDiGL001Bew71X8"
>
<Button title = "Book Now" />
</StripeCheckout>
The code snippet provided is an example of how you might use the "react-stripe-checkout" library to integrate Stripe's payment processing into a React application. Here's an explanation of the code:
-
<StripeCheckout> Component: This is the main component from the "react-stripe-checkout" library, and it acts as a wrapper around your payment button. It allows you to configure and handle the payment process.
-
token={onToken}: This prop specifies the callback function (
onToken
) that will be called when the payment is successful. Thetoken
contains the payment information, which you can then use to process the payment on the server. -
amount={selectedSeats.length * show.ticketPrice * 100}: This prop specifies the total amount to charge in the smallest currency unit (cents in this case). It calculates the total based on the number of selected seats and their individual ticket prices.
-
billingAddress: This prop indicates that you want to collect the customer's billing address during the payment process.
-
stripeKey: This prop is where you provide your Stripe API key (in test mode, as indicated by the "pk_test_" prefix). It connects your React app to your Stripe account.
-
: This is the button or UI element that triggers the payment process when clicked. It's wrapped inside the
<StripeCheckout>
component, making it the "Pay Now" or "Book Now" button.
When a user clicks the "Book Now" button, the <StripeCheckout>
component will handle the payment process. If the payment is successful, it will call the onToken
callback
function, allowing you to perform further actions, such as storing the payment details on your server or updating the UI to reflect the successful payment.
Make sure to replace the stripeKey
value with your actual Stripe API key, and ensure that your Stripe account is set up correctly to handle payments in test mode.
In bookShow.js file make the below changes as follows:
const onToken = async (token) => {
// console.log(token)
try {
dispatch(ShowLoading());
const response = await MakePayment(
token,
selectedSeats.length * show.ticketPrice * 100
);
if (response.success) {
message.success(response.message);
// console.log(response.data);
book(response.data);
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
- The onToken function is defined, which handles payment processing using Stripe.
- It dispatches actions for showing and hiding a loading indicator.
- It makes a payment request to the server using the MakePayment function and the payment token.
- Depending on the response, it displays success or error messages and potentially triggers further actions like booking.
In bookRoute.js file make the below changes as follows:
const authMiddleware = require("../middlewares/authMiddleware");
const router = require("express").Router();
const stripe = require("stripe")(process.env.stripe_key);
const Booking = require('../models/bookingModel')
const Show = require('../models/showModel')
router.post('/make-payment',authMiddleware,async(req,res) => {
try{
const {token,amount} = req.body;
console.log(token);
const customer = await stripe.customers.create({
email:token.email,
source:token.id
})
const charge = await stripe.charges.create({
amount:amount,
currency:"usd",
customer:customer.id,
receipt_email:token.email,
description:"Ticket has been booked for a movie"
})
const transactionId = charge.id;
res.send({
success:true,
message:"Payment done, Ticket booked",
data:transactionId
})
}catch(err){
res.send({
success:false,
message:err.message
})
}
})
- The Express route "/make-payment" is defined, which expects a POST request with payment-related data, including the token and amount.
- It uses the Stripe API to create a customer and charge their card.
- If the payment is successful, it returns a success message along with a transaction ID.
- If there's an error, it returns an error message.
In booking.js file make the below changes as follows:
import { axiosInstance } from "."
export const MakePayment = async(token,amount) => {
try{
const response = await axiosInstance.post('/api/bookings/make-payment',{token,amount});
return response.data
}catch(err){
return err.response
}
}
The MakePayment function is defined, which makes an API call to your server's "/make-payment" endpoint with the payment token and amount. It returns the response data if successful or the error response if there's an issue.
Bookings in the Profile
const mongoose = require("mongoose");
const bookingSchema = new mongoose.Schema(
{
show: {
type: mongoose.Schema.Types.ObjectId,
ref: "shows",
},
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "users",
},
seats: {
type: Array,
required: true,
},
transactionId: {
type: String,
required: true,
},
},
{ timestamps: true }
);
module.exports = mongoose.model("bookings", bookingSchema);
- A Mongoose schema called bookingSchema is defined, representing the structure of booking documents.
- It includes fields such as show (a reference to the show), user (a reference to the user making the booking), seats (an array of selected seats), and transactionId (a unique identifier for the transaction).
- The schema is exported as a Mongoose model named "bookings."
In bookingRoute.js file make the below changes as follows:
//book shows
router.post("/book-show", authMiddleware, async (req, res) => {
try {
// save booking
const newBooking = new Booking(req.body);
await newBooking.save();
const show = await Show.findById(req.body.show);
// update seats
await Show.findByIdAndUpdate(req.body.show, {
bookedSeats: [...show.bookedSeats, ...req.body.seats],
});
res.send({
success: true,
message: "Show booked successfully",
data: newBooking,
});
} catch (error) {
res.send({
success: false,
message: error.message,
});
}
});
router.get("/get-bookings", authMiddleware, async (req, res) => {
try {
const bookings = await Booking.find({ user: req.body.userId })
.populate("user")
.populate("show")
.populate({
path: "show",
populate: {
path: "movie",
model: "movies",
},
})
.populate({
path: "show",
populate: {
path: "theatre",
model: "theatres",
},
});
res.send({
success: true,
message: "Bookings fetched successfully",
data: bookings,
});
} catch (error) {
res.send({
success: false,
message: error.message,
});
}
});
- A new route handler "
/book-show
" is defined, which expects a POST request to book a show. - It creates a new booking record using the provided data and saves it to the database.
- It also fetches the existing show data to update the list of booked seats.
- The response includes a success message and the newly created booking data or an error message in case of failure.
- Another route handler "
/get-bookings
" is defined, which expects a GET request to fetch all bookings of a user. - It retrieves user-specific booking records, populates relevant data (user, show, movie, and theatre), and sends them in the response.
- The response includes a success message and the booking data or an error message in case of failure.
In bookings.js file make the below changes as follows:
// book shows
export const BookShowTickets = async (payload) => {
try {
const response = await axiosInstance.post(
"/api/bookings/book-show",
payload
);
return response.data;
} catch (error) {
return error.response.data;
}
};
export const GetBookingsOfUser = async () => {
try {
const response = await axiosInstance.get("/api/bookings/get-bookings");
return response.data;
} catch (error) {
return error.response.data;
}
};
- Two functions are defined to make API calls related to bookings.
- BookShowTickets sends a POST request to the "
/book-show
" endpoint to book show tickets, including the show ID, selected seats, transaction ID, and user ID. GetBookingsOfUser
sends a GET request to the "/get-bookings
" endpoint to fetch the bookings of the currently authenticated user.
In bookingShow.js file make the below changes as follows:
const book = async (transactionId) => {
try {
dispatch(ShowLoading());
const response = await BookShowTickets({
show: params.id,
seats: selectedSeats,
transactionId,
user: user._id,
});
if (response.success) {
message.success(response.message);
navigate("/profile");
} else {
message.error(response.message);
}
dispatch(HideLoading());
} catch (error) {
message.error(error.message);
dispatch(HideLoading());
}
};
- The book function is defined, which is responsible for handling the booking of show tickets.
- It makes an API call using BookShowTickets, passing relevant data such as the show ID, selected seats, transaction ID, and user ID.
- Depending on the response, it displays success or error messages and potentially navigates to the user's profile page.
- Loading indicators are shown while the booking process is in progress.