Smart Voice-Controlled Resort & Hospitality Management System
A production-grade omnichannel resort management platform with AI-driven voice automation. Handles real-time room reservations, voice food ordering, and housekeeping dispatch with row-level concurrency locking and automated folio billing.
1. Introduction & System Overview
Smart Voice-Controlled Resort & Hospitality Management System is an enterprise-grade, full-stack resort automation platform designed to deliver a hands-free guest experience while optimizing resort operations. Built with Next.js (App Router), MySQL, and Web Speech API, it integrates conversational voice AI with relational database integrity. The system coordinates room reservations, in-room food ordering, and housekeeping dispatching in real time, preventing double bookings through row-level database locking (FOR UPDATE) and consolidating charges into an automated guest master folio.
π¨ For Resort Guests
- Interact naturally using English voice commands for room bookings, dining, and cleaning.
- Explore virtual room previews with dynamic UI image popups based on spoken room categories.
- Experience uninterrupted conversations with full-duplex speech AI barge-in handling.
- Receive instant audio confirmation and order breakdowns before final booking or dispatch.
- Enjoy seamless consolidated billing where all room services accumulate onto a single master card.
π¨βπ³ For Kitchen & Operations Staff
- Receive FIFO-queued room food orders on real-time kitchen display screens via SSE.
- Track housekeeping dispatches automatically categorized by room numbers and status.
- Update task statuses instantly (Pending β In-Progress β Completed) with clean status sync.
- Eliminate duplicate cleaning requests through intelligent backend state verification.
βοΈ For Resort Administrators
- Manage resort room inventories, category pricing, view features, and dynamic image URLs.
- Control food menu availability, pricing tiers, and housekeeping staff assignments.
- Monitor active check-ins, aggregated guest folio billings, and revenue analytics.
- Inspect raw voice interaction transcripts, confidence scores, and system audit logs.
Core Objective
Develop a production-ready, voice-assisted Relational Database System (MySQL + Next.js) that automates speech interaction, enforces ACID transactional integrity across 10 normalized (3NF) tables, handles speech confidence fallbacks, resolves race conditions during concurrent room bookings, and maintains real-time synchronization between guest actions and resort service dashboards using Server-Sent Events (SSE).
2. System Architecture & Data Flow
Smart Voice-Controlled Resort & Hospitality Management System is engineered on a unified, high-concurrency client-server architecture powered by Next.js (App Router) and MySQL. The client side utilizes browser-native Web Speech APIs for hands-free voice capture and full-duplex speech synthesis. The backend manages complex resort operational workflowsβincluding NLP intent parsing, room booking state checks, automated housekeeping dispatching, row-level concurrency locking (FOR UPDATE), and transactional aggregation across 10 normalized (3NF) relational database tables.
ποΈ System Layers Breakdown
Next.js Client Components + Web Speech API
Handles interactive guest interfaces, dynamic category image previews, and operational dashboards. Integrates SpeechRecognition (STT) for natural voice capture with audio barge-in support and SpeechSynthesis (TTS) for instant voice responses.
Next.js App Router API Routes & Server Logic
Extracts user intents (room reservations, menu orders, cleaning requests), validates input confidence scores, executes ACID transaction logic, logs interaction transcripts, and streams live updates to staff screens using Server-Sent Events (SSE).
MySQL Relational Database (3NF)
Stores fully normalized resort records across 10 structured tables (Rooms, Categories, Bookings, Food Orders, Order Details, Housekeeping, Folio Billing, Voice Logs, Guests, Menu Items). Guarantees data consistency with strict foreign keys and atomic transaction locks.
π Step-by-Step Multi-Service Voice Data Flow
- Voice Capture & Speech-to-Text: The system greets the user via audio speech synthesis. When the guest speaks an intent (e.g., "Book an Ocean View room for 2 nights" or "Order 2 burgers for Room 302"), the Web Speech API transcribes the audio into clean text.
- Intent Extraction & Entity Parsing: The transcribed text string is dispatched to a Next.js API route. The server-side intent engine parses action types, room numbers, dates, quantities, and item titles, cross-referencing requested data with active MySQL database tables.
- Concurrency Locking & Validation (Preventing Overbooking): For room reservations, the backend initiates an atomic SQL transaction with row-level locking (
SELECT ... FOR UPDATE) onROOMSto check availability and prevent double bookings during simultaneous guest requests. - Automated Fallback Handling: If the audio input is noisy, ambiguous, below the confidence threshold, or references unavailable items/rooms, the system logs the event in
VOICE_INTERACTION_LOGSand plays a polite voice fallback prompt:"I didn't quite catch that. Could you please repeat your request?" - Confirmation & Master Folio Billing: Upon successful validation, the system reads back an audio summary and cost calculation. Once confirmed, transactions are committed to MySQL, automatically updating the consolidated bill in
FOLIO_BILLINGlinked to the guest's active booking ID. - Real-time Operations Sync (SSE Push): Kitchen orders and housekeeping dispatches are immediately broadcast over Server-Sent Events (SSE) to the respective Kitchen Display Systems (KDS) and Housekeeping Dashboards. Staff status updates (Pending β In-Progress β Completed) dynamically sync back to the database in real time.
3. Database Schema & 3NF Normalization
To ensure transactional integrity, support concurrent voice operations, eliminate redundancy, and maintain accurate billing, the Smart Resort Database schema is strictly normalized up to Third Normal Form (3NF). The database model covers 10 primary relational entities, enabling seamless coordination across room reservations, in-room food orders, housekeeping task dispatching, and master folio billing.
ποΈ Relational Tables Overview (10 Core Entities)
1. room_categories
Defines resort room tiers, bed configurations, base nightly rates, descriptive summaries, showcase image references, and creation timestamps.
2. rooms
Stores individual physical room units, room numbers, specific view features, category references, and live operational states (AVAILABLE, OCCUPIED, CLEANING_REQUIRED, MAINTENANCE).
3. guests
Holds registered resort guest profiles, unique phone numbers, and optional email contact records for booking references.
4. room_bookings
Core reservation ledger tracking check-in/check-out calendar dates, room assignments, guest linkages, calculated total charges, and reservation statuses.
5. menu_items
Holds in-room dining catalog items including dish titles, item pricing, real-time availability toggles, and food image assets.
6. room_food_orders
Header table for room dining requests tracking linked booking IDs, destination room numbers, subtotal amounts, and kitchen workflow statuses.
7. food_order_details
Junction table capturing itemized line items per order, quantity requested, and unit price snapshots for accurate item-level ledgering.
8. housekeeping_requests
Tracks voice-triggered cleaning or maintenance requests, destination room references, assigned staff names, priority request types, and fulfillment timelines.
9. folio_billing
Itemized transactional billing ledger recording individual line-item charge types (room fees, food orders, laundry), payment statuses, payment methods, and transaction reference IDs.
10. voice_interaction_logs
Records raw STT transcripts, recognized intent strings, confidence ratings, session identifiers, and optional room number contexts for NLU quality auditing.
π Database Schema Constraints (SQL Field Level Breakdown)
- id (INT, PK, Auto Increment)
- category_name (VARCHAR 100, NOT NULL)
- bed_type (VARCHAR 50, NOT NULL)
- base_price (DECIMAL 10,2, NOT NULL)
- description (TEXT, NULL)
- image_url (VARCHAR 255, NOT NULL)
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- category_id (INT, FK -> room_categories.id)
- room_number (VARCHAR 20, UNIQUE, NOT NULL)
- feature_view (VARCHAR 100, NULL)
- room_status (ENUM: 'AVAILABLE', 'OCCUPIED', 'CLEANING_REQUIRED', 'MAINTENANCE')
- image_url (VARCHAR 255, NULL)
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- full_name (VARCHAR 100, NOT NULL)
- phone (VARCHAR 20, UNIQUE, NOT NULL)
- email (VARCHAR 100, NULL)
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- guest_id (INT, FK -> guests.id)
- room_id (INT, FK -> rooms.id)
- check_in_date (DATE, NOT NULL)
- check_out_date (DATE, NOT NULL)
- total_amount (DECIMAL 10,2, NOT NULL)
- booking_status (ENUM: 'CONFIRMED', 'CHECKED_IN', 'CHECKED_OUT', 'CANCELLED')
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- item_name (VARCHAR 100, NOT NULL)
- price (DECIMAL 10,2, NOT NULL)
- is_available (BOOLEAN, DEFAULT TRUE)
- image_url (VARCHAR 255, NULL)
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- booking_id (INT, FK -> room_bookings.id)
- room_number (VARCHAR 20, NOT NULL)
- subtotal (DECIMAL 10,2, NOT NULL)
- order_status (ENUM: 'PENDING_KITCHEN', 'PREPARING', 'DELIVERED', 'CANCELLED')
- ordered_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- order_id (INT, FK -> room_food_orders.id)
- menu_item_id (INT, FK -> menu_items.id)
- quantity (INT, NOT NULL, DEFAULT 1)
- unit_price (DECIMAL 10,2, NOT NULL)
- id (INT, PK, Auto Increment)
- room_id (INT, FK -> rooms.id)
- room_number (VARCHAR 20, NOT NULL)
- request_type (VARCHAR 50, DEFAULT 'ROOM_CLEANING')
- assigned_staff_name (VARCHAR 100, DEFAULT 'Unassigned')
- status (ENUM: 'PENDING', 'IN_PROGRESS', 'COMPLETED')
- requested_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- completed_at (TIMESTAMP, NULL)
- id (INT, PK, Auto Increment)
- booking_id (INT, FK -> room_bookings.id)
- charge_type (ENUM: 'ROOM_FEE', 'FOOD_ORDER', 'LAUNDRY_CLEANING', 'OTHER')
- reference_id (INT, NULL)
- amount (DECIMAL 10,2, NOT NULL)
- payment_status (ENUM: 'UNPAID', 'PAID')
- payment_method (ENUM: 'CASH', 'ONLINE_CARD', 'BKASH_NAGAD', 'PENDING')
- transaction_id (VARCHAR 100, NULL)
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
- id (INT, PK, Auto Increment)
- session_id (VARCHAR 100, NOT NULL)
- room_number (VARCHAR 20, NULL)
- user_transcript (TEXT, NOT NULL)
- detected_intent (VARCHAR 50, NOT NULL)
- confidence_score (DECIMAL 5,2, NULL)
- created_at (TIMESTAMP, DEFAULT CURRENT_TIMESTAMP)
π‘ How 3NF Normalization is Maintained in this Resort Architecture
- 1NF (First Normal Form): Eliminates repeating groups and multivalued attributes. Ordered dishes and requested services are recorded in atomic junction tables (
food_order_details,folio_billing) rather than comma-separated lists. - 2NF (Second Normal Form): Ensures full functional dependency on primary keys. Attributes inside
food_order_detailsdepend fully on the composite linkage betweenroom_food_ordersandmenu_items. - 3NF (Third Normal Form): Removes transitive dependencies across tables. Guest profile data lives exclusively in
guests, tier definitions are encapsulated inroom_categories, and price snapshots are stored in transaction line items so future price changes do not distort historical financial records.
π Entity-Relationship (ER) Diagram
Visual representation of Primary/Foreign Keys, cardinalities, and relational constraints across all 10 Smart Resort entities.
4. Key Features & Business Logic
The Smart Resort Voice System processes voice-driven guest interactions, automates room charge posting to master folios, dispatches operational tasks to kitchen and housekeeping pipelines, and settles payments securely while strictly maintaining role-based data isolation across guests, resort staff, and administrative management.
1. AI Voice Intent Parsing & Multi-Service Dispatch
Captures natural spoken audio via in-room micro-terminals or web applications. Converts speech to text and extracts intent strings (BOOK_ROOM, ORDER_FOOD, CLEAN_ROOM). Identified food items match against active menu_items catalog records while every raw transcript, parsed intent, confidence score, and session token is permanently recorded in voice_interaction_logs for quality auditing.
2. Master Folio Aggregation & Price Snapshotting
Calculates food order subtotals and logs itemized quantities into food_order_details. To protect past ledger integrity against future catalog updates, unit prices are frozen at the precise moment of ordering (food_order_details.unit_price). Concurrently, an itemized ledger entry is dispatched to folio_billing mapped directly to the guest's active booking_id.
Folio Charge = Subtotal β Mapped to folio_billing.amount
3. Kitchen Order Pipeline Engine
Pushes voice or menu order creations into room_food_orders and routes line items directly to active chef displays. Enforces strict state transitions across order execution workflows:
4. Housekeeping Task Dispatch & Room State Sync
Voice commands requesting cleaning or maintenance create records in housekeeping_requests with defaults like request_type = 'ROOM_CLEANING'. Task assignment updates staff records, and room statuses in rooms.room_status transition dynamically from CLEANING_REQUIRED to AVAILABLE upon completion timestamp insertion.
π Role-Based Access Control (RBAC) Matrix
Enforces operational domain boundary security and resource-level query isolation across all resort modules.
| Resort Module / Operation | Admin / Manager | Kitchen Staff (Chef) | Housekeeping Team | In-Room Guest |
|---|---|---|---|---|
| Voice Assistant & Room Ordering | System Oversight | β Restricted | β Restricted | β Issue Commands & Place Orders |
| Room Category & Unit Inventory | β Full CRUD Management | β Restricted | View Statuses Only | View Category Availability |
Kitchen Pipeline (room_food_orders) | Full Audit Access | β Update Pipeline Status | β Restricted | View Active Order Status |
Housekeeping Queue (housekeeping_requests) | β Reassign & Override | β Restricted | β Claim & Complete Tasks | Trigger Room Cleanup Request |
Master Folio & Settlement (folio_billing) | β Full Checkout Settlement | β Restricted | β Restricted | View Unsettled Folio Charges |
Audit Logs (voice_interaction_logs) | β Analytics & Quality Inspection | β Restricted | β Restricted | β Restricted |
π REST API Endpoints Architecture
Provides deterministic REST integration endpoints mapped to database tables for voice parsing, reservation tracking, order execution, and ledger management.
| HTTP Method | Endpoint Path | Target Database Table | Access Level | Detailed Business Logic Execution |
|---|---|---|---|---|
| GET | /api/v1/rooms/categories | room_categories | Public / Guest | Queries available room categories, bed configurations, base nightly rates, and showcase images. |
| POST | /api/v1/bookings/create | room_bookings, guests | Public / Guest | Registers new guest record if missing, creates active room reservation with check-in/out dates, and writes initial ROOM_FEE entry to folio_billing. |
| POST | /api/v1/voice/process | voice_interaction_logs | Guest / In-Room Voice | Receives audio STT transcript, identifies voice intent string, logs confidence score in voice_interaction_logs, and routes target service payloads to kitchen or housekeeping endpoints. |
| GET | /api/v1/menu/items | menu_items | Public / Guest | Fetches all available food and beverage catalog items where is_available = TRUE along with unit price details. |
| POST | /api/v1/orders/food | room_food_orders, food_order_details, folio_billing | Guest / In-Room Voice | Creates food order header (order_status = 'PENDING_KITCHEN'), inserts line items into food_order_details with frozen unit prices, and posts a corresponding FOOD_ORDER charge to folio_billing. |
| PATCH | /api/v1/orders/food/:id/status | room_food_orders | Kitchen Staff / Chef | Updates kitchen pipeline status (PENDING_KITCHEN β PREPARING β DELIVERED). |
| POST | /api/v1/housekeeping/request | housekeeping_requests, rooms | Guest / Voice Terminal | Dispatches new cleaning or maintenance task (status = 'PENDING') and shifts destination room status in rooms table to CLEANING_REQUIRED. |
| PATCH | /api/v1/housekeeping/:id/complete | housekeeping_requests, rooms | Housekeeping Staff | Assigns staff name, updates task status to COMPLETED, writes completed_at timestamp, and resets room state back to AVAILABLE. |
| GET | /api/v1/billing/folio/:booking_id | folio_billing | Guest / Admin | Queries total aggregated room fees, dining charges, and service expenses linked to an active booking folio. |
| POST | /api/v1/billing/settle | folio_billing, room_bookings | Admin / Front Desk | Executes payment settlement (payment_status = 'PAID'), records payment method (CASH, ONLINE_CARD, BKASH_NAGAD), logs transaction reference ID, and updates booking status to CHECKED_OUT. |
5. Core SQL Queries & Backend API Logic
Production-ready Next.js App Router API handlers and relational MySQL transactional logic powering voice-driven room reservations, dynamic food ordering with live kitchen state checks, housekeeping dispatches, and real-time master folio bill aggregations.
01. Voice Room Booking & Dynamic Image Payload Engine
Handles multi-turn room booking conversations, returns high-res room preview images when room details are requested, executes atomic reservations, and posts room charges directly to folio_billing.
// app/api/v1/voice/room-booking/route.js
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
export async function POST(req) {
const connection = await pool.getConnection();
try {
const { action, room_type_id, guest_id, check_in, check_out, total_nights } = await req.json();
// 1. GUEST REQUESTS ROOM DETAILS -> RETURN IMAGE & METADATA (NO RESERVATION YET)
if (action === 'QUERY_DETAILS') {
const [categories] = await connection.query(
'SELECT category_name, base_price, description, image_url FROM room_categories WHERE id = ? LIMIT 1',
[room_type_id]
);
if (categories.length === 0) {
return NextResponse.json({
status: 'FAILED',
tts_speech: 'Sorry, I could not find details for the requested room category.'
}, { status: 404 });
}
const room = categories[0];
return NextResponse.json({
status: 'SUCCESS',
show_image: true,
image_url: room.image_url,
category_name: room.category_name,
price_per_night: room.base_price,
tts_speech: `The ${room.category_name} is available for ${room.base_price} per night. ${room.description}. Would you like to confirm the booking?`
});
}
// 2. GUEST CANCELS BOOKING AT CONFIRMATION STEP
if (action === 'CANCEL_BOOKING') {
return NextResponse.json({
status: 'CANCELLED',
show_image: false,
tts_speech: 'Your room reservation process has been cancelled. Let me know if you need anything else!'
});
}
// 3. GUEST CONFIRMS BOOKING -> ATOMIC TRANSACTION & FOLIO BILLING UPDATE
if (action === 'CONFIRM_BOOKING') {
await connection.beginTransaction();
// Check room availability
const [availableRooms] = await connection.query(
'SELECT id FROM rooms WHERE category_id = ? AND room_status = "AVAILABLE" LIMIT 1 FOR UPDATE',
[room_type_id]
);
if (availableRooms.length === 0) {
await connection.rollback();
return NextResponse.json({
status: 'UNAVAILABLE',
show_image: false,
tts_speech: 'We are extremely sorry, but that room category just sold out.'
}, { status: 400 });
}
const assignedRoomId = availableRooms[0].id;
// Get Price per night
const [rateRows] = await connection.query('SELECT base_price FROM room_categories WHERE id = ?', [room_type_id]);
const nightlyRate = Number(rateRows[0].base_price);
const totalRoomCharge = nightlyRate * (total_nights || 1);
// Create Booking Record
const [bookingResult] = await connection.query(
'INSERT INTO room_bookings (guest_id, room_id, check_in_date, check_out_date, booking_status, total_amount) VALUES (?, ?, ?, ?, "CONFIRMED", ?)',
[guest_id, assignedRoomId, check_in, check_out, totalRoomCharge]
);
const newBookingId = bookingResult.insertId;
// Update Room State to OCCUPIED
await connection.query('UPDATE rooms SET room_status = "OCCUPIED" WHERE id = ?', [assignedRoomId]);
// Automatically post charge to Guest's Master Folio
await connection.query(
'INSERT INTO folio_billing (booking_id, charge_type, description, amount) VALUES (?, "ROOM_FEE", ?, ?)',
[newBookingId, `Room Charge (${total_nights} Nights)`, totalRoomCharge]
);
await connection.commit();
return NextResponse.json({
status: 'CONFIRMED',
booking_id: newBookingId,
room_id: assignedRoomId,
total_charged: totalRoomCharge,
show_image: false,
tts_speech: `Congratulations! Your room reservation is confirmed. A total of ${totalRoomCharge} has been added to your resort master folio.`
});
}
return NextResponse.json({ error: 'Invalid action provided' }, { status: 400 });
} catch (error) {
await connection.rollback();
return NextResponse.json({ error: 'Booking Transaction Failed: ' + error.message }, { status: 500 });
} finally {
connection.release();
}
}02. Food Ordering API with Active Cooking State Verification
Checks if the guest already has an active order in COOKING status. If so, alerts the guest about their current order and prompts for additional items. Freezes prices and updates folio billing automatically.
// app/api/v1/voice/food-order/route.js
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
export async function POST(req) {
const connection = await pool.getConnection();
try {
const { booking_id, room_number, raw_transcript, parsed_items } = await req.json();
// 1. CHECK FOR EXISTING ACTIVE ORDERS IN 'PENDING' OR 'COOKING' STATE
const [activeOrders] = await connection.query(
`SELECT o.id, o.order_status, m.item_name, od.quantity
FROM room_food_orders o
JOIN food_order_details od ON o.id = od.order_id
JOIN menu_items m ON od.item_id = m.id
WHERE o.booking_id = ? AND o.order_status IN ('PENDING_KITCHEN', 'PREPARING')`,
[booking_id]
);
let activeOrderNotice = "";
if (activeOrders.length > 0) {
const existingItems = activeOrders.map(i => `${i.quantity} x ${i.item_name}`).join(', ');
const currentStatus = activeOrders[0].order_status === 'PREPARING' ? 'currently cooking in the kitchen' : 'received and waiting in queue';
activeOrderNotice = `Notice: Your previous order of (${existingItems}) is ${currentStatus}. `;
}
// 2. IF NO NEW ITEMS PASSED, JUST RETURN STATUS UPDATE
if (!parsed_items || parsed_items.length === 0) {
return NextResponse.json({
status: 'STATUS_CHECK',
tts_speech: `${activeOrderNotice}Would you like to add any new dishes to your room?`
});
}
// 3. PROCESS NEW FOOD ORDER & UPDATE MASTER FOLIO
await connection.beginTransaction();
let subtotal = 0;
const verifiedItems = [];
for (const item of parsed_items) {
const [menuRows] = await connection.query(
'SELECT id, item_name, price FROM menu_items WHERE item_name LIKE ? AND is_available = TRUE LIMIT 1',
[`%${item.item_name}%`]
);
if (menuRows.length > 0) {
const menuItem = menuRows[0];
const lineTotal = Number(menuItem.price) * (item.qty || 1);
subtotal += lineTotal;
verifiedItems.push({
id: menuItem.id,
name: menuItem.item_name,
qty: item.qty || 1,
unit_price: menuItem.price,
lineTotal
});
}
}
if (verifiedItems.length === 0) {
await connection.rollback();
return NextResponse.json({
status: 'FAILED',
tts_speech: `${activeOrderNotice}Sorry, we couldn't find those requested items on our active menu. Would you like something else?`
}, { status: 404 });
}
// Create Order Header
const [orderResult] = await connection.query(
'INSERT INTO room_food_orders (booking_id, room_number, order_status, total_amount) VALUES (?, ?, "PENDING_KITCHEN", ?)',
[booking_id, room_number, subtotal]
);
const orderId = orderResult.insertId;
// Insert Line Items with Frozen Prices
for (const line of verifiedItems) {
await connection.query(
'INSERT INTO food_order_details (order_id, item_id, quantity, unit_price, total_item_price) VALUES (?, ?, ?, ?, ?)',
[orderId, line.id, line.qty, line.unit_price, line.lineTotal]
);
}
// Post Food Charge to Guest Master Folio
const itemSummary = verifiedItems.map(i => `${i.qty}x ${i.name}`).join(', ');
await connection.query(
'INSERT INTO folio_billing (booking_id, charge_type, description, amount) VALUES (?, "FOOD_ORDER", ?, ?)',
[booking_id, `In-Room Dining: Order #${orderId} (${itemSummary})`, subtotal]
);
// Save Voice Audit Logs
await connection.query(
'INSERT INTO voice_interaction_logs (booking_id, raw_transcript, parsed_intent, confidence_score) VALUES (?, ?, "ORDER_FOOD", 0.98)',
[booking_id, raw_transcript]
);
await connection.commit();
const confirmedText = `${activeOrderNotice} Your new order for ${itemSummary} worth ${subtotal} has been placed and added to your master folio!`;
return NextResponse.json({
status: 'CONFIRMED',
order_id: orderId,
added_amount: subtotal,
tts_speech: confirmedText
});
} catch (error) {
await connection.rollback();
return NextResponse.json({ error: 'Food Transaction Failed: ' + error.message }, { status: 500 });
} finally {
connection.release();
}
}03. Voice Housekeeping Dispatch & Room State Sync
Processes voice commands for room cleaning or maintenance. Creates active staff task requests and transitions the room status to CLEANING_REQUIRED.
// app/api/v1/voice/housekeeping/route.js
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
export async function POST(req) {
const connection = await pool.getConnection();
try {
const { booking_id, room_id, request_type, notes } = await req.json(); // request_type: 'ROOM_CLEANING' | 'TOWELS' | 'REPAIR'
await connection.beginTransaction();
// 1. Create Housekeeping Request Entry
const [requestResult] = await connection.query(
'INSERT INTO housekeeping_requests (booking_id, room_id, request_type, request_status, notes) VALUES (?, ?, ?, "PENDING", ?)',
[booking_id, room_id, request_type || 'ROOM_CLEANING', notes || 'Requested via Voice Terminal']
);
// 2. Update Target Room Operational Status
await connection.query(
'UPDATE rooms SET room_status = "CLEANING_REQUIRED" WHERE id = ?',
[room_id]
);
await connection.commit();
return NextResponse.json({
status: 'DISPATCHED',
request_id: requestResult.insertId,
tts_speech: `Housekeeping request for ${(request_type || 'cleaning').toLowerCase().replace('_', ' ')} has been logged. Our resort attendant will arrive at room shorty!`
});
} catch (error) {
await connection.rollback();
return NextResponse.json({ error: 'Housekeeping Dispatch Failed: ' + error.message }, { status: 500 });
} finally {
connection.release();
}
}04. Master Folio Live Calculation & Summary API
Aggregates all room fees, food orders, and service expenses for an active guest booking to update dashboard bill cards in real-time.
// app/api/v1/billing/folio/[bookingId]/route.js
import { NextResponse } from 'next/server';
import pool from '@/lib/db';
export async function GET(req, { params }) {
try {
const { bookingId } = await params;
// Fetch itemized charges for current guest folio
const [charges] = await pool.query(
`SELECT id, charge_type, description, amount, payment_status, created_at
FROM folio_billing
WHERE booking_id = ?
ORDER BY created_at ASC`,
[bookingId]
);
// Calculate total unpaid folio amount dynamically
const totalAmount = charges.reduce((sum, item) => sum + Number(item.amount), 0);
return NextResponse.json({
booking_id: Number(bookingId),
total_unsettled_amount: Number(totalAmount.toFixed(2)),
itemized_charges: charges
});
} catch (error) {
return NextResponse.json({ error: 'Failed to calculate folio bill' }, { status: 500 });
}
}05. Client-Side Voice Engine & Live Folio Dashboard Component
React/Next.js Client Component combining Speech Recognition (STT), Speech Synthesis (TTS), Dynamic Image Displays, and Live Folio Billing Cards.
// components/SmartResortVoiceTerminal.jsx
'use client';
import { useState, useEffect } from 'react';
export default function SmartResortVoiceTerminal({ bookingId, roomId, roomNumber, guestId }) {
const [isListening, setIsListening] = useState(false);
const [transcript, setTranscript] = useState('');
const [aiSpeechResponse, setAiSpeechResponse] = useState('');
const [roomImage, setRoomImage] = useState(null);
const [folioTotal, setFolioTotal] = useState(0);
const [chargeList, setChargeList] = useState([]);
// Fetch Live Master Folio Card Amount
const refreshFolioCard = async () => {
try {
const res = await fetch(`/api/v1/billing/folio/${bookingId}`);
const data = await res.json();
if (data.total_unsettled_amount !== undefined) {
setFolioTotal(data.total_unsettled_amount);
setChargeList(data.itemized_charges || []);
}
} catch (e) {
console.error('Folio Sync Error', e);
}
};
useEffect(() => {
if (bookingId) refreshFolioCard();
}, [bookingId]);
// Browser Text-to-Speech (TTS) Engine
const speak = (text) => {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = 'en-US';
utterance.rate = 0.95;
window.speechSynthesis.speak(utterance);
}
};
// Process Intent with Backend APIs
const handleVoiceIntent = async (userText) => {
const lower = userText.toLowerCase();
// SCENARIO 1: ROOM DETAILS OR SHOW ROOM IMAGE
if (lower.includes('room details') || lower.includes('show room') || lower.includes('deluxe')) {
const res = await fetch('/api/v1/voice/room-booking', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'QUERY_DETAILS', room_type_id: 1 })
});
const data = await res.json();
setAiSpeechResponse(data.tts_speech);
speak(data.tts_speech);
if (data.show_image) setRoomImage(data.image_url);
return;
}
// SCENARIO 2: CANCEL BOOKING AT CONFIRMATION
if (lower.includes('cancel booking') || lower.includes('dont book')) {
const res = await fetch('/api/v1/voice/room-booking', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'CANCEL_BOOKING' })
});
const data = await res.json();
setRoomImage(null); // Hide image on cancellation
setAiSpeechResponse(data.tts_speech);
speak(data.tts_speech);
return;
}
// SCENARIO 3: HOUSEKEEPING / CLEAN ROOM
if (lower.includes('clean') || lower.includes('housekeeping') || lower.includes('towel')) {
setRoomImage(null);
const res = await fetch('/api/v1/voice/housekeeping', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ booking_id: bookingId, room_id: roomId, request_type: 'ROOM_CLEANING' })
});
const data = await res.json();
setAiSpeechResponse(data.tts_speech);
speak(data.tts_speech);
return;
}
// SCENARIO 4: ORDER FOOD (WITH COOKING STATUS CHECK)
setRoomImage(null);
const res = await fetch('/api/v1/voice/food-order', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
booking_id: bookingId,
room_number: roomNumber,
raw_transcript: userText,
parsed_items: [{ item_name: userText, qty: 1 }]
})
});
const data = await res.json();
setAiSpeechResponse(data.tts_speech);
speak(data.tts_speech);
refreshFolioCard(); // Instantly update master folio bill amount
};
// Browser Speech-to-Text (STT) Engine
const startListening = () => {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
if (!SpeechRecognition) {
alert('Browser does not support Speech Recognition.');
return;
}
const recognition = new SpeechRecognition();
recognition.lang = 'en-US';
recognition.onstart = () => setIsListening(true);
recognition.onend = () => setIsListening(false);
recognition.onresult = (event) => {
const voiceInput = event.results[0][0].transcript;
setTranscript(voiceInput);
handleVoiceIntent(voiceInput);
};
recognition.start();
};
return (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 bg-slate-50 p-6 rounded-2xl border border-slate-200">
{/* VOICE TERMINAL INTERACTION CARD */}
<div className="md:col-span-2 bg-white p-5 rounded-xl border border-slate-200 space-y-4 shadow-sm">
<div className="flex items-center justify-between border-b pb-3">
<h4 className="font-bold text-slate-800">ποΈ Smart Voice Assistant (Room #{roomNumber})</h4>
<span className="text-xs bg-blue-100 text-blue-700 font-bold px-2.5 py-1 rounded-full">System Ready</span>
</div>
<button
onClick={startListening}
className={`w-full py-3 rounded-xl font-bold transition-all shadow-md ${
isListening ? 'bg-rose-500 text-white animate-pulse' : 'bg-blue-600 hover:bg-blue-700 text-white'
}`}
>
{isListening ? 'Listening to your voice...' : 'Push to Speak'}
</button>
<div className="space-y-2 text-xs">
<p className="p-2.5 bg-slate-100 rounded-lg"><strong>Guest Voice Input:</strong> {transcript || 'Waiting for speech...'}</p>
<p className="p-2.5 bg-blue-50 text-blue-900 rounded-lg"><strong>AI Assistant Response:</strong> {aiSpeechResponse || 'Hello! How can I assist you today?'}</p>
</div>
{/* DYNAMIC ROOM IMAGE PREVIEW (SHOWN ONLY WHEN DETAILS ARE REQUESTED) */}
{roomImage && (
<div className="mt-4 border rounded-xl overflow-hidden bg-slate-900 space-y-2 p-2">
<img src={roomImage} alt="Room Category Preview" className="w-full h-48 object-cover rounded-lg" />
<p className="text-[11px] text-slate-300 text-center font-sans">Showing preview image based on voice query.</p>
</div>
)}
</div>
{/* LIVE MASTER FOLIO BILL CARD */}
<div className="bg-slate-900 text-white p-5 rounded-xl space-y-4 shadow-md flex flex-col justify-between">
<div className="space-y-3">
<span className="text-xs font-mono text-blue-400 font-bold tracking-wider uppercase">Master Folio Ledger</span>
<div className="border-b border-slate-800 pb-3">
<h3 className="text-2xl font-extrabold text-white">{folioTotal.toFixed(2)} <span className="text-sm font-normal text-slate-400">BDT</span></h3>
<p className="text-[11px] text-emerald-400">β Live Auto-Updated Balance</p>
</div>
<div className="space-y-2 max-h-40 overflow-y-auto text-[11px] font-mono text-slate-300">
{chargeList.map((item) => (
<div key={item.id} className="flex justify-between border-b border-slate-800/80 pb-1">
<span className="truncate max-w-35">{item.description}</span>
<span className="font-bold text-white">+{item.amount}</span>
</div>
))}
</div>
</div>
<button onClick={refreshFolioCard} className="w-full bg-slate-800 hover:bg-slate-700 text-xs py-2 rounded-lg text-slate-300 transition">
π Sync Folio Charges
</button>
</div>
</div>
);
}Thank You π
