hospitality

StayDiscover

Hotel Management & Booking Platform

ReactTypeScriptReduxNode.jsSequelizePostgreSQLLeaflet Maps
3
Portals Built
4+
Pricing Models
Leaflet
Map Integration

The Problem

StayDiscover needed a complete hotel booking platform where hotel vendors could manage properties, rooms, and pricing, while customers could search, filter, and book accommodations. The challenge was building a unified pricing calculation system that supported multiple pricing models: weekend vs weekday rates, per-night vs per-person pricing, breakfast options, and per-trip addons — all while maintaining real-time availability and booking workflows.

What Made It Hard

Complex Pricing Engine

Hotels price rooms differently based on:

  • Weekend vs Weekday rates
  • Per-night pricing (fixed room cost)
  • Per-person pricing (cost scales with guests)
  • Breakfast inclusions (per person, per night)
  • Per-trip addons (airport shuttle, spa packages)
  • Date range overlaps with different rate schedules

The system needed to calculate accurate totals in real-time while users adjusted dates, guest counts, and addon selections.

Multi-Step Property Onboarding

Vendors had to complete complex onboarding flows:

  • Property details (name, location, amenities)
  • Room configuration (types, capacities, pricing models)
  • Image uploads (property photos, room images)
  • Availability calendars
  • Pricing rules and schedules

Challenge: Maintaining form state across 5+ steps, with validation at each stage, without losing progress.

Real-Time Availability

  • Rooms booked by one customer must be unavailable to others instantly
  • Calendars must reflect blocked dates, pending bookings, and confirmed reservations
  • State synchronization across vendor portal and customer site

My Solution

3-Portal Architecture

Built a complete ecosystem with clear separation:

  1. Vendor Hub - Property and room management, bookings, financials
  2. Customer Site - Browse hotels, filter by amenities, book rooms
  3. Backend API - Centralized business logic, pricing engine, availability

Unified Pricing Calculation Engine

Created a composable pricing system that handles all pricing models:

interface PricingBreakdown {
  basePrice: number;        // Room cost
  guestCharges: number;     // Per-person fees
  breakfastCost: number;    // Breakfast addon
  tripAddons: number;       // Airport shuttle, etc.
  weekendSurcharge: number; // Weekend premium
  total: number;
}

function calculateBookingTotal(
  room: Room,
  checkIn: Date,
  checkOut: Date,
  guests: number,
  addons: Addon[]
): PricingBreakdown {
  // Iterate through each night
  // Apply per-night or per-person pricing
  // Add weekend surcharge if applicable
  // Include breakfast (per person * nights)
  // Add one-time trip addons
}

Key Features:

  • Real-time calculation as user adjusts dates/guests
  • Breakdown displayed line-by-line (transparency)
  • Handles edge cases (checkout day not charged, weekend detection)
  • Supports promo codes and discounts

Multi-Step Form with State Persistence

Implemented Redux-persisted form state across onboarding:

Pattern Used:

  • Each step is a separate component
  • Redux stores form data in normalized entities
  • redux-persist saves to localStorage after each step
  • User can navigate back/forward without data loss
  • Final submission consolidates all steps

Validation Strategy:

  • Step-level validation (can't proceed if incomplete)
  • Warning indicators on incomplete sections
  • Optional sections marked clearly
  • Draft save before final submission

Leaflet Map Integration for Location Selection

Used Leaflet.js for property location:

  • Interactive map for vendor to place property pin
  • Reverse geocoding to get address from coordinates
  • Customer site displays properties on map view
  • Geolocation-based search ("hotels near me")

Dynamic Availability Management

Built a calendar-based availability system:

  • Vendors block dates for maintenance
  • Booked dates automatically unavailable
  • Pending bookings hold inventory for 15 minutes
  • Real-time updates via polling (every 30s on calendar view)

Technical Decisions

Why Redux over Context API?

  • Multi-step forms need deep state nesting
  • Undo/redo for property edits
  • Time-travel debugging during development
  • Normalized state for rooms, properties, bookings

Why Leaflet over Google Maps?

  • No API key limits (Google Maps pricing)
  • Fully open-source
  • Customizable tile layers
  • Better performance for map-heavy interfaces

Why Per-Component Validation?

  • Better UX (instant feedback)
  • Prevents invalid state propagation
  • Reusable validation schemas (shared with backend)
  • Clear error messages per field

Why PostgreSQL?

  • Complex relational data (properties → rooms → bookings)
  • JSON columns for flexible addon storage
  • Full-text search for property descriptions
  • Strong consistency for booking transactions

Impact

Business Metrics

  • 3 portals delivered (vendor, customer, backend)
  • 4+ pricing models supported in unified engine
  • 100+ hotel properties onboarded
  • Real-time availability preventing double-bookings

Technical Achievements

  • Zero double-booking incidents (critical for reputation)
  • Sub-300ms pricing calculations even with complex rules
  • 95% form completion rate (state persistence reduced abandonment)
  • Leaflet map integration with custom clustering

Developer Experience

  • Reusable pricing calculation library
  • Composable form validation schemas
  • Redux DevTools for debugging booking flows
  • Automated E2E tests for critical paths (booking, payment)

What I'd Do Differently

Pricing Calculation Performance

Would implement memoization for pricing calculations:

  • Cache results per (room, dates, guests) combination
  • Invalidate only on rate changes
  • Current approach recalculates on every render (performance hit with many rooms)

Multi-Step Form Library

Would explore React Hook Form or Formik:

  • Less Redux boilerplate for form state
  • Built-in validation patterns
  • Better performance with uncontrolled inputs
  • Current Redux approach works but verbose

Availability Polling

Would switch to WebSockets for real-time updates:

  • 30-second polling is crude (lag for high-traffic periods)
  • WebSocket would give instant availability updates
  • Reduces server load (no repeated polling)

Image Uploads

Would use Cloudinary or Imgix:

  • Current approach stores images in S3 directly
  • No automatic optimization or resizing
  • Cloudinary provides transformations on-the-fly
  • Better performance for customer site

Key Takeaways

  1. Unified pricing engines are hard - edge cases multiply fast (weekends, holidays, seasons)
  2. State persistence is critical for multi-step flows - users abandon if they lose progress
  3. Real-time availability is non-negotiable - double-bookings destroy trust
  4. Redux shines for complex state - worth the boilerplate for deeply nested entities
  5. Leaflet is underrated - great Maps alternative for location-heavy apps