[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n.pnpm-debug.log*\n\n# local env files\n.env*.local\n.env\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"typescript.tsdk\": \"node_modules\\\\typescript\\\\lib\",\n  \"typescript.enablePromptUseWorkspaceTsdk\": true\n}"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\">\n\n  <img src=\"https://user-images.githubusercontent.com/99184393/185779974-a31a9f47-f8d3-42ea-b7f8-4a2971774615.png\" alt=\"logo\" width=\"250\" height=\"auto\" />\n  \n# Airbnb Clone with Next.js 13!\n  \n  <p>\nFull Stack Airbnb Clone with Next.js 13 Tailwind-css, Prisma, MongoDB, NextAuth, Framer-motionSocial, Login (Google and Facebook), Image upload, Cloudinary CDN, Location selection, Map component, Country autocomplete, Fetching listings with server components.\n  </p>\n  \n<!-- Badges -->\n<a href=\"https://airbnb-sclone.vercel.app\" target=\"_blank\">![](https://img.shields.io/website-up-down-green-red/http/monip.org.svg)</a>\n![](https://img.shields.io/badge/Maintained-Yes-indigo)\n![](https://img.shields.io/github/forks/SashenJayathilaka/Airbnb-Build.svg)\n![](https://img.shields.io/github/stars/SashenJayathilaka/Airbnb-Build.svg)\n![](https://img.shields.io/github/issues/SashenJayathilaka/Airbnb-Build)\n![](https://img.shields.io/github/last-commit/SashenJayathilaka/Airbnb-Build)\n\n<h4>\n    <a href=\"https://abproject-sclone.vercel.app\">View Demo</a>\n  <span> · </span>\n    <a href=\"https://github.com/SashenJayathilaka/Airbnb-Build/blob/master/README.md\">Documentation</a>\n  <span> · </span>\n    <a href=\"https://github.com/SashenJayathilaka/Airbnb-Build/issues\">Report Bug</a>\n  <span> · </span>\n    <a href=\"https://github.com/SashenJayathilaka/Airbnb-Build/issues\">Request Feature</a>\n  </h4>\n</div>\n\n<br />\n\n<!-- Table of Contents -->\n\n## :notebook_with_decorative_cover: Table of Contents\n\n- [About the Project](#star2-about-the-project)\n  - [Screenshots](#camera-screenshots)\n  - [Tech Stack](#space_invader-tech-stack)\n  - [Environment Variables](#key-environment-variables)\n- [Getting Started](#toolbox-getting-started)\n  - [Prerequisites](#bangbang-prerequisites)\n  - [Installation](#gear-installation)\n  - [Run Locally](#running-run-locally)\n  - [Deployment](#triangular_flag_on_post-deployment)\n- [Contact](#handshake-contact)\n\n<!-- About the Project -->\n\n## :star2: About the Project\n\n<!-- Screenshots -->\n\n### :camera: Screenshots\n\n- Reservation functionality & Description and Price, Listing creation, Listing card component\n\n<div align=\"center\">\n<a href=\"https://airbnb-sclone.vercel.app\"><img  src='./demo/ezgif-4-27f1be5f56.gif' alt='image'/></a>\n</div>\n\n<br />\n\n- Searching functionality Favorite functionality, Individual Listing View, Listing reservation component\n\n<div align=\"center\">\n<a href=\"https://airbnb-sclone.vercel.app\"><img  src='./demo/ezgif-4-8ac9db77ff.gif' alt='image'/></a>\n</div>\n\n## <a href=\"https://airbnb-sclone.vercel.app\" target=\"_blank\">LIVE DEMO 💥</a>\n\n![forthebadge](https://forthebadge.com/images/badges/built-with-love.svg)\n![forthebadge](https://forthebadge.com/images/badges/for-you.svg)\n![forthebadge](https://forthebadge.com/images/badges/powered-by-coffee.svg)\n\n### :space_invader: Tech Stack\n\n<details>\n  <summary>Client</summary>\n  <ul>\n    <li><a href=\"https://#/\">Typescript</a></li>\n    <li><a href=\"https://nextjs.org/\">Next.js</a></li>\n    <li><a href=\"https://reactjs.org/\">React.js</a></li>\n    <li><a href=\"https://tailwindcss.com/\">TailwindCSS</a></li>\n    <li><a href=\"https://www.prisma.io\">Prisma</a></li>\n  </ul>\n</details>\n\n<details>\n<summary>Database</summary>\n  <ul>\n  <li><a href=\"https://firebase.google.com\">Mongodb</a></li>\n  <li><a href=\"https://cloudinary.com/\">Cloudinary</a></li>\n  </ul>\n</details>\n\n<br />\n\n<table>\n    <tr>\n        <td>\n<a href=\"#\"><img src=\"https://raw.githubusercontent.com/devicons/devicon/master/icons/react/react-original.svg\" alt=\"\" width=\"30\" height=\"30\" /></a>\n        </td>\n                <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/183096870-fdf58e59-d78c-44f4-bd1c-f9033c16d907.png\" alt=\"Google\" width=\"30\" height=\"30\" /></a>\n        </td>\n                        <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/179383376-874f547c-4e6f-4826-850e-706b009e7e2b.png\" alt=\"\" width=\"30\" height=\"30\" /></a>\n        </td>\n                              <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/181918664-569af962-756c-438c-b350-294f042e6f61.png\" alt=\"\" width=\"30\" height=\"30\" /></a>\n        </td>\n                        <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/180462270-ea4a249c-627c-4479-9431-5c3fd25454c4.png\" alt=\"\" width=\"30\" height=\"30\" /></a>\n        </td>\n                                <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/185779974-a31a9f47-f8d3-42ea-b7f8-4a2971774615.png\" alt=\"\" width=\"30\"height=\"30\"/></a>\n        </td>\n                                      <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/229775276-a7cb148b-7fbd-4334-a07f-f2223bc49f62.png\" alt=\"\" width=\"30\"height=\"30\"/></a>\n        </td>\n      <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/204170976-0e5c6e2a-2b41-483d-adbd-d5d1e40b8d15.png\" alt=\"\" width=\"30\"height=\"30\"/></a>\n        </td>\n        <td>\n<a href=\"#\"><img src=\"https://user-images.githubusercontent.com/99184393/214867309-7b59fa0e-c872-484e-bc8f-462896c54d2a.png\" alt=\"\" height=\"30\"/></a>\n        </td>\n    </tr>\n</table>\n\n## :toolbox: Getting Started\n\n### :bangbang: Prerequisites\n\n- Install Node JS in your computer <a href='https://nodejs.org/en/'>HERE</a>\n- Sign up for a Cloudinary account <a href='https://cloudinary.com/'>HERE</a>\n- Sign up for a Google Cloud Platform <a href='https://console.cloud.google.com/'>HERE</a>\n- Sign up for a Meta for Developers <a href='https://developers.facebook.com'>HERE</a>\n- Get Lookup APi Key <a href='https://extreme-ip-lookup.com/'>HERE</a>\n\n<!-- Env Variables -->\n\n### :key: Environment Variables\n\nTo run this project, you will need to add the following environment variables to your .env file\n\n`DATABASE_URL`\n\n`GOOGLE_CLIENT_ID`\n\n`GOOGLE_CLIENT_SECRET`\n\n`FACEBOOK_ID`\n\n`FACEBOOK_SECRET`\n\n`NEXTAUTH_SECRET`\n\n`NEXTAUTH_URL`\n\n`NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME`\n\n`NEXT_PUBLIC_LOOKUP_KEY`\n\nThis project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\n### :gear: Installation\n\n![](https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB)\n![](https://img.shields.io/badge/next.js-20232A?style=for-the-badge&logo=next.js&logoColor=61DAFB)\n\nInstall my-project with npm\n\n```\nnpx create-next-app@latest my-project --typescript --eslint\n```\n\n```\ncd my-project\n```\n\nInstall dependencies\n\n### :test_tube: Install Tailwind CSS with Next.js\n\n#### Install Tailwind CSS\n\n![](https://img.shields.io/badge/Tailwind_CSS-38B2AC?style=for-the-badge&logo=tailwind-css&logoColor=white)\n\nInstall tailwindcss and its peer dependencies via npm, and then run the init command to generate both `tailwind.config.js` and `postcss.config.js`.\n\n```\nnpm install -D tailwindcss postcss autoprefixer\n```\n\n```\nnpx tailwindcss init -p\n```\n\n#### Configure your template paths\n\nAdd the paths to all of your template files in your `tailwind.config.js` file.\n<br>\n\n```js\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    \"./app/**/*.{js,ts,jsx,tsx}\",\n    \"./pages/**/*.{js,ts,jsx,tsx}\",\n    \"./components/**/*.{js,ts,jsx,tsx}\",\n\n    // Or if using `src` directory:\n    \"./src/**/*.{js,ts,jsx,tsx}\",\n  ],\n  theme: {\n    extend: {},\n  },\n  plugins: [],\n};\n```\n\n#### Add the Tailwind directives to your CSS\n\nAdd the `@tailwind` directives for each of Tailwind’s layers to your `./styles/globals.css` file.\n\n```css\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\n```\n\nInstall dependencies\n\n<a href=\"https://github.com/SashenJayathilaka/Airbnb-Build/blob/master/package.json\" target=\"_blank\">🔶 Dependency Info</a>\n\n<!-- Run Locally -->\n\n### :running: Run Locally\n\n![](https://img.shields.io/badge/GIT-E44C30?style=for-the-badge&logo=git&logoColor=white)\n\nClone the project\n\n```bash\n  git clone https://github.com/SashenJayathilaka/Airbnb-Build.git\n```\n\nchange directory\n\n```bash\n  cd Airbnb-Build\n```\n\nInstall dependencies\n\n```bash\n  npm install\n```\n\nStart the server\n\n```bash\n  npm run dev\n```\n\n<hr />\n\nThis is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n<hr />\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.\n\n[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.\n\nThe `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.\n\n### Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n<!-- Deployment -->\n\n### :triangular_flag_on_post: Deployment\n\nTo deploy this project run\n\n##### Deploy on Vercel\n\n![](https://img.shields.io/badge/Vercel-000000?style=for-the-badge&logo=vercel&logoColor=white)\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n\n## :handshake: Contact\n\nSashen - [@twitter_handle](https://twitter.com/SashenHasinduJ) - sashenjayathilaka95@gmail.com\n\nProject Link: [https://github.com/SashenJayathilaka/Airbnb-Build.git](https://github.com/SashenJayathilaka/Airbnb-Build.git)\n\n<br />\n\n<div align=\"center\">\n<a href=\"https://airbnb-sclone.vercel.app\"><img  src='https://user-images.githubusercontent.com/99184393/229773559-72e7f64a-361d-4285-976a-00a8919dd783.png' alt='image'/></a>\n</div>\n\n<br />\n\n<div align=\"center\">Don't forget to leave a star ⭐️</div>\n"
  },
  {
    "path": "app/actions/getCurrentUser.ts",
    "content": "import prisma from \"@/lib/prismadb\";\nimport { authOptions } from \"@/pages/api/auth/[...nextauth]\";\nimport { getServerSession } from \"next-auth/next\";\n\nexport async function getSession() {\n  return await getServerSession(authOptions);\n}\n\nexport default async function getCurrentUser() {\n  try {\n    const session = await getSession();\n\n    if (!session?.user?.email) {\n      return null;\n    }\n\n    const currentUser = await prisma.user.findUnique({\n      where: {\n        email: session.user.email as string,\n      },\n    });\n\n    if (!currentUser) {\n      return null;\n    }\n\n    return {\n      ...currentUser,\n      createdAt: currentUser.createdAt.toISOString(),\n      updatedAt: currentUser.updatedAt.toISOString(),\n      emailVerified: currentUser.emailVerified?.toISOString() || null,\n    };\n  } catch (error: any) {\n    console.log(\n      \"🚀 ~ file: getCurrentUser.ts:13 ~ getCurrentUser ~ error:\",\n      error\n    );\n  }\n}\n"
  },
  {
    "path": "app/actions/getFavoriteListings.ts",
    "content": "import prisma from \"@/lib/prismadb\";\nimport getCurrentUser from \"./getCurrentUser\";\n\nexport default async function getFavoriteListings() {\n  try {\n    const currentUser = await getCurrentUser();\n\n    if (!currentUser) {\n      return [];\n    }\n\n    const favorites = await prisma.listing.findMany({\n      where: {\n        id: {\n          in: [...(currentUser.favoriteIds || [])],\n        },\n      },\n    });\n\n    const safeFavorite = favorites.map((favorite) => ({\n      ...favorite,\n      createdAt: favorite.createdAt.toString(),\n    }));\n\n    return safeFavorite;\n  } catch (error: any) {\n    throw new Error(error.message);\n  }\n}\n"
  },
  {
    "path": "app/actions/getListingById.ts",
    "content": "import prisma from \"@/lib/prismadb\";\n\ninterface IParams {\n  listingId?: string;\n}\n\nexport default async function getListingById(params: IParams) {\n  try {\n    const { listingId } = params;\n\n    const listing = await prisma.listing.findUnique({\n      where: {\n        id: listingId,\n      },\n      include: {\n        user: true,\n      },\n    });\n\n    if (!listing) {\n      return null;\n    }\n\n    return {\n      ...listing,\n      createdAt: listing.createdAt.toString(),\n      user: {\n        ...listing.user,\n        createdAt: listing.user.createdAt.toString(),\n        updatedAt: listing.user.updatedAt.toString(),\n        emailVerified: listing.user.emailVerified?.toString() || null,\n      },\n    };\n  } catch (error: any) {\n    throw new Error(error.message);\n  }\n}\n"
  },
  {
    "path": "app/actions/getListings.ts",
    "content": "import prisma from \"@/lib/prismadb\";\n\nexport interface IListingsParams {\n  userId?: string;\n  guestCount?: number;\n  roomCount?: number;\n  bathroomCount?: number;\n  startDate?: string;\n  endDate?: string;\n  locationValue?: string;\n  category?: string;\n}\n\nexport default async function getListings(params: IListingsParams) {\n  try {\n    const {\n      userId,\n      roomCount,\n      guestCount,\n      bathroomCount,\n      locationValue,\n      startDate,\n      endDate,\n      category,\n    } = params;\n\n    let query: any = {};\n\n    if (userId) {\n      query.userId = userId;\n    }\n\n    if (category) {\n      query.category = category;\n    }\n\n    if (roomCount) {\n      query.roomCount = {\n        gte: +roomCount,\n      };\n    }\n\n    if (guestCount) {\n      query.guestCount = {\n        gte: +guestCount,\n      };\n    }\n\n    if (bathroomCount) {\n      query.bathroomCount = {\n        gte: +bathroomCount,\n      };\n    }\n\n    if (locationValue) {\n      query.locationValue = locationValue;\n    }\n\n    if (startDate && endDate) {\n      query.NOT = {\n        reservations: {\n          some: {\n            OR: [\n              {\n                endDate: { gte: startDate },\n                startDate: { lte: startDate },\n              },\n              {\n                startDate: { lte: endDate },\n                endDate: { gte: endDate },\n              },\n            ],\n          },\n        },\n      };\n    }\n\n    const listing = await prisma.listing.findMany({\n      where: query,\n      orderBy: {\n        createdAt: \"desc\",\n      },\n    });\n\n    const safeListings = listing.map((list) => ({\n      ...list,\n      createdAt: list.createdAt.toISOString(),\n    }));\n\n    return safeListings;\n  } catch (error: any) {\n    throw new Error(error.message);\n  }\n}\n"
  },
  {
    "path": "app/actions/getReservations.ts",
    "content": "import prisma from \"@/lib/prismadb\";\n\ninterface IParams {\n  listingId?: string;\n  userId?: string;\n  authorId?: string;\n}\n\nexport default async function getReservation(params: IParams) {\n  try {\n    const { listingId, userId, authorId } = params;\n\n    const query: any = {};\n\n    if (listingId) {\n      query.listingId = listingId;\n    }\n\n    if (userId) {\n      query.userId = userId;\n    }\n\n    if (authorId) {\n      query.listing = { userId: authorId };\n    }\n\n    const reservation = await prisma.reservation.findMany({\n      where: query,\n      include: {\n        listing: true,\n      },\n      orderBy: {\n        createdAt: \"desc\",\n      },\n    });\n\n    const safeReservations = reservation.map((reservation) => ({\n      ...reservation,\n      createdAt: reservation.createdAt.toISOString(),\n      startDate: reservation.startDate.toISOString(),\n      endDate: reservation.endDate.toISOString(),\n      listing: {\n        ...reservation.listing,\n        createdAt: reservation.listing.createdAt.toISOString(),\n      },\n    }));\n\n    return safeReservations;\n  } catch (error: any) {\n    throw new Error(error.message);\n  }\n}\n"
  },
  {
    "path": "app/api/favorites/[listingId]/route.ts",
    "content": "import getCurrentUser from \"@/app/actions/getCurrentUser\";\nimport prisma from \"@/lib/prismadb\";\nimport { NextResponse } from \"next/server\";\n\ninterface IPrisma {\n  listingId?: string;\n}\n\nexport async function POST(request: Request, { params }: { params: IPrisma }) {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return NextResponse.error();\n  }\n\n  const { listingId } = params;\n\n  if (!listingId || typeof listingId !== \"string\") {\n    throw new Error(\"Invalid Id\");\n  }\n\n  let favoriteIds = [...(currentUser.favoriteIds || [])];\n\n  favoriteIds.push(listingId);\n\n  const user = await prisma.user.update({\n    where: {\n      id: currentUser.id,\n    },\n    data: {\n      favoriteIds,\n    },\n  });\n\n  return NextResponse.json(user);\n}\n\nexport async function DELETE(\n  request: Request,\n  { params }: { params: IPrisma }\n) {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return NextResponse.error();\n  }\n\n  const { listingId } = params;\n\n  if (!listingId || typeof listingId !== \"string\") {\n    throw new Error(\"Invalid Id\");\n  }\n\n  let favoriteIds = [...(currentUser.favoriteIds || [])];\n\n  favoriteIds = favoriteIds.filter((id) => id !== listingId);\n\n  const user = await prisma.user.update({\n    where: {\n      id: currentUser.id,\n    },\n    data: {\n      favoriteIds,\n    },\n  });\n\n  return NextResponse.json(user);\n}\n"
  },
  {
    "path": "app/api/listings/[listingId]/route.ts",
    "content": "import { NextResponse } from \"next/server\";\nimport getCurrentUser from \"@/app/actions/getCurrentUser\";\nimport prisma from \"@/lib/prismadb\";\n\ninterface IParams {\n  listingId?: string;\n}\n\nexport async function DELETE(\n  request: Request,\n  { params }: { params: IParams }\n) {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return NextResponse.error();\n  }\n\n  const { listingId } = params;\n\n  if (!listingId || typeof listingId !== \"string\") {\n    throw new Error(\"Invalid Id\");\n  }\n\n  const listing = await prisma.listing.deleteMany({\n    where: {\n      id: listingId,\n      userId: currentUser.id,\n    },\n  });\n\n  return NextResponse.json(listing);\n}\n"
  },
  {
    "path": "app/api/listings/route.ts",
    "content": "import getCurrentUser from \"@/app/actions/getCurrentUser\";\nimport prisma from \"@/lib/prismadb\";\nimport { NextResponse } from \"next/server\";\n\nexport async function POST(request: Request) {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return NextResponse.error();\n  }\n\n  const body = await request.json();\n  const {\n    title,\n    description,\n    imageSrc,\n    category,\n    roomCount,\n    bathroomCount,\n    guestCount,\n    location,\n    price,\n  } = body;\n\n  Object.keys(body).forEach((value: any) => {\n    if (!body[value]) {\n      NextResponse.error();\n    }\n  });\n\n  const listen = await prisma.listing.create({\n    data: {\n      title,\n      description,\n      imageSrc,\n      category,\n      roomCount,\n      bathroomCount,\n      guestCount,\n      locationValue: location.value,\n      price: parseInt(price, 10),\n      userId: currentUser.id,\n    },\n  });\n\n  return NextResponse.json(listen);\n}\n"
  },
  {
    "path": "app/api/register/route.ts",
    "content": "import prisma from \"@/lib/prismadb\";\nimport bcrypt from \"bcrypt\";\nimport { NextResponse } from \"next/server\";\n\nexport async function POST(request: Request) {\n  const body = await request.json();\n  const { email, name, password } = body;\n\n  const hashedPassword = await bcrypt.hash(password, 12);\n\n  const user = await prisma.user.create({\n    data: {\n      email,\n      name,\n      hashedPassword,\n    },\n  });\n\n  return NextResponse.json(user);\n}\n"
  },
  {
    "path": "app/api/reservations/[reservationId]/route.ts",
    "content": "import getCurrentUser from \"@/app/actions/getCurrentUser\";\nimport prisma from \"@/lib/prismadb\";\nimport { NextResponse } from \"next/server\";\n\ninterface IParams {\n  reservationId?: string;\n}\n\nexport async function DELETE(\n  request: Request,\n  { params }: { params: IParams }\n) {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return NextResponse.error();\n  }\n\n  const { reservationId } = params;\n\n  if (!reservationId || typeof reservationId !== \"string\") {\n    throw new Error(\"Invalid Id\");\n  }\n\n  const reservation = await prisma.reservation.deleteMany({\n    where: {\n      id: reservationId,\n      OR: [{ userId: currentUser.id }, { listing: { userId: currentUser.id } }],\n    },\n  });\n\n  return NextResponse.json(reservation);\n}\n"
  },
  {
    "path": "app/api/reservations/route.ts",
    "content": "import getCurrentUser from \"@/app/actions/getCurrentUser\";\nimport prisma from \"@/lib/prismadb\";\nimport { NextResponse } from \"next/server\";\n\nexport async function POST(request: Request) {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return NextResponse.error();\n  }\n\n  const body = await request.json();\n\n  const { listingId, startDate, endDate, totalPrice } = body;\n\n  if (!listingId || !startDate || !endDate || !totalPrice) {\n    return NextResponse.error();\n  }\n\n  const listenAndReservation = await prisma.listing.update({\n    where: {\n      id: listingId,\n    },\n    data: {\n      reservations: {\n        create: {\n          userId: currentUser.id,\n          startDate,\n          endDate,\n          totalPrice,\n        },\n      },\n    },\n  });\n\n  return NextResponse.json(listenAndReservation);\n}\n"
  },
  {
    "path": "app/error.tsx",
    "content": "\"use client\";\n\nimport EmptyState from \"@/components/EmptyState\";\nimport { useEffect } from \"react\";\n\ntype Props = {\n  error: Error;\n};\n\nfunction ErrorState({ error }: Props) {\n  useEffect(() => {\n    console.log(\"🚀 ~ file: error.tsx:12 ~ ErrorState ~ error:\", error);\n  }, [error]);\n\n  return <EmptyState title=\"Uh Oh\" subtitle=\"Something went wrong!\" />;\n}\n\nexport default ErrorState;\n"
  },
  {
    "path": "app/favorites/FavoritesClient.tsx",
    "content": "import Container from \"@/components/Container\";\nimport Heading from \"@/components/Heading\";\nimport ListingCard from \"@/components/listing/ListingCard\";\nimport { SafeUser, safeListing } from \"@/types\";\n\ntype Props = {\n  listings: safeListing[];\n  currentUser?: SafeUser | null;\n};\n\nfunction FavoritesClient({ listings, currentUser }: Props) {\n  return (\n    <Container>\n      <Heading title=\"Favorites\" subtitle=\"List of places you favorites!\" />\n      <div className=\"mt-10 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5 gap-8\">\n        {listings.map((listing) => (\n          <ListingCard\n            currentUser={currentUser}\n            key={listing.id}\n            data={listing}\n          />\n        ))}\n      </div>\n    </Container>\n  );\n}\n\nexport default FavoritesClient;\n"
  },
  {
    "path": "app/favorites/page.tsx",
    "content": "import ClientOnly from \"@/components/ClientOnly\";\nimport EmptyState from \"@/components/EmptyState\";\nimport React from \"react\";\nimport getCurrentUser from \"../actions/getCurrentUser\";\nimport getFavoriteListings from \"../actions/getFavoriteListings\";\nimport FavoritesClient from \"./FavoritesClient\";\n\ntype Props = {};\n\nconst FavoritePage = async (props: Props) => {\n  const currentUser = await getCurrentUser();\n  const listings = await getFavoriteListings();\n\n  if (!currentUser) {\n    return (\n      <ClientOnly>\n        <EmptyState title=\"Unauthorized\" subtitle=\"Please login\" />\n      </ClientOnly>\n    );\n  }\n\n  if (listings.length === 0) {\n    return (\n      <ClientOnly>\n        <EmptyState\n          title=\"No favorites found\"\n          subtitle=\"Looks like you have no favorite listings.\"\n        />\n      </ClientOnly>\n    );\n  }\n\n  return (\n    <ClientOnly>\n      <FavoritesClient listings={listings} currentUser={currentUser} />\n    </ClientOnly>\n  );\n};\n\nexport default FavoritePage;\n"
  },
  {
    "path": "app/layout.tsx",
    "content": "import ClientOnly from \"@/components/ClientOnly\";\nimport Footer from \"@/components/Footer\";\nimport ToastContainerBar from \"@/components/ToastContainerBar\";\nimport LoginModal from \"@/components/models/LoginModal\";\nimport RegisterModal from \"@/components/models/RegisterModal\";\nimport RentModal from \"@/components/models/RentModal\";\nimport SearchModal from \"@/components/models/SearchModal\";\nimport Navbar from \"@/components/navbar/Navbar\";\nimport { Nunito } from \"next/font/google\";\nimport \"../styles/globals.css\";\nimport getCurrentUser from \"./actions/getCurrentUser\";\n\nexport const metadata = {\n  title: \"Airbnb Clone\",\n  description: \"Airbnb Clone\",\n  icons: \"https://www.seekpng.com/png/full/957-9571167_airbnb-png.png\",\n};\n\nconst font = Nunito({\n  subsets: [\"latin\"],\n});\n\nexport default async function RootLayout({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  const currentUser = await getCurrentUser();\n\n  return (\n    <html lang=\"en\">\n      <body className={font.className}>\n        <ClientOnly>\n          <ToastContainerBar />\n          <SearchModal />\n          <RegisterModal />\n          <LoginModal />\n          <RentModal />\n          <Navbar currentUser={currentUser} />\n        </ClientOnly>\n        <div className=\"pb-20 pt-28\">{children}</div>\n        <Footer />\n      </body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "app/listings/[listingId]/page.tsx",
    "content": "import getCurrentUser from \"@/app/actions/getCurrentUser\";\nimport getListingById from \"@/app/actions/getListingById\";\nimport getReservation from \"@/app/actions/getReservations\";\nimport ClientOnly from \"@/components/ClientOnly\";\nimport EmptyState from \"@/components/EmptyState\";\nimport ListingClient from \"@/components/ListingClient\";\n\ninterface IParams {\n  listingId?: string;\n}\n\nconst ListingPage = async ({ params }: { params: IParams }) => {\n  const listing = await getListingById(params);\n  const reservations = await getReservation(params);\n  const currentUser = await getCurrentUser();\n\n  if (!listing) {\n    return (\n      <ClientOnly>\n        <EmptyState />\n      </ClientOnly>\n    );\n  }\n\n  return (\n    <ClientOnly>\n      <ListingClient\n        listing={listing}\n        currentUser={currentUser}\n        reservations={reservations}\n      />\n    </ClientOnly>\n  );\n};\n\nexport default ListingPage;\n"
  },
  {
    "path": "app/loading.tsx",
    "content": "import Loader from \"@/components/Loader\";\n\ntype Props = {};\n\nfunction Loading({}: Props) {\n  return <Loader />;\n}\n\nexport default Loading;\n"
  },
  {
    "path": "app/page.tsx",
    "content": "import ClientOnly from \"@/components/ClientOnly\";\nimport Container from \"@/components/Container\";\nimport EmptyState from \"@/components/EmptyState\";\nimport ListingCard from \"@/components/listing/ListingCard\";\nimport getCurrentUser from \"./actions/getCurrentUser\";\nimport getListings, { IListingsParams } from \"./actions/getListings\";\n\ninterface HomeProps {\n  searchParams: IListingsParams;\n}\n\nexport default async function Home({ searchParams }: HomeProps) {\n  const listing = await getListings(searchParams);\n  const currentUser = await getCurrentUser();\n\n  if (listing.length === 0) {\n    return (\n      <ClientOnly>\n        <EmptyState showReset />\n      </ClientOnly>\n    );\n  }\n\n  return (\n    <ClientOnly>\n      <Container>\n        <div className=\"pt-24 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-4 gap-8 overflow-x-hidden\">\n          {listing.map((list) => {\n            return (\n              <ListingCard\n                key={list.id}\n                data={list}\n                currentUser={currentUser}\n              />\n            );\n          })}\n        </div>\n      </Container>\n    </ClientOnly>\n  );\n}\n"
  },
  {
    "path": "app/properties/PropertiesClient.tsx",
    "content": "\"use client\";\n\nimport Container from \"@/components/Container\";\nimport Heading from \"@/components/Heading\";\nimport ListingCard from \"@/components/listing/ListingCard\";\nimport { SafeUser, safeListing } from \"@/types\";\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\nimport { useCallback, useState } from \"react\";\nimport { toast } from \"react-toastify\";\n\ntype Props = {\n  listings: safeListing[];\n  currentUser?: SafeUser | null;\n};\n\nfunction PropertiesClient({ listings, currentUser }: Props) {\n  const router = useRouter();\n  const [deletingId, setDeletingId] = useState(\"\");\n\n  const onDelete = useCallback(\n    (id: string) => {\n      setDeletingId(id);\n\n      axios\n        .delete(`/api/listings/${id}`)\n        .then(() => {\n          toast.info(\"Listing deleted\");\n          router.refresh();\n        })\n        .catch((error) => {\n          toast.error(error?.response?.data?.error);\n        })\n        .finally(() => {\n          setDeletingId(\"\");\n        });\n    },\n    [router]\n  );\n\n  return (\n    <Container>\n      <Heading title=\"Properties\" subtitle=\"List of your properties\" />\n      <div className=\"mt-10 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5 gap-8\">\n        {listings.map((listing: any) => (\n          <ListingCard\n            key={listing.id}\n            data={listing}\n            actionId={listing.id}\n            onAction={onDelete}\n            disabled={deletingId === listing.id}\n            actionLabel=\"Delete property\"\n            currentUser={currentUser}\n          />\n        ))}\n      </div>\n    </Container>\n  );\n}\n\nexport default PropertiesClient;\n"
  },
  {
    "path": "app/properties/page.tsx",
    "content": "import ClientOnly from \"@/components/ClientOnly\";\nimport EmptyState from \"@/components/EmptyState\";\nimport getCurrentUser from \"../actions/getCurrentUser\";\nimport getListings from \"../actions/getListings\";\nimport PropertiesClient from \"./PropertiesClient\";\n\ntype Props = {};\n\nconst PropertiesPage = async (props: Props) => {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return (\n      <ClientOnly>\n        <EmptyState title=\"Unauthorized\" subtitle=\"Please login\" />\n      </ClientOnly>\n    );\n  }\n\n  const listings = await getListings({ userId: currentUser.id });\n\n  if (listings.length === 0) {\n    return (\n      <ClientOnly>\n        <EmptyState\n          title=\"No Properties found\"\n          subtitle=\"Looks like you have not any Properties\"\n        />\n      </ClientOnly>\n    );\n  }\n  return (\n    <ClientOnly>\n      <PropertiesClient listings={listings} currentUser={currentUser} />\n    </ClientOnly>\n  );\n};\n\nexport default PropertiesPage;\n"
  },
  {
    "path": "app/reservations/ReservationsClient.tsx",
    "content": "\"use client\";\n\nimport { SafeReservation, SafeUser } from \"@/types\";\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\nimport React, { useCallback, useState } from \"react\";\nimport { toast } from \"react-toastify\";\n\nimport Container from \"@/components/Container\";\nimport Heading from \"@/components/Heading\";\nimport ListingCard from \"@/components/listing/ListingCard\";\n\ntype Props = {\n  reservations: SafeReservation[];\n  currentUser?: SafeUser | null;\n};\n\nfunction ReservationsClient({ reservations, currentUser }: Props) {\n  const router = useRouter();\n  const [deletingId, setDeletingId] = useState(\"\");\n\n  const onCancel = useCallback(\n    (id: string) => {\n      setDeletingId(id);\n\n      axios\n        .delete(`/api/reservations/${id}`)\n        .then(() => {\n          toast.info(\"Reservation cancelled\");\n          router.refresh();\n        })\n        .catch((error) => {\n          toast.error(error?.response?.data?.error);\n        })\n        .finally(() => {\n          setDeletingId(\"\");\n        });\n    },\n    [router]\n  );\n\n  return (\n    <Container>\n      <Heading title=\"Reservations\" subtitle=\"Bookings on your properties\" />\n      <div className=\"mt-10 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5 gap-8\">\n        {reservations.map((reservation) => (\n          <ListingCard\n            key={reservation.id}\n            data={reservation.listing}\n            reservation={reservation}\n            actionId={reservation.id}\n            onAction={onCancel}\n            disabled={deletingId === reservation.id}\n            actionLabel=\"Cancel reservation\"\n            currentUser={currentUser}\n          />\n        ))}\n      </div>\n    </Container>\n  );\n}\n\nexport default ReservationsClient;\n"
  },
  {
    "path": "app/reservations/page.tsx",
    "content": "import ClientOnly from \"@/components/ClientOnly\";\nimport EmptyState from \"@/components/EmptyState\";\nimport React from \"react\";\nimport getCurrentUser from \"../actions/getCurrentUser\";\nimport getReservation from \"../actions/getReservations\";\nimport ReservationsClient from \"./ReservationsClient\";\n\ntype Props = {};\n\nconst ReservationsPage = async (props: Props) => {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return (\n      <ClientOnly>\n        <EmptyState title=\"Unauthorized\" subtitle=\"Please login\" />\n      </ClientOnly>\n    );\n  }\n\n  const reservations = await getReservation({\n    authorId: currentUser.id,\n  });\n\n  if (reservations.length === 0) {\n    return (\n      <ClientOnly>\n        <EmptyState\n          title=\"No Reservation found\"\n          subtitle=\"Looks like you have no reservations on your properties.\"\n        />\n      </ClientOnly>\n    );\n  }\n\n  return (\n    <ClientOnly>\n      <ReservationsClient\n        reservations={reservations}\n        currentUser={currentUser}\n      />\n    </ClientOnly>\n  );\n};\n\nexport default ReservationsPage;\n"
  },
  {
    "path": "app/trips/TripsClient.tsx",
    "content": "\"use client\";\n\nimport Container from \"@/components/Container\";\nimport Heading from \"@/components/Heading\";\nimport ListingCard from \"@/components/listing/ListingCard\";\nimport { SafeReservation, SafeUser } from \"@/types\";\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\nimport React, { useCallback, useState } from \"react\";\nimport { toast } from \"react-toastify\";\n\ntype Props = {\n  reservations: SafeReservation[];\n  currentUser?: SafeUser | null;\n};\n\nfunction TripsClient({ reservations, currentUser }: Props) {\n  const router = useRouter();\n  const [deletingId, setDeletingId] = useState(\"\");\n\n  const onCancel = useCallback(\n    (id: string) => {\n      setDeletingId(id);\n\n      axios\n        .delete(`/api/reservations/${id}`)\n        .then(() => {\n          toast.info(\"Reservation cancelled\");\n          router.refresh();\n        })\n        .catch((error) => {\n          toast.error(error?.response?.data?.error);\n        })\n        .finally(() => {\n          setDeletingId(\"\");\n        });\n    },\n    [router]\n  );\n\n  return (\n    <Container>\n      <Heading\n        title=\"Trips\"\n        subtitle=\"Where you've been and where you're going\"\n      />\n      <div className=\"mt-10 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-4 2xl:grid-cols-5 gap-8\">\n        {reservations.map((reservation) => (\n          <ListingCard\n            key={reservation.id}\n            data={reservation.listing}\n            reservation={reservation}\n            actionId={reservation.id}\n            onAction={onCancel}\n            disabled={deletingId === reservation.id}\n            actionLabel=\"Cancel reservation\"\n            currentUser={currentUser}\n          />\n        ))}\n      </div>\n    </Container>\n  );\n}\n\nexport default TripsClient;\n"
  },
  {
    "path": "app/trips/page.tsx",
    "content": "import ClientOnly from \"@/components/ClientOnly\";\nimport EmptyState from \"@/components/EmptyState\";\nimport React from \"react\";\nimport getCurrentUser from \"../actions/getCurrentUser\";\nimport getReservation from \"../actions/getReservations\";\nimport TripsClient from \"./TripsClient\";\n\ntype Props = {};\n\nconst TripsPage = async (props: Props) => {\n  const currentUser = await getCurrentUser();\n\n  if (!currentUser) {\n    return (\n      <ClientOnly>\n        <EmptyState title=\"Unauthorized\" subtitle=\"Please login\" />\n      </ClientOnly>\n    );\n  }\n\n  const reservations = await getReservation({\n    userId: currentUser.id,\n  });\n\n  if (reservations.length === 0) {\n    return (\n      <ClientOnly>\n        <EmptyState\n          title=\"No trips found\"\n          subtitle=\"Looks like you havent reserved any trips.\"\n        />\n      </ClientOnly>\n    );\n  }\n\n  return (\n    <ClientOnly>\n      <TripsClient reservations={reservations} currentUser={currentUser} />\n    </ClientOnly>\n  );\n};\n\nexport default TripsPage;\n"
  },
  {
    "path": "components/Avatar.tsx",
    "content": "\"use client\";\n\nimport Image from \"next/image\";\nimport React from \"react\";\n\ntype Props = {\n  src: string | null | undefined;\n  userName?: string | null | undefined;\n};\n\nfunction Avatar({ src, userName }: Props) {\n  return (\n    <div>\n      {src ? (\n        <Image\n          className=\"rounded-full\"\n          height=\"30\"\n          width=\"30\"\n          alt=\"hasImag\"\n          src={src}\n        />\n      ) : userName ? (\n        <img\n          className=\"rounded-full h-[30px] w-[30px]\"\n          alt=\"nameImage\"\n          src={`https://ui-avatars.com/api/?name=${userName}`}\n        />\n      ) : (\n        <Image\n          className=\"rounded-full\"\n          height=\"30\"\n          width=\"30\"\n          alt=\"noUser\"\n          src=\"/assets/avatar.png\"\n        />\n      )}\n    </div>\n  );\n}\n\nexport default Avatar;\n"
  },
  {
    "path": "components/Button.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { IconType } from \"react-icons\";\n\ntype Props = {\n  label: string;\n  onClick: (e: React.MouseEvent<HTMLButtonElement>) => void;\n  disabled?: boolean;\n  outline?: boolean;\n  small?: boolean;\n  icon?: IconType;\n  isColor?: boolean;\n};\n\nfunction Button({\n  label,\n  onClick,\n  disabled,\n  outline,\n  small,\n  icon: Icon,\n  isColor,\n}: Props) {\n  return (\n    <button\n      disabled={disabled}\n      onClick={onClick}\n      className={`relative disabled:opacity-70 disabled:cursor-not-allowed rounded-lg hover:opacity-80 transition w-full ${\n        outline ? \"bg-white\" : \"bg-rose-500\"\n      } ${outline ? \"border-black\" : \"border-rose-500\"} ${\n        outline ? \"text-black\" : \"text-white\"\n      } ${small ? \"text-sm\" : \"text-md\"} ${small ? \"py-1\" : \"py-3\"} ${\n        small ? \"font-light\" : \"font-semibold\"\n      } ${small ? \"border-[1px]\" : \"border-2\"}`}\n    >\n      {Icon && (\n        <Icon\n          size={24}\n          className={`absolute left-4 top-3 ${isColor && \"text-blue-600\"}`}\n        />\n      )}\n      {label}\n    </button>\n  );\n}\n\nexport default Button;\n"
  },
  {
    "path": "components/CategoryBox.tsx",
    "content": "\"use client\";\n\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport qs from \"query-string\";\nimport React, { useCallback } from \"react\";\nimport { IconType } from \"react-icons\";\n\ntype Props = {\n  icon: IconType;\n  label: string;\n  selected?: boolean;\n};\n\nfunction CategoryBox({ icon: Icon, label, selected }: Props) {\n  const router = useRouter();\n  const params = useSearchParams();\n\n  const handleClick = useCallback(() => {\n    let currentQuery = {};\n\n    if (params) {\n      currentQuery = qs.parse(params.toString());\n    }\n\n    const updatedQuery: any = {\n      ...currentQuery,\n      category: label,\n    };\n\n    if (params?.get(\"category\") === label) {\n      delete updatedQuery.category;\n    }\n\n    const url = qs.stringifyUrl(\n      {\n        url: \"/\",\n        query: updatedQuery,\n      },\n      { skipNull: true }\n    );\n\n    router.push(url);\n  }, [label, params, router]);\n\n  return (\n    <div\n      onClick={handleClick}\n      className={`flex flex-col items-center justify-center gap-2 p-3 border-b-2 hover:text-neutral-800 transition cursor-pointer ${\n        selected ? \"border-b-neutral-800\" : \"border-transparent\"\n      } ${selected ? \"text-neutral-800\" : \"text-neutral-500\"}`}\n    >\n      <Icon size={26} />\n      <div className=\"font-medium text-xs\">{label}</div>\n    </div>\n  );\n}\n\nexport default CategoryBox;\n"
  },
  {
    "path": "components/ClientOnly.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport React, { useEffect, useState } from \"react\";\n\ntype Props = {\n  children: React.ReactNode;\n};\n\nfunction ClientOnly({ children }: Props) {\n  const [hasMounted, setHasMounted] = useState(false);\n\n  useEffect(() => {\n    setHasMounted(true);\n  }, []);\n\n  if (!hasMounted) {\n    return null;\n  }\n\n  return (\n    <motion.div\n      initial={{ opacity: 0 }}\n      whileInView={{ opacity: 1 }}\n      viewport={{ once: true }}\n    >\n      {children}\n    </motion.div>\n  );\n}\n\nexport default ClientOnly;\n"
  },
  {
    "path": "components/Container.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\n\ntype Props = {\n  children: React.ReactNode;\n};\n\nfunction Container({ children }: Props) {\n  return (\n    <div className=\"max-w-[2520px] mx-auto xl:px-20 md:px-10 sm:px-2 px-4\">\n      {children}\n    </div>\n  );\n}\n\nexport default Container;\n"
  },
  {
    "path": "components/EmptyState.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { useRouter } from \"next/navigation\";\nimport React from \"react\";\nimport Button from \"./Button\";\nimport Heading from \"./Heading\";\n\ntype Props = {\n  title?: string;\n  subtitle?: string;\n  showReset?: boolean;\n};\n\nfunction EmptyState({\n  title = \"No exact matches\",\n  subtitle = \"Try changing or removing some of your filters.\",\n  showReset,\n}: Props) {\n  const router = useRouter();\n\n  return (\n    <motion.div\n      initial={{ opacity: 0 }}\n      whileInView={{ opacity: 1 }}\n      viewport={{ once: true }}\n      className=\"h-[60vh] flex flex-col gap-2 justify-center items-center\"\n    >\n      <Heading center title={title} subtitle={subtitle} />\n      <div className=\"w-48 mt-4\">\n        {showReset && (\n          <Button\n            outline\n            label=\"Remove all filters\"\n            onClick={() => router.push(\"/\")}\n          />\n        )}\n      </div>\n    </motion.div>\n  );\n}\n\nexport default EmptyState;\n"
  },
  {
    "path": "components/Footer.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport React, { useEffect, useState } from \"react\";\nimport ClientOnly from \"./ClientOnly\";\nimport FooterColumn from \"@/components/FooterColumn\";\n\ntype Props = {};\n\nfunction Footer({}: Props) {\n  const [country, setCountry] = useState(\"United States\");\n\n  const itemData = [\n    [\"ABOUT\", \"Newsroom\", \"Learn about new features\", \"Letter from our founders\", \"Careers\", \"Investors\"],\n    [\"Support\", \"Help Center\", \"AirCover\", \"Cancellation options\", \"Safety information\", \"Report a neighborhood concern\"],\n    [\"Community\", \"Newsroom\", \"Learn about new features\", \"Letter from our founders\", \"Careers\", \"Investors\"],\n    [\"Hosting\",\"Try hosting\",\"AirCover for Hosts\",\"Explore hosting resources\",\"Safety information\",\"How to host responsibly\"],\n  ];\n\n  useEffect(() => {\n    fetch(\n      `https://extreme-ip-lookup.com/json/?key=${process.env.NEXT_PUBLIC_LOOKUP_KEY}`\n    )\n      .then((res) => res.json())\n      .then((data) => setCountry(data.country));\n  }, []);\n\n  const footerColumns = itemData.map((item, index) => (\n    <FooterColumn index={index} data={item} />\n  ))\n\n  return (\n    <ClientOnly>\n      <div className=\"grid grid-cols-1 md:grid-cols-4 gap-y-10 px-32 py-14 bg-gray-100 text-gray-600\">\n        {footerColumns}\n        <p className=\"text-sm\">{country}</p>\n      </div>\n    </ClientOnly>\n  );\n}\n\nexport default Footer;\n"
  },
  {
    "path": "components/FooterColumn.tsx",
    "content": "\"user client\"\n\nimport { motion } from \"framer-motion\";\n\ntype Props = {\n    index: number;\n    data: Array<string>;\n  };\n\nfunction FooterColumn({ index, data }: Props) {\n    const columnItems = data.map((item, index) => \n        index === 0 \n        ? <h5 className=\"font-bold\">{item}</h5>\n        : <p>{item}</p>);\n\n    return (<motion.div\n        initial={{\n          x: index % 2 === 0 ? -200 : 200,\n          opacity: 0,\n        }}\n        transition={{ duration: 1 }}\n        whileInView={{ opacity: 1, x: 0 }}\n        className=\"space-y-4 text-xs text-gray-800\"\n      >\n        {columnItems}\n      </motion.div>\n    );\n}\n\nexport default FooterColumn;"
  },
  {
    "path": "components/Heading.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\n\ntype Props = {\n  title: string;\n  subtitle?: string;\n  center?: boolean;\n};\n\nfunction Heading({ title, subtitle, center }: Props) {\n  return (\n    <div className={center ? \"text-center\" : \"text-start\"}>\n      <div className=\"text-2xl font-bold\">{title}</div>\n      <div className=\"font-light text-neutral-500 mt-2\">{subtitle}</div>\n    </div>\n  );\n}\n\nexport default Heading;\n"
  },
  {
    "path": "components/HeartButton.tsx",
    "content": "\"use client\";\n\nimport useFavorite from \"@/hook/useFavorite\";\nimport { SafeUser } from \"@/types\";\nimport React from \"react\";\nimport { AiFillHeart, AiOutlineHeart } from \"react-icons/ai\";\n\ntype Props = {\n  listingId: string;\n  currentUser?: SafeUser | null;\n};\n\nfunction HeartButton({ listingId, currentUser }: Props) {\n  const { hasFavorite, toggleFavorite } = useFavorite({\n    listingId,\n    currentUser,\n  });\n\n  return (\n    <div\n      onClick={toggleFavorite}\n      className=\" relative hover:opacity-80 transition cursor-pointer\"\n    >\n      <AiOutlineHeart\n        size={28}\n        className=\"fill-white absolute -top-[2px] -right-[2px]\"\n      />\n      <AiFillHeart\n        size={24}\n        className={hasFavorite ? \"fill-rose-500\" : \"fill-neutral-500/70\"}\n      />\n    </div>\n  );\n}\n\nexport default HeartButton;\n"
  },
  {
    "path": "components/ListingClient.tsx",
    "content": "\"use client\";\n\nimport useLoginModel from \"@/hook/useLoginModal\";\nimport { SafeReservation, SafeUser, safeListing } from \"@/types\";\nimport axios from \"axios\";\nimport { differenceInCalendarDays, eachDayOfInterval } from \"date-fns\";\nimport { useRouter } from \"next/navigation\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { Range } from \"react-date-range\";\nimport { toast } from \"react-toastify\";\n\nimport Container from \"./Container\";\nimport ListingHead from \"./listing/ListingHead\";\nimport ListingInfo from \"./listing/ListingInfo\";\nimport ListingReservation from \"./listing/ListingReservation\";\nimport { categories } from \"./navbar/Categories\";\n\nconst initialDateRange = {\n  startDate: new Date(),\n  endDate: new Date(),\n  key: \"selection\",\n};\n\ntype Props = {\n  reservations?: SafeReservation[];\n  listing: safeListing & {\n    user: SafeUser;\n  };\n  currentUser?: SafeUser | null;\n};\n\nfunction ListingClient({ reservations = [], listing, currentUser }: Props) {\n  const router = useRouter();\n  const loginModal = useLoginModel();\n\n  const disableDates = useMemo(() => {\n    let dates: Date[] = [];\n\n    reservations.forEach((reservation) => {\n      const range = eachDayOfInterval({\n        start: new Date(reservation.startDate),\n        end: new Date(reservation.endDate),\n      });\n\n      dates = [...dates, ...range];\n    });\n\n    return dates;\n  }, [reservations]);\n\n  const [isLoading, setIsLoading] = useState(false);\n  const [totalPrice, setTotalPrice] = useState(listing.price);\n  const [dateRange, setDateRange] = useState<Range>(initialDateRange);\n\n  const onCreateReservation = useCallback(() => {\n    if (!currentUser) {\n      return loginModal.onOpen();\n    }\n\n    setIsLoading(true);\n\n    axios\n      .post(\"/api/reservations\", {\n        totalPrice,\n        startDate: dateRange.startDate,\n        endDate: dateRange.endDate,\n        listingId: listing?.id,\n      })\n      .then(() => {\n        toast.success(\"Success!\");\n        setDateRange(initialDateRange);\n        router.push(\"/trips\");\n      })\n      .catch(() => {\n        toast.error(\"Something Went Wrong\");\n      })\n      .finally(() => {\n        setIsLoading(false);\n      });\n  }, [totalPrice, dateRange, listing?.id, router, currentUser, loginModal]);\n\n  useEffect(() => {\n    if (dateRange.startDate && dateRange.endDate) {\n      const dayCount = differenceInCalendarDays(\n        dateRange.endDate,\n        dateRange.startDate\n      );\n\n      if (dayCount && listing.price) {\n        setTotalPrice(dayCount * listing.price);\n      } else {\n        setTotalPrice(listing.price);\n      }\n    }\n  }, [dateRange, listing.price]);\n\n  const category = useMemo(() => {\n    return categories.find((item) => item.label === listing.category);\n  }, [listing.category]);\n\n  return (\n    <Container>\n      <div className=\"max-w-screen-lg mx-auto\">\n        <div className=\"flex flex-col gap-6\">\n          <ListingHead\n            title={listing.title}\n            imageSrc={listing.imageSrc}\n            locationValue={listing.locationValue}\n            id={listing.id}\n            currentUser={currentUser}\n          />\n          <div className=\"grid grid-cols-1 md:grid-cols-7 md:gap-10 mt-6\">\n            <ListingInfo\n              user={listing.user}\n              category={category}\n              description={listing.description}\n              roomCount={listing.roomCount}\n              guestCount={listing.guestCount}\n              bathroomCount={listing.bathroomCount}\n              locationValue={listing.locationValue}\n            />\n            <div className=\"order-first mb-10 md:order-last md:col-span-3\">\n              <ListingReservation\n                price={listing.price}\n                totalPrice={totalPrice}\n                onChangeDate={(value) => setDateRange(value)}\n                dateRange={dateRange}\n                onSubmit={onCreateReservation}\n                disabled={isLoading}\n                disabledDates={disableDates}\n              />\n            </div>\n          </div>\n        </div>\n      </div>\n    </Container>\n  );\n}\n\nexport default ListingClient;\n"
  },
  {
    "path": "components/Loader.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport React from \"react\";\n\ntype Props = {};\n\nfunction Loader({}: Props) {\n  return (\n    <motion.div\n      initial={{ opacity: 0 }}\n      whileInView={{ opacity: 1 }}\n      viewport={{ once: true }}\n      className=\"h-[70vh] flex flex-col justify-center items-center overflow-hidden\"\n    >\n      <div className=\"px-4 py-12\">\n        <div className=\"rounded relative bg-white py-12\">\n          <div className=\"rounded-full bg-indigo-100 w-[177px] h-[177px] relative flex justify-center items-center mx-auto animate-spin\">\n            <svg\n              className=\"absolute top-0 0 left-0\"\n              width={177}\n              height={177}\n              viewBox=\"0 0 177 177\"\n              fill=\"none\"\n              xmlns=\"http://www.w3.org/2000/svg\"\n            >\n              <path\n                d=\"M169.667 88.5C173.717 88.5 177.032 85.2113 176.696 81.1755C175.457 66.2542 170.448 51.8477 162.085 39.332C152.361 24.7783 138.539 13.435 122.367 6.73666C106.196 0.0383073 88.4018 -1.71429 71.2345 1.7005C54.0672 5.11529 38.298 13.5441 25.9211 25.921C13.5441 38.298 5.1153 54.0672 1.7005 71.2345C-1.71429 88.4018 0.0383044 106.196 6.73666 122.367C13.435 138.539 24.7783 152.361 39.332 162.085C51.8477 170.448 66.2542 175.457 81.1755 176.696C85.2113 177.032 88.5 173.717 88.5 169.667V169.667C88.5 165.618 85.2089 162.373 81.1792 161.971C69.1624 160.774 57.5826 156.642 47.4795 149.891C35.3374 141.778 25.8738 130.247 20.2855 116.755C14.6971 103.264 13.2349 88.4181 16.0838 74.0955C18.9328 59.773 25.9649 46.6168 36.2909 36.2908C46.6169 25.9649 59.773 18.9328 74.0955 16.0838C88.4181 13.2349 103.264 14.6971 116.755 20.2855C130.247 25.8739 141.778 35.3375 149.891 47.4795C156.642 57.5826 160.774 69.1624 161.971 81.1793C162.373 85.209 165.618 88.5 169.667 88.5V88.5Z\"\n                fill=\"#FF5A5F\"\n              />\n            </svg>\n            <div className=\"div rounded-full bg-white w-[150px] h-[150px]\" />\n          </div>\n          <p className=\"mt-6 font-medium text-gray-800 text-center animate-bounce text-xl\">\n            Loading ...\n          </p>\n        </div>\n      </div>\n    </motion.div>\n  );\n}\n\nexport default Loader;\n"
  },
  {
    "path": "components/Map.tsx",
    "content": "\"use client\";\n\nimport L from \"leaflet\";\nimport React from \"react\";\nimport { MapContainer, Marker, Popup, TileLayer } from \"react-leaflet\";\n\nimport markerIcon2x from \"leaflet/dist/images/marker-icon-2x.png\";\nimport markerIcon from \"leaflet/dist/images/marker-icon.png\";\nimport markerShadow from \"leaflet/dist/images/marker-shadow.png\";\nimport \"leaflet/dist/leaflet.css\";\nimport Flag from \"react-world-flags\";\n\n// @ts-ignore\ndelete L.Icon.Default.prototype._getIconUrl;\nL.Icon.Default.mergeOptions({\n  iconUrl: markerIcon.src,\n  iconRetinaUrl: markerIcon2x.src,\n  shadowUrl: markerShadow.src,\n});\n\ntype Props = {\n  center?: number[];\n  locationValue?: string;\n};\n\nfunction Map({ center, locationValue }: Props) {\n  return (\n    <MapContainer\n      center={(center as L.LatLngExpression) || [51, -0.09]}\n      zoom={center ? 4 : 2}\n      scrollWheelZoom={false}\n      className=\"h-[35vh] rounded-lg\"\n    >\n      <TileLayer\n        url=\"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n        attribution='&copy; <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors'\n      />\n      {locationValue ? (\n        <>\n          {center && (\n            <Marker position={center as L.LatLngExpression}>\n              <Popup>\n                <div className=\"flex justify-center items-center animate-bounce\">\n                  <Flag code={locationValue} className=\"w-10\" />\n                </div>\n              </Popup>\n            </Marker>\n          )}\n        </>\n      ) : (\n        <>{center && <Marker position={center as L.LatLngExpression} />}</>\n      )}\n    </MapContainer>\n  );\n}\n\nexport default Map;\n"
  },
  {
    "path": "components/Offers.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { AiOutlineCar, AiOutlineWifi } from \"react-icons/ai\";\nimport { BiCctv } from \"react-icons/bi\";\nimport { BsFire } from \"react-icons/bs\";\nimport { FaFireExtinguisher } from \"react-icons/fa\";\nimport { GiButterflyFlower } from \"react-icons/gi\";\nimport { GrWorkshop } from \"react-icons/gr\";\nimport { MdOutlineBathtub, MdOutlineCoffeeMaker } from \"react-icons/md\";\nimport { RiSafeLine } from \"react-icons/ri\";\n\nconst offersRowOne = [\n  {\n    label: \"Garden view\",\n    icon: GiButterflyFlower,\n  },\n  {\n    label: \"Hot water\",\n    icon: BsFire,\n  },\n\n  {\n    label: \"Wifi\",\n    icon: AiOutlineWifi,\n  },\n  {\n    label: \"Coffee\",\n    icon: MdOutlineCoffeeMaker,\n  },\n  {\n    label: \"Security cameras on property\",\n    icon: BiCctv,\n  },\n];\n\nconst offersRowTwo = [\n  {\n    label: \"Bathtub\",\n    icon: MdOutlineBathtub,\n  },\n  {\n    label: \"Dedicated workspace\",\n    icon: GrWorkshop,\n  },\n  {\n    label: \"Safe\",\n    icon: RiSafeLine,\n  },\n  {\n    label: \"Free parking on premises\",\n    icon: AiOutlineCar,\n  },\n  {\n    label: \"Fire extinguisher\",\n    icon: FaFireExtinguisher,\n  },\n];\n\ntype Props = {};\n\nfunction Offers({}: Props) {\n  return (\n    <div>\n      <p className=\"text-xl font-semibold\">What this place offers</p>\n      <div className=\"flex justify-start space-x-12 pt-6\">\n        <div className=\"flex flex-col gap-2\">\n          {offersRowOne.map((item, index) => (\n            <motion.div\n              initial={{\n                x: -200,\n                opacity: 0,\n              }}\n              transition={{ duration: 1 }}\n              whileInView={{ opacity: 1, x: 0 }}\n              key={index}\n              className=\"flex justify-start items-center text-center gap-4 my-1 cursor-pointer\"\n            >\n              <item.icon size={25} className=\"text-gray-700\" />\n              <p className=\"text-neutral-500\">{item.label}</p>\n            </motion.div>\n          ))}\n        </div>\n        {/* another row */}\n        <div className=\"flex flex-col gap-2\">\n          {offersRowTwo.map((item, index) => (\n            <motion.div\n              initial={{\n                x: 200,\n                opacity: 0,\n              }}\n              transition={{ duration: 1 }}\n              whileInView={{ opacity: 1, x: 0 }}\n              key={index}\n              className=\"flex justify-start items-center text-center gap-4 my-1 cursor-pointer\"\n            >\n              <item.icon size={25} className=\"text-gray-700\" />\n              <p className=\"text-neutral-500\">{item.label}</p>\n            </motion.div>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Offers;\n"
  },
  {
    "path": "components/Sleep.tsx",
    "content": "\"use client\";\n\nimport { motion } from \"framer-motion\";\nimport { BiBed } from \"react-icons/bi\";\nimport { IoBedOutline } from \"react-icons/io5\";\n\ntype Props = {};\n\nfunction Sleep({}: Props) {\n  return (\n    <div>\n      <p className=\"text-xl font-semibold\">{`Where you'll sleep`}</p>\n      <div className=\"flex justify-between pt-6\">\n        <motion.div\n          initial={{\n            x: 200,\n            opacity: 0,\n          }}\n          transition={{ duration: 1 }}\n          whileInView={{ opacity: 1, x: 0 }}\n          className=\"border border-black rounded-md cursor-pointer\"\n        >\n          <div className=\"flex flex-col justify-start items-start px-6 py-6 gap-1 text-center\">\n            <IoBedOutline size={25} />\n            <p className=\"text-lg text-black font-medium\">Bedroom 1</p>\n            <p className=\"text-sm text-neutral-500\">1 king bed</p>\n          </div>\n        </motion.div>\n        <motion.div\n          initial={{\n            x: -200,\n            opacity: 0,\n          }}\n          transition={{ duration: 1 }}\n          whileInView={{ opacity: 1, x: 0 }}\n          className=\"border border-black rounded-md cursor-pointer\"\n        >\n          <div className=\"flex flex-col justify-start items-start px-6 py-6 gap-1 text-center\">\n            <IoBedOutline size={25} />\n            <p className=\"text-lg text-black font-medium\">Bedroom 2</p>\n            <p className=\"text-sm text-neutral-500\">1 queen bed</p>\n          </div>\n        </motion.div>\n        <motion.div\n          initial={{\n            x: 200,\n            opacity: 0,\n          }}\n          transition={{ duration: 1 }}\n          whileInView={{ opacity: 1, x: 0 }}\n          className=\"border border-black rounded-md cursor-pointer\"\n        >\n          <div className=\"flex flex-col justify-start items-start px-6 py-6 gap-1 text-center\">\n            <div className=\"flex justify-between gap-2\">\n              <IoBedOutline size={25} />\n              <BiBed size={25} />\n            </div>\n            <p className=\"text-lg text-black font-medium\">Bedroom 3</p>\n            <p className=\"text-sm text-neutral-500\">\n              1 queen bed, 1 single bed\n            </p>\n          </div>\n        </motion.div>\n      </div>\n    </div>\n  );\n}\n\nexport default Sleep;\n"
  },
  {
    "path": "components/ToastContainerBar.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { ToastContainer } from \"react-toastify\";\n\nimport \"react-toastify/dist/ReactToastify.css\";\n\ntype Props = {};\n\nfunction ToastContainerBar({}: Props) {\n  return (\n    <>\n      <ToastContainer\n        position=\"bottom-left\"\n        autoClose={5000}\n        hideProgressBar={false}\n        newestOnTop={false}\n        closeOnClick\n        pauseOnFocusLoss\n        pauseOnHover\n        theme=\"colored\"\n      />\n    </>\n  );\n}\n\nexport default ToastContainerBar;\n"
  },
  {
    "path": "components/inputs/Calendar.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { DateRange, Range, RangeKeyDict } from \"react-date-range\";\n\nimport \"react-date-range/dist/styles.css\";\nimport \"react-date-range/dist/theme/default.css\";\n\ntype Props = {\n  value: Range;\n  onChange: (value: RangeKeyDict) => void;\n  disabledDates?: Date[];\n};\n\nfunction Calendar({ value, onChange, disabledDates }: Props) {\n  return (\n    <DateRange\n      rangeColors={[\"#262626\"]}\n      ranges={[value]}\n      date={new Date()}\n      onChange={onChange}\n      direction=\"vertical\"\n      showDateDisplay={false}\n      minDate={new Date()}\n      disabledDates={disabledDates}\n    />\n  );\n}\n\nexport default Calendar;\n"
  },
  {
    "path": "components/inputs/CategoryInput.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { IconType } from \"react-icons\";\n\ntype Props = {\n  icon: IconType;\n  label: string;\n  selected?: boolean;\n  onClick: (value: string) => void;\n};\n\nfunction CategoryInput({ icon: Icon, label, selected, onClick }: Props) {\n  return (\n    <div\n      onClick={() => onClick(label)}\n      className={` rounded-xl border-2 p-4 flex flex-col gap-3 hover:border-black transition cursor-pointer ${\n        selected ? \"border-black\" : \"border-neutral-200\"\n      }`}\n    >\n      <Icon size={30} />\n      <div className=\"font-semibold\">{label}</div>\n    </div>\n  );\n}\n\nexport default CategoryInput;\n"
  },
  {
    "path": "components/inputs/Counter.tsx",
    "content": "\"use client\";\n\nimport React, { useCallback } from \"react\";\nimport { AiOutlineMinus, AiOutlinePlus } from \"react-icons/ai\";\n\ntype Props = {\n  title: string;\n  subtitle: string;\n  value: number;\n  onChange: (value: number) => void;\n};\n\nfunction Counter({ title, subtitle, value, onChange }: Props) {\n  const onAdd = useCallback(() => {\n    onChange(value + 1);\n  }, [onChange, value]);\n\n  const onReduce = useCallback(() => {\n    if (value === 1) {\n      return;\n    }\n\n    onChange(value - 1);\n  }, [value, onChange]);\n\n  return (\n    <div className=\"flex flex-row items-center justify-between\">\n      <div className=\"flex flex-col\">\n        <div className=\"font-medium\">{title}</div>\n        <div className=\"font-light text-gray-600\">{subtitle}</div>\n      </div>\n      <div className=\"flex flex-row items-center gap-4\">\n        <div\n          onClick={onReduce}\n          className=\" w-10 h-10 rounded-full border-[1px] border-neutral-400 flex items-center justify-center text-neutral-600 cursor-pointer hover:opacity-80 transition\"\n        >\n          <AiOutlineMinus />\n        </div>\n        <div className=\"font-light text-xl text-neutral-600\">{value}</div>\n        <div\n          onClick={onAdd}\n          className=\"w-10 h-10 rounded-full border-[1px] border-neutral-400 flex items-center justify-center text-neutral-600 cursor-pointer hover:opacity-80 transition\"\n        >\n          <AiOutlinePlus />\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Counter;\n"
  },
  {
    "path": "components/inputs/CountrySelect.tsx",
    "content": "\"use client\";\n\nimport useCountries from \"@/hook/useCountries\";\nimport Select from \"react-select\";\nimport Flag from \"react-world-flags\";\n\nexport type CountrySelectValue = {\n  flag: string;\n  label: string;\n  latlng: number[];\n  region: string;\n  value: string;\n};\n\ntype Props = {\n  value?: CountrySelectValue;\n  onChange: (value: CountrySelectValue) => void;\n};\n\nfunction CountrySelect({ value, onChange }: Props) {\n  const { getAll } = useCountries();\n\n  return (\n    <div>\n      <Select\n        placeholder=\"Anywhere\"\n        isClearable\n        options={getAll()}\n        value={value}\n        onChange={(value) => onChange(value as CountrySelectValue)}\n        formatOptionLabel={(option: any) => (\n          <div className=\"flex flex-row items-center gap-3\">\n            <Flag code={option.value} className=\"w-5\" />\n            <div>\n              {option.label},\n              <span className=\"text-neutral-500 ml-1\">{option.region}</span>\n            </div>\n          </div>\n        )}\n        classNames={{\n          control: () => \"p-3 border-2\",\n          input: () => \"text-lg\",\n          option: () => \"text-lg\",\n        }}\n        theme={(theme) => ({\n          ...theme,\n          borderRadius: 6,\n          colors: {\n            ...theme.colors,\n            primary: \"black\",\n            primary25: \"#ffe4e6\",\n          },\n        })}\n      />\n    </div>\n  );\n}\n\nexport default CountrySelect;\n"
  },
  {
    "path": "components/inputs/ImageUpload.tsx",
    "content": "\"use client\";\n\nimport { CldUploadWidget } from \"next-cloudinary\";\nimport Image from \"next/image\";\nimport React, { useCallback } from \"react\";\nimport { TbPhotoPlus } from \"react-icons/tb\";\n\ndeclare global {\n  var cloudinary: any;\n}\n\ntype Props = {\n  onChange: (value: string) => void;\n  value: string;\n};\n\nfunction ImageUpload({ onChange, value }: Props) {\n  const handleCallback = useCallback(\n    (result: any) => {\n      onChange(result.info.secure_url);\n    },\n    [onchange]\n  );\n\n  return (\n    <CldUploadWidget\n      onUpload={handleCallback}\n      uploadPreset=\"cptcecyi\"\n      options={{\n        maxFiles: 1,\n      }}\n    >\n      {({ open }) => {\n        return (\n          <div\n            onClick={() => open?.()}\n            className=\" relative cursor-pointer hover:opacity-70 transition border-dashed border-2 p-20 border-neutral-300 flex flex-col justify-center items-center gap-4 text-neutral-600\"\n          >\n            <TbPhotoPlus size={50} />\n            <div className=\"font-semibold text-lg\">Click to upload</div>\n            {value && (\n              <div className=\" absolute inset-0 w-full h-full\">\n                <Image\n                  alt=\"uploade\"\n                  fill\n                  style={{ objectFit: \"cover\" }}\n                  src={value}\n                />\n              </div>\n            )}\n          </div>\n        );\n      }}\n    </CldUploadWidget>\n  );\n}\n\nexport default ImageUpload;\n"
  },
  {
    "path": "components/inputs/Input.tsx",
    "content": "import React from \"react\";\nimport { FieldErrors, FieldValues, UseFormRegister } from \"react-hook-form\";\nimport { BiDollar } from \"react-icons/bi\";\n\ntype Props = {\n  id: string;\n  label: string;\n  type?: string;\n  disabled?: boolean;\n  formatPrice?: boolean;\n  required?: boolean;\n  register: UseFormRegister<FieldValues>;\n  errors: FieldErrors;\n};\n\nfunction Input({\n  id,\n  label,\n  type = \"text\",\n  disabled,\n  formatPrice,\n  register,\n  required,\n  errors,\n}: Props) {\n  return (\n    <div className=\"w-full relative\">\n      {formatPrice && (\n        <BiDollar\n          size={24}\n          className=\"\n            text-neutral-700\n            absolute\n            top-5\n            left-2\n          \"\n        />\n      )}\n      <input\n        id={id}\n        disabled={disabled}\n        {...register(id, { required })}\n        placeholder=\" \"\n        type={type}\n        className={`peer w-full p-4 pt-6 font-light bg-white border-2 rounded-md outline-none transition disabled:opacity-70 disabled:cursor-not-allowed ${\n          formatPrice ? \"pl-9\" : \"pl-4\"\n        } ${errors[id] ? \"border-rose-500\" : \"border-neutral-300\"} ${\n          errors[id] ? \"focus:border-rose-500\" : \"focus:border-black\"\n        }`}\n      />\n      <label\n        className={`absolute text-md duration-150 transform -translate-y-3 top-5 z-10 origin-[0] ${\n          formatPrice ? \"left-9\" : \"left-4\"\n        } peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-4 ${\n          errors[id] ? \"text-rose-500\" : \"text-zinc-400\"\n        }`}\n      >\n        {label}\n      </label>\n    </div>\n  );\n}\n\nexport default Input;\n"
  },
  {
    "path": "components/listing/ListingCard.tsx",
    "content": "\"use client\";\n\nimport useCountries from \"@/hook/useCountries\";\nimport { SafeReservation, SafeUser, safeListing } from \"@/types\";\nimport { format } from \"date-fns\";\nimport { motion } from \"framer-motion\";\nimport Image from \"next/image\";\nimport { useRouter } from \"next/navigation\";\nimport React, { useCallback, useMemo } from \"react\";\nimport Button from \"../Button\";\nimport HeartButton from \"../HeartButton\";\n\ntype Props = {\n  data: safeListing;\n  reservation?: SafeReservation;\n  onAction?: (id: string) => void;\n  disabled?: boolean;\n  actionLabel?: string;\n  actionId?: string;\n  currentUser?: SafeUser | null;\n};\n\nfunction ListingCard({\n  data,\n  reservation,\n  onAction,\n  disabled,\n  actionLabel,\n  actionId = \"\",\n  currentUser,\n}: Props) {\n  const router = useRouter();\n  const { getByValue } = useCountries();\n\n  const location = getByValue(data.locationValue);\n\n  const handleCancel = useCallback(\n    (e: React.MouseEvent<HTMLButtonElement>) => {\n      e.stopPropagation();\n\n      if (disabled) return;\n\n      onAction?.(actionId);\n    },\n    [onAction, actionId, disabled]\n  );\n\n  const price = useMemo(() => {\n    if (reservation) {\n      return reservation.totalPrice;\n    }\n\n    return data.price;\n  }, [reservation, data.price]);\n\n  const reservationDate = useMemo(() => {\n    if (!reservation) {\n      return null;\n    }\n\n    const start = new Date(reservation.startDate);\n    const end = new Date(reservation.endDate);\n\n    return `${format(start, \"PP\")} - ${format(end, \"PP\")}`;\n  }, [reservation]);\n\n  return (\n    <motion.div\n      initial={{ opacity: 0, scale: 0.5 }}\n      animate={{ opacity: 1, scale: 1 }}\n      transition={{\n        duration: 0.8,\n        delay: 0.5,\n        ease: [0, 0.71, 0.2, 1.01],\n      }}\n      onClick={() => router.push(`/listings/${data.id}`)}\n      className=\"col-span-1 cursor-pointer group\"\n    >\n      <div className=\"flex flex-col gap-2 w-full\">\n        <div className=\"aspect-square w-full relative overflow-hidden rounded-xl\">\n          <Image\n            fill\n            className=\"object-cover h-full w-full group-hover:scale-110 transition\"\n            src={data.imageSrc}\n            alt=\"listing\"\n          />\n          <div className=\"absolute top-3 right-3\">\n            <HeartButton listingId={data.id} currentUser={currentUser} />\n          </div>\n        </div>\n        <div className=\"font-semibold text-lg\">\n          {location?.region}, {location?.label}\n        </div>\n        <div className=\"font-light text-neutral-500\">\n          {reservationDate || data.category}\n        </div>\n        <div className=\"flex flex-row items-center gap-\">\n          <div className=\"flex gap-1 font-semibold\">\n            ${price} {!reservation && <div className=\"font-light\"> Night</div>}\n          </div>\n        </div>\n        {onAction && actionLabel && (\n          <Button\n            disabled={disabled}\n            small\n            label={actionLabel}\n            onClick={handleCancel}\n          />\n        )}\n      </div>\n    </motion.div>\n  );\n}\n\nexport default ListingCard;\n"
  },
  {
    "path": "components/listing/ListingCategory.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { IconType } from \"react-icons\";\n\ntype Props = {\n  icon: IconType;\n  label: string;\n  description: string;\n};\n\nfunction ListingCategory({ icon: Icon, label, description }: Props) {\n  return (\n    <div className=\"flex flex-col gap-6\">\n      <div className=\"flex flex-row items-center gap-4\">\n        <Icon size={40} className=\"text-neutral-600\" />\n        <div className=\"flex flex-col\">\n          <p className=\"text-lg font-semibold\">{label}</p>\n          <p className=\"text-neutral-500 font-light\">{description}</p>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default ListingCategory;\n"
  },
  {
    "path": "components/listing/ListingHead.tsx",
    "content": "\"use client\";\n\nimport useCountries from \"@/hook/useCountries\";\nimport { SafeUser } from \"@/types\";\nimport { motion } from \"framer-motion\";\nimport Image from \"next/image\";\nimport Heading from \"../Heading\";\nimport HeartButton from \"../HeartButton\";\n\ntype Props = {\n  title: string;\n  locationValue: string;\n  imageSrc: string;\n  id: string;\n  currentUser?: SafeUser | null;\n};\n\nfunction ListingHead({\n  title,\n  locationValue,\n  imageSrc,\n  id,\n  currentUser,\n}: Props) {\n  const { getByValue } = useCountries();\n  const location = getByValue(locationValue);\n\n  return (\n    <>\n      <Heading\n        title={title}\n        subtitle={`${location?.region}, ${location?.label}`}\n      />\n      <motion.div\n        initial={{ opacity: 0, scale: 0.5 }}\n        animate={{ opacity: 1, scale: 1 }}\n        transition={{\n          duration: 0.8,\n          delay: 0.5,\n          ease: [0, 0.71, 0.2, 1.01],\n        }}\n        className=\"w-full h-[60vh] overflow-hidden rounded-xl relative\"\n      >\n        <Image\n          src={imageSrc}\n          alt=\"image\"\n          fill\n          className=\"object-cover w-full\"\n        />\n        <div className=\"absolute top-5 right-5\">\n          <HeartButton listingId={id} currentUser={currentUser} />\n        </div>\n      </motion.div>\n    </>\n  );\n}\n\nexport default ListingHead;\n"
  },
  {
    "path": "components/listing/ListingInfo.tsx",
    "content": "\"use client\";\n\nimport useCountries from \"@/hook/useCountries\";\nimport { SafeUser } from \"@/types\";\nimport dynamic from \"next/dynamic\";\nimport React from \"react\";\nimport { IconType } from \"react-icons\";\nimport Avatar from \"../Avatar\";\nimport ListingCategory from \"./ListingCategory\";\nimport Sleep from \"../Sleep\";\nimport Offers from \"../Offers\";\n\nconst Map = dynamic(() => import(\"../Map\"), {\n  ssr: false,\n});\n\ntype Props = {\n  user: SafeUser;\n  description: string;\n  guestCount: number;\n  roomCount: number;\n  bathroomCount: number;\n  category:\n    | {\n        icon: IconType;\n        label: string;\n        description: string;\n      }\n    | undefined;\n  locationValue: string;\n};\n\nfunction ListingInfo({\n  user,\n  description,\n  guestCount,\n  roomCount,\n  bathroomCount,\n  category,\n  locationValue,\n}: Props) {\n  const { getByValue } = useCountries();\n  const coordinates = getByValue(locationValue)?.latlng;\n\n  return (\n    <div className=\"col-span-4 flex flex-col gap-8\">\n      <div className=\"flex flex-col gap-2\">\n        <div className=\" text-xl font-semibold flex flex-row items-center gap-2\">\n          <div>Hosted by {user?.name}</div>\n          <Avatar src={user?.image} userName={user?.name} />\n        </div>\n        <div className=\"flex flex-row items-center gap-4 font-light text-neutral-500\">\n          <p>{guestCount} guests</p>\n          <p>{roomCount} rooms</p>\n          <p>{bathroomCount} bathrooms</p>\n        </div>\n      </div>\n      <hr />\n      {category && (\n        <ListingCategory\n          icon={category.icon}\n          label={category?.label}\n          description={category?.description}\n        />\n      )}\n      <hr />\n      <div className=\"flex flex-col\">\n        <p className=\"text-4xl font-bold text-[#FF5A5F]\">\n          air<span className=\"text-black\">cover</span>\n        </p>\n        <p className=\"text-neutral-500 pt-3\">\n          Every booking includes free protection from Host cancellations,\n          listing inaccuracies, and other issues like trouble checking in.\n        </p>\n        <p className=\"text-black font-bold underline pt-3 cursor-pointer\">\n          Learn more\n        </p>\n      </div>\n      <hr />\n      <p className=\"text-lg font-light text-neutral-500\">{description}</p>\n      <hr />\n      <Sleep />\n      <hr />\n      <Offers />\n      <hr />\n      <p className=\"text-xl font-semibold\">{`Where you’ll be`}</p>\n      <Map center={coordinates} locationValue={locationValue} />\n    </div>\n  );\n}\n\nexport default ListingInfo;\n"
  },
  {
    "path": "components/listing/ListingReservation.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\nimport { Range } from \"react-date-range\";\nimport Calendar from \"../inputs/Calendar\";\nimport Button from \"../Button\";\n\ntype Props = {\n  price: number;\n  dateRange: Range;\n  totalPrice: number;\n  onChangeDate: (value: Range) => void;\n  onSubmit: () => void;\n  disabled?: boolean;\n  disabledDates: Date[];\n};\n\nfunction ListingReservation({\n  price,\n  dateRange,\n  totalPrice,\n  onChangeDate,\n  onSubmit,\n  disabled,\n  disabledDates,\n}: Props) {\n  return (\n    <div className=\"bg-white rounded-xl border-[1px] border-neutral-200 overflow-hidden\">\n      <div className=\"flex flex-row items-center gap-1 p-4\">\n        <p className=\"flex gap-1 text-2xl font-semibold\">\n          $ {price} <p className=\"font-light text-neutral-600\">night</p>\n        </p>\n      </div>\n      <hr />\n      <Calendar\n        value={dateRange}\n        disabledDates={disabledDates}\n        onChange={(value) => onChangeDate(value.selection)}\n      />\n      <hr />\n      <div className=\"p-4\">\n        <Button disabled={disabled} label=\"Reserve\" onClick={onSubmit} />\n      </div>\n      <hr />\n      <div className=\"p-4 flex flex-row items-center justify-between font-semibold text-lg\">\n        <p>Total</p>\n        <p> $ {totalPrice}</p>\n      </div>\n    </div>\n  );\n}\n\nexport default ListingReservation;\n"
  },
  {
    "path": "components/models/LoginModal.tsx",
    "content": "\"use client\";\n\nimport useLoginModel from \"@/hook/useLoginModal\";\nimport useRegisterModal from \"@/hook/useRegisterModal\";\nimport { signIn } from \"next-auth/react\";\nimport { useRouter } from \"next/navigation\";\nimport { useCallback, useState } from \"react\";\nimport { FieldValues, SubmitHandler, useForm } from \"react-hook-form\";\nimport { AiFillFacebook } from \"react-icons/ai\";\nimport { FcGoogle } from \"react-icons/fc\";\nimport { toast } from \"react-toastify\";\n\nimport Button from \"../Button\";\nimport Heading from \"../Heading\";\nimport Input from \"../inputs/Input\";\nimport Modal from \"./Modal\";\n\ntype Props = {};\n\nfunction LoginModal({}: Props) {\n  const router = useRouter();\n  const registerModel = useRegisterModal();\n  const loginModel = useLoginModel();\n  const [isLoading, setIsLoading] = useState(false);\n\n  const {\n    register,\n    handleSubmit,\n    formState: { errors },\n  } = useForm<FieldValues>({\n    defaultValues: {\n      email: \"\",\n      password: \"\",\n    },\n  });\n\n  const onSubmit: SubmitHandler<FieldValues> = (data) => {\n    setIsLoading(true);\n\n    signIn(\"credentials\", {\n      ...data,\n      redirect: false,\n    }).then((callback) => {\n      setIsLoading(false);\n\n      if (callback?.ok) {\n        toast.success(\"Login Successfully\");\n        router.refresh();\n        loginModel.onClose();\n      } else if (callback?.error) {\n        toast.error(\"Something Went Wrong\");\n      }\n    });\n  };\n\n  const toggle = useCallback(() => {\n    loginModel.onClose();\n    registerModel.onOpen();\n  }, [loginModel, registerModel]);\n\n  const bodyContent = (\n    <div className=\"flex flex-col gap-4\">\n      <Heading title=\"Welcome Back\" subtitle=\"Login to your Account!\" center />\n      <Input\n        id=\"email\"\n        label=\"Email Address\"\n        disabled={isLoading}\n        register={register}\n        errors={errors}\n        required\n      />\n      <Input\n        id=\"password\"\n        label=\"Password\"\n        disabled={isLoading}\n        register={register}\n        errors={errors}\n        required\n      />\n    </div>\n  );\n\n  const footerContent = (\n    <div className=\"flex flex-col gap-4 mt-3\">\n      <hr />\n      <Button\n        outline\n        label=\"Continue with Google\"\n        icon={FcGoogle}\n        onClick={() => signIn(\"google\")}\n      />\n      <Button\n        outline\n        label=\"Continue with Facebook\"\n        icon={AiFillFacebook}\n        onClick={() => signIn(\"facebook\")}\n        isColor\n      />\n      <div className=\"text-neutral-500 text-center mt-4 font-light\">\n        <div>\n          {`Didn't have an Account?`}{\" \"}\n          <span\n            onClick={toggle}\n            className=\"text-neutral-800 cursor-pointer hover:underline\"\n          >\n            Create an Account\n          </span>\n        </div>\n      </div>\n    </div>\n  );\n  return (\n    <Modal\n      disabled={isLoading}\n      isOpen={loginModel.isOpen}\n      title=\"Login\"\n      actionLabel=\"Continue\"\n      onClose={loginModel.onClose}\n      onSubmit={handleSubmit(onSubmit)}\n      body={bodyContent}\n      footer={footerContent}\n    />\n  );\n}\n\nexport default LoginModal;\n"
  },
  {
    "path": "components/models/Modal.tsx",
    "content": "\"use client\";\n\nimport React, { useCallback, useEffect, useState } from \"react\";\nimport { IoMdClose } from \"react-icons/io\";\nimport Button from \"../Button\";\n\ntype Props = {\n  isOpen?: boolean;\n  onClose: () => void;\n  onSubmit: () => void;\n  title?: string;\n  body?: React.ReactElement;\n  footer?: React.ReactElement;\n  actionLabel: string;\n  disabled?: boolean;\n  secondaryAction?: () => void;\n  secondaryActionLabel?: string;\n};\n\nfunction Modal({\n  isOpen,\n  onClose,\n  onSubmit,\n  title,\n  body,\n  actionLabel,\n  footer,\n  disabled,\n  secondaryAction,\n  secondaryActionLabel,\n}: Props) {\n  const [showModal, setShowModal] = useState(isOpen);\n\n  useEffect(() => {\n    setShowModal(isOpen);\n  }, [isOpen]);\n\n  const handleClose = useCallback(() => {\n    if (disabled) {\n      return;\n    }\n\n    setShowModal(false);\n    setTimeout(() => {\n      onClose();\n    }, 300);\n  }, [disabled, onClose]);\n\n  const handleSubmit = useCallback(() => {\n    if (disabled) {\n      return;\n    }\n\n    onSubmit();\n  }, [onSubmit, disabled]);\n\n  const handleSecondAction = useCallback(() => {\n    if (disabled || !secondaryAction) {\n      return;\n    }\n\n    secondaryAction();\n  }, [disabled, secondaryAction]);\n\n  if (!isOpen) {\n    return null;\n  }\n\n  return (\n    <>\n      <div className=\"justify-center items-center flex overflow-x-hidden overflow-y-auto fixed inset-0 z-50 outline-none focus:outline-none bg-neutral-800/70\">\n        <div className=\"relative w-full md:w-4/6 lg:w-3/6 xl:w-2/5 my-6 mx-auto h-full lg:h-auto md:h-auto\">\n          <div\n            className={`translate duration-300 h-full ${\n              showModal ? \"translate-y-0\" : \"translate-y-full\"\n            } ${showModal ? \"opacity-100\" : \"opacity-0\"}`}\n          >\n            <div className=\"translate h-full lg:h-auto md:h-auto border-0 rounded-lg shadow-lg relative flex flex-col w-full bg-white outline-none focus:outline-none\">\n              <div className=\"flex items-center p-6 rounded-t justify-center relative border-b-[1px]\">\n                <button\n                  className=\"p-1 border-0 hover:opacity-70 transition absolute left-9\"\n                  onClick={handleClose}\n                >\n                  <IoMdClose size={18} />\n                </button>\n                <div className=\"text-lg font-semibold\">{title}</div>\n              </div>\n              <div className=\"relative p-6 flex-auto\">{body}</div>\n              <div className=\"flex flex-col gap-2 p-6\">\n                <div className=\"flex flex-row items-center gap-4 w-full\">\n                  {secondaryAction && secondaryActionLabel && (\n                    <Button\n                      outline\n                      disabled={disabled}\n                      label={secondaryActionLabel}\n                      onClick={handleSecondAction}\n                    />\n                  )}\n                  <Button\n                    disabled={disabled}\n                    label={actionLabel}\n                    onClick={handleSubmit}\n                  />\n                </div>\n                {footer}\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </>\n  );\n}\n\nexport default Modal;\n"
  },
  {
    "path": "components/models/RegisterModal.tsx",
    "content": "\"use client\";\n\nimport useLoginModel from \"@/hook/useLoginModal\";\nimport useRegisterModal from \"@/hook/useRegisterModal\";\nimport axios from \"axios\";\nimport { useCallback, useState } from \"react\";\nimport { FieldValues, SubmitHandler, useForm } from \"react-hook-form\";\nimport { AiFillFacebook } from \"react-icons/ai\";\nimport { FcGoogle } from \"react-icons/fc\";\nimport { toast } from \"react-toastify\";\n\nimport { signIn } from \"next-auth/react\";\nimport Button from \"../Button\";\nimport Heading from \"../Heading\";\nimport Input from \"../inputs/Input\";\nimport Modal from \"./Modal\";\n\ntype Props = {};\n\nfunction RegisterModal({}: Props) {\n  const registerModel = useRegisterModal();\n  const loginModel = useLoginModel();\n  const [isLoading, setIsLoading] = useState(false);\n\n  const {\n    register,\n    handleSubmit,\n    formState: { errors },\n  } = useForm<FieldValues>({\n    defaultValues: {\n      name: \"\",\n      email: \"\",\n      password: \"\",\n    },\n  });\n\n  const onSubmit: SubmitHandler<FieldValues> = (data) => {\n    setIsLoading(true);\n\n    axios\n      .post(\"/api/register\", data)\n      .then(() => {\n        toast.success(\"Success!\");\n        loginModel.onOpen();\n        registerModel.onClose();\n      })\n      .catch((err: any) => toast.error(\"Something Went Wrong\"))\n      .finally(() => {\n        setIsLoading(false);\n        toast.success(\"Register Successfully\");\n      });\n  };\n\n  const toggle = useCallback(() => {\n    loginModel.onOpen();\n    registerModel.onClose();\n  }, [loginModel, registerModel]);\n\n  const bodyContent = (\n    <div className=\"flex flex-col gap-4\">\n      <Heading\n        title=\"Welcome to Airbnb-Clone\"\n        subtitle=\"Create an Account!\"\n        center\n      />\n      <Input\n        id=\"email\"\n        label=\"Email Address\"\n        disabled={isLoading}\n        register={register}\n        errors={errors}\n        required\n      />\n      <Input\n        id=\"name\"\n        label=\"User Name\"\n        disabled={isLoading}\n        register={register}\n        errors={errors}\n        required\n      />\n      <Input\n        id=\"password\"\n        label=\"Password\"\n        disabled={isLoading}\n        register={register}\n        errors={errors}\n        required\n      />\n    </div>\n  );\n\n  const footerContent = (\n    <div className=\"flex flex-col gap-4 mt-3\">\n      <hr />\n      <Button\n        outline\n        label=\"Continue with Google\"\n        icon={FcGoogle}\n        onClick={() => signIn(\"google\")}\n      />\n      <Button\n        outline\n        label=\"Continue with Facebook\"\n        icon={AiFillFacebook}\n        onClick={() => signIn(\"facebook\")}\n        isColor\n      />\n      <div className=\"text-neutral-500 text-center mt-4 font-light\">\n        <div>\n          Already have an account?{\" \"}\n          <span\n            onClick={toggle}\n            className=\"text-neutral-800 cursor-pointer hover:underline\"\n          >\n            Log in\n          </span>\n        </div>\n      </div>\n    </div>\n  );\n\n  return (\n    <Modal\n      disabled={isLoading}\n      isOpen={registerModel.isOpen}\n      title=\"Register\"\n      actionLabel=\"Continue\"\n      onClose={registerModel.onClose}\n      onSubmit={handleSubmit(onSubmit)}\n      body={bodyContent}\n      footer={footerContent}\n    />\n  );\n}\n\nexport default RegisterModal;\n"
  },
  {
    "path": "components/models/RentModal.tsx",
    "content": "\"use client\";\n\nimport useRentModal from \"@/hook/useRentModal\";\nimport axios from \"axios\";\nimport dynamic from \"next/dynamic\";\nimport { useRouter } from \"next/navigation\";\nimport { useMemo, useState } from \"react\";\nimport { FieldValues, SubmitHandler, useForm } from \"react-hook-form\";\nimport { toast } from \"react-toastify\";\n\nimport Heading from \"../Heading\";\nimport CategoryInput from \"../inputs/CategoryInput\";\nimport Counter from \"../inputs/Counter\";\nimport CountrySelect from \"../inputs/CountrySelect\";\nimport ImageUpload from \"../inputs/ImageUpload\";\nimport Input from \"../inputs/Input\";\nimport { categories } from \"../navbar/Categories\";\nimport Modal from \"./Modal\";\n\ntype Props = {};\n\nenum STEPS {\n  CATEGORY = 0,\n  LOCATION = 1,\n  INFO = 2,\n  IMAGES = 3,\n  DESCRIPTION = 4,\n  PRICE = 5,\n}\n\nfunction RentModal({}: Props) {\n  const router = useRouter();\n  const rentModel = useRentModal();\n  const [step, setStep] = useState(STEPS.CATEGORY);\n  const [isLoading, setIsLoading] = useState(false);\n\n  const {\n    register,\n    handleSubmit,\n    setValue,\n    watch,\n    formState: { errors },\n    reset,\n  } = useForm<FieldValues>({\n    defaultValues: {\n      category: \"\",\n      location: null,\n      guestCount: 1,\n      roomCount: 1,\n      bathroomCount: 1,\n      imageSrc: \"\",\n      price: 1,\n      title: \"\",\n      description: \"\",\n    },\n  });\n\n  const category = watch(\"category\");\n  const location = watch(\"location\");\n  const guestCount = watch(\"guestCount\");\n  const roomCount = watch(\"roomCount\");\n  const bathroomCount = watch(\"bathroomCount\");\n  const imageSrc = watch(\"imageSrc\");\n\n  const Map = useMemo(\n    () =>\n      dynamic(() => import(\"../Map\"), {\n        ssr: false,\n      }),\n    [location]\n  );\n\n  const setCustomValue = (id: string, value: any) => {\n    setValue(id, value, {\n      shouldValidate: true,\n      shouldDirty: true,\n      shouldTouch: true,\n    });\n  };\n\n  const onBack = () => {\n    setStep((value) => value - 1);\n  };\n\n  const onNext = () => {\n    setStep((value) => value + 1);\n  };\n\n  const onSubmit: SubmitHandler<FieldValues> = (data) => {\n    if (step !== STEPS.PRICE) {\n      return onNext();\n    }\n\n    setIsLoading(true);\n\n    axios\n      .post(\"/api/listings\", data)\n      .then(() => {\n        toast.success(\"Listing Created!\");\n        router.refresh();\n        reset();\n        setStep(STEPS.CATEGORY);\n        rentModel.onClose();\n      })\n      .catch(() => {\n        toast.error(\"Something Went Wrong\");\n      })\n      .finally(() => {\n        setIsLoading(false);\n      });\n  };\n\n  const actionLabel = useMemo(() => {\n    if (step === STEPS.PRICE) {\n      return \"Create\";\n    }\n\n    return \"Next\";\n  }, [step]);\n\n  const secondActionLabel = useMemo(() => {\n    if (step === STEPS.CATEGORY) {\n      return undefined;\n    }\n\n    return \"Back\";\n  }, [step]);\n\n  let bodyContent = (\n    <div className=\"flex flex-col gap-8\">\n      <Heading\n        title=\"Which of these best describes your place?\"\n        subtitle=\"Pick a category\"\n      />\n      <div className=\"grid grid-cols-1 md:grid-cols-2 gap-3 max-h-[50vh] overflow-y-auto scrollbar-thin scrollbar-thumb-[#FF5A5F]\">\n        {categories.map((item, index) => (\n          <div key={index} className=\"col-span-1\">\n            <CategoryInput\n              onClick={(category) => setCustomValue(\"category\", category)}\n              selected={category === item.label}\n              label={item.label}\n              icon={item.icon}\n            />\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n\n  if (step === STEPS.LOCATION) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading\n          title=\"Where is your place located?\"\n          subtitle=\"Help guests find you!\"\n        />\n        <CountrySelect\n          value={location}\n          onChange={(value) => setCustomValue(\"location\", value)}\n        />\n        <Map center={location?.latlng} />\n      </div>\n    );\n  }\n\n  if (step === STEPS.INFO) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading\n          title=\"Share some basics about your place\"\n          subtitle=\"What amenities do you have?\"\n        />\n        <Counter\n          title=\"Guests\"\n          subtitle=\"How many guest do you allow?\"\n          value={guestCount}\n          onChange={(value) => setCustomValue(\"guestCount\", value)}\n        />\n        <hr />\n        <Counter\n          title=\"Rooms\"\n          subtitle=\"How many rooms do you have?\"\n          value={roomCount}\n          onChange={(value) => setCustomValue(\"roomCount\", value)}\n        />\n        <hr />\n        <Counter\n          title=\"Bathrooms\"\n          subtitle=\"How many Bathrooms do you have?\"\n          value={bathroomCount}\n          onChange={(value) => setCustomValue(\"bathroomCount\", value)}\n        />\n      </div>\n    );\n  }\n\n  if (step === STEPS.IMAGES) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading\n          title=\"Add a photo of your place\"\n          subtitle=\"Show guests what your place looks like!\"\n        />\n        <ImageUpload\n          onChange={(value) => setCustomValue(\"imageSrc\", value)}\n          value={imageSrc}\n        />\n      </div>\n    );\n  }\n\n  if (step === STEPS.DESCRIPTION) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading\n          title=\"Now, set your price\"\n          subtitle=\"How much do you charge per night?\"\n        />\n        <Input\n          id=\"title\"\n          label=\"Title\"\n          disabled={isLoading}\n          register={register}\n          errors={errors}\n          required\n        />\n        <hr />\n        <Input\n          id=\"description\"\n          label=\"Description\"\n          disabled={isLoading}\n          register={register}\n          errors={errors}\n          required\n        />\n      </div>\n    );\n  }\n\n  if (step == STEPS.PRICE) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading\n          title=\"Now, set your price\"\n          subtitle=\"How much do you charge per night?\"\n        />\n        <Input\n          id=\"price\"\n          label=\"Price\"\n          formatPrice\n          type=\"number\"\n          disabled={isLoading}\n          register={register}\n          errors={errors}\n          required\n        />\n      </div>\n    );\n  }\n\n  return (\n    <Modal\n      disabled={isLoading}\n      isOpen={rentModel.isOpen}\n      title=\"Airbnb your home!\"\n      actionLabel={actionLabel}\n      onSubmit={handleSubmit(onSubmit)}\n      secondaryActionLabel={secondActionLabel}\n      secondaryAction={step === STEPS.CATEGORY ? undefined : onBack}\n      onClose={rentModel.onClose}\n      body={bodyContent}\n    />\n  );\n}\n\nexport default RentModal;\n"
  },
  {
    "path": "components/models/SearchModal.tsx",
    "content": "\"use client\";\n\nimport useSearchModal from \"@/hook/useSearchModal\";\nimport { formatISO } from \"date-fns\";\nimport dynamic from \"next/dynamic\";\nimport { useRouter, useSearchParams } from \"next/navigation\";\nimport qs from \"query-string\";\nimport { useCallback, useMemo, useState } from \"react\";\nimport { Range } from \"react-date-range\";\n\nimport Heading from \"../Heading\";\nimport Calendar from \"../inputs/Calendar\";\nimport Counter from \"../inputs/Counter\";\nimport CountrySelect, { CountrySelectValue } from \"../inputs/CountrySelect\";\nimport Modal from \"./Modal\";\n\nenum STEPS {\n  LOCATION = 0,\n  DATE = 1,\n  INFO = 2,\n}\n\ntype Props = {};\n\nfunction SearchModal({}: Props) {\n  const router = useRouter();\n  const params = useSearchParams();\n  const searchModel = useSearchModal();\n\n  const [location, setLocation] = useState<CountrySelectValue>();\n  const [step, setStep] = useState(STEPS.LOCATION);\n  const [guestCount, setGuestCount] = useState(1);\n  const [roomCount, setRoomCount] = useState(1);\n  const [bathroomCount, setBathroomCount] = useState(1);\n  const [dateRange, setDateRange] = useState<Range>({\n    startDate: new Date(),\n    endDate: new Date(),\n    key: \"selection\",\n  });\n\n  const Map = useMemo(\n    () =>\n      dynamic(() => import(\"../Map\"), {\n        ssr: false,\n      }),\n    [location]\n  );\n\n  const onBack = () => {\n    setStep((value) => value - 1);\n  };\n\n  const onNext = () => {\n    setStep((value) => value + 1);\n  };\n\n  const onSubmit = useCallback(async () => {\n    if (step !== STEPS.INFO) {\n      return onNext();\n    }\n\n    let currentQuery = {};\n\n    if (params) {\n      currentQuery = qs.parse(params.toString());\n    }\n\n    const updatedQuery: any = {\n      ...currentQuery,\n      locationValue: location?.value,\n      guestCount,\n      roomCount,\n      bathroomCount,\n    };\n\n    if (dateRange.startDate) {\n      updatedQuery.startDate = formatISO(dateRange.startDate);\n    }\n\n    if (dateRange.endDate) {\n      updatedQuery.endDate = formatISO(dateRange.endDate);\n    }\n\n    const url = qs.stringifyUrl(\n      {\n        url: \"/\",\n        query: updatedQuery,\n      },\n      { skipNull: true }\n    );\n\n    setStep(STEPS.LOCATION);\n    searchModel.onClose();\n\n    router.push(url);\n  }, [\n    step,\n    searchModel,\n    location,\n    router,\n    guestCount,\n    roomCount,\n    bathroomCount,\n    dateRange,\n    onNext,\n    params,\n  ]);\n\n  const actionLabel = useMemo(() => {\n    if (step === STEPS.INFO) {\n      return \"Search\";\n    }\n\n    return \"Next\";\n  }, [step]);\n\n  const secondActionLabel = useMemo(() => {\n    if (step === STEPS.LOCATION) {\n      return undefined;\n    }\n\n    return \"Back\";\n  }, [step]);\n\n  let bodyContent = (\n    <div className=\"flex flex-col gap-8\">\n      <Heading\n        title=\"Where do you wanna go?\"\n        subtitle=\"Find the perfect location!\"\n      />\n      <CountrySelect\n        value={location}\n        onChange={(value) => setLocation(value as CountrySelectValue)}\n      />\n      <hr />\n      <Map center={location?.latlng} />\n    </div>\n  );\n\n  if (step === STEPS.DATE) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading\n          title=\"When do you plan to go?\"\n          subtitle=\"Make sure everyone is free!\"\n        />\n        <Calendar\n          onChange={(value) => setDateRange(value.selection)}\n          value={dateRange}\n        />\n      </div>\n    );\n  }\n\n  if (step === STEPS.INFO) {\n    bodyContent = (\n      <div className=\"flex flex-col gap-8\">\n        <Heading title=\"More information\" subtitle=\"Find your perfect place!\" />\n        <Counter\n          onChange={(value) => setGuestCount(value)}\n          value={guestCount}\n          title=\"Guests\"\n          subtitle=\"How many guests are coming?\"\n        />\n        <hr />\n        <Counter\n          onChange={(value) => setRoomCount(value)}\n          value={roomCount}\n          title=\"Rooms\"\n          subtitle=\"How many rooms do you need?\"\n        />\n        <hr />\n        <Counter\n          onChange={(value) => {\n            setBathroomCount(value);\n          }}\n          value={bathroomCount}\n          title=\"Bathrooms\"\n          subtitle=\"How many bahtrooms do you need?\"\n        />\n      </div>\n    );\n  }\n\n  return (\n    <Modal\n      isOpen={searchModel.isOpen}\n      onClose={searchModel.onClose}\n      onSubmit={onSubmit}\n      secondaryAction={step === STEPS.LOCATION ? undefined : onBack}\n      secondaryActionLabel={secondActionLabel}\n      title=\"Filters\"\n      actionLabel=\"Search\"\n      body={bodyContent}\n    />\n  );\n}\n\nexport default SearchModal;\n"
  },
  {
    "path": "components/navbar/Categories.tsx",
    "content": "\"use client\";\n\nimport { usePathname, useSearchParams } from \"next/navigation\";\nimport { BsSnow } from \"react-icons/bs\";\nimport { FaSkiing } from \"react-icons/fa\";\nimport {\n  GiBarn,\n  GiBoatFishing,\n  GiCactus,\n  GiCastle,\n  GiCaveEntrance,\n  GiForestCamp,\n  GiIsland,\n  GiWindmill,\n} from \"react-icons/gi\";\nimport { IoDiamond } from \"react-icons/io5\";\nimport { MdOutlineVilla } from \"react-icons/md\";\nimport { TbBeach, TbMountain, TbPool } from \"react-icons/tb\";\nimport CategoryBox from \"../CategoryBox\";\nimport Container from \"../Container\";\n\nexport const categories = [\n  {\n    label: \"Beach\",\n    icon: TbBeach,\n    description: \"This property is close to the beach!\",\n  },\n  {\n    label: \"Windmills\",\n    icon: GiWindmill,\n    description: \"This property is has windmills!\",\n  },\n  {\n    label: \"Modern\",\n    icon: MdOutlineVilla,\n    description: \"This property is modern!\",\n  },\n  {\n    label: \"Countryside\",\n    icon: TbMountain,\n    description: \"This property is in the countryside!\",\n  },\n  {\n    label: \"Pools\",\n    icon: TbPool,\n    description: \"This is property has a beautiful pool!\",\n  },\n  {\n    label: \"Islands\",\n    icon: GiIsland,\n    description: \"This property is on an island!\",\n  },\n  {\n    label: \"Lake\",\n    icon: GiBoatFishing,\n    description: \"This property is near a lake!\",\n  },\n  {\n    label: \"Skiing\",\n    icon: FaSkiing,\n    description: \"This property has skiing activies!\",\n  },\n  {\n    label: \"Castles\",\n    icon: GiCastle,\n    description: \"This property is an ancient castle!\",\n  },\n  {\n    label: \"Caves\",\n    icon: GiCaveEntrance,\n    description: \"This property is in a spooky cave!\",\n  },\n  {\n    label: \"Camping\",\n    icon: GiForestCamp,\n    description: \"This property offers camping activities!\",\n  },\n  {\n    label: \"Arctic\",\n    icon: BsSnow,\n    description: \"This property is in arctic environment!\",\n  },\n  {\n    label: \"Desert\",\n    icon: GiCactus,\n    description: \"This property is in the desert!\",\n  },\n  {\n    label: \"Barns\",\n    icon: GiBarn,\n    description: \"This property is in a barn!\",\n  },\n  {\n    label: \"Lux\",\n    icon: IoDiamond,\n    description: \"This property is brand new and luxurious!\",\n  },\n];\n\ntype Props = {};\n\nfunction Categories({}: Props) {\n  const params = useSearchParams();\n  const category = params?.get(\"category\");\n  const pathname = usePathname();\n\n  const isMainPage = pathname === \"/\";\n\n  if (!isMainPage) {\n    return null;\n  }\n\n  return (\n    <Container>\n      <div className=\"pt-4 flex flex-row items-center justify-between overflow-x-auto\">\n        {categories.map((items, index) => (\n          <CategoryBox\n            key={index}\n            icon={items.icon}\n            label={items.label}\n            selected={category === items.label}\n          />\n        ))}\n      </div>\n    </Container>\n  );\n}\n\nexport default Categories;\n"
  },
  {
    "path": "components/navbar/Logo.tsx",
    "content": "\"use client\";\n\nimport Image from \"next/image\";\nimport { useRouter } from \"next/navigation\";\nimport React from \"react\";\n\ntype Props = {};\n\nfunction Logo({}: Props) {\n  const router = useRouter();\n\n  return (\n    <div onClick={() => router.push(\"/\")}>\n      <Image\n        alt=\"logo\"\n        className=\"hidden md:block cursor-pointer\"\n        height=\"100\"\n        width=\"100\"\n        src=\"/assets/logo.png\"\n      />\n    </div>\n  );\n}\n\nexport default Logo;\n"
  },
  {
    "path": "components/navbar/MenuItem.tsx",
    "content": "\"use client\";\n\nimport React from \"react\";\n\ntype Props = {\n  onClick: () => void;\n  label: string;\n};\n\nfunction MenuItem({ onClick, label }: Props) {\n  return (\n    <div\n      className=\" px-4 py-3 hover:bg-neutral-100 transition font-semibold\"\n      onClick={onClick}\n    >\n      {label}\n    </div>\n  );\n}\n\nexport default MenuItem;\n"
  },
  {
    "path": "components/navbar/Navbar.tsx",
    "content": "\"use client\";\n\nimport { SafeUser } from \"@/types\";\nimport Container from \"../Container\";\nimport Logo from \"./Logo\";\nimport Search from \"./Search\";\nimport UserMenu from \"./UserMenu\";\nimport Categories from \"./Categories\";\n\ntype Props = {\n  currentUser?: SafeUser | null;\n};\n\nfunction Navbar({ currentUser }: Props) {\n  return (\n    <div className=\"fixed w-full bg-white z-10 shadow-sm\">\n      <div className=\"py-4 border-b-[1px]\">\n        <Container>\n          <div className=\"flex flex-row items-center justify-between gap-3 md:gap-0\">\n            <Logo />\n            <Search />\n            <UserMenu currentUser={currentUser} />\n          </div>\n        </Container>\n      </div>\n      <Categories />\n    </div>\n  );\n}\n\nexport default Navbar;\n"
  },
  {
    "path": "components/navbar/Search.tsx",
    "content": "\"use client\";\n\nimport useCountries from \"@/hook/useCountries\";\nimport useSearchModal from \"@/hook/useSearchModal\";\nimport { differenceInDays } from \"date-fns\";\nimport { useSearchParams } from \"next/navigation\";\nimport { useMemo } from \"react\";\nimport { BiSearch } from \"react-icons/bi\";\n\ntype Props = {};\n\nfunction Search({}: Props) {\n  const searchModel = useSearchModal();\n  const params = useSearchParams();\n  const { getByValue } = useCountries();\n\n  const locationValue = params?.get(\"locationValue\");\n  const startDate = params?.get(\"startDate\");\n  const endDate = params?.get(\"endDate\");\n  const guestCount = params?.get(\"guestCount\");\n\n  const locationLabel = useMemo(() => {\n    if (locationValue) {\n      return getByValue(locationValue as string)?.label;\n    }\n\n    return \"Anywhere\";\n  }, [getByValue, locationValue]);\n\n  const durationLabel = useMemo(() => {\n    if (startDate && endDate) {\n      const start = new Date(startDate as string);\n      const end = new Date(endDate as string);\n      let diff = differenceInDays(end, start);\n\n      if (diff === 0) {\n        diff = 1;\n      }\n\n      return `${diff} Days`;\n    }\n\n    return \"Any Week\";\n  }, [startDate, endDate]);\n\n  const guessLabel = useMemo(() => {\n    if (guestCount) {\n      return `${guestCount} Guests`;\n    }\n\n    return \"Add Guests\";\n  }, []);\n\n  return (\n    <div\n      onClick={searchModel.onOpen}\n      className=\"border-[1px] w-full md:w-auto py-2 rounded-full shadow-sm hover:shadow-md transition cursor-pointer\"\n    >\n      <div className=\"flex flex-row items-center justify-between\">\n        <div className=\"text-sm font-semibold px-6\">{locationLabel}</div>\n        <div className=\"hidden sm:block text-losm font-semibold px-6 border-x-[1px] flex-1 text-center\">\n          {durationLabel}\n        </div>\n        <div className=\"text-sm pl-6 pr-2 text-gray-600 flex flex-row items-center gap-3\">\n          <div className=\"hidden sm:block text-center\">{guessLabel}</div>\n          <div className=\"p-2 bg-rose-500 rounded-full text-white\">\n            <BiSearch size={18} />\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default Search;\n"
  },
  {
    "path": "components/navbar/UserMenu.tsx",
    "content": "\"use client\";\n\nimport useLoginModel from \"@/hook/useLoginModal\";\nimport useRegisterModal from \"@/hook/useRegisterModal\";\nimport useRentModal from \"@/hook/useRentModal\";\nimport Image from \"next/image\";\nimport { useRouter } from \"next/navigation\";\n\nimport { SafeUser } from \"@/types\";\nimport { signOut } from \"next-auth/react\";\nimport { useCallback, useState } from \"react\";\nimport { AiOutlineMenu } from \"react-icons/ai\";\nimport Avatar from \"../Avatar\";\nimport MenuItem from \"./MenuItem\";\n\ntype Props = {\n  currentUser?: SafeUser | null;\n};\n\nfunction UserMenu({ currentUser }: Props) {\n  const router = useRouter();\n  const registerModel = useRegisterModal();\n  const loginModel = useLoginModel();\n  const rentModel = useRentModal();\n  const [isOpen, setIsOpen] = useState(false);\n\n  const toggleOpen = useCallback(() => {\n    setIsOpen((value) => !value);\n  }, []);\n\n  const onRent = useCallback(() => {\n    if (!currentUser) {\n      return loginModel.onOpen();\n    }\n\n    rentModel.onOpen();\n  }, [currentUser, loginModel, rentModel]);\n\n  return (\n    <div className=\"relative\">\n      <div className=\"flex flex-row items-center gap-3\">\n        <div\n          className=\"hidden md:block text-sm font-semibold py-3 px-4 rounded-full hover:bg-neutral-100 transition cursor-pointer\"\n          onClick={onRent}\n        >\n          Airbnb your Home\n        </div>\n        <div\n          onClick={toggleOpen}\n          className=\"p-4 md:py-1 md:px-2 border-[1px] flex flex-row items-center gap-3 rounded-full cursor-pointer hover:shadow-md transition\"\n        >\n          <AiOutlineMenu />\n          <div className=\"hidden md:block\">\n            {currentUser ? (\n              <Avatar src={currentUser?.image!} userName={currentUser?.name} />\n            ) : (\n              <Image\n                className=\"rounded-full\"\n                height=\"30\"\n                width=\"30\"\n                alt=\"Avatar\"\n                src=\"/assets/avatar.png\"\n              />\n            )}\n          </div>\n        </div>\n      </div>\n      {isOpen && (\n        <div className=\"absolute rounded-xl shadow-md w-[40vw] md:w-3/4 bg-white overflow-hidden right-0 top-12 text-sm\">\n          <div className=\"flex flex-col cursor-pointer\">\n            {currentUser ? (\n              <>\n                <MenuItem\n                  onClick={() => router.push(\"/trips\")}\n                  label=\"My trips\"\n                />\n                <MenuItem\n                  onClick={() => router.push(\"/favorites\")}\n                  label=\"My favorites\"\n                />\n                <MenuItem\n                  onClick={() => router.push(\"/reservations\")}\n                  label=\"My reservations\"\n                />\n                <MenuItem\n                  onClick={() => router.push(\"/properties\")}\n                  label=\"My properties\"\n                />\n                <MenuItem onClick={onRent} label=\"Airbnb your home\" />\n                <hr />\n                <MenuItem onClick={() => signOut()} label=\"Logout\" />\n              </>\n            ) : (\n              <>\n                <MenuItem onClick={loginModel.onOpen} label=\"Login\" />\n                <MenuItem onClick={registerModel.onOpen} label=\"Sign up\" />\n              </>\n            )}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n}\n\nexport default UserMenu;\n"
  },
  {
    "path": "hook/useCountries.ts",
    "content": "import countries from \"world-countries\";\n\nconst formattedCountries = countries.map((country) => ({\n  value: country.cca2,\n  label: country.name.common,\n  flag: country.flag,\n  latlng: country.latlng,\n  region: country.region,\n}));\n\nconst useCountries = () => {\n  const getAll = () => formattedCountries;\n\n  const getByValue = (value: string) => {\n    return formattedCountries.find((item) => item.value === value);\n  };\n\n  return {\n    getAll,\n    getByValue,\n  };\n};\n\nexport default useCountries;\n"
  },
  {
    "path": "hook/useFavorite.ts",
    "content": "import { SafeUser } from \"@/types\";\nimport axios from \"axios\";\nimport { useRouter } from \"next/navigation\";\nimport { useCallback, useMemo } from \"react\";\nimport { toast } from \"react-toastify\";\nimport useLoginModel from \"./useLoginModal\";\n\ntype Props = {\n  listingId: string;\n  currentUser?: SafeUser | null;\n};\n\nfunction useFavorite({ listingId, currentUser }: Props) {\n  const router = useRouter();\n  const loginModel = useLoginModel();\n\n  const hasFavorite = useMemo(() => {\n    const list = currentUser?.favoriteIds || [];\n\n    return list.includes(listingId);\n  }, [currentUser, listingId]);\n\n  const toggleFavorite = useCallback(\n    async (e: React.MouseEvent<HTMLDivElement>) => {\n      e.stopPropagation();\n\n      if (!currentUser) {\n        return loginModel.onOpen();\n      }\n\n      try {\n        let request;\n\n        if (hasFavorite) {\n          request = () => axios.delete(`/api/favorites/${listingId}`);\n        } else {\n          request = () => axios.post(`/api/favorites/${listingId}`);\n        }\n\n        await request();\n        router.refresh();\n        toast.success(\"Success\");\n      } catch (error: any) {\n        toast.error(\"Something Went Wrong\");\n      }\n    },\n    [currentUser, hasFavorite, listingId, loginModel]\n  );\n\n  return {\n    hasFavorite,\n    toggleFavorite,\n  };\n}\n\nexport default useFavorite;\n"
  },
  {
    "path": "hook/useLoginModal.ts",
    "content": "import { create } from \"zustand\";\n\ninterface LoginModelState {\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}\n\nconst useLoginModel = create<LoginModelState>((set) => ({\n  isOpen: false,\n  onOpen: () => set({ isOpen: true }),\n  onClose: () => set({ isOpen: false }),\n}));\n\nexport default useLoginModel;\n"
  },
  {
    "path": "hook/useRegisterModal.ts",
    "content": "import { create } from \"zustand\";\n\ninterface RegisterModelStore {\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}\n\nconst useRegisterModal = create<RegisterModelStore>((set) => ({\n  isOpen: false,\n  onOpen: () => set({ isOpen: true }),\n  onClose: () => set({ isOpen: false }),\n}));\n\nexport default useRegisterModal;\n"
  },
  {
    "path": "hook/useRentModal.ts",
    "content": "import { create } from \"zustand\";\n\ninterface RentModelStore {\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}\n\nconst useRentModal = create<RentModelStore>((set) => ({\n  isOpen: false,\n  onOpen: () => set({ isOpen: true }),\n  onClose: () => set({ isOpen: false }),\n}));\n\nexport default useRentModal;\n"
  },
  {
    "path": "hook/useSearchModal.ts",
    "content": "import { create } from \"zustand\";\n\ninterface SearchModalStore {\n  isOpen: boolean;\n  onOpen: () => void;\n  onClose: () => void;\n}\n\nconst useSearchModal = create<SearchModalStore>((set) => ({\n  isOpen: false,\n  onOpen: () => set({ isOpen: true }),\n  onClose: () => set({ isOpen: false }),\n}));\n\nexport default useSearchModal;\n"
  },
  {
    "path": "lib/prismadb.ts",
    "content": "import { PrismaClient } from \"@prisma/client\";\n\ndeclare global {\n  var prisma: PrismaClient | undefined;\n}\n\nconst client = globalThis.prisma || new PrismaClient();\n\nif (process.env.NODE_ENV !== \"production\") globalThis.prisma = client;\n\nexport default client;\n"
  },
  {
    "path": "middleware.ts",
    "content": "export { default } from \"next-auth/middleware\";\n\nexport const config = {\n  matcher: [\"/trips\", \"/reservations\", \"/properties\", \"/favorites\"],\n};\n"
  },
  {
    "path": "next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  experimental: {\n    appDir: true,\n  },\n  images: {\n    domains: [\"lh3.googleusercontent.com\", \"res.cloudinary.com\"],\n  },\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"airbnb-clone\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@next-auth/prisma-adapter\": \"^1.0.5\",\n    \"@prisma/client\": \"^4.12.0\",\n    \"@types/date-fns\": \"^2.6.0\",\n    \"@types/node\": \"18.15.11\",\n    \"@types/react\": \"18.0.31\",\n    \"@types/react-dom\": \"18.0.11\",\n    \"axios\": \"^1.3.4\",\n    \"bcrypt\": \"^5.1.0\",\n    \"date-fns\": \"^2.29.3\",\n    \"eslint\": \"8.37.0\",\n    \"eslint-config-next\": \"13.2.4\",\n    \"framer-motion\": \"^10.10.0\",\n    \"leaflet\": \"^1.9.4\",\n    \"next\": \"13.2.4\",\n    \"next-auth\": \"^4.20.1\",\n    \"next-cloudinary\": \"^4.1.1\",\n    \"query-string\": \"^8.1.0\",\n    \"react\": \"18.2.0\",\n    \"react-date-range\": \"^1.4.0\",\n    \"react-dom\": \"18.2.0\",\n    \"react-hook-form\": \"^7.43.9\",\n    \"react-icons\": \"^4.8.0\",\n    \"react-leaflet\": \"^4.2.1\",\n    \"react-select\": \"^5.7.2\",\n    \"react-toastify\": \"^9.1.2\",\n    \"react-world-flags\": \"^1.5.1\",\n    \"tailwind-scrollbar\": \"^3.0.0\",\n    \"typescript\": \"5.0.3\",\n    \"world-countries\": \"^4.0.0\",\n    \"zustand\": \"^4.3.7\"\n  },\n  \"devDependencies\": {\n    \"@types/bcrypt\": \"^5.0.0\",\n    \"@types/leaflet\": \"^1.9.3\",\n    \"@types/react-date-range\": \"^1.4.4\",\n    \"@types/react-world-flags\": \"^1.4.2\",\n    \"autoprefixer\": \"^10.4.14\",\n    \"postcss\": \"^8.4.21\",\n    \"prisma\": \"^4.12.0\",\n    \"tailwindcss\": \"^3.3.1\"\n  }\n}\n"
  },
  {
    "path": "pages/api/auth/[...nextauth].ts",
    "content": "import prisma from \"@/lib/prismadb\";\nimport { PrismaAdapter } from \"@next-auth/prisma-adapter\";\nimport bcrypt from \"bcrypt\";\nimport NextAuth, { AuthOptions } from \"next-auth\";\nimport CredentialsProvider from \"next-auth/providers/credentials\";\nimport FacebookProvider from \"next-auth/providers/facebook\";\nimport GoogleProvider from \"next-auth/providers/google\";\n\nexport const authOptions: AuthOptions = {\n  adapter: PrismaAdapter(prisma),\n  providers: [\n    GoogleProvider({\n      clientId: process.env.GOOGLE_CLIENT_ID as string,\n      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,\n    }),\n    FacebookProvider({\n      clientId: process.env.FACEBOOK_ID as string,\n      clientSecret: process.env.FACEBOOK_SECRET as string,\n    }),\n    CredentialsProvider({\n      name: \"credentials\",\n      credentials: {\n        email: { label: \"email\", type: \"text\" },\n        password: { label: \"password\", type: \"password\" },\n      },\n      async authorize(credentials) {\n        if (!credentials?.email || !credentials?.password) {\n          throw new Error(\"Invalid credentials\");\n        }\n\n        const user = await prisma.user.findUnique({\n          where: {\n            email: credentials.email,\n          },\n        });\n\n        if (!user || !user?.hashedPassword) {\n          throw new Error(\"Invalid credentials\");\n        }\n\n        const isCorrectPassword = await bcrypt.compare(\n          credentials.password,\n          user.hashedPassword\n        );\n        if (!isCorrectPassword) {\n          throw new Error(\"Invalid credentials\");\n        }\n        return user;\n      },\n    }),\n  ],\n  pages: {\n    signIn: \"/\",\n  },\n  debug: process.env.NODE_ENV === \"development\",\n  session: {\n    strategy: \"jwt\",\n  },\n  secret: process.env.NEXTAUTH_SECRET,\n};\n\nexport default NextAuth(authOptions);\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "prisma/schema.prisma",
    "content": "// This is your Prisma schema file,\n// learn more about it in the docs: https://pris.ly/d/prisma-schema\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\ndatasource db {\n  provider = \"mongodb\"\n  url      = env(\"DATABASE_URL\")\n}\n\nmodel User {\n  id             String        @id @default(auto()) @map(\"_id\") @db.ObjectId\n  name           String?\n  email          String?       @unique\n  emailVerified  DateTime?\n  image          String?\n  hashedPassword String?\n  createdAt      DateTime      @default(now())\n  updatedAt      DateTime      @updatedAt\n  favoriteIds    String[]      @db.ObjectId\n  accounts       Account[]\n  listings       Listing[]\n  reservations   Reservation[]\n}\n\nmodel Account {\n  id                String  @id @default(auto()) @map(\"_id\") @db.ObjectId\n  userId            String  @db.ObjectId\n  type              String\n  provider          String\n  providerAccountId String\n  refresh_token     String? @db.String\n  access_token      String? @db.String\n  expires_at        Int?\n  token_type        String?\n  scope             String?\n  id_token          String? @db.String\n  session_state     String?\n  user              User    @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n  @@unique([provider, providerAccountId])\n}\n\nmodel Listing {\n  id            String        @id @default(auto()) @map(\"_id\") @db.ObjectId\n  title         String\n  description   String\n  imageSrc      String\n  createdAt     DateTime      @default(now())\n  category      String\n  roomCount     Int\n  bathroomCount Int\n  guestCount    Int\n  locationValue String\n  userId        String        @db.ObjectId\n  price         Int\n  user          User          @relation(fields: [userId], references: [id], onDelete: Cascade)\n  reservations  Reservation[]\n}\n\nmodel Reservation {\n  id         String   @id @default(auto()) @map(\"_id\") @db.ObjectId\n  userId     String   @db.ObjectId\n  listingId  String   @db.ObjectId\n  startDate  DateTime\n  endDate    DateTime\n  totalPrice Int\n  createdAt  DateTime @default(now())\n  user       User     @relation(fields: [userId], references: [id], onDelete: Cascade)\n  listing    Listing  @relation(fields: [listingId], references: [id], onDelete: Cascade)\n}\n"
  },
  {
    "path": "styles/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  body {\n    @apply !scrollbar-thin !scrollbar-track-transparent !scrollbar-thumb-[#FF5A5F];\n  }\n}\n\nhtml,\nbody,\n:root{\n    height: 100%;\n}\n\n.leaflet-bottom,\n.leaflet-control,\n.leaflet-pane,\n.leaflet-top {\n    z-index: 0 !important;\n}\n\n.rdrMonth {\n    width: 100% !important;\n}\n\n.rdrCalendarWrapper {\n    font-size: 16px !important;\n    width: 100% !important;\n}"
  },
  {
    "path": "tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    \"./app/**/*.{js,ts,jsx,tsx}\",\n    \"./pages/**/*.{js,ts,jsx,tsx}\",\n    \"./components/**/*.{js,ts,jsx,tsx}\",\n  ],\n  theme: {\n    extend: {},\n  },\n  plugins: [require(\"tailwind-scrollbar\")],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "types.ts",
    "content": "import { Listing, Reservation, User } from \"@prisma/client\";\n\nexport type safeListing = Omit<Listing, \"createdAt\"> & {\n  createdAt: string;\n};\n\nexport type SafeReservation = Omit<\n  Reservation,\n  \"createdAt\" | \"startDate\" | \"endDate\" | \"listing\"\n> & {\n  createdAt: string;\n  startDate: string;\n  endDate: string;\n  listing: safeListing;\n};\n\nexport type SafeUser = Omit<\n  User,\n  \"createdAt\" | \"updatedAt\" | \"emailVerified\"\n> & {\n  createdAt: string;\n  updatedAt: string;\n  emailVerified: string | null;\n};\n"
  }
]