Index.
html
<!doctype html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/[Link]" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>QuickShow</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/[Link]"></script>
</body>
</html>
.env
VITE_CURRENCY = '$'
VITE_CLERK_PUBLISHABLE_KEY = <API_KEY>
21
src/[Link]
@import
url('[Link]
@import "tailwindcss";
@theme {
--color-primary: #F84565;
--color-primry-dull: #D63854;
}
*{
font-family: "Outfit", sans-serif;
scrollbar-width: thin;
scrollbar-color: #787777 #1e1e1e;
}
body {
color: white;
background-color: #09090B;
}
.no-scrollbar {
scrollbar-width: none;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
}
22
input::-webkit-calender-picker-indicator {
filter: invert(1);
}
src/[Link]
import { createRoot } from 'react-dom/client'
import './[Link]'
import App from './[Link]'
import { BrowserRouter } from 'react-router-dom'
import { ClerkProvider } from '@clerk/clerk-react'
const PUBLISHABLE_KEY = [Link].VITE_CLERK_PUBLISHABLE_KEY
if (!PUBLISHABLE_KEY) {
throw new Error('Missing Publishable Key')
}
createRoot([Link]('root')).render(
<ClerkProvider publishableKey={PUBLISHABLE_KEY}>
<BrowserRouter>
<App />
</BrowserRouter>
</ClerkProvider>,
)
23
src/[Link]
import React from 'react'
import Navbar from './components/Navbar'
import { Route, Routes, useLocation } from 'react-router-dom'
import Home from './pages/home'
import Movies from './pages/Movies'
import MovieDetails from './pages/MovieDetails'
import SeatLayout from './pages/SeatLayout'
import MyBookings from './pages/MyBookings'
import Favorite from './pages/Favorite'
import Footer from './components/Footer'
import { Toaster } from 'react-hot-toast'
import Layout from './pages/admin/Layout'
import Dashboard from './pages/admin/Dashboard'
import AddShows from './pages/admin/AddShows'
import ListShows from './pages/admin/ListShows'
import ListBookings from './pages/admin/ListBookings'
const App = () => {
const isAdminRoute = useLocation().[Link]('/admin')
return (
<>
<Toaster />
24
{!isAdminRoute && <Navbar/>}
<Routes>
<Route path='/' element={<Home/>} />
<Route path='/movies' element={<Movies/>} />
<Route path='/movies/:id' element={<MovieDetails/>} />
<Route path='/movies/:id/:date' element={<SeatLayout/>} />
<Route path='/my-bookings' element={<MyBookings/>} />
<Route path='/favorite' element={<Favorite/>} />
<Route path='/admin/*' element={<Layout/>}>
<Route index element={<Dashboard/>} />
<Route path='add-shows' element={<AddShows/>} />
<Route path='list-shows' element={<ListShows/>} />
<Route path='list-bookings' element={<ListBookings/>} />
</Route>
</Routes>
{!isAdminRoute && <Footer/>}
</>
)
}
export default App
25
[Link]
export const dateFormat = (date) => {
return new Date(date).toLocaleString('en-US', {
weekday: 'short',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric'
})
}
[Link]
const isoTimeFormat = (dateTime) => {
const date = new Date(dateTime);
const localTime = [Link]('en-US', {
hour: '2-digit',
minute: '2-digit',
hour12: true,
});
return localTime;
}
export default isoTimeFormat
26
[Link]
export const kConverter = (num) => {
if (num => 1000) {
return (num / 1000).toFixed(1) + "k"
} else {
return num;
}
}
[Link]
const timeFormat = (minutes)=>{
const hours = [Link](minutes / 60);
const minutesRemainder = minutes % 60;
return `${hours}h ${minutesRemainder}m`
}
export default timeFormat;
27
[Link]
import React, { useState } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { assets } from '../assets/assets'
import { MenuIcon, SearchIcon, TicketPlus, XIcon } from 'lucide-react'
import { useClerk, UserButton, useUser } from '@clerk/clerk-react'
const Navbar = () => {
const [isOpen, setIsOpen] = useState(false)
const {user} = useUser()
const {openSignIn} = useClerk()
const navigate = useNavigate()
return (
<div className='fixed top-0 left-0 w-full flex items-center justify-between px-6 md:px-36
py-5 z-50'>
<Link to='/' className='max-md:flex-1'>
<img src={[Link]} alt="" className='w-36 h-auto'/>
</Link>
<div className={`max-md:absolute max-md:top-0 max-md:left-0 max-md:font-
medium max-md:text-lg z-50 flex flex-col md:flex-row items-center max-md:justify-center
gap-8 min-md:px-8 py-3 max-md:h-screen min-md:rounded-full backdrop-blur bg-black/70
md:bg-white/10 md:border border-gray-300/20 overflow-hidden transition-[width]
duration-300 ${isOpen ? 'max-md:w-full' : 'max-md:w-0'}`}>
<XIcon className='md:hidden absolute top-6 right-6 w-6 h-6 cursor-pointer'
onClick={()=> setIsOpen(!isOpen)} />
28
<Link onClick={()=> {scrollTo(0,0); setIsOpen(false)}} to='/'>Home</Link>
<Link onClick={()=> {scrollTo(0,0); setIsOpen(false)}} to='/movies'>Movies</Link>
<Link onClick={()=> {scrollTo(0,0); setIsOpen(false)}} to='/'>Theaters</Link>
<Link onClick={()=> {scrollTo(0,0); setIsOpen(false)}} to='/'>Releases</Link>
<Link onClick={()=> {scrollTo(0,0); setIsOpen(false)}} to='/favorite'>Favorites</Link>
</div>
<div className='flex items-center gap-8'>
<SearchIcon className='max-md:hidden w-6 h-6 cursor-pointer' />
{
!user ? (
<button onClick={openSignIn} className='px-4 py-1 sm:px-7 sm:py-2 bg-primary
hover:bg-primry-dull transition rounded-full font-medium cursor-pointer'>Login</button>
):(
<UserButton>
<[Link]>
<[Link] label='My Bookings' labelIcon={<TicketPlus
width={15}/>} onClick={()=> navigate('/my-bookings')} />
</[Link]>
</UserButton>
)
}
</div>
<MenuIcon className='max-md:ml-4 md:hidden w-8 h-8 cursor-pointer' onClick={()=>
setIsOpen(!isOpen)} />
</div>
)
}
29
export default Navbar
30
[Link]
import React from 'react'
import { assets } from '../assets/assets'
import { ArrowRight, CalendarIcon, ClockIcon } from 'lucide-react'
import { useNavigate } from 'react-router-dom'
const HeroSection = () => {
const navigate = useNavigate()
return (
<div className='flex flex-col items-start justify-center gap-4 px-6 md:px-6
lg:px-36 bg-[url("/[Link]")] bg-cover bg-center h-screen'>
<img src={[Link]} alt="" className='max-h-11 lg:h-11 mt-20'/>
<h1 className='text-5xl md:text-[70px] md:leading-18 font-semibold max-
w-110'>Guardians <br /> of the Galaxy </h1>
<div className='flex items-center gap-4 text-gray-300'>
<span>Action | Adventure | Sci-Fi</span>
<div className='flex items-center gap-1'>
<CalendarIcon className='w-4.5 h-4.5'/> 2018
</div>
<div className='flex items-center gap-1'>
<ClockIcon Calendar1Icon className='w-4.5 h-4.5'/> 2h 8m
</div>
31
</div>
<p className='max-w-md text-gray-300'>In a post-apocalyptic world
where cities ride on wheels and consume each other to survive, two people
meet in London and try to stop a conspiracy.</p>
<button onClick={()=>navigate('/movies')} className='flex items-center
gap-1 px-6 py-3 text-sm bg-primary hover:bg-primry-dull transition rounded-full
font-medium cursor-pointer'>
Explore Movies
<ArrowRight className='w-5 h-5' />
</button>
</div>
)
}
export default HeroSection
Nav Bar
32
[Link]
import { ArrowRight } from 'lucide-react'
import React from 'react'
import { useNavigate } from 'react-router-dom'
import BlurCircle from './BlurCircle'
import { dummyShowsData } from '../assets/assets'
import MovieCard from './MovieCard'
const FeaturedSection = () => {
const navigate = useNavigate()
return (
<div className='px-6 md:px-16 lg:px-24 xl:px-44 overflow-hidden'>
<div className='relative flex items-center justify-between pt-20 pb-10'>
<BlurCircle top='0' right='-80px'/>
<p className='text-gray-300 font-medium text-lg'>Now Showing</p>
<button onClick={()=> navigate('/movies')} className='group flex items-
center gap-2 text-sm text-gray-300 cursor-pointer'>
View All
<ArrowRight className='group-hover:translate-x-0.5 transition w-4.5
h-4.5'/>
</button>
</div>
33
<div className='flex flex-wrap justify-center gap-8 mt-8'>
{[Link](0, 5).map((show)=>(
<MovieCard key={show._id} movie={show}/>
))}
</div>
<div className='flex justify-center mt-20'>
<button onClick={()=> {navigate('/movies'); scrollTo(0,0)}}
className='px-10 py-3 text-sm bg-primary hover:bg-primry-dull
transition rounded-md font-medium cursor-pointer'>Show more</button>
</div>
</div>
)
}
export default FeaturedSection
34
[Link]
import { StarIcon } from 'lucide-react'
import React from 'react'
import { useNavigate } from 'react-router-dom'
import timeFormat from '../lib/timeFormat'
const MovieCard = ({movie}) => {
const navigate = useNavigate()
return (
<div className='flex flex-col justify-between p-3 bg-gray-800 rounded-2xl
hover:-translate-y-1 transition duration-300 w-66'>
<img onClick={()=> {navigate(`/movies/${movie._id}`); scrollTo(0,0)}}
src={movie.backdrop_path} alt="" className='rounded-lg h-52 w-full
object-cover object-right-bottom cursor-pointer'/>
<p className='font-semibold mt-2 truncate'>{[Link]}</p>
<p className='text-sm text-gray-400 mt-2'>
{new Date(movie.release_date).getFullYear()} •
{[Link](0,2).map(genre => [Link]).join(" | ")} •
{timeFormat([Link])}
</p>
35
<div className='flex items-center justify-between mt-4 pb-3'>
<button onClick={()=> {navigate(`/movies/${movie._id}`); scrollTo(0,0)}}
className='px-4 py-2 text-xs bg-primary hover:bg-primry-dull transition
rounded-full font-medium cursor-pointer'>Buy Tickets</button>
<p className='flex items-center gap-1 text-sm text-gray-400 mt-1 pr-1'>
<StarIcon className='w-4 h-4 text-primary fill-primary'/>
{movie.vote_average.toFixed(1)}
</p>
</div>
</div>
)
}
export default MovieCard
Movie Card
36
[Link]
import React, { useState } from 'react'
import { dummyTrailers } from '../assets/assets'
import BlurCircle from './BlurCircle'
import ReactPlayer from 'react-player'
import { PlayCircleIcon } from 'lucide-react'
const TrailerSection = () => {
const [currentTrailer, setCurrentTrailer] = useState(dummyTrailers[0])
return (
<div className='px-6 md:px-16 lg:px-24 xl:px-44 py-20 overflow-hidden'>
<p className='text-gray-300 font-medium text-lg max-w-[900px] mx-
auto'>Trailer</p>
<div className='relative mt-6'>
<BlurCircle top='-100px' right='-100px' />
<ReactPlayer url={[Link]} controls={false}
className='mx-auto max-w-full' width="960px" height="540px" />
</div>
<div className='group grid grid-cols-4 gap-4 md:gap-8 mt-8 max-w-3xl mx-
auto'>
{[Link]((trailer)=>(
37
<div key={[Link]} className='relative group-hover:not-
hover:opacity-50 hover:-translate-y-1 duration-300 transition max-md:h-60
md:max-h-60 cursor-pointer' onClick={()=> setCurrentTrailer(trailer)}>
<img src={[Link]} alt="trailer" className='rounded-lg w-full h-
full object-cover brightness-75'/>
<PlayCircleIcon strokeWidth={1.6} className='absolute top-1/2 left-
1/2 w-5 md:w-8 h-8 md:h-12 transform -translate-x-1/2 -translate-y-1/2' />
</div>
))}
</div>
</div>
)
}
export default TrailerSection
38
[Link]
import React from 'react'
import { assets } from '../assets/assets'
const Footer = () => {
return (
<footer className="px-6 md:px-16 lg:px-36 mt-40 w-full text-gray-300">
<div className="flex flex-col md:flex-row justify-between w-full gap-10
border-b border-gray-500 pb-14">
<div className="md:max-w-96">
<img alt="logo" className="w-36 h-auto" src={[Link]} />
<p className="mt-6 text-sm">
Lorem Ipsum has been the industry's standard dummy text ever
since the 1500s, when an unknown printer took a galley of type and scrambled
it to make a type specimen book.
</p>
<div className="flex items-center gap-2 mt-4">
<img src={[Link]} alt="google play" className="h-9 w-
auto" />
<img src={[Link]} alt="app store" className="h-9 w-
auto" />
</div>
</div>
<div className="flex-1 flex items-start md:justify-end gap-20 md:gap-
40">
<div>
39
<h2 className="font-semibold mb-5">Company</h2>
<ul className="text-sm space-y-2">
<li><a href="#">Home</a></li>
<li><a href="#">About us</a></li>
<li><a href="#">Contact us</a></li>
<li><a href="#">Privacy policy</a></li>
</ul>
</div>
<div>
<h2 className="font-semibold mb-5">Get in touch</h2>
<div className="text-sm space-y-2">
<p>+1-234-567-890</p>
<p>contact@[Link]</p>
</div>
</div>
</div>
</div>
<p className="pt-4 text-center text-sm pb-5">
Copyright {new Date().getFullYear()} © QuickShow. All Right Reserved.
</p>
</footer>
)
}
export default Footer
40
[Link]
import React from 'react'
const Loading = () => {
return (
<div className='flex justify-center items-center h-[80vh]'>
<div className='animate-spin rounded-full h-14 w-14 border-2 border-t-
primary'></div>
</div>
)
}
export default Loading
[Link]
const BlurCircle = ({top = "auto", left = "auto", right = "auto", bottom = "auto"}) => {
return (
<div className="absolute -z-50 h-58 w-58 aspect-square rounded-full bg-primary/30
blur-3xl"
style={{top: top, left: left, right: right, bottom: bottom}}>
</div>
)
}
export default BlurCircle
41
[Link]
import React, { useState } from 'react'
import BlurCircle from './BlurCircle'
import { ChevronLeftIcon, ChevronsRightIcon } from 'lucide-react'
import toast from 'react-hot-toast'
import { useNavigate } from 'react-router-dom'
const DateSelect = ({dateTime, id}) => {
const navigate = useNavigate()
const [selected, setSelected] = useState(null)
const onBookHandler = ()=>{
if (!selected) {
return toast('Please select a date')
}
navigate(`/movies/${id}/${selected}`)
scrollTo(0,0)
}
return (
<div id='dateSelect' className='pt-30'>
<div className='flex flex-col md:flex-row items-center justify-between gap-10 relative
p-8 bg-primary/10 border border-primary/20 rounded-lg'>
<BlurCircle top='-100px' left='-100px' />
<BlurCircle top='100px' right='0px' />
42
<div>
<p className='text-lg font-semibold'>Choose Date</p>
<div className='flex items-center gap-6 text-sm mt-5'>
<ChevronLeftIcon width={28} />
<span className='grid grid-cols-3 md:flex flex-wrap md:max-w-lg gap-4'>
{[Link](dateTime).map((date)=>(
<button onClick={()=>setSelected(date)} key={date} className={`flex flex-
col items-center justify-center h-14 w-14 aspect-square rounded cursor-pointer ${selected
=== date ? "bg-primary text-white" : "border border-primary/70"}`}>
<span>{new Date(date).getDate()}</span>
<span>{new Date(date).toLocaleDateString("en-US", {month:
"short"})}</span>
</button>
))}
</span>
<ChevronsRightIcon width={28}/>
</div>
</div>
<button onClick={onBookHandler} className='bg-primary text-white px-8 py-2 mt-6
rounded hover:bg-primary/90 transition-all cursor-pointer'>Book Now</button>
</div>
</div>
)
}
export default DateSelect
43
[Link]
import React from 'react'
import HeroSection from '../components/HeroSection'
import FeaturedSection from '../components/FeaturedSection'
import TrailerSection from '../components/TrailerSection'
const Home = () => {
return (
<>
<HeroSection />
<FeaturedSection />
<TrailerSection />
</>
)
}
export default Home
44
[Link]
import React from 'react'
import { dummyShowsData } from '../assets/assets'
import MovieCard from '../components/MovieCard'
import BlurCircle from '../components/BlurCircle'
const Movies = () => {
return [Link] > 0 ? (
<div className='relative my-40 mb-60 px-6 md:px-16 lg:px-40 xl:px-44
overflow-hidden min-h-[80vh]'>
<BlurCircle top='150px' left='0px' />
<BlurCircle bottom='50px' right='50px' />
<h1 className='text-lg font-medium my-4'>Now Showing</h1>
<div className='flex flex-wrap justify-center gap-8'>
{[Link]((movie)=>(
<MovieCard movie={movie} key={movie._id}/>
))}
</div>
</div>
):(
<div className='flex flex-col items-center justify-center h-screen'>
<h1 className='text-3xl font-bold text-center'>No movies available</h1>
</div>
)
}
45
export default Movies
46
[Link]
import React, { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { dummyDateTimeData, dummyShowsData } from '../assets/assets'
import BlurCircle from '../components/BlurCircle'
import { Heart, PlayCircleIcon, StarIcon } from 'lucide-react'
import timeFormat from '../lib/timeFormat'
import DateSelect from '../components/DateSelect'
import MovieCard from '../components/MovieCard'
import Loading from '../components/Loading'
const MovieDetails = () => {
const navigate = useNavigate()
const {id} = useParams()
const [show, setShow] = useState(null)
const getShow = async ()=>{
const show = [Link](show => show._id === id)
if (show) {
setShow({
movie: show,
dateTime: dummyDateTimeData,
})
}
}
47
useEffect(()=>{
getShow()
}, [id])
return show ? (
<div className='px-6 md:px-16 lg:px-40 pt-30 md:pt-50'>
<div className='flex flex-col md:flex-row gap-8 max-w-6xl mx-auto'>
<img src={[Link].poster_path} alt="" className='max-md:mx-auto
rounded-xl h-104 max-w-70 object-cover' />
<div className='relative flex flex-col gap-3'>
<BlurCircle top='-100px' left='-100px' />
<p className='text-primary'>ENGLISH</p>
<h1 className='text-4xl font-semibold max-w-96 text-
balance'>{[Link]}</h1>
<div className='flex items-center gap-2 text-gray-300'>
<StarIcon className='w-5 h-5 text-primary fill-primary' />
{[Link].vote_average.toFixed(1)} User Rating
</div>
<p className='text-gray-400 mt-2 text-sm leading-tight max-w-
xl'>{[Link]}</p>
<p>
{timeFormat([Link])} • {[Link](genre =>
[Link]).join(", ")} • {[Link].release_date.split("-")[0]}
</p>
48
<div className='flex items-center flex-wrap gap-4 mt-4'>
<button className='flex items-center gap-2 px-7 py-3 text-sm bg-gray-800
hover:bg-gray-900 transition rounded-md font-medium cursor-pointer active:scale-
95'>
<PlayCircleIcon className='w-5 h-5' />
Watch Trailer
</button>
<a href="#dateSelect" className='px-10 py-3 text-sm bg-primary hover:bg-
primry-dull transition rounded-md font-medium cursor-pointer active:scale-95'>Buy
Ticket</a>
<button className='bg-gray-700 p-2.5 rounded-full transition cursor-pointer
active:scale-95'>
<Heart className= {`w-5 h-5`} />
</button>
</div>
</div>
</div>
<p className='text-lg font-medium mt-20'>Your Favorite Cast</p>
<div className='overflow-x-auto no-scrollbar mt-8 pb-4'>
<div className='flex items-center gap-4 w-max px-4'>
{[Link](0,12).map((cast, index)=>(
<div key={index} className='flex flex-col items-center text-center'>
<img src={cast.profile_path} alt="" className='rounded-full h-20 md:h-20
aspect-square object-cover'/>
<p className='font-medium text-xs mt-3'>{[Link]}</p>
</div>
49
))}
</div>
</div>
<DateSelect dateTime={[Link]} id={id} />
<p className='text-lg font-medium mt-20 mb-8'>You May Also Like</p>
<div className='flex flex-wrap justify-center gap-8'>
{[Link](0,4).map((movie, index)=> (
<MovieCard key={index} movie={movie} />
))}
</div>
<div className='flex justify-center mt-20'>
<button onClick={()=> {navigate('/movies'); scrollTo(0,0)}}
className='px-10 py-3 text-sm bg-primary hover:bg-primry-dull transition
rounded-md font-medium cursor-pointer'>Show More</button>
</div>
</div>
) : <Loading />
}
export default MovieDetails
50
[Link]
import React from 'react'
import { dummyShowsData } from '../assets/assets'
import MovieCard from '../components/MovieCard'
import BlurCircle from '../components/BlurCircle'
const Favorite = () => {
return [Link] > 0 ? (
<div className='relative my-40 mb-60 px-6 md:px-16 lg:px-40 xl:px-44 overflow-hidden
min-h-[80vh]'>
<BlurCircle top='150px' left='0px' />
<BlurCircle bottom='50px' right='50px' />
<h1 className='text-lg font-medium my-4'>Your Favorite Movies</h1>
<div className='flex flex-wrap justify-center gap-8'>
{[Link]((movie)=>(
<MovieCard movie={movie} key={movie._id}/>
))}
</div>
</div>
):(
<div className='flex flex-col items-center justify-center h-screen'>
<h1 className='text-3xl font-bold text-center'>No movies available</h1>
</div>
)
}
export default Favorite
51
[Link]
import React, { useEffect, useState } from 'react'
import { dummyBookingData } from '../assets/assets'
import Loading from '../components/Loading'
import BlurCircle from '../components/BlurCircle'
import timeFormat from '../lib/timeFormat'
import { dateFormat } from '../lib/dateFormat'
const MyBookings = () => {
const currency = [Link].VITE_CURRENCY
const [bookings, setBookings] = useState([])
const [isLoading, setIsLoading] = useState(true)
const getMyBookings = async () =>{
setBookings(dummyBookingData)
setIsLoading(false)
}
useEffect(()=>{
getMyBookings()
},[])
return !isLoading ?(
52
<div className='relative px-6 md:px-16 lg:px-40 pt-30 md:pt-40 min-h-
[80vh]'>
<BlurCircle top='100px' left='100px' />
<div>
<BlurCircle bottom='0px' left='600px' />
</div>
<h1 className='text-lg font-semibold mb-4'>My Bookings</h1>
{[Link]((item, index)=>(
<div key={index} className='flex flex-col md:flex-row justify-between bg-
primary/8 border-primary/20 rounded-lg mt-4 p-2 max-w-3xl'>
<div className='flex flex-col md:flex-row'>
<img src={[Link].poster_path} alt="" className='md:max-w-
45 aspect-video h-auto object-cover object-bottom rounded' />
<div className='flex flex-col p-4'>
<p className='text-lg font-semibold'>{[Link]}</p>
<p className='text-gray-400 text-
sm'>{timeFormat([Link])}</p>
<p className='text-gray-400 text-sm mt-
auto'>{dateFormat([Link])}</p>
</div>
</div>
<div className='flex flex-col md:items-end md:text-right justify-between
p-4'>
<div className='flex items-center gap-4'>
<p className='text-2xl font-semibold mb-
3'>{currency}{[Link]}</p>
53
{![Link] && <button className='bg-primary px-4 py-1.5 mb-3 text-
sm rounded-full font-medium cursor-pointer'>Pay Now</button>}
</div>
<div className='text-sm'>
<p>
<span className='text-gray-400'>Total Tickets
</span>{[Link]}
</p>
<p><span className='text-gray-400'>Seat Number
</span>{[Link](", ")}</p>
</div>
</div>
</div>
))}
</div>
) : <Loading />
}
export default MyBookings
54
[Link]
import React, { useEffect, useState } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { assets, dummyDateTimeData, dummyShowsData } from '../assets/assets'
import Loading from '../components/Loading'
import { ArrowRightIcon, ClockIcon } from 'lucide-react'
import isoTimeFormat from '../lib/isoTimeFormat'
import BlurCircle from '../components/BlurCircle'
import toast from 'react-hot-toast'
const SeatLayout = () => {
const groupRows = [["A", "B"], ["C", "D"], ["E", "F"], ["G", "H"], ["I", "J"]]
const {id, date } = useParams()
const [selectedSeats, setSelectedSeats] = useState([])
const [selectedTime, setSelectedTime] = useState(null)
const [show, setShow] = useState(null)
const navigate = useNavigate()
const getShow = async ()=>{
const show = [Link](show => show._id === id)
if (show) {
setShow({
movie: show,
dateTime: dummyDateTimeData
55
})
}
}
const handleSeatClick = (seatId) =>{
if (!selectedTime) {
return toast("Please select time first")
}
if ( && [Link] > 4) {
return toast("You can only select 5 seats")
}
setSelectedSeats(prev => [Link](seatId) ? [Link](seat => seat !== seatId) :
[...prev, seatId])
}
const renderSeats = (row, count = 9)=>(
<div key={row} className='flex gap-2 mt-2'>
<div className='flex flex-wrap items-center justify-center gap-2'>
{[Link]({ length: count }, (_, i) => {
const seatId = `${row}${i + 1}`;
return (
<button key={seatId} onClick={() => handleSeatClick(seatId)} className={`h-8 w-8
rounded border border-primary/60 cursor-pointer ${[Link](seatId) && "bg-
primary text-white"}`}>
{seatId}
</button>
);
})}
</div>
</div>
56
)
useEffect(()=>{
getShow()
},[])
return show ? (
<div className='flex flex-col md:flex-row px-6 md:px-16 lg:px-40 py-30 md:pt-50'>
{/* Available Timings */}
<div className='w-60 bg-primary/10 border border-primary/20 rounded-lg py-10 h-
max md:sticky md:top-30'>
<p className='text-lg font-semibold px-6'>Available Timings</p>
<div className='mt-5 space-y-1'>
{[Link][date].map((item)=>(
<div key={[Link]} onClick={()=> setSelectedTime(item)} className={`flex items-
center gap-2 px-6 py-2 w-max rounded-r-md cursor-pointer transition ${selectedTime?.time
=== [Link] ? "bg-primary text-white" : "hover:bg-primary/20"}`}>
<ClockIcon className='w-4 h-4'/>
<p className='text-sm'>{isoTimeFormat([Link])}</p>
</div>
))}
</div>
</div>
{/* Seat Layout */}
<div className='relative flex-1 flex flex-col items-center max-md:mt-16'>
<BlurCircle top='-100px' left='-100px'/>
<BlurCircle bottom='0px' right='0px'/>
<h1 className='text-2xl font-semibold mb-4'>Select Your Seat</h1>
57
<img src={[Link]} alt="screen" />
<p className='text-gray-400 text-sm mb-6'>SCREEN SIDE</p>
<div className='flex flex-col items-center mt-10 text-xs text-gray-300'>
<div className='grid grid-cols-2 md:grid-cols-1 gap-8 md:gap-2 mb-6'>
{groupRows[0].map(row => renderSeats(row))}
</div>
<div className='grid grid-cols-2 gap-11'>
{[Link](1).map((group, idx)=>(
<div key={idx}>
{[Link](row => renderSeats(row))}
</div>
))}
</div>
</div>
<button onClick={()=> navigate('/my-bookings')} className='flex items-center gap-1
mt-20 px-10 py-3 text-sm bg-primary hover:bg-primry-dull transition rounded-full font-
medium cursor-pointer active:scale-95'>
Proceed to Checkout
<ArrowRightIcon strokeWidth={3} className='w-4 h-4' />
</button>
</div>
</div>
):(
<Loading />
)
}
58
export default SeatLayout
59
ADMIN SECTION
[Link]
import React from 'react'
import { Link } from 'react-router-dom'
import { assets } from '../../assets/assets'
const AdminNavbar = () => {
return (
<div className='flex items-center justify-between px-6 md:px-10 h-16
border-b border-gray-300/30'>
<Link to="/">
<img src={[Link]} alt="logo" className='w-36 h-auto'/>
</Link>
</div>
)
}
export default AdminNavbar
60
[Link]
import React, { use } from 'react'
import { assets } from '../../assets/assets'
import { LayoutDashboardIcon, ListCollapseIcon, ListIcon, PlusSquareIcon } from 'lucide-
react'
import { NavLink } from 'react-router-dom'
const AdminSidebar = () => {
const user = {
firstName: 'Admin',
lastName: 'User',
imageurl: [Link],
}
const adminNavlinks = [
{name: 'Dashboard', path: '/admin', icon: LayoutDashboardIcon},
{name: 'Add Shows', path: '/admin/add-shows', icon: PlusSquareIcon},
{name: 'List Shows', path: '/admin/list-shows', icon: ListIcon},
{name: 'List Bookings', path: '/admin/list-bookings', icon: ListCollapseIcon},
]
return (
<div className='h-[calc(100vh-64px)] md:flex flex-col items-center pt-8 max-w-13
md:max-w-60 w-full border-r border-gray-300/20 text-sm' >
<img className='h-9 md:h-14 w-9 md:w-14 rounded-full mx-auto' src={[Link]}
alt="sidebar" />
<p className='mt-2 text-base max-md:hidden'>{[Link]} {[Link]}</p>
61
<div className='w-full'>
{[Link]((link, index)=>(
<NavLink key={index} to={[Link]} end className={({ isActive }) => `relative flex
items-center max-md:justify-center gap-2 w-full py-2.5 min-md:pl-10 first:mt-6 text-gray-
400 ${isActive && 'bg-primary/15 text-primary group'}`}>
{({ isActive })=>(
<>
<[Link] className='w-5 h-5'/>
<p className='max-md:hidden'>{[Link]}</p>
<span className={`w-1.5 h-10 rounded-l right-0 absolute ${isActive && 'bg-
primary'}`} />
</>
)}
</NavLink>
))}
</div>
</div>
)
}
export default AdminSidebar
62
[Link]
import React from 'react'
const Title = ({ text1, text2 }) => {
return (
<div>
<h1 className='font-medium text-2xl'>
{text1} <span className='underline text-primary'>
{text2}</span>
</h1>
</div>
)
}
export default Title
63
[Link]
import { ChartLineIcon, CircleDollarSignIcon, PlayCircleIcon, StarIcon, UserIcon } from
'lucide-react';
import React, { useEffect, useState } from 'react'
import { dummyDashboardData } from '../../assets/assets';
import Loading from '../../components/Loading';
import Title from '../../components/admin/Title';
import BlurCircle from '../../components/BlurCircle';
import { dateFormat } from '../../lib/dateFormat';
const Dashboard = () => {
const currency = [Link].VITE_CURRENCY
const [dashboardData, setDashboardData] = useState({
totalBookings: 0,
totalRevenue: 0,
activeShows: [],
totalUser: 0
});
const [loading, setLoading] = useState(true);
const dashboardCards = [
{ title: "Total Bookings", value: [Link] || "0", icon:
ChartLineIcon },
{ title: "Total Revenue", value: currency + [Link] || "0", icon:
CircleDollarSignIcon },
64
{ title: "Acitve Shows", value: [Link] || "0", icon:
PlayCircleIcon },
{ title: "Active Users", value: [Link] || "0", icon: UserIcon }
]
const fetchDashboardData = async () => {
setDashboardData(dummyDashboardData)
setLoading(false)
};
useEffect(() => {
fetchDashboardData();
}, []);
return !loading ? (
<>
<Title text1="Admin" text2="Dashboard" />
<div className='relative flex flex-wrap gap-4 mt-6'>
<BlurCircle top='-100px' left='0'/>
<div className='flex flex-wrap gap-4 w-full'>
{[Link]((card, index) => (
<div key={index} className='flex items-center justify-between px-4 py-3 bg-
primary/10 border border-primary/20 rounded-md max-w-50 w-full'>
<div>
<h1 className='text-sm'>{[Link]}</h1>
<p className='text-xl font-medium mt-1'>{[Link]}</p>
</div>
<[Link] className='w-6 h-6' />
65
</div>
))}
</div>
</div>
<p className='mt-10 text-lg font-medium'>Active Shows</p>
<div className='relative flex flex-wrap gap-6 mt-4 max-w-5xl'>
<BlurCircle top='100px' left='-10%' />
{[Link]((show) => (
<div key={show._id} className='w-55 rounded-lg overflow-hidden h-full pb-3 bg-
primary/10 border border-primary/20 hover:-translate-y-1 transition duration-300'>
<img src={[Link].poster_path} alt="" className='h-60 w-full object-cover'
/>
<p className='font-medium p-2 truncate'>{[Link]}</p>
<div className='flex items-center justify-between px-2'>
<p className='text-lg font-medium'>{currency} {[Link]}</p>
<p className='flex items-center gap-1 text-sm to-gray-400 mt-1 pr-1'>
<StarIcon className='w-4 h-4 text-primary fill-primary' />
{[Link].vote_average.toFixed(1)}
</p>
</div>
<p className='px-2 pt-2 text-sm text-gray-
500'>{dateFormat([Link])}</p>
</div>
))}
</div>
</>
) : <Loading />
}
export default Dashboard
66
[Link]
import React, { useEffect, useState } from 'react'
import Title from '../../components/admin/Title'
import { dummyShowsData } from '../../assets/assets';
import Loading from '../../components/Loading';
import { CheckIcon, DeleteIcon, StarIcon } from 'lucide-react';
import { kConverter } from '../../lib/kConverter';
const AddShows = () => {
const currency = [Link].VITE_CURRENCY
const [nowPlayingMovies, setNowPlayingMovies] = useState([]);
const [selectMovie, setSelectMovie] = useState(true);
const [dateTimeSelection, setDateTimeSelection] = useState({});
const [dateTimeInput, setDateTimeInput] = useState("");
const [showPrice, setShowPrice] = useState("");
const fetchNowPlayingMovies = async => {
setNowPlayingMovies(dummyShowsData)
};
const handleDateTimeAdd = () => {
if (!dateTimeInput) return;
const [date, time] = [Link]("T");
if (!date || !time) return;
67
setDateTimeSelection((prev) => {
const times = prev[date] || [];
if () {
return {...prev, [date]: [...times, time] };
}
return prev;
});
};
const handleRemoveTime = (date, time) => {
setDateTimeSelection((prev) => {
const filteredTimes = prev[date].filter((t) => t !== time);
if ([Link] === 0) {
const { [date]: _, ...rest } = prev;
return rest;
}
return {
...prev,
[date]: filteredTimes,
};
});
};
useEffect(() => {
fetchNowPlayingMovies();
}, []);
return [Link] > 0 ? (
<>
68
<Title text1="Add" text2="Shows" />
<p className='mt-10 text-lg font-medium'>Now Playing Movies</p>
<div className='overflow-x-auto pb-4'>
<div className='group flex flex-wrap gap-4 mt-4 w-max'>
{[Link]((movie) =>(
<div onClick={() => setSelectMovie([Link])} key={[Link]}
className={`relative max-w-40 cursor-pointer group-hover:not-hover:opacity-40 hover:-
translate-y-1 transition duration-300`}>
<div className='relative rounded-lg overflow-hidden'>
<img src={movie.poster_path} alt="" className='w-full object-cover
brightness-90' />
<div className='text-sm flex items-center justify-between p-2 bg-black/70
w-full absolute bottom-0 left-0'>
<p className='flex items-center gap-1 to-gray-400'>
<StarIcon className='w-4 h-4 text-primary fill-primary' />
{movie.vote_average.toFixed(1)}
</p>
<p className='to-gray-300'>{kConverter(movie.vote_count)} Votes</p>
</div>
</div>
{selectMovie === [Link] && (
<div className='absolute top-2 right-2 flex items-center justify-center bg-
primary h-6 w-6 rounded'>
<CheckIcon className='w-4 h-4 text-white' strokeWidth={2.5} />
</div>
)}
<p className='font-medium truncate'>{[Link]}</p>
<p className='text-gray-400 text-sm'>{movie.release_date}</p>
</div>
))}
69
</div>
</div>
{/* Show Price Input */}
<div className='mt-8'>
<label className='block text-sm font-medium mb-2'>Show Price</label>
<div className='inline-flex items-center gap-2 border border-gray-600 px-3 py-2
rounded-md'>
<p className='text-gray-400 text-sm'>{currency}</p>
<input min={0} type="number" value={showPrice} onChange={(e) =>
setShowPrice([Link])} placeholder='Enter show price' className='outline-none' />
</div>
</div>
{/* Date & Time Selection */}
<div className='mt-6'>
<label className='block text-sm font-medium mb-2'>Select Date & Time</label>
<div className='inline-flex gap-5 border border-gray-600 p-1 pl-3 rounded-lg'>
<input type="datetime-local" value={dateTimeInput} onChange={(e) =>
setDateTimeInput([Link])} className='outline-none rounded-md'/>
<button onClick={handleDateTimeAdd} className='bg-primary/80 text-white px-3
py-2 text-sm rounded-lg hover:bg-primary cursor-pointer'>Add Time</button>
</div>
</div>
{/* Display Selected Time */}
{[Link](dateTimeSelection).length > 0 && (
<div className='mt-6'>
<h2 className='mb-2'>Selected Date-Time</h2>
<ul className='space-y-3'>
{[Link](dateTimeSelection).map(([date, times]) =>(
70
<li key={date}>
<div className='font-medium'>{date}</div>
<div className='flex flex-wrap gap-2 mt-1 text-sm'>
{[Link]((time) => (
<div key={time} className='border border-primary px-2 py-1 flex
items-center rounded'>
<span>{time}</span>
<DeleteIcon onClick={() => handleRemoveTime(date, time)}
width={15} className='ml-2 text-red-500 hover:text-red-700 cursor-pointer' />
</div>
))}
</div>
</li>
))}
</ul>
</div>
)}
<button className='bg-primary text-white px-8 py-2 mt-6 rounded hover:bg-
primary/90 transition-all cursor-pointer'>Add Show</button>
</>
) : <Loading />
}
export default AddShows
71
[Link]
import React from 'react'
import AdminNavbar from '../../components/admin/AdminNavBar'
import AdminSidebar from '../../components/admin/AdminSidebar'
import { Outlet } from 'react-router-dom'
const Layout = () => {
return (
<>
<AdminNavbar />
<div className='flex'>
<AdminSidebar />
<div className='flex-1 px-4 py-10 md:px-10 h-[calc(100vh-64px)] overflow-y-auto'>
<Outlet />
</div>
</div>
</>
)
}
export default Layout
72
[Link]
import React, { useEffect, useState } from 'react'
import { dummyBookingData } from '../../assets/assets';
import Loading from '../../components/Loading';
import Title from '../../components/admin/Title';
import { dateFormat } from '../../lib/dateFormat';
const ListBookings = () => {
const currency = [Link].VITE_CURRENCY
const [bookings, setBookings] = useState([]);
const [isloading, setIsLoading] = useState(true);
const getAllBookings = async () => {
setBookings(dummyBookingData)
setIsLoading(false)
};
useEffect(() => {
getAllBookings();
}, [])
return !isloading ? (
<>
<Title text1="List" text2="Bookings" />
<div className='max-w-4xl mt-6 overflow-x-auto'>
<table className='w-full border-collapse rounded-md overflow-hidden text-
nowrap'>
<thead>
73
<tr className='bg-primary/20 text-left text-white'>
<th className='p-2 font-medium pl-5'>User Name</th>
<th className='p-3 font-medium'>Movie Name</th>
<th className='p-3 font-medium'>Show Time</th>
<th className='p-3 font-medium'>Seats</th>
<th className='p-3 font-medium'>Amount</th>
</tr>
</thead>
<tbody className='text-sm font-light'>
{[Link]((item, index) => (
<tr key={index} className='border-b border-primary/20 bg-primary/5
even:bg-primary/10'>
<td className='p-2 min-w-45 pl-5'>{[Link]}</td>
<td className='p-2'>{[Link]}</td>
<td className='p-2'>{dateFormat([Link])}</td>
<td className='p-2'>{[Link]([Link]).map(seat =>
[Link][seat]).join(", ")}</td>
<td className='p-2'>{currency} {[Link]}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
) : <Loading />
}
export default ListBookings
74
[Link]
import React, { useEffect, useState } from 'react'
import { dummyShowsData } from '../../assets/assets';
import Title from '../../components/admin/Title';
import Loading from '../../components/Loading';
import { dateFormat } from '../../lib/dateFormat';
const ListShows = () => {
const currency = [Link].VITE_CURRENCY
const [shows, setShows] = useState([]);
const [loading, setLoading] = useState(true);
const getAllShows = async () =>{
try {
setShows([{
movie: dummyShowsData[0],
showDateTime: "2025-06-30T02:30:00.000Z",
showPrice: 59,
occupiedSeats: {
A1: "user_1",
B1: "user_2",
C1: "user_3",
}
}]);
75
setLoading(false);
} catch (error) {
[Link](error);
}
}
useEffect(() => {
getAllShows();
}, []);
return !loading ? (
<>
<Title text1="List" text2="Shows" />
<div className='max-w-4xl mt-6 overflow-x-auto'>
<table className='w-full border-collapse rounded-md overflow-hidden text-
nowrap'>
<thead>
<tr className='bg-primary/20 text-left text-white'>
<th className='p-2 font-medium pl-5'>Movie Name</th>
<th className='p-2 font-medium'>Show Time</th>
<th className='p-2 font-medium'>Total Bookings</th>
<th className='p-2 font-medium'>Earnings</th>
</tr>
</thead>
<tbody className='text-sm font-light'>
{[Link]((show, index) => (
<tr key={index} className='border-b border-primary/10 bg-primary/5
even:bg-primary/10'>
76
<td className='p-2 min-w-45 pl-5'>{[Link]}</td>
<td className='p-2'>{dateFormat([Link])}</td>
<td className='p-2'>{[Link]([Link]).length}</td>
<td className='p-2'>{currency}
{[Link]([Link]).length * [Link]}</td>
</tr>
))}
</tbody>
</table>
</div>
</>
) : <Loading />
}
export default ListShows
77
OUTPUT :
Nav Bar
Login Button
Home Page
Features Page
78
Trailer Page
Footer
79
Login & SignUp
Options
My Bookings
Manage Account
Signout
Multiple User User Profile
Account Add
80
Movie Details
Choose Booking Date
81
82
83
84
Payment Page
85
CONCLUSION
The Movie Ticket Booking Web Application, developed under the project name
QuickShow, was successfully designed, developed, and tested by a team of five members
as the final year project. The project set out to solve a real-world problem faced by
moviegoers and cinema administrators by providing a complete, reliable, and easy-to-use
online platform for booking movie tickets. Upon completion, it can be confidently stated
that all the objectives defined at the beginning of the project have been achieved.
The application provides users with a seamless end-to-end ticket booking experience. From
creating a personal account and browsing currently showing movies, to selecting preferred
seats from an interactive cinema hall layout, making a secure online payment, and receiving
an instant booking confirmation — every step of the process has been carefully designed
and implemented to be as smooth and intuitive as possible. Users can also revisit their
complete booking history at any time through their personal dashboard, giving them full
visibility over all their past and upcoming reservations.
The development process was a valuable learning experience for the entire team. Working
on a real-world application from scratch gave each team member practical exposure to the
complete software development lifecycle. The use of Git and GitHub for version control
taught the team how to collaborate effectively on a shared codebase in a professional
manner.
In conclusion, QuickShow is a fully functional, well-structured, and scalable web application
that successfully meets all its defined objectives. It stands as a strong demonstration of the
knowledge and skills gained throughout the undergraduate program.
REFERENCES
YouTube – [Link]
GeeksforGeeks – [Link]
[Link] Official Documentation – [Link]
React Official Documentation – [Link]
Clerk – [Link]
MongoDB Official Documentation – [Link]
[Link] Official Documentation – [Link]
Tailwind CSS Official Documentation – [Link]
MDN Web Docs – [Link]
Vite Official Documentation – [Link]
86