# 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 (