# The Old Vine Hotel - Development Documentation ## ๐Ÿ—๏ธ Architecture Overview This hotel management system follows a modern, scalable architecture designed for enterprise-level operations. ### System Architecture ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ React Client โ”‚โ”€โ”€โ”€โ”€โ”‚ Express API โ”‚โ”€โ”€โ”€โ”€โ”‚ MongoDB โ”‚ โ”‚ (Port 3000) โ”‚ โ”‚ (Port 5000) โ”‚ โ”‚ Database โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ โ”‚ Integrations โ”‚ โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”‚ External APIs โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ€ข Opera PMS โ”‚ โ”‚ โ€ข Booking.com โ”‚ โ”‚ โ€ข Expedia โ”‚ โ”‚ โ€ข Trip.com โ”‚ โ”‚ โ€ข Stripe โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ## ๐Ÿ› ๏ธ Development Setup ### Prerequisites ```bash # Install Node.js (v16+) curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - sudo apt-get install -y nodejs # Install MongoDB wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add - echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list sudo apt-get update sudo apt-get install -y mongodb-org sudo systemctl start mongod sudo systemctl enable mongod ``` ### Environment Setup #### Development Database ```bash # Create development database mongo use oldvinehotel_dev db.createUser({ user: "hotel_admin", pwd: "secure_password", roles: ["readWrite"] }) ``` #### Environment Variables **Server (.env)** ```bash # Development configuration NODE_ENV=development PORT=5000 CLIENT_URL=http://localhost:3000 # Database MONGODB_URI=mongodb://hotel_admin:secure_password@localhost:27017/oldvinehotel_dev # Security JWT_SECRET=your_super_secret_jwt_key_for_development JWT_EXPIRES_IN=7d # External APIs (Development keys) STRIPE_SECRET_KEY=sk_test_your_development_key GOOGLE_MAPS_API_KEY=your_development_maps_key # Email (Development) EMAIL_HOST=smtp.mailtrap.io EMAIL_PORT=2525 EMAIL_USER=your_mailtrap_user EMAIL_PASS=your_mailtrap_pass ``` **Client (.env)** ```bash REACT_APP_API_URL=http://localhost:5000 REACT_APP_STRIPE_PUBLISHABLE_KEY=pk_test_your_development_key REACT_APP_GOOGLE_MAPS_API_KEY=your_development_maps_key ``` ## ๐Ÿ“Š Database Schema ### Core Models #### Room Model ```javascript { name: String, // "Deluxe Ocean Suite" type: String, // "Suite", "Deluxe", etc. roomNumber: String, // "101", "201A" basePrice: Number, // 299.99 maxOccupancy: Number, // 4 amenities: [String], // ["WiFi", "Ocean View"] status: String, // "Available", "Occupied" operaRoomId: String, // PMS integration ID images: [{ url: String, alt: String, isPrimary: Boolean }] } ``` #### Booking Model ```javascript { bookingNumber: String, // "OVH202512345" confirmationCode: String, // "ABC12345" guest: ObjectId, // Reference to Guest room: ObjectId, // Reference to Room checkInDate: Date, checkOutDate: Date, numberOfGuests: { adults: Number, children: Number }, totalAmount: Number, paymentStatus: String, // "Paid", "Pending" status: String, // "Confirmed", "Cancelled" stripePaymentIntentId: String } ``` #### Guest Model ```javascript { firstName: String, lastName: String, email: String, phone: String, password: String, // Hashed loyaltyProgram: { tier: String, // "Bronze", "Silver", "Gold" points: Number }, preferences: { roomType: String, language: String }, totalStays: Number, isVIP: Boolean } ``` ### Database Indexes ```javascript // Room indexes for performance db.rooms.createIndex({ "roomNumber": 1 }, { unique: true }) db.rooms.createIndex({ "type": 1, "status": 1 }) db.rooms.createIndex({ "operaRoomId": 1 }) // Booking indexes db.bookings.createIndex({ "bookingNumber": 1 }, { unique: true }) db.bookings.createIndex({ "confirmationCode": 1 }) db.bookings.createIndex({ "checkInDate": 1, "checkOutDate": 1 }) db.bookings.createIndex({ "guest": 1 }) db.bookings.createIndex({ "room": 1 }) // Guest indexes db.guests.createIndex({ "email": 1 }, { unique: true }) db.guests.createIndex({ "loyaltyProgram.tier": 1 }) ``` ## ๐Ÿ”Œ API Design ### RESTful API Structure ``` GET /api/rooms # List rooms with filters GET /api/rooms/:id # Get specific room POST /api/rooms/:id/availability # Check availability POST /api/bookings # Create booking GET /api/bookings/:bookingNumber # Get booking details PUT /api/bookings/:id/cancel # Cancel booking POST /api/auth/register # Guest registration POST /api/auth/login # Guest login GET /api/auth/me # Current user info POST /api/contact # Send contact message GET /api/contact/info # Hotel information ``` ### Response Format ```javascript // Success Response { "success": true, "data": { // Response data }, "message": "Operation successful" } // Error Response { "success": false, "message": "Error description", "errors": [ { "field": "email", "message": "Invalid email format" } ] } // Paginated Response { "success": true, "data": { "items": [...], "pagination": { "currentPage": 1, "totalPages": 10, "totalCount": 100, "hasNextPage": true, "hasPrevPage": false } } } ``` ## ๐Ÿ” Authentication Flow ### JWT Implementation ```javascript // Token Generation const generateToken = (payload) => { return jwt.sign(payload, process.env.JWT_SECRET, { expiresIn: process.env.JWT_EXPIRES_IN || '7d' }); }; // Token Verification Middleware const auth = async (req, res, next) => { const token = req.header('Authorization')?.replace('Bearer ', ''); if (!token) { return res.status(401).json({ success: false, message: 'Access denied' }); } try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = await User.findById(decoded.id); next(); } catch (error) { res.status(401).json({ success: false, message: 'Invalid token' }); } }; ``` ### Password Security ```javascript // Password Hashing (in model) guestSchema.pre('save', async function(next) { if (!this.isModified('password')) return next(); this.password = await bcrypt.hash(this.password, 12); next(); }); // Password Comparison guestSchema.methods.comparePassword = async function(candidatePassword) { return bcrypt.compare(candidatePassword, this.password); }; ``` ## ๐Ÿ’ณ Payment Integration ### Stripe Implementation ```javascript // Create Payment Intent const createPayment = async (amount, bookingData) => { const paymentIntent = await stripe.paymentIntents.create({ amount: Math.round(amount * 100), // Convert to cents currency: 'usd', metadata: { bookingNumber: bookingData.bookingNumber, guestEmail: bookingData.guest.email } }); return paymentIntent; }; // Handle Webhook const handleStripeWebhook = (req, res) => { const sig = req.headers['stripe-signature']; let event; try { event = stripe.webhooks.constructEvent( req.body, sig, process.env.STRIPE_WEBHOOK_SECRET ); } catch (err) { return res.status(400).send(`Webhook signature verification failed.`); } switch (event.type) { case 'payment_intent.succeeded': // Handle successful payment break; case 'payment_intent.payment_failed': // Handle failed payment break; } res.json({ received: true }); }; ``` ## ๐Ÿ”— Integration Services ### Opera PMS Integration ```javascript class OperaPMSService { async createReservation(booking) { const xmlRequest = this.generateXMLRequest('OTA_HotelResRQ', { // XML structure for Opera PMS }); const response = await this.client.post('/reservations', xmlRequest); return this.parseXMLResponse(response.data); } async syncAvailability() { // Sync room availability with Opera PMS } async updateRates(roomType, rates) { // Update room rates in Opera PMS } } ``` ### Booking Platform APIs ```javascript // Booking.com Integration class BookingComService { async updateAvailability(roomType, dates, availability) { const xmlRequest = this.buildAvailabilityXML(roomType, dates, availability); return this.sendRequest(xmlRequest); } async processWebhook(webhookData) { switch (webhookData.event_type) { case 'booking_created': await this.importBooking(webhookData); break; case 'booking_cancelled': await this.cancelBooking(webhookData); break; } } } ``` ## ๐ŸŽจ Frontend Architecture ### React Component Structure ``` src/ โ”œโ”€โ”€ components/ โ”‚ โ”œโ”€โ”€ common/ # Shared components โ”‚ โ”‚ โ”œโ”€โ”€ Button/ โ”‚ โ”‚ โ”œโ”€โ”€ Modal/ โ”‚ โ”‚ โ””โ”€โ”€ Loading/ โ”‚ โ”œโ”€โ”€ layout/ # Layout components โ”‚ โ”‚ โ”œโ”€โ”€ Header/ โ”‚ โ”‚ โ”œโ”€โ”€ Footer/ โ”‚ โ”‚ โ””โ”€โ”€ Sidebar/ โ”‚ โ””โ”€โ”€ booking/ # Booking-specific components โ”‚ โ”œโ”€โ”€ RoomCard/ โ”‚ โ”œโ”€โ”€ BookingForm/ โ”‚ โ””โ”€โ”€ PaymentForm/ โ”œโ”€โ”€ pages/ # Page components โ”œโ”€โ”€ services/ # API services โ”œโ”€โ”€ hooks/ # Custom React hooks โ”œโ”€โ”€ utils/ # Utility functions โ””โ”€โ”€ context/ # React contexts ``` ### State Management ```javascript // React Query for server state const { data: rooms, isLoading } = useQuery( ['rooms', filters], () => roomsAPI.getRooms(filters), { staleTime: 5 * 60 * 1000, // 5 minutes cacheTime: 10 * 60 * 1000, // 10 minutes } ); // Context for global state const AuthContext = createContext(); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); const [isAuthenticated, setIsAuthenticated] = useState(false); return ( {children} ); }; ``` ### API Service Layer ```javascript // API service structure class RoomsAPI { async getRooms(filters = {}) { const params = new URLSearchParams(filters); const response = await api.get(`/rooms?${params}`); return response.data; } async checkAvailability(roomId, dates) { const response = await api.post(`/rooms/${roomId}/availability`, dates); return response.data; } async createBooking(bookingData) { const response = await api.post('/bookings', bookingData); return response.data; } } export const roomsAPI = new RoomsAPI(); ``` ## ๐Ÿงช Testing Strategy ### Backend Testing ```javascript // Unit Tests (Jest) describe('Room Model', () => { test('should calculate current price with seasonal pricing', () => { const room = new Room({ basePrice: 200, seasonalPricing: [{ season: 'summer', startDate: new Date('2024-06-01'), endDate: new Date('2024-08-31'), priceMultiplier: 1.5 }] }); expect(room.currentPrice).toBe(300); }); }); // Integration Tests describe('Booking API', () => { test('POST /api/bookings should create booking', async () => { const bookingData = { roomId: 'room123', checkInDate: '2024-06-01', checkOutDate: '2024-06-03', guestInfo: { /* guest data */ } }; const response = await request(app) .post('/api/bookings') .send(bookingData) .expect(201); expect(response.body.success).toBe(true); expect(response.body.data.booking).toBeDefined(); }); }); ``` ### Frontend Testing ```javascript // Component Tests (React Testing Library) import { render, screen, fireEvent } from '@testing-library/react'; import BookingForm from '../BookingForm'; test('should submit booking form with valid data', async () => { render(); fireEvent.change(screen.getByLabelText(/check-in date/i), { target: { value: '2024-06-01' } }); fireEvent.click(screen.getByRole('button', { name: /book now/i })); await waitFor(() => { expect(screen.getByText(/booking confirmed/i)).toBeInTheDocument(); }); }); ``` ## ๐Ÿ“Š Performance Optimization ### Database Optimization ```javascript // Efficient aggregation for room availability const getAvailableRooms = async (checkIn, checkOut) => { return Room.aggregate([ { $match: { status: 'Available', isActive: true } }, { $lookup: { from: 'bookings', let: { roomId: '$_id' }, pipeline: [ { $match: { $expr: { $eq: ['$room', '$$roomId'] }, status: { $in: ['Confirmed', 'Checked In'] }, $or: [ { checkInDate: { $lt: checkOut }, checkOutDate: { $gt: checkIn } } ] } } ], as: 'conflictingBookings' } }, { $match: { conflictingBookings: { $size: 0 } } } ]); }; ``` ### Frontend Optimization ```javascript // Code splitting and lazy loading const Booking = lazy(() => import('./pages/Booking')); const AdminDashboard = lazy(() => import('./pages/admin/Dashboard')); // Memoization for expensive calculations const ExpensiveComponent = memo(({ data }) => { const processedData = useMemo(() => { return expensiveCalculation(data); }, [data]); return
{processedData}
; }); // Image optimization const OptimizedImage = ({ src, alt, ...props }) => { const [imageSrc, setImageSrc] = useState(src + '?w=50&q=10'); // Placeholder useEffect(() => { const img = new Image(); img.onload = () => setImageSrc(src); img.src = src; }, [src]); return {alt}; }; ``` ## ๐Ÿ” Monitoring & Logging ### Application Logging ```javascript // Winston logger configuration const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), transports: [ new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), new winston.transports.File({ filename: 'logs/combined.log' }), new winston.transports.File({ filename: 'logs/bookings.log', format: winston.format((info) => { return info.type === 'booking' ? info : false; })() }) ] }); // Usage in application logger.info('Booking created', { type: 'booking', bookingId: booking._id, guestEmail: booking.guest.email, amount: booking.totalAmount }); ``` ### Health Monitoring ```javascript // Health check endpoint app.get('/health', async (req, res) => { const healthCheck = { status: 'OK', timestamp: new Date().toISOString(), uptime: process.uptime(), checks: { database: await checkDatabaseHealth(), redis: await checkRedisHealth(), external_apis: await checkExternalAPIs() } }; const isHealthy = Object.values(healthCheck.checks).every(check => check.status === 'OK'); res.status(isHealthy ? 200 : 503).json(healthCheck); }); ``` ## ๐Ÿš€ Deployment ### Docker Configuration ```dockerfile # Dockerfile for backend FROM node:16-alpine WORKDIR /app COPY package*.json ./ RUN npm ci --only=production COPY . . EXPOSE 5000 CMD ["npm", "start"] ``` ```yaml # docker-compose.yml version: '3.8' services: frontend: build: ./client ports: - "3000:3000" environment: - REACT_APP_API_URL=http://backend:5000 depends_on: - backend backend: build: ./server ports: - "5000:5000" environment: - NODE_ENV=production - MONGODB_URI=mongodb://mongodb:27017/oldvinehotel depends_on: - mongodb mongodb: image: mongo:5.0 ports: - "27017:27017" volumes: - mongodb_data:/data/db volumes: mongodb_data: ``` ### Production Deployment ```bash # Build and deploy script #!/bin/bash # Build frontend cd client npm run build # Deploy to CDN (e.g., AWS S3 + CloudFront) aws s3 sync build/ s3://hotel-website-bucket --delete aws cloudfront create-invalidation --distribution-id ABCD1234 --paths "/*" # Deploy backend cd ../server # Build Docker image docker build -t hotel-api:latest . # Deploy to container registry docker tag hotel-api:latest your-registry/hotel-api:latest docker push your-registry/hotel-api:latest # Deploy to production environment kubectl apply -f k8s/ ``` ## ๐Ÿ”ง Troubleshooting ### Common Issues #### Database Connection ```bash # Check MongoDB status sudo systemctl status mongod # View MongoDB logs sudo journalctl -u mongod # Test connection mongo --eval "db.adminCommand('ismaster')" ``` #### API Issues ```bash # Check API health curl http://localhost:5000/health # Test specific endpoint curl -X POST http://localhost:5000/api/rooms/availability \ -H "Content-Type: application/json" \ -d '{"checkIn":"2024-06-01","checkOut":"2024-06-03"}' ``` #### Frontend Issues ```bash # Clear React cache npm start -- --reset-cache # Check bundle size npm run build -- --analyze # Test production build locally npx serve -s build ``` ### Performance Debugging ```javascript // Database query profiling db.setProfilingLevel(2); // Profile all operations db.system.profile.find().limit(5).sort({ts:-1}).pretty(); // API response time monitoring const responseTime = require('response-time'); app.use(responseTime((req, res, time) => { logger.info('API Response Time', { method: req.method, url: req.originalUrl, responseTime: time, statusCode: res.statusCode }); })); ``` --- This documentation provides a comprehensive guide for developers working on The Old Vine Hotel project. For specific implementation details, refer to the code comments and individual component documentation.