| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Restaurant Table Booking</title> |
| <style> |
| body { |
| font-family: Arial, sans-serif; |
| margin: 0; |
| padding: 0; |
| background: linear-gradient(135deg, #6e8efb, #a777e3); |
| color: #fff; |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| min-height: 100vh; |
| } |
| .container { |
| text-align: center; |
| padding: 20px; |
| } |
| h1 { |
| font-size: 2.5em; |
| margin-bottom: 20px; |
| text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); |
| } |
| .tables { |
| display: flex; |
| flex-wrap: wrap; |
| justify-content: center; |
| gap: 20px; |
| } |
| .table { |
| background: rgba(255, 255, 255, 0.1); |
| border-radius: 10px; |
| padding: 20px; |
| width: 120px; |
| text-align: center; |
| cursor: pointer; |
| transition: transform 0.3s, background 0.3s; |
| box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); |
| } |
| .table:hover { |
| transform: scale(1.1); |
| background: rgba(255, 255, 255, 0.2); |
| } |
| .table h3 { |
| margin: 0; |
| font-size: 1.2em; |
| } |
| .table p { |
| margin: 5px 0 0; |
| font-size: 0.9em; |
| color: #ddd; |
| } |
| .availability-text { |
| font-size: 0.8em; |
| color: #ffd700; |
| margin-top: 10px; |
| } |
| |
| .modal { |
| display: none; |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background: rgba(0, 0, 0, 0.7); |
| justify-content: center; |
| align-items: center; |
| z-index: 1000; |
| } |
| .modal-content { |
| background: #fff; |
| color: #333; |
| border-radius: 10px; |
| padding: 20px; |
| width: 90%; |
| max-width: 500px; |
| max-height: 80vh; |
| overflow-y: auto; |
| position: relative; |
| } |
| .modal-content h2 { |
| margin-top: 0; |
| color: #6e8efb; |
| } |
| .close { |
| position: absolute; |
| top: 10px; |
| right: 15px; |
| font-size: 1.5em; |
| cursor: pointer; |
| color: #333; |
| } |
| .menu-item { |
| margin: 10px 0; |
| } |
| .menu-item h4 { |
| margin: 5px 0; |
| color: #a777e3; |
| } |
| select, button { |
| padding: 10px; |
| margin: 10px 0; |
| border: none; |
| border-radius: 5px; |
| font-size: 1em; |
| } |
| select { |
| width: 100%; |
| background: #f0f0f0; |
| } |
| button { |
| background: #6e8efb; |
| color: #fff; |
| cursor: pointer; |
| transition: background 0.3s; |
| } |
| button:hover { |
| background: #a777e3; |
| } |
| .availability { |
| margin: 10px 0; |
| font-size: 0.9em; |
| color: #555; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="container"> |
| <h1>Restaurant Table Booking</h1> |
| <div class="tables" id="tables"> |
| |
| </div> |
| </div> |
|
|
| |
| <div id="modal" class="modal"> |
| <div class="modal-content"> |
| <span class="close" onclick="closeModal()">×</span> |
| <h2 id="modal-title">Table Menu</h2> |
| <div id="availability" class="availability"></div> |
| <h3>Menu</h3> |
| <div id="menu"> |
| <div class="menu-item"> |
| <h4>Margherita Pizza</h4> |
| <p>Classic pizza with tomato, mozzarella, and basil. $12</p> |
| </div> |
| <div class="menu-item"> |
| <h4>Caesar Salad</h4> |
| <p>Fresh romaine, croutons, and Caesar dressing. $8</p> |
| </div> |
| <div class="menu-item"> |
| <h4>Spaghetti Carbonara</h4> |
| <p>Pasta with creamy sauce, pancetta, and parmesan. $15</p> |
| </div> |
| </div> |
| <h3>Book Table</h3> |
| <select id="time-slot"> |
| |
| </select> |
| <button onclick="bookTable()">Book Now</button> |
| </div> |
| </div> |
|
|
| <script> |
| const tables = [ |
| { id: 1, name: "Table 1" }, |
| { id: 2, name: "Table 2" }, |
| { id: 3, name: "Table 3" }, |
| { id: 4, name: "Table 4" }, |
| { id: 5, name: "Table 5" } |
| ]; |
| |
| |
| const tablesContainer = document.getElementById("tables"); |
| tables.forEach(table => { |
| const tableDiv = document.createElement("div"); |
| tableDiv.className = "table"; |
| tableDiv.id = `table-${table.id}`; |
| tableDiv.innerHTML = ` |
| <h3>${table.name}</h3> |
| <p>Click to view menu</p> |
| <p class="availability-text" id="availability-${table.id}">Loading...</p> |
| `; |
| tableDiv.onclick = () => openModal(table.id, table.name); |
| tablesContainer.appendChild(tableDiv); |
| }); |
| |
| let currentTableId = null; |
| |
| |
| async function updateLiveAvailability() { |
| for (const table of tables) { |
| try { |
| const response = await fetch(`/api/availability/${table.id}`); |
| const data = await response.json(); |
| const availabilityText = document.getElementById(`availability-${table.id}`); |
| if (data.next_slot) { |
| availabilityText.textContent = `Next: ${data.next_slot}`; |
| availabilityText.style.color = "#ffd700"; |
| } else { |
| availabilityText.textContent = "Booked"; |
| availabilityText.style.color = "#ff4d4d"; |
| } |
| } catch (error) { |
| console.error(`Error fetching availability for table ${table.id}:`, error); |
| document.getElementById(`availability-${table.id}`).textContent = "Error"; |
| } |
| } |
| } |
| |
| |
| updateLiveAvailability(); |
| setInterval(updateLiveAvailability, 30000); |
| |
| |
| async function openModal(tableId, tableName) { |
| currentTableId = tableId; |
| document.getElementById("modal-title").textContent = `${tableName} Menu`; |
| document.getElementById("modal").style.display = "flex"; |
| |
| |
| try { |
| const response = await fetch(`/api/availability/${tableId}`); |
| const data = await response.json(); |
| const availableSlots = data.available_slots; |
| |
| |
| const availabilityDiv = document.getElementById("availability"); |
| availabilityDiv.innerHTML = availableSlots.length > 0 |
| ? `Available slots: ${availableSlots.join(", ")}` |
| : "No available slots"; |
| |
| |
| const timeSlotSelect = document.getElementById("time-slot"); |
| timeSlotSelect.innerHTML = ""; |
| if (availableSlots.length > 0) { |
| availableSlots.forEach(slot => { |
| const option = document.createElement("option"); |
| option.value = slot; |
| option.textContent = slot; |
| timeSlotSelect.appendChild(option); |
| }); |
| } else { |
| const option = document.createElement("option"); |
| option.value = ""; |
| option.textContent = "No slots available"; |
| timeSlotSelect.appendChild(option); |
| } |
| } catch (error) { |
| console.error("Error fetching availability:", error); |
| document.getElementById("availability").innerHTML = "Error loading availability"; |
| } |
| } |
| |
| |
| function closeModal() { |
| document.getElementById("modal").style.display = "none"; |
| currentTableId = null; |
| } |
| |
| |
| async function bookTable() { |
| if (!currentTableId) return; |
| |
| const timeSlot = document.getElementById("time-slot").value; |
| if (!timeSlot) { |
| alert("Please select a time slot"); |
| return; |
| } |
| |
| try { |
| const response = await fetch("/api/book", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| body: JSON.stringify({ table_id: currentTableId, time_slot: timeSlot }) |
| }); |
| const result = await response.json(); |
| if (result.success) { |
| alert(`Table ${currentTableId} booked for ${timeSlot}`); |
| closeModal(); |
| updateLiveAvailability(); |
| } else { |
| alert("Booking failed: " + result.message); |
| } |
| } catch (error) { |
| console.error("Error booking table:", error); |
| alert("Error booking table"); |
| } |
| } |
| |
| |
| window.onclick = function(event) { |
| if (event.target === document.getElementById("modal")) { |
| closeModal(); |
| } |
| }; |
| </script> |
| </body> |
| </html> |