Spaces:
Runtime error
Runtime error
Update templates/menu.html
Browse files- templates/menu.html +88 -31
templates/menu.html
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
|
@@ -174,7 +174,6 @@
|
|
| 174 |
background-color: #ffe4c4;
|
| 175 |
color: #333;
|
| 176 |
}
|
| 177 |
-
/* Added styles for upload/delete options */
|
| 178 |
.upload-item:hover,
|
| 179 |
.delete-item:hover {
|
| 180 |
background-color: #ffe4c4;
|
|
@@ -246,6 +245,17 @@
|
|
| 246 |
.mic-icon.active {
|
| 247 |
color: #007bff;
|
| 248 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
.autocomplete-suggestions {
|
| 250 |
position: absolute;
|
| 251 |
top: 100%;
|
|
@@ -599,8 +609,6 @@
|
|
| 599 |
font-size: 12px;
|
| 600 |
margin-left: 8px;
|
| 601 |
}
|
| 602 |
-
|
| 603 |
-
/* Mic Popup Styles */
|
| 604 |
.mic-popup {
|
| 605 |
position: fixed;
|
| 606 |
top: 50%;
|
|
@@ -617,24 +625,20 @@
|
|
| 617 |
max-width: 90%;
|
| 618 |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
| 619 |
}
|
| 620 |
-
|
| 621 |
.mic-popup.active {
|
| 622 |
display: block;
|
| 623 |
}
|
| 624 |
-
|
| 625 |
.mic-popup-icon {
|
| 626 |
font-size: 48px;
|
| 627 |
margin-bottom: 20px;
|
| 628 |
color: #ff4444;
|
| 629 |
animation: pulse 1.5s infinite;
|
| 630 |
}
|
| 631 |
-
|
| 632 |
.mic-popup-text {
|
| 633 |
font-size: 18px;
|
| 634 |
margin-bottom: 15px;
|
| 635 |
min-height: 24px;
|
| 636 |
}
|
| 637 |
-
|
| 638 |
.mic-popup-cancel {
|
| 639 |
background-color: #ff4444;
|
| 640 |
color: white;
|
|
@@ -644,13 +648,11 @@
|
|
| 644 |
cursor: pointer;
|
| 645 |
font-weight: bold;
|
| 646 |
}
|
| 647 |
-
|
| 648 |
@keyframes pulse {
|
| 649 |
0% { transform: scale(1); }
|
| 650 |
50% { transform: scale(1.1); }
|
| 651 |
100% { transform: scale(1); }
|
| 652 |
}
|
| 653 |
-
|
| 654 |
@media (max-width: 576px) {
|
| 655 |
.fixed-top-bar {
|
| 656 |
height: 60px;
|
|
@@ -824,7 +826,6 @@
|
|
| 824 |
.toggle-details {
|
| 825 |
font-size: 0.8rem;
|
| 826 |
}
|
| 827 |
-
/* Mic Popup Mobile Styles */
|
| 828 |
.mic-popup {
|
| 829 |
padding: 20px;
|
| 830 |
width: 280px;
|
|
@@ -874,6 +875,7 @@
|
|
| 874 |
<input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
|
| 875 |
<i class="bi bi-search search-icon"></i>
|
| 876 |
<i class="bi bi-mic mic-icon" id="micIcon"></i>
|
|
|
|
| 877 |
<div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
|
| 878 |
</div>
|
| 879 |
</div>
|
|
@@ -893,7 +895,7 @@
|
|
| 893 |
{% if selected_category == "Customized Dish" %}
|
| 894 |
<div id="custom-dish-form" class="mt-4">
|
| 895 |
<h3>Create Your Custom Dish</h3>
|
| 896 |
-
<form method="POST" action="/customdish/generate_custom_dish">
|
| 897 |
<div class="mb-3">
|
| 898 |
<label for="custom-dish-name" class="form-label">Dish Name</label>
|
| 899 |
<input type="text" class="form-control" id="custom-dish-name" name="name" required>
|
|
@@ -953,7 +955,7 @@
|
|
| 953 |
<button class="btn btn-primary"
|
| 954 |
data-bs-toggle="modal"
|
| 955 |
data-bs-target="#itemModal"
|
| 956 |
-
onclick="showItemDetails('{{ item.Name | default('Unnamed Item') }}', '{{ item.Price__c | default('0.00') }}', '{{ item.Image2__c | default(item.Image1__c) }}', '{{ item.Description__c | default('No description') }}', '{{ item.IngredientsInfo__c | default('Not specified') }}', '{{ item.NutritionalInfo__c | default('Not available') }}', '{{ item.Allergens__c | default('None listed') }}', '{{ item.Section__c | default(section) }}', '{{ selected_category }}')">
|
| 957 |
ADD
|
| 958 |
</button>
|
| 959 |
{% endif %}
|
|
@@ -968,8 +970,8 @@
|
|
| 968 |
<div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
|
| 969 |
<h6>Description</h6>
|
| 970 |
<p>{{ item.Description__c | default('No description available') }}</p>
|
| 971 |
-
<h6>
|
| 972 |
-
<p>{{ item.
|
| 973 |
<h6>Nutritional Info</h6>
|
| 974 |
<p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
|
| 975 |
<h6>Allergens</h6>
|
|
@@ -1081,7 +1083,7 @@
|
|
| 1081 |
const menuItems = [
|
| 1082 |
{% for section, items in ordered_menu.items() %}
|
| 1083 |
{% for item in items %}
|
| 1084 |
-
"{{ item.Name | default('Unnamed Item') }}",
|
| 1085 |
{% endfor %}
|
| 1086 |
{% endfor %}
|
| 1087 |
];
|
|
@@ -1102,6 +1104,13 @@
|
|
| 1102 |
"Whole Wheat Flour", "Yogurt (Curd)"
|
| 1103 |
];
|
| 1104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1105 |
function addToCartLocalStorage(payload) {
|
| 1106 |
let cart = JSON.parse(localStorage.getItem('cart')) || [];
|
| 1107 |
const existingItem = cart.find(item =>
|
|
@@ -1151,7 +1160,7 @@
|
|
| 1151 |
function showSoftDrinkModal(button) {
|
| 1152 |
currentSoftDrinkButton = button;
|
| 1153 |
const buttonContainer = button.closest('.button-container');
|
| 1154 |
-
const itemName = buttonContainer.getAttribute('data-item-name');
|
| 1155 |
const itemPrice = buttonContainer.getAttribute('data-item-price');
|
| 1156 |
const itemImage = buttonContainer.getAttribute('data-item-image');
|
| 1157 |
|
|
@@ -1171,11 +1180,11 @@
|
|
| 1171 |
const buttonContainer = currentSoftDrinkButton.closest('.button-container');
|
| 1172 |
const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
|
| 1173 |
|
| 1174 |
-
const itemName = buttonContainer.getAttribute('data-item-name');
|
| 1175 |
const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
|
| 1176 |
const itemImage = buttonContainer.getAttribute('data-item-image');
|
| 1177 |
-
const section = buttonContainer.getAttribute('data-item-section');
|
| 1178 |
-
const selectedCategory = buttonContainer.getAttribute('data-item-category');
|
| 1179 |
|
| 1180 |
const cartPayload = {
|
| 1181 |
itemName: itemName,
|
|
@@ -1198,11 +1207,13 @@
|
|
| 1198 |
.then(response => response.json())
|
| 1199 |
.then(data => {
|
| 1200 |
if (data.success) {
|
|
|
|
| 1201 |
updateCartUI(data.cart);
|
| 1202 |
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
|
| 1203 |
modal.hide();
|
| 1204 |
} else {
|
| 1205 |
console.error('Failed to add item to cart:', data.error);
|
|
|
|
| 1206 |
const cart = addToCartLocalStorage(cartPayload);
|
| 1207 |
updateCartUI(cart);
|
| 1208 |
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
|
|
@@ -1211,6 +1222,7 @@
|
|
| 1211 |
})
|
| 1212 |
.catch(err => {
|
| 1213 |
console.error('Error adding item to cart:', err);
|
|
|
|
| 1214 |
const cart = addToCartLocalStorage(cartPayload);
|
| 1215 |
updateCartUI(cart);
|
| 1216 |
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
|
|
@@ -1422,10 +1434,12 @@
|
|
| 1422 |
categoryForm.submit();
|
| 1423 |
});
|
| 1424 |
});
|
|
|
|
| 1425 |
const searchBar = document.getElementById('searchBar');
|
| 1426 |
const suggestionsContainer = document.getElementById('autocompleteSuggestions');
|
|
|
|
| 1427 |
searchBar.addEventListener('input', function () {
|
| 1428 |
-
const input = this.value.trim().toLowerCase();
|
| 1429 |
suggestionsContainer.innerHTML = '';
|
| 1430 |
suggestionsContainer.style.display = 'none';
|
| 1431 |
if (input) {
|
|
@@ -1440,20 +1454,21 @@
|
|
| 1440 |
suggestionDiv.addEventListener('click', function () {
|
| 1441 |
searchBar.value = item;
|
| 1442 |
suggestionsContainer.style.display = 'none';
|
| 1443 |
-
|
| 1444 |
});
|
| 1445 |
suggestionsContainer.appendChild(suggestionDiv);
|
| 1446 |
});
|
| 1447 |
suggestionsContainer.style.display = 'block';
|
| 1448 |
}
|
| 1449 |
}
|
| 1450 |
-
|
| 1451 |
});
|
| 1452 |
document.addEventListener('click', function (event) {
|
| 1453 |
if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
|
| 1454 |
suggestionsContainer.style.display = 'none';
|
| 1455 |
}
|
| 1456 |
});
|
|
|
|
| 1457 |
const descriptionTextarea = document.getElementById('custom-dish-description');
|
| 1458 |
const descriptionSuggestions = document.getElementById('descriptionSuggestions');
|
| 1459 |
if (descriptionTextarea && descriptionSuggestions) {
|
|
@@ -1507,6 +1522,46 @@
|
|
| 1507 |
}
|
| 1508 |
});
|
| 1509 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1510 |
fetch('/cart/get')
|
| 1511 |
.then(response => {
|
| 1512 |
if (!response.ok) {
|
|
@@ -1528,12 +1583,14 @@
|
|
| 1528 |
const cart = getCartLocalStorage();
|
| 1529 |
updateCartUI(cart);
|
| 1530 |
});
|
|
|
|
| 1531 |
const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
|
| 1532 |
preloadedVideos.forEach(link => {
|
| 1533 |
const video = document.createElement('video');
|
| 1534 |
video.src = link.href;
|
| 1535 |
video.preload = 'auto';
|
| 1536 |
});
|
|
|
|
| 1537 |
const decreaseBtn = document.getElementById('decreaseQuantity');
|
| 1538 |
const increaseBtn = document.getElementById('increaseQuantity');
|
| 1539 |
const quantityInput = document.getElementById('quantityInput');
|
|
@@ -1572,6 +1629,7 @@
|
|
| 1572 |
|
| 1573 |
// Mic Popup Functionality
|
| 1574 |
const micIcon = document.getElementById('micIcon');
|
|
|
|
| 1575 |
const micPopup = document.getElementById('micPopup');
|
| 1576 |
const micPopupText = document.getElementById('micPopupText');
|
| 1577 |
const micPopupCancel = document.getElementById('micPopupCancel');
|
|
@@ -1602,15 +1660,13 @@
|
|
| 1602 |
}
|
| 1603 |
}
|
| 1604 |
|
| 1605 |
-
// Display interim results
|
| 1606 |
if (interimTranscript) {
|
| 1607 |
micPopupText.textContent = interimTranscript;
|
| 1608 |
}
|
| 1609 |
|
| 1610 |
-
// When we have a final result
|
| 1611 |
if (finalTranscript) {
|
| 1612 |
-
searchBar.value = finalTranscript.trim();
|
| 1613 |
-
|
| 1614 |
micPopup.classList.remove('active');
|
| 1615 |
}
|
| 1616 |
};
|
|
@@ -1618,7 +1674,6 @@
|
|
| 1618 |
recognition.onend = () => {
|
| 1619 |
micIcon.classList.remove('active');
|
| 1620 |
if (micPopup.classList.contains('active')) {
|
| 1621 |
-
// If still active, it means it ended unexpectedly
|
| 1622 |
setTimeout(() => {
|
| 1623 |
micPopup.classList.remove('active');
|
| 1624 |
}, 1000);
|
|
@@ -1653,11 +1708,12 @@
|
|
| 1653 |
});
|
| 1654 |
} else {
|
| 1655 |
micIcon.style.display = 'none';
|
|
|
|
| 1656 |
}
|
| 1657 |
});
|
| 1658 |
|
| 1659 |
function filterMenu() {
|
| 1660 |
-
const input = document.getElementById('searchBar').value.trim().toLowerCase();
|
| 1661 |
const sections = document.querySelectorAll('h3');
|
| 1662 |
const items = document.querySelectorAll('.menu-card');
|
| 1663 |
let matchedSections = new Set();
|
|
@@ -1757,7 +1813,7 @@
|
|
| 1757 |
|
| 1758 |
function handleAddonClick(checkbox) {
|
| 1759 |
const groupName = checkbox.getAttribute('data-group');
|
| 1760 |
-
const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo","Beverages","Sauces"].includes(groupName);
|
| 1761 |
if (!isMultiSelectGroup) {
|
| 1762 |
const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
|
| 1763 |
checkboxes.forEach(otherCheckbox => {
|
|
@@ -1797,7 +1853,8 @@
|
|
| 1797 |
itemImage: itemImage,
|
| 1798 |
section: section,
|
| 1799 |
category: selectedCategory,
|
| 1800 |
-
|
|
|
|
| 1801 |
instructions: instructions,
|
| 1802 |
quantity: quantity
|
| 1803 |
};
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
<html lang="en">
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
|
|
|
| 174 |
background-color: #ffe4c4;
|
| 175 |
color: #333;
|
| 176 |
}
|
|
|
|
| 177 |
.upload-item:hover,
|
| 178 |
.delete-item:hover {
|
| 179 |
background-color: #ffe4c4;
|
|
|
|
| 245 |
.mic-icon.active {
|
| 246 |
color: #007bff;
|
| 247 |
}
|
| 248 |
+
.mic-unsupported {
|
| 249 |
+
display: none;
|
| 250 |
+
position: absolute;
|
| 251 |
+
right: 15px;
|
| 252 |
+
font-size: 14px;
|
| 253 |
+
color: #888;
|
| 254 |
+
background-color: #fff;
|
| 255 |
+
padding: 2px 8px;
|
| 256 |
+
border-radius: 10px;
|
| 257 |
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
| 258 |
+
}
|
| 259 |
.autocomplete-suggestions {
|
| 260 |
position: absolute;
|
| 261 |
top: 100%;
|
|
|
|
| 609 |
font-size: 12px;
|
| 610 |
margin-left: 8px;
|
| 611 |
}
|
|
|
|
|
|
|
| 612 |
.mic-popup {
|
| 613 |
position: fixed;
|
| 614 |
top: 50%;
|
|
|
|
| 625 |
max-width: 90%;
|
| 626 |
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
| 627 |
}
|
|
|
|
| 628 |
.mic-popup.active {
|
| 629 |
display: block;
|
| 630 |
}
|
|
|
|
| 631 |
.mic-popup-icon {
|
| 632 |
font-size: 48px;
|
| 633 |
margin-bottom: 20px;
|
| 634 |
color: #ff4444;
|
| 635 |
animation: pulse 1.5s infinite;
|
| 636 |
}
|
|
|
|
| 637 |
.mic-popup-text {
|
| 638 |
font-size: 18px;
|
| 639 |
margin-bottom: 15px;
|
| 640 |
min-height: 24px;
|
| 641 |
}
|
|
|
|
| 642 |
.mic-popup-cancel {
|
| 643 |
background-color: #ff4444;
|
| 644 |
color: white;
|
|
|
|
| 648 |
cursor: pointer;
|
| 649 |
font-weight: bold;
|
| 650 |
}
|
|
|
|
| 651 |
@keyframes pulse {
|
| 652 |
0% { transform: scale(1); }
|
| 653 |
50% { transform: scale(1.1); }
|
| 654 |
100% { transform: scale(1); }
|
| 655 |
}
|
|
|
|
| 656 |
@media (max-width: 576px) {
|
| 657 |
.fixed-top-bar {
|
| 658 |
height: 60px;
|
|
|
|
| 826 |
.toggle-details {
|
| 827 |
font-size: 0.8rem;
|
| 828 |
}
|
|
|
|
| 829 |
.mic-popup {
|
| 830 |
padding: 20px;
|
| 831 |
width: 280px;
|
|
|
|
| 875 |
<input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
|
| 876 |
<i class="bi bi-search search-icon"></i>
|
| 877 |
<i class="bi bi-mic mic-icon" id="micIcon"></i>
|
| 878 |
+
<span class="mic-unsupported" id="micUnsupported">Mic not supported</span>
|
| 879 |
<div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
|
| 880 |
</div>
|
| 881 |
</div>
|
|
|
|
| 895 |
{% if selected_category == "Customized Dish" %}
|
| 896 |
<div id="custom-dish-form" class="mt-4">
|
| 897 |
<h3>Create Your Custom Dish</h3>
|
| 898 |
+
<form method="POST" action="/customdish/generate_custom_dish" id="customDishForm">
|
| 899 |
<div class="mb-3">
|
| 900 |
<label for="custom-dish-name" class="form-label">Dish Name</label>
|
| 901 |
<input type="text" class="form-control" id="custom-dish-name" name="name" required>
|
|
|
|
| 955 |
<button class="btn btn-primary"
|
| 956 |
data-bs-toggle="modal"
|
| 957 |
data-bs-target="#itemModal"
|
| 958 |
+
onclick="showItemDetails('{{ item.Name | default('Unnamed Item') | e }}', '{{ item.Price__c | default('0.00') }}', '{{ item.Image2__c | default(item.Image1__c) | default('/static/placeholder.jpg') }}', '{{ item.Description__c | default('No description') | e }}', '{{ item.IngredientsInfo__c | default('Not specified') | e }}', '{{ item.NutritionalInfo__c | default('Not available') | e }}', '{{ item.Allergens__c | default('None listed') | e }}', '{{ item.Section__c | default(section) | e }}', '{{ selected_category | e }}')">
|
| 959 |
ADD
|
| 960 |
</button>
|
| 961 |
{% endif %}
|
|
|
|
| 970 |
<div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
|
| 971 |
<h6>Description</h6>
|
| 972 |
<p>{{ item.Description__c | default('No description available') }}</p>
|
| 973 |
+
<h6>Ingredients Info</h6>
|
| 974 |
+
<p>{{ item.IngredientsInfo__c | default('Not specified') }}</p>
|
| 975 |
<h6>Nutritional Info</h6>
|
| 976 |
<p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
|
| 977 |
<h6>Allergens</h6>
|
|
|
|
| 1083 |
const menuItems = [
|
| 1084 |
{% for section, items in ordered_menu.items() %}
|
| 1085 |
{% for item in items %}
|
| 1086 |
+
"{{ item.Name | default('Unnamed Item') | e }}",
|
| 1087 |
{% endfor %}
|
| 1088 |
{% endfor %}
|
| 1089 |
];
|
|
|
|
| 1104 |
"Whole Wheat Flour", "Yogurt (Curd)"
|
| 1105 |
];
|
| 1106 |
|
| 1107 |
+
// Utility function to sanitize input to prevent XSS
|
| 1108 |
+
function sanitizeInput(input) {
|
| 1109 |
+
const div = document.createElement('div');
|
| 1110 |
+
div.textContent = input;
|
| 1111 |
+
return div.innerHTML;
|
| 1112 |
+
}
|
| 1113 |
+
|
| 1114 |
function addToCartLocalStorage(payload) {
|
| 1115 |
let cart = JSON.parse(localStorage.getItem('cart')) || [];
|
| 1116 |
const existingItem = cart.find(item =>
|
|
|
|
| 1160 |
function showSoftDrinkModal(button) {
|
| 1161 |
currentSoftDrinkButton = button;
|
| 1162 |
const buttonContainer = button.closest('.button-container');
|
| 1163 |
+
const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
|
| 1164 |
const itemPrice = buttonContainer.getAttribute('data-item-price');
|
| 1165 |
const itemImage = buttonContainer.getAttribute('data-item-image');
|
| 1166 |
|
|
|
|
| 1180 |
const buttonContainer = currentSoftDrinkButton.closest('.button-container');
|
| 1181 |
const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
|
| 1182 |
|
| 1183 |
+
const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
|
| 1184 |
const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
|
| 1185 |
const itemImage = buttonContainer.getAttribute('data-item-image');
|
| 1186 |
+
const section = sanitizeInput(buttonContainer.getAttribute('data-item-section'));
|
| 1187 |
+
const selectedCategory = sanitizeInput(buttonContainer.getAttribute('data-item-category'));
|
| 1188 |
|
| 1189 |
const cartPayload = {
|
| 1190 |
itemName: itemName,
|
|
|
|
| 1207 |
.then(response => response.json())
|
| 1208 |
.then(data => {
|
| 1209 |
if (data.success) {
|
| 1210 |
+
alert('Item added to cart successfully!');
|
| 1211 |
updateCartUI(data.cart);
|
| 1212 |
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
|
| 1213 |
modal.hide();
|
| 1214 |
} else {
|
| 1215 |
console.error('Failed to add item to cart:', data.error);
|
| 1216 |
+
alert(data.error || 'Failed to add item to cart. Using local storage as fallback.');
|
| 1217 |
const cart = addToCartLocalStorage(cartPayload);
|
| 1218 |
updateCartUI(cart);
|
| 1219 |
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
|
|
|
|
| 1222 |
})
|
| 1223 |
.catch(err => {
|
| 1224 |
console.error('Error adding item to cart:', err);
|
| 1225 |
+
alert('Error adding item to cart. Using local storage as fallback.');
|
| 1226 |
const cart = addToCartLocalStorage(cartPayload);
|
| 1227 |
updateCartUI(cart);
|
| 1228 |
const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
|
|
|
|
| 1434 |
categoryForm.submit();
|
| 1435 |
});
|
| 1436 |
});
|
| 1437 |
+
|
| 1438 |
const searchBar = document.getElementById('searchBar');
|
| 1439 |
const suggestionsContainer = document.getElementById('autocompleteSuggestions');
|
| 1440 |
+
const debouncedFilterMenu = debounce(filterMenu, 300);
|
| 1441 |
searchBar.addEventListener('input', function () {
|
| 1442 |
+
const input = sanitizeInput(this.value.trim().toLowerCase());
|
| 1443 |
suggestionsContainer.innerHTML = '';
|
| 1444 |
suggestionsContainer.style.display = 'none';
|
| 1445 |
if (input) {
|
|
|
|
| 1454 |
suggestionDiv.addEventListener('click', function () {
|
| 1455 |
searchBar.value = item;
|
| 1456 |
suggestionsContainer.style.display = 'none';
|
| 1457 |
+
debouncedFilterMenu();
|
| 1458 |
});
|
| 1459 |
suggestionsContainer.appendChild(suggestionDiv);
|
| 1460 |
});
|
| 1461 |
suggestionsContainer.style.display = 'block';
|
| 1462 |
}
|
| 1463 |
}
|
| 1464 |
+
debouncedFilterMenu();
|
| 1465 |
});
|
| 1466 |
document.addEventListener('click', function (event) {
|
| 1467 |
if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
|
| 1468 |
suggestionsContainer.style.display = 'none';
|
| 1469 |
}
|
| 1470 |
});
|
| 1471 |
+
|
| 1472 |
const descriptionTextarea = document.getElementById('custom-dish-description');
|
| 1473 |
const descriptionSuggestions = document.getElementById('descriptionSuggestions');
|
| 1474 |
if (descriptionTextarea && descriptionSuggestions) {
|
|
|
|
| 1522 |
}
|
| 1523 |
});
|
| 1524 |
}
|
| 1525 |
+
|
| 1526 |
+
// Custom Dish Form Validation
|
| 1527 |
+
const customDishForm = document.getElementById('customDishForm');
|
| 1528 |
+
if (customDishForm) {
|
| 1529 |
+
customDishForm.addEventListener('submit', function(event) {
|
| 1530 |
+
const dishName = document.getElementById('custom-dish-name').value.trim();
|
| 1531 |
+
const description = document.getElementById('custom-dish-description').value.trim();
|
| 1532 |
+
if (!dishName || !description) {
|
| 1533 |
+
event.preventDefault();
|
| 1534 |
+
alert('Please fill in both the dish name and description.');
|
| 1535 |
+
return;
|
| 1536 |
+
}
|
| 1537 |
+
// Optional: Add AJAX submission to provide feedback
|
| 1538 |
+
event.preventDefault();
|
| 1539 |
+
fetch('/customdish/generate_custom_dish', {
|
| 1540 |
+
method: 'POST',
|
| 1541 |
+
headers: {
|
| 1542 |
+
'Content-Type': 'application/x-www-form-urlencoded',
|
| 1543 |
+
},
|
| 1544 |
+
body: new URLSearchParams({
|
| 1545 |
+
'name': dishName,
|
| 1546 |
+
'description': description
|
| 1547 |
+
})
|
| 1548 |
+
})
|
| 1549 |
+
.then(response => response.json())
|
| 1550 |
+
.then(data => {
|
| 1551 |
+
if (data.success) {
|
| 1552 |
+
alert('Custom dish submitted successfully!');
|
| 1553 |
+
window.location.reload();
|
| 1554 |
+
} else {
|
| 1555 |
+
alert('Failed to submit custom dish: ' + (data.error || 'Unknown error'));
|
| 1556 |
+
}
|
| 1557 |
+
})
|
| 1558 |
+
.catch(error => {
|
| 1559 |
+
console.error('Error submitting custom dish:', error);
|
| 1560 |
+
alert('Error submitting custom dish. Please try again.');
|
| 1561 |
+
});
|
| 1562 |
+
});
|
| 1563 |
+
}
|
| 1564 |
+
|
| 1565 |
fetch('/cart/get')
|
| 1566 |
.then(response => {
|
| 1567 |
if (!response.ok) {
|
|
|
|
| 1583 |
const cart = getCartLocalStorage();
|
| 1584 |
updateCartUI(cart);
|
| 1585 |
});
|
| 1586 |
+
|
| 1587 |
const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
|
| 1588 |
preloadedVideos.forEach(link => {
|
| 1589 |
const video = document.createElement('video');
|
| 1590 |
video.src = link.href;
|
| 1591 |
video.preload = 'auto';
|
| 1592 |
});
|
| 1593 |
+
|
| 1594 |
const decreaseBtn = document.getElementById('decreaseQuantity');
|
| 1595 |
const increaseBtn = document.getElementById('increaseQuantity');
|
| 1596 |
const quantityInput = document.getElementById('quantityInput');
|
|
|
|
| 1629 |
|
| 1630 |
// Mic Popup Functionality
|
| 1631 |
const micIcon = document.getElementById('micIcon');
|
| 1632 |
+
const micUnsupported = document.getElementById('micUnsupported');
|
| 1633 |
const micPopup = document.getElementById('micPopup');
|
| 1634 |
const micPopupText = document.getElementById('micPopupText');
|
| 1635 |
const micPopupCancel = document.getElementById('micPopupCancel');
|
|
|
|
| 1660 |
}
|
| 1661 |
}
|
| 1662 |
|
|
|
|
| 1663 |
if (interimTranscript) {
|
| 1664 |
micPopupText.textContent = interimTranscript;
|
| 1665 |
}
|
| 1666 |
|
|
|
|
| 1667 |
if (finalTranscript) {
|
| 1668 |
+
searchBar.value = sanitizeInput(finalTranscript.trim());
|
| 1669 |
+
debouncedFilterMenu();
|
| 1670 |
micPopup.classList.remove('active');
|
| 1671 |
}
|
| 1672 |
};
|
|
|
|
| 1674 |
recognition.onend = () => {
|
| 1675 |
micIcon.classList.remove('active');
|
| 1676 |
if (micPopup.classList.contains('active')) {
|
|
|
|
| 1677 |
setTimeout(() => {
|
| 1678 |
micPopup.classList.remove('active');
|
| 1679 |
}, 1000);
|
|
|
|
| 1708 |
});
|
| 1709 |
} else {
|
| 1710 |
micIcon.style.display = 'none';
|
| 1711 |
+
micUnsupported.style.display = 'block';
|
| 1712 |
}
|
| 1713 |
});
|
| 1714 |
|
| 1715 |
function filterMenu() {
|
| 1716 |
+
const input = sanitizeInput(document.getElementById('searchBar').value.trim().toLowerCase());
|
| 1717 |
const sections = document.querySelectorAll('h3');
|
| 1718 |
const items = document.querySelectorAll('.menu-card');
|
| 1719 |
let matchedSections = new Set();
|
|
|
|
| 1813 |
|
| 1814 |
function handleAddonClick(checkbox) {
|
| 1815 |
const groupName = checkbox.getAttribute('data-group');
|
| 1816 |
+
const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
|
| 1817 |
if (!isMultiSelectGroup) {
|
| 1818 |
const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
|
| 1819 |
checkboxes.forEach(otherCheckbox => {
|
|
|
|
| 1853 |
itemImage: itemImage,
|
| 1854 |
section: section,
|
| 1855 |
category: selectedCategory,
|
| 1856 |
+
|
| 1857 |
+
addons: selectedAddOns,
|
| 1858 |
instructions: instructions,
|
| 1859 |
quantity: quantity
|
| 1860 |
};
|