Spaces:
Sleeping
Sleeping
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Search Menu</title> | |
<!-- Bootstrap CSS --> | |
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> | |
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet"> | |
<!-- Preload Placeholder Image --> | |
<link rel="preload" href="/static/placeholder.jpg" as="image"> | |
<style> | |
body { | |
font-family: Arial, sans-serif; | |
background-color: #fdf4e3; | |
margin: 0; | |
padding: 0; | |
display: flex; | |
flex-direction: column; | |
padding-bottom: 70px; | |
} | |
.container { | |
max-width: 900px; | |
} | |
.menu-heading { | |
font-size: 2rem; | |
font-weight: 700; | |
color: #fff; | |
text-align: center; | |
padding: 15px 20px; | |
margin: 20px 0; | |
background: linear-gradient(45deg, #FFA07A, #FFB347); | |
border-radius: 10px; | |
box-shadow: 0 4px 8px rgba(0,0,0,0.2); | |
width: 100%; | |
max-width: 900px; | |
margin-left: auto; | |
margin-right: auto; | |
} | |
.menu-card { | |
max-width: 350px; | |
border-radius: 15px; | |
overflow: hidden; | |
background-color: #fff; | |
margin: auto; | |
display: flex; | |
flex-direction: column; | |
box-shadow: 0 4px 8px rgba(0,0,0,0.1); | |
cursor: pointer; | |
} | |
.card-img-container { | |
position: relative; | |
width: 100%; | |
height: 200px; | |
} | |
.card-img { | |
height: 100%; | |
width: 100%; | |
object-fit: cover; | |
border-radius: 15px 15px 0 0; | |
display: block; | |
} | |
.card-title { | |
position: absolute; | |
top: 10px; | |
left: 50%; | |
transform: translateX(-50%); | |
font-size: 1.2rem; | |
font-weight: 600; | |
color: #fff; | |
text-align: center; | |
margin: 0; | |
padding: 5px 10px; | |
background-color: rgba(0, 0, 0, 0.5); | |
border-radius: 5px; | |
width: 90%; | |
white-space: nowrap; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
z-index: 1; | |
} | |
.menu-card .card-body { | |
padding: 10px; | |
text-align: center; | |
} | |
.menu-card .card-body .card-text.section { | |
font-size: 0.9rem; | |
color: #6c757d; | |
text-align: center; | |
margin-bottom: 10px; | |
} | |
.fixed-top-bar { | |
position: relative; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 54px; | |
background: linear-gradient(45deg, #FFA07A, #FFB347); | |
color: white; | |
padding: 15px; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
z-index: 1000; | |
} | |
.back-arrow-container { | |
position: absolute; | |
left: 15px; | |
top: 50%; | |
transform: translateY(-50%); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
z-index: 1100; /* Ensure back arrow is above other elements */ | |
} | |
.back-arrow { | |
width: 48px; | |
height: 48px; | |
border-radius: 50%; | |
background: linear-gradient(135deg, #FF6F61, #FFD700); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
color: #fff; | |
font-size: 28px; | |
cursor: pointer; | |
text-decoration: none; | |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); | |
transition: transform 0.3s ease, background 0.3s ease, box-shadow 0.3s ease; | |
border: 2px solid #fff; | |
} | |
.back-arrow:hover { | |
background: linear-gradient(135deg, #FF4500, #FFA500); | |
transform: translateY(-50%) rotate(-15deg) scale(1.15); | |
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); | |
} | |
.back-arrow:active { | |
transform: translateY(-50%) scale(0.95); | |
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); | |
} | |
.search-bar-container { | |
position: absolute; | |
left: 80px; /* Adjusted for larger back arrow */ | |
top: 50%; | |
transform: translateY(-50%); | |
display: flex; | |
align-items: center; | |
width: 300px; | |
max-width: calc(90% - 80px); /* Adjusted for larger back arrow */ | |
z-index: 1000; /* Ensure search bar is below back arrow */ | |
} | |
.search-bar-container input { | |
width: 100%; | |
padding: 8px 40px 8px 40px; | |
font-size: 16px; | |
border-radius: 25px; | |
border: none; | |
background-color: #fff; | |
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); | |
outline: none; | |
transition: border-bottom 0.3s ease; | |
} | |
.search-bar-container input:focus { | |
border-bottom: 2px solid #FFA07A; | |
} | |
.search-bar-container input::placeholder { | |
color: #888; | |
} | |
.search-icon { | |
position: absolute; | |
left: 15px; | |
font-size: 18px; | |
color: #888; | |
} | |
.mic-icon { | |
position: absolute; | |
right: 15px; | |
font-size: 18px; | |
color: #888; | |
cursor: pointer; | |
transition: color 0.3s ease; | |
} | |
.mic-icon.active { | |
color: #007bff; | |
} | |
.search-popup { | |
position: absolute; | |
top: 100%; | |
left: 0; | |
right: 0; | |
background-color: #fff; | |
border-radius: 5px; | |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | |
max-height: 300px; | |
overflow-y: auto; | |
z-index: 1000; | |
display: none; | |
margin-top: 5px; | |
} | |
.search-popup-item { | |
display: flex; | |
align-items: center; | |
padding: 10px; | |
border-bottom: 1px solid #eee; | |
cursor: pointer; | |
transition: background-color 0.2s ease; | |
} | |
.search-popup-item:hover { | |
background-color: #f8f9fa; | |
} | |
.search-popup-item img { | |
width: 50px; | |
height: 50px; | |
object-fit: cover; | |
border-radius: 5px; | |
margin-right: 10px; | |
} | |
.search-popup-item span { | |
font-size: 14px; | |
color: #333; | |
} | |
.bottom-action-bar { | |
position: fixed; | |
bottom: 0; | |
left: 0; | |
right: 0; | |
background-color: white; | |
padding: 10px 20px; | |
display: flex; | |
justify-content: space-between; | |
align-items: center; | |
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1); | |
z-index: 1000; | |
max-width: 900px; | |
margin: 0 auto; | |
} | |
.bottom-action-bar .btn { | |
flex: 1; | |
margin: 0 5px; | |
padding: 10px 15px; | |
border-radius: 8px; | |
font-weight: bold; | |
font-size: 16px; | |
color: white; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
text-align: center; | |
min-width: 0; | |
white-space: nowrap; | |
} | |
.bottom-action-bar .btn-order-history { | |
background-color: #FFA07A; | |
border-color: #FFA07A; | |
} | |
.bottom-action-bar .btn-order-history:hover { | |
background-color: #FF8C61; | |
border-color: #FF8C61; | |
} | |
.bottom-action-bar .btn-view-cart { | |
background-color: #0FAA39; | |
border-color: #0FAA39; | |
} | |
.bottom-action-bar .btn-view-cart:hover { | |
background-color: #0D9232; | |
border-color: #0D9232; | |
} | |
.cart-icon-badge { | |
background-color: white; | |
color: #0FAA39; | |
border-radius: 50%; | |
width: 20px; | |
height: 20px; | |
display: inline-flex; | |
align-items: center; | |
justify-content: center; | |
font-size: 12px; | |
margin-left: 8px; | |
} | |
.no-results { | |
text-align: center; | |
font-size: 1.2rem; | |
color: #6c757d; | |
margin-top: 20px; | |
} | |
@media (max-width: 576px) { | |
.fixed-top-bar { | |
height: 60px; | |
padding: 10px; | |
} | |
.back-arrow-container { | |
left: 10px; | |
} | |
.back-arrow { | |
width: 40px; | |
height: 40px; | |
font-size: 24px; | |
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2); | |
} | |
.search-bar-container { | |
width: calc(80% - 60px); | |
max-width: calc(100% - 60px); | |
left: 60px; /* Adjusted for smaller back arrow on mobile */ | |
} | |
.search-bar-container input { | |
padding: 6px 35px 6px 35px; | |
font-size: 14px; | |
border-radius: 20px; | |
} | |
.search-icon { | |
left: 12px; | |
font-size: 16px; | |
} | |
.mic-icon { | |
right: 12px; | |
font-size: 16px; | |
} | |
.menu-heading { | |
font-size: 1.5rem; | |
padding: 10px 15px; | |
margin: 15px 0; | |
} | |
.menu-card { | |
max-width: 100%; | |
} | |
.card-img-container { | |
height: 150px; | |
} | |
.card-title { | |
font-size: 1rem; | |
top: 8px; | |
padding: 4px 8px; | |
} | |
.menu-card .card-body .card-text.section { | |
font-size: 0.8rem; | |
} | |
.bottom-action-bar { | |
padding: 8px 10px; | |
} | |
.bottom-action-bar .btn { | |
padding: 8px 10px; | |
font-size: 14px; | |
} | |
.cart-icon-badge { | |
width: 18px; | |
height: 18px; | |
font-size: 10px; | |
margin-left: 5px; | |
} | |
.search-popup { | |
width: 100%; | |
} | |
.search-popup-item img { | |
width: 40px; | |
height: 40px; | |
} | |
.search-popup-item span { | |
font-size: 12px; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="fixed-top-bar"> | |
<div class="back-arrow-container"> | |
<a href="{{ url_for('menu.menu') }}" class="back-arrow" aria-label="Back to Menu"> | |
<i class="bi bi-arrow-left"></i> | |
</a> | |
</div> | |
<div class="search-bar-container"> | |
<input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off"> | |
<i class="bi bi-search search-icon"></i> | |
<i class="bi bi-mic mic-icon" id="micIcon"></i> | |
<div class="search-popup" id="searchPopup"></div> | |
</div> | |
</div> | |
<!-- Timer and Success Message Container --> | |
<div id="orderTimerContainer" style="display: none; text-align: center; margin-top: 20px;"> | |
<div id="orderTimer" style="font-size: 1.5rem; font-weight: bold; color: #FFA07A;"></div> | |
<div id="orderSuccessMessage" style="font-size: 1.5rem; font-weight: bold; color: #0FAA39; display: none;">Order Successfully Placed!</div> | |
</div> | |
<div class="container mt-4"> | |
<h1 class="menu-heading">Search Menu Items</h1> | |
<div class="row" id="menuItems"> | |
{% for section, items in ordered_menu.items() %} | |
{% for item in items %} | |
<div class="col-md-6 mb-4 menu-item" data-name="{{ item.Name | default('Unnamed Item') }}" data-section="{{ item.Section__c | default(section) }}"> | |
<div class="card menu-card" onclick близко="selectItem('{{ item.Name | default('Unnamed Item') }}', '{{ item.Section__c | default(section) }}')"> | |
<div class="card-img-container"> | |
<img src="{{ item.Image1__c | default('/static/placeholder.jpg') }}" alt="{{ item.Name | default('Unnamed Item') }}" class="card-img" loading="eager" decoding="async" width="350" height="200"> | |
<h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5> | |
</div> | |
<div class="card-body"> | |
<p class="card-text section">{{ item.Section__c | default(section) }}</p> | |
</div> | |
</div> | |
</div> | |
{% endfor %} | |
{% endfor %} | |
</div> | |
<div class="no-results" id="noResults" style="display: none;"> | |
No items found matching your search. | |
</div> | |
</div> | |
<div class="bottom-action-bar"> | |
<a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history"> | |
<i class="bi bi-clock-history"></i> Order History | |
</a> | |
<a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart"> | |
<i class="bi bi-cart"></i> View Cart | |
<span id="cart-item-count" class="cart-icon-badge" style="display: none;">0</span> | |
</a> | |
</div> | |
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script> | |
<script> | |
const menuItems = [ | |
{% for section, items in ordered_menu.items() %} | |
{% for item in items %} | |
{ | |
name: "{{ item.Name | default('Unnamed Item') }}", | |
section: "{{ item.Section__c | default(section) }}", | |
image: "{{ item.Image1__c | default('/static/placeholder.jpg') }}" | |
}, | |
{% endfor %} | |
{% endfor %} | |
]; | |
function updateCartUI(cart) { | |
if (!Array.isArray(cart)) { | |
console.error('Invalid cart data:', cart); | |
return; | |
} | |
let totalQuantity = 0; | |
cart.forEach(item => { | |
totalQuantity += item.quantity; | |
}); | |
const cartItemCount = document.getElementById('cart-item-count'); | |
if (cartItemCount) { | |
cartItemCount.innerText = totalQuantity; | |
cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none'; | |
} | |
} | |
function getCartLocalStorage() { | |
return JSON.parse(localStorage.getItem('cart')) || []; | |
} | |
function selectItem(itemName, section) { | |
localStorage.setItem('selectedItem', JSON.stringify({ name: itemName, section: section })); | |
window.location.href = '/menu'; | |
} | |
function filterMenuItems(query) { | |
const menuItemElements = document.querySelectorAll('.menu-item'); | |
const noResults = document.getElementById('noResults'); | |
let hasResults = false; | |
menuItemElements.forEach(item => { | |
const name = item.getAttribute('data-name').toLowerCase(); | |
const section = item.getAttribute('data-section').toLowerCase(); | |
const matches = name.includes(query.toLowerCase()) || section.includes(query.toLowerCase()); | |
item.style.display = matches ? '' : 'none'; | |
if (matches) hasResults = true; | |
}); | |
noResults.style.display = hasResults ? 'none' : 'block'; | |
} | |
function showSearchPopup(query) { | |
const searchPopup = document.getElementById('searchPopup'); | |
searchPopup.innerHTML = ''; | |
if (!query) { | |
searchPopup.style.display = 'none'; | |
return; | |
} | |
const filteredItems = menuItems.filter(item => | |
item.name.toLowerCase().includes(query.toLowerCase()) || | |
item.section.toLowerCase().includes(query.toLowerCase()) | |
); | |
if (filteredItems.length === 0) { | |
searchPopup.style.display = 'none'; | |
return; | |
} | |
filteredItems.forEach(item => { | |
const popupItem = document.createElement('div'); | |
popupItem.className = 'search-popup-item'; | |
popupItem.innerHTML = ` | |
<img src="${item.image}" alt="${item.name}"> | |
<span>${item.name}</span> | |
`; | |
popupItem.addEventListener('click', () => { | |
selectItem(item.name, item.section); | |
}); | |
searchPopup.appendChild(popupItem); | |
}); | |
searchPopup.style.display = 'block'; | |
} | |
// Timer and Success Message Functions | |
let orderTimerInterval; | |
let orderDurationInSeconds = 0; | |
function startOrderTimer(durationInSeconds) { | |
orderDurationInSeconds = durationInSeconds; | |
const timerElement = document.getElementById('orderTimer'); | |
const successMessageElement = document.getElementById('orderSuccessMessage'); | |
const timerContainer = document.getElementById('orderTimerContainer'); | |
if (!timerElement || !successMessageElement || !timerContainer) { | |
console.error("Timer elements not found."); | |
return; | |
} | |
timerContainer.style.display = 'block'; | |
successMessageElement.style.display = 'none'; | |
let secondsRemaining = orderDurationInSeconds; | |
timerElement.innerText = formatTime(secondsRemaining); | |
timerElement.style.display = 'block'; | |
orderTimerInterval = setInterval(() => { | |
secondsRemaining--; | |
if (secondsRemaining <= 0) { | |
clearInterval(orderTimerInterval); | |
timerElement.style.display = 'none'; | |
successMessageElement.style.display = 'block'; | |
setTimeout(() => { | |
timerContainer.style.display = 'none'; | |
successMessageElement.style.display = 'none'; | |
timerElement.style.display = 'block'; | |
}, 5000); | |
} else { | |
timerElement.innerText = formatTime(secondsRemaining); | |
} | |
}, 1000); | |
} | |
function formatTime(seconds) { | |
const minutes = Math.floor(seconds / 60); | |
const remainingSeconds = seconds % 60; | |
const formattedMinutes = String(minutes).padStart(2, '0'); | |
const formattedSeconds = String(remainingSeconds).padStart(2, '0'); | |
return `${formattedMinutes}:${formattedSeconds}`; | |
} | |
function handleNewOrderSuccess(estimatedCompletionTimeInSeconds) { | |
console.log("New order successful, starting timer..."); | |
startOrderTimer(estimatedCompletionTimeInSeconds); | |
} | |
document.addEventListener('DOMContentLoaded', function () { | |
// Prevent back arrow click from affecting search bar | |
const backArrow = document.querySelector('.back-arrow'); | |
backArrow.addEventListener('click', function (event) { | |
event.stopPropagation(); // Prevent click from bubbling to search bar | |
}); | |
// Search Bar Functionality | |
const searchBar = document.getElementById('searchBar'); | |
const searchPopup = document.getElementById('searchPopup'); | |
const searchQuery = localStorage.getItem('searchQuery'); | |
if (searchQuery) { | |
searchBar.value = searchQuery; | |
filterMenuItems(searchQuery); | |
showSearchPopup(searchQuery); | |
localStorage.removeItem('searchQuery'); | |
} | |
searchBar.addEventListener('input', function () { | |
filterMenuItems(this.value); | |
showSearchPopup(this.value); | |
}); | |
document.addEventListener('click', function (event) { | |
if (!searchBar.contains(event.target) && !searchPopup.contains(event.target)) { | |
searchPopup.style.display = 'none'; | |
} | |
}); | |
// Voice Recognition | |
const micIcon = document.getElementById('micIcon'); | |
if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) { | |
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
const recognition = new SpeechRecognition(); | |
recognition.lang = 'en-US'; | |
recognition.onstart = () => micIcon.classList.add('active'); | |
recognition.onresult = (event) => { | |
const query = event.results[0][0].transcript.trim(); | |
searchBar.value = query; | |
filterMenuItems(query); | |
showSearchPopup(query); | |
}; | |
recognition.onend = () => micIcon.classList.remove('active'); | |
recognition.onerror = (event) => { | |
micIcon.classList.remove('active'); | |
console.error('Speech error:', event.error); | |
}; | |
micIcon.addEventListener('click', () => { | |
recognition.start(); | |
}); | |
} else { | |
micIcon.style.display = 'none'; | |
} | |
// Fetch Cart | |
fetch('/cart/get') | |
.then(response => { | |
if (!response.ok) { | |
throw new Error(`HTTP error! Status: ${response.status}`); | |
} | |
return response.json(); | |
}) | |
.then(data => { | |
if (data.success) { | |
updateCartUI(data.cart); | |
} else { | |
console.error('Failed to fetch cart:', data.error); | |
const cart = getCartLocalStorage(); | |
updateCartUI(cart); | |
} | |
}) | |
.catch(err => { | |
console.error('Error fetching cart:', err); | |
const cart = getCartLocalStorage(); | |
updateCartUI(cart); | |
}); | |
// Timer Persistence | |
const savedOrderTimer = localStorage.getItem('orderTimer'); | |
if (savedOrderTimer) { | |
const timerData = JSON.parse(savedOrderTimer); | |
const now = Date.now(); | |
const elapsed = Math.floor((now - timerData.startTime) / 1000); | |
const remaining = timerData.duration - elapsed; | |
if (remaining > 0) { | |
startOrderTimer(remaining); | |
} else { | |
const successMessageElement = document.getElementById('orderSuccessMessage'); | |
const timerContainer = document.getElementById('orderTimerContainer'); | |
timerContainer.style.display = 'block'; | |
successMessageElement.style.display = 'block'; | |
setTimeout(() => { | |
timerContainer.style.display = 'none'; | |
successMessageElement.style.display = 'none'; | |
}, 3000); | |
localStorage.removeItem('orderTimer'); | |
} | |
} | |
}); | |
function handleOrderPlacedAndStartTimer(estimatedCompletionTimeInSeconds) { | |
const startTime = Date.now(); | |
localStorage.setItem('orderTimer', JSON.stringify({ | |
startTime: startTime, | |
duration: estimatedCompletionTimeInSeconds | |
})); | |
startOrderTimer(estimatedCompletionTimeInSeconds); | |
} | |
</script> | |
</body> | |
</html> |