Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Dining Assistant</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| background-color: #f4f4f9; | |
| text-align: center; | |
| margin: 0; | |
| padding: 0; | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| height: 100vh; | |
| } | |
| .container { | |
| text-align: center; | |
| background-color: #fff; | |
| padding: 40px; | |
| border-radius: 10px; | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | |
| width: 80%; | |
| max-width: 600px; | |
| } | |
| h1 { | |
| color: #333; | |
| } | |
| .mic-button { | |
| width: 80px; | |
| height: 80px; | |
| border-radius: 50%; | |
| background-color: #007bff; | |
| color: white; | |
| font-size: 40px; | |
| border: none; | |
| cursor: pointer; | |
| } | |
| .status { | |
| margin-top: 20px; | |
| font-size: 1.2rem; | |
| color: #555; | |
| } | |
| .menu-items-container { | |
| margin-top: 30px; | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| } | |
| .menu-item { | |
| background-color: #f9f9f9; | |
| padding: 20px; | |
| border-radius: 10px; | |
| box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1); | |
| text-align: left; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>AI Dining Assistant</h1> | |
| <button class="mic-button" id="mic-button">🎤</button> | |
| <div class="status" id="status">Press the mic button to start...</div> | |
| <div class="menu-items-container" id="menu-items"> | |
| <!-- Menu items will be populated here dynamically --> | |
| </div> | |
| </div> | |
| <script> | |
| const micButton = document.getElementById('mic-button'); | |
| const status = document.getElementById('status'); | |
| const menuItemsContainer = document.getElementById('menu-items'); | |
| let isListening = false; | |
| let selectedItem = ''; | |
| let quantity = 0; | |
| micButton.addEventListener('click', () => { | |
| if (!isListening) { | |
| isListening = true; | |
| greetUser(); | |
| } | |
| }); | |
| // Greet user | |
| function greetUser() { | |
| const utterance = new SpeechSynthesisUtterance("Hi, welcome to Biryani Hub! Can I know your name so I can greet you personally?"); | |
| speechSynthesis.speak(utterance); | |
| utterance.onend = () => { | |
| status.textContent = "Listening for your name..."; | |
| startListeningForName(); | |
| }; | |
| } | |
| // Start listening for user input (name) | |
| async function startListeningForName() { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| const mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm;codecs=opus" }); | |
| const audioChunks = []; | |
| mediaRecorder.ondataavailable = (event) => audioChunks.push(event.data); | |
| mediaRecorder.onstop = async () => { | |
| const audioBlob = new Blob(audioChunks, { type: "audio/webm" }); | |
| const formData = new FormData(); | |
| formData.append("audio", audioBlob); | |
| status.textContent = "Processing your name... Please wait."; | |
| try { | |
| const result = await fetch("/process-audio", { method: "POST", body: formData }); | |
| const data = await result.json(); | |
| selectedItem = data.name; | |
| status.textContent = `Hello ${selectedItem}! How can I help you today?`; | |
| suggestFood(); | |
| } catch (error) { | |
| status.textContent = "Sorry, I couldn’t understand that. Could you please repeat?"; | |
| isListening = false; | |
| } | |
| }; | |
| mediaRecorder.start(); | |
| setTimeout(() => mediaRecorder.stop(), 5000); // Stop recording after 5 seconds | |
| } | |
| // Suggest food items to the user | |
| function suggestFood() { | |
| fetch('/menu') // GET request to fetch menu items from the server | |
| .then(response => response.json()) | |
| .then(data => { | |
| menuItemsContainer.innerHTML = ""; | |
| data.forEach(item => { | |
| const menuItemDiv = document.createElement('div'); | |
| menuItemDiv.classList.add('menu-item'); | |
| menuItemDiv.innerHTML = ` | |
| <h3>${item.Name}</h3> | |
| <p><strong>Price:</strong> $${item.Price__c}</p> | |
| <p><strong>Ingredients:</strong> ${item.Ingredients__c}</p> | |
| <p><strong>Category:</strong> ${item.Category__c}</p> | |
| <button class="order-btn" data-item="${item.Name}" onclick="takeOrder('${item.Name}')">Order</button> | |
| `; | |
| menuItemsContainer.appendChild(menuItemDiv); | |
| }); | |
| speakMenuItems(data); | |
| }); | |
| } | |
| // Read out the menu items | |
| function speakMenuItems(items) { | |
| const utterance = new SpeechSynthesisUtterance("Here are the menu items:"); | |
| speechSynthesis.speak(utterance); | |
| items.forEach(item => { | |
| const itemUtterance = new SpeechSynthesisUtterance(`${item.Name}, Price: $${item.Price__c}, Ingredients: ${item.Ingredients__c}`); | |
| speechSynthesis.speak(itemUtterance); | |
| }); | |
| } | |
| // Handle user order | |
| function takeOrder(item) { | |
| selectedItem = item; | |
| const utterance = new SpeechSynthesisUtterance(`You selected ${item}. How many would you like?`); | |
| speechSynthesis.speak(utterance); | |
| status.textContent = `You selected ${item}. How many would you like?`; | |
| startListeningForQuantity(); | |
| } | |
| // Start listening for quantity | |
| async function startListeningForQuantity() { | |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); | |
| const mediaRecorder = new MediaRecorder(stream, { mimeType: "audio/webm;codecs=opus" }); | |
| const audioChunks = []; | |
| mediaRecorder.ondataavailable = (event) => audioChunks.push(event.data); | |
| mediaRecorder.onstop = async () => { | |
| const audioBlob = new Blob(audioChunks, { type: "audio/webm" }); | |
| const formData = new FormData(); | |
| formData.append("audio", audioBlob); | |
| status.textContent = "Processing your quantity... Please wait."; | |
| try { | |
| const result = await fetch("/process-audio", { method: "POST", body: formData }); | |
| const data = await result.json(); | |
| quantity = parseInt(data.quantity); | |
| status.textContent = `You want ${quantity} of ${selectedItem}. Please confirm your order.`; | |
| confirmOrder(); | |
| } catch (error) { | |
| status.textContent = "Sorry, I couldn’t understand that. Could you please repeat?"; | |
| isListening = false; | |
| } | |
| }; | |
| mediaRecorder.start(); | |
| setTimeout(() => mediaRecorder.stop(), 5000); // Stop recording after 5 seconds | |
| } | |
| // Confirm the order and send it to the backend | |
| function confirmOrder() { | |
| const orderDetails = { | |
| item: selectedItem, | |
| quantity: quantity | |
| }; | |
| fetch('/order', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify(orderDetails) | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| status.textContent = `Order placed for ${quantity} of ${selectedItem}.`; | |
| }) | |
| .catch(error => { | |
| status.textContent = "Error placing order."; | |
| }); | |
| } | |
| </script> | |
| </body> | |
| </html> | |