[
  {
    "path": ".gitignore",
    "content": "/node_modules"
  },
  {
    "path": "backend/config/db.js",
    "content": "import mongoose from \"mongoose\";\n\nconst connectDB = async () => {\n  try {\n    await mongoose.connect(process.env.MONGO_URI);\n    console.log(`Successfully connected to MongoDB 👍`);\n  } catch (error) {\n    console.error(`Error: ${error.message}`);\n    process.exit(1);\n  }\n};\n\nexport default connectDB;\n"
  },
  {
    "path": "backend/controllers/genreController.js",
    "content": "import Genre from \"../models/Genre.js\";\nimport asyncHandler from \"../middlewares/asyncHandler.js\";\n\nconst createGenre = asyncHandler(async (req, res) => {\n  try {\n    const { name } = req.body;\n\n    if (!name) {\n      return res.json({ error: \"Name is required\" });\n    }\n\n    const existingGenre = await Genre.findOne({ name });\n\n    if (existingGenre) {\n      return res.json({ error: \"Already exists\" });\n    }\n\n    const genre = await new Genre({ name }).save();\n    res.json(genre);\n  } catch (error) {\n    console.log(error);\n    return res.status(400).json(error);\n  }\n});\n\nconst updateGenre = asyncHandler(async (req, res) => {\n  try {\n    const { name } = req.body;\n    const { id } = req.params;\n\n    const genre = await Genre.findOne({ _id: id });\n\n    if (!genre) {\n      return res.status(404).json({ error: \"Genre not found\" });\n    }\n\n    genre.name = name;\n\n    const updatedGenre = await genre.save();\n    res.json(updatedGenre);\n  } catch (error) {\n    console.error(error);\n    res.status(500).json({ error: \"Internal server error\" });\n  }\n});\n\nconst removeGenre = asyncHandler(async (req, res) => {\n  try {\n    const { id } = req.params;\n    const removed = await Genre.findByIdAndDelete(id);\n\n    if (!removed) {\n      return res.status(404).json({ error: \"Genre not found\" });\n    }\n\n    res.json(removed);\n  } catch (error) {\n    console.error(error);\n    res.status(500).json({ error: \"Interval server error\" });\n  }\n});\n\nconst listGenres = asyncHandler(async (req, res) => {\n  try {\n    const all = await Genre.find({});\n    res.json(all);\n  } catch (error) {\n    console.log(error);\n    return res.status(400).json(error.message);\n  }\n});\n\nconst readGenre = asyncHandler(async (req, res) => {\n  try {\n    const genre = await Genre.findOne({ _id: req.params.id });\n    res.json(genre);\n  } catch (error) {\n    console.log(error);\n    return res.status(400).json(error.message);\n  }\n});\n\nexport { createGenre, updateGenre, removeGenre, listGenres, readGenre };\n"
  },
  {
    "path": "backend/controllers/movieController.js",
    "content": "import Movie from \"../models/Movie.js\";\n\nconst createMovie = async (req, res) => {\n  try {\n    const newMovie = new Movie(req.body);\n    const savedMovie = await newMovie.save();\n    res.json(savedMovie);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst getAllMovies = async (req, res) => {\n  try {\n    const movies = await Movie.find();\n    res.json(movies);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst getSpecificMovie = async (req, res) => {\n  try {\n    const { id } = req.params;\n    const specificMovie = await Movie.findById(id);\n    if (!specificMovie) {\n      return res.status(404).json({ message: \"Movie not found\" });\n    }\n\n    res.json(specificMovie);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst updateMovie = async (req, res) => {\n  try {\n    const { id } = req.params;\n    const updatedMovie = await Movie.findByIdAndUpdate(id, req.body, {\n      new: true,\n    });\n\n    if (!updatedMovie) {\n      return res.status(404).json({ message: \"Movie not found\" });\n    }\n\n    res.json(updatedMovie);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst movieReview = async (req, res) => {\n  try {\n    const { rating, comment } = req.body;\n    const movie = await Movie.findById(req.params.id);\n\n    if (movie) {\n      const alreadyReviewed = movie.reviews.find(\n        (r) => r.user.toString() === req.user._id.toString()\n      );\n\n      if (alreadyReviewed) {\n        res.status(400);\n        throw new Error(\"Movie already reviewed\");\n      }\n\n      const review = {\n        name: req.user.username,\n        rating: Number(rating),\n        comment,\n        user: req.user._id,\n      };\n\n      movie.reviews.push(review);\n      movie.numReviews = movie.reviews.length;\n      movie.rating =\n        movie.reviews.reduce((acc, item) => item.rating + acc, 0) /\n        movie.reviews.length;\n\n      await movie.save();\n      res.status(201).json({ message: \"Review Added\" });\n    } else {\n      res.status(404);\n      throw new Error(\"Movie not found\");\n    }\n  } catch (error) {\n    console.error(error);\n    res.status(400).json(error.message);\n  }\n};\n\nconst deleteMovie = async (req, res) => {\n  try {\n    const { id } = req.params;\n    const deleteMovie = await Movie.findByIdAndDelete(id);\n\n    if (!deleteMovie) {\n      return res.status(404).json({ message: \"Movie not found\" });\n    }\n\n    res.json({ message: \"Movie Deleted Successfully\" });\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst deleteComment = async (req, res) => {\n  try {\n    const { movieId, reviewId } = req.body;\n    const movie = await Movie.findById(movieId);\n\n    if (!movie) {\n      return res.status(404).json({ message: \"Movie not found\" });\n    }\n\n    const reviewIndex = movie.reviews.findIndex(\n      (r) => r._id.toString() === reviewId\n    );\n\n    if (reviewIndex === -1) {\n      return res.status(404).json({ message: \"Comment not found\" });\n    }\n\n    movie.reviews.splice(reviewIndex, 1);\n    movie.numReviews = movie.reviews.length;\n    movie.rating =\n      movie.reviews.length > 0\n        ? movie.reviews.reduce((acc, item) => item.rating + acc, 0) /\n          movie.reviews.length\n        : 0;\n\n    await movie.save();\n    res.json({ message: \"Comment Deleted Successfully\" });\n  } catch (error) {\n    console.error(error);\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst getNewMovies = async (req, res) => {\n  try {\n    const newMovies = await Movie.find().sort({ createdAt: -1 }).limit(10);\n    res.json(newMovies);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst getTopMovies = async (req, res) => {\n  try {\n    const topRatedMovies = await Movie.find()\n      .sort({ numReviews: -1 })\n      .limit(10);\n    res.json(topRatedMovies);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nconst getRandomMovies = async (req, res) => {\n  try {\n    const randomMovies = await Movie.aggregate([{ $sample: { size: 10 } }]);\n    res.json(randomMovies);\n  } catch (error) {\n    res.status(500).json({ error: error.message });\n  }\n};\n\nexport {\n  createMovie,\n  getAllMovies,\n  getSpecificMovie,\n  updateMovie,\n  movieReview,\n  deleteMovie,\n  deleteComment,\n  getNewMovies,\n  getTopMovies,\n  getRandomMovies,\n};\n"
  },
  {
    "path": "backend/controllers/userController.js",
    "content": "import User from \"../models/User.js\";\nimport bcrypt from \"bcryptjs\";\nimport asyncHandler from \"../middlewares/asyncHandler.js\";\nimport createToken from \"../utils/createToken.js\";\n\nconst createUser = asyncHandler(async (req, res) => {\n  const { username, email, password } = req.body;\n\n  if (!username || !email || !password) {\n    throw new Error(\"Please fill all the fields\");\n  }\n\n  const userExists = await User.findOne({ email });\n  if (userExists) res.status(400).send(\"User already exists\");\n\n  // Hash the user password\n  const salt = await bcrypt.genSalt(10);\n  const hashedPassword = await bcrypt.hash(password, salt);\n  const newUser = new User({ username, email, password: hashedPassword });\n\n  try {\n    await newUser.save();\n    createToken(res, newUser._id);\n\n    res.status(201).json({\n      _id: newUser._id,\n      username: newUser.username,\n      email: newUser.email,\n      isAdmin: newUser.isAdmin,\n    });\n  } catch (error) {\n    res.status(400);\n    throw new Error(\"Invalid user data\");\n  }\n});\n\nconst loginUser = asyncHandler(async (req, res) => {\n  const { email, password } = req.body;\n\n  const existingUser = await User.findOne({ email });\n\n  if (existingUser) {\n    const isPasswordValid = await bcrypt.compare(\n      password,\n      existingUser.password\n    );\n\n    if (isPasswordValid) {\n      createToken(res, existingUser._id);\n\n      res.status(201).json({\n        _id: existingUser._id,\n        username: existingUser.username,\n        email: existingUser.email,\n        isAdmin: existingUser.isAdmin,\n      });\n    } else {\n      res.status(401).json({ message: \"Invalid Password\" });\n    }\n  } else {\n    res.status(401).json({ message: \"User not found\" });\n  }\n});\n\nconst logoutCurrentUser = asyncHandler(async (req, res) => {\n  res.cookie(\"jwt\", \"\", {\n    httpOnly: true,\n    expires: new Date(0),\n  });\n\n  res.status(200).json({ message: \"Logged out successfully\" });\n});\n\nconst getAllUsers = asyncHandler(async (req, res) => {\n  const users = await User.find({});\n  res.json(users);\n});\n\nconst getCurrentUserProfile = asyncHandler(async (req, res) => {\n  const user = await User.findById(req.user._id);\n\n  if (user) {\n    res.json({\n      _id: user._id,\n      username: user.username,\n      email: user.email,\n    });\n  } else {\n    res.status(404);\n    throw new Error(\"User not found.\");\n  }\n});\n\nconst updateCurrentUserProfile = asyncHandler(async (req, res) => {\n  const user = await User.findById(req.user._id);\n\n  if (user) {\n    user.username = req.body.username || user.username;\n    user.email = req.body.email || user.email;\n\n    if (req.body.password) {\n      const salt = await bcrypt.genSalt(10);\n      const hashedPassword = await bcrypt.hash(req.body.password, salt);\n      user.password = hashedPassword;\n    }\n\n    const updatedUser = await user.save();\n\n    res.json({\n      _id: updatedUser._id,\n      username: updatedUser.username,\n      email: updatedUser.email,\n      isAdmin: updatedUser.isAdmin,\n    });\n  } else {\n    res.status(404);\n    throw new Error(\"User not found\");\n  }\n});\n\nexport {\n  createUser,\n  loginUser,\n  logoutCurrentUser,\n  getAllUsers,\n  getCurrentUserProfile,\n  updateCurrentUserProfile,\n};\n"
  },
  {
    "path": "backend/index.js",
    "content": "// Packages\nimport express from \"express\";\nimport cookieParser from \"cookie-parser\";\nimport dotenv from \"dotenv\";\nimport path from \"path\";\n\n// Files\nimport connectDB from \"./config/db.js\";\nimport userRoutes from \"./routes/userRoutes.js\";\nimport genreRoutes from \"./routes/genreRoutes.js\";\nimport moviesRoutes from \"./routes/moviesRoutes.js\";\nimport uploadRoutes from \"./routes/uploadRoutes.js\";\n\n// Configuration\ndotenv.config();\nconnectDB();\n\nconst app = express();\n\n// middlewares\napp.use(express.json());\napp.use(express.urlencoded({ extended: true }));\napp.use(cookieParser());\n\nconst PORT = process.env.PORT || 3000;\n\n// Routes\napp.use(\"/api/v1/users\", userRoutes);\napp.use(\"/api/v1/genre\", genreRoutes);\napp.use(\"/api/v1/movies\", moviesRoutes);\napp.use(\"/api/v1/upload\", uploadRoutes);\n\nconst __dirname = path.resolve();\napp.use(\"/uploads\", express.static(path.join(__dirname + \"/uploads\")));\n\napp.listen(PORT, () => console.log(`Server is running on port ${PORT}`));\n"
  },
  {
    "path": "backend/middlewares/asyncHandler.js",
    "content": "const asyncHandler = (fn) => (req, res, next) => {\n  Promise.resolve(fn(req, res, next)).catch((error) => {\n    res.status(500).json({ message: error.message });\n  });\n};\n\nexport default asyncHandler;\n"
  },
  {
    "path": "backend/middlewares/authMiddleware.js",
    "content": "import jwt from \"jsonwebtoken\";\nimport User from \"../models/User.js\";\nimport asyncHandler from \"./asyncHandler.js\";\n\n// Check if the user is authenticated or not\nconst authenticate = asyncHandler(async (req, res, next) => {\n  let token;\n\n  // Read JWT from the 'jwt' cookie\n  token = req.cookies.jwt;\n\n  if (token) {\n    try {\n      const decoded = jwt.verify(token, process.env.JWT_SECRET);\n      req.user = await User.findById(decoded.userId).select(\"-password\");\n      next();\n    } catch (error) {\n      res.status(401);\n      throw new Error(\"Not authorized, token failed.\");\n    }\n  } else {\n    res.status(401);\n    throw new Error(\"Not authorized, no token\");\n  }\n});\n\n// Check if the user is admin or not\nconst authorizeAdmin = (req, res, next) => {\n  if (req.user && req.user.isAdmin) {\n    next();\n  } else {\n    res.status(401).send(\"Not authorized as an admin\");\n  }\n};\n\nexport { authenticate, authorizeAdmin };\n"
  },
  {
    "path": "backend/middlewares/checkId.js",
    "content": "import { isValidObjectId } from \"mongoose\";\n\nfunction checkId(req, res, next) {\n  if (!isValidObjectId(req.params.id)) {\n    res.status(404);\n    throw new Error(`Invalid Object Of: ${req.params.id}`);\n  }\n  next();\n}\n\nexport default checkId;\n"
  },
  {
    "path": "backend/models/Genre.js",
    "content": "import mongoose from \"mongoose\";\n\nconst genreSchema = new mongoose.Schema({\n  name: {\n    type: String,\n    trim: true,\n    required: true,\n    maxLength: 32,\n    unique: true,\n  },\n});\n\nexport default mongoose.model(\"Genre\", genreSchema);\n"
  },
  {
    "path": "backend/models/Movie.js",
    "content": "import mongoose from \"mongoose\";\nconst { ObjectId } = mongoose.Schema;\n\nconst reviewSchema = mongoose.Schema(\n  {\n    name: { type: String, required: true },\n    rating: { type: Number, required: true },\n    comment: { type: String, required: true },\n    user: {\n      type: mongoose.Schema.Types.ObjectId,\n      required: true,\n      ref: \"User\",\n    },\n  },\n  { timestamps: true }\n);\n\nconst movieSchema = new mongoose.Schema(\n  {\n    name: { type: String, required: true },\n    image: { type: String },\n    year: { type: Number, required: true },\n    genre: { type: ObjectId, ref: \"Genre\", required: true },\n    detail: { type: String, required: true },\n    cast: [{ type: String }],\n    reviews: [reviewSchema],\n    numReviews: { type: Number, required: true, default: 0 },\n    createdAt: { type: Date, default: Date.now },\n  },\n  { timestamps: true }\n);\n\nconst Movie = mongoose.model(\"Movie\", movieSchema);\nexport default Movie;\n"
  },
  {
    "path": "backend/models/User.js",
    "content": "import mongoose from \"mongoose\";\n\nconst userSchema = mongoose.Schema(\n  {\n    username: {\n      type: String,\n      required: true,\n    },\n\n    email: {\n      type: String,\n      required: true,\n      unique: true,\n    },\n\n    password: {\n      type: String,\n      required: true,\n    },\n\n    isAdmin: {\n      type: Boolean,\n      required: true,\n      default: false,\n    },\n  },\n  { timestamps: true }\n);\n\nconst User = mongoose.model(\"User\", userSchema);\nexport default User;\n"
  },
  {
    "path": "backend/routes/genreRoutes.js",
    "content": "import express from \"express\";\nconst router = express.Router();\n\n// Controllers\nimport {\n  createGenre,\n  updateGenre,\n  removeGenre,\n  listGenres,\n  readGenre,\n} from \"../controllers/genreController.js\";\n\n// Middlewares\nimport { authenticate, authorizeAdmin } from \"../middlewares/authMiddleware.js\";\n\nrouter.route(\"/\").post(authenticate, authorizeAdmin, createGenre);\nrouter.route(\"/:id\").put(authenticate, authorizeAdmin, updateGenre);\nrouter.route(\"/:id\").delete(authenticate, authorizeAdmin, removeGenre);\nrouter.route(\"/genres\").get(listGenres);\nrouter.route(\"/:id\").get(readGenre);\n\nexport default router;\n"
  },
  {
    "path": "backend/routes/moviesRoutes.js",
    "content": "import express from \"express\";\nconst router = express.Router();\n\n// Controllers\nimport {\n  createMovie,\n  getAllMovies,\n  getSpecificMovie,\n  updateMovie,\n  movieReview,\n  deleteMovie,\n  deleteComment,\n  getNewMovies,\n  getTopMovies,\n  getRandomMovies,\n} from \"../controllers/movieController.js\";\n// Middlewares\nimport { authenticate, authorizeAdmin } from \"../middlewares/authMiddleware.js\";\nimport checkId from \"../middlewares/checkId.js\";\n\n// Public Routes\nrouter.get(\"/all-movies\", getAllMovies);\nrouter.get(\"/specific-movie/:id\", getSpecificMovie);\nrouter.get(\"/new-movies\", getNewMovies);\nrouter.get(\"/top-movies\", getTopMovies);\nrouter.get(\"/random-movies\", getRandomMovies);\n\n// Restricted Routes\nrouter.post(\"/:id/reviews\", authenticate, checkId, movieReview);\n\n// Admin\nrouter.post(\"/create-movie\", authenticate, authorizeAdmin, createMovie);\nrouter.put(\"/update-movie/:id\", authenticate, authorizeAdmin, updateMovie);\nrouter.delete(\"/delete-movie/:id\", authenticate, authorizeAdmin, deleteMovie);\nrouter.delete(\"/delete-comment\", authenticate, authorizeAdmin, deleteComment);\nexport default router;\n"
  },
  {
    "path": "backend/routes/uploadRoutes.js",
    "content": "import path from \"path\";\nimport express from \"express\";\nimport multer from \"multer\";\n\nconst router = express.Router();\n\nconst storage = multer.diskStorage({\n  destination: (req, file, cb) => {\n    cb(null, \"uploads/\");\n  },\n\n  filename: (req, file, cb) => {\n    const extname = path.extname(file.originalname);\n    cb(null, `${file.fieldname}-${Date.now()}${extname}`);\n  },\n});\n\nconst fileFilter = (req, file, cb) => {\n  const filetypes = /jpe?g|png|webp/;\n  const mimetypes = /image\\/jpe?g|image\\/png||image\\/webp/;\n\n  const extname = path.extname(file.originalname);\n  const mimetype = file.mimetype;\n\n  if (filetypes.test(extname) && mimetypes.test(mimetype)) {\n    cb(null, true);\n  } else {\n    cb(new Error(\"Images only\"), false);\n  }\n};\n\nconst upload = multer({ storage, fileFilter });\nconst uploadSingleImage = upload.single(\"image\");\n\nrouter.post(\"/\", (req, res) => {\n  uploadSingleImage(req, res, (err) => {\n    if (err) {\n      res.status(400).send({ message: err.message });\n    } else if (req.file) {\n      res.status(200).send({\n        message: \"Image uploaded successfully\",\n        image: `/${req.file.path}`,\n      });\n    } else {\n      res.status(400).send({ message: \"No image file provided\" });\n    }\n  });\n});\n\nexport default router;\n"
  },
  {
    "path": "backend/routes/userRoutes.js",
    "content": "import express from \"express\";\n// controllers\nimport {\n  createUser,\n  loginUser,\n  logoutCurrentUser,\n  getAllUsers,\n  getCurrentUserProfile,\n  updateCurrentUserProfile,\n} from \"../controllers/userController.js\";\n\n// middlewares\nimport { authenticate, authorizeAdmin } from \"../middlewares/authMiddleware.js\";\n\nconst router = express.Router();\n\nrouter\n  .route(\"/\")\n  .post(createUser)\n  .get(authenticate, authorizeAdmin, getAllUsers);\n\nrouter.post(\"/auth\", loginUser);\nrouter.post(\"/logout\", logoutCurrentUser);\n\nrouter\n  .route(\"/profile\")\n  .get(authenticate, getCurrentUserProfile)\n  .put(authenticate, updateCurrentUserProfile);\n\nexport default router;\n"
  },
  {
    "path": "backend/utils/createToken.js",
    "content": "import jwt from \"jsonwebtoken\";\n\nconst generateToken = (res, userId) => {\n  const token = jwt.sign({ userId }, process.env.JWT_SECRET, {\n    expiresIn: \"30d\",\n  });\n\n  // Set JWT as an HTTP-Only Cookie\n  res.cookie(\"jwt\", token, {\n    httpOnly: true,\n    secure: process.env.NODE_ENV !== \"development\",\n    sameSite: \"strict\",\n    maxAge: 30 * 24 * 60 * 60 * 1000,\n  });\n\n  return token;\n};\n\nexport default generateToken;\n"
  },
  {
    "path": "frontend/.eslintrc.cjs",
    "content": "module.exports = {\n  root: true,\n  env: { browser: true, es2020: true },\n  extends: [\n    'eslint:recommended',\n    'plugin:react/recommended',\n    'plugin:react/jsx-runtime',\n    'plugin:react-hooks/recommended',\n  ],\n  ignorePatterns: ['dist', '.eslintrc.cjs'],\n  parserOptions: { ecmaVersion: 'latest', sourceType: 'module' },\n  settings: { react: { version: '18.2' } },\n  plugins: ['react-refresh'],\n  rules: {\n    'react-refresh/only-export-components': [\n      'warn',\n      { allowConstantExport: true },\n    ],\n  },\n}\n"
  },
  {
    "path": "frontend/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "frontend/README.md",
    "content": "# React + Vite\n\nThis template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.\n\nCurrently, two official plugins are available:\n\n- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh\n- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh\n"
  },
  {
    "path": "frontend/index.html",
    "content": "<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.jsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "frontend/package.json",
    "content": "{\n  \"name\": \"frontend\",\n  \"private\": true,\n  \"version\": \"0.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"vite build\",\n    \"lint\": \"eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"@reduxjs/toolkit\": \"^2.1.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-icons\": \"^5.0.1\",\n    \"react-redux\": \"^9.1.0\",\n    \"react-router\": \"^6.21.3\",\n    \"react-router-dom\": \"^6.21.3\",\n    \"react-slick\": \"^0.30.1\",\n    \"react-toastify\": \"^10.0.4\",\n    \"slick-carousel\": \"^1.8.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.2.43\",\n    \"@types/react-dom\": \"^18.2.17\",\n    \"@vitejs/plugin-react\": \"^4.2.1\",\n    \"autoprefixer\": \"^10.4.17\",\n    \"eslint\": \"^8.55.0\",\n    \"eslint-plugin-react\": \"^7.33.2\",\n    \"eslint-plugin-react-hooks\": \"^4.6.0\",\n    \"eslint-plugin-react-refresh\": \"^0.4.5\",\n    \"postcss\": \"^8.4.33\",\n    \"tailwindcss\": \"^3.4.1\",\n    \"vite\": \"^5.0.8\"\n  }\n}\n"
  },
  {
    "path": "frontend/postcss.config.js",
    "content": "export default {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "frontend/src/App.jsx",
    "content": "import { Outlet } from \"react-router-dom\";\nimport { ToastContainer } from \"react-toastify\";\nimport \"react-toastify/dist/ReactToastify.css\";\nimport Navigation from \"./pages/Auth/Navigation\";\n\nconst App = () => {\n  return (\n    <>\n      <ToastContainer />\n      <Navigation />\n      <main className=\"py-3\">\n        <Outlet />\n      </main>\n    </>\n  );\n};\n\nexport default App;\n"
  },
  {
    "path": "frontend/src/component/GenreForm.jsx",
    "content": "const GenreForm = ({\n  value,\n  setValue,\n  handleSubmit,\n  buttonText = \"Submit\",\n  handleDelete,\n}) => {\n  return (\n    <div className=\"p-3\">\n      <form onSubmit={handleSubmit} className=\"space-y-3\">\n        <input\n          type=\"text\"\n          className=\"py-3 px-4 border rounded-lg w-[60rem]\"\n          placeholder=\"Write genre name\"\n          value={value}\n          onChange={(e) => setValue(e.target.value)}\n        />\n\n        <div className=\"flex justify-between\">\n          <button className=\"bg-teal-500 text-white py-2 px-4 rounded-lg hover:bg-teal-600 focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-opacity-50\">\n            {buttonText}\n          </button>\n\n          {handleDelete && (\n            <button\n              onClick={handleDelete}\n              className=\"bg-red-500 text-white py-2 px-4 rounded-lg hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50\"\n            >\n              Delete\n            </button>\n          )}\n        </div>\n      </form>\n    </div>\n  );\n};\n\nexport default GenreForm;\n"
  },
  {
    "path": "frontend/src/component/Loader.jsx",
    "content": "const Loader = () => {\n  return (\n    <div className=\"animate-spin rounded-full h-16 w-16 border-t-4 border-teal-500 border-opacity-50\"></div>\n  );\n};\nexport default Loader;\n"
  },
  {
    "path": "frontend/src/component/Modal.jsx",
    "content": "const Modal = ({ isOpen, onClose, children }) => {\n  return (\n    <>\n      {isOpen && (\n        <div className=\"fixed inset-0 flex items-center justify-center z-50\">\n          <div className=\"fixed inset-0 bg-black opacity-50\"></div>\n          <div className=\"absolute top-[40%] left-[20%] bg-white p-4 rounded-lg z-10 text-right\">\n            <button\n              className=\"text-black font-semibold hover:text-gray-700 focus:outline-none mr-2\"\n              onClick={onClose}\n            >\n              X\n            </button>\n            {children}\n          </div>\n        </div>\n      )}\n    </>\n  );\n};\nexport default Modal;\n"
  },
  {
    "path": "frontend/src/component/SliderUtil.jsx",
    "content": "import Slider from \"react-slick\";\nimport \"slick-carousel/slick/slick.css\";\nimport \"slick-carousel/slick/slick-theme.css\";\nimport MovieCard from \"../pages/Movies/MovieCard\";\n\nconst SliderUtil = ({ data }) => {\n  const settings = {\n    dots: true,\n    infinite: true,\n    speed: 500,\n    slidesToShow: 4,\n    slidesToScroll: 2,\n  };\n\n  return (\n    <Slider {...settings}>\n      {data?.map((movie) => (\n        <MovieCard key={movie._id} movie={movie} />\n      ))}\n    </Slider>\n  );\n};\n\nexport default SliderUtil;\n"
  },
  {
    "path": "frontend/src/index.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n  background: #0f0f10;\n  color: #fff;\n}\n\ninput,\ntextarea {\n  color: #000;\n}\n\nselect option {\n  color: black;\n}\n"
  },
  {
    "path": "frontend/src/main.jsx",
    "content": "import ReactDOM from \"react-dom/client\";\nimport App from \"./App.jsx\";\nimport \"./index.css\";\nimport store from \"./redux/store.js\";\nimport { Provider } from \"react-redux\";\nimport { Route, RouterProvider, createRoutesFromElements } from \"react-router\";\nimport { createBrowserRouter } from \"react-router-dom\";\n\n// Auth\nimport AdminRoute from \"./pages/Admin/AdminRoute.jsx\";\nimport GenreList from \"./pages/Admin/GenreList.jsx\";\n\n// Restricted\nimport Login from \"./pages/Auth/Login.jsx\";\nimport Register from \"./pages/Auth/Register.jsx\";\nimport PrivateRoute from \"./pages/Auth/PrivateRoute.jsx\";\n\nimport Home from \"./pages/Home.jsx\";\nimport Profile from \"./pages/User/Profile.jsx\";\nimport AdminMoviesList from \"./pages/Admin/AdminMoviesList.jsx\";\nimport UpdateMovie from \"./pages/Admin/UpdateMovie.jsx\";\nimport CreateMovie from \"./pages/Admin/CreateMovie.jsx\";\nimport AllMovies from \"./pages/Movies/AllMovies.jsx\";\nimport MovieDetails from \"./pages/Movies/MovieDetails.jsx\";\nimport AllComments from \"./pages/Admin/AllComments.jsx\";\nimport AdminDashboard from \"./pages/Admin/Dashboard/AdminDashboard.jsx\";\n\nconst router = createBrowserRouter(\n  createRoutesFromElements(\n    <Route path=\"/\" element={<App />}>\n      <Route index={true} path=\"/\" element={<Home />} />\n      <Route path=\"/movies\" element={<AllMovies />} />\n      <Route path=\"/login\" element={<Login />} />\n      <Route path=\"/register\" element={<Register />} />\n      <Route path=\"/movies/:id\" element={<MovieDetails />} />\n\n      <Route path=\"\" element={<PrivateRoute />}>\n        <Route path=\"/profile\" element={<Profile />} />\n      </Route>\n\n      <Route path=\"\" element={<AdminRoute />}>\n        <Route path=\"/admin/movies/genre\" element={<GenreList />} />\n        <Route path=\"/admin/movies/create\" element={<CreateMovie />} />\n        <Route path=\"/admin/movies-list\" element={<AdminMoviesList />} />\n        <Route path=\"/admin/movies/update/:id\" element={<UpdateMovie />} />\n        <Route path=\"/admin/movies/dashboard\" element={<AdminDashboard />} />\n        <Route path=\"/admin/movies/comments\" element={<AllComments />} />\n      </Route>\n    </Route>\n  )\n);\n\nReactDOM.createRoot(document.getElementById(\"root\")).render(\n  <Provider store={store}>\n    <RouterProvider router={router} />\n  </Provider>\n);\n"
  },
  {
    "path": "frontend/src/pages/Admin/AdminMoviesList.jsx",
    "content": "import { Link } from \"react-router-dom\";\nimport { useGetAllMoviesQuery } from \"../../redux/api/movies\";\n\nconst AdminMoviesList = () => {\n  const { data: movies } = useGetAllMoviesQuery();\n\n  return (\n    <div className=\"container mx-[9rem]\">\n      <div className=\"flex flex-col md:flex-row\">\n        <div className=\"p-3\">\n          <div className=\"ml-[2rem] text-xl font-bold h-12\">\n            All Movies ({movies?.length})\n          </div>\n\n          <div className=\"flex flex-wrap justify-around items-center p-[2rem]\">\n            {movies?.map((movie) => (\n              <Link\n                key={movie._id}\n                to={`/admin/movies/update/${movie._id}`}\n                className=\"block mb-4 overflow-hidden\"\n              >\n                <div className=\"flex\">\n                  <div\n                    key={movie._id}\n                    className=\"max-w-sm  m-[2rem] rounded overflow-hidden shadow-lg\"\n                  >\n                    <img\n                      src={movie.image}\n                      alt={movie.name}\n                      className=\"w-full h-48 object-cover\"\n                    />\n                    <div className=\"px-6 py-4 border border-gray-400\">\n                      <div className=\"font-bold text-xl mb-2\">{movie.name}</div>\n                    </div>\n\n                    <p className=\"text-gray-700 text-base\">{movie.detail}</p>\n\n                    <div className=\"mt-[2rem] mb-[1rem]\">\n                      <Link\n                        to={`/admin/movies/update/${movie._id}`}\n                        className=\"bg-teal-500 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded\"\n                      >\n                        Update Movie\n                      </Link>\n                    </div>\n                  </div>\n                </div>\n              </Link>\n            ))}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default AdminMoviesList;\n"
  },
  {
    "path": "frontend/src/pages/Admin/AdminRoute.jsx",
    "content": "import { Navigate, Outlet } from \"react-router-dom\";\nimport { useSelector } from \"react-redux\";\n\nconst AdminRoute = () => {\n  const { userInfo } = useSelector((state) => state.auth);\n\n  return userInfo && userInfo.isAdmin ? (\n    <Outlet />\n  ) : (\n    <Navigate to=\"/login\" replace />\n  );\n};\nexport default AdminRoute;\n"
  },
  {
    "path": "frontend/src/pages/Admin/AllComments.jsx",
    "content": "import {\n  useDeleteCommentMutation,\n  useGetAllMoviesQuery,\n} from \"../../redux/api/movies\";\nimport { toast } from \"react-toastify\";\n\nconst AllComments = () => {\n  const { data: movie, refetch } = useGetAllMoviesQuery();\n\n  const [deleteComment] = useDeleteCommentMutation();\n\n  const handleDeleteComment = async (movieId, reviewId) => {\n    try {\n      await deleteComment({ movieId, reviewId });\n      toast.success(\"Comment Deleted\");\n      refetch();\n    } catch (error) {\n      console.error(\"Error deleting comment: \", error);\n    }\n  };\n\n  return (\n    <div>\n      {movie?.map((m) => (\n        <section\n          key={m._id}\n          className=\"flex flex-col justify-center items-center\"\n        >\n          {m?.reviews.map((review) => (\n            <div\n              key={review._id}\n              className=\"bg-[#1A1A1A] p-4 rounded-lg w-[50%] mt-[2rem]\"\n            >\n              <div className=\"flex justify-between\">\n                <strong className=\"text-[#B0B0B0]\">{review.name}</strong>\n                <p className=\"text-[#B0B0B0]\">\n                  {review.createdAt.substring(0, 10)}\n                </p>\n              </div>\n\n              <p className=\"my-4\">{review.comment}</p>\n\n              <button\n                className=\"text-red-500\"\n                onClick={() => handleDeleteComment(m._id, review._id)}\n              >\n                Delete\n              </button>\n            </div>\n          ))}\n        </section>\n      ))}\n    </div>\n  );\n};\nexport default AllComments;\n"
  },
  {
    "path": "frontend/src/pages/Admin/CreateMovie.jsx",
    "content": "import { useEffect, useState } from \"react\";\nimport { useNavigate } from \"react-router-dom\";\nimport {\n  useCreateMovieMutation,\n  useUploadImageMutation,\n} from \"../../redux/api/movies\";\nimport { useFetchGenresQuery } from \"../../redux/api/genre\";\nimport { toast } from \"react-toastify\";\n\nconst CreateMovie = () => {\n  const navigate = useNavigate();\n\n  const [movieData, setMovieData] = useState({\n    name: \"\",\n    year: 0,\n    detail: \"\",\n    cast: [],\n    rating: 0,\n    image: null,\n    genre: \"\",\n  });\n\n  const [selectedImage, setSelectedImage] = useState(null);\n\n  const [\n    createMovie,\n    { isLoading: isCreatingMovie, error: createMovieErrorDetail },\n  ] = useCreateMovieMutation();\n\n  const [\n    uploadImage,\n    { isLoading: isUploadingImage, error: uploadImageErrorDetails },\n  ] = useUploadImageMutation();\n\n  const { data: genres, isLoading: isLoadingGenres } = useFetchGenresQuery();\n\n  useEffect(() => {\n    if (genres) {\n      setMovieData((prevData) => ({\n        ...prevData,\n        genre: genres[0]?._id || \"\",\n      }));\n      console.log(genres[0]?._id);\n    }\n  }, [genres]);\n\n  const handleChange = (e) => {\n    const { name, value } = e.target;\n\n    if (name === \"genre\") {\n      const selectedGenre = genres.find((genre) => genre.name === value);\n\n      setMovieData((prevData) => ({\n        ...prevData,\n        genre: selectedGenre ? selectedGenre._id : \"\",\n      }));\n    } else {\n      setMovieData((prevData) => ({\n        ...prevData,\n        [name]: value,\n      }));\n    }\n  };\n\n  const handleImageChange = (e) => {\n    const file = e.target.files[0];\n    setSelectedImage(file);\n  };\n\n  const handleCreateMovie = async () => {\n    try {\n      if (\n        !movieData.name ||\n        !movieData.year ||\n        !movieData.detail ||\n        !movieData.cast ||\n        !selectedImage\n      ) {\n        toast.error(\"Please fill all required fields\");\n        return;\n      }\n\n      let uploadedImagePath = null;\n\n      if (selectedImage) {\n        const formData = new FormData();\n        formData.append(\"image\", selectedImage);\n\n        const uploadImageResponse = await uploadImage(formData);\n\n        if (uploadImageResponse.data) {\n          uploadedImagePath = uploadImageResponse.data.image;\n        } else {\n          console.error(\"Failed to upload image: \", uploadImageErrorDetails);\n          toast.error(\"Failed to upload image\");\n          return;\n        }\n\n        await createMovie({\n          ...movieData,\n          image: uploadedImagePath,\n        });\n\n        navigate(\"/admin/movies-list\");\n\n        setMovieData({\n          name: \"\",\n          year: 0,\n          detail: \"\",\n          cast: [],\n          ratings: 0,\n          image: null,\n          genre: \"\",\n        });\n\n        toast.success(\"Movie Added To Database\");\n      }\n    } catch (error) {\n      console.error(\"Failed to create movie: \", createMovieErrorDetail);\n      toast.error(`Failed to create movie: ${createMovieErrorDetail?.message}`);\n    }\n  };\n\n  return (\n    <div className=\"container flex justify-center items-center mt-4\">\n      <form>\n        <p className=\"text-green-200 w-[50rem] text-2xl mb-4\">Create Movie</p>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Name:\n            <input\n              type=\"text\"\n              name=\"name\"\n              value={movieData.name}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Year:\n            <input\n              type=\"number\"\n              name=\"year\"\n              value={movieData.year}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Detail:\n            <textarea\n              name=\"detail\"\n              value={movieData.detail}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            ></textarea>\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Cast (comma-separated):\n            <input\n              type=\"text\"\n              name=\"cast\"\n              value={movieData.cast.join(\", \")}\n              onChange={(e) =>\n                setMovieData({ ...movieData, cast: e.target.value.split(\", \") })\n              }\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Genre:\n            <select\n              name=\"genre\"\n              value={movieData.genre}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            >\n              {isLoadingGenres ? (\n                <option>Loading genres...</option>\n              ) : (\n                genres.map((genre) => (\n                  <option key={genre.id} value={genre.id}>\n                    {genre.name}\n                  </option>\n                ))\n              )}\n            </select>\n          </label>\n        </div>\n\n        <div className=\"mb-4\">\n          <label\n            style={\n              !selectedImage\n                ? {\n                    border: \"1px solid #888\",\n                    borderRadius: \"5px\",\n                    padding: \"8px\",\n                  }\n                : {\n                    border: \"0\",\n                    borderRadius: \"0\",\n                    padding: \"0\",\n                  }\n            }\n          >\n            {!selectedImage && \"Upload Image\"}\n            <input\n              type=\"file\"\n              accept=\"image/*\"\n              onChange={handleImageChange}\n              style={{ display: !selectedImage ? \"none\" : \"block\" }}\n            />\n          </label>\n        </div>\n\n        <button\n          type=\"button\"\n          onClick={handleCreateMovie}\n          className=\"bg-teal-500 text-white px-4 py-2 rounded\"\n          disabled={isCreatingMovie || isUploadingImage}\n        >\n          {isCreatingMovie || isUploadingImage ? \"Creating...\" : \"Create Movie\"}\n        </button>\n      </form>\n    </div>\n  );\n};\nexport default CreateMovie;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/AdminDashboard.jsx",
    "content": "import Main from \"./Main/Main\";\nimport Sidebar from \"./Sidebar/Sidebar\";\n\nconst AdminDashboard = () => {\n  return (\n    <>\n      <Sidebar />\n      <Main />\n    </>\n  );\n};\n\nexport default AdminDashboard;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/Main/Main.jsx",
    "content": "import SecondaryCard from \"./SecondaryCard\";\nimport VideoCard from \"./VideoCard\";\nimport ReactTimeCard from \"./RealTimeCard\";\n\nimport {\n  useGetTopMoviesQuery,\n  useGetAllMoviesQuery,\n} from \"../../../../redux/api/movies\";\nimport { useGetUsersQuery } from \"../../../../redux/api/users\";\nimport RealTimeCard from \"./RealTimeCard\";\n\nconst Main = () => {\n  const { data: topMovies } = useGetTopMoviesQuery();\n  const { data: visitors } = useGetUsersQuery();\n  const { data: allMovies } = useGetAllMoviesQuery();\n\n  const totalCommentsLength = allMovies?.map((m) => m.numReviews);\n  const sumOfCommentsLength = totalCommentsLength?.reduce(\n    (acc, length) => acc + length,\n    0\n  );\n\n  return (\n    <div>\n      <section className=\"flex justify-around\">\n        <div className=\"ml-[14rem] mt-10\">\n          <div className=\"-translate-x-4 flex\">\n            <SecondaryCard\n              pill=\"Users\"\n              content={visitors?.length}\n              info=\"20.2k more then usual\"\n              gradient=\"from-teal-500 to-lime-400\"\n            />\n            <SecondaryCard\n              pill=\"Comments\"\n              content={sumOfCommentsLength}\n              info=\"742.8 more then usual\"\n              gradient=\"from-[#CCC514] to-[#CDCB8E]\"\n            />\n            <SecondaryCard\n              pill=\"Movies\"\n              content={allMovies?.length}\n              info=\"372+ more then usual\"\n              gradient=\"from-green-500 to-lime-400\"\n            />\n          </div>\n          <div className=\"flex justify-between w-[90%] text-white mt-10 font-bold\">\n            <p>Top Content</p>\n            <p>Comments</p>\n          </div>\n\n          {topMovies?.map((movie) => (\n            <VideoCard\n              key={movie._id}\n              image={movie.image}\n              title={movie.name}\n              date={movie.year}\n              comments={movie.numReviews}\n            />\n          ))}\n        </div>\n\n        <div>\n          <RealTimeCard />\n        </div>\n      </section>\n    </div>\n  );\n};\n\nexport default Main;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/Main/PrimaryCard.jsx",
    "content": "import { useGetUsersQuery } from \"../../../../redux/api/users\";\n\nconst PrimaryCard = () => {\n  const { data: visitors } = useGetUsersQuery();\n\n  return (\n    <div className=\"w-[100%] h-[10%]  bg-[#282828] text-white rounded-lg p-6\">\n      <h2 className=\"text-2xl font-bold mb-4\">Congratulations!</h2>\n      <p>You have {visitors?.length} new users, watching your content.</p>\n    </div>\n  );\n};\n\nexport default PrimaryCard;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/Main/RealTimeCard.jsx",
    "content": "import { useGetUsersQuery } from \"../../../../redux/api/users\";\nimport PrimaryCard from \"./PrimaryCard\";\n\nconst RealTimeCard = () => {\n  const { data: visitors } = useGetUsersQuery();\n\n  return (\n    <div className=\"w-[30rem] mt-10 bg-[#282828] text-[#fff] rounded-lg shadow-lg p-4\">\n      <h2 className=\"text-2xl font-bold mb-2\">Realtime</h2>\n      <p className=\"text-gray-500 mb-4\">Update Live</p>\n      <div className=\"border-t border-[#666] my-7\"></div>\n      <h2 className=\"text-2xl font-bold mb-2\">{visitors?.length}</h2>\n      <p className=\"text-gray-500 mb-2\">Subscribe</p>\n      <hr />\n\n      <PrimaryCard />\n    </div>\n  );\n};\n\nexport default RealTimeCard;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/Main/SecondaryCard.jsx",
    "content": "const SecondaryCard = ({ pill, content, info, gradient }) => {\n  return (\n    <div\n      className={`w-[15rem] h-[12rem] relative mt-10 bg-gradient-to-b ${gradient} rounded-lg shadow-lg ml-5`}\n    >\n      <div\n        className={`absolute -top-4 left-[5rem] border bg-gradient-to-b ${gradient} rounded-full py-2 px-5 text-sm text-gray-800 font-semibold`}\n      >\n        {pill}\n      </div>\n\n      <div className=\"flex items-center justify-center h-full\">\n        <h2 className=\"text-5xl font-bold text-white\">{content}</h2>\n      </div>\n\n      <div className=\"absolute bottom-4 left-12 text-sm text-white\">{info}</div>\n    </div>\n  );\n};\n\nexport default SecondaryCard;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/Main/VideoCard.jsx",
    "content": "const VideoCard = ({ image, title, date, comments }) => {\n  return (\n    <>\n      <div className=\"flex items-center w-[90%] mt-5\">\n        <div>\n          <img src={image} alt=\"Card Image\" className=\"h-[3rem]\" />\n        </div>\n\n        <div className=\"ml-4\">\n          <h2 className=\"text-lg text-white\">{title}</h2>\n          <p className=\"text-gray-500 mb-3\">{date}</p>\n        </div>\n\n        <div className=\"flex-grow mb-5 flex justify-end items-center\">\n          <div className=\"text-white text-lg\">{comments}</div>\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default VideoCard;\n"
  },
  {
    "path": "frontend/src/pages/Admin/Dashboard/Sidebar/Sidebar.jsx",
    "content": "import { Link } from \"react-router-dom\";\n\nconst Sidebar = () => {\n  return (\n    <div className=\"-translate-y-10 flex h-screen fixed mt-10 border-r-2 border-[#242424]\">\n      <aside className=\"text-white w-64 flex-shrink-0\">\n        <ul className=\"py-4\">\n          <li className=\"text-lg  bg-gradient-to-b from-green-500 to-lime-400 rounded-full -translate-x-6\">\n            <Link\n              to=\"/admin/movies/dashboard\"\n              className=\"block p-2 ml-20 mb-10\"\n            >\n              Dashboard\n            </Link>\n          </li>\n          <li className=\"text-lg -translate-x-6 hover:bg-gradient-to-b from-green-500 to-lime-400 rounded-full\">\n            <Link to=\"/admin/movies/create\" className=\"block p-2 ml-20 mb-10\">\n              Create Movie\n            </Link>\n          </li>\n          <li className=\"text-lg -translate-x-6 hover:bg-gradient-to-b from-green-500 to-lime-400 rounded-full\">\n            <Link to=\"/admin/movies/genre\" className=\"block p-2 ml-20 mb-10\">\n              Create Genre\n            </Link>\n          </li>\n          <li className=\"text-lg -translate-x-6 hover:bg-gradient-to-b from-green-500 to-lime-400 rounded-full\">\n            <Link to=\"/admin/movies-list\" className=\"block p-2 ml-20 mb-10\">\n              Update Movie\n            </Link>\n          </li>\n          <li className=\"text-lg -translate-x-6 hover:bg-gradient-to-b from-green-500 to-lime-400 rounded-full\">\n            <Link to=\"/admin/movies/comments\" className=\"block p-2 ml-20 mb-10\">\n              Comments\n            </Link>\n          </li>\n        </ul>\n      </aside>\n    </div>\n  );\n};\n\nexport default Sidebar;\n"
  },
  {
    "path": "frontend/src/pages/Admin/GenreList.jsx",
    "content": "import { useState } from \"react\";\nimport {\n  useCreateGenreMutation,\n  useUpdateGenreMutation,\n  useDeleteGenreMutation,\n  useFetchGenresQuery,\n} from \"../../redux/api/genre\";\n\nimport { toast } from \"react-toastify\";\nimport GenreForm from \"../../component/GenreForm\";\nimport Modal from \"../../component/Modal\";\n\nconst GenreList = () => {\n  const { data: genres, refetch } = useFetchGenresQuery();\n  const [name, setName] = useState(\"\");\n  const [selectedGenre, setSelectedGenre] = useState(null);\n  const [updatingName, setUpdatingName] = useState(\"\");\n  const [modalVisible, setModalVisible] = useState(false);\n\n  const [createGenre] = useCreateGenreMutation();\n  const [updateGenre] = useUpdateGenreMutation();\n  const [deleteGenre] = useDeleteGenreMutation();\n\n  const handleCreateGenre = async (e) => {\n    e.preventDefault();\n\n    if (!name) {\n      toast.error(\"Genre name is required\");\n      return;\n    }\n\n    try {\n      const result = await createGenre({ name }).unwrap();\n\n      if (result.error) {\n        toast.error(result.error);\n      } else {\n        setName(\"\");\n        toast.success(`${result.name} is created.`);\n        refetch();\n      }\n    } catch (error) {\n      console.error(error);\n      toast.error(\"Creating genre failed, try again.\");\n    }\n  };\n\n  const handleUpdateGenre = async (e) => {\n    e.preventDefault();\n\n    if (!updateGenre) {\n      toast.error(\"Genre name is required\");\n      return;\n    }\n\n    try {\n      const result = await updateGenre({\n        id: selectedGenre._id,\n        updateGenre: {\n          name: updatingName,\n        },\n      }).unwrap();\n\n      if (result.error) {\n        toast.error(result.error);\n      } else {\n        toast.success(`${result.name} is updated`);\n        refetch();\n        setSelectedGenre(null);\n        setUpdatingName(\"\");\n        setModalVisible(false);\n      }\n    } catch (error) {\n      console.error(error);\n    }\n  };\n\n  const handleDeleteGenre = async () => {\n    try {\n      const result = await deleteGenre(selectedGenre._id).unwrap();\n\n      if (result.error) {\n        toast.error(result.error);\n      } else {\n        toast.success(`${result.name} is deleted.`);\n        refetch();\n        setSelectedGenre(null);\n        setModalVisible(false);\n      }\n    } catch (error) {\n      console.error(error);\n      toast.error(\"Genre deletion failed. Tray again.\");\n    }\n  };\n\n  return (\n    <div className=\"ml-[10rem] flex flex-col md:flex-row\">\n      <div className=\"md:w-3/4 p-3\">\n        <h1 className=\"h-12\">Manage Genres</h1>\n        <GenreForm\n          value={name}\n          setValue={setName}\n          handleSubmit={handleCreateGenre}\n        />\n\n        <br />\n\n        <div className=\"flex flex-wrap\">\n          {genres?.map((genre) => (\n            <div key={genre._id}>\n              <button\n                className=\"bg-white border border-teal-500 text-teal-500 py-2 px-4 rounded-lg m-3 hover:bg-teal-500 hover:text-white focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-opacity-50\"\n                onClick={() => {\n                  {\n                    setModalVisible(true);\n                    setSelectedGenre(genre);\n                    setUpdatingName(genre.name);\n                  }\n                }}\n              >\n                {genre.name}\n              </button>\n            </div>\n          ))}\n        </div>\n\n        <Modal isOpen={modalVisible} onClose={() => setModalVisible(false)}>\n          <GenreForm\n            value={updatingName}\n            setValue={(value) => setUpdatingName(value)}\n            handleSubmit={handleUpdateGenre}\n            buttonText=\"Update\"\n            handleDelete={handleDeleteGenre}\n          />\n        </Modal>\n      </div>\n    </div>\n  );\n};\n\nexport default GenreList;\n"
  },
  {
    "path": "frontend/src/pages/Admin/UpdateMovie.jsx",
    "content": "import { useEffect, useState } from \"react\";\nimport { useParams, useNavigate } from \"react-router-dom\";\nimport {\n  useGetSpecificMovieQuery,\n  useUpdateMovieMutation,\n  useUploadImageMutation,\n  useDeleteMovieMutation,\n} from \"../../redux/api/movies\";\nimport { toast } from \"react-toastify\";\n\nconst UpdateMovie = () => {\n  const { id } = useParams();\n  const navigate = useNavigate();\n\n  const [movieData, setMovieData] = useState({\n    name: \"\",\n    year: 0,\n    detail: \"\",\n    cast: [],\n    ratings: 0,\n    image: null,\n  });\n\n  const [selectedImage, setSelectedImage] = useState(null);\n  const { data: initialMovieData } = useGetSpecificMovieQuery(id);\n\n  useEffect(() => {\n    if (initialMovieData) {\n      setMovieData(initialMovieData);\n    }\n  }, [initialMovieData]);\n\n  const [updateMovie, { isLoading: isUpdatingMovie }] =\n    useUpdateMovieMutation();\n\n  const [\n    uploadImage,\n    { isLoading: isUploadingImage, error: uploadImageErrorDetails },\n  ] = useUploadImageMutation();\n\n  const [deleteMovie] = useDeleteMovieMutation();\n\n  const handleChange = (e) => {\n    const { name, value } = e.target;\n    setMovieData((prevData) => ({\n      ...prevData,\n      [name]: value,\n    }));\n  };\n\n  const handleImageChange = (e) => {\n    const file = e.target.files[0];\n    setSelectedImage(file);\n  };\n\n  const handleUpdateMovie = async () => {\n    try {\n      if (\n        !movieData.name ||\n        !movieData.year ||\n        !movieData.detail ||\n        !movieData.cast\n      ) {\n        toast.error(\"Please fill in all required fields\");\n        return;\n      }\n\n      let uploadedImagePath = movieData.image;\n\n      if (selectedImage) {\n        const formData = new FormData();\n        formData.append(\"image\", selectedImage);\n\n        const uploadImageResponse = await uploadImage(formData);\n\n        if (uploadImageResponse.data) {\n          uploadedImagePath = uploadImageResponse.data.image;\n        } else {\n          console.error(\"Failed to upload image:\", uploadImageErrorDetails);\n          toast.error(\"Failed to upload image\");\n          return;\n        }\n      }\n\n      await updateMovie({\n        id: id,\n        updatedMovie: {\n          ...movieData,\n          image: uploadedImagePath,\n        },\n      });\n\n      navigate(\"/movies\");\n    } catch (error) {\n      console.error(\"Failed to update movie:\", error);\n    }\n  };\n\n  const handleDeleteMovie = async () => {\n    try {\n      toast.success(\"Movie deleted successfully\");\n      await deleteMovie(id);\n      navigate(\"/movies\");\n    } catch (error) {\n      console.error(\"Failed to delete movie:\", error);\n      toast.error(`Failed to delete movie: ${error?.message}`);\n    }\n  };\n\n  return (\n    <div className=\"container flex justify-center items-center mt-4\">\n      <form>\n        <p className=\"text-green-200 w-[50rem] text-2xl mb-4\">Update Movie</p>\n\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Name:\n            <input\n              type=\"text\"\n              name=\"name\"\n              value={movieData.name}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Year:\n            <input\n              type=\"number\"\n              name=\"year\"\n              value={movieData.year}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Detail:\n            <textarea\n              name=\"detail\"\n              value={movieData.detail}\n              onChange={handleChange}\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n        <div className=\"mb-4\">\n          <label className=\"block\">\n            Cast (comma-separated):\n            <input\n              type=\"text\"\n              name=\"cast\"\n              value={movieData.cast.join(\", \")}\n              onChange={(e) =>\n                setMovieData({ ...movieData, cast: e.target.value.split(\", \") })\n              }\n              className=\"border px-2 py-1 w-full\"\n            />\n          </label>\n        </div>\n\n        <div className=\"mb-4\">\n          <label\n            style={\n              !selectedImage\n                ? {\n                    border: \"1px solid #888\",\n                    borderRadius: \"5px\",\n                    padding: \"8px\",\n                  }\n                : {\n                    border: \"0\",\n                    borderRadius: \"0\",\n                    padding: \"0\",\n                  }\n            }\n          >\n            {!selectedImage && \"Upload Image\"}\n            <input\n              type=\"file\"\n              accept=\"image/*\"\n              onChange={handleImageChange}\n              style={{ display: !selectedImage ? \"none\" : \"block\" }}\n            />\n          </label>\n        </div>\n\n        <button\n          type=\"button\"\n          onClick={handleUpdateMovie}\n          className=\"bg-teal-500 text-white px-4 py-2 rounded\"\n          disabled={isUpdatingMovie || isUploadingImage}\n        >\n          {isUpdatingMovie || isUploadingImage ? \"Updating...\" : \"Update Movie\"}\n        </button>\n\n        <button\n          type=\"button\"\n          onClick={handleDeleteMovie}\n          className=\"bg-red-500 text-white px-4 py-2 rounded ml-2\"\n          disabled={isUpdatingMovie || isUploadingImage}\n        >\n          {isUpdatingMovie || isUploadingImage ? \"Deleting...\" : \"Delete Movie\"}\n        </button>\n      </form>\n    </div>\n  );\n};\nexport default UpdateMovie;\n"
  },
  {
    "path": "frontend/src/pages/Auth/Login.jsx",
    "content": "import { useState, useEffect } from \"react\";\nimport { Link, useLocation, useNavigate } from \"react-router-dom\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport Loader from \"../../component/Loader\";\nimport { setCredentials } from \"../../redux/features/auth/authSlice\";\nimport { useLoginMutation } from \"../../redux/api/users\";\nimport { toast } from \"react-toastify\";\n\nconst Login = () => {\n  const [email, setEmail] = useState(\"\");\n  const [password, setPassword] = useState(\"\");\n\n  const dispatch = useDispatch();\n  const navigate = useNavigate();\n\n  const [login, { isLoading }] = useLoginMutation();\n\n  const { userInfo } = useSelector((state) => state.auth);\n\n  const { search } = useLocation();\n  const sp = new URLSearchParams(search);\n  const redirect = sp.get(\"redirect\") || \"/\";\n\n  useEffect(() => {\n    if (userInfo) {\n      navigate(redirect);\n    }\n  }, [navigate, redirect, userInfo]);\n\n  const submitHandler = async (e) => {\n    e.preventDefault();\n\n    try {\n      const res = await login({ email, password }).unwrap();\n      dispatch(setCredentials({ ...res }));\n      navigate(redirect);\n    } catch (err) {\n      toast.error(err?.data?.message || err.error);\n    }\n  };\n\n  return (\n    <div>\n      <section className=\"pl-[10rem] flex flex-wrap\">\n        <div className=\"mr-[4rem] mt-[5rem]\">\n          <h1 className=\"text-2xl font-semibold mb-4\">Sign In</h1>\n\n          <form onSubmit={submitHandler} className=\"container w-[40rem]\">\n            <div className=\"my-[2rem]\">\n              <label\n                htmlFor=\"email\"\n                className=\"block text-sm font-medium text-white\"\n              >\n                Email Address\n              </label>\n              <input\n                type=\"email\"\n                id=\"email\"\n                className=\"mt-1 p-2 border rounded w-full\"\n                placeholder=\"Enter Email\"\n                value={email}\n                onChange={(e) => setEmail(e.target.value)}\n              />\n            </div>\n            <div className=\"my-[2rem]\">\n              <label\n                htmlFor=\"password\"\n                className=\"block text-sm font-medium text-white\"\n              >\n                Password\n              </label>\n              <input\n                type=\"password\"\n                id=\"password\"\n                className=\"mt-1 p-2 border rounded w-full\"\n                placeholder=\"Enter Password\"\n                value={password}\n                onChange={(e) => setPassword(e.target.value)}\n              />\n            </div>\n\n            <button\n              disabled={isLoading}\n              type=\"submit\"\n              className=\"bg-teal-500 text-white px-4 py-2 rounded cursor-pointer my-[1rem]\"\n            >\n              {isLoading ? \"Signing In ...\" : \"Sign In\"}\n            </button>\n            {isLoading && <Loader />}\n          </form>\n\n          <div className=\"mt-4\">\n            <p className=\"text-white\">\n              New Customer?{\" \"}\n              <Link\n                to={redirect ? `/register?redirect=${redirect}` : \"/register\"}\n                className=\"text-teal-500 hover:underline\"\n              >\n                Register\n              </Link>\n            </p>\n          </div>\n        </div>\n\n        <img\n          src=\"https://images.unsplash.com/photo-1485095329183-d0797cdc5676?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n          alt=\"\"\n          className=\"h-[65rem] w-[55%] xl:block md:hidden sm:hidden rounded-lg\"\n        />\n      </section>\n    </div>\n  );\n};\n\nexport default Login;\n"
  },
  {
    "path": "frontend/src/pages/Auth/Navigation.jsx",
    "content": "import { useState } from \"react\";\nimport {\n  AiOutlineHome,\n  AiOutlineLogin,\n  AiOutlineUserAdd,\n} from \"react-icons/ai\";\nimport { MdOutlineLocalMovies } from \"react-icons/md\";\nimport { Link } from \"react-router-dom\";\nimport { useNavigate } from \"react-router-dom\";\nimport { useSelector, useDispatch } from \"react-redux\";\nimport { useLogoutMutation } from \"../../redux/api/users\";\nimport { logout } from \"../../redux/features/auth/authSlice\";\n\nconst Navigation = () => {\n  const { userInfo } = useSelector((state) => state.auth);\n  const [dropdownOpen, setDropdownOpen] = useState(false);\n\n  const toggleDropdown = () => {\n    setDropdownOpen(!dropdownOpen);\n  };\n\n  const dispatch = useDispatch();\n  const navigate = useNavigate();\n\n  const [logoutApiCall] = useLogoutMutation();\n\n  const logoutHandler = async () => {\n    try {\n      await logoutApiCall().unwrap();\n      dispatch(logout());\n      navigate(\"/login\");\n    } catch (error) {\n      console.error(error);\n    }\n  };\n\n  return (\n    <div className=\"fixed bottom-10 left-[30rem] transform translate-x-1/2 translate-y-1/2 z-50 bg-[#0f0f0f] border w-[30%] px-[4rem] mb-[2rem] rounded\">\n      <section className=\"flex justify-between items-center\">\n        {/* Section 1 */}\n        <div className=\"flex justify-center items-center mb-[2rem]\">\n          <Link\n            to=\"/\"\n            className=\"flex items-center transition-transform transform hover:translate-x-2\"\n          >\n            <AiOutlineHome className=\"mr-2 mt-[3rem]\" size={26} />\n            <span className=\"hidden nav-item-name mt-[3rem]\">Home</span>\n          </Link>\n\n          <Link\n            to=\"/movies\"\n            className=\"flex items-center transition-transform transform hover:translate-x-2 ml-[1rem]\"\n          >\n            <MdOutlineLocalMovies className=\"mr-2 mt-[3rem]\" size={26} />\n            <span className=\"hidden nav-item-name mt-[3rem]\">SHOP</span>\n          </Link>\n        </div>\n        {/* Section 2 */}\n        <div className=\"relative\">\n          <button\n            onClick={toggleDropdown}\n            className=\"text-gray-800 focus:outline-none\"\n          >\n            {userInfo ? (\n              <span className=\"text-white\">{userInfo.username}</span>\n            ) : (\n              <></>\n            )}\n\n            {userInfo && (\n              <svg\n                xmlns=\"http://www.w3.org/2000/svg\"\n                className={`h-4 w-4 ml-1 ${\n                  dropdownOpen ? \"transform rotate-180\" : \"\"\n                }`}\n                fill=\"none\"\n                viewBox=\"0 0 24 24\"\n                stroke=\"white\"\n              >\n                <path\n                  strokeLinecap=\"round\"\n                  strokeLinejoin=\"round\"\n                  strokeWidth=\"2\"\n                  d={dropdownOpen ? \"M5 15l7-7 7 7\" : \"M19 9l-7 7-7-7\"}\n                />\n              </svg>\n            )}\n          </button>\n\n          {dropdownOpen && userInfo && (\n            <ul\n              className={`absolute right-0 mt-2 mr-14 w-[10rem] space-y-2 bg-white text-gray-600 ${\n                !userInfo.isAdmin ? \"-top-20\" : \"-top-24\"\n              }`}\n            >\n              {userInfo.isAdmin && (\n                <>\n                  <li>\n                    <Link\n                      to=\"/admin/movies/dashboard\"\n                      className=\"block px-4 py-2 hover:bg-gray-100\"\n                    >\n                      Dashboard\n                    </Link>\n                  </li>\n                </>\n              )}\n\n              <li>\n                <Link\n                  to=\"/profile\"\n                  className=\"block px-4 py-2 hover:bg-gray-100\"\n                >\n                  Profile\n                </Link>\n              </li>\n\n              <li>\n                <button\n                  onClick={logoutHandler}\n                  className=\"block w-full px-4 py-2 text-left hover:bg-gray-100\"\n                >\n                  Logout\n                </button>\n              </li>\n            </ul>\n          )}\n\n          {!userInfo && (\n            <ul className=\"flex\">\n              <li>\n                <Link\n                  to=\"/login\"\n                  className=\"flex items-center mt-5 transition-transform transform hover:translate-x-2 mb-[2rem]\"\n                >\n                  <AiOutlineLogin className=\"mr-2 mt-[4px]\" size={26} />\n                  <span className=\"hidden nav-item-name\">LOGIN</span>\n                </Link>\n              </li>\n\n              <li>\n                <Link\n                  to=\"/register\"\n                  className=\"flex items-center mt-5 transition-transform transform hover:translate-x-2 ml-[1rem]\"\n                >\n                  <AiOutlineUserAdd size={26} />\n                  <span className=\"hidden nav-item-name\">REGISTER</span>\n                </Link>\n              </li>\n            </ul>\n          )}\n        </div>\n      </section>\n    </div>\n  );\n};\n\nexport default Navigation;\n"
  },
  {
    "path": "frontend/src/pages/Auth/PrivateRoute.jsx",
    "content": "import { Navigate, Outlet } from \"react-router-dom\";\nimport { useSelector } from \"react-redux\";\n\nconst PrivateRoute = () => {\n  const { userInfo } = useSelector((state) => state.auth);\n  return userInfo ? <Outlet /> : <Navigate to=\"/login\" replace />;\n};\n\nexport default PrivateRoute;\n"
  },
  {
    "path": "frontend/src/pages/Auth/Register.jsx",
    "content": "import { useState, useEffect } from \"react\";\nimport { Link, useLocation, useNavigate } from \"react-router-dom\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport Loader from \"../../component/Loader\";\nimport { setCredentials } from \"../../redux/features/auth/authSlice\";\nimport { useRegisterMutation } from \"../../redux/api/users\";\nimport { toast } from \"react-toastify\";\n\nconst Register = () => {\n  const [username, setUsername] = useState(\"\");\n  const [email, setEmail] = useState(\"\");\n  const [password, setPassword] = useState(\"\");\n  const [confirmPassword, setConfirmPassword] = useState(\"\");\n\n  const dispatch = useDispatch();\n  const navigate = useNavigate();\n\n  const [register, { isLoading }] = useRegisterMutation();\n\n  const { userInfo } = useSelector((state) => state.auth);\n\n  const { search } = useLocation();\n  const sp = new URLSearchParams(search);\n  const redirect = sp.get(\"redirect\") || \"/\";\n\n  useEffect(() => {\n    if (userInfo) {\n      navigate(redirect);\n    }\n  }, [navigate, redirect, userInfo]);\n\n  const submitHandler = async (e) => {\n    e.preventDefault();\n\n    if (password !== confirmPassword) {\n      toast.error(\"Password do not match\");\n    } else {\n      try {\n        const res = await register({ username, email, password }).unwrap();\n        dispatch(setCredentials({ ...res }));\n        navigate(redirect);\n        toast.success(\"User successfully registered.\");\n      } catch (err) {\n        console.log(err);\n        toast.error(err.data.message);\n      }\n    }\n  };\n\n  return (\n    <div className=\"pl-[10rem] flex flex-wrap\">\n      <div className=\"mr-[4rem] mt-[5rem]\">\n        <h1 className=\"text-2xl font-semibold mb-4\">Register</h1>\n\n        <form onSubmit={submitHandler} className=\"container w-[40rem]\">\n          <div className=\"my-[2rem]\">\n            <label\n              htmlFor=\"name\"\n              className=\"block text-sm font-medium text-white\"\n            >\n              Name\n            </label>\n            <input\n              type=\"text\"\n              id=\"name\"\n              className=\"mt-1 p-2 border rounded w-full\"\n              placeholder=\"Enter Name\"\n              value={username}\n              onChange={(e) => setUsername(e.target.value)}\n            />\n          </div>\n          <div className=\"my-[2rem]\">\n            <label\n              htmlFor=\"email\"\n              className=\"block text-sm font-medium text-white\"\n            >\n              Email Address\n            </label>\n            <input\n              type=\"email\"\n              id=\"email\"\n              className=\"mt-1 p-2 border rounded w-full\"\n              placeholder=\"Enter Email\"\n              value={email}\n              onChange={(e) => setEmail(e.target.value)}\n            />\n          </div>\n          <div className=\"my-[2rem]\">\n            <label\n              htmlFor=\"password\"\n              className=\"block text-sm font-medium text-white\"\n            >\n              Password\n            </label>\n            <input\n              type=\"password\"\n              id=\"password\"\n              className=\"mt-1 p-2 border rounded w-full\"\n              placeholder=\"Enter Password\"\n              value={password}\n              onChange={(e) => setPassword(e.target.value)}\n            />\n          </div>\n          <div className=\"my-[2rem]\">\n            <label\n              htmlFor=\"confirmPassword\"\n              className=\"block text-sm font-medium text-white\"\n            >\n              Confirm Password\n            </label>\n            <input\n              type=\"password\"\n              id=\"confirmPassword\"\n              className=\"mt-1 p-2 border rounded w-full\"\n              placeholder=\"Confirm Password\"\n              value={confirmPassword}\n              onChange={(e) => setConfirmPassword(e.target.value)}\n            />\n          </div>\n\n          <button\n            disabled={isLoading}\n            type=\"submit\"\n            className=\"bg-teal-500 text-white px-4 py-2 rounded cursor-pointer my-[1rem]\"\n          >\n            {isLoading ? \"Registering...\" : \"Register\"}\n          </button>\n\n          {isLoading && <Loader />}\n        </form>\n\n        <div className=\"mt-4\">\n          <p className=\"text-white\">\n            Already have an account?{\" \"}\n            <Link\n              to={redirect ? `/login?redirect=${redirect}` : \"/login\"}\n              className=\"text-teal-500 hover:underline\"\n            >\n              Login\n            </Link>\n          </p>\n        </div>\n      </div>\n      <img\n        src=\"https://images.unsplash.com/photo-1489599849927-2ee91cede3ba?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D\"\n        alt=\"\"\n        className=\"h-[65rem] w-[55%] xl:block md:hidden sm:hidden rounded-lg\"\n      />\n    </div>\n  );\n};\nexport default Register;\n"
  },
  {
    "path": "frontend/src/pages/Home.jsx",
    "content": "import Header from \"./Movies/Header\";\nimport MoviesContainerPage from \"./Movies/MoviesContainerPage\";\n\nconst Home = () => {\n  return (\n    <>\n      <Header />\n\n      <section className=\"mt-[10rem]\">\n        <MoviesContainerPage />\n      </section>\n    </>\n  );\n};\n\nexport default Home;\n"
  },
  {
    "path": "frontend/src/pages/Movies/AllMovies.jsx",
    "content": "import { useGetAllMoviesQuery } from \"../../redux/api/movies\";\nimport { useFetchGenresQuery } from \"../../redux/api/genre\";\nimport {\n  useGetNewMoviesQuery,\n  useGetTopMoviesQuery,\n  useGetRandomMoviesQuery,\n} from \"../../redux/api/movies\";\nimport MovieCard from \"./MovieCard\";\nimport { useEffect } from \"react\";\nimport { useSelector, useDispatch } from \"react-redux\";\nimport banner from \"../../assets/banner.jpg\";\nimport {\n  setMoviesFilter,\n  setFilteredMovies,\n  setMovieYears,\n  setUniqueYears,\n} from \"../../redux/features/movies/moviesSlice\";\n\nconst AllMovies = () => {\n  const dispatch = useDispatch();\n  const { data } = useGetAllMoviesQuery();\n  const { data: genres } = useFetchGenresQuery();\n  const { data: newMovies } = useGetNewMoviesQuery();\n  const { data: topMovies } = useGetTopMoviesQuery();\n  const { data: randomMovies } = useGetRandomMoviesQuery();\n\n  const { moviesFilter, filteredMovies } = useSelector((state) => state.movies);\n\n  const movieYears = data?.map((movie) => movie.year);\n  const uniqueYears = Array.from(new Set(movieYears));\n\n  useEffect(() => {\n    dispatch(setFilteredMovies(data || []));\n    dispatch(setMovieYears(movieYears));\n    dispatch(setUniqueYears(uniqueYears));\n  }, [data, dispatch]);\n\n  const handleSearchChange = (e) => {\n    dispatch(setMoviesFilter({ searchTerm: e.target.value }));\n\n    const filteredMovies = data.filter((movie) =>\n      movie.name.toLowerCase().includes(e.target.value.toLowerCase())\n    );\n\n    dispatch(setFilteredMovies(filteredMovies));\n  };\n\n  const handleGenreClick = (genreId) => {\n    const filterByGenre = data.filter((movie) => movie.genre === genreId);\n    dispatch(setFilteredMovies(filterByGenre));\n  };\n\n  const handleYearChange = (year) => {\n    const filterByYear = data.filter((movie) => movie.year === +year);\n    dispatch(setFilteredMovies(filterByYear));\n  };\n\n  const handleSortChange = (sortOption) => {\n    switch (sortOption) {\n      case \"new\":\n        dispatch(setFilteredMovies(newMovies));\n        break;\n      case \"top\":\n        dispatch(setFilteredMovies(topMovies));\n        break;\n      case \"random\":\n        dispatch(setFilteredMovies(randomMovies));\n        break;\n\n      default:\n        dispatch(setFilteredMovies([]));\n        break;\n    }\n  };\n\n  return (\n    <div className=\"grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 -translate-y-[5rem]\">\n      <>\n        <section>\n          <div\n            className=\"relative h-[50rem] w-screen mb-10 flex items-center justify-center bg-cover\"\n            style={{ backgroundImage: `url(${banner})` }}\n          >\n            <div className=\"absolute inset-0 bg-gradient-to-b from-gray-800 to-black opacity-60\"></div>\n\n            <div className=\"relative z-10 text-center text-white mt-[10rem]\">\n              <h1 className=\"text-8xl font-bold mb-4\">The Movies Hub</h1>\n              <p className=\"text-2xl\">\n                Cinematic Odyssey: Unveiling the Magic of Movies\n              </p>\n            </div>\n\n            <section className=\"absolute -bottom-[5rem]\">\n              <input\n                type=\"text\"\n                className=\"w-[100%] h-[5rem] border px-10 outline-none rounded\"\n                placeholder=\"Search Movie\"\n                value={moviesFilter.searchTerm}\n                onChange={handleSearchChange}\n              />\n              <section className=\"sorts-container mt-[2rem] ml-[10rem]  w-[30rem]\">\n                <select\n                  className=\"border p-2 rounded text-black\"\n                  value={moviesFilter.selectedGenre}\n                  onChange={(e) => handleGenreClick(e.target.value)}\n                >\n                  <option value=\"\">Genres</option>\n                  {genres?.map((genre) => (\n                    <option key={genre._id} value={genre._id}>\n                      {genre.name}\n                    </option>\n                  ))}\n                </select>\n\n                <select\n                  className=\"border p-2 rounded ml-4 text-black\"\n                  value={moviesFilter.selectedYear}\n                  onChange={(e) => handleYearChange(e.target.value)}\n                >\n                  <option value=\"\">Year</option>\n                  {uniqueYears.map((year) => (\n                    <option key={year} value={year}>\n                      {year}\n                    </option>\n                  ))}\n                </select>\n\n                <select\n                  className=\"border p-2 rounded ml-4 text-black\"\n                  value={moviesFilter.selectedSort}\n                  onChange={(e) => handleSortChange(e.target.value)}\n                >\n                  <option value=\"\">Sort By</option>\n                  <option value=\"new\">New Movies</option>\n                  <option value=\"top\">Top Movies</option>\n                  <option value=\"random\">Random Movies</option>\n                </select>\n              </section>\n            </section>\n          </div>\n\n          <section className=\"mt-[10rem] w-screen flex justify-center items-center flex-wrap\">\n            {filteredMovies?.map((movie) => (\n              <MovieCard key={movie._id} movie={movie} />\n            ))}\n          </section>\n        </section>\n      </>\n    </div>\n  );\n};\n\nexport default AllMovies;\n"
  },
  {
    "path": "frontend/src/pages/Movies/Header.jsx",
    "content": "import SliderUtil from \"../../component/SliderUtil\";\nimport { useGetNewMoviesQuery } from \"../../redux/api/movies\";\nimport { Link } from \"react-router-dom\";\n\nconst Header = () => {\n  const { data } = useGetNewMoviesQuery();\n\n  return (\n    <div className=\"flex flex-col mt-[2rem] ml-[2rem] md:flex-row justify-between items-center md:items-start\">\n      <nav className=\"w-full md:w-[10rem] ml-0 md:ml-2 mb-4 md:mb-0\">\n        <Link\n          to=\"/\"\n          className=\"transition duration-300 ease-in-out hover:bg-teal-200  block p-2 rounded mb-1 md:mb-2 text-lg\"\n        >\n          Home\n        </Link>\n        <Link\n          to=\"/movies\"\n          className=\"transition duration-300 ease-in-out hover:bg-teal-200  block p-2 rounded mb-1 md:mb-2 text-lg\"\n        >\n          Browse Movies\n        </Link>\n      </nav>\n\n      <div className=\"w-full md:w-[80%] mr-0 md:mr-2\">\n        <SliderUtil data={data} />\n      </div>\n    </div>\n  );\n};\n\nexport default Header;\n"
  },
  {
    "path": "frontend/src/pages/Movies/MovieCard.jsx",
    "content": "import { Link } from \"react-router-dom\";\n\nconst MovieCard = ({ movie }) => {\n  return (\n    <div key={movie._id} className=\"relative group m-[2rem]\">\n      <Link to={`/movies/${movie._id}`}>\n        <img\n          src={movie.image}\n          alt={movie.name}\n          className=\"w-[20rem] h-[20rem] rounded m-0 p-0 transition duration-300 ease-in-out transform group-hover:opacity-50\"\n        />\n      </Link>\n\n      <p className=\"absolute top-[85%] left-[2rem] right-0 bottom-0 opacity-0 transition duration-300 ease-in-out group-hover:opacity-100\">\n        {movie.name}\n      </p>\n    </div>\n  );\n};\n\nexport default MovieCard;\n"
  },
  {
    "path": "frontend/src/pages/Movies/MovieDetails.jsx",
    "content": "import { useState } from \"react\";\nimport { useParams, Link } from \"react-router-dom\";\nimport { useSelector } from \"react-redux\";\nimport { toast } from \"react-toastify\";\nimport {\n  useGetSpecificMovieQuery,\n  useAddMovieReviewMutation,\n} from \"../../redux/api/movies\";\nimport MovieTabs from \"./MovieTabs\";\n\nconst MovieDetails = () => {\n  const { id: movieId } = useParams();\n  const [rating, setRating] = useState(0);\n  const [comment, setComment] = useState(\"\");\n  const { data: movie, refetch } = useGetSpecificMovieQuery(movieId);\n  const { userInfo } = useSelector((state) => state.auth);\n  const [createReview, { isLoading: loadingMovieReview }] =\n    useAddMovieReviewMutation();\n\n  const submitHandler = async (e) => {\n    e.preventDefault();\n\n    try {\n      await createReview({\n        id: movieId,\n        rating,\n        comment,\n      }).unwrap();\n\n      refetch();\n\n      toast.success(\"Review created successfully\");\n    } catch (error) {\n      toast.error(error.data || error.message);\n    }\n  };\n\n  return (\n    <>\n      <div>\n        <Link\n          to=\"/\"\n          className=\"  text-white font-semibold hover:underline ml-[20rem]\"\n        >\n          Go Back\n        </Link>\n      </div>\n\n      <div className=\"mt-[2rem]\">\n        <div className=\"flex justify-center items-center\">\n          <img\n            src={movie?.image}\n            alt={movie?.name}\n            className=\"w-[70%] rounded\"\n          />\n        </div>\n        {/* Container One */}\n        <div className=\"container  flex justify-between ml-[20rem] mt-[3rem]\">\n          <section>\n            <h2 className=\"text-5xl my-4 font-extrabold\">{movie?.name}</h2>\n            <p className=\"my-4 xl:w-[35rem] lg:w-[35rem] md:w-[30rem] text-[#B0B0B0]\">\n              {movie?.detail}\n            </p>\n          </section>\n\n          <div className=\"mr-[5rem]\">\n            <p className=\"text-2xl font-semibold\">\n              Releasing Date: {movie?.year}\n            </p>\n\n            <div>\n              {movie?.cast.map((c) => (\n                <ul key={c._id}>\n                  <li className=\"mt-[1rem]\">{c}</li>\n                </ul>\n              ))}\n            </div>\n          </div>\n        </div>\n\n        <div className=\"container ml-[20rem]\">\n          <MovieTabs\n            loadingMovieReview={loadingMovieReview}\n            userInfo={userInfo}\n            submitHandler={submitHandler}\n            rating={rating}\n            setRating={setRating}\n            comment={comment}\n            setComment={setComment}\n            movie={movie}\n          />\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default MovieDetails;\n"
  },
  {
    "path": "frontend/src/pages/Movies/MovieTabs.jsx",
    "content": "import { Link } from \"react-router-dom\";\n\nconst MovieTabs = ({ userInfo, submitHandler, comment, setComment, movie }) => {\n  return (\n    <div>\n      <section>\n        {userInfo ? (\n          <form onSubmit={submitHandler}>\n            <div className=\"my-2\">\n              <label htmlFor=\"comment\" className=\"block text-xl mb-2\">\n                Write Your Review\n              </label>\n\n              <textarea\n                id=\"comment\"\n                rows=\"3\"\n                required\n                value={comment}\n                onChange={(e) => setComment(e.target.value)}\n                className=\"p-2 border rounded-lg xl:w-[40rem] text-black\"\n              ></textarea>\n            </div>\n\n            <button\n              type=\"submit\"\n              className=\"bg-teal-600 text-white py-2 px-4 rounded-lg\"\n            >\n              Submit\n            </button>\n          </form>\n        ) : (\n          <p>\n            Please <Link to=\"/login\">Sign In</Link> to write a review\n          </p>\n        )}\n      </section>\n\n      <section className=\"mt-[3rem]\">\n        <div>{movie?.reviews.length === 0 && <p>No Reviews</p>}</div>\n\n        <div>\n          {movie?.reviews.map((review) => (\n            <div\n              key={review._id}\n              className=\"bg-[#1A1A1A] p-4 rounded-lg w-[50%] mt-[2rem]\"\n            >\n              <div className=\"flex justify-between\">\n                <strong className=\"text-[#B0B0B0]\">{review.name}</strong>\n                <p className=\"text-[#B0B0B0]\">\n                  {review.createdAt.substring(0, 10)}\n                </p>\n              </div>\n\n              <p className=\"my-4\">{review.comment}</p>\n            </div>\n          ))}\n        </div>\n      </section>\n    </div>\n  );\n};\n\nexport default MovieTabs;\n"
  },
  {
    "path": "frontend/src/pages/Movies/MoviesContainerPage.jsx",
    "content": "import { useState } from \"react\";\nimport {\n  useGetNewMoviesQuery,\n  useGetTopMoviesQuery,\n  useGetRandomMoviesQuery,\n} from \"../../redux/api/movies\";\n\nimport { useFetchGenresQuery } from \"../../redux/api/genre\";\nimport SliderUtil from \"../../component/SliderUtil\";\n\nconst MoviesContainerPage = () => {\n  const { data } = useGetNewMoviesQuery();\n  const { data: topMovies } = useGetTopMoviesQuery();\n  const { data: genres } = useFetchGenresQuery();\n  const { data: randomMovies } = useGetRandomMoviesQuery();\n\n  const [selectedGenre, setSelectedGenre] = useState(null);\n\n  const handleGenreClick = (genreId) => {\n    setSelectedGenre(genreId);\n  };\n\n  const filteredMovies = data?.filter(\n    (movie) => selectedGenre === null || movie.genre === selectedGenre\n  );\n\n  return (\n    <div className=\"flex flex-col lg:flex-row lg:justify-between items-center\">\n      <nav className=\" ml-[4rem] flex flex-row xl:flex-col lg:flex-col md:flex-row sm:flex-row\">\n        {genres?.map((g) => (\n          <button\n            key={g._id}\n            className={`transition duration-300 ease-in-out hover:bg-gray-200 block p-2 rounded mb-[1rem] text-lg ${\n              selectedGenre === g._id ? \"bg-gray-200\" : \"\"\n            }`}\n            onClick={() => handleGenreClick(g._id)}\n          >\n            {g.name}\n          </button>\n        ))}\n      </nav>\n\n      <section className=\"flex flex-col justify-center items-center w-full lg:w-auto\">\n        <div className=\"w-full lg:w-[100rem] mb-8 \">\n          <h1 className=\"mb-5\">Choose For You</h1>\n          <SliderUtil data={randomMovies} />\n        </div>\n\n        <div className=\"w-full lg:w-[100rem] mb-8\">\n          <h1 className=\"mb-5\">Top Movies</h1>\n          <SliderUtil data={topMovies} />\n        </div>\n\n        <div className=\"w-full lg:w-[100rem] mb-8\">\n          <h1 className=\"mb-5\">Choose Movie</h1>\n          <SliderUtil data={filteredMovies} />\n        </div>\n      </section>\n    </div>\n  );\n};\n\nexport default MoviesContainerPage;\n"
  },
  {
    "path": "frontend/src/pages/User/Profile.jsx",
    "content": "import { useEffect, useState } from \"react\";\nimport { useDispatch, useSelector } from \"react-redux\";\nimport { toast } from \"react-toastify\";\nimport Loader from \"../../component/Loader\";\nimport { useProfileMutation } from \"../../redux/api/users\";\nimport { setCredentials } from \"../../redux/features/auth/authSlice\";\n\nconst Profile = () => {\n  const [username, setUsername] = useState(\"\");\n  const [email, setEmail] = useState(\"\");\n  const [password, setPassword] = useState(\"\");\n  const [confirmPassword, setConfirmPassword] = useState(\"\");\n\n  const { userInfo } = useSelector((state) => state.auth);\n\n  const [updateProfile, { isLoading: loadingUpdateProfile }] =\n    useProfileMutation();\n\n  useEffect(() => {\n    setUsername(userInfo.username);\n    setEmail(userInfo.email);\n  }, [userInfo.email, userInfo.username]);\n\n  const dispatch = useDispatch();\n\n  const submitHandler = async (e) => {\n    e.preventDefault();\n\n    if (password !== confirmPassword) {\n      toast.error(\"Passwords do not match\");\n    } else {\n      try {\n        const res = await updateProfile({\n          _id: userInfo._id,\n          username,\n          email,\n          password,\n        }).unwrap();\n        dispatch(setCredentials({ ...res }));\n        toast.success(\"Profile updated successfully\");\n      } catch (err) {\n        toast.error(err?.data?.message || err.error);\n      }\n    }\n  };\n\n  return (\n    <div>\n      <div className=\"container mx-auto p-4 mt-[10rem]\">\n        <div className=\"flex justify-center align-center md:flex md:space-x-4\">\n          <div className=\"md:w-1/3\">\n            <h2 className=\"text-2xl font-semibold mb-4\">Update Profile</h2>\n\n            <form onSubmit={submitHandler}>\n              <div className=\"mb-4\">\n                <label className=\"block text-white mb-2\">Name</label>\n                <input\n                  type=\"text\"\n                  placeholder=\"Enter name\"\n                  className=\"form-input p-4 rounded-sm w-full\"\n                  value={username}\n                  onChange={(e) => setUsername(e.target.value)}\n                />\n              </div>\n              <div className=\"mb-4\">\n                <label className=\"block text-white mb-2\">Email Address</label>\n                <input\n                  type=\"email\"\n                  placeholder=\"Enter email\"\n                  className=\"form-input p-4 rounded-sm w-full\"\n                  value={email}\n                  onChange={(e) => setEmail(e.target.value)}\n                />\n              </div>\n              <div className=\"mb-4\">\n                <label className=\"block text-white mb-2\">Password</label>\n                <input\n                  type=\"password\"\n                  placeholder=\"Enter password\"\n                  className=\"form-input p-4 rounded-sm w-full\"\n                  value={password}\n                  onChange={(e) => setPassword(e.target.value)}\n                />\n              </div>\n              <div className=\"mb-4\">\n                <label className=\"block text-white mb-2\">\n                  Confirm Password\n                </label>\n                <input\n                  type=\"password\"\n                  placeholder=\"Confirm Password\"\n                  className=\"form-input p-4 rounded-sm w-full\"\n                  value={confirmPassword}\n                  onChange={(e) => setConfirmPassword(e.target.value)}\n                />\n              </div>\n\n              <div className=\"flex justify-between\">\n                <button\n                  type=\"submit\"\n                  className=\"bg-teal-500 w-screen mt-[2rem] font-bold text-white py-2 px-4 rounded hover:bg-teal-600\"\n                >\n                  Update\n                </button>\n\n                {loadingUpdateProfile && <Loader />}\n              </div>\n            </form>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default Profile;\n"
  },
  {
    "path": "frontend/src/redux/api/apiSlice.js",
    "content": "import { fetchBaseQuery, createApi } from \"@reduxjs/toolkit/query/react\";\nimport { BASE_URL } from \"../constants\";\n\nconst baseQuery = fetchBaseQuery({ baseUrl: BASE_URL });\n\nexport const apiSlice = createApi({\n  baseQuery,\n  endpoints: () => ({}),\n});\n"
  },
  {
    "path": "frontend/src/redux/api/genre.js",
    "content": "import { apiSlice } from \"./apiSlice\";\nimport { GENRE_URL } from \"../constants\";\n\nexport const genreApiSlice = apiSlice.injectEndpoints({\n  endpoints: (builder) => ({\n    createGenre: builder.mutation({\n      query: (newGenre) => ({\n        url: `${GENRE_URL}`,\n        method: \"POST\",\n        body: newGenre,\n      }),\n    }),\n\n    updateGenre: builder.mutation({\n      query: ({ id, updateGenre }) => ({\n        url: `${GENRE_URL}/${id}`,\n        method: \"PUT\",\n        body: updateGenre,\n      }),\n    }),\n\n    deleteGenre: builder.mutation({\n      query: (id) => ({\n        url: `${GENRE_URL}/${id}`,\n        method: \"DELETE\",\n      }),\n    }),\n\n    fetchGenres: builder.query({\n      query: () => `${GENRE_URL}/genres`,\n    }),\n  }),\n});\n\nexport const {\n  useCreateGenreMutation,\n  useUpdateGenreMutation,\n  useDeleteGenreMutation,\n  useFetchGenresQuery,\n} = genreApiSlice;\n"
  },
  {
    "path": "frontend/src/redux/api/movies.js",
    "content": "import { apiSlice } from \"./apiSlice\";\nimport { MOVIE_URL, UPLOAD_URL } from \"../constants\";\n\nexport const moviesApiSlice = apiSlice.injectEndpoints({\n  endpoints: (builder) => ({\n    getAllMovies: builder.query({\n      query: () => `${MOVIE_URL}/all-movies`,\n    }),\n    createMovie: builder.mutation({\n      query: (newMovie) => ({\n        url: `${MOVIE_URL}/create-movie`,\n        method: \"POST\",\n        body: newMovie,\n      }),\n    }),\n\n    updateMovie: builder.mutation({\n      query: ({ id, updatedMovie }) => ({\n        url: `${MOVIE_URL}/update-movie/${id}`,\n        method: \"PUT\",\n        body: updatedMovie,\n      }),\n    }),\n\n    addMovieReview: builder.mutation({\n      query: ({ id, rating, comment }) => ({\n        url: `${MOVIE_URL}/${id}/reviews`,\n        method: \"POST\",\n        body: { rating, id, comment },\n      }),\n    }),\n\n    deleteComment: builder.mutation({\n      query: ({ movieId, reviewId }) => ({\n        url: `${MOVIE_URL}/delete-comment`,\n        method: \"DELETE\",\n        body: { movieId, reviewId },\n      }),\n    }),\n\n    deleteMovie: builder.mutation({\n      query: (id) => ({\n        url: `${MOVIE_URL}/delete-movie/${id}`,\n        method: \"DELETE\",\n      }),\n    }),\n\n    getSpecificMovie: builder.query({\n      query: (id) => `${MOVIE_URL}/specific-movie/${id}`,\n    }),\n\n    uploadImage: builder.mutation({\n      query: (formData) => ({\n        url: `${UPLOAD_URL}`,\n        method: \"POST\",\n        body: formData,\n      }),\n    }),\n\n    getNewMovies: builder.query({\n      query: () => `${MOVIE_URL}/new-movies`,\n    }),\n\n    getTopMovies: builder.query({\n      query: () => `${MOVIE_URL}/top-movies`,\n    }),\n\n    getRandomMovies: builder.query({\n      query: () => `${MOVIE_URL}/random-movies`,\n    }),\n  }),\n});\n\nexport const {\n  useGetAllMoviesQuery,\n  useCreateMovieMutation,\n  useUpdateMovieMutation,\n  useAddMovieReviewMutation,\n  useDeleteCommentMutation,\n  useGetSpecificMovieQuery,\n  useUploadImageMutation,\n  useDeleteMovieMutation,\n  //\n  useGetNewMoviesQuery,\n  useGetTopMoviesQuery,\n  useGetRandomMoviesQuery,\n} = moviesApiSlice;\n"
  },
  {
    "path": "frontend/src/redux/api/users.js",
    "content": "import { apiSlice } from \"./apiSlice\";\nimport { USERS_URL } from \"../constants\";\n\nexport const userApiSlice = apiSlice.injectEndpoints({\n  endpoints: (builder) => ({\n    login: builder.mutation({\n      query: (data) => ({\n        url: `${USERS_URL}/auth`,\n        method: \"POST\",\n        body: data,\n      }),\n    }),\n\n    register: builder.mutation({\n      query: (data) => ({\n        url: `${USERS_URL}`,\n        method: \"POST\",\n        body: data,\n      }),\n    }),\n\n    logout: builder.mutation({\n      query: () => ({\n        url: `${USERS_URL}/logout`,\n        method: \"POST\",\n      }),\n    }),\n\n    profile: builder.mutation({\n      query: (data) => ({\n        url: `${USERS_URL}/profile`,\n        method: \"PUT\",\n        body: data,\n      }),\n    }),\n\n    getUsers: builder.query({\n      query: () => ({\n        url: USERS_URL,\n      }),\n    }),\n  }),\n});\n\nexport const {\n  useLoginMutation,\n  useRegisterMutation,\n  useLogoutMutation,\n  useProfileMutation,\n  useGetUsersQuery,\n} = userApiSlice;\n"
  },
  {
    "path": "frontend/src/redux/constants.js",
    "content": "export const BASE_URL = \"\";\nexport const USERS_URL = \"/api/v1/users\";\nexport const GENRE_URL = \"/api/v1/genre\";\nexport const MOVIE_URL = \"/api/v1/movies\";\nexport const UPLOAD_URL = \"/api/v1/upload\";\n"
  },
  {
    "path": "frontend/src/redux/features/auth/authSlice.js",
    "content": "import { createSlice } from \"@reduxjs/toolkit\";\n\nconst initialState = {\n  userInfo: localStorage.getItem(\"userInfo\")\n    ? JSON.parse(localStorage.getItem(\"userInfo\"))\n    : null,\n};\n\nconst authSlice = createSlice({\n  name: \"auth\",\n  initialState,\n  reducers: {\n    setCredentials: (state, action) => {\n      state.userInfo = action.payload;\n      localStorage.setItem(\"userInfo\", JSON.stringify(action.payload));\n\n      const expirationTime = new Date().getTime() + 30 * 24 * 60 * 60 * 1000;\n      localStorage.setItem(\"expirationTime\", expirationTime);\n    },\n\n    logout: (state) => {\n      state.userInfo = null;\n      localStorage.clear();\n    },\n  },\n});\n\nexport const { setCredentials, logout } = authSlice.actions;\nexport default authSlice.reducer;\n"
  },
  {
    "path": "frontend/src/redux/features/movies/moviesSlice.js",
    "content": "import { createSlice } from \"@reduxjs/toolkit\";\n\nconst moviesSlice = createSlice({\n  name: \"movies\",\n  initialState: {\n    moviesFilter: {\n      searchTerm: \"\",\n      selectedGenre: \"\",\n      selectedYear: \"\",\n      selectedSort: [],\n    },\n\n    filteredMovies: [],\n    movieYears: [],\n    uniqueYear: [],\n  },\n\n  reducers: {\n    setMoviesFilter: (state, action) => {\n      state.moviesFilter = { ...state.moviesFilter, ...action.payload };\n    },\n\n    setFilteredMovies: (state, action) => {\n      state.filteredMovies = action.payload;\n    },\n\n    setMovieYears: (state, action) => {\n      state.movieYears = action.payload;\n    },\n\n    setUniqueYears: (state, action) => {\n      state.uniqueYear = action.payload;\n    },\n  },\n});\n\nexport const {\n  setMoviesFilter,\n  setFilteredMovies,\n  setMovieYears,\n  setUniqueYears,\n} = moviesSlice.actions;\n\nexport default moviesSlice.reducer;\n"
  },
  {
    "path": "frontend/src/redux/store.js",
    "content": "import { configureStore } from \"@reduxjs/toolkit\";\nimport { setupListeners } from \"@reduxjs/toolkit/query/react\";\nimport { apiSlice } from \"./api/apiSlice\";\nimport authReducer from \"./features/auth/authSlice\";\nimport moviesReducer from \"../redux/features/movies/moviesSlice\";\n\nconst store = configureStore({\n  reducer: {\n    [apiSlice.reducerPath]: apiSlice.reducer,\n    auth: authReducer,\n    movies: moviesReducer,\n  },\n  middleware: (getDefaultMiddleware) =>\n    getDefaultMiddleware().concat(apiSlice.middleware),\n  devTools: true,\n});\n\nsetupListeners(store.dispatch);\nexport default store;\n"
  },
  {
    "path": "frontend/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./index.html\", \"./src/**/*.{js,ts,jsx,tsx}\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};\n"
  },
  {
    "path": "frontend/vite.config.js",
    "content": "import { defineConfig } from \"vite\";\nimport react from \"@vitejs/plugin-react\";\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react()],\n  server: {\n    proxy: {\n      \"/api/\": \"http://localhost:3000\",\n      \"/uploads/\": \"http://localhost:3000\",\n    },\n  },\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"my-movies\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"type\": \"module\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"fullstack\": \"concurrently \\\"npm run backend\\\" \\\"npm run frontend\\\"\",\n    \"backend\": \"nodemon backend/index.js\",\n    \"frontend\": \"cd frontend && npm run dev\"\n  },\n  \"keywords\": [],\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"bcryptjs\": \"^2.4.3\",\n    \"body-parser\": \"^1.20.2\",\n    \"concurrently\": \"^8.2.2\",\n    \"cookie-parser\": \"^1.4.6\",\n    \"dotenv\": \"^16.4.1\",\n    \"express\": \"^4.18.2\",\n    \"jsonwebtoken\": \"^9.0.2\",\n    \"mongoose\": \"^8.1.1\",\n    \"multer\": \"^1.4.5-lts.1\",\n    \"nodemon\": \"^3.0.3\"\n  }\n}\n"
  }
]