lokesh341 commited on
Commit
52b3d7d
·
verified ·
1 Parent(s): b7fb80a

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +432 -684
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">
12
  {% for section, items in ordered_menu.items() %}
13
  {% for item in items[:1] %}
14
- <link rel="preload" href="{{ item.Image1__c }}" as="image" fetchpriority="high">
15
  {% endfor %}
16
  {% endfor %}
17
  <style>
@@ -22,6 +22,7 @@
22
  padding: 0;
23
  display: flex;
24
  flex-direction: column;
 
25
  }
26
  .container {
27
  max-width: 900px;
@@ -36,78 +37,38 @@
36
  flex-direction: column;
37
  opacity: 0;
38
  transition: opacity 0.3s ease-in-out;
 
39
  }
40
  .menu-card.visible {
41
  opacity: 1;
42
  }
43
-
44
- /* Video Container Styles */
45
- .video-container {
46
- position: relative;
47
- width: 100%;
48
  height: 200px;
49
- overflow: hidden;
50
- border-radius: 15px 15px 0 0;
51
- background-color: #000;
52
- }
53
-
54
- .video-container video {
55
  width: 100%;
56
- height: 100%;
57
  object-fit: cover;
58
- transition: opacity 0.5s ease;
59
- }
60
-
61
- .video-container .video-placeholder {
62
- position: absolute;
63
- top: 0;
64
- left: 0;
65
- width: 100%;
66
- height: 100%;
67
- display: flex;
68
- align-items: center;
69
- justify-content: center;
70
- background-color: #f0f0f0;
71
- color: #666;
72
- font-size: 14px;
73
- }
74
-
75
- .video-container .play-button {
76
- position: absolute;
77
- top: 50%;
78
- left: 50%;
79
- transform: translate(-50%, -50%);
80
- width: 50px;
81
- height: 50px;
82
- background-color: rgba(0, 0, 0, 0.7);
83
- border-radius: 50%;
84
- display: flex;
85
- align-items: center;
86
- justify-content: center;
87
- cursor: pointer;
88
- z-index: 10;
89
  opacity: 0;
90
- transition: opacity 0.3s ease;
 
91
  }
92
-
93
- /* .video-container:hover .play-button {
94
  opacity: 1;
95
  }
96
-
97
- .video-container .play-button i {
98
- color: white;
99
- font-size: 20px;
100
- margin-left: 3px;
101
- } */
102
-
103
- .card-title {
104
  font-size: 1.2rem;
105
- font-weight: bold;
106
  margin: 10px 0;
 
107
  }
108
- .card-text {
109
  font-size: 1rem;
110
- color: #6c757d;
 
 
111
  }
112
  .addbutton .btn {
113
  background-color: #28a745;
@@ -164,53 +125,6 @@
164
  box-shadow: none;
165
  transform: scale(0.98);
166
  }
167
- .view-cart-container {
168
- position: fixed;
169
- bottom: 20px;
170
- right: 20px;
171
- z-index: 999;
172
- display: flex;
173
- align-items: center;
174
- }
175
- .view-cart-button {
176
- background-color: #0FAA39;
177
- color: #fff;
178
- padding: 10px 20px;
179
- border-radius: 30px;
180
- font-size: 1rem;
181
- font-weight: bold;
182
- text-decoration: none;
183
- box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
184
- display: flex;
185
- align-items: center;
186
- justify-content: center;
187
- gap: 8px;
188
- transition: background-color 0.3s ease, transform 0.2s ease;
189
- }
190
- .view-cart-button:hover {
191
- background-color: #109835;
192
- text-decoration: none;
193
- color: #fff;
194
- transform: scale(1.05);
195
- }
196
- .cart-icon-badge {
197
- background-color: #fff;
198
- color: #0FAA39;
199
- border-radius: 50%;
200
- width: 24px;
201
- height: 24px;
202
- display: flex;
203
- align-items: center;
204
- justify-content: center;
205
- font-size: 12px;
206
- font-weight: bold;
207
- margin-left: 5px;
208
- transition: background-color 0.3s ease, color 0.3s ease;
209
- }
210
- .cart-icon-badge.active {
211
- background-color: #ffcc00;
212
- color: #0FAA39;
213
- }
214
  .avatar-dropdown-container {
215
  position: absolute;
216
  right: 10px;
@@ -224,7 +138,7 @@
224
  width: 40px;
225
  height: 40px;
226
  border-radius: 50%;
227
- background-color: #5bbfc1;
228
  cursor: pointer;
229
  display: flex;
230
  align-items: center;
@@ -237,25 +151,28 @@
237
  position: absolute;
238
  right: 0;
239
  top: 100%;
240
- background-color: #fff;
241
  border-radius: 5px;
242
  width: 220px;
243
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
244
  display: none;
 
245
  }
246
  .dropdown-menu .dropdown-item {
247
  padding: 12px 16px;
248
  text-decoration: none;
249
  color: #333;
250
- border-bottom: 1px solid #ddd;
251
  display: block;
252
  font-size: 15px;
 
253
  }
254
  .dropdown-menu .dropdown-item:last-child {
255
  border-bottom: none;
256
  }
257
  .dropdown-menu .dropdown-item:hover {
258
- background-color: #f1f1f1;
 
259
  }
260
  .fixed-top-bar {
261
  position: relative;
@@ -280,10 +197,11 @@
280
  align-items: center;
281
  width: 300px;
282
  max-width: 90%;
 
283
  }
284
  .search-bar-container input {
285
  width: 100%;
286
- padding: 8px 15px 8px 40px;
287
  font-size: 16px;
288
  border-radius: 25px;
289
  border: none;
@@ -336,7 +254,7 @@
336
  }
337
  .addon-section {
338
  background-color: #fff;
339
- border: 2px solid #6c757d;
340
  border-radius: 8px;
341
  padding: 12px;
342
  margin-bottom: 10px;
@@ -416,11 +334,12 @@
416
  font-weight: bold;
417
  text-align: center;
418
  margin-bottom: 5px;
 
419
  }
420
  .modal-body #modal-price {
421
  font-size: 16px;
422
- font-weight: bold;
423
- color: #6c757d;
424
  text-align: center;
425
  margin-bottom: 10px;
426
  }
@@ -489,6 +408,7 @@
489
  font-size: 0.9rem;
490
  font-weight: bold;
491
  margin-bottom: 5px;
 
492
  }
493
  .item-details p {
494
  font-size: 0.85rem;
@@ -585,6 +505,86 @@
585
  .modal-body::-webkit-scrollbar-thumb:hover {
586
  background: #0D9232;
587
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
588
  @media (max-width: 576px) {
589
  .fixed-top-bar {
590
  height: 60px;
@@ -598,7 +598,7 @@
598
  transform: translateY(-50%);
599
  }
600
  .search-bar-container input {
601
- padding: 6px 12px 6px 35px;
602
  font-size: 14px;
603
  border-radius: 20px;
604
  }
@@ -727,19 +727,23 @@
727
  height: 18px;
728
  font-size: 9px;
729
  }
730
- .view-cart-button {
731
- padding: 8px 16px;
 
 
 
 
 
 
 
 
732
  font-size: 14px;
733
  }
734
  .cart-icon-badge {
735
- width: 20px;
736
- height: 20px;
737
  font-size: 10px;
738
- }
739
-
740
- /* Mobile video adjustments */
741
- .video-container {
742
- height: 150px;
743
  }
744
  }
745
  </style>
@@ -790,93 +794,87 @@
790
  <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
791
  <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
792
  </div>
793
- <button type="submit" class="btn btn-primary">Submit</button>
794
  </form>
795
  </div>
796
  {% else %}
797
- {% for section, items in ordered_menu.items() %}
798
- <h3>{{ section }}</h3>
799
- <div class="row">
800
- {% for item in items %}
801
- <div class="col-md-6 mb-4">
802
- <div class="card menu-card">
803
- <div class="video-container">
 
 
804
  <video
805
- id="video-{{ loop.index }}"
806
- preload="metadata"
807
- poster="{{ item.Image1__c if item.Image1__c else '/static/placeholder.jpg' }}"
808
- data-src="{{ item.Video1__c }}"
809
- muted
810
- loop
811
- >
812
- <source src="{{ item.Video1__c }}" type="video/mp4">
 
 
 
 
813
  Your browser does not support the video tag.
814
  </video>
815
- {% if not item.Video1__c %}
816
- <div class="video-placeholder">
817
- No video available
818
- </div>
819
- {% endif %}
820
- <div class="play-button" onclick="togglePlay(this)">
821
- <i class="bi bi-play-fill"></i>
822
- </div>
823
- </div>
824
-
825
- <div class="addbutton">
826
- <div class="card-body d-flex align-items-center justify-content-between">
827
- <div>
828
- <h5 class="card-title">{{ item.Name }}</h5>
829
- <p class="card-text">${{ item.Price__c }}</p>
830
- </div>
831
- <div class="d-flex flex-column align-item-center justify-content-center">
832
- <div class="button-container" data-item-name="{{ item.Name }}" data-item-price="{{ item.Price__c }}" data-item-image="{{ item.Image1__c }}" data-item-section="{{ item.Section__c }}" data-item-category="{{ selected_category }}">
833
- {% if item.Section__c == 'Soft Drinks' %}
834
- <button class="btn btn-primary add-to-cart-btn" onclick="handleSoftDrinkAdd(this)">ADD</button>
835
- <div class="quantity-selector" style="display: none;">
836
- <button class="btn btn-outline-secondary decrease-btn" onclick="decreaseQuantity(this)">-</button>
837
- <select class="quantity-to-remove">
838
- {% for i in range(1, 21) %}
839
- <option value="{{ i }}">{{ i }}</option>
840
- {% endfor %}
841
- </select>
842
- <span class="quantity-display">0</span>
843
- <button class="btn btn-outline-secondary increase-btn" onclick="increaseQuantity(this)">+</button>
844
- <select class="quantity-to-add">
845
- {% for i in range(1, 21) %}
846
- <option value="{{ i }}">{{ i }}</option>
847
- {% endfor %}
848
- </select>
849
- </div>
850
- {% else %}
851
- <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModal"
852
- onclick="showItemDetails('{{ item.Name }}', '{{ item.Price__c }}', '{{ item.Image2__c }}', '{{ item.Description__c }}', '{{ item.Section__c }}','{{ selected_category }}')">
853
- ADD
854
- </button>
855
- {% endif %}
856
- {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
857
- <span class="customisable-text">Customisable</span>
858
- {% endif %}
859
  </div>
860
  </div>
861
  </div>
 
 
862
  </div>
863
- <div class="toggle-details" data-item-name="{{ item.Name }}">Show Details</div>
864
- <div class="item-details" id="details-{{ item.Name | replace(' ', '-') }}"></div>
865
  </div>
866
- </div>
867
- {% endfor %}
868
- </div>
869
- {% endfor %}
870
  {% endif %}
871
  </div>
872
 
873
- <div class="view-cart-container">
874
- <a href="{{ url_for('cart.cart') }}" class="view-cart-button">
875
- <i class="bi bi-cart"></i>
876
- View Cart
877
- <span id="cart-item-count" class="cart-icon-badge {% if cart_item_count > 0 %}active{% endif %}">
878
- {{ cart_item_count if cart_item_count > 0 else '' }}
879
- </span>
880
  </a>
881
  </div>
882
 
@@ -889,15 +887,10 @@
889
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
890
  </div>
891
  <div class="modal-body">
892
- <!-- Item Image -->
893
  <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" style="max-height: 200px; object-fit: cover;">
894
- <!-- Item Name -->
895
  <h5 id="modal-name" class="fw-bold text-center"></h5>
896
- <!-- Item Price -->
897
  <p id="modal-price" class="text-muted text-center"></p>
898
- <!-- Item Description -->
899
  <p id="modal-description" class="text-secondary"></p>
900
- <!-- Add-ons -->
901
  <div id="modal-addons" class="modal-addons mt-4">
902
  <h6>Customization Options</h6>
903
  <div id="addons-list" class="addons-container">Loading customization options...</div>
@@ -908,53 +901,26 @@
908
  </div>
909
  <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
910
  </div>
911
- <!-- Quantity Controls and Add to Cart Button -->
912
  <div class="modal-footer d-flex align-items-center justify-content-between">
913
- <!-- Quantity Controls -->
914
  <div class="d-flex align-items-center gap-2">
915
  <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
916
  <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
917
  <button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
918
  </div>
919
- <!-- Add to Cart Button -->
920
  <button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
921
  </div>
922
  </div>
923
  </div>
924
  </div>
925
 
926
-
927
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
928
  <script>
929
- // Video Handling Functions
930
- function togglePlay(button) {
931
- const videoContainer = button.closest('.video-container');
932
- const video = videoContainer.querySelector('video');
933
-
934
- if (video.paused) {
935
- // Load video source if not already loaded
936
- if (!video.src && video.dataset.src) {
937
- video.src = video.dataset.src;
938
- }
939
- video.play()
940
- .then(() => {
941
- button.innerHTML = '<i class="bi bi-pause-fill"></i>';
942
- })
943
- .catch(e => {
944
- console.log('Video play error:', e);
945
- // Show error to user if needed
946
- });
947
- } else {
948
- video.pause();
949
- button.innerHTML = '<i class="bi bi-play-fill"></i>';
950
- }
951
- }
952
 
953
- // Dynamically populate menuItems from ordered_menu
954
  const menuItems = [
955
  {% for section, items in ordered_menu.items() %}
956
  {% for item in items %}
957
- "{{ item.Name }}",
958
  {% endfor %}
959
  {% endfor %}
960
  ];
@@ -1076,7 +1042,7 @@
1076
  JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1077
  );
1078
  if (existingItem) {
1079
- existingItem.quantity += payload.quantity;
1080
  } else {
1081
  cart.push(payload);
1082
  }
@@ -1106,97 +1072,210 @@
1106
  return JSON.parse(localStorage.getItem('cart')) || [];
1107
  }
1108
 
1109
- document.addEventListener('DOMContentLoaded', function () {
1110
- // Initialize video handling
1111
- const videoContainers = document.querySelectorAll('.video-container');
 
 
 
 
 
 
 
 
 
 
1112
 
1113
- videoContainers.forEach(container => {
1114
- const video = container.querySelector('video');
1115
-
1116
- // Only load video when container is in viewport
1117
- const observer = new IntersectionObserver((entries) => {
1118
- entries.forEach(entry => {
1119
- if (entry.isIntersecting && video.dataset.src && !video.src) {
1120
- video.src = video.dataset.src;
1121
- observer.unobserve(entry.target);
1122
- }
1123
- });
1124
- }, {
1125
- rootMargin: '200px',
1126
- threshold: 0.1
1127
- });
1128
-
1129
- observer.observe(container);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1130
 
1131
- // Handle hover play/pause
1132
- container.addEventListener('mouseenter', () => {
1133
- if (video.src) {
1134
- video.play().catch(e => console.log('Autoplay prevented:', e));
 
 
 
 
 
 
 
 
 
 
 
 
1135
  }
 
 
 
 
 
 
 
 
1136
  });
1137
-
1138
- container.addEventListener('mouseleave', () => {
1139
- if (!video.paused) {
1140
- video.pause();
1141
- const playButton = container.querySelector('.play-button');
1142
- if (playButton) {
1143
- playButton.innerHTML = '<i class="bi bi-play-fill"></i>';
1144
- }
1145
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1146
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1147
  });
1148
 
1149
- // Initialize microphone for voice search
1150
- const micIcon = document.getElementById('micIcon');
1151
- if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
1152
- const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1153
- const recognition = new SpeechRecognition();
1154
- recognition.lang = 'en-US';
1155
- recognition.onstart = () => micIcon.classList.add('active');
1156
- recognition.onresult = (event) => {
1157
- const searchBar = document.getElementById('searchBar');
1158
- searchBar.value = event.results[0][0].transcript.trim();
1159
- filterMenu();
1160
- };
1161
- recognition.onend = () => micIcon.classList.remove('active');
1162
- recognition.onerror = (event) => {
1163
- micIcon.classList.remove('active');
1164
- console.error('Speech error:', event.error);
1165
- };
1166
- micIcon.addEventListener('click', () => {
1167
- recognition.start();
1168
- });
1169
- } else {
1170
- micIcon.style.display = 'none';
1171
  }
1172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1173
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1174
  const dropdownMenu = document.querySelector('.dropdown-menu');
1175
-
1176
  avatarContainer.addEventListener('click', function (event) {
1177
  event.stopPropagation();
1178
  dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1179
  });
1180
-
1181
  document.addEventListener('click', function (event) {
1182
  if (!avatarContainer.contains(event.target)) {
1183
  dropdownMenu.style.display = 'none';
1184
  }
1185
  });
1186
-
1187
  const dropdownItems = document.querySelectorAll('.dropdown-item');
1188
  dropdownItems.forEach(item => {
1189
  item.addEventListener('click', function () {
1190
  dropdownMenu.style.display = 'none';
1191
  });
1192
  });
1193
-
1194
  const menuCards = document.querySelectorAll('.menu-card');
1195
  menuCards.forEach(card => {
1196
  const itemName = card.querySelector('.card-title').innerText;
1197
  const detailsDiv = card.querySelector('.item-details');
1198
  const detailsData = menuItemDetails[itemName];
1199
-
1200
  if (detailsData) {
1201
  detailsDiv.innerHTML = `
1202
  <h6>Ingredients</h6>
@@ -1215,7 +1294,7 @@
1215
  detailsDiv.innerHTML = '<p>No details available for this item.</p>';
1216
  }
1217
  });
1218
-
1219
  const cardObserver = new IntersectionObserver((entries, observer) => {
1220
  entries.forEach(entry => {
1221
  if (entry.isIntersecting) {
@@ -1228,9 +1307,27 @@
1228
  rootMargin: '0px',
1229
  threshold: 0.1
1230
  });
1231
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
  menuCards.forEach(card => cardObserver.observe(card));
1233
-
1234
  const toggleLinks = document.querySelectorAll('.toggle-details');
1235
  toggleLinks.forEach(link => {
1236
  link.addEventListener('click', function () {
@@ -1240,16 +1337,13 @@
1240
  this.innerText = detailsDiv.classList.contains('show') ? 'Hide Details' : 'Show Details';
1241
  });
1242
  });
1243
-
1244
  const categoryButtons = document.querySelectorAll('.category-button');
1245
  const categoryForm = document.getElementById('categoryForm');
1246
  const selectedCategoryInput = document.getElementById('selectedCategoryInput');
1247
-
1248
  if (!selectedCategoryInput.value) {
1249
  selectedCategoryInput.value = "All";
1250
  document.querySelector('.category-button[data-category="All"]').classList.add('selected');
1251
  }
1252
-
1253
  categoryButtons.forEach(button => {
1254
  button.addEventListener('click', function () {
1255
  categoryButtons.forEach(btn => btn.classList.remove('selected'));
@@ -1258,15 +1352,12 @@
1258
  categoryForm.submit();
1259
  });
1260
  });
1261
-
1262
  const searchBar = document.getElementById('searchBar');
1263
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1264
-
1265
  searchBar.addEventListener('input', function () {
1266
  const input = this.value.trim().toLowerCase();
1267
  suggestionsContainer.innerHTML = '';
1268
  suggestionsContainer.style.display = 'none';
1269
-
1270
  if (input) {
1271
  const filteredItems = menuItems.filter(item =>
1272
  item.toLowerCase().includes(input)
@@ -1288,19 +1379,15 @@
1288
  }
1289
  filterMenu();
1290
  });
1291
-
1292
  document.addEventListener('click', function (event) {
1293
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1294
  suggestionsContainer.style.display = 'none';
1295
  }
1296
  });
1297
-
1298
  const descriptionTextarea = document.getElementById('custom-dish-description');
1299
  const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1300
-
1301
  if (descriptionTextarea && descriptionSuggestions) {
1302
  let usedIngredients = new Set();
1303
-
1304
  function updateUsedIngredients() {
1305
  const inputText = descriptionTextarea.value.trim();
1306
  usedIngredients.clear();
@@ -1313,16 +1400,13 @@
1313
  });
1314
  }
1315
  }
1316
-
1317
  descriptionTextarea.addEventListener('input', function () {
1318
  const inputText = this.value.trim();
1319
  const words = inputText.split(/,\s*/);
1320
  const lastWord = words[words.length - 1].trim().toLowerCase();
1321
  descriptionSuggestions.innerHTML = '';
1322
  descriptionSuggestions.style.display = 'none';
1323
-
1324
  updateUsedIngredients();
1325
-
1326
  if (lastWord) {
1327
  const filteredIngredients = ingredientsList.filter(ingredient =>
1328
  ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
@@ -1347,14 +1431,12 @@
1347
  }
1348
  }
1349
  });
1350
-
1351
  document.addEventListener('click', function (event) {
1352
  if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1353
  descriptionSuggestions.style.display = 'none';
1354
  }
1355
  });
1356
  }
1357
-
1358
  fetch('/cart/get')
1359
  .then(response => {
1360
  if (!response.ok) {
@@ -1376,17 +1458,15 @@
1376
  const cart = getCartLocalStorage();
1377
  updateCartUI(cart);
1378
  });
1379
-
1380
- const preloadedImages = document.querySelectorAll('link[rel="preload"]');
1381
- preloadedImages.forEach(link => {
1382
- const img = new Image();
1383
- img.src = link.href;
1384
  });
1385
-
1386
  const decreaseBtn = document.getElementById('decreaseQuantity');
1387
  const increaseBtn = document.getElementById('increaseQuantity');
1388
  const quantityInput = document.getElementById('quantityInput');
1389
-
1390
  decreaseBtn.addEventListener('click', function () {
1391
  let currentQuantity = parseInt(quantityInput.value);
1392
  if (currentQuantity > 1) {
@@ -1394,32 +1474,43 @@
1394
  quantityInput.value = currentQuantity;
1395
  }
1396
  });
1397
-
1398
  increaseBtn.addEventListener('click', function () {
1399
  let currentQuantity = parseInt(quantityInput.value);
1400
  currentQuantity++;
1401
  quantityInput.value = currentQuantity;
1402
  });
1403
- });
1404
 
1405
- function debounce(func, wait) {
1406
- let timeout;
1407
- return function (...args) {
1408
- clearTimeout(timeout);
1409
- timeout = setTimeout(() => func.apply(this, args), wait);
1410
- };
1411
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1412
 
1413
  function filterMenu() {
1414
  const input = document.getElementById('searchBar').value.trim().toLowerCase();
1415
  const sections = document.querySelectorAll('h3');
1416
  const items = document.querySelectorAll('.menu-card');
1417
  let matchedSections = new Set();
1418
-
1419
  items.forEach(item => {
1420
  const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1421
  const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
1422
-
1423
  if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1424
  item.style.display = 'block';
1425
  item.classList.add('visible');
@@ -1428,7 +1519,6 @@
1428
  item.style.display = 'none';
1429
  }
1430
  });
1431
-
1432
  sections.forEach(section => {
1433
  const sectionRow = section.nextElementSibling;
1434
  if (matchedSections.has(sectionRow)) {
@@ -1439,7 +1529,6 @@
1439
  sectionRow.style.display = 'none';
1440
  }
1441
  });
1442
-
1443
  if (!input) {
1444
  sections.forEach(section => {
1445
  section.style.display = 'block';
@@ -1456,22 +1545,8 @@
1456
  document.getElementById('modal-name').innerText = name;
1457
  document.getElementById('modal-price').innerText = `$${price}`;
1458
  const modalImg = document.getElementById('modal-img');
1459
- if (section.toLowerCase() === 'soft drinks') {
1460
- modalImg.style.display = 'none';
1461
- } else {
1462
- modalImg.style.display = 'block';
1463
- modalImg.src = image || '/static/placeholder.jpg';
1464
- }
1465
  document.getElementById('modal-description').innerText = description || 'No description available.';
1466
- const nutritionalInfoEl = document.getElementById('modal-nutritional-info');
1467
- const itemDetails = menuItemDetails[name];
1468
- if (itemDetails && itemDetails.nutritionalInfo) {
1469
- const { calories, protein, carbs, fats, fiber, sugar } = itemDetails.nutritionalInfo;
1470
- nutritionalInfoEl.innerText = `[Energy: ${calories} kcal, Protein: ${protein}g, Carbohydrates: ${carbs}g, Fiber: ${fiber}g, Fat: ${fats}g, Sugar: ${sugar}g]`;
1471
- } else {
1472
- nutritionalInfoEl.innerText = '[No nutritional info available.]';
1473
- }
1474
-
1475
  document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1476
  document.getElementById('modal-instructions').value = '';
1477
  const modalSectionEl = document.getElementById('modal-section');
@@ -1479,148 +1554,28 @@
1479
  modalSectionEl.setAttribute('data-category', selectedCategory);
1480
  document.getElementById('quantityInput').value = 1;
1481
 
1482
- const prepStyleOptions = document.getElementById('prep-style-options');
1483
- const typeOptions = document.getElementById('type-options');
1484
- const spiceLevelOptions = document.getElementById('spice-level-options');
1485
- const firstRow = document.getElementById('first-row');
1486
- const firstRowTitle = document.getElementById('first-row-title');
1487
- const addonsTitle = document.getElementById('addons-title');
1488
-
1489
- prepStyleOptions.innerHTML = '';
1490
- typeOptions.innerHTML = '';
1491
- spiceLevelOptions.innerHTML = '';
1492
-
1493
- if (section.toLowerCase() === 'starters') {
1494
- firstRow.style.display = 'block';
1495
- firstRowTitle.style.display = 'block';
1496
- addonsTitle.style.display = 'none';
1497
- } else {
1498
- firstRow.style.display = 'none';
1499
- firstRowTitle.style.display = 'none';
1500
- addonsTitle.style.display = 'block';
1501
- }
1502
-
1503
- const addonsList = document.getElementById('addons-list');
1504
- addonsList.innerHTML = '';
1505
- const dummySections = [
1506
- { name: "Beverages", options: ["Sprite ($3)", "Thums Up ($3)", "Virgin Mojito ($3)", "Lemonade ($3)", "Blue Lagoon Mocktail ($3)"] },
1507
- { name: "Sauces", options: ["Mint Chutney", "Tomato Sauce"] },
1508
- { name: "Extra Toppings", options: ["Cheese ($2)", "Olives ($1)", "Jalapenos ($1)", "Mushrooms ($2)", "Peppers ($1)"] },
1509
- { name: "Sides", options: ["Fries ($3)", "Salad ($2)", "Garlic Bread ($3)", "Onion Rings ($2)", "Coleslaw ($2)"] },
1510
- { name: "Desserts", options: ["Ice Cream ($3)", "Brownie ($3)", "Cheesecake ($4)", "Gulab Jamun ($3)", "Rasmalai ($4)"] }
1511
- ];
1512
-
1513
- dummySections.forEach(addon => {
1514
- const sectionDiv = document.createElement('div');
1515
- sectionDiv.classList.add('addon-section');
1516
- sectionDiv.setAttribute('data-addon-name', addon.name);
1517
-
1518
- const title = document.createElement('h6');
1519
- title.innerText = addon.name;
1520
- sectionDiv.appendChild(title);
1521
-
1522
- const optionsContainer = document.createElement('div');
1523
- addon.options.forEach((option, index) => {
1524
- const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1525
- const listItem = document.createElement('div');
1526
- listItem.classList.add('form-check');
1527
-
1528
- listItem.innerHTML = `
1529
- <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1530
- data-name="${option}" data-group="${addon.name}" data-price="0">
1531
- <label class="form-check-label" for="${optionId}">
1532
- ${option}
1533
- </label>
1534
- `;
1535
- optionsContainer.appendChild(listItem);
1536
- });
1537
- sectionDiv.appendChild(optionsContainer);
1538
- addonsList.appendChild(sectionDiv);
1539
- });
1540
-
1541
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1542
  .then(response => response.json())
1543
  .then(data => {
1544
  const addonsList = document.getElementById('addons-list');
1545
  addonsList.innerHTML = '';
1546
-
1547
  if (!data.success || !data.addons || data.addons.length === 0) {
1548
  addonsList.innerHTML = '<p>No customization options available.</p>';
1549
- addonsTitle.style.display = 'none';
1550
  return;
1551
  }
1552
-
1553
- if (section.toLowerCase() === 'starters') {
1554
- data.addons.forEach(addon => {
1555
- if (addon.name.toLowerCase() === "choose preparation style") {
1556
- addon.options.forEach(option => {
1557
- const optionId = `addon-prep-style-${option}`;
1558
- const optionHTML = `
1559
- <div class="form-check">
1560
- <input type="checkbox" class="form-check-input" id="${optionId}" value="${option}" data-name="${option}" data-group="Choose Preparation Style">
1561
- <label class="form-check-label" for="${optionId}">
1562
- ${option}
1563
- </label>
1564
- </div>
1565
- `;
1566
- prepStyleOptions.innerHTML += optionHTML;
1567
- });
1568
- }
1569
- if (addon.name.toLowerCase() === "type") {
1570
- addon.options.forEach(option => {
1571
- const optionId = `addon-type-${option}`;
1572
- const optionHTML = `
1573
- <div class="form-check">
1574
- <input type="checkbox" class="form-check-input" id="${optionId}" value="${option}" data-name="${option}" data-group="Type">
1575
- <label class="form-check-label" for="${optionId}">
1576
- ${option}
1577
- </label>
1578
- </div>
1579
- `;
1580
- typeOptions.innerHTML += optionHTML;
1581
- });
1582
- }
1583
- if (addon.name.toLowerCase() === "spice level") {
1584
- addon.options.forEach(option => {
1585
- const optionId = `addon-spice-level-${option}`;
1586
- const optionHTML = `
1587
- <div class="form-check">
1588
- <input type="checkbox" class="form-check-input spice-level-option" id="${optionId}" value="${option}" data-name="${option}" data-group="Spice Level">
1589
- <label class="form-check-label" for="${optionId}">
1590
- ${option}
1591
- </label>
1592
- </div>
1593
- `;
1594
- spiceLevelOptions.innerHTML += optionHTML;
1595
- });
1596
- }
1597
- });
1598
- }
1599
-
1600
  data.addons.forEach(addon => {
1601
- if (section.toLowerCase() === 'starters' &&
1602
- (addon.name.toLowerCase() === "type" ||
1603
- addon.name.toLowerCase() === "spice level" ||
1604
- addon.name.toLowerCase() === "choose preparation style")) {
1605
- return;
1606
- }
1607
-
1608
  const sectionDiv = document.createElement('div');
1609
  sectionDiv.classList.add('addon-section');
1610
- sectionDiv.setAttribute('data-addon-name', addon.name);
1611
-
1612
  const title = document.createElement('h6');
1613
  title.innerText = addon.name;
1614
  sectionDiv.appendChild(title);
1615
-
1616
  const optionsContainer = document.createElement('div');
1617
  addon.options.forEach((option, index) => {
1618
  const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1619
  const listItem = document.createElement('div');
1620
  listItem.classList.add('form-check');
1621
-
1622
  listItem.innerHTML = `
1623
- <input type="checkbox" class="form-check-input ${addon.name.toLowerCase() === 'spice level' ? 'spice-level-option' : 'addon-option'}" id="${optionId}" value="${option}"
1624
  data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1625
  <label class="form-check-label" for="${optionId}">
1626
  ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
@@ -1631,66 +1586,23 @@
1631
  sectionDiv.appendChild(optionsContainer);
1632
  addonsList.appendChild(sectionDiv);
1633
  });
1634
-
1635
- const startersOrder = [
1636
- "Select Dip/Sauce",
1637
- "Extra Add-ons",
1638
- "Make it a Combo"
1639
- ];
1640
-
1641
- const desiredOrder = [
1642
- "Spice Level",
1643
- "Choose Preparation Style",
1644
- "Select Dip/Sauce",
1645
- "Extra Add-ons",
1646
- "Make it a Combo",
1647
- "Type"
1648
- ];
1649
-
1650
- const orderToUse = section.toLowerCase() === 'starters' ? startersOrder : desiredOrder;
1651
-
1652
- const sections = Array.from(addonsList.children);
1653
- addonsList.innerHTML = '';
1654
-
1655
- orderToUse.forEach(sectionName => {
1656
- const section = sections.find(s => s.getAttribute('data-addon-name') === sectionName);
1657
- if (section) {
1658
- addonsList.appendChild(section);
1659
- }
1660
- });
1661
-
1662
- sections.forEach(section => {
1663
- if (!orderToUse.includes(section.getAttribute('data-addon-name'))) {
1664
- addonsList.appendChild(section);
1665
- }
1666
- });
1667
  })
1668
  .catch(err => {
1669
  console.error('Error fetching add-ons:', err);
1670
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1671
- addonsTitle.style.display = 'none';
1672
  });
1673
  }
1674
 
1675
  document.addEventListener('click', function(event) {
1676
- if (event.target.classList.contains('spice-level-option') || event.target.classList.contains('addon-option')) {
1677
  handleAddonClick(event.target);
1678
  }
1679
  });
1680
 
1681
  function handleAddonClick(checkbox) {
1682
  const groupName = checkbox.getAttribute('data-group');
1683
- const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
1684
-
1685
- if (groupName.toLowerCase() === "spice level") {
1686
- const allSpiceLevelCheckboxes = document.querySelectorAll('.spice-level-option');
1687
- allSpiceLevelCheckboxes.forEach(otherCheckbox => {
1688
- if (otherCheckbox !== checkbox) {
1689
- otherCheckbox.checked = false;
1690
- }
1691
- });
1692
- }
1693
- else if (!isMultiSelectGroup) {
1694
  const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1695
  checkboxes.forEach(otherCheckbox => {
1696
  if (otherCheckbox !== checkbox) {
@@ -1712,45 +1624,17 @@
1712
  const section = modalSectionEl.getAttribute('data-section');
1713
  const selectedCategory = modalSectionEl.getAttribute('data-category');
1714
  if (!itemName || !itemPrice || !section || !itemImage) {
1715
- console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage});
1716
  return;
1717
  }
1718
-
1719
- let selectedAddOns = [];
1720
- const addonsListOptions = document.querySelectorAll('#addons-list .addon-option');
1721
- addonsListOptions.forEach(option => {
1722
- if (option.checked) {
1723
- selectedAddOns.push({
1724
- name: option.getAttribute('data-name') || 'Default Name',
1725
- price: parseFloat(option.getAttribute('data-price') || 0)
1726
- });
1727
- }
1728
- });
1729
-
1730
- if (section.toLowerCase() === 'starters') {
1731
- const prepStyleOptions = Array.from(
1732
- document.querySelectorAll('#prep-style-options input[type="checkbox"]:checked')
1733
- ).map(option => ({
1734
- name: option.getAttribute('data-name') || 'Default Prep Style',
1735
- price: 0
1736
- }));
1737
- const typeOptions = Array.from(
1738
- document.querySelectorAll('#type-options input[type="checkbox"]:checked')
1739
- ).map(option => ({
1740
- name: option.getAttribute('data-name') || 'Default Type',
1741
- price: 0
1742
- }));
1743
- const spiceLevelOption = document.querySelector('#spice-level-options input[type="checkbox"].spice-level-option:checked');
1744
- const spiceLevelOptions = spiceLevelOption ? [{
1745
- name: spiceLevelOption.getAttribute('data-name') || 'Default Spice Level',
1746
- price: 0
1747
- }] : [];
1748
- selectedAddOns = [...selectedAddOns, ...prepStyleOptions, ...typeOptions, ...spiceLevelOptions];
1749
- }
1750
-
1751
  const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1752
  const instructions = document.getElementById('modal-instructions').value;
1753
-
1754
  const cartPayload = {
1755
  itemName: itemName,
1756
  itemPrice: itemPrice,
@@ -1761,76 +1645,7 @@
1761
  instructions: instructions,
1762
  quantity: quantity
1763
  };
1764
-
1765
- fetch('/cart/add', {
1766
- method: 'POST',
1767
- headers: {
1768
- 'Content-Type': 'application/json',
1769
- },
1770
- body: JSON.stringify(cartPayload)
1771
- })
1772
- .then(response => {
1773
- if (!response.ok) {
1774
- throw new Error(`HTTP error! Status: ${response.status}`);
1775
- }
1776
- return response.json();
1777
- })
1778
- .then(data => {
1779
- if (data.success) {
1780
- alert('Item added to cart successfully!');
1781
- updateCartUI(data.cart);
1782
- const modal = document.getElementById('itemModal');
1783
- const modalInstance = bootstrap.Modal.getInstance(modal);
1784
- modalInstance.hide();
1785
- } else {
1786
- console.error('Failed to add item to cart:', data.error);
1787
- alert(data.error || 'Failed to add item to cart.');
1788
- }
1789
- })
1790
- .catch(err => {
1791
- console.error('Error adding item to cart:', err);
1792
- alert('An error occurred while adding the item to the cart: ' + err.message);
1793
- const cart = addToCartLocalStorage(cartPayload);
1794
- updateCartUI(cart);
1795
- const modal = document.getElementById('itemModal');
1796
- const modalInstance = bootstrap.Modal.getInstance(modal);
1797
- modalInstance.hide();
1798
- });
1799
- }
1800
-
1801
- function handleSoftDrinkAdd(button) {
1802
- const buttonContainer = button.closest('.button-container');
1803
- const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1804
- const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1805
- const quantityDisplay = quantitySelector.querySelector('.quantity-display');
1806
- const quantityToAddSelect = quantitySelector.querySelector('.quantity-to-add');
1807
- const quantityToAdd = parseInt(quantityToAddSelect.value);
1808
- const itemName = buttonContainer.getAttribute('data-item-name');
1809
- const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1810
- const itemImage = buttonContainer.getAttribute('data-item-image');
1811
- const section = buttonContainer.getAttribute('data-item-section');
1812
- const selectedCategory = buttonContainer.getAttribute('data-item-category');
1813
-
1814
- addButton.style.display = 'none';
1815
- quantitySelector.style.display = 'flex';
1816
-
1817
- let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1818
- currentQuantity += quantityToAdd;
1819
- quantityDisplay.innerText = currentQuantity;
1820
-
1821
- const cartPayload = {
1822
- itemName: itemName,
1823
- itemPrice: itemPrice,
1824
- itemImage: itemImage,
1825
- section: section,
1826
- category: selectedCategory,
1827
- addons: [],
1828
- instructions: '',
1829
- quantity: quantityToAdd
1830
- };
1831
-
1832
- button.disabled = true;
1833
-
1834
  fetch('/cart/add', {
1835
  method: 'POST',
1836
  headers: {
@@ -1838,75 +1653,8 @@
1838
  },
1839
  body: JSON.stringify(cartPayload)
1840
  })
1841
- .then(response => {
1842
- if (!response.ok) {
1843
- throw new Error(`HTTP error! Status: ${response.status}`);
1844
- }
1845
- return response.json();
1846
- })
1847
  .then(data => {
1848
- if (data.success) {
1849
- alert(`Added ${quantityToAdd} item(s) to cart successfully!`);
1850
- updateCartUI(data.cart);
1851
- } else {
1852
- console.error('Failed to add item to cart:', data.error);
1853
- alert(data.error || 'Failed to add item to cart.');
1854
- currentQuantity -= quantityToAdd;
1855
- quantityDisplay.innerText = currentQuantity;
1856
- }
1857
- })
1858
- .catch(err => {
1859
- console.error('Error adding item to cart:', err);
1860
- alert('An error occurred while adding the item to the cart: ' + err.message);
1861
- const cart = addToCartLocalStorage(cartPayload);
1862
- updateCartUI(cart);
1863
- })
1864
- .finally(() => {
1865
- button.disabled = false;
1866
- });
1867
- }
1868
-
1869
- function increaseQuantity(button) {
1870
- const buttonContainer = button.closest('.button-container');
1871
- const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1872
- const quantityToAddSelect = buttonContainer.querySelector('.quantity-to-add');
1873
- const quantityToAdd = parseInt(quantityToAddSelect.value);
1874
- const itemName = buttonContainer.getAttribute('data-item-name');
1875
- const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1876
- const itemImage = buttonContainer.getAttribute('data-item-image');
1877
- const section = buttonContainer.getAttribute('data-item-section');
1878
- const selectedCategory = buttonContainer.getAttribute('data-item-category');
1879
-
1880
- let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1881
- currentQuantity += quantityToAdd;
1882
- quantityDisplay.innerText = currentQuantity;
1883
-
1884
- const cartPayload = {
1885
- itemName: itemName,
1886
- itemPrice: itemPrice,
1887
- itemImage: itemImage,
1888
- section: section,
1889
- category: selectedCategory,
1890
- addons: [],
1891
- instructions: '',
1892
- quantity: quantityToAdd
1893
- };
1894
-
1895
- fetch('/cart/add', {
1896
- method: 'POST',
1897
- headers: {
1898
- 'Content-Type': 'application/json',
1899
- },
1900
- body: JSON.stringify(cartPayload)
1901
- })
1902
- .then(response => {
1903
- if (!response.ok) {
1904
- throw new Error(`HTTP error! Status: ${response.status}`);
1905
- }
1906
- return response.json();
1907
- })
1908
-
1909
- then(data => {
1910
  if (data.success) {
1911
  alert('Item added to cart successfully!');
1912
  updateCartUI(data.cart);
 
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">
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>
 
22
  padding: 0;
23
  display: flex;
24
  flex-direction: column;
25
+ padding-bottom: 70px;
26
  }
27
  .container {
28
  max-width: 900px;
 
37
  flex-direction: column;
38
  opacity: 0;
39
  transition: opacity 0.3s ease-in-out;
40
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
41
  }
42
  .menu-card.visible {
43
  opacity: 1;
44
  }
45
+ .menu-video {
 
 
 
 
46
  height: 200px;
 
 
 
 
 
 
47
  width: 100%;
 
48
  object-fit: cover;
49
+ border-radius: 15px 15px 0 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  opacity: 0;
51
+ transition: opacity 0.5s ease-in-out;
52
+ background-color: #000; /* Fallback color if video fails */
53
  }
54
+ .menu-video.loaded {
 
55
  opacity: 1;
56
  }
57
+ .menu-card:hover .menu-video {
58
+ opacity: 1;
59
+ transform: scale(1.05); /* Slight zoom effect on hover */
60
+ }
61
+ .menu-card .card-body .card-title {
 
 
 
62
  font-size: 1.2rem;
63
+ font-weight: 600;
64
  margin: 10px 0;
65
+ color: #333333;
66
  }
67
+ .menu-card .card-body .card-text.price {
68
  font-size: 1rem;
69
+ font-weight: 500;
70
+ color: #000000;
71
+ margin-bottom: 5px;
72
  }
73
  .addbutton .btn {
74
  background-color: #28a745;
 
125
  box-shadow: none;
126
  transform: scale(0.98);
127
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  .avatar-dropdown-container {
129
  position: absolute;
130
  right: 10px;
 
138
  width: 40px;
139
  height: 40px;
140
  border-radius: 50%;
141
+ background-color: #007bff;
142
  cursor: pointer;
143
  display: flex;
144
  align-items: center;
 
151
  position: absolute;
152
  right: 0;
153
  top: 100%;
154
+ background-color: #fff8f0;
155
  border-radius: 5px;
156
  width: 220px;
157
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
158
  display: none;
159
+ border: 1px solid #ffd8b1;
160
  }
161
  .dropdown-menu .dropdown-item {
162
  padding: 12px 16px;
163
  text-decoration: none;
164
  color: #333;
165
+ border-bottom: 1px solid #ffd8b1;
166
  display: block;
167
  font-size: 15px;
168
+ transition: background-color 0.2s ease;
169
  }
170
  .dropdown-menu .dropdown-item:last-child {
171
  border-bottom: none;
172
  }
173
  .dropdown-menu .dropdown-item:hover {
174
+ background-color: #ffe4c4;
175
+ color: #333;
176
  }
177
  .fixed-top-bar {
178
  position: relative;
 
197
  align-items: center;
198
  width: 300px;
199
  max-width: 90%;
200
+ position: relative;
201
  }
202
  .search-bar-container input {
203
  width: 100%;
204
+ padding: 8px 40px 8px 40px;
205
  font-size: 16px;
206
  border-radius: 25px;
207
  border: none;
 
254
  }
255
  .addon-section {
256
  background-color: #fff;
257
+ border: 2px solid #ffa500;
258
  border-radius: 8px;
259
  padding: 12px;
260
  margin-bottom: 10px;
 
334
  font-weight: bold;
335
  text-align: center;
336
  margin-bottom: 5px;
337
+ color: #333333;
338
  }
339
  .modal-body #modal-price {
340
  font-size: 16px;
341
+ font-weight: 500;
342
+ color: #000000;
343
  text-align: center;
344
  margin-bottom: 10px;
345
  }
 
408
  font-size: 0.9rem;
409
  font-weight: bold;
410
  margin-bottom: 5px;
411
+ color: #333333;
412
  }
413
  .item-details p {
414
  font-size: 0.85rem;
 
505
  .modal-body::-webkit-scrollbar-thumb:hover {
506
  background: #0D9232;
507
  }
508
+ .btn-primary:disabled {
509
+ opacity: 0.65;
510
+ cursor: not-allowed;
511
+ }
512
+ .quantity-selector select {
513
+ width: 60px;
514
+ height: 35px;
515
+ padding: 5px;
516
+ border-radius: 5px;
517
+ border: 1px solid #ced4da;
518
+ }
519
+ #custom-dish-form {
520
+ position: relative;
521
+ padding-bottom: 80px;
522
+ }
523
+ #custom-dish-form .btn-primary {
524
+ position: absolute;
525
+ right: 15px;
526
+ bottom: 15px;
527
+ width: auto;
528
+ padding: 10px 20px;
529
+ }
530
+ .bottom-action-bar {
531
+ position: fixed;
532
+ bottom: 0;
533
+ left: 0;
534
+ right: 0;
535
+ background-color: white;
536
+ padding: 10px 20px;
537
+ display: flex;
538
+ justify-content: space-between;
539
+ align-items: center;
540
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
541
+ z-index: 1000;
542
+ max-width: 900px;
543
+ margin: 0 auto;
544
+ }
545
+ .bottom-action-bar .btn {
546
+ flex: 1;
547
+ margin: 0 5px;
548
+ padding: 10px 15px;
549
+ border-radius: 8px;
550
+ font-weight: bold;
551
+ font-size: 16px;
552
+ color: white;
553
+ display: flex;
554
+ align-items: center;
555
+ justify-content: center;
556
+ text-align: center;
557
+ min-width: 0;
558
+ white-space: nowrap;
559
+ }
560
+ .bottom-action-bar .btn-order-history {
561
+ background-color: #FFA07A;
562
+ border-color: #FFA07A;
563
+ }
564
+ .bottom-action-bar .btn-order-history:hover {
565
+ background-color: #FF8C61;
566
+ border-color: #FF8C61;
567
+ }
568
+ .bottom-action-bar .btn-view-cart {
569
+ background-color: #0FAA39;
570
+ border-color: #0FAA39;
571
+ }
572
+ .bottom-action-bar .btn-view-cart:hover {
573
+ background-color: #0D9232;
574
+ border-color: #0D9232;
575
+ }
576
+ .cart-icon-badge {
577
+ background-color: white;
578
+ color: #0FAA39;
579
+ border-radius: 50%;
580
+ width: 20px;
581
+ height: 20px;
582
+ display: inline-flex;
583
+ align-items: center;
584
+ justify-content: center;
585
+ font-size: 12px;
586
+ margin-left: 8px;
587
+ }
588
  @media (max-width: 576px) {
589
  .fixed-top-bar {
590
  height: 60px;
 
598
  transform: translateY(-50%);
599
  }
600
  .search-bar-container input {
601
+ padding: 6px 35px 6px 35px;
602
  font-size: 14px;
603
  border-radius: 20px;
604
  }
 
727
  height: 18px;
728
  font-size: 9px;
729
  }
730
+ .quantity-selector select {
731
+ width: 50px;
732
+ height: 30px;
733
+ font-size: 12px;
734
+ }
735
+ .bottom-action-bar {
736
+ padding: 8px 10px;
737
+ }
738
+ .bottom-action-bar .btn {
739
+ padding: 8px 10px;
740
  font-size: 14px;
741
  }
742
  .cart-icon-badge {
743
+ width: 18px;
744
+ height: 18px;
745
  font-size: 10px;
746
+ margin-left: 5px;
 
 
 
 
747
  }
748
  }
749
  </style>
 
794
  <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
795
  <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
796
  </div>
797
+ <button type="submit" class="btn btn-primary">Submit Custom Dish</button>
798
  </form>
799
  </div>
800
  {% else %}
801
+ {% if ordered_menu.items()|length == 0 %}
802
+ <p>No menu items available for this category.</p>
803
+ {% else %}
804
+ {% for section, items in ordered_menu.items() %}
805
+ <h3>{{ section }}</h3>
806
+ <div class="row">
807
+ {% for item in items %}
808
+ <div class="col-md-6 mb-4">
809
+ <div class="card menu-card">
810
  <video
811
+ class="card-img-top menu-video"
812
+ muted
813
+ loop
814
+ preload="auto"
815
+ data-src="{{ item.Video1__c | default('/static/placeholder.mp4') }}"
816
+ poster="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
817
+ width="350"
818
+ height="200"
819
+ onmouseover="this.play()"
820
+ onmouseout="this.pause(); this.currentTime = 0;"
821
+ onerror="this.poster='/static/placeholder.jpg';">
822
+ <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
823
  Your browser does not support the video tag.
824
  </video>
825
+ <div class="addbutton">
826
+ <div class="card-body d-flex align-items-center justify-content-between">
827
+ <div>
828
+ <h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5>
829
+ <p class="card-text">${{ item.Price__c | default('0.00') }}</p>
830
+ </div>
831
+ <div class="d-flex flex-column align-item-center justify-content-center">
832
+ <div class="button-container"
833
+ data-item-name="{{ item.Name | default('Unnamed Item') }}"
834
+ data-item-price="{{ item.Price__c | default('0.00') }}"
835
+ data-item-image="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
836
+ data-item-section="{{ item.Section__c | default(section) }}"
837
+ data-item-category="{{ selected_category }}">
838
+ {% if item.Section__c == 'Soft Drinks' %}
839
+ <button class="btn btn-primary add-to-cart-btn" onclick="handleSoftDrinkAdd(this)">ADD</button>
840
+ <div class="quantity-selector" style="display: none;">
841
+ <button class="btn btn-outline-secondary decrease-btn" onclick="decreaseQuantity(this)">-</button>
842
+ <span class="quantity-display">0</span>
843
+ <button class="btn btn-outline-secondary increase-btn" onclick="increaseQuantity(this)">+</button>
844
+ </div>
845
+ {% else %}
846
+ <button class="btn btn-primary"
847
+ data-bs-toggle="modal"
848
+ data-bs-target="#itemModal"
849
+ 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 }}')">
850
+ ADD
851
+ </button>
852
+ {% endif %}
853
+ {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
854
+ <span class="customisable-text">Customisable</span>
855
+ {% endif %}
856
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
857
  </div>
858
  </div>
859
  </div>
860
+ <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
861
+ <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}"></div>
862
  </div>
 
 
863
  </div>
864
+ {% endfor %}
865
+ </div>
866
+ {% endfor %}
867
+ {% endif %}
868
  {% endif %}
869
  </div>
870
 
871
+ <div class="bottom-action-bar">
872
+ <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history">
873
+ <i class="bi bi-clock-history"></i> Order History
874
+ </a>
875
+ <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart">
876
+ <i class="bi bi-cart"></i> View Cart
877
+ <span class="cart-icon-badge" id="cart-item-count">0</span>
878
  </a>
879
  </div>
880
 
 
887
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
888
  </div>
889
  <div class="modal-body">
 
890
  <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" style="max-height: 200px; object-fit: cover;">
 
891
  <h5 id="modal-name" class="fw-bold text-center"></h5>
 
892
  <p id="modal-price" class="text-muted text-center"></p>
 
893
  <p id="modal-description" class="text-secondary"></p>
 
894
  <div id="modal-addons" class="modal-addons mt-4">
895
  <h6>Customization Options</h6>
896
  <div id="addons-list" class="addons-container">Loading customization options...</div>
 
901
  </div>
902
  <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
903
  </div>
 
904
  <div class="modal-footer d-flex align-items-center justify-content-between">
 
905
  <div class="d-flex align-items-center gap-2">
906
  <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
907
  <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
908
  <button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
909
  </div>
 
910
  <button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
911
  </div>
912
  </div>
913
  </div>
914
  </div>
915
 
 
916
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
917
  <script>
918
+ let isProcessingRequest = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
919
 
 
920
  const menuItems = [
921
  {% for section, items in ordered_menu.items() %}
922
  {% for item in items %}
923
+ "{{ item.Name | default('Unnamed Item') }}",
924
  {% endfor %}
925
  {% endfor %}
926
  ];
 
1042
  JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1043
  );
1044
  if (existingItem) {
1045
+ existingItem.quantity = payload.quantity;
1046
  } else {
1047
  cart.push(payload);
1048
  }
 
1072
  return JSON.parse(localStorage.getItem('cart')) || [];
1073
  }
1074
 
1075
+ function debounce(func, wait) {
1076
+ let timeout;
1077
+ return function (...args) {
1078
+ clearTimeout(timeout);
1079
+ timeout = setTimeout(() => func.apply(this, args), wait);
1080
+ };
1081
+ }
1082
+
1083
+ function handleSoftDrinkAdd(button) {
1084
+ const buttonContainer = button.closest('.button-container');
1085
+ const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1086
+ const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1087
+ const quantityDisplay = quantitySelector.querySelector('.quantity-display');
1088
 
1089
+ addButton.style.display = 'none';
1090
+ quantitySelector.style.display = 'flex';
1091
+
1092
+ if (parseInt(quantityDisplay.innerText) === 0) {
1093
+ quantityDisplay.innerText = '1';
1094
+ updateCartQuantity(buttonContainer, 1);
1095
+ }
1096
+ }
1097
+
1098
+ function increaseQuantity(button) {
1099
+ if (isProcessingRequest) return;
1100
+ isProcessingRequest = true;
1101
+
1102
+ const buttonContainer = button.closest('.button-container');
1103
+ const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1104
+ let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1105
+
1106
+ if (currentQuantity < 10) {
1107
+ const newQuantity = currentQuantity + 1;
1108
+ quantityDisplay.innerText = newQuantity;
1109
+ debouncedUpdateCart(buttonContainer, newQuantity);
1110
+ } else {
1111
+ console.log('Maximum quantity of 10 reached for Soft Drinks');
1112
+ isProcessingRequest = false;
1113
+ }
1114
+ }
1115
+
1116
+ function decreaseQuantity(button) {
1117
+ if (isProcessingRequest) return;
1118
+ isProcessingRequest = true;
1119
+
1120
+ const buttonContainer = button.closest('.button-container');
1121
+ const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1122
+ const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1123
+ const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1124
+ const currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1125
+
1126
+ if (currentQuantity <= 1) {
1127
+ quantityDisplay.innerText = '0';
1128
+ addButton.style.display = 'block';
1129
+ quantitySelector.style.display = 'none';
1130
+ updateCartQuantity(buttonContainer, 0);
1131
+ } else {
1132
+ const newQuantity = currentQuantity - 1;
1133
+ quantityDisplay.innerText = newQuantity;
1134
+ debouncedUpdateCart(buttonContainer, newQuantity);
1135
+ }
1136
+ }
1137
+
1138
+ function updateCartQuantity(buttonContainer, newQuantity) {
1139
+ const itemName = buttonContainer.getAttribute('data-item-name');
1140
+ const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1141
+ const itemImage = buttonContainer.getAttribute('data-item-image');
1142
+ const section = buttonContainer.getAttribute('data-item-section');
1143
+ const selectedCategory = buttonContainer.getAttribute('data-item-category');
1144
+
1145
+ const cartPayload = {
1146
+ itemName: itemName,
1147
+ itemPrice: itemPrice,
1148
+ itemImage: itemImage,
1149
+ section: section,
1150
+ category: selectedCategory,
1151
+ addons: [],
1152
+ instructions: '',
1153
+ quantity: newQuantity
1154
+ };
1155
 
1156
+ if (newQuantity > 0) {
1157
+ fetch('/cart/add', {
1158
+ method: 'POST',
1159
+ headers: {
1160
+ 'Content-Type': 'application/json',
1161
+ },
1162
+ body: JSON.stringify(cartPayload)
1163
+ })
1164
+ .then(response => response.json())
1165
+ .then(data => {
1166
+ if (data.success) {
1167
+ updateCartUI(data.cart);
1168
+ } else {
1169
+ console.error('Failed to update cart:', data.error);
1170
+ const cart = addToCartLocalStorage(cartPayload);
1171
+ updateCartUI(cart);
1172
  }
1173
+ })
1174
+ .catch(err => {
1175
+ console.error('Error updating cart:', err);
1176
+ const cart = addToCartLocalStorage(cartPayload);
1177
+ updateCartUI(cart);
1178
+ })
1179
+ .finally(() => {
1180
+ isProcessingRequest = false;
1181
  });
1182
+ } else {
1183
+ fetch(`/cart/remove?item_name=${encodeURIComponent(itemName)}&instructions=&addons=[]`, {
1184
+ method: 'POST',
1185
+ headers: {
1186
+ 'Content-Type': 'application/json',
 
 
 
1187
  }
1188
+ })
1189
+ .then(response => response.json())
1190
+ .then(data => {
1191
+ if (data.success) {
1192
+ updateCartUI(data.cart);
1193
+ } else {
1194
+ console.error('Failed to remove item:', data.error);
1195
+ const cart = removeFromCartLocalStorage(itemName, 1, '', []);
1196
+ updateCartUI(cart);
1197
+ }
1198
+ })
1199
+ .catch(err => {
1200
+ console.error('Error removing item:', err);
1201
+ const cart = removeFromCartLocalStorage(itemName, 1, '', []);
1202
+ updateCartUI(cart);
1203
+ })
1204
+ .finally(() => {
1205
+ isProcessingRequest = false;
1206
  });
1207
+ }
1208
+ }
1209
+
1210
+ const debouncedUpdateCart = debounce(updateCartQuantity, 300);
1211
+
1212
+ function updateCartUI(cart) {
1213
+ if (!Array.isArray(cart)) {
1214
+ console.error('Invalid cart data:', cart);
1215
+ return;
1216
+ }
1217
+
1218
+ let totalQuantity = 0;
1219
+ cart.forEach(item => {
1220
+ totalQuantity += item.quantity;
1221
  });
1222
 
1223
+ const cartItemCount = document.getElementById('cart-item-count');
1224
+ if (cartItemCount) {
1225
+ cartItemCount.innerText = totalQuantity;
1226
+ cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1227
  }
1228
 
1229
+ const buttonContainers = document.querySelectorAll('.button-container');
1230
+ buttonContainers.forEach(container => {
1231
+ const itemName = container.getAttribute('data-item-name');
1232
+ const section = container.getAttribute('data-item-section');
1233
+ const quantityDisplay = container.querySelector('.quantity-display');
1234
+ const addButton = container.querySelector('.add-to-cart-btn');
1235
+ const quantitySelector = container.querySelector('.quantity-selector');
1236
+
1237
+ const cartItem = cart.find(item =>
1238
+ item.itemName === itemName &&
1239
+ item.section === section &&
1240
+ item.instructions === '' &&
1241
+ JSON.stringify(item.addons) === JSON.stringify([])
1242
+ );
1243
+
1244
+ if (cartItem && cartItem.quantity > 0) {
1245
+ quantityDisplay.innerText = cartItem.quantity;
1246
+ addButton.style.display = 'none';
1247
+ quantitySelector.style.display = 'flex';
1248
+ } else {
1249
+ quantityDisplay.innerText = '0';
1250
+ addButton.style.display = 'block';
1251
+ quantitySelector.style.display = 'none';
1252
+ }
1253
+ });
1254
+ }
1255
+
1256
+ document.addEventListener('DOMContentLoaded', function () {
1257
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1258
  const dropdownMenu = document.querySelector('.dropdown-menu');
 
1259
  avatarContainer.addEventListener('click', function (event) {
1260
  event.stopPropagation();
1261
  dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1262
  });
 
1263
  document.addEventListener('click', function (event) {
1264
  if (!avatarContainer.contains(event.target)) {
1265
  dropdownMenu.style.display = 'none';
1266
  }
1267
  });
 
1268
  const dropdownItems = document.querySelectorAll('.dropdown-item');
1269
  dropdownItems.forEach(item => {
1270
  item.addEventListener('click', function () {
1271
  dropdownMenu.style.display = 'none';
1272
  });
1273
  });
 
1274
  const menuCards = document.querySelectorAll('.menu-card');
1275
  menuCards.forEach(card => {
1276
  const itemName = card.querySelector('.card-title').innerText;
1277
  const detailsDiv = card.querySelector('.item-details');
1278
  const detailsData = menuItemDetails[itemName];
 
1279
  if (detailsData) {
1280
  detailsDiv.innerHTML = `
1281
  <h6>Ingredients</h6>
 
1294
  detailsDiv.innerHTML = '<p>No details available for this item.</p>';
1295
  }
1296
  });
1297
+ const menuVideos = document.querySelectorAll('.menu-video');
1298
  const cardObserver = new IntersectionObserver((entries, observer) => {
1299
  entries.forEach(entry => {
1300
  if (entry.isIntersecting) {
 
1307
  rootMargin: '0px',
1308
  threshold: 0.1
1309
  });
1310
+ const videoObserver = new IntersectionObserver((entries, observer) => {
1311
+ entries.forEach(entry => {
1312
+ if (entry.isIntersecting) {
1313
+ const video = entry.target;
1314
+ const src = video.getAttribute('data-src');
1315
+ if (src && !video.querySelector('source[src="' + src + '"]')) {
1316
+ const source = video.querySelector('source');
1317
+ source.src = src;
1318
+ video.load();
1319
+ }
1320
+ video.classList.add('loaded');
1321
+ observer.unobserve(video);
1322
+ }
1323
+ });
1324
+ }, {
1325
+ root: null,
1326
+ rootMargin: '200px',
1327
+ threshold: 0.01
1328
+ });
1329
  menuCards.forEach(card => cardObserver.observe(card));
1330
+ menuVideos.forEach(video => videoObserver.observe(video));
1331
  const toggleLinks = document.querySelectorAll('.toggle-details');
1332
  toggleLinks.forEach(link => {
1333
  link.addEventListener('click', function () {
 
1337
  this.innerText = detailsDiv.classList.contains('show') ? 'Hide Details' : 'Show Details';
1338
  });
1339
  });
 
1340
  const categoryButtons = document.querySelectorAll('.category-button');
1341
  const categoryForm = document.getElementById('categoryForm');
1342
  const selectedCategoryInput = document.getElementById('selectedCategoryInput');
 
1343
  if (!selectedCategoryInput.value) {
1344
  selectedCategoryInput.value = "All";
1345
  document.querySelector('.category-button[data-category="All"]').classList.add('selected');
1346
  }
 
1347
  categoryButtons.forEach(button => {
1348
  button.addEventListener('click', function () {
1349
  categoryButtons.forEach(btn => btn.classList.remove('selected'));
 
1352
  categoryForm.submit();
1353
  });
1354
  });
 
1355
  const searchBar = document.getElementById('searchBar');
1356
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
 
1357
  searchBar.addEventListener('input', function () {
1358
  const input = this.value.trim().toLowerCase();
1359
  suggestionsContainer.innerHTML = '';
1360
  suggestionsContainer.style.display = 'none';
 
1361
  if (input) {
1362
  const filteredItems = menuItems.filter(item =>
1363
  item.toLowerCase().includes(input)
 
1379
  }
1380
  filterMenu();
1381
  });
 
1382
  document.addEventListener('click', function (event) {
1383
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1384
  suggestionsContainer.style.display = 'none';
1385
  }
1386
  });
 
1387
  const descriptionTextarea = document.getElementById('custom-dish-description');
1388
  const descriptionSuggestions = document.getElementById('descriptionSuggestions');
 
1389
  if (descriptionTextarea && descriptionSuggestions) {
1390
  let usedIngredients = new Set();
 
1391
  function updateUsedIngredients() {
1392
  const inputText = descriptionTextarea.value.trim();
1393
  usedIngredients.clear();
 
1400
  });
1401
  }
1402
  }
 
1403
  descriptionTextarea.addEventListener('input', function () {
1404
  const inputText = this.value.trim();
1405
  const words = inputText.split(/,\s*/);
1406
  const lastWord = words[words.length - 1].trim().toLowerCase();
1407
  descriptionSuggestions.innerHTML = '';
1408
  descriptionSuggestions.style.display = 'none';
 
1409
  updateUsedIngredients();
 
1410
  if (lastWord) {
1411
  const filteredIngredients = ingredientsList.filter(ingredient =>
1412
  ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
 
1431
  }
1432
  }
1433
  });
 
1434
  document.addEventListener('click', function (event) {
1435
  if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1436
  descriptionSuggestions.style.display = 'none';
1437
  }
1438
  });
1439
  }
 
1440
  fetch('/cart/get')
1441
  .then(response => {
1442
  if (!response.ok) {
 
1458
  const cart = getCartLocalStorage();
1459
  updateCartUI(cart);
1460
  });
1461
+ const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1462
+ preloadedVideos.forEach(link => {
1463
+ const video = document.createElement('video');
1464
+ video.src = link.href;
1465
+ video.preload = 'auto';
1466
  });
 
1467
  const decreaseBtn = document.getElementById('decreaseQuantity');
1468
  const increaseBtn = document.getElementById('increaseQuantity');
1469
  const quantityInput = document.getElementById('quantityInput');
 
1470
  decreaseBtn.addEventListener('click', function () {
1471
  let currentQuantity = parseInt(quantityInput.value);
1472
  if (currentQuantity > 1) {
 
1474
  quantityInput.value = currentQuantity;
1475
  }
1476
  });
 
1477
  increaseBtn.addEventListener('click', function () {
1478
  let currentQuantity = parseInt(quantityInput.value);
1479
  currentQuantity++;
1480
  quantityInput.value = currentQuantity;
1481
  });
 
1482
 
1483
+ const micIcon = document.getElementById('micIcon');
1484
+ if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
1485
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1486
+ const recognition = new SpeechRecognition();
1487
+ recognition.lang = 'en-US';
1488
+ recognition.onstart = () => micIcon.classList.add('active');
1489
+ recognition.onresult = (event) => {
1490
+ searchBar.value = event.results[0][0].transcript.trim();
1491
+ filterMenu();
1492
+ };
1493
+ recognition.onend = () => micIcon.classList.remove('active');
1494
+ recognition.onerror = (event) => {
1495
+ micIcon.classList.remove('active');
1496
+ console.error('Speech error:', event.error);
1497
+ };
1498
+ micIcon.addEventListener('click', () => {
1499
+ recognition.start();
1500
+ });
1501
+ } else {
1502
+ micIcon.style.display = 'none';
1503
+ }
1504
+ });
1505
 
1506
  function filterMenu() {
1507
  const input = document.getElementById('searchBar').value.trim().toLowerCase();
1508
  const sections = document.querySelectorAll('h3');
1509
  const items = document.querySelectorAll('.menu-card');
1510
  let matchedSections = new Set();
 
1511
  items.forEach(item => {
1512
  const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1513
  const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
 
1514
  if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1515
  item.style.display = 'block';
1516
  item.classList.add('visible');
 
1519
  item.style.display = 'none';
1520
  }
1521
  });
 
1522
  sections.forEach(section => {
1523
  const sectionRow = section.nextElementSibling;
1524
  if (matchedSections.has(sectionRow)) {
 
1529
  sectionRow.style.display = 'none';
1530
  }
1531
  });
 
1532
  if (!input) {
1533
  sections.forEach(section => {
1534
  section.style.display = 'block';
 
1545
  document.getElementById('modal-name').innerText = name;
1546
  document.getElementById('modal-price').innerText = `$${price}`;
1547
  const modalImg = document.getElementById('modal-img');
1548
+ modalImg.src = image || '/static/placeholder.jpg';
 
 
 
 
 
1549
  document.getElementById('modal-description').innerText = description || 'No description available.';
 
 
 
 
 
 
 
 
 
1550
  document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1551
  document.getElementById('modal-instructions').value = '';
1552
  const modalSectionEl = document.getElementById('modal-section');
 
1554
  modalSectionEl.setAttribute('data-category', selectedCategory);
1555
  document.getElementById('quantityInput').value = 1;
1556
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1557
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1558
  .then(response => response.json())
1559
  .then(data => {
1560
  const addonsList = document.getElementById('addons-list');
1561
  addonsList.innerHTML = '';
 
1562
  if (!data.success || !data.addons || data.addons.length === 0) {
1563
  addonsList.innerHTML = '<p>No customization options available.</p>';
 
1564
  return;
1565
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1566
  data.addons.forEach(addon => {
 
 
 
 
 
 
 
1567
  const sectionDiv = document.createElement('div');
1568
  sectionDiv.classList.add('addon-section');
 
 
1569
  const title = document.createElement('h6');
1570
  title.innerText = addon.name;
1571
  sectionDiv.appendChild(title);
 
1572
  const optionsContainer = document.createElement('div');
1573
  addon.options.forEach((option, index) => {
1574
  const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1575
  const listItem = document.createElement('div');
1576
  listItem.classList.add('form-check');
 
1577
  listItem.innerHTML = `
1578
+ <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1579
  data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1580
  <label class="form-check-label" for="${optionId}">
1581
  ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
 
1586
  sectionDiv.appendChild(optionsContainer);
1587
  addonsList.appendChild(sectionDiv);
1588
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1589
  })
1590
  .catch(err => {
1591
  console.error('Error fetching add-ons:', err);
1592
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
 
1593
  });
1594
  }
1595
 
1596
  document.addEventListener('click', function(event) {
1597
+ if (event.target.classList.contains('addon-option')) {
1598
  handleAddonClick(event.target);
1599
  }
1600
  });
1601
 
1602
  function handleAddonClick(checkbox) {
1603
  const groupName = checkbox.getAttribute('data-group');
1604
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo"].includes(groupName);
1605
+ if (!isMultiSelectGroup) {
 
 
 
 
 
 
 
 
 
1606
  const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1607
  checkboxes.forEach(otherCheckbox => {
1608
  if (otherCheckbox !== checkbox) {
 
1624
  const section = modalSectionEl.getAttribute('data-section');
1625
  const selectedCategory = modalSectionEl.getAttribute('data-category');
1626
  if (!itemName || !itemPrice || !section || !itemImage) {
1627
+ console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage });
1628
  return;
1629
  }
1630
+ const selectedAddOns = Array.from(
1631
+ document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
1632
+ ).map(addon => ({
1633
+ name: addon.getAttribute('data-name') || 'Default Name',
1634
+ price: parseFloat(addon.getAttribute('data-price') || 0)
1635
+ }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1636
  const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1637
  const instructions = document.getElementById('modal-instructions').value;
 
1638
  const cartPayload = {
1639
  itemName: itemName,
1640
  itemPrice: itemPrice,
 
1645
  instructions: instructions,
1646
  quantity: quantity
1647
  };
1648
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1649
  fetch('/cart/add', {
1650
  method: 'POST',
1651
  headers: {
 
1653
  },
1654
  body: JSON.stringify(cartPayload)
1655
  })
1656
+ .then(response => response.json())
 
 
 
 
 
1657
  .then(data => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1658
  if (data.success) {
1659
  alert('Item added to cart successfully!');
1660
  updateCartUI(data.cart);