A professional full-stack web application for resort management, featuring a robust reservation engine with conflict prevention and an administrative dashboard.
Built to demonstrate: Advanced booking logic, relational data integrity, type-safe API design, and modern full-stack architecture.
This project serves as a demonstration of production-grade full-stack development practices. It tackles real-world challenges in the hospitality domain:
- Booking Conflict Prevention โ Server-side logic to prevent double-bookings
- Data Integrity โ Cascading deletes and indexed foreign keys for scalable relational data
- Type Safety โ End-to-end TypeScript with Prisma for compile-time guarantees
- Admin UI Demo Mode โ Toggle-based conditional rendering for rapid prototyping
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript |
| Database | PostgreSQL (Supabase-hosted) |
| ORM | Prisma |
| Styling | Tailwind CSS v4 |
| UI Components | Radix UI (Slot), shadcn/ui, Lucide Icons |
| Deployment | Vercel-ready |
The reservation system prevents overlapping bookings at the application layer with database-validated constraints:
- Overlap Detection: Server-side logic checks if a new reservation conflicts with existing dates for a specific room
- Date Validation: Ensures check-out dates are strictly after check-in dates
- Application-level Conflict Prevention: Pre-creation checks prevent double-bookings
// Actual implementation: Prevent overlapping reservations
const overlapping = await prisma.reservation.findFirst({
where: {
roomId,
AND: [
{ checkIn: { lt: checkOutDate } },
{ checkOut: { gt: checkInDate } },
],
},
});
if (overlapping) {
return NextResponse.json(
{ error: "Room is already booked for selected dates" },
{ status: 409 }
);
}Built with a structured PostgreSQL schema to ensure referential integrity:
- Cascading Deletes: Configured
onDelete: Cascadeon theReservationโRoomrelation, so deleting a room automatically removes all associated reservations - Optimized Queries:
@@indexonroomIdforeign key ensures fast lookups as the dataset scales - UUID Primary Keys: Uses universally unique identifiers for distributed-system readiness
All API routes leverage Next.js 14 App Router with standardized response patterns:
- HTTP Status Codes:
400for validation errors,409for booking conflicts,201for successful creation,204for deletions - Prisma Client Integration: Full type safety from database schema to API responses
- React Server Components: Server-side rendering for optimized initial page loads
// Example: Type-safe route handler
export async function POST(req: Request) {
const body = await req.json();
const reservation = await prisma.reservation.create({
data: {
roomId: body.roomId,
guestName: body.guestName,
checkIn: new Date(body.checkIn),
checkOut: new Date(body.checkOut),
},
});
return NextResponse.json(reservation, { status: 201 });
}resort-reservation-system/
โโโ app/
โ โโโ resorts/
โ โ โโโ [id]/
โ โ โ โโโ rooms/new/
โ โ โ โ โโโ page.tsx # Add room form
โ โ โ โโโ page.tsx # Resort detail view
โ โ โโโ page.tsx # Resorts listing page
โ โโโ admin/
โ โ โโโ reservations/
โ โ โ โโโ page.tsx # Reservation management (admin)
โ โ โโโ resorts/
โ โ โโโ [id]/
โ โ โ โโโ rooms/new/
โ โ โ โโโ page.tsx # Add room form (admin)
โ โ โโโ new/
โ โ โโโ page.tsx # Create resort form (admin)
โ โโโ api/
โ โ โโโ reservations/
โ โ โ โโโ [id]/
โ โ โ โ โโโ route.ts # PATCH, DELETE specific reservation
โ โ โ โโโ route.ts # GET, POST, DELETE reservation
โ โ โโโ resorts/
โ โ โ โโโ [id]/
โ โ โ โ โโโ route.ts # GET, DELETE resort
โ โ โ โโโ route.ts # POST create resort
โ โ โโโ rooms/
โ โ โโโ [id]/
โ โ โ โโโ route.ts # PUT, DELETE room
โ โ โโโ route.ts # POST create room
โ โโโ favicon.ico
โ โโโ globals.css
โ โโโ layout.tsx # Root layout with navigation
โ โโโ page.tsx # Homepage/resort listing
โโโ components/
โ โโโ ui/
โ โ โโโ button.tsx # Reusable UI primitives
โ โโโ AdminEditRoomPanel.tsx # Room edit modal (admin)
โ โโโ BookRoomForm.tsx # Public booking form
โ โโโ CancelReservationButton.tsx
โ โโโ DeleteResortButton.tsx
โ โโโ DeleteRoomButton.tsx
โ โโโ EditReservationForm.tsx
โ โโโ EditRoomForm.tsx
โ โโโ TopNav.tsx # Navigation bar
โโโ lib/
โ โโโ prisma.ts # Prisma client instance
โ โโโ utils.ts # Utility functions (cn, etc.)
โโโ prisma/
โ โโโ migrations/ # Migration history
โ โโโ schema.prisma # Database schema
โโโ .env # Environment variables
โโโ .gitignore
โโโ components.json # shadcn/ui config
โโโ eslint.config.mjs
โโโ next.config.ts
โโโ package.json
โโโ postcss.config.mjs
โโโ prisma.config.ts
โโโ README.md
โโโ tailwind.config.ts
โโโ tsconfig.json
The application uses a normalized relational structure with three core entities:
model Resort {
id String @id @default(uuid())
name String
location String
description String?
createdAt DateTime @default(now())
rooms Room[]
}
model Room {
id String @id @default(uuid())
resortId String
name String
price Int
capacity Int
createdAt DateTime @default(now())
resort Resort @relation(fields: [resortId], references: [id])
reservations Reservation[]
}
model Reservation {
id String @id @default(uuid())
roomId String
guestName String
checkIn DateTime
checkOut DateTime
createdAt DateTime @default(now())
room Room @relation(fields: [roomId], references: [id], onDelete: Cascade)
@@index([roomId])
}Design Decisions:
- UUIDs over auto-increment IDs: Better for distributed systems and prevents enumeration attacks
- Indexed foreign keys:
@@index([roomId])on reservations for O(log n) lookups - Cascade deletes: Maintains referential integrity without orphaned records
- Browse resorts and view available rooms
- Check room availability for date ranges
- Create reservations with guest details
- View all reservations per room
- Create and delete resorts
- Add rooms to resorts with pricing and capacity
- Edit room details (price, capacity)
- Delete rooms (cascades to reservations)
- Edit reservation details
- Cancel reservations
- View all system reservations
Note on Admin Access: This project uses navigation-based admin routes (e.g.,
/admin/reservations,/admin/resorts/new) for demonstration purposes. Admin features are conditionally rendered using a localisAdminflag within components for demo purposes. This design choice allows rapid testing and demonstration without implementing OAuth/JWT flows. In production, these routes would be protected with proper authentication middleware (e.g., NextAuth.js, Clerk, or Auth0) and role-based access control.
- Node.js 18+ and npm
- PostgreSQL database (local or cloud-hosted)
-
Clone the repository
git clone <your-repo-url> cd resort-reservation-system
-
Install dependencies
npm install
-
Set up environment variables
Create a
.envfile in the root:DATABASE_URL="postgresql://user:password@localhost:5432/resort_db"
-
Initialize the database
npx prisma migrate dev --name init npx prisma generate
-
Seed the database (optional)
npx prisma db seed
-
Start the development server
npm run dev
-
Open the app
http://localhost:3000
All API endpoints follow RESTful conventions:
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/resorts |
List all resorts |
POST |
/api/resorts |
Create a resort |
GET |
/api/resorts/[id] |
Get resort details |
DELETE |
/api/resorts/[id] |
Delete a resort |
POST |
/api/rooms |
Create a room |
PUT |
/api/rooms/[id] |
Update room details |
DELETE |
/api/rooms/[id] |
Delete a room |
POST |
/api/reservations |
Create a reservation |
GET |
/api/reservations?roomId={id} |
Get reservations for a room |
DELETE |
/api/reservations?id={id} |
Cancel a reservation |
Response Standards:
- Success:
200 OK,201 Created,204 No Content - Client Errors:
400 Bad Request,404 Not Found,409 Conflict - Server Errors:
500 Internal Server Error
This application showcases:
Full-stack TypeScript proficiency โ End-to-end type safety from database to UI
Booking conflict logic โ Server-side overlap detection with date validation
Database design expertise โ Normalized schema with cascading relationships and indexes
Modern React patterns โ App Router with server and client components
RESTful API design โ Proper HTTP methods, status codes, and error handling
Production-ready structure โ Environment variables, migrations, and deployment-ready setup
Potential improvements for a production deployment:
- Authentication: Integrate NextAuth.js or Clerk for secure admin access
- Image Uploads: Add room photos with Cloudinary or S3
- Search & Filtering: Full-text search on resorts and date-based availability filters
- Payment Integration: Stripe for handling deposits and booking payments
- Email Notifications: Confirmation emails via Resend or SendGrid
- Calendar View: Visual date picker showing room availability
- Multi-currency Support: Dynamic pricing based on user locale
- Performance Monitoring: Integrate Sentry or LogRocket for error tracking
Jack Jalandoni
GitHub โข LinkedIn
This project is open source and available under the MIT License.
