A full-stack Airbnb-inspired accommodation platform where users can list properties, search with advanced filters (location, dates, guests), make reservations with date-range calendars, manage favorites, and explore locations on interactive Leaflet maps — built with Next.js 13, Prisma with MongoDB, NextAuth.js, and Cloudinary.
How the system is structured from frontend to external services.
Server-rendered pages with client-side interactivity. Multi-step modals (search + rent) managed via Zustand. Interactive Leaflet maps for location selection and display. Date range calendar with booked-date exclusion.
RESTful API routes for listings, reservations, and favorites CRUD. NextAuth.js handles credentials and Google OAuth with JWT sessions. Server actions fetch data with complex Prisma queries including date-range overlap detection.
MongoDB document store with Prisma ORM. Four core models: User, Listing, Reservation, Account. Favorites stored as ObjectId array on User document (no join table). Date overlap queries use Prisma NOT + OR conditions.
Cloudinary for property image uploads with next-cloudinary widget. Google OAuth via NextAuth provider. OpenStreetMap tiles for Leaflet map rendering with country-based centering.
Core entities and how they relate to each other.
User accounts with credentials and OAuth support
id (ObjectId)nameemail (unique)emailVerified (DateTime)hashedPasswordimagefavoriteIds (String[] @db.ObjectId)createdAtupdatedAtOAuth provider links (Google) managed by NextAuth Prisma Adapter
iduserId → UsertypeproviderproviderAccountIdaccess_tokenrefresh_tokenProperty listings with location, pricing, and category
id (ObjectId)titledescriptionimageSrc (Cloudinary URL)categoryroomCountbathroomCountguestCountlocationValue (country code)price (Int)userId → UserGuest bookings with date range and total price
id (ObjectId)userId → User (guest)listingId → ListingstartDateendDatetotalPrice (Int)createdAtStep-by-step walkthrough of the main user and system flows.
When searching for available listings, you need to exclude properties that already have reservations overlapping with the requested dates. A simple date comparison isn't enough — you need to detect all types of overlap (partial, full, enclosing).
Prisma's NOT + OR query filters out listings with conflicting reservations. The overlap condition checks if any existing reservation overlaps the requested date range (reservation.startDate <= requestedEndDate AND reservation.endDate >= requestedStartDate). If true, that listing is excluded from results.
The Rent modal has 6 steps (category → location → details → image → description → price), each building on previous selections. Managing form state across steps while keeping validation and allowing back navigation is complex.
Used a single React Hook Form instance with a step counter. Each step conditionally renders its inputs while the form state persists across all steps. Used form.setValue() for programmatic updates (e.g., when selecting a category or uploading an image) and form.watch() to read values reactively for the map and preview. Back/Next buttons increment/decrement the step counter, and the form only submits on the final step.
Leaflet depends on the window object for map rendering, which doesn't exist during server-side rendering in Next.js. Importing Leaflet in a server-rendered component causes 'window is not defined' errors.
Used Next.js dynamic import with { ssr: false } to load the Map component only on the client side. The map component is wrapped in a useMemo that re-renders when the center coordinates change (when the user selects a new country). Custom marker icons are configured with explicit iconUrl paths to fix the default icon loading issue in Next.js's module bundling.
Both the guest who made the reservation AND the property owner should be able to cancel a reservation, but they access it from different pages (/trips vs /reservations). The API needs to verify the requester is one of these two authorized parties.
The DELETE route uses Prisma's findUnique with an OR condition — it only returns the reservation if the requesting user is either the listing owner (listing.userId) or the guest who made the reservation (reservation.userId). If findUnique returns null, the route returns 400.
WanderStay is a full-stack Airbnb-inspired platform built with Next.js 13, Prisma, and MongoDB. It features a multi-step search modal with country selection (world-countries data + Leaflet maps), date range filtering with overlap detection to ensure availability, and guest/room/bathroom counters. Property owners can list homes through a 6-step modal covering category, location, details, Cloudinary image upload, and pricing. The reservation system calculates total price from night count, disables booked dates on the calendar, and supports cancellation by both guests and hosts. Favorites use MongoDB's document model with an ObjectId array on the User document. Authentication combines email/password (bcrypt) with Google OAuth via NextAuth.js using JWT sessions.