lokeshloki143 commited on
Commit
9eefad3
·
verified ·
1 Parent(s): b5400c3

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +74 -61
templates/menu.html CHANGED
@@ -8,10 +8,10 @@
8
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
9
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
10
  <!-- Preload Critical Resources -->
11
- <link rel="preload" href="/static/placeholder.jpg" as="image" fetchpriority="high">
12
  {% for section, items in ordered_menu.items() %}
13
  {% for item in items[:1] %}
14
- <link rel="preload" href="{{ item.Image1__c | default('/static/placeholder.jpg') }}" as="image" fetchpriority="high">
15
  {% endfor %}
16
  {% endfor %}
17
  <style>
@@ -30,6 +30,7 @@
30
  .menu-card {
31
  max-width: 350px;
32
  border-radius: 15px;
 
33
  background-color: #fff;
34
  margin: auto;
35
  display: flex;
@@ -44,7 +45,9 @@
44
  position: relative;
45
  width: 100%;
46
  height: 200px;
 
47
  border-radius: 15px 15px 0 0;
 
48
  transition: transform 0.3s ease;
49
  }
50
  .media-wrapper:hover, .media-wrapper.touched {
@@ -65,12 +68,11 @@
65
  }
66
  .menu-image {
67
  position: absolute;
68
- top: 50%;
69
- left: 50%;
70
- transform: translate(-50%, -50%);
71
  width: 100%;
72
  height: 100%;
73
- object-fit: contain;
74
  opacity: 1;
75
  transition: opacity 0.3s ease;
76
  }
@@ -255,6 +257,7 @@
255
  border-radius: 10px;
256
  margin-bottom: 15px;
257
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
 
258
  }
259
  .addon-section h6 {
260
  margin: 0;
@@ -338,11 +341,26 @@
338
  }
339
  .addon-options {
340
  max-height: 500px;
 
341
  transition: max-height 0.3s ease;
342
  }
343
  .addon-options.collapsed {
344
  max-height: 0;
345
- overflow: hidden;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
346
  }
347
  /* Soft Drinks Modal Styling */
348
  #softDrinkModal .modal-dialog {
@@ -375,7 +393,7 @@
375
  #softDrinkModal .modal-body img {
376
  max-height: 160px;
377
  width: 100%;
378
- object-fit: contain;
379
  border-radius: 8px;
380
  margin-bottom: 15px;
381
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
@@ -509,7 +527,7 @@
509
  #veg-toggle:checked {
510
  background-color: #0FAA39; /* Green when on */
511
  }
512
- /* Customized Dish Toggle */
513
  #category-CustomizedDish:checked {
514
  background: linear-gradient(45deg, #32CD32, #3CB371);
515
  }
@@ -578,7 +596,7 @@
578
  #micModal .modal-body #mic-item-image {
579
  max-height: 150px;
580
  width: 100%;
581
- object-fit: contain;
582
  border-radius: 8px;
583
  margin-bottom: 10px;
584
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
@@ -606,6 +624,10 @@
606
  from { transform: scale(0); }
607
  to { transform: scale(1); }
608
  }
 
 
 
 
609
  @keyframes fadeIn {
610
  from { opacity: 0; transform: translateY(10px); }
611
  to { opacity: 1; transform: translateY(0); }
@@ -634,7 +656,7 @@
634
  .modal-body #modal-img {
635
  max-height: 200px;
636
  width: 100%;
637
- object-fit: contain;
638
  border-radius: 8px;
639
  margin-bottom: 10px;
640
  }
@@ -840,12 +862,12 @@
840
  .bottom-action-bar .btn-order-history {
841
  background: linear-gradient(45deg, #FFA07A, #FF69B4, #0FAA39);
842
  border-color: transparent;
843
- color: #000000;
844
  }
845
  .bottom-action-bar .btn-order-history:hover {
846
  background: linear-gradient(45deg, #FF8C61, #FF1493, #0D9232);
847
  border-color: transparent;
848
- color: #000000;
849
  }
850
  .bottom-action-bar .btn-view-cart {
851
  background-color: #0FAA39;
@@ -928,7 +950,6 @@
928
  max-width: 150px;
929
  margin: 0 auto 8px;
930
  display: block;
931
- object-fit: contain;
932
  }
933
  .modal-body #modal-name {
934
  font-size: 18px;
@@ -1022,6 +1043,7 @@
1022
  font-size: 10px;
1023
  margin-left: 5px;
1024
  }
 
1025
  .addon-section {
1026
  margin-bottom: 10px;
1027
  }
@@ -1050,6 +1072,7 @@
1050
  margin-left: 6px;
1051
  font-size: 0.8rem;
1052
  }
 
1053
  #softDrinkModal .modal-dialog {
1054
  max-width: 90%;
1055
  }
@@ -1101,6 +1124,7 @@
1101
  font-size: 1rem;
1102
  line-height: 28px;
1103
  }
 
1104
  #filter-form {
1105
  margin-bottom: 15px;
1106
  }
@@ -1126,6 +1150,7 @@
1126
  .toggle-container label {
1127
  font-size: 0.9rem;
1128
  }
 
1129
  #micModal .modal-dialog {
1130
  max-width: 90%;
1131
  }
@@ -1173,6 +1198,7 @@
1173
  <form method="get" action="/menu" class="text-center mb-4" id="filter-form">
1174
  <label class="form-label fw-bold">Filters:</label>
1175
  <div class="toggle-container">
 
1176
  <input type="checkbox" id="veg-toggle" name="veg"
1177
  {% if selected_category == "Veg" %}checked{% endif %}
1178
  class="custom-toggle" onchange="handleToggle('veg')"
@@ -1180,6 +1206,7 @@
1180
  <label for="veg-toggle">Veg</label>
1181
  </div>
1182
  <div class="toggle-container">
 
1183
  <input type="radio" id="category-CustomizedDish" name="category" value="Customized Dish"
1184
  {% if selected_category == "Customized Dish" %}checked{% endif %}
1185
  class="custom-toggle" onchange="handleToggle('custom')"
@@ -1220,9 +1247,9 @@
1220
  class="menu-image"
1221
  src="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
1222
  alt="{{ item.Name | default('Unnamed Item') }}"
1223
- preload="auto"
1224
- fetchpriority="high"
1225
- decoding="async">
1226
  <video
1227
  class="menu-video"
1228
  muted
@@ -1230,6 +1257,7 @@
1230
  preload="metadata"
1231
  width="350"
1232
  height="200"
 
1233
  fetchpriority="low"
1234
  onerror="this.classList.remove('active'); this.previousElementSibling.classList.remove('hidden');">
1235
  <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
@@ -1309,13 +1337,13 @@
1309
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1310
  </div>
1311
  <div class="modal-body">
1312
- <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" preload="auto" fetchpriority="high" decoding="async">
1313
  <h5 id="modal-name" class="fw-bold text-center"></h5>
1314
  <p id="modal-price" class="text-muted text-center"></p>
1315
  <p id="modal-description" class="text-secondary"></p>
1316
  <div id="modal-addons" class="modal-addons mt-4">
1317
  <h5>Customization Options</h5>
1318
- <div id="addons-list" class="addons-container"></div>
1319
  </div>
1320
  <div class="mt-4">
1321
  <h6>Special Instructions</h6>
@@ -1344,7 +1372,7 @@
1344
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1345
  </div>
1346
  <div class="modal-body">
1347
- <img id="soft-drink-image" class="img-fluid rounded mb-3 d-block mx-auto" alt="Soft Drink Image" preload="auto" fetchpriority="high" decoding="async">
1348
  <div class="text-center mb-3">
1349
  <h5 id="soft-drink-name"></h5>
1350
  <p id="soft-drink-price"></p>
@@ -1377,7 +1405,7 @@
1377
  <i class="bi bi-mic" id="mic-icon-animation"></i>
1378
  <p id="mic-status" class="mt-3">Say the name of a menu item...</p>
1379
  <div id="mic-item-details" style="display: none;">
1380
- <img id="mic-item-image" class="img-fluid rounded mb-3 d-block mx-auto" alt="Spoken Item Image" preload="auto" fetchpriority="high" decoding="async">
1381
  <h5 id="mic-item-name" class="fw-bold text-center"></h5>
1382
  </div>
1383
  </div>
@@ -1420,7 +1448,6 @@
1420
  "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
1421
  "Whole Wheat Flour", "Yogurt (Curd)"
1422
  ];
1423
-
1424
  function addToCartLocalStorage(payload) {
1425
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1426
  const existingItem = cart.find(item =>
@@ -1436,7 +1463,6 @@
1436
  localStorage.setItem('cart', JSON.stringify(cart));
1437
  return cart;
1438
  }
1439
-
1440
  function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
1441
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1442
  const itemIndex = cart.findIndex(item =>
@@ -1454,11 +1480,9 @@
1454
  localStorage.setItem('cart', JSON.stringify(cart));
1455
  return cart;
1456
  }
1457
-
1458
  function getCartLocalStorage() {
1459
  return JSON.parse(localStorage.getItem('cart')) || [];
1460
  }
1461
-
1462
  function debounce(func, wait) {
1463
  let timeout;
1464
  return function (...args) {
@@ -1466,7 +1490,6 @@
1466
  timeout = setTimeout(() => func.apply(this, args), wait);
1467
  };
1468
  }
1469
-
1470
  function updateModalPrice() {
1471
  const selectedAddOns = Array.from(
1472
  document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
@@ -1475,14 +1498,12 @@
1475
  const totalPrice = baseItemPrice + totalAddOnPrice;
1476
  document.getElementById('modal-price').innerText = `$${totalPrice.toFixed(2)}`;
1477
  }
1478
-
1479
  function updateSoftDrinkQuantity(delta) {
1480
  const quantityInput = document.getElementById('soft-drink-quantity');
1481
  let currentQuantity = parseInt(quantityInput.value) || 1;
1482
  currentQuantity = Math.max(1, currentQuantity + delta);
1483
  quantityInput.value = currentQuantity;
1484
  }
1485
-
1486
  function showSoftDrinkModal(button) {
1487
  currentSoftDrinkButton = button;
1488
  const buttonContainer = button.closest('.button-container');
@@ -1498,7 +1519,6 @@
1498
  const modal = new bootstrap.Modal(document.getElementById('softDrinkModal'));
1499
  modal.show();
1500
  }
1501
-
1502
  function addSoftDrinkToCart() {
1503
  if (!currentSoftDrinkButton) return;
1504
 
@@ -1551,7 +1571,6 @@
1551
  modal.hide();
1552
  });
1553
  }
1554
-
1555
  function updateCartUI(cart) {
1556
  if (!Array.isArray(cart)) {
1557
  console.error('Invalid cart data:', cart);
@@ -1567,10 +1586,8 @@
1567
  cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
1568
  }
1569
  }
1570
-
1571
  window.most_common_addons = {{ most_common_addons | tojson }};
1572
  console.log("Most common add-ons: ", window.most_common_addons);
1573
-
1574
  function showItemDetails(name, price, image, description, section, selectedCategory) {
1575
  document.getElementById('modal-name').innerText = name;
1576
  baseItemPrice = parseFloat(price) || 0;
@@ -1579,16 +1596,17 @@
1579
  modalImg.src = image || '/static/placeholder.jpg';
1580
  document.getElementById('modal-description').innerText = description || 'No description available.';
1581
  document.getElementById('addons-list').innerHTML = '';
 
1582
  document.getElementById('modal-instructions').value = '';
1583
  const modalSectionEl = document.getElementById('modal-section');
1584
  modalSectionEl.setAttribute('data-section', section);
1585
  modalSectionEl.setAttribute('data-category', selectedCategory);
1586
  document.getElementById('quantityInput').value = 1;
1587
-
1588
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1589
  .then(response => response.json())
1590
  .then(data => {
1591
  const addonsList = document.getElementById('addons-list');
 
1592
  addonsList.innerHTML = '';
1593
  if (!data.success || !data.addons || data.addons.length === 0) {
1594
  addonsList.innerHTML = '<p>No customization options available.</p>';
@@ -1680,10 +1698,10 @@
1680
  })
1681
  .catch(err => {
1682
  console.error('Error fetching add-ons:', err);
 
1683
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1684
  });
1685
  }
1686
-
1687
  function addToCartFromModal() {
1688
  if (isProcessingRequest) return;
1689
  isProcessingRequest = true;
@@ -1749,7 +1767,6 @@
1749
  isProcessingRequest = false;
1750
  });
1751
  }
1752
-
1753
  function handleToggle(source) {
1754
  const form = document.getElementById("filter-form");
1755
  const veg = document.getElementById("veg-toggle");
@@ -1768,7 +1785,6 @@
1768
  }
1769
  form.submit();
1770
  }
1771
-
1772
  function stopSpeechRecognition() {
1773
  if (recognition) {
1774
  recognition.stop();
@@ -1780,7 +1796,6 @@
1780
  }
1781
  }
1782
  }
1783
-
1784
  function resetMicModal() {
1785
  document.getElementById('mic-status').textContent = 'Say the name of a menu item...';
1786
  document.getElementById('mic-status').style.display = 'block';
@@ -1788,7 +1803,6 @@
1788
  document.getElementById('mic-item-image').src = '';
1789
  document.getElementById('mic-item-name').textContent = '';
1790
  }
1791
-
1792
  document.addEventListener('DOMContentLoaded', function () {
1793
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1794
  const dropdownMenu = document.querySelector('.dropdown-menu');
@@ -1811,7 +1825,6 @@
1811
  searchBar.addEventListener('click', function () {
1812
  window.location.href = '/search';
1813
  });
1814
-
1815
  const selectedItem = localStorage.getItem('selectedItem');
1816
  if (selectedItem) {
1817
  try {
@@ -1831,32 +1844,32 @@
1831
  if (toggleLink) {
1832
  toggleLink.click();
1833
  }
1834
- }
1835
- });
1836
- if (targetCard && buttonContainer) {
1837
- const name = buttonContainer.getAttribute('data-item-name');
1838
- const price = buttonContainer.getAttribute('data-item-price');
1839
- const image = buttonContainer.getAttribute('data-item-image2');
1840
- const description = buttonContainer.getAttribute('data-item-description');
1841
- const category = buttonContainer.getAttribute('data-item-category');
1842
- setTimeout(() => {
1843
- if (section === 'Soft Drinks') {
1844
- showSoftDrinkModal(buttonContainer.querySelector('.add-to-cart-btn'));
1845
- } else {
1846
- showItemDetails(name, price, image, description, section, category);
1847
- const modal = new bootstrap.Modal(document.getElementById('itemModal'));
1848
- modal.show();
1849
  }
1850
- }, 1000);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1851
  }
1852
- } catch (e) {
1853
- console.error('Error parsing selectedItem:', e);
1854
- } finally {
1855
- localStorage.removeItem('selectedItem');
1856
  }
1857
- }
1858
 
1859
- // Image-to-Video hover/touch functionality
1860
  const mediaWrappers = document.querySelectorAll('.media-wrapper');
1861
  mediaWrappers.forEach(wrapper => {
1862
  const video = wrapper.querySelector('.menu-video');
 
8
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
9
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
10
  <!-- Preload Critical Resources -->
11
+ <link rel="preload" href="/static/placeholder.mp4" as="video" fetchpriority="high">
12
  {% for section, items in ordered_menu.items() %}
13
  {% for item in items[:1] %}
14
+ <link rel="preload" href="{{ item.Video1__c | default('/static/placeholder.mp4') }}" as="video" fetchpriority="high">
15
  {% endfor %}
16
  {% endfor %}
17
  <style>
 
30
  .menu-card {
31
  max-width: 350px;
32
  border-radius: 15px;
33
+ overflow: hidden;
34
  background-color: #fff;
35
  margin: auto;
36
  display: flex;
 
45
  position: relative;
46
  width: 100%;
47
  height: 200px;
48
+ overflow: hidden;
49
  border-radius: 15px 15px 0 0;
50
+ background-color: #000;
51
  transition: transform 0.3s ease;
52
  }
53
  .media-wrapper:hover, .media-wrapper.touched {
 
68
  }
69
  .menu-image {
70
  position: absolute;
71
+ top: 0;
72
+ left: 0;
 
73
  width: 100%;
74
  height: 100%;
75
+ object-fit: cover;
76
  opacity: 1;
77
  transition: opacity 0.3s ease;
78
  }
 
257
  border-radius: 10px;
258
  margin-bottom: 15px;
259
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
260
+ overflow: hidden;
261
  }
262
  .addon-section h6 {
263
  margin: 0;
 
341
  }
342
  .addon-options {
343
  max-height: 500px;
344
+ overflow: hidden;
345
  transition: max-height 0.3s ease;
346
  }
347
  .addon-options.collapsed {
348
  max-height: 0;
349
+ }
350
+ .addon-loading {
351
+ display: flex;
352
+ justify-content: center;
353
+ align-items: center;
354
+ padding: 15px;
355
+ }
356
+ .addon-loading::after {
357
+ content: '';
358
+ width: 20px;
359
+ height: 20px;
360
+ border: 3px solid #0FAA39;
361
+ border-top: 3px solid transparent;
362
+ border-radius: 50%;
363
+ animation: spin 1s linear infinite;
364
  }
365
  /* Soft Drinks Modal Styling */
366
  #softDrinkModal .modal-dialog {
 
393
  #softDrinkModal .modal-body img {
394
  max-height: 160px;
395
  width: 100%;
396
+ object-fit: cover;
397
  border-radius: 8px;
398
  margin-bottom: 15px;
399
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
 
527
  #veg-toggle:checked {
528
  background-color: #0FAA39; /* Green when on */
529
  }
530
+ /* Customized Dish Toggle Retains Original Styling */
531
  #category-CustomizedDish:checked {
532
  background: linear-gradient(45deg, #32CD32, #3CB371);
533
  }
 
596
  #micModal .modal-body #mic-item-image {
597
  max-height: 150px;
598
  width: 100%;
599
+ object-fit: cover;
600
  border-radius: 8px;
601
  margin-bottom: 10px;
602
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
 
624
  from { transform: scale(0); }
625
  to { transform: scale(1); }
626
  }
627
+ @keyframes spin {
628
+ 0% { transform: rotate(0deg); }
629
+ 100% { transform: rotate(360deg); }
630
+ }
631
  @keyframes fadeIn {
632
  from { opacity: 0; transform: translateY(10px); }
633
  to { opacity: 1; transform: translateY(0); }
 
656
  .modal-body #modal-img {
657
  max-height: 200px;
658
  width: 100%;
659
+ object-fit: cover;
660
  border-radius: 8px;
661
  margin-bottom: 10px;
662
  }
 
862
  .bottom-action-bar .btn-order-history {
863
  background: linear-gradient(45deg, #FFA07A, #FF69B4, #0FAA39);
864
  border-color: transparent;
865
+ color: #000000; /* Black text */
866
  }
867
  .bottom-action-bar .btn-order-history:hover {
868
  background: linear-gradient(45deg, #FF8C61, #FF1493, #0D9232);
869
  border-color: transparent;
870
+ color: #000000; /* Black text on hover */
871
  }
872
  .bottom-action-bar .btn-view-cart {
873
  background-color: #0FAA39;
 
950
  max-width: 150px;
951
  margin: 0 auto 8px;
952
  display: block;
 
953
  }
954
  .modal-body #modal-name {
955
  font-size: 18px;
 
1043
  font-size: 10px;
1044
  margin-left: 5px;
1045
  }
1046
+ /* Mobile-Specific Addon Styles */
1047
  .addon-section {
1048
  margin-bottom: 10px;
1049
  }
 
1072
  margin-left: 6px;
1073
  font-size: 0.8rem;
1074
  }
1075
+ /* Mobile-Specific Soft Drinks Modal Styles */
1076
  #softDrinkModal .modal-dialog {
1077
  max-width: 90%;
1078
  }
 
1124
  font-size: 1rem;
1125
  line-height: 28px;
1126
  }
1127
+ /* Mobile-Specific Filter Form Styles */
1128
  #filter-form {
1129
  margin-bottom: 15px;
1130
  }
 
1150
  .toggle-container label {
1151
  font-size: 0.9rem;
1152
  }
1153
+ /* Mobile-Specific Microphone Modal Styles */
1154
  #micModal .modal-dialog {
1155
  max-width: 90%;
1156
  }
 
1198
  <form method="get" action="/menu" class="text-center mb-4" id="filter-form">
1199
  <label class="form-label fw-bold">Filters:</label>
1200
  <div class="toggle-container">
1201
+ <!-- Veg Only Toggle -->
1202
  <input type="checkbox" id="veg-toggle" name="veg"
1203
  {% if selected_category == "Veg" %}checked{% endif %}
1204
  class="custom-toggle" onchange="handleToggle('veg')"
 
1206
  <label for="veg-toggle">Veg</label>
1207
  </div>
1208
  <div class="toggle-container">
1209
+ <!-- Customized Dish Toggle -->
1210
  <input type="radio" id="category-CustomizedDish" name="category" value="Customized Dish"
1211
  {% if selected_category == "Customized Dish" %}checked{% endif %}
1212
  class="custom-toggle" onchange="handleToggle('custom')"
 
1247
  class="menu-image"
1248
  src="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
1249
  alt="{{ item.Name | default('Unnamed Item') }}"
1250
+ loading="lazy"
1251
+ decoding="async"
1252
+ fetchpriority="low">
1253
  <video
1254
  class="menu-video"
1255
  muted
 
1257
  preload="metadata"
1258
  width="350"
1259
  height="200"
1260
+ loading="lazy"
1261
  fetchpriority="low"
1262
  onerror="this.classList.remove('active'); this.previousElementSibling.classList.remove('hidden');">
1263
  <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
 
1337
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1338
  </div>
1339
  <div class="modal-body">
1340
+ <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" style="max-height: 200px; object-fit: cover;" loading="lazy" decoding="async">
1341
  <h5 id="modal-name" class="fw-bold text-center"></h5>
1342
  <p id="modal-price" class="text-muted text-center"></p>
1343
  <p id="modal-description" class="text-secondary"></p>
1344
  <div id="modal-addons" class="modal-addons mt-4">
1345
  <h5>Customization Options</h5>
1346
+ <div id="addons-list" class="addons-container addon-loading"></div>
1347
  </div>
1348
  <div class="mt-4">
1349
  <h6>Special Instructions</h6>
 
1372
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1373
  </div>
1374
  <div class="modal-body">
1375
+ <img id="soft-drink-image" class="img-fluid rounded mb-3 d-block mx-auto" alt="Soft Drink Image" loading="lazy" decoding="async">
1376
  <div class="text-center mb-3">
1377
  <h5 id="soft-drink-name"></h5>
1378
  <p id="soft-drink-price"></p>
 
1405
  <i class="bi bi-mic" id="mic-icon-animation"></i>
1406
  <p id="mic-status" class="mt-3">Say the name of a menu item...</p>
1407
  <div id="mic-item-details" style="display: none;">
1408
+ <img id="mic-item-image" class="img-fluid rounded mb-3 d-block mx-auto" alt="Spoken Item Image" loading="lazy" decoding="async">
1409
  <h5 id="mic-item-name" class="fw-bold text-center"></h5>
1410
  </div>
1411
  </div>
 
1448
  "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
1449
  "Whole Wheat Flour", "Yogurt (Curd)"
1450
  ];
 
1451
  function addToCartLocalStorage(payload) {
1452
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1453
  const existingItem = cart.find(item =>
 
1463
  localStorage.setItem('cart', JSON.stringify(cart));
1464
  return cart;
1465
  }
 
1466
  function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
1467
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1468
  const itemIndex = cart.findIndex(item =>
 
1480
  localStorage.setItem('cart', JSON.stringify(cart));
1481
  return cart;
1482
  }
 
1483
  function getCartLocalStorage() {
1484
  return JSON.parse(localStorage.getItem('cart')) || [];
1485
  }
 
1486
  function debounce(func, wait) {
1487
  let timeout;
1488
  return function (...args) {
 
1490
  timeout = setTimeout(() => func.apply(this, args), wait);
1491
  };
1492
  }
 
1493
  function updateModalPrice() {
1494
  const selectedAddOns = Array.from(
1495
  document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
 
1498
  const totalPrice = baseItemPrice + totalAddOnPrice;
1499
  document.getElementById('modal-price').innerText = `$${totalPrice.toFixed(2)}`;
1500
  }
 
1501
  function updateSoftDrinkQuantity(delta) {
1502
  const quantityInput = document.getElementById('soft-drink-quantity');
1503
  let currentQuantity = parseInt(quantityInput.value) || 1;
1504
  currentQuantity = Math.max(1, currentQuantity + delta);
1505
  quantityInput.value = currentQuantity;
1506
  }
 
1507
  function showSoftDrinkModal(button) {
1508
  currentSoftDrinkButton = button;
1509
  const buttonContainer = button.closest('.button-container');
 
1519
  const modal = new bootstrap.Modal(document.getElementById('softDrinkModal'));
1520
  modal.show();
1521
  }
 
1522
  function addSoftDrinkToCart() {
1523
  if (!currentSoftDrinkButton) return;
1524
 
 
1571
  modal.hide();
1572
  });
1573
  }
 
1574
  function updateCartUI(cart) {
1575
  if (!Array.isArray(cart)) {
1576
  console.error('Invalid cart data:', cart);
 
1586
  cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
1587
  }
1588
  }
 
1589
  window.most_common_addons = {{ most_common_addons | tojson }};
1590
  console.log("Most common add-ons: ", window.most_common_addons);
 
1591
  function showItemDetails(name, price, image, description, section, selectedCategory) {
1592
  document.getElementById('modal-name').innerText = name;
1593
  baseItemPrice = parseFloat(price) || 0;
 
1596
  modalImg.src = image || '/static/placeholder.jpg';
1597
  document.getElementById('modal-description').innerText = description || 'No description available.';
1598
  document.getElementById('addons-list').innerHTML = '';
1599
+ document.getElementById('addons-list').classList.add('addon-loading');
1600
  document.getElementById('modal-instructions').value = '';
1601
  const modalSectionEl = document.getElementById('modal-section');
1602
  modalSectionEl.setAttribute('data-section', section);
1603
  modalSectionEl.setAttribute('data-category', selectedCategory);
1604
  document.getElementById('quantityInput').value = 1;
 
1605
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1606
  .then(response => response.json())
1607
  .then(data => {
1608
  const addonsList = document.getElementById('addons-list');
1609
+ addonsList.classList.remove('addon-loading');
1610
  addonsList.innerHTML = '';
1611
  if (!data.success || !data.addons || data.addons.length === 0) {
1612
  addonsList.innerHTML = '<p>No customization options available.</p>';
 
1698
  })
1699
  .catch(err => {
1700
  console.error('Error fetching add-ons:', err);
1701
+ document.getElementById('addons-list').classList.remove('addon-loading');
1702
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1703
  });
1704
  }
 
1705
  function addToCartFromModal() {
1706
  if (isProcessingRequest) return;
1707
  isProcessingRequest = true;
 
1767
  isProcessingRequest = false;
1768
  });
1769
  }
 
1770
  function handleToggle(source) {
1771
  const form = document.getElementById("filter-form");
1772
  const veg = document.getElementById("veg-toggle");
 
1785
  }
1786
  form.submit();
1787
  }
 
1788
  function stopSpeechRecognition() {
1789
  if (recognition) {
1790
  recognition.stop();
 
1796
  }
1797
  }
1798
  }
 
1799
  function resetMicModal() {
1800
  document.getElementById('mic-status').textContent = 'Say the name of a menu item...';
1801
  document.getElementById('mic-status').style.display = 'block';
 
1803
  document.getElementById('mic-item-image').src = '';
1804
  document.getElementById('mic-item-name').textContent = '';
1805
  }
 
1806
  document.addEventListener('DOMContentLoaded', function () {
1807
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1808
  const dropdownMenu = document.querySelector('.dropdown-menu');
 
1825
  searchBar.addEventListener('click', function () {
1826
  window.location.href = '/search';
1827
  });
 
1828
  const selectedItem = localStorage.getItem('selectedItem');
1829
  if (selectedItem) {
1830
  try {
 
1844
  if (toggleLink) {
1845
  toggleLink.click();
1846
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1847
  }
1848
+ });
1849
+ if (targetCard && buttonContainer) {
1850
+ const name = buttonContainer.getAttribute('data-item-name');
1851
+ const price = buttonContainer.getAttribute('data-item-price');
1852
+ const image = buttonContainer.getAttribute('data-item-image2');
1853
+ const description = buttonContainer.getAttribute('data-item-description');
1854
+ const category = buttonContainer.getAttribute('data-item-category');
1855
+ setTimeout(() => {
1856
+ if (section === 'Soft Drinks') {
1857
+ showSoftDrinkModal(buttonContainer.querySelector('.add-to-cart-btn'));
1858
+ } else {
1859
+ showItemDetails(name, price, image, description, section, category);
1860
+ const modal = new bootstrap.Modal(document.getElementById('itemModal'));
1861
+ modal.show();
1862
+ }
1863
+ }, 1000);
1864
+ }
1865
+ } catch (e) {
1866
+ console.error('Error parsing selectedItem:', e);
1867
+ } finally {
1868
+ localStorage.removeItem('selectedItem');
1869
  }
 
 
 
 
1870
  }
 
1871
 
1872
+ // Image-to-Video hover/touch functionality
1873
  const mediaWrappers = document.querySelectorAll('.media-wrapper');
1874
  mediaWrappers.forEach(wrapper => {
1875
  const video = wrapper.querySelector('.menu-video');