Last active
October 11, 2025 09:02
-
-
Save amritmaurya1504/ac6f351d0bbfbd92d6dc6f68af3bb535 to your computer and use it in GitHub Desktop.
bookMyScreen(utils)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { Types } from "mongoose"; | |
| import { IMovie } from "../modules/movie/movie.interface"; | |
| import { IShow } from "../modules/show/show.interface"; | |
| import { ITheatre } from "../modules/theater/theater.interface"; | |
| type GroupedShow = { | |
| movie: Types.ObjectId | IMovie; | |
| theater: { | |
| theaterDetails: Types.ObjectId | ITheatre; | |
| shows: Array<{ | |
| _id: string; | |
| date: string; | |
| startTime: string; | |
| format: string; | |
| audioType: string; | |
| }>; | |
| }; | |
| }; | |
| export const isValidEmail = (email: string): boolean => { | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| return emailRegex.test(email); | |
| }; | |
| export const generateSeatLayout = () => { | |
| return [ | |
| { | |
| row: "E", | |
| type: "PREMIUM", | |
| price: 510, | |
| seats: Array.from({ length: 10 }, (_, i) => ({ | |
| number: i + 1, | |
| status: "AVAILABLE", | |
| })), | |
| }, | |
| { | |
| row: "D", | |
| type: "EXECUTIVE", | |
| price: 290, | |
| seats: Array.from({ length: 20 }, (_, i) => ({ | |
| number: i + 1, | |
| status: "AVAILABLE", | |
| })), | |
| }, | |
| { | |
| row: "C", | |
| type: "EXECUTIVE", | |
| price: 290, | |
| seats: Array.from({ length: 20 }, (_, i) => ({ | |
| number: i + 1, | |
| status: "AVAILABLE", | |
| })), | |
| }, | |
| { | |
| row: "B", | |
| type: "EXECUTIVE", | |
| price: 290, | |
| seats: Array.from({ length: 20 }, (_, i) => ({ | |
| number: i + 1, | |
| status: "AVAILABLE", | |
| })), | |
| }, | |
| { | |
| row: "A", | |
| type: "NORMAL", | |
| price: 180, | |
| seats: Array.from({ length: 20 }, (_, i) => ({ | |
| number: i + 1, | |
| status: "AVAILABLE", | |
| })), | |
| }, | |
| ]; | |
| }; | |
| // Grouping function | |
| export const groupShowsByTheatreAndMovie = (shows: IShow[]): GroupedShow[] => { | |
| const grouped: Record<string, GroupedShow> = {}; | |
| shows.forEach((show) => { | |
| const movieId = show.movie._id; | |
| const theatreId = show.theatre._id; | |
| const key = `${movieId}_${theatreId}`; | |
| if (!grouped[key]) { | |
| grouped[key] = { | |
| movie: show.movie, | |
| theater: { | |
| theaterDetails: show.theatre, | |
| shows: [], | |
| }, | |
| }; | |
| } | |
| grouped[key].theater.shows.push({ | |
| _id: show._id ?? "", | |
| date: show.date ?? "", | |
| startTime: show.startTime ?? "", | |
| format: show.format ?? "", | |
| audioType: show.audioType ?? "", | |
| }); | |
| }); | |
| return Object.values(grouped); | |
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import Header from "../components/seat-layout/Header"; | |
| import Footer from "../components/seat-layout/Footer"; | |
| import { useParams } from "react-router-dom"; | |
| import { keepPreviousData, useQuery } from "@tanstack/react-query"; | |
| import { getShowById } from "../apis/index"; | |
| import screenImg from "../assets/screen.png"; | |
| const Seat = ({ seat, row }) => { | |
| const seatId = `${row}${seat.number}`; | |
| return ( | |
| <button | |
| className={`w-9 h-9 m-[2px] rounded-lg border text-sm | |
| ${ | |
| "hover:bg-gray-100 border-black cursor-pointer" | |
| }`} | |
| disabled={seat.status === "occupied"} | |
| > | |
| {seat.status === "occupied" ? "X" : seat.number} | |
| </button> | |
| ); | |
| }; | |
| const SeatLayout = () => { | |
| const { showId } = useParams(); | |
| const { | |
| data: showData, | |
| isLoading, | |
| isError, | |
| } = useQuery({ | |
| queryKey: ["show", showId], | |
| queryFn: async () => await getShowById(showId), | |
| placeholderData: keepPreviousData, | |
| enabled: !!showId, | |
| select: (res) => res.data, | |
| }); | |
| console.log(showData); | |
| return ( | |
| <> | |
| <div className="h-screen overflow-y-hidden"> | |
| {/* Fixed Header */} | |
| <div className="fixed top-0 left-0 w-full z-10"> | |
| <Header showData={showData} /> | |
| </div> | |
| {/* Scrollable Seat Layout */} | |
| <div className="max-w-7xl mx-auto mt-[210px] px-6 pb-4 bg-white h-[calc(100vh-320px)] overflow-y-scroll scrollbar-hide"> | |
| <div className="flex flex-col items-center justify-center"> | |
| {showData?.seatLayout && ( | |
| <div className="flex flex-col items-center justify-center"> | |
| {Object.entries( | |
| showData.seatLayout.reduce((acc, curr) => { | |
| if (!acc[curr.type]) | |
| acc[curr.type] = { price: curr.price, rows: [] }; | |
| acc[curr.type].rows.push(curr); | |
| return acc; | |
| }, {}) | |
| ).map(([type, { price, rows }]) => ( | |
| <div | |
| key={type} | |
| className="mb-12 w-full flex flex-col items-center justify-center" | |
| > | |
| <h2 className="text-center font-semibold text-lg mb-4"> | |
| {type} : βΉ{price} | |
| </h2> | |
| <div className="space-y-2"> | |
| {rows.map((rowObj) => ( | |
| <div key={rowObj.row} className="flex items-center"> | |
| <div className="w-6 text-right mr-2 text-sm text-gray-600"> | |
| {rowObj.row} | |
| </div> | |
| <div className="flex flex-wrap gap-1"> | |
| {rowObj.seats.map((seat, i) => ( | |
| <Seat | |
| key={i} | |
| seat={seat} | |
| row={rowObj.row} | |
| /> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| </div> | |
| ))} | |
| </div> | |
| )} | |
| <div className="flex justify-center mt-5"> | |
| <img | |
| src={screenImg} // or "/screen.png" if in public | |
| alt="Screen" | |
| className="w-[300px] md:w-[400px] object-contain opacity-80" | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| {/* Fixed Footer */} | |
| <div className="fixed bottom-0 left-0 w-full h-[100px] bg-white border-t border-gray-200 py-4 px-4 z-10"> | |
| <Footer /> | |
| </div> | |
| </div> | |
| </> | |
| ); | |
| }; | |
| export default SeatLayout; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // File: src/modules/movie/movie.seed.ts | |
| import mongoose from "mongoose"; | |
| import { MovieModel } from "../modules/movie/movie.model"; | |
| import { config } from "../config/config"; | |
| const movies = [ | |
| { | |
| title: "Maa", | |
| genre: ["Fantasy", "Horror", "Mythological", "Thriller"], | |
| rating: 7.2, | |
| votes: 2700, | |
| languages: ["Hindi"], | |
| certification: "UA16+", | |
| duration: "2h 15m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790461/jn7silixkmp7caq0gpwr.avif", | |
| releaseDate: new Date("2025-06-27"), | |
| description: | |
| "The story of a mother who becomes Kali to end a demonic curse rooted in fear, blood, and betrayal.", | |
| }, | |
| { | |
| title: "Kannappa", | |
| genre: ["Action", "Mythological"], | |
| rating: 7.3, | |
| votes: 10700, | |
| languages: ["Telugu", "Hindi", "Tamil", "Malayalam"], | |
| certification: "UA13+", | |
| duration: "2h 30m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790461/fkbk6wzzxrvbn3ysrums.avif", | |
| releaseDate: new Date("2025-08-01"), | |
| description: "The tale of Kannappa, a devoted follower of Lord Shiva.", | |
| }, | |
| { | |
| title: "Mission: Impossible - The Final Reckoning", | |
| genre: ["Action", "Thriller"], | |
| rating: 8.6, | |
| votes: 84100, | |
| languages: ["English", "Hindi", "Telugu", "Tamil"], | |
| certification: "UA13+", | |
| duration: "2h 40m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790462/yomilxtf8umhsqekxzvv.avif", | |
| releaseDate: new Date("2025-07-18"), | |
| description: | |
| "Ethan Hunt returns for a high-stakes mission to save the world from impending doom.", | |
| }, | |
| { | |
| title: "F1: The Movie", | |
| genre: ["Sports", "Documentary"], | |
| rating: 9.5, | |
| votes: 6800, | |
| languages: ["English", "Hindi", "Tamil", "Telugu"], | |
| certification: "UA16+", | |
| duration: "2h", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790461/psdublbrlv4crojvtzqc.avif", | |
| releaseDate: new Date("2025-07-10"), | |
| description: | |
| "An inside look at the world of Formula 1 racing and its iconic champions.", | |
| format: ["2D", "3D", "IMAX 3D"] | |
| }, | |
| { | |
| title: "From the World of John Wick: Ballerina", | |
| genre: ["Action", "Thriller"], | |
| rating: 8.7, | |
| votes: 15200, | |
| languages: ["English"], | |
| certification: "A", | |
| duration: "2h 10m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790461/pdw9hxw1xlz1abpyenzc.avif", | |
| releaseDate: new Date("2025-07-25"), | |
| description: | |
| "A ballerina assassin seeks revenge in the dark world of the High Table.", | |
| }, | |
| { | |
| title: "Metro In Dino", | |
| genre: ["Romance", "Drama"], | |
| rating: 7.5, | |
| votes: 8600, | |
| languages: ["Hindi"], | |
| certification: "UA", | |
| duration: "2h 10m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751826680/u4vtkrc4iinsiyjwqrsu.avif", | |
| releaseDate: "2025-09-02", | |
| description: | |
| "Multiple stories of love and life intertwine in the bustling metro city of Mumbai.", | |
| }, | |
| { | |
| title: "How to Train Your Dragon: Return of Night Fury", | |
| genre: ["Animation", "Fantasy", "Adventure"], | |
| rating: 8.8, | |
| votes: 32500, | |
| languages: ["English", "Hindi"], | |
| certification: "UA", | |
| duration: "1h 45m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751826680/lkpu6rs2rxu4jckxtony.avif", | |
| releaseDate: "2025-09-10", | |
| description: | |
| "Hiccup and Toothless return for a magical journey as a new Night Fury rises.", | |
| format: ["2D", "3D"] | |
| }, | |
| { | |
| title: "Jurassic Park: Rebirth", | |
| genre: ["Sci-Fi", "Adventure", "Action"], | |
| rating: 9.0, | |
| votes: 60500, | |
| languages: ["English", "Hindi", "Tamil", "Telugu"], | |
| certification: "UA16+", | |
| duration: "2h 35m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790815/kw1gearclw4vjmnkxw0o.avif", | |
| releaseDate: "2025-09-01", | |
| description: | |
| "Dinosaurs return in a world no longer in control β the race for survival begins anew.", | |
| format: ["2D", "3D", "IMAX 3D"] | |
| }, | |
| { | |
| title: "Sitaare Zameen Par", | |
| genre: ["Drama", "Family"], | |
| rating: 8.5, | |
| votes: 39600, | |
| languages: ["Hindi"], | |
| certification: "UA", | |
| duration: "2h 20m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790462/huw3x0efjerh3zxoqtaq.avif", | |
| releaseDate: "2025-07-12", | |
| description: | |
| "A heartwarming story of a teacher who helps a dyslexic child discover the star within.", | |
| }, | |
| { | |
| title: "M3GAN 2.0", | |
| genre: ["Horror", "Sci-Fi", "Thriller"], | |
| rating: 8.4, | |
| votes: 117, | |
| languages: ["English", "Hindi"], | |
| certification: "A", | |
| duration: "1h 55m", | |
| posterUrl: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751790461/zfxzrvffdu8zfled6nzt.avif", | |
| releaseDate: "2025-07-22", | |
| description: | |
| "M3GAN returns with upgraded AI and deadlier instincts in this spine-chilling tech horror sequel.", | |
| }, | |
| ]; | |
| const seedMovies = async () => { | |
| try { | |
| await mongoose.connect(config.databaseUrl as string); | |
| console.log("Connected to DB"); | |
| await MovieModel.deleteMany(); | |
| await MovieModel.insertMany(movies); | |
| console.log("Movies seeded successfully"); | |
| process.exit(0); | |
| } catch (err) { | |
| console.error("Error seeding movies:", err); | |
| process.exit(1); | |
| } | |
| }; | |
| seedMovies(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // seed/showSeeder.ts | |
| import mongoose from "mongoose"; | |
| import dayjs from "dayjs"; | |
| import { MovieModel } from "../modules/movie/movie.model"; | |
| import { TheaterModel } from "../modules/theater/theater.model"; | |
| import { ShowModel } from "../modules/show/show.model"; | |
| import { config } from "../config/config"; | |
| import { generateSeatLayout } from "../utils/index" | |
| const generatePriceMap = () => | |
| new Map([ | |
| ["PREMIUM", 510], | |
| ["EXECUTIVE", 290], | |
| ["NORMAL", 270], | |
| ]); | |
| const formats = ["2D", "3D", "IMAX", "PVR PXL"]; | |
| // ποΈ Realistic time slots | |
| const fixedTimeSlots = [ | |
| { start: "09:00 AM", end: "11:30 AM" }, | |
| { start: "12:30 PM", end: "03:00 PM" }, | |
| { start: "04:00 PM", end: "06:30 PM" }, | |
| { start: "07:30 PM", end: "10:00 PM" }, | |
| { start: "10:30 PM", end: "01:00 AM" }, | |
| ]; | |
| const toDateWithTime = (baseDate: Date, timeStr: string) => { | |
| return dayjs(baseDate) | |
| .hour(dayjs(timeStr, ["hh:mm A"]).hour()) | |
| .minute(dayjs(timeStr, ["hh:mm A"]).minute()) | |
| .second(0) | |
| .toDate(); | |
| }; | |
| export const seedShow = async () => { | |
| const movieIds = ["68e224451aeabaafaa43ac58", "68e224451aeabaafaa43ac57"]; | |
| const movies = await MovieModel.find({ _id: { $in: movieIds } }); | |
| const theatres = await TheaterModel.find({ state: "West Bengal" }); | |
| if (!movies.length || !theatres.length) { | |
| console.error("Movies or theatres not found. Please check IDs or state name."); | |
| return; | |
| } | |
| const today = dayjs().startOf("day"); | |
| for (const movie of movies) { | |
| for (const theatre of theatres) { | |
| for (let d = 0; d < 2; d++) { // β today and tomorrow | |
| const showDate = today.add(d, "day"); | |
| const formattedDate = showDate.format("DD-MM-YYYY"); | |
| const numShows = Math.floor(Math.random() * 3) + 2; // 2β4 shows | |
| const selectedSlots = fixedTimeSlots.slice(0, numShows); | |
| for (const slot of selectedSlots) { | |
| const startTime = toDateWithTime(showDate.toDate(), slot.start); | |
| const endTime = toDateWithTime(showDate.toDate(), slot.end); | |
| const newShow = new ShowModel({ | |
| movie: movie._id, | |
| theater: theatre._id, | |
| location: theatre.state, | |
| format: formats[Math.floor(Math.random() * formats.length)], | |
| audioType: "Dolby 7.1", | |
| startTime: slot.start, | |
| date: formattedDate, // β "DD-MM-YYYY" | |
| priceMap: generatePriceMap(), | |
| seatLayout: generateSeatLayout(), | |
| }); | |
| await newShow.save(); | |
| console.log( | |
| `π¬ Show created for ${movie.title} at ${theatre.name} on ${formattedDate} (${slot.start} - ${slot.end})` | |
| ); | |
| } | |
| } | |
| } | |
| } | |
| console.log("β Show seeding completed for selected movies in West Bengal."); | |
| }; | |
| mongoose | |
| .connect(config.databaseUrl as string) | |
| .then(async () => { | |
| console.log("DB connected"); | |
| await ShowModel.deleteMany({}); | |
| console.log("π§Ή Existing shows deleted."); | |
| await seedShow(); | |
| mongoose.disconnect(); | |
| }) | |
| .catch((err) => console.log(err)); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import mongoose from "mongoose"; | |
| import dotenv from "dotenv"; | |
| import { TheaterModel } from "../modules/theater/theater.model"; | |
| import { config } from "../config/config"; | |
| dotenv.config(); | |
| mongoose | |
| .connect(config.databaseUrl as string) | |
| .then(async () => { | |
| console.log("Connected to MongoDB β "); | |
| const cities = [ | |
| { | |
| name: "Mumbai", | |
| state: "Maharashtra", | |
| areas: ["Andheri", "Bandra", "Powai", "Borivali"], | |
| }, | |
| { | |
| name: "Delhi", | |
| state: "Delhi", | |
| areas: ["Connaught Place", "Saket", "Dwarka", "Karol Bagh"], | |
| }, | |
| { | |
| name: "Bangalore", | |
| state: "Karnataka", | |
| areas: ["Whitefield", "Koramangala", "Indiranagar", "Marathahalli"], | |
| }, | |
| { | |
| name: "Hyderabad", | |
| state: "Telangana", | |
| areas: ["Banjara Hills", "Gachibowli", "Madhapur", "Ameerpet"], | |
| }, | |
| { | |
| name: "Kolkata", | |
| state: "West Bengal", | |
| areas: ["Salt Lake", "New Town", "Park Street", "Gariahat"], | |
| }, | |
| { | |
| name: "Chennai", | |
| state: "Tamil Nadu", | |
| areas: ["T Nagar", "Velachery", "Adyar", "Anna Nagar"], | |
| }, | |
| { | |
| name: "Ahmedabad", | |
| state: "Gujarat", | |
| areas: ["Navrangpura", "Maninagar", "Thaltej", "Vastrapur"], | |
| }, | |
| { | |
| name: "Pune", | |
| state: "Maharashtra", | |
| areas: ["Hinjewadi", "Kothrud", "Viman Nagar", "Baner"], | |
| }, | |
| { | |
| name: "Jaipur", | |
| state: "Rajasthan", | |
| areas: ["Malviya Nagar", "Vaishali Nagar", "C Scheme", "Mansarovar"], | |
| }, | |
| { | |
| name: "Lucknow", | |
| state: "Uttar Pradesh", | |
| areas: ["Hazratganj", "Gomti Nagar", "Alambagh", "Indira Nagar"], | |
| }, | |
| { | |
| name: "Chandigarh", | |
| state: "Chandigarh", | |
| areas: ["Sector 17", "Sector 35", "Sector 22", "Manimajra"], | |
| }, | |
| { | |
| name: "Indore", | |
| state: "Madhya Pradesh", | |
| areas: ["Vijay Nagar", "Rajwada", "Palasia", "MG Road"], | |
| }, | |
| { | |
| name: "Bhopal", | |
| state: "Madhya Pradesh", | |
| areas: ["MP Nagar", "Arera Colony", "Kolar", "TT Nagar"], | |
| }, | |
| { | |
| name: "Nagpur", | |
| state: "Maharashtra", | |
| areas: ["Sitabuldi", "Dharampeth", "Wardha Road", "Ambazari"], | |
| }, | |
| { | |
| name: "Patna", | |
| state: "Bihar", | |
| areas: ["Boring Road", "Kankarbagh", "Patliputra", "Bailey Road"], | |
| }, | |
| { | |
| name: "Ranchi", | |
| state: "Jharkhand", | |
| areas: ["Lalpur", "Harmu", "Morabadi", "Kokar"], | |
| }, | |
| { | |
| name: "Surat", | |
| state: "Gujarat", | |
| areas: ["Adajan", "Piplod", "Vesu", "Varachha"], | |
| }, | |
| { | |
| name: "Noida", | |
| state: "Uttar Pradesh", | |
| areas: ["Sector 18", "Sector 62", "Sector 137", "Sector 50"], | |
| }, | |
| { | |
| name: "Guwahati", | |
| state: "Assam", | |
| areas: ["Paltan Bazar", "Silpukhuri", "Ganeshguri", "Dispur"], | |
| }, | |
| { | |
| name: "Vizag", | |
| state: "Andhra Pradesh", | |
| areas: ["MVP Colony", "Gajuwaka", "Dwaraka Nagar", "Waltair Uplands"], | |
| }, | |
| ]; | |
| const brands = ["PVR", "INOX", "Cinepolis"]; | |
| const logos: Record<string, string> = { | |
| PVR: "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751788726/omht27letnpbbaj2w0op.avif", | |
| INOX: "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751788726/yxjgnxhxlccfdon3fyzg.avif", | |
| Cinepolis: | |
| "https://res.cloudinary.com/amritrajmaurya/image/upload/v1751788726/eebu3t34depdmmgxyknq.avif", | |
| }; | |
| const theatres = []; | |
| for (const city of cities) { | |
| const numTheatres = Math.floor(Math.random() * 2) + 3; // 3 or 4 per city | |
| for (let i = 0; i < numTheatres; i++) { | |
| const brand = brands[i % brands.length]; | |
| const area = city.areas[i % city.areas.length]; | |
| theatres.push({ | |
| name: `${brand} ${area}`, | |
| location: `${area}, ${city.name}`, | |
| city: city.name, | |
| state: city.state, // β Added here | |
| logo: logos[brand], | |
| }); | |
| } | |
| } | |
| await TheaterModel.deleteMany({}); | |
| await TheaterModel.insertMany(theatres); | |
| console.log(`β Seeded ${theatres.length} theatres successfully.`); | |
| process.exit(0); | |
| }) | |
| .catch((err) => { | |
| console.error("β MongoDB connection failed:", err); | |
| process.exit(1); | |
| }); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment