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:
- Vendor Hub - Property and room management, bookings, financials
- Customer Site - Browse hotels, filter by amenities, book rooms
- 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-persistsaves 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
- Unified pricing engines are hard - edge cases multiply fast (weekends, holidays, seasons)
- State persistence is critical for multi-step flows - users abandon if they lose progress
- Real-time availability is non-negotiable - double-bookings destroy trust
- Redux shines for complex state - worth the boilerplate for deeply nested entities
- Leaflet is underrated - great Maps alternative for location-heavy apps