Customer-facing website for a hotel management experience (learning project), built with Next.js 14 (App Router), Tailwind CSS, Supabase, and Auth.js (NextAuth v5 beta).
Inspired by the Wild Oasis website from Jonas Schmedtmann’s Ultimate React Course: https://github.com/jonasschmedtmann/ultimate-react-course
This README is the entry point (how to run locally + where to find documentation). Longer guides live in docs/.
Live demo: https://tourmate-iota.vercel.app/
- Getting started
- Project guide
- API endpoints
- Authentication
- Interactivity & mutations
- Project history
- Documentation
- Node.js 18+ (recommended)
- npm
npm install
npm run dev
Then open http://localhost:3000
Create .env.local in the project root.
Auth.js (Google OAuth):
AUTH_GOOGLE_ID="..." AUTH_GOOGLE_SECRET="..." AUTH_SECRET="..."
AUTH_URL="http://localhost:3000"
Supabase:
SUPABASE_URL="https://YOUR_PROJECT.supabase.co" SUPABASE_KEY="YOUR_SUPABASE_ANON_KEY"
Note: keep keys server-side. Never expose Supabase service role keys to the browser.
- npm run dev: start dev server
- npm run build: production build
- npm run start: start production server
- npm run prod: build + start
- npm run lint: run ESLint
app/
_components/ UI components
_lib/ data + Supabase client
_styles/ global styles
about/ /about
account/ /account (+ nested routes)
cabins/ /cabins (+ dynamic cabin page)
error.js segment error boundary
loading.js root loading UI
not-found.js root 404
layout.js root layout
page.js /
public/
Path alias: @/* maps to the project root (see jsconfig.json).
- / -> app/page.js
- /cabins -> app/cabins/page.js
- /cabins/[cabinid] -> app/cabins/[cabinid]/page.js
- /about -> app/about/page.js
- /account -> app/account/page.js
- /account/profile -> app/account/profile/page.js
- /account/reservations -> app/account/reservations/page.js
- app/error.js and app/not-found.js control error/404 UI.
- Use notFound() for "missing resource" flows (see getCabin() in app/_lib/data-service.js).
- fetch() is cached by default in Server Components; opt out explicitly when you need always-fresh reads.
GET /api/cabins/[cabinid]→ app/api/cabins/[cabinid]/route.js
Auth.js configuration + flows are documented in:
This app uses Next.js App Router + Server Actions to handle mutations (create/update/delete) without building separate REST endpoints for every form.
- Mutations live on the server: Server Actions run in a trusted environment, so they’re the right place for authorization and DB writes.
- Never trust client input: validate and normalize numbers/dates/strings before inserting or updating.
- UI freshness is explicit: after a mutation, revalidate the routes that render the changed data.
Server Actions are async functions marked with "use server" (see app/_lib/actions.js). You can call them from forms or from Client Components.
Typical pattern:
- Read data from
formData - Get the current user/session (
auth()) - Validate/normalize
- Write to Supabase
revalidatePath()(and oftenredirect())
In the App Router, data can be cached at multiple layers. If you mutate data and the UI doesn’t update, it usually means the route that renders that data wasn’t revalidated.
Common approach after a booking mutation:
revalidatePath('/account/reservations')to refresh the reservations listrevalidatePath(/cabins/${cabinId})to refresh availability/calendarredirect('/account/reservations')to take the user back to the updated view
| Hook | Best for | What it changes |
|---|---|---|
useFormStatus |
Pending state for a <form> action |
Disables buttons, shows loading state |
useTransition |
Running async work without blocking UI | Marks state updates as non-urgent |
useOptimistic |
Immediate UI updates before server confirms | Great for “feels instant” deletes/edits |
-
Create a booking
- UI:
app/_components/ReservationForm.jssubmits a form tocreateBooking. - State: selected dates come from
app/_components/ReservationContext.js. - Server:
createBookinginapp/_lib/actions.jsvalidates inputs, inserts intobookings, then revalidates and redirects.
- UI:
-
Update a reservation
- Server:
updateReservationinapp/_lib/actions.jsvalidates inputs and updates the booking.
- Server:
-
Delete a reservation
- UI:
app/_components/ReservationList.jstriggers deletion. - Server:
deleteBookinginapp/_lib/actions.jsshould enforce ownership on the delete query (not just client checks).
- UI:
On the server, always validate and normalize inputs (dates, numbers), then insert and revalidate.
This repo’s development timeline (derived from the full git commit history) lives here:
Long-form docs live in docs/: