lokesh341 commited on
Commit
55bb9ef
·
verified ·
1 Parent(s): d83205a

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +437 -275
templates/menu.html CHANGED
@@ -41,6 +41,8 @@
41
  }
42
  .menu-card.visible {
43
  opacity: 1;
 
 
44
  }
45
  .menu-card.highlighted {
46
  border: 3px solid #0FAA39;
@@ -548,6 +550,48 @@
548
  font-size: 1.2rem;
549
  color: #333;
550
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
  @keyframes checkmark {
552
  from { transform: scale(0); }
553
  to { transform: scale(1); }
@@ -636,6 +680,7 @@
636
  height: 40px;
637
  text-align: center;
638
  }
 
639
  .modal-footer .btn-primary {
640
  background-color: #0FAA39;
641
  border-color: #0FAA39;
@@ -1080,9 +1125,12 @@
1080
  </style>
1081
  </head>
1082
  <body>
 
 
 
1083
  <div class="fixed-top-bar">
1084
  <div class="avatar-dropdown-container">
1085
- <div class="avatar-icon">
1086
  <span>{{ first_letter }}</span>
1087
  </div>
1088
  <div class="dropdown-menu">
@@ -1092,26 +1140,26 @@
1092
  </div>
1093
  </div>
1094
  <div class="search-bar-container">
1095
- <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
1096
  <i class="bi bi-search search-icon"></i>
1097
- <i class="bi bi-mic mic-icon" id="micIcon"></i>
 
 
1098
  </div>
1099
  </div>
1100
 
1101
  <form method="get" action="/menu" class="text-center mb-4" id="filter-form">
1102
  <label class="form-label fw-bold">Filters:</label>
1103
  <div class="toggle-container">
1104
- <!-- Veg Only Toggle -->
1105
  <input type="checkbox" id="veg-toggle" name="veg"
1106
  {% if selected_category == "Veg" %}checked{% endif %}
1107
- class="custom-toggle" onchange="handleToggle('veg')">
1108
  <label for="veg-toggle">Veg</label>
1109
  </div>
1110
  <div class="toggle-container">
1111
- <!-- Customized Dish Toggle -->
1112
  <input type="radio" id="category-CustomizedDish" name="category" value="Customized Dish"
1113
  {% if selected_category == "Customized Dish" %}checked{% endif %}
1114
- class="custom-toggle" onchange="handleToggle('custom')">
1115
  <label for="category-CustomizedDish">Customized Dish</label>
1116
  </div>
1117
  </form>
@@ -1123,14 +1171,14 @@
1123
  <form method="POST" action="/customdish/generate_custom_dish">
1124
  <div class="mb-3">
1125
  <label for="custom-dish-name" class="form-label">Dish Name</label>
1126
- <input type="text" class="form-control" id="custom-dish-name" name="name" required>
1127
  </div>
1128
  <div class="mb-3 position-relative">
1129
  <label for="custom-dish-description" class="form-label">Dish Description</label>
1130
- <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
1131
  <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
1132
  </div>
1133
- <button type="submit" class="btn btn-primary">Submit Custom Dish</button>
1134
  </form>
1135
  </div>
1136
  {% else %}
@@ -1174,12 +1222,13 @@
1174
  data-item-description="{{ item.Description__c | default('No description') }}"
1175
  data-item-image2="{{ item.Image2__c | default(item.Image1__c) }}">
1176
  {% if item.Section__c == 'Soft Drinks' %}
1177
- <button class="btn btn-primary add-to-cart-btn" onclick="showSoftDrinkModal(this)">ADD</button>
1178
  {% else %}
1179
  <button class="btn btn-primary"
1180
  data-bs-toggle="modal"
1181
  data-bs-target="#itemModal"
1182
- 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.Section__c | default(section) }}', '{{ selected_category }}')">
 
1183
  ADD
1184
  </button>
1185
  {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' %}
@@ -1191,7 +1240,7 @@
1191
  </div>
1192
  </div>
1193
  {% if item.Section__c != 'Soft Drinks' %}
1194
- <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
1195
  <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
1196
  <h6>Description</h6>
1197
  <p>{{ item.Description__c | default('No description available') }}</p>
@@ -1213,10 +1262,10 @@
1213
  </div>
1214
 
1215
  <div class="bottom-action-bar">
1216
- <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history">
1217
  <i class="bi bi-clock-history"></i> Order History
1218
  </a>
1219
- <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart">
1220
  <i class="bi bi-cart"></i> View Cart
1221
  <span id="cart-item-count" class="cart-icon-badge" style="display: none;">0</span>
1222
  </a>
@@ -1241,17 +1290,17 @@
1241
  </div>
1242
  <div class="mt-4">
1243
  <h6>Special Instructions</h6>
1244
- <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
1245
  </div>
1246
  <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
1247
  </div>
1248
  <div class="modal-footer d-flex align-items-center justify-content-between">
1249
  <div class="d-flex align-items-center gap-2">
1250
- <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
1251
- <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
1252
- <button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
1253
  </div>
1254
- <button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
1255
  </div>
1256
  </div>
1257
  </div>
@@ -1290,9 +1339,12 @@
1290
 
1291
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1292
  <script>
 
1293
  let isProcessingRequest = false;
1294
  let currentSoftDrinkButton = null;
1295
  let baseItemPrice = 0;
 
 
1296
  const menuItems = [
1297
  {% for section, items in ordered_menu.items() %}
1298
  {% for item in items %}
@@ -1303,6 +1355,8 @@
1303
  {% endfor %}
1304
  {% endfor %}
1305
  ];
 
 
1306
  const ingredientsList = [
1307
  "Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
1308
  "Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
@@ -1318,6 +1372,31 @@
1318
  "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
1319
  "Whole Wheat Flour", "Yogurt (Curd)"
1320
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1321
  function addToCartLocalStorage(payload) {
1322
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1323
  const existingItem = cart.find(item =>
@@ -1333,6 +1412,7 @@
1333
  localStorage.setItem('cart', JSON.stringify(cart));
1334
  return cart;
1335
  }
 
1336
  function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
1337
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1338
  const itemIndex = cart.findIndex(item =>
@@ -1350,16 +1430,29 @@
1350
  localStorage.setItem('cart', JSON.stringify(cart));
1351
  return cart;
1352
  }
 
1353
  function getCartLocalStorage() {
1354
  return JSON.parse(localStorage.getItem('cart')) || [];
1355
  }
1356
- function debounce(func, wait) {
1357
- let timeout;
1358
- return function (...args) {
1359
- clearTimeout(timeout);
1360
- timeout = setTimeout(() => func.apply(this, args), wait);
1361
- };
 
 
 
 
 
 
 
 
 
 
1362
  }
 
 
1363
  function updateModalPrice() {
1364
  const selectedAddOns = Array.from(
1365
  document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
@@ -1368,12 +1461,14 @@
1368
  const totalPrice = baseItemPrice + totalAddOnPrice;
1369
  document.getElementById('modal-price').innerText = `$${totalPrice.toFixed(2)}`;
1370
  }
 
1371
  function updateSoftDrinkQuantity(delta) {
1372
  const quantityInput = document.getElementById('soft-drink-quantity');
1373
  let currentQuantity = parseInt(quantityInput.value) || 1;
1374
  currentQuantity = Math.max(1, currentQuantity + delta);
1375
  quantityInput.value = currentQuantity;
1376
  }
 
1377
  function showSoftDrinkModal(button) {
1378
  currentSoftDrinkButton = button;
1379
  const buttonContainer = button.closest('.button-container');
@@ -1382,13 +1477,14 @@
1382
  const itemImage = buttonContainer.getAttribute('data-item-image');
1383
 
1384
  document.getElementById('soft-drink-name').textContent = itemName;
1385
- document.getElementById('soft-drink-price').textContent = `${itemPrice}`;
1386
  document.getElementById('soft-drink-quantity').value = '1';
1387
  document.getElementById('soft-drink-image').src = itemImage || '/static/placeholder.jpg';
1388
 
1389
  const modal = new bootstrap.Modal(document.getElementById('softDrinkModal'));
1390
  modal.show();
1391
  }
 
1392
  function addSoftDrinkToCart() {
1393
  if (!currentSoftDrinkButton) return;
1394
 
@@ -1427,6 +1523,7 @@
1427
  modal.hide();
1428
  } else {
1429
  console.error('Failed to add item to cart:', data.error);
 
1430
  const cart = addToCartLocalStorage(cartPayload);
1431
  updateCartUI(cart);
1432
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
@@ -1435,27 +1532,14 @@
1435
  })
1436
  .catch(err => {
1437
  console.error('Error adding item to cart:', err);
 
1438
  const cart = addToCartLocalStorage(cartPayload);
1439
  updateCartUI(cart);
1440
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1441
  modal.hide();
1442
  });
1443
  }
1444
- function updateCartUI(cart) {
1445
- if (!Array.isArray(cart)) {
1446
- console.error('Invalid cart data:', cart);
1447
- return;
1448
- }
1449
- let totalQuantity = 0;
1450
- cart.forEach(item => {
1451
- totalQuantity += item.quantity;
1452
- });
1453
- const cartItemCount = document.getElementById('cart-item-count');
1454
- if (cartItemCount) {
1455
- cartItemCount.innerText = totalQuantity;
1456
- cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
1457
- }
1458
- }
1459
  function showItemDetails(name, price, image, description, section, selectedCategory) {
1460
  document.getElementById('modal-name').innerText = name;
1461
  baseItemPrice = parseFloat(price) || 0;
@@ -1471,79 +1555,92 @@
1471
  modalSectionEl.setAttribute('data-category', selectedCategory);
1472
  document.getElementById('quantityInput').value = 1;
1473
 
1474
- fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1475
- .then(response => response.json())
1476
- .then(data => {
1477
- const addonsList = document.getElementById('addons-list');
1478
- addonsList.classList.remove('addon-loading');
1479
- addonsList.innerHTML = '';
1480
- if (!data.success || !data.addons || data.addons.length === 0) {
1481
- addonsList.innerHTML = '<p>No customization options available.</p>';
1482
- return;
1483
- }
 
 
 
 
1484
 
1485
- data.addons.forEach(addon => {
1486
- const sectionDiv = document.createElement('div');
1487
- sectionDiv.classList.add('addon-section');
1488
- const title = document.createElement('h6');
1489
- title.innerText = addon.name;
1490
- sectionDiv.appendChild(title);
1491
- const optionsContainer = document.createElement('div');
1492
- optionsContainer.classList.add('addon-options');
1493
 
1494
- addon.options.forEach((option, index) => {
1495
- const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1496
- const listItem = document.createElement('div');
1497
- listItem.classList.add('form-check');
1498
- listItem.innerHTML = `
1499
- <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1500
- data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}"
1501
- aria-label="Select ${option} for ${addon.name}">
1502
- <label class="form-check-label" for="${optionId}">
1503
- ${option} ${addon.extra_charge ? `<span class="extra-charge">($${addon.extra_charge_amount.toFixed(2)})</span>` : ''}
1504
- </label>
1505
- `;
1506
- optionsContainer.appendChild(listItem);
1507
- });
1508
 
1509
- sectionDiv.appendChild(optionsContainer);
1510
- addonsList.appendChild(sectionDiv);
1511
- });
1512
 
1513
- const addonSections = addonsList.querySelectorAll('.addon-section');
1514
- addonSections.forEach(section => {
1515
- const title = section.querySelector('h6');
1516
- const options = section.querySelector('.addon-options');
1517
- title.addEventListener('click', () => {
1518
- section.classList.toggle('collapsed');
1519
- options.classList.toggle('collapsed');
 
1520
  });
1521
- });
1522
 
1523
- document.querySelectorAll('.addon-option').forEach(checkbox => {
1524
- checkbox.addEventListener('change', updateModalPrice);
1525
- });
1526
 
1527
- document.querySelectorAll('.addon-option').forEach(checkbox => {
1528
- checkbox.addEventListener('change', function () {
1529
- const groupName = this.getAttribute('data-group');
1530
- const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
1531
- if (!isMultiSelectGroup && this.checked) {
1532
- document.querySelectorAll(`.addon-option[data-group="${groupName}"]`).forEach(otherCheckbox => {
1533
- if (otherCheckbox !== this) {
1534
- otherCheckbox.checked = false;
1535
- }
1536
- });
1537
- }
 
1538
  });
 
 
 
 
 
 
 
 
 
 
 
1539
  });
1540
- })
1541
- .catch(err => {
1542
- console.error('Error fetching add-ons:', err);
1543
- document.getElementById('addons-list').classList.remove('addon-loading');
1544
- document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1545
- });
1546
  }
 
1547
  function addToCartFromModal() {
1548
  if (isProcessingRequest) return;
1549
  isProcessingRequest = true;
@@ -1563,7 +1660,7 @@
1563
  }));
1564
  if (!itemName || isNaN(itemPrice) || !section || !itemImage || quantity < 1) {
1565
  console.error('Invalid cart item data:', { itemName, itemPrice, section, itemImage, quantity });
1566
- alert('Invalid item data. Please try again.');
1567
  isProcessingRequest = false;
1568
  return;
1569
  }
@@ -1592,6 +1689,7 @@
1592
  modal.hide();
1593
  } else {
1594
  console.error('Failed to add item to cart:', data.error);
 
1595
  const cart = addToCartLocalStorage(cartPayload);
1596
  updateCartUI(cart);
1597
  const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
@@ -1600,6 +1698,7 @@
1600
  })
1601
  .catch(err => {
1602
  console.error('Error adding item to cart:', err);
 
1603
  const cart = addToCartLocalStorage(cartPayload);
1604
  updateCartUI(cart);
1605
  const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
@@ -1609,35 +1708,39 @@
1609
  isProcessingRequest = false;
1610
  });
1611
  }
 
 
1612
  function handleToggle(source) {
1613
  const form = document.getElementById("filter-form");
1614
  const veg = document.getElementById("veg-toggle");
1615
  const custom = document.getElementById("category-CustomizedDish");
1616
 
 
 
 
 
 
 
1617
  if (source === 'veg') {
1618
- // If Veg is being turned ON, turn OFF Customized Dish
1619
  if (veg.checked) {
1620
  custom.checked = false;
1621
  }
1622
  } else if (source === 'custom') {
1623
- // If Customized Dish is being turned ON, turn OFF Veg
1624
  if (custom.checked) {
1625
  veg.checked = false;
1626
  }
1627
  }
1628
 
1629
- // If Customized Dish is turned OFF manually, check Veg toggle and show all items if Veg is also off
1630
- if (!custom.checked) {
1631
- // If both are off, show all items
1632
- if (!veg.checked) {
1633
- document.getElementById('category-CustomizedDish').checked = false; // Ensure Customized Dish is OFF
1634
- }
1635
  }
1636
 
1637
- // Submit form after toggling
1638
  form.submit();
1639
  }
 
 
1640
  document.addEventListener('DOMContentLoaded', function () {
 
1641
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1642
  const dropdownMenu = document.querySelector('.dropdown-menu');
1643
  avatarContainer.addEventListener('click', function (event) {
@@ -1655,10 +1758,47 @@
1655
  dropdownMenu.style.display = 'none';
1656
  });
1657
  });
 
 
1658
  const searchBar = document.getElementById('searchBar');
1659
- searchBar.addEventListener('click', function () {
1660
- window.location.href = '/search';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1661
  });
 
 
1662
  const selectedItem = localStorage.getItem('selectedItem');
1663
  if (selectedItem) {
1664
  try {
@@ -1696,172 +1836,194 @@
1696
  }
1697
  } catch (err) {
1698
  console.error('Error parsing selected item:', err);
 
1699
  }
1700
  localStorage.removeItem('selectedItem');
1701
  }
1702
- const menuCards = document.querySelectorAll('.menu-card');
1703
- const menuVideos = document.querySelectorAll('.menu-video');
1704
- const cardObserver = new IntersectionObserver((entries, observer) => {
1705
- entries.forEach(entry => {
1706
- if (entry.isIntersecting) {
1707
- entry.target.classList.add('visible');
1708
- observer.unobserve(entry.target);
1709
- }
1710
- });
1711
- }, {
1712
- root: null,
1713
- rootMargin: '0px',
1714
- threshold: 0.1
1715
- });
1716
- const videoObserver = new IntersectionObserver((entries, observer) => {
1717
- entries.forEach(entry => {
1718
- if (entry.isIntersecting) {
1719
- const video = entry.target;
1720
- const src = video.getAttribute('data-src');
1721
- if (src && !video.querySelector('source[src="' + src + '"]')) {
1722
- const nextSibling = video.nextElementSibling;
1723
- if (nextSibling && nextSibling.tagName === 'SOURCE') {
1724
- nextSibling.src = src;
1725
- } else {
1726
- const source = video.querySelector('source');
1727
- if (source) {
1728
- source.src = src;
1729
- }
1730
- }
1731
- video.load();
1732
- }
1733
- video.classList.add('loaded');
1734
- observer.unobserve(video);
1735
- }
1736
- });
1737
- }, {
1738
- root: null,
1739
- rootMargin: '200px',
1740
- threshold: 0.01
1741
- });
1742
- menuCards.forEach(card => cardObserver.observe(card));
1743
- menuVideos.forEach(video => videoObserver.observe(video));
1744
- const toggleLinks = document.querySelectorAll('.toggle-details');
1745
- toggleLinks.forEach(link => {
1746
- link.addEventListener('click', function () {
1747
- const itemName = this.getAttribute('data-item-name').replace(/ /g, '-');
1748
- const detailsDiv = document.getElementById(`details-${itemName}`);
1749
- const isCurrentlyShown = detailsDiv.classList.contains('show');
1750
- document.querySelectorAll('.item-details.show').forEach(otherDetails => {
1751
- if (otherDetails !== detailsDiv) {
1752
- otherDetails.classList.remove('show');
1753
- const otherLink = document.querySelector(`.toggle-details[data-item-name="${otherDetails.id.replace('details-', '').replace(/-/g, ' ')}"]`);
1754
- if (otherLink) {
1755
- otherLink.innerText = 'Show Details';
1756
- }
1757
- }
1758
  });
1759
- if (!isCurrentlyShown) {
1760
- detailsDiv.classList.add('show');
1761
- this.innerText = 'Hide Details';
1762
- } else {
1763
- detailsDiv.classList.remove('show');
1764
- this.innerText = 'Show Details';
1765
- }
1766
- });
1767
- });
1768
- const descriptionTextarea = document.getElementById('custom-dish-description');
1769
- const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1770
- if (descriptionTextarea && descriptionSuggestions) {
1771
- let usedIngredients = new Set();
1772
- function updateUsedIngredients() {
1773
- const inputText = descriptionTextarea.value.trim();
1774
- usedIngredients.clear();
1775
- if (inputText) {
1776
- const words = inputText.split(/,\s*/).map(word => word.trim());
1777
- words.forEach(word => {
1778
- if (word && ingredientsList.includes(word)) {
1779
- usedIngredients.add(word);
1780
- }
1781
- });
1782
- }
1783
  }
1784
- descriptionTextarea.addEventListener('input', function () {
1785
- const inputText = this.value.trim();
1786
- const words = inputText.split(/,\s*/);
1787
- const lastWord = words[words.length - 1].trim().toLowerCase();
1788
- descriptionSuggestions.innerHTML = '';
1789
- descriptionSuggestions.style.display = 'none';
1790
- updateUsedIngredients();
1791
- if (lastWord) {
1792
- const filteredIngredients = ingredientsList.filter(ingredient =>
1793
- ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
1794
- );
1795
- if (filteredIngredients.length > 0) {
1796
- filteredIngredients.forEach(ingredient => {
1797
- const suggestionDiv = document.createElement('div');
1798
- suggestionDiv.classList.add('suggestion-item');
1799
- suggestionDiv.innerText = ingredient;
1800
- suggestionDiv.addEventListener('click', function () {
1801
- const currentValue = descriptionTextarea.value;
1802
- const lastCommaIndex = currentValue.lastIndexOf(',');
1803
- const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
1804
- descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
1805
- descriptionSuggestions.style.display = 'none';
1806
- descriptionTextarea.focus();
1807
- updateUsedIngredients();
1808
- });
1809
- descriptionSuggestions.appendChild(suggestionDiv);
1810
- });
1811
- descriptionSuggestions.style.display = 'block';
1812
- }
1813
- }
1814
- });
1815
- document.addEventListener('click', function (event) {
1816
- if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1817
  descriptionSuggestions.style.display = 'none';
1818
- }
 
 
 
1819
  });
 
1820
  }
1821
- fetch('/cart/get')
1822
- .then(response => {
1823
- if (!response.ok) {
1824
- throw new Error(`HTTP error! Status: ${response.status}`);
1825
- }
1826
- return response.json();
1827
- })
1828
- .then(data => {
1829
- if (data.success) {
1830
- updateCartUI(data.cart);
1831
- } else {
1832
- console.error('Failed to fetch cart:', data.error);
1833
- const cart = getCartLocalStorage();
1834
- updateCartUI(cart);
1835
- }
1836
- })
1837
- .catch(err => {
1838
- console.error('Error fetching cart:', err);
1839
- const cart = getCartLocalStorage();
1840
- updateCartUI(cart);
1841
- });
1842
- const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1843
- preloadedVideos.forEach(link => {
1844
- const video = document.createElement('video');
1845
- video.src = link.href;
1846
- video.preload = 'auto';
1847
- });
1848
- const decreaseBtn = document.getElementById('decreaseQuantity');
1849
- const increaseBtn = document.getElementById('increaseQuantity');
1850
- const quantityInput = document.getElementById('quantityInput');
1851
- decreaseBtn.addEventListener('click', function () {
1852
- let quantity = parseInt(quantityInput.value) || 1;
1853
- quantity = Math.max(1, quantity - 1);
1854
- quantityInput.value = quantity;
1855
- });
1856
- increaseBtn.addEventListener('click', function () {
1857
- let quantity = parseInt(quantityInput.value) || 1;
1858
- quantity += 1;
1859
- quantityInput.value = quantity;
1860
- });
1861
- });
1862
- </script>
1863
- </body>
1864
- </html>
1865
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1866
 
 
 
 
 
 
 
 
1867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
  .menu-card.visible {
43
  opacity: 1;
44
+ /* NEW: Staggered animation for smoother loading */
45
+ animation: fadeIn 0.3s ease-in-out;
46
  }
47
  .menu-card.highlighted {
48
  border: 3px solid #0FAA39;
 
550
  font-size: 1.2rem;
551
  color: #333;
552
  }
553
+ /* NEW: Toast Notification Styling */
554
+ .toast-container {
555
+ position: fixed;
556
+ top: 20px;
557
+ right: 20px;
558
+ z-index: 2000;
559
+ }
560
+ .toast {
561
+ background-color: #dc3545;
562
+ color: white;
563
+ padding: 15px;
564
+ border-radius: 5px;
565
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
566
+ opacity: 0;
567
+ transition: opacity 0.3s ease;
568
+ }
569
+ .toast.show {
570
+ opacity: 1;
571
+ }
572
+ /* NEW: Search Suggestions */
573
+ .search-suggestions {
574
+ position: absolute;
575
+ top: 100%;
576
+ left: 0;
577
+ right: 0;
578
+ background-color: #fff;
579
+ border-radius: 5px;
580
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
581
+ max-height: 200px;
582
+ overflow-y: auto;
583
+ display: none;
584
+ z-index: 1000;
585
+ }
586
+ .search-suggestions .suggestion-item {
587
+ padding: 10px;
588
+ cursor: pointer;
589
+ font-size: 14px;
590
+ color: #333;
591
+ }
592
+ .search-suggestions .suggestion-item:hover {
593
+ background-color: #e6f4ea;
594
+ }
595
  @keyframes checkmark {
596
  from { transform: scale(0); }
597
  to { transform: scale(1); }
 
680
  height: 40px;
681
  text-align: center;
682
  }
683
+ /* NEW: Streamline modal-footer btn-primary */
684
  .modal-footer .btn-primary {
685
  background-color: #0FAA39;
686
  border-color: #0FAA39;
 
1125
  </style>
1126
  </head>
1127
  <body>
1128
+ <!-- NEW: Toast Container -->
1129
+ <div class="toast-container" id="toastContainer"></div>
1130
+
1131
  <div class="fixed-top-bar">
1132
  <div class="avatar-dropdown-container">
1133
+ <div class="avatar-icon" role="button" aria-label="Open user menu">
1134
  <span>{{ first_letter }}</span>
1135
  </div>
1136
  <div class="dropdown-menu">
 
1140
  </div>
1141
  </div>
1142
  <div class="search-bar-container">
1143
+ <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off" aria-label="Search menu items">
1144
  <i class="bi bi-search search-icon"></i>
1145
+ <i class="bi bi-mic mic-icon" id="micIcon" aria-label="Voice search"></i>
1146
+ <!-- NEW: Search Suggestions -->
1147
+ <div class="search-suggestions" id="searchSuggestions"></div>
1148
  </div>
1149
  </div>
1150
 
1151
  <form method="get" action="/menu" class="text-center mb-4" id="filter-form">
1152
  <label class="form-label fw-bold">Filters:</label>
1153
  <div class="toggle-container">
 
1154
  <input type="checkbox" id="veg-toggle" name="veg"
1155
  {% if selected_category == "Veg" %}checked{% endif %}
1156
+ class="custom-toggle" onchange="handleToggle('veg')" aria-label="Filter vegetarian items">
1157
  <label for="veg-toggle">Veg</label>
1158
  </div>
1159
  <div class="toggle-container">
 
1160
  <input type="radio" id="category-CustomizedDish" name="category" value="Customized Dish"
1161
  {% if selected_category == "Customized Dish" %}checked{% endif %}
1162
+ class="custom-toggle" onchange="handleToggle('custom')" aria-label="Filter customized dishes">
1163
  <label for="category-CustomizedDish">Customized Dish</label>
1164
  </div>
1165
  </form>
 
1171
  <form method="POST" action="/customdish/generate_custom_dish">
1172
  <div class="mb-3">
1173
  <label for="custom-dish-name" class="form-label">Dish Name</label>
1174
+ <input type="text" class="form-control" id="custom-dish-name" name="name" required aria-required="true">
1175
  </div>
1176
  <div class="mb-3 position-relative">
1177
  <label for="custom-dish-description" class="form-label">Dish Description</label>
1178
+ <textarea class="form-control" id="custom-dish-description" name="description" required aria-required="true"></textarea>
1179
  <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
1180
  </div>
1181
+ <button type="submit" class="btn btn-primary" aria-label="Submit custom dish">Submit Custom Dish</button>
1182
  </form>
1183
  </div>
1184
  {% else %}
 
1222
  data-item-description="{{ item.Description__c | default('No description') }}"
1223
  data-item-image2="{{ item.Image2__c | default(item.Image1__c) }}">
1224
  {% if item.Section__c == 'Soft Drinks' %}
1225
+ <button class="btn btn-primary add-to-cart-btn" onclick="showSoftDrinkModal(this)" aria-label="Add {{ item.Name | default('Unnamed Item') }} to cart">ADD</button>
1226
  {% else %}
1227
  <button class="btn btn-primary"
1228
  data-bs-toggle="modal"
1229
  data-bs-target="#itemModal"
1230
+ 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.Section__c | default(section) }}', '{{ selected_category }}')"
1231
+ aria-label="Customize {{ item.Name | default('Unnamed Item') }}">
1232
  ADD
1233
  </button>
1234
  {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' %}
 
1240
  </div>
1241
  </div>
1242
  {% if item.Section__c != 'Soft Drinks' %}
1243
+ <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}" role="button" aria-label="Toggle details for {{ item.Name | default('Unnamed Item') }}">Show Details</div>
1244
  <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
1245
  <h6>Description</h6>
1246
  <p>{{ item.Description__c | default('No description available') }}</p>
 
1262
  </div>
1263
 
1264
  <div class="bottom-action-bar">
1265
+ <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history" aria-label="View order history">
1266
  <i class="bi bi-clock-history"></i> Order History
1267
  </a>
1268
+ <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart" aria-label="View cart">
1269
  <i class="bi bi-cart"></i> View Cart
1270
  <span id="cart-item-count" class="cart-icon-badge" style="display: none;">0</span>
1271
  </a>
 
1290
  </div>
1291
  <div class="mt-4">
1292
  <h6>Special Instructions</h6>
1293
+ <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..." aria-label="Special instructions"></textarea>
1294
  </div>
1295
  <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
1296
  </div>
1297
  <div class="modal-footer d-flex align-items-center justify-content-between">
1298
  <div class="d-flex align-items-center gap-2">
1299
+ <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity" aria-label="Decrease quantity">-</button>
1300
+ <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;" aria-label="Selected quantity"/>
1301
+ <button type="button" class="btn btn-outline-secondary" id="increaseQuantity" aria-label="Increase quantity">+</button>
1302
  </div>
1303
+ <button type="button" class="btn btn-primary" onclick="addToCartFromModal()" aria-label="Add to cart">Add to Cart</button>
1304
  </div>
1305
  </div>
1306
  </div>
 
1339
 
1340
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1341
  <script>
1342
+ // === Global State ===
1343
  let isProcessingRequest = false;
1344
  let currentSoftDrinkButton = null;
1345
  let baseItemPrice = 0;
1346
+
1347
+ // === Menu Data ===
1348
  const menuItems = [
1349
  {% for section, items in ordered_menu.items() %}
1350
  {% for item in items %}
 
1355
  {% endfor %}
1356
  {% endfor %}
1357
  ];
1358
+
1359
+ // === Ingredients for Custom Dish ===
1360
  const ingredientsList = [
1361
  "Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
1362
  "Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
 
1372
  "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
1373
  "Whole Wheat Flour", "Yogurt (Curd)"
1374
  ];
1375
+
1376
+ // === Utility Functions ===
1377
+ function debounce(func, wait) {
1378
+ let timeout;
1379
+ return function (...args) {
1380
+ clearTimeout(timeout);
1381
+ timeout = setTimeout(() => func.apply(this, args), wait);
1382
+ };
1383
+ }
1384
+
1385
+ // NEW: Show Toast Notification
1386
+ function showToast(message) {
1387
+ const toastContainer = document.getElementById('toastContainer');
1388
+ const toast = document.createElement('div');
1389
+ toast.classList.add('toast');
1390
+ toast.innerText = message;
1391
+ toastContainer.appendChild(toast);
1392
+ setTimeout(() => toast.classList.add('show'), 100);
1393
+ setTimeout(() => {
1394
+ toast.classList.remove('show');
1395
+ setTimeout(() => toast.remove(), 300);
1396
+ }, 3000);
1397
+ }
1398
+
1399
+ // === Cart Functions ===
1400
  function addToCartLocalStorage(payload) {
1401
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1402
  const existingItem = cart.find(item =>
 
1412
  localStorage.setItem('cart', JSON.stringify(cart));
1413
  return cart;
1414
  }
1415
+
1416
  function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
1417
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1418
  const itemIndex = cart.findIndex(item =>
 
1430
  localStorage.setItem('cart', JSON.stringify(cart));
1431
  return cart;
1432
  }
1433
+
1434
  function getCartLocalStorage() {
1435
  return JSON.parse(localStorage.getItem('cart')) || [];
1436
  }
1437
+
1438
+ function updateCartUI(cart) {
1439
+ if (!Array.isArray(cart)) {
1440
+ console.error('Invalid cart data:', cart);
1441
+ showToast('Error updating cart');
1442
+ return;
1443
+ }
1444
+ let totalQuantity = 0;
1445
+ cart.forEach(item => {
1446
+ totalQuantity += item.quantity;
1447
+ });
1448
+ const cartItemCount = document.getElementById('cart-item-count');
1449
+ if (cartItemCount) {
1450
+ cartItemCount.innerText = totalQuantity;
1451
+ cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
1452
+ }
1453
  }
1454
+
1455
+ // === Modal Functions ===
1456
  function updateModalPrice() {
1457
  const selectedAddOns = Array.from(
1458
  document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
 
1461
  const totalPrice = baseItemPrice + totalAddOnPrice;
1462
  document.getElementById('modal-price').innerText = `$${totalPrice.toFixed(2)}`;
1463
  }
1464
+
1465
  function updateSoftDrinkQuantity(delta) {
1466
  const quantityInput = document.getElementById('soft-drink-quantity');
1467
  let currentQuantity = parseInt(quantityInput.value) || 1;
1468
  currentQuantity = Math.max(1, currentQuantity + delta);
1469
  quantityInput.value = currentQuantity;
1470
  }
1471
+
1472
  function showSoftDrinkModal(button) {
1473
  currentSoftDrinkButton = button;
1474
  const buttonContainer = button.closest('.button-container');
 
1477
  const itemImage = buttonContainer.getAttribute('data-item-image');
1478
 
1479
  document.getElementById('soft-drink-name').textContent = itemName;
1480
+ document.getElementById('soft-drink-price').textContent = `$${itemPrice}`;
1481
  document.getElementById('soft-drink-quantity').value = '1';
1482
  document.getElementById('soft-drink-image').src = itemImage || '/static/placeholder.jpg';
1483
 
1484
  const modal = new bootstrap.Modal(document.getElementById('softDrinkModal'));
1485
  modal.show();
1486
  }
1487
+
1488
  function addSoftDrinkToCart() {
1489
  if (!currentSoftDrinkButton) return;
1490
 
 
1523
  modal.hide();
1524
  } else {
1525
  console.error('Failed to add item to cart:', data.error);
1526
+ showToast('Failed to add item to cart');
1527
  const cart = addToCartLocalStorage(cartPayload);
1528
  updateCartUI(cart);
1529
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
 
1532
  })
1533
  .catch(err => {
1534
  console.error('Error adding item to cart:', err);
1535
+ showToast('Error adding item to cart');
1536
  const cart = addToCartLocalStorage(cartPayload);
1537
  updateCartUI(cart);
1538
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1539
  modal.hide();
1540
  });
1541
  }
1542
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1543
  function showItemDetails(name, price, image, description, section, selectedCategory) {
1544
  document.getElementById('modal-name').innerText = name;
1545
  baseItemPrice = parseFloat(price) || 0;
 
1555
  modalSectionEl.setAttribute('data-category', selectedCategory);
1556
  document.getElementById('quantityInput').value = 1;
1557
 
1558
+ // NEW: Retry mechanism for add-ons
1559
+ let retryCount = 0;
1560
+ const maxRetries = 2;
1561
+ function fetchAddons() {
1562
+ fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1563
+ .then(response => response.json())
1564
+ .then(data => {
1565
+ const addonsList = document.getElementById('addons-list');
1566
+ addonsList.classList.remove('addon-loading');
1567
+ addonsList.innerHTML = '';
1568
+ if (!data.success || !data.addons || data.addons.length === 0) {
1569
+ addonsList.innerHTML = '<p>No customization options available.</p>';
1570
+ return;
1571
+ }
1572
 
1573
+ data.addons.forEach(addon => {
1574
+ const sectionDiv = document.createElement('div');
1575
+ sectionDiv.classList.add('addon-section');
1576
+ const title = document.createElement('h6');
1577
+ title.innerText = addon.name;
1578
+ sectionDiv.appendChild(title);
1579
+ const optionsContainer = document.createElement('div');
1580
+ optionsContainer.classList.add('addon-options');
1581
 
1582
+ addon.options.forEach((option, index) => {
1583
+ const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1584
+ const listItem = document.createElement('div');
1585
+ listItem.classList.add('form-check');
1586
+ listItem.innerHTML = `
1587
+ <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1588
+ data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}"
1589
+ aria-label="Select ${option} for ${addon.name}">
1590
+ <label class="form-check-label" for="${optionId}">
1591
+ ${option} ${addon.extra_charge ? `<span class="extra-charge">($${addon.extra_charge_amount.toFixed(2)})</span>` : ''}
1592
+ </label>
1593
+ `;
1594
+ optionsContainer.appendChild(listItem);
1595
+ });
1596
 
1597
+ sectionDiv.appendChild(optionsContainer);
1598
+ addonsList.appendChild(sectionDiv);
1599
+ });
1600
 
1601
+ const addonSections = addonsList.querySelectorAll('.addon-section');
1602
+ addonSections.forEach(section => {
1603
+ const title = section.querySelector('h6');
1604
+ const options = section.querySelector('.addon-options');
1605
+ title.addEventListener('click', () => {
1606
+ section.classList.toggle('collapsed');
1607
+ options.classList.toggle('collapsed');
1608
+ });
1609
  });
 
1610
 
1611
+ document.querySelectorAll('.addon-option').forEach(checkbox => {
1612
+ checkbox.addEventListener('change', updateModalPrice);
1613
+ });
1614
 
1615
+ document.querySelectorAll('.addon-option').forEach(checkbox => {
1616
+ checkbox.addEventListener('change', function () {
1617
+ const groupName = this.getAttribute('data-group');
1618
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
1619
+ if (!isMultiSelectGroup && this.checked) {
1620
+ document.querySelectorAll(`.addon-option[data-group="${groupName}"]`).forEach(otherCheckbox => {
1621
+ if (otherCheckbox !== this) {
1622
+ otherCheckbox.checked = false;
1623
+ }
1624
+ });
1625
+ }
1626
+ });
1627
  });
1628
+ })
1629
+ .catch(err => {
1630
+ console.error('Error fetching add-ons:', err);
1631
+ if (retryCount < maxRetries) {
1632
+ retryCount++;
1633
+ setTimeout(fetchAddons, 1000);
1634
+ } else {
1635
+ document.getElementById('addons-list').classList.remove('addon-loading');
1636
+ document.getElementById('addons-list').innerHTML = '<p>Failed to load customization options. Please try again later.</p>';
1637
+ showToast('Error loading customization options');
1638
+ }
1639
  });
1640
+ }
1641
+ fetchAddons();
 
 
 
 
1642
  }
1643
+
1644
  function addToCartFromModal() {
1645
  if (isProcessingRequest) return;
1646
  isProcessingRequest = true;
 
1660
  }));
1661
  if (!itemName || isNaN(itemPrice) || !section || !itemImage || quantity < 1) {
1662
  console.error('Invalid cart item data:', { itemName, itemPrice, section, itemImage, quantity });
1663
+ showToast('Invalid item data. Please try again.');
1664
  isProcessingRequest = false;
1665
  return;
1666
  }
 
1689
  modal.hide();
1690
  } else {
1691
  console.error('Failed to add item to cart:', data.error);
1692
+ showToast('Failed to add item to cart');
1693
  const cart = addToCartLocalStorage(cartPayload);
1694
  updateCartUI(cart);
1695
  const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
 
1698
  })
1699
  .catch(err => {
1700
  console.error('Error adding item to cart:', err);
1701
+ showToast('Error adding item to cart');
1702
  const cart = addToCartLocalStorage(cartPayload);
1703
  updateCartUI(cart);
1704
  const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
 
1708
  isProcessingRequest = false;
1709
  });
1710
  }
1711
+
1712
+ // === Filter Form ===
1713
  function handleToggle(source) {
1714
  const form = document.getElementById("filter-form");
1715
  const veg = document.getElementById("veg-toggle");
1716
  const custom = document.getElementById("category-CustomizedDish");
1717
 
1718
+ if (!form || !veg || !custom) {
1719
+ console.error("Form or toggle elements not found");
1720
+ showToast("Error applying filter");
1721
+ return;
1722
+ }
1723
+
1724
  if (source === 'veg') {
 
1725
  if (veg.checked) {
1726
  custom.checked = false;
1727
  }
1728
  } else if (source === 'custom') {
 
1729
  if (custom.checked) {
1730
  veg.checked = false;
1731
  }
1732
  }
1733
 
1734
+ if (!custom.checked && !veg.checked) {
1735
+ custom.checked = false;
 
 
 
 
1736
  }
1737
 
 
1738
  form.submit();
1739
  }
1740
+
1741
+ // === Initialization ===
1742
  document.addEventListener('DOMContentLoaded', function () {
1743
+ // Avatar Dropdown
1744
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1745
  const dropdownMenu = document.querySelector('.dropdown-menu');
1746
  avatarContainer.addEventListener('click', function (event) {
 
1758
  dropdownMenu.style.display = 'none';
1759
  });
1760
  });
1761
+
1762
+ // NEW: Search Bar with Client-Side Filtering
1763
  const searchBar = document.getElementById('searchBar');
1764
+ const searchSuggestions = document.getElementById('searchSuggestions');
1765
+ searchBar.addEventListener('input', debounce(function () {
1766
+ const query = searchBar.value.trim().toLowerCase();
1767
+ searchSuggestions.innerHTML = '';
1768
+ searchSuggestions.style.display = 'none';
1769
+ if (query.length > 0) {
1770
+ const filteredItems = menuItems.filter(item =>
1771
+ item.name.toLowerCase().includes(query) ||
1772
+ item.section.toLowerCase().includes(query)
1773
+ );
1774
+ if (filteredItems.length > 0) {
1775
+ filteredItems.forEach(item => {
1776
+ const suggestionDiv = document.createElement('div');
1777
+ suggestionDiv.classList.add('suggestion-item');
1778
+ suggestionDiv.innerText = `${item.name} (${item.section})`;
1779
+ suggestionDiv.addEventListener('click', () => {
1780
+ localStorage.setItem('selectedItem', JSON.stringify(item));
1781
+ window.location.reload();
1782
+ });
1783
+ searchSuggestions.appendChild(suggestionDiv);
1784
+ });
1785
+ searchSuggestions.style.display = 'block';
1786
+ }
1787
+ }
1788
+ }, 300));
1789
+ searchBar.addEventListener('click', function (e) {
1790
+ e.stopPropagation();
1791
+ if (searchBar.value.trim()) {
1792
+ searchSuggestions.style.display = 'block';
1793
+ }
1794
+ });
1795
+ document.addEventListener('click', function (e) {
1796
+ if (!searchBar.contains(e.target) && !searchSuggestions.contains(e.target)) {
1797
+ searchSuggestions.style.display = 'none';
1798
+ }
1799
  });
1800
+
1801
+ // Highlight Selected Item
1802
  const selectedItem = localStorage.getItem('selectedItem');
1803
  if (selectedItem) {
1804
  try {
 
1836
  }
1837
  } catch (err) {
1838
  console.error('Error parsing selected item:', err);
1839
+ showToast('Error loading selected item');
1840
  }
1841
  localStorage.removeItem('selectedItem');
1842
  }
1843
+
1844
+ // Intersection Observers
1845
+ const menuCards = document.querySelectorAll('.menu-card');
1846
+ const menuVideos = document.querySelectorAll('.menu-video');
1847
+ const cardObserver = new IntersectionObserver((entries, observer) => {
1848
+ entries.forEach(entry => {
1849
+ if (entry.isIntersecting) {
1850
+ entry.target.classList.add('visible');
1851
+ observer.unobserve(entry.target);
1852
+ }
1853
+ });
1854
+ }, {
1855
+ root: null,
1856
+ rootMargin: '0px',
1857
+ threshold: 0.1
1858
+ });
1859
+ const videoObserver = new IntersectionObserver((entries, observer) => {
1860
+ entries.forEach(entry => {
1861
+ if (entry.isIntersecting) {
1862
+ const video = entry.target;
1863
+ const src = video.getAttribute('data-src');
1864
+ // NEW: Enhanced error handling for invalid video sources
1865
+ if (src && src !== '/static/placeholder.mp4') {
1866
+ const source = video.querySelector('source');
1867
+ if (source && !source.src) {
1868
+ source.src = src;
1869
+ video.load().catch(err => {
1870
+ console.error('Error loading video:', err);
1871
+ video.poster = '/static/placeholder.jpg';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1872
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1873
  }
1874
+ } else {
1875
+ video.poster = '/static/placeholder.jpg';
1876
+ }
1877
+ video.classList.add('loaded');
1878
+ observer.unobserve(video);
1879
+ }
1880
+ });
1881
+ }, {
1882
+ root: null,
1883
+ rootMargin: '200px',
1884
+ threshold: 0.01
1885
+ });
1886
+ menuCards.forEach(card => cardObserver.observe(card));
1887
+ menuVideos.forEach(video => videoObserver.observe(video));
1888
+
1889
+ // Toggle Details
1890
+ const toggleLinks = document.querySelectorAll('.toggle-details');
1891
+ toggleLinks.forEach(link => {
1892
+ link.addEventListener('click', function () {
1893
+ const itemName = this.getAttribute('data-item-name').replace(/ /g, '-');
1894
+ const detailsDiv = document.getElementById(`details-${itemName}`);
1895
+ const isCurrentlyShown = detailsDiv.classList.contains('show');
1896
+ document.querySelectorAll('.item-details.show').forEach(otherDetails => {
1897
+ if (otherDetails !== detailsDiv) {
1898
+ otherDetails.classList.remove('show');
1899
+ const otherLink = document.querySelector(`.toggle-details[data-item-name="${otherDetails.id.replace('details-', '').replace(/-/g, ' ')}"]`);
1900
+ if (otherLink) {
1901
+ otherLink.innerText = 'Show Details';
1902
+ }
1903
+ }
1904
+ });
1905
+ if (!isCurrentlyShown) {
1906
+ detailsDiv.classList.add('show');
1907
+ this.innerText = 'Hide Details';
1908
+ } else {
1909
+ detailsDiv.classList.remove('show');
1910
+ this.innerText = 'Show Details';
1911
+ }
1912
+ });
1913
+ });
1914
+
1915
+ // Custom Dish Form Suggestions
1916
+ const descriptionTextarea = document.getElementById('custom-dish-description');
1917
+ const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1918
+ if (descriptionTextarea && descriptionSuggestions) {
1919
+ let usedIngredients = new Set();
1920
+ function updateUsedIngredients() {
1921
+ const inputText = descriptionTextarea.value.trim();
1922
+ usedIngredients.clear();
1923
+ if (inputText) {
1924
+ const words = inputText.split(/,\s*/).map(word => word.trim());
1925
+ words.forEach(word => {
1926
+ if (word && ingredientsList.includes(word)) {
1927
+ usedIngredients.add(word);
1928
+ }
1929
+ });
1930
+ }
1931
+ }
1932
+ // NEW: Debounced input handler for suggestions
1933
+ const debouncedSuggestions = debounce(function () {
1934
+ const inputText = descriptionTextarea.value.trim();
1935
+ const words = inputText.split(/,\s*/);
1936
+ const lastWord = words[words.length - 1].trim().toLowerCase();
1937
+ descriptionSuggestions.innerHTML = '';
1938
+ descriptionSuggestions.style.display = 'none';
1939
+ updateUsedIngredients();
1940
+ if (lastWord) {
1941
+ const filteredIngredients = ingredientsList.filter(ingredient =>
1942
+ ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
1943
+ );
1944
+ if (filteredIngredients.length > 0) {
1945
+ filteredIngredients.forEach(ingredient => {
1946
+ const suggestionDiv = document.createElement('div');
1947
+ suggestionDiv.classList.add('suggestion-item');
1948
+ suggestionDiv.innerText = ingredient;
1949
+ suggestionDiv.addEventListener('click', function () {
1950
+ const currentValue = descriptionTextarea.value;
1951
+ const lastCommaIndex = currentValue.lastIndexOf(',');
1952
+ const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
1953
+ descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
1954
  descriptionSuggestions.style.display = 'none';
1955
+ descriptionTextarea.focus();
1956
+ updateUsedIngredients();
1957
+ });
1958
+ descriptionSuggestions.appendChild(suggestionDiv);
1959
  });
1960
+ descriptionSuggestions.style.display = 'block';
1961
  }
1962
+ }
1963
+ }, 300);
1964
+ descriptionTextarea.addEventListener('input', debouncedSuggestions);
1965
+ document.addEventListener('click', function (event) {
1966
+ if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1967
+ descriptionSuggestions.style.display = 'none';
1968
+ }
1969
+ });
1970
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1971
 
1972
+ // Fetch Initial Cart
1973
+ fetch('/cart/get')
1974
+ .then(response => {
1975
+ if (!response.ok) {
1976
+ throw new Error(`HTTP error! Status: ${response.status}`);
1977
+ }
1978
+ return response.json();
1979
+ })
1980
+ .then(data => {
1981
+ if (data.success) {
1982
+ updateCartUI(data.cart);
1983
+ } else {
1984
+ console.error('Failed to fetch cart:', data.error);
1985
+ showToast('Failed to load cart');
1986
+ const cart = getCartLocalStorage();
1987
+ updateCartUI(cart);
1988
+ }
1989
+ })
1990
+ .catch(err => {
1991
+ console.error('Error fetching cart:', err);
1992
+ showToast('Error loading cart');
1993
+ const cart = getCartLocalStorage();
1994
+ updateCartUI(cart);
1995
+ });
1996
 
1997
+ // NEW: Remove redundant video preloading (handled by <link rel="preload">)
1998
+ // const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1999
+ // preloadedVideos.forEach(link => {
2000
+ // const video = document.createElement('video');
2001
+ // video.src = link.href;
2002
+ // video.preload = 'auto';
2003
+ // });
2004
 
2005
+ // Quantity Controls for Item Modal
2006
+ const decreaseBtn = document.getElementById('decreaseQuantity');
2007
+ const increaseBtn = document.getElementById('increaseQuantity');
2008
+ const quantityInput = document.getElementById('quantityInput');
2009
+ decreaseBtn.addEventListener('click', function () {
2010
+ let quantity = parseInt(quantityInput.value) || 1;
2011
+ quantity = Math.max(1, quantity - 1);
2012
+ quantityInput.value = quantity;
2013
+ });
2014
+ increaseBtn.addEventListener('click', function () {
2015
+ let quantity = parseInt(quantityInput.value) || 1;
2016
+ quantity += 1;
2017
+ quantityInput.value = quantity;
2018
+ });
2019
+
2020
+ // NEW: Keyboard Accessibility for Search Bar
2021
+ searchBar.addEventListener('keydown', function (e) {
2022
+ if (e.key === 'Enter' && searchBar.value.trim()) {
2023
+ window.location.href = '/search';
2024
+ }
2025
+ });
2026
+ });
2027
+ </script>
2028
+ </body>
2029
+ </html>