Overview
The Reservation System manages table bookings with intelligent availability checking, automatic table assignment, and configurable time-based rules. The system integrates with table management and supports multi-channel reservation creation.
Key Benefits
Intelligent Table Assignment : Automatically finds optimal table combinations for party size
Real-Time Availability : Checks against existing reservations and active dine-in orders
Configurable Time Rules : Set minimum booking time, duration, and slot intervals
Multi-Channel Support : Accept reservations from admin, web assistant, or WhatsApp bot
Schedule Integration : Respects business hours and special schedule exceptions
Reservation Data Structure
Reservation Interface
From types.ts:126-141:
export interface Reservation {
id : string ; // Format: RES-{timestamp}-{random}
customerName : string ;
customerPhone ?: string ;
guests : number ; // Number of people
reservationTime : string ; // ISO timestamp of booking
tableIds : string []; // Assigned table IDs
status : ReservationStatus ; // Current status
statusHistory : StatusHistory []; // Full audit trail
finishedAt : string | null ; // When completed/cancelled
cancellationReason ?: ReservationCancellationReason ;
notes ?: string ;
createdAt : string ; // When reservation was made
orderId ?: string ; // Link to order if customer orders
createdBy : CreatedBy ; // Source: Admin | Web | WhatsApp
}
Reservation Statuses
From types.ts:111-118:
export enum ReservationStatus {
PENDING = 'Pendiente' , // Initial state
CONFIRMED = 'Confirmada' , // Reservation confirmed
SEATED = 'Sentado' , // Customer arrived and seated
COMPLETED = 'Completada' , // Dining finished
CANCELLED = 'Cancelada' , // Cancelled before arrival
NO_SHOW = 'No Se Presentó' , // Customer didn't arrive
}
Cancellation Reasons
From types.ts:120-124:
export enum ReservationCancellationReason {
USER = 'Cancelado por el cliente' ,
ADMIN = 'Cancelado por el local' ,
SYSTEM = 'Cancelado por el sistema' ,
}
Reservation Settings
From types.ts:143-150:
export interface ReservationSettings {
duration : number ; // Reservation duration in minutes (default: 90)
minBookingTime : number ; // Minimum advance booking time in minutes (default: 60)
initialBlockTime : number ; // How early tables get blocked (default: 60)
extensionBlockTime : number ; // Extended block time (default: 30)
modificationLockTime : number ; // How late modifications allowed (default: 60)
slotInterval : number ; // Time between available slots (default: 30)
}
Default Settings Explanation
duration: 90 minutes
How long each reservation lasts. Tables are blocked for this duration.minBookingTime: 60 minutes
Customers must book at least 60 minutes in advance for same-day reservations.initialBlockTime: 60 minutes
Tables become “blocked” (not available) 60 minutes before reservation time.extensionBlockTime: 30 minutes
Additional buffer for table turnover.modificationLockTime: 60 minutes
Reservations can’t be modified within 60 minutes of start time.slotInterval: 30 minutes
Available time slots are shown in 30-minute increments (e.g., 19:00, 19:30, 20:00).
Core Service Functions
From services/reservationService.ts:
Creating Reservations
addReservation (
reservationData : Omit < Reservation , 'id' | 'status' | 'createdAt' | 'statusHistory' | 'finishedAt' >
): Promise < Reservation >
Example Usage :
import { addReservation } from './services/reservationService' ;
import { CreatedBy } from './types' ;
const newReservation = await addReservation ({
customerName: 'María González' ,
customerPhone: '1234567890' ,
guests: 4 ,
reservationTime: '2026-03-15T20:00:00.000Z' ,
tableIds: [ 'TBL-001' , 'TBL-002' ], // Pre-assigned tables
notes: 'Cumpleaños - preparar decoración' ,
createdBy: CreatedBy . ADMIN ,
});
// Returns reservation with:
// - Auto-generated ID (RES-{timestamp}-{random})
// - status: PENDING
// - statusHistory initialized
Finding Available Tables
findAvailableTables (
time : Date ,
guests : number ,
reservationToIgnoreId ?: string // For editing existing reservation
): string [] | null
How It Works (from reservationService.ts:256-274):
Check Single Table Fit : Looks for one table with capacity ≥ guests
Combine Tables : If no single table fits, combines multiple tables
Return Best Match : Returns smallest suitable table or combination
Example :
import { findAvailableTables } from './services/reservationService' ;
const reservationTime = new Date ( '2026-03-15T20:00:00.000Z' );
const tableIds = findAvailableTables ( reservationTime , 6 );
if ( tableIds ) {
console . log ( `Tables assigned: ${ tableIds . join ( ', ' ) } ` );
// e.g., "TBL-123, TBL-456" (two 4-person tables for 6 guests)
} else {
console . log ( 'No availability for 6 guests at this time' );
}
Checking Availability Slots
getAvailability (
date : Date ,
guests : number
): string [] // Returns array of available time slots like ["19:00", "19:30", "20:00"]
Example :
import { getAvailability } from './services/reservationService' ;
const date = new Date ( '2026-03-15' );
const availableSlots = getAvailability ( date , 4 );
console . log ( availableSlots );
// Output: ["19:00", "19:30", "20:00", "20:30", "21:00", "21:30"]
How It Works (from reservationService.ts:276-321):
Check Business Schedule
Verifies the restaurant is open on the requested date.
Apply Schedule Exceptions
Checks for holidays or special hours that override normal schedule.
Generate Time Slots
Creates slots based on slotInterval (e.g., every 30 minutes).
Filter Past Times
For today, removes slots earlier than current time + minBookingTime.
Check Table Availability
For each slot, calls findAvailableTables() to verify capacity.
Return Available Slots
Returns array of time strings (“HH:MM”) that have availability.
Updating Reservation Status
updateReservationStatus (
reservationId : string ,
status : ReservationStatus ,
cancellationReason ?: ReservationCancellationReason
): Promise < Reservation >
Example :
import { updateReservationStatus } from './services/reservationService' ;
import { ReservationStatus , ReservationCancellationReason } from './types' ;
// Customer arrived - seat them
await updateReservationStatus (
'RES-1234567890-abc' ,
ReservationStatus . SEATED
);
// Customer cancelled
await updateReservationStatus (
'RES-1234567890-abc' ,
ReservationStatus . CANCELLED ,
ReservationCancellationReason . USER
);
Status Update Rules :
Cannot modify reservations in finished states (COMPLETED, CANCELLED, NO_SHOW)
Cancellation requires a reason
Status changes create notifications
finishedAt timestamp auto-set when moving to finished state
Managing Reservation Settings
// Get current settings
getReservationSettings (): ReservationSettings
// Update settings
saveReservationSettings ( settings : ReservationSettings ): Promise < void >
// Fetch from Firebase and cache
fetchAndCacheReservationSettings (): Promise < ReservationSettings >
Example :
import { getReservationSettings , saveReservationSettings } from './services/reservationService' ;
// Get current settings
const settings = getReservationSettings ();
console . log ( `Reservation duration: ${ settings . duration } minutes` );
// Update settings
await saveReservationSettings ({
... settings ,
duration: 120 , // Increase to 2 hours
slotInterval: 15 , // Offer slots every 15 minutes
});
Availability Logic
The system checks multiple factors when determining availability:
Conflict Detection
From reservationService.ts:222-254:
Existing Reservations
Active Dine-In Orders
Schedule Validation
Checks if tables are already reserved: // For each confirmed/seated reservation
const existingResStart = new Date ( reservation . reservationTime );
const existingResEnd = new Date ( existingResStart . getTime () + duration * 60000 );
// Check overlap
if ( newResStart < existingResEnd && newResEnd > existingResStart ) {
// Tables are blocked
}
Checks if tables have active orders: // Filter dine-in orders that aren't finished
const occupiedOrders = orders . filter (
o => o . type === OrderType . DINE_IN && ! isOrderFinished ( o . status )
);
// Block tables with active orders during reservation window
Verifies restaurant is open: const daySchedule = schedule [ dayName ];
// Check for schedule exceptions (holidays, etc.)
const exception = exceptions . find (
ex => date >= ex . startDate && date <= ex . endDate
);
if ( exception && exception . type === ExceptionType . CLOSED ) {
return []; // No availability
}
Table Assignment Strategy
From reservationService.ts:256-274:
Single Table First :
const singleFit = availableTables
. filter ( t => t . capacity >= guests )
. sort (( a , b ) => a . capacity - b . capacity )[ 0 ]; // Smallest suitable table
if ( singleFit ) return [ singleFit . id ];
Combine Tables :
const sortedTables = [ ... availableTables ]. sort (( a , b ) => b . capacity - a . capacity );
const selectedTables : Table [] = [];
let currentCapacity = 0 ;
for ( const table of sortedTables ) {
if ( currentCapacity < guests ) {
selectedTables . push ( table );
currentCapacity += table . capacity ;
}
}
return currentCapacity >= guests ? selectedTables . map ( t => t . id ) : null ;
User Workflows
Admin Creating a Reservation
Navigate to Reservations Panel
Click “Reservas” in the admin sidebar.
Click 'Nueva Reserva'
Opens reservation form.
Select Date and Time
Choose date and time. System shows available slots based on:
Business hours
Existing reservations
Active dine-in orders
Table capacity
Enter Party Size
Number of guests. System automatically:
Finds suitable tables
Assigns optimal combination
Shows if no availability
Add Customer Details
Name, phone (optional), and any special notes.
Submit Reservation
System:
Assigns tables automatically
Sets status to PENDING
Creates notification
Syncs to Firebase
AI Assistant Reservation Flow
From components/ChatAssistantModal.tsx:212-252 and services/geminiService.ts:
Customer Requests Reservation
AI sets actionLock: 'reservation' to focus conversation.
Gather Details
AI collects:
Party size
Date (handles “today”, “tomorrow”, “Friday”)
Time
Customer name and phone
Check Availability in Real-Time
AI has access to current availability (from reservationService.ts:87-142):
Sees busy time slots
Knows table capacity limits
Suggests alternatives if requested time is full
Confirm Details
AI presents summary and asks for confirmation.
Generate Reservation JSON
{
"intent" : "RESERVATION" ,
"customerName" : "Carlos Ruiz" ,
"customerPhone" : "1234567890" ,
"guests" : 4 ,
"date" : "2026-03-15" ,
"time" : "20:00"
}
System Creates Reservation
Calls findAvailableTables() one final time
If available: creates reservation
If not available: AI offers alternatives
Processing Reservations
Reservation Created (PENDING)
Appears in admin panel, notification sent.
Admin Confirms (CONFIRMED)
Usually done after verifying details or calling customer.
Customer Arrives (SEATED)
Admin marks as seated when customer arrives. Tables now show as “Occupied”.
Customer Finishes (COMPLETED)
After dining and payment, mark as completed. Alternative Outcomes :
CANCELLED: Customer cancelled in advance
NO_SHOW: Customer didn’t arrive
Time-Based Rules
Minimum Booking Time
From reservationService.ts:310-313:
if ( isToday && currentTime < new Date ( now . getTime () + settings . minBookingTime * 60 * 1000 )) {
// Skip this slot - too soon
continue ;
}
Example : If it’s 6:00 PM and minBookingTime: 60, customers cannot book for 6:30 PM (only 30 minutes away). First available slot would be 7:00 PM.
Table Blocking Window
From components/AdminDashboard.tsx and tableService.ts:189-214:
const blockWindowEnd = new Date ( now . getTime () + 60 * 60 * 1000 ); // 1 hour from now
const blockedTableMap = new Map < string , Reservation >();
const reservedTableMap = new Map < string , Reservation >();
confirmedReservations . forEach ( res => {
const resTime = new Date ( res . reservationTime );
if ( resTime > now && resTime <= blockWindowEnd ) {
// Within 1 hour - table shows as "Bloqueada" (Blocked)
res . tableIds . forEach ( tableId => blockedTableMap . set ( tableId , res ));
} else if ( resTime > blockWindowEnd && resTime <= reservationWindowEnd ) {
// 1-2 hours away - table shows as "Reservada" (Reserved)
res . tableIds . forEach ( tableId => reservedTableMap . set ( tableId , res ));
}
});
Visual States in Admin Panel :
Blocked (red): Reservation starts within 1 hour
Reserved (yellow): Reservation is 1-2 hours away
Free (green): No upcoming reservations
Schedule Integration
From reservationService.ts:276-291:
Business Hours Check
const schedule = getScheduleFromCache ();
const dayNames = [ 'sunday' , 'monday' , 'tuesday' , 'wednesday' , 'thursday' , 'friday' , 'saturday' ];
const dayName = dayNames [ date . getDay ()];
let daySchedule = schedule [ dayName ];
if ( ! daySchedule || ! daySchedule . isOpen ) {
return []; // No slots available - closed
}
Schedule Exceptions
const exceptions = getScheduleExceptionsFromCache ();
const applicableException = exceptions . find (
ex => dateStr >= ex . startDate && dateStr <= ex . endDate
);
if ( applicableException ) {
if ( applicableException . type === ExceptionType . CLOSED ) {
return []; // Closed for holiday
}
if ( applicableException . type === ExceptionType . SPECIAL_HOURS && applicableException . slots ) {
daySchedule = { isOpen: true , slots: applicableException . slots };
}
}
Real-Time Synchronization
From components/AdminDashboard.tsx:107-108:
const unsubReservations = onSnapshot (
collection ( db , 'Reservations' ),
( querySnapshot ) => {
const reservations = querySnapshot . docs . map ( doc => doc . data () as Reservation );
updateReservationsCache ( reservations );
setDataTimestamp ( Date . now ());
}
);
const unsubSettings = onSnapshot (
doc ( db , 'ReservationSettings' , 'main' ),
( docSnap ) => {
if ( docSnap . exists ()) {
updateSettingsCache ( docSnap . data () as ReservationSettings );
}
}
);
Notifications
From components/AdminDashboard.tsx:189-203:
// Check for reservations starting within 15 minutes
const upcomingReservations = getReservationsFromCache (). filter ( r => {
if ( r . status !== ReservationStatus . CONFIRMED ) return false ;
const diffMinutes = ( new Date ( r . reservationTime ). getTime () - Date . now ()) / 60000 ;
return diffMinutes > 0 && diffMinutes <= 15 ;
});
upcomingReservations . forEach ( res => {
addNotification ({
message: `La reserva para ${ res . customerName } ( ${ res . guests } p) comienza en menos de 15 minutos.` ,
type: 'reservation' ,
relatedId: res . id ,
});
});
Configuration Best Practices
Set Realistic Duration Default 90 minutes works for most restaurants. Adjust based on your average dining time.
Balance Slot Intervals Smaller intervals (15-30 min) offer flexibility but may overwhelm kitchen. 30-60 min recommended.
Require Advance Booking 60-minute minBookingTime prevents last-minute rushes and ensures prep time.
Use Schedule Exceptions Configure holidays and special events in advance to prevent booking issues.
Tables - Reservation system assigns and blocks tables
Orders - Reservations can link to orders when customer dines
AI Assistants - Automated reservation booking via Slice and WhatsApp