Skip to content

Instantly share code, notes, and snippets.

@amritmaurya1504
Last active October 11, 2025 09:02
Show Gist options
  • Select an option

  • Save amritmaurya1504/ac6f351d0bbfbd92d6dc6f68af3bb535 to your computer and use it in GitHub Desktop.

Select an option

Save amritmaurya1504/ac6f351d0bbfbd92d6dc6f68af3bb535 to your computer and use it in GitHub Desktop.
bookMyScreen(utils)
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);
};
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;
// 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();
// 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));
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