|
<!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> |