lokesh341 commited on
Commit
d454c12
·
verified ·
1 Parent(s): 0d7a729

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +497 -327
templates/menu.html CHANGED
@@ -52,8 +52,6 @@
52
  opacity: 0;
53
  transition: opacity 0.5s ease-in-out, transform 0.2s ease;
54
  background-color: #000;
55
- font-weight: bold;
56
- border: none;
57
  }
58
  .menu-video.loaded {
59
  opacity: 1;
@@ -130,6 +128,13 @@
130
  border-color: #0D9232;
131
  transform: scale(1.05);
132
  }
 
 
 
 
 
 
 
133
  .avatar-dropdown-container {
134
  position: absolute;
135
  right: 10px;
@@ -178,7 +183,7 @@
178
  .dropdown-menu .dropdown-item {
179
  padding: 10px 16px;
180
  text-decoration: none;
181
- color: #2c2c2c;
182
  font-size: 14px;
183
  transition: background-color 0.2s ease;
184
  cursor: pointer;
@@ -212,8 +217,7 @@
212
  max-width: 90%;
213
  background-color: #fff;
214
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
215
- outline: none;
216
- transition: box-shadow 0.3s ease;
217
  }
218
  .search-bar-container input {
219
  width: 100%;
@@ -222,14 +226,13 @@
222
  border-radius: 25px;
223
  border: none;
224
  background-color: transparent;
 
225
  }
226
  .search-bar-container input:focus {
227
  box-shadow: 0 4px 8px rgba(0,0,0,0.15);
228
  }
229
  .search-bar-container input::placeholder {
230
  color: #888;
231
- font-size: 14px;
232
- transition: background-color 0.2s ease;
233
  }
234
  .search-icon {
235
  position: absolute;
@@ -243,6 +246,7 @@
243
  font-size: 18px;
244
  color: #888;
245
  cursor: pointer;
 
246
  }
247
  .mic-icon.active {
248
  color: #007bff;
@@ -268,9 +272,215 @@
268
  z-index: 1000;
269
  display: none;
270
  }
 
 
 
 
 
 
271
  .autocomplete-suggestions .suggestion-item:hover {
272
  background-color: #f1f1f1;
273
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
  #custom-dish-form {
275
  position: relative;
276
  padding: 20px;
@@ -306,7 +516,7 @@
306
  #custom-dish-form::-webkit-scrollbar-thumb:hover {
307
  background: #0D9232;
308
  }
309
- #dishNameSuggestions {
310
  position: absolute;
311
  width: calc(100% - 30px);
312
  max-height: 200px;
@@ -318,12 +528,6 @@
318
  display: none;
319
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
320
  }
321
- .selection-buttons .btn {
322
- width: 100px;
323
- }
324
- .chat-bar {
325
- margin-top: 20px;
326
- }
327
  .bottom-action-bar {
328
  position: fixed;
329
  bottom: 0;
@@ -338,18 +542,34 @@
338
  z-index: 1000;
339
  max-width: 900px;
340
  margin: 0 auto;
341
- text-align: center;
342
- transition: transform 0.2s ease;
343
  }
344
- .bottom-action-bar .btn:hover {
345
- transform: scale(1.05);
 
 
 
 
 
 
 
 
 
346
  }
347
  .bottom-action-bar .btn-order-history {
348
  background-color: #FFA07A;
349
  border-color: #FFA07A;
350
- font-size: 12px;
351
- margin-left: 8px;
352
- transition: all 0.3s ease;
 
 
 
 
 
 
 
 
 
353
  }
354
  .cart-icon-badge {
355
  background-color: white;
@@ -387,6 +607,19 @@
387
  color: #ff4444;
388
  animation: pulse 1.5s infinite;
389
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
390
  @keyframes pulse {
391
  0% { transform: scale(1); }
392
  50% { transform: scale(1.1); }
@@ -400,6 +633,15 @@
400
  .menu-card {
401
  max-width: 100%;
402
  }
 
 
 
 
 
 
 
 
 
403
  }
404
  </style>
405
  </head>
@@ -453,45 +695,18 @@
453
  {% if selected_category == "Customized Dish" %}
454
  <div id="custom-dish-form" class="mt-4">
455
  <h3>Create Your Custom Dish</h3>
456
- <div class="selection-buttons mb-3">
457
- <button id="veg-btn" class="btn btn-outline-success me-2" aria-label="Select Vegetarian">Veg</button>
458
- <button id="non-veg-btn" class="btn btn-outline-danger" aria-label="Select Non-Vegetarian">Non-Veg</button>
459
- </div>
460
- <form id="customDishForm" role="form">
461
- <div class="mb-3 position-relative">
462
  <label for="custom-dish-name" class="form-label">Dish Name</label>
463
  <input type="text" class="form-control" id="custom-dish-name" name="name" required autocomplete="off" placeholder="e.g., Spicy Chicken Curry" aria-label="Custom Dish Name">
464
- <div id="dishNameSuggestions" class="autocomplete-suggestions"></div>
465
  </div>
466
- <div class="mb-3">
467
- <label for="custom-dish-ingredients" class="form-label">Ingredients List</label>
468
- <textarea class="form-control" id="custom-dish-ingredients" name="ingredients" required rows="3" placeholder="e.g., Chicken, Tomatoes, Spices" aria-label="Ingredients List"></textarea>
469
- </div>
470
- <div class="mb-3">
471
  <label for="custom-dish-description" class="form-label">Dish Description</label>
472
- <textarea class="form-control" id="custom-dish-description" name="description" required rows="3" placeholder="e.g., A spicy and flavorful chicken dish" aria-label="Dish Description"></textarea>
 
473
  </div>
474
- <div class="mb-3">
475
- <label for="custom-dish-price" class="form-label">Price ($)</label>
476
- <input type="number" class="form-control" id="custom-dish-price" name="price" step="0.01" min="0.01" required placeholder="e.g., 12.99" aria-label="Price">
477
- </div>
478
- <button type="submit" class="btn btn-primary" id="submitCustomDish" aria-label="Submit to Salesforce">Submit to Salesforce</button>
479
- <div id="form-feedback" class="mt-3" style="display: none; text-align: center;"></div>
480
  </form>
481
- <div id="item-details" class="mt-3" style="display: none; background: #f8f9fa; padding: 15px; border-radius: 8px;">
482
- <h4>Selected Item Details</h4>
483
- <p><strong>Name:</strong> <span id="detail-name"></span></p>
484
- <p><strong>Ingredients:</strong> <span id="detail-ingredients"></span></p>
485
- <p><strong>Description:</strong> <span id="detail-description"></span></p>
486
- <p><strong>Price:</strong> $<span id="detail-price"></span></p>
487
- </div>
488
- <div class="chat-bar mt-3">
489
- <div class="input-group">
490
- <input type="text" class="form-control" id="chat-input" placeholder="Type 'hi' or 'veg'/'non-veg'..." aria-label="Chat Input">
491
- <button class="btn btn-primary" id="send-btn" aria-label="Send Message">Send</button>
492
- </div>
493
- <div id="chat-feedback" class="mt-2" style="text-align: center;"></div>
494
- </div>
495
  </div>
496
  {% else %}
497
  {% if ordered_menu.items()|length == 0 %}
@@ -680,25 +895,6 @@
680
  <button class="mic-popup-cancel" id="micPopupCancel" aria-label="Cancel Voice Search">Cancel</button>
681
  </div>
682
 
683
- <!-- Confirmation Modal -->
684
- <div class="modal fade" id="confirmationModal" tabindex="-1" aria-labelledby="confirmationModalLabel" aria-hidden="true">
685
- <div class="modal-dialog modal-dialog-centered">
686
- <div class="modal-content">
687
- <div class="modal-header">
688
- <h5 class="modal-title" id="confirmationModalLabel">Confirm Action</h5>
689
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
690
- </div>
691
- <div class="modal-body">
692
- <p id="confirmationMessage"></p>
693
- </div>
694
- <div class="modal-footer">
695
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-label="Cancel">Cancel</button>
696
- <button type="button" class="btn btn-primary" id="confirmAction" aria-label="Confirm">Confirm</button>
697
- </div>
698
- </div>
699
- </div>
700
- </div>
701
-
702
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
703
  <script>
704
  let isProcessingRequest = false;
@@ -718,22 +914,21 @@
718
  {% endfor %}
719
  ];
720
 
721
- const dishDatabase = {
722
- veg: {
723
- "Aloo Paratha": { ingredients: "Wheat flour, potatoes, spices (cumin, coriander), ghee", description: "Stuffed potato flatbread cooked with ghee, a North Indian breakfast favorite.", price: 8.99 },
724
- "Dosa": { ingredients: "Rice, urad dal, salt (fermented batter)", description: "Crispy South Indian crepe, typically served with sambar and chutney.", price: 8.99 },
725
- "Dal Tadka": { ingredients: "Toor dal, garlic, cumin, ghee, tomatoes", description: "Tempered lentil dish with aromatic spices, a staple across India.", price: 7.99 },
726
- "Pav Bhaji": { ingredients: "Mixed veggies (potatoes, peas, capsicum), butter, pav bread", description: "Mumbai's famous buttery vegetable mash with soft bread rolls.", price: 10.49 },
727
- "Masala Dosa": { ingredients: "Dosa batter, potato filling, mustard seeds, curry leaves", description: "Crispy crepe stuffed with spiced potatoes, served with chutney.", price: 9.99 }
728
- },
729
- nonVeg: {
730
- "Butter Chicken": { ingredients: "Chicken, tomato puree, butter, cream, garam masala", description: "Creamy Punjabi curry with tender chicken pieces in rich tomato sauce.", price: 15.99 },
731
- "Biryani": { ingredients: "Basmati rice, meat (chicken/mutton), spices (saffron, bay leaf), yogurt, fried onions", description: "Fragrant layered rice dish with spiced meat, a celebration favorite.", price: 14.99 },
732
- "Chicken Chettinad": { ingredients: "Chicken, coconut, black pepper, fennel, curry leaves", description: "Fiery Tamil Nadu chicken curry with aromatic spices and coconut.", price: 16.99 },
733
- "Fish Curry": { ingredients: "Fish (pomfret/rohu), coconut milk, tamarind, curry leaves", description: "Goan/Kerala style fish in tangy coconut gravy with spices.", price: 17.99 },
734
- "Tandoori Chicken": { ingredients: "Chicken, yogurt, red chili, tandoori masala, lemon", description: "Clay oven-roasted chicken marinated in yogurt and spices.", price: 15.99 }
735
- }
736
- };
737
 
738
  function sanitizeInput(input) {
739
  const div = document.createElement('div');
@@ -769,17 +964,6 @@
769
  cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
770
  }
771
 
772
- function showConfirmation(message, callback) {
773
- document.getElementById('confirmationMessage').textContent = message;
774
- const confirmBtn = document.getElementById('confirmAction');
775
- confirmBtn.onclick = function() {
776
- callback();
777
- bootstrap.Modal.getInstance(document.getElementById('confirmationModal')).hide();
778
- };
779
- const modal = new bootstrap.Modal(document.getElementById('confirmationModal'));
780
- modal.show();
781
- }
782
-
783
  function showSoftDrinkModal(button) {
784
  currentSoftDrinkButton = button;
785
  const buttonContainer = button.closest('.button-container');
@@ -795,11 +979,13 @@
795
  }
796
 
797
  function addSoftDrinkToCart() {
798
- if (!currentSoftDrinkButton) return;
 
799
  const buttonContainer = currentSoftDrinkButton.closest('.button-container');
800
  const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
801
  if (quantity < 1 || quantity > 99) {
802
  alert('Quantity must be between 1 and 99.');
 
803
  return;
804
  }
805
  const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
@@ -838,6 +1024,9 @@
838
  const cart = addToCartLocalStorage(cartPayload);
839
  updateCartUI(cart);
840
  bootstrap.Modal.getInstance(document.getElementById('softDrinkModal')).hide();
 
 
 
841
  });
842
  }
843
 
@@ -905,6 +1094,12 @@
905
  avatarUpload.addEventListener('change', function(event) {
906
  const file = event.target.files[0];
907
  if (file) {
 
 
 
 
 
 
908
  const reader = new FileReader();
909
  reader.onload = function(e) {
910
  const base64Image = e.target.result;
@@ -925,10 +1120,21 @@
925
  saveAvatar(data.image);
926
  ensureAvatarOptions();
927
  dropdownMenu.style.display = 'none';
 
 
928
  }
929
  })
930
  .catch(error => {
931
- alert('Error uploading image.');
 
 
 
 
 
 
 
 
 
932
  });
933
  };
934
  reader.readAsDataURL(file);
@@ -937,23 +1143,23 @@
937
 
938
  function addDeleteListener(deleteElement) {
939
  deleteElement.addEventListener('click', function() {
940
- showConfirmation('Are you sure you want to delete your avatar?', function() {
941
- fetch('/delete_avatar', {
942
- method: 'POST',
943
- headers: { 'Content-Type': 'application/json' }
944
- })
945
- .then(response => response.json())
946
- .then(data => {
947
- if (data.success) {
948
- clearAvatar();
949
- dropdownMenu.style.display = 'none';
950
- }
951
- })
952
- .catch(error => {
953
- alert('Error deleting image. Cleared locally.');
954
  clearAvatar();
955
  dropdownMenu.style.display = 'none';
956
- });
 
 
 
 
 
 
 
957
  });
958
  });
959
  }
@@ -1065,12 +1271,14 @@
1065
  suggestionDiv.addEventListener('click', function() {
1066
  searchBar.value = item;
1067
  suggestionsContainer.style.display = 'none';
 
1068
  });
1069
  suggestionsContainer.appendChild(suggestionDiv);
1070
  });
1071
  suggestionsContainer.style.display = 'block';
1072
  }
1073
  }
 
1074
  });
1075
  document.addEventListener('click', function(event) {
1076
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
@@ -1080,181 +1288,85 @@
1080
 
1081
  const customDishForm = document.getElementById('customDishForm');
1082
  if (customDishForm) {
1083
- const dishNameInput = document.getElementById('custom-dish-name');
1084
- const dishIngredients = document.getElementById('custom-dish-ingredients');
1085
- const dishDescription = document.getElementById('custom-dish-description');
1086
- const dishPrice = document.getElementById('custom-dish-price');
1087
- const dishSuggestions = document.getElementById('dishNameSuggestions');
1088
- const formFeedback = document.getElementById('form-feedback');
1089
- const chatInput = document.getElementById('chat-input');
1090
- const sendBtn = document.getElementById('send-btn');
1091
- const chatFeedback = document.getElementById('chat-feedback');
1092
- const vegBtn = document.getElementById('veg-btn');
1093
- const nonVegBtn = document.getElementById('non-veg-btn');
1094
- const itemDetails = document.getElementById('item-details');
1095
- let filterType = 'all';
1096
 
1097
- function showItemDetails(dish) {
1098
- if (dish) {
1099
- itemDetails.style.display = 'block';
1100
- document.getElementById('detail-name').textContent = dish;
1101
- document.getElementById('detail-ingredients').textContent = (dishDatabase.veg[dish] || dishDatabase.nonVeg[dish]).ingredients;
1102
- document.getElementById('detail-description').textContent = (dishDatabase.veg[dish] || dishDatabase.nonVeg[dish]).description;
1103
- document.getElementById('detail-price').textContent = (dishDatabase.veg[dish] || dishDatabase.nonVeg[dish]).price.toFixed(2);
1104
- } else {
1105
- itemDetails.style.display = 'none';
 
1106
  }
1107
  }
1108
 
1109
- function updateSuggestions(input) {
1110
- const sanitizedInput = sanitizeInput(input.trim().toLowerCase());
1111
- dishSuggestions.innerHTML = '';
1112
- dishSuggestions.style.display = 'none';
1113
- if (sanitizedInput.length > 0) {
1114
- let matches = [];
1115
- if (filterType === 'veg') {
1116
- matches = Object.keys(dishDatabase.veg).filter(dish => dish.toLowerCase().includes(sanitizedInput));
1117
- } else if (filterType === 'non-veg') {
1118
- matches = Object.keys(dishDatabase.nonVeg).filter(dish => dish.toLowerCase().includes(sanitizedInput));
1119
- } else {
1120
- matches = [...Object.keys(dishDatabase.veg), ...Object.keys(dishDatabase.nonVeg)].filter(dish => dish.toLowerCase().includes(sanitizedInput));
1121
- }
1122
- if (matches.length > 0) {
1123
- matches.slice(0, 5).forEach(dish => {
1124
- const suggestion = document.createElement('div');
1125
- suggestion.classList.add('suggestion-item');
1126
- suggestion.textContent = dish;
1127
- suggestion.addEventListener('click', function() {
1128
- dishNameInput.value = dish;
1129
- const dishData = dishDatabase.veg[dish] || dishDatabase.nonVeg[dish];
1130
- if (dishData) {
1131
- dishIngredients.value = dishData.ingredients;
1132
- dishDescription.value = dishData.description;
1133
- dishPrice.value = dishData.price.toFixed(2);
1134
- showItemDetails(dish);
1135
- }
1136
- dishSuggestions.style.display = 'none';
1137
  });
1138
- dishSuggestions.appendChild(suggestion);
1139
  });
1140
- dishSuggestions.style.display = 'block';
1141
  }
1142
  }
1143
- }
1144
-
1145
- dishNameInput.addEventListener('input', function() {
1146
- updateSuggestions(this.value);
1147
- });
1148
-
1149
- document.addEventListener('click', function(e) {
1150
- if (e.target !== dishNameInput && !dishSuggestions.contains(e.target)) {
1151
- dishSuggestions.style.display = 'none';
1152
- }
1153
  });
1154
 
1155
- vegBtn.addEventListener('click', function() {
1156
- filterType = 'veg';
1157
- vegBtn.classList.remove('btn-outline-success');
1158
- vegBtn.classList.add('btn-success');
1159
- nonVegBtn.classList.remove('btn-danger');
1160
- nonVegBtn.classList.add('btn-outline-danger');
1161
- updateSuggestions(dishNameInput.value);
1162
- });
1163
-
1164
- nonVegBtn.addEventListener('click', function() {
1165
- filterType = 'non-veg';
1166
- nonVegBtn.classList.remove('btn-outline-danger');
1167
- nonVegBtn.classList.add('btn-danger');
1168
- vegBtn.classList.remove('btn-success');
1169
- vegBtn.classList.add('btn-outline-success');
1170
- updateSuggestions(dishNameInput.value);
1171
- });
1172
-
1173
- sendBtn.addEventListener('click', function() {
1174
- const input = sanitizeInput(chatInput.value.trim().toLowerCase());
1175
- chatFeedback.innerHTML = '';
1176
- if (input === 'hi') {
1177
- fetch('/salesforce/get_customer_name', {
1178
- method: 'GET',
1179
- headers: { 'Content-Type': 'application/json' }
1180
- })
1181
- .then(response => response.json())
1182
- .then(data => {
1183
- const customerName = data.name || 'Customer';
1184
- chatFeedback.innerHTML = `Welcome ${customerName}!`;
1185
- chatFeedback.className = 'text-success';
1186
- })
1187
- .catch(() => {
1188
- chatFeedback.innerHTML = 'Welcome Guest!';
1189
- chatFeedback.className = 'text-success';
1190
- });
1191
- } else if (input === 'veg') {
1192
- filterType = 'veg';
1193
- vegBtn.click();
1194
- chatFeedback.innerHTML = 'Showing vegetarian items.';
1195
- chatFeedback.className = 'text-success';
1196
- } else if (input === 'non-veg' || input === 'non veg') {
1197
- filterType = 'non-veg';
1198
- nonVegBtn.click();
1199
- chatFeedback.innerHTML = 'Showing non-vegetarian items.';
1200
- chatFeedback.className = 'text-success';
1201
- } else {
1202
- chatFeedback.innerHTML = 'Please type "hi", "veg", or "non-veg".';
1203
- chatFeedback.className = 'text-danger';
1204
  }
1205
- chatInput.value = '';
1206
  });
1207
 
1208
  customDishForm.addEventListener('submit', function(event) {
1209
  event.preventDefault();
1210
- formFeedback.style.display = 'none';
1211
- formFeedback.innerHTML = '';
1212
- const dishName = sanitizeInput(dishNameInput.value.trim());
1213
- const ingredients = sanitizeInput(dishIngredients.value.trim());
1214
- const description = sanitizeInput(dishDescription.value.trim());
1215
- const price = parseFloat(dishPrice.value);
1216
- if (!dishName || !ingredients || !description || isNaN(price) || price <= 0) {
1217
- formFeedback.innerHTML = 'Please fill all fields with valid data.';
1218
- formFeedback.className = 'text-danger';
1219
- formFeedback.style.display = 'block';
1220
  return;
1221
  }
1222
- const submitBtn = document.getElementById('submitCustomDish');
1223
- submitBtn.disabled = true;
1224
- submitBtn.innerText = 'Submitting...';
1225
- const formData = {
1226
- name: dishName,
1227
- ingredients: ingredients,
1228
- description: description,
1229
- price: price,
1230
- type: filterType === 'veg' ? 'Vegetarian' : 'Non-Vegetarian'
1231
- };
1232
- fetch('/salesforce/submit_custom_dish', {
1233
  method: 'POST',
1234
- headers: { 'Content-Type': 'application/json' },
1235
- body: JSON.stringify(formData)
1236
  })
1237
  .then(response => response.json())
1238
  .then(data => {
1239
  if (data.success) {
1240
- formFeedback.innerHTML = 'Custom dish submitted successfully!';
1241
- formFeedback.className = 'text-success';
1242
- formFeedback.style.display = 'block';
1243
- customDishForm.reset();
1244
- itemDetails.style.display = 'none';
1245
- setTimeout(() => window.location.reload(), 1500);
1246
  } else {
1247
  throw new Error(data.error || 'Submission failed');
1248
  }
1249
  })
1250
  .catch(error => {
1251
- formFeedback.innerHTML = `Error: ${error.message}. Please try again.`;
1252
- formFeedback.className = 'text-danger';
1253
- formFeedback.style.display = 'block';
1254
- })
1255
- .finally(() => {
1256
- submitBtn.disabled = false;
1257
- submitBtn.innerText = 'Submit to Salesforce';
1258
  });
1259
  });
1260
  }
@@ -1269,7 +1381,7 @@
1269
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1270
  const recognition = new SpeechRecognition();
1271
  recognition.continuous = false;
1272
- recognition.interimResults = false;
1273
  recognition.lang = 'en-US';
1274
 
1275
  micIcon.addEventListener('click', function() {
@@ -1283,14 +1395,25 @@
1283
  };
1284
 
1285
  recognition.onresult = function(event) {
1286
- const transcript = event.results[0][0].transcript.trim().toLowerCase();
1287
- micPopupText.textContent = `You said: "${transcript}"`;
1288
- setTimeout(() => {
 
 
 
 
 
 
 
 
 
 
 
 
 
1289
  micPopup.classList.remove('active');
1290
- searchBar.value = transcript;
1291
- performSearch(transcript);
1292
  micIcon.classList.remove('active');
1293
- }, 1000);
1294
  };
1295
 
1296
  recognition.onerror = function(event) {
@@ -1321,25 +1444,6 @@
1321
  micUnsupported.style.display = 'block';
1322
  }
1323
 
1324
- function performSearch(query) {
1325
- const lowerQuery = query.toLowerCase();
1326
- const matchingSection = sections.find(section => section.toLowerCase().includes(lowerQuery));
1327
- if (matchingSection) {
1328
- selectedCategoryInput.value = matchingSection;
1329
- categoryForm.submit();
1330
- } else {
1331
- const matchingItem = menuItems.find(item => item.toLowerCase().includes(lowerQuery));
1332
- if (matchingItem) {
1333
- const itemCard = document.querySelector(`[data-item-name="${matchingItem}"]`);
1334
- if (itemCard) {
1335
- itemCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
1336
- }
1337
- } else {
1338
- alert('No matching items or sections found.');
1339
- }
1340
- }
1341
- }
1342
-
1343
  document.getElementById('increaseQuantity').addEventListener('click', function() {
1344
  let quantity = parseInt(document.getElementById('quantityInput').value);
1345
  if (quantity < 99) document.getElementById('quantityInput').value = quantity + 1;
@@ -1362,18 +1466,67 @@
1362
 
1363
  const initialCart = getCartLocalStorage();
1364
  updateCartUI(initialCart);
 
 
 
 
 
 
 
 
 
 
 
1365
  });
1366
 
1367
- function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, category) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1368
  document.getElementById('modal-name').textContent = name;
1369
  document.getElementById('modal-price').textContent = `$${price}`;
1370
- document.getElementById('modal-img').src = image;
1371
- document.getElementById('modal-description').textContent = description;
1372
- document.getElementById('modal-ingredients').textContent = ingredients;
1373
- document.getElementById('modal-nutrition').textContent = nutrition;
1374
- document.getElementById('modal-allergens').textContent = allergens;
1375
  document.getElementById('modal-section').dataset.section = section;
1376
- document.getElementById('modal-section').dataset.category = category;
1377
  document.getElementById('quantityInput').value = '1';
1378
  document.getElementById('modal-instructions').value = '';
1379
  fetchAddons(name, section);
@@ -1382,38 +1535,58 @@
1382
  function fetchAddons(itemName, section) {
1383
  const addonsList = document.getElementById('addons-list');
1384
  addonsList.innerHTML = 'Loading customization options...';
1385
- fetch(`/menu/addons?item_name=${encodeURIComponent(itemName)}&section=${encodeURIComponent(section)}`)
1386
  .then(response => response.json())
1387
  .then(data => {
1388
  addonsList.innerHTML = '';
1389
- if (data.addons && data.addons.length > 0) {
1390
- data.addons.forEach(addon => {
1391
- const addonDiv = document.createElement('div');
1392
- addonDiv.className = 'addon-section mb-3';
1393
- addonDiv.innerHTML = `
1394
- <h6>${addon.name}</h6>
1395
- ${addon.options.map(option => `
1396
- <div class="form-check">
1397
- <input class="form-check-input" type="${addon.type === 'single' ? 'radio' : 'checkbox'}"
1398
- name="${addon.name}" id="${option.id}" value="${option.name}"
1399
- data-price="${option.price}">
1400
- <label class="form-check-label" for="${option.id}">
1401
- ${option.name} ${option.price > 0 ? '(+$' + option.price.toFixed(2) + ')' : ''}
1402
- </label>
1403
- </div>
1404
- `).join('')}
1405
- `;
1406
- addonsList.appendChild(addonDiv);
1407
- });
1408
- } else {
1409
  addonsList.innerHTML = '<p>No customization options available.</p>';
 
1410
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1411
  })
1412
- .catch(error => {
1413
  addonsList.innerHTML = '<p>Error loading customization options.</p>';
1414
  });
1415
  }
1416
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1417
  function addToCartFromModal() {
1418
  if (isProcessingRequest) return;
1419
  isProcessingRequest = true;
@@ -1429,13 +1602,10 @@
1429
  isProcessingRequest = false;
1430
  return;
1431
  }
1432
- const addons = [];
1433
- document.querySelectorAll('#addons-list .form-check-input:checked').forEach(input => {
1434
- const addonName = input.getAttribute('name');
1435
- const optionName = input.value;
1436
- const optionPrice = parseFloat(input.getAttribute('data-price')) || 0;
1437
- addons.push({ addonName, optionName, optionPrice });
1438
- });
1439
  const cartPayload = {
1440
  itemName: itemName,
1441
  itemPrice: itemPrice,
 
52
  opacity: 0;
53
  transition: opacity 0.5s ease-in-out, transform 0.2s ease;
54
  background-color: #000;
 
 
55
  }
56
  .menu-video.loaded {
57
  opacity: 1;
 
128
  border-color: #0D9232;
129
  transform: scale(1.05);
130
  }
131
+ .btn-primary:active,
132
+ .btn-primary:focus {
133
+ background-color: #0B7A29;
134
+ border-color: #0B7A29;
135
+ box-shadow: none;
136
+ transform: scale(0.98);
137
+ }
138
  .avatar-dropdown-container {
139
  position: absolute;
140
  right: 10px;
 
183
  .dropdown-menu .dropdown-item {
184
  padding: 10px 16px;
185
  text-decoration: none;
186
+ color: #333;
187
  font-size: 14px;
188
  transition: background-color 0.2s ease;
189
  cursor: pointer;
 
217
  max-width: 90%;
218
  background-color: #fff;
219
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
220
+ border-radius: 25px;
 
221
  }
222
  .search-bar-container input {
223
  width: 100%;
 
226
  border-radius: 25px;
227
  border: none;
228
  background-color: transparent;
229
+ outline: none;
230
  }
231
  .search-bar-container input:focus {
232
  box-shadow: 0 4px 8px rgba(0,0,0,0.15);
233
  }
234
  .search-bar-container input::placeholder {
235
  color: #888;
 
 
236
  }
237
  .search-icon {
238
  position: absolute;
 
246
  font-size: 18px;
247
  color: #888;
248
  cursor: pointer;
249
+ transition: color 0.3s ease;
250
  }
251
  .mic-icon.active {
252
  color: #007bff;
 
272
  z-index: 1000;
273
  display: none;
274
  }
275
+ .autocomplete-suggestions .suggestion-item {
276
+ padding: 8px 15px;
277
+ cursor: pointer;
278
+ font-size: 14px;
279
+ color: #333;
280
+ }
281
  .autocomplete-suggestions .suggestion-item:hover {
282
  background-color: #f1f1f1;
283
  }
284
+ .addon-section {
285
+ background-color: #fff;
286
+ border: 2px solid #ffa500;
287
+ border-radius: 8px;
288
+ padding: 12px;
289
+ margin-bottom: 10px;
290
+ }
291
+ .addon-section h6 {
292
+ margin-bottom: 10px;
293
+ font-size: 1.1rem;
294
+ font-weight: bold;
295
+ color: #343a40;
296
+ }
297
+ .addon-section .form-check {
298
+ display: inline-flex;
299
+ align-items: center;
300
+ margin-left: 10px;
301
+ color: #343a40;
302
+ }
303
+ .addon-section .form-check-input {
304
+ width: 20px;
305
+ height: 20px;
306
+ border: 2px solid #343a40;
307
+ border-radius: 5px;
308
+ background-color: #f0f0f0;
309
+ position: relative;
310
+ margin-right: 10px;
311
+ }
312
+ .addon-section .form-check-input:checked {
313
+ background-color: #006400;
314
+ border-color: #006400;
315
+ }
316
+ .addon-section .form-check-input:checked::before {
317
+ content: '\2713';
318
+ font-size: 14px;
319
+ position: absolute;
320
+ top: 3px;
321
+ left: 4px;
322
+ color: white;
323
+ }
324
+ .addon-section .form-check-label {
325
+ font-size: 16px;
326
+ margin-right: 15px;
327
+ cursor: pointer;
328
+ }
329
+ form.text-center.mb-4 {
330
+ display: flex;
331
+ flex-direction: column;
332
+ align-items: center;
333
+ justify-content: center;
334
+ margin-bottom: 5px;
335
+ }
336
+ .modal-header {
337
+ padding: 10px 15px;
338
+ }
339
+ .modal-title {
340
+ font-size: 16px;
341
+ font-weight: bold;
342
+ }
343
+ .modal-body {
344
+ max-height: 60vh;
345
+ overflow-y: auto;
346
+ padding: 15px;
347
+ text-align: center;
348
+ }
349
+ .modal-body #avatar-modal-img {
350
+ max-width: 100%;
351
+ max-height: 400px;
352
+ object-fit: contain;
353
+ border-radius: 8px;
354
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
355
+ }
356
+ .modal-body #modal-img {
357
+ max-height: 200px;
358
+ width: 100%;
359
+ object-fit: cover;
360
+ border-radius: 8px;
361
+ margin-bottom: 10px;
362
+ }
363
+ .modal-body #modal-name {
364
+ font-size: 20px;
365
+ font-weight: bold;
366
+ margin-bottom: 5px;
367
+ color: #333333;
368
+ }
369
+ .modal-body #modal-price {
370
+ font-size: 16px;
371
+ font-weight: 500;
372
+ color: #000000;
373
+ margin-bottom: 10px;
374
+ }
375
+ .modal-body #modal-description {
376
+ font-size: 14px;
377
+ color: #6c757d;
378
+ margin-bottom: 10px;
379
+ }
380
+ .modal-body .nutritional-info {
381
+ font-size: 12px;
382
+ color: #6c757d;
383
+ margin-bottom: 10px;
384
+ }
385
+ .modal-body #modal-addons h6 {
386
+ font-size: 14px;
387
+ font-weight: bold;
388
+ margin-bottom: 10px;
389
+ }
390
+ .modal-footer {
391
+ display: flex;
392
+ align-items: center;
393
+ justify-content: space-between;
394
+ padding: 10px;
395
+ }
396
+ .modal-footer .btn {
397
+ height: 40px;
398
+ padding: 0 15px;
399
+ }
400
+ .modal-footer .form-control {
401
+ width: 50px;
402
+ height: 40px;
403
+ text-align: center;
404
+ }
405
+ .modal-footer .btn-primary {
406
+ padding: 10px 20px;
407
+ width: auto;
408
+ }
409
+ .modal-footer .btn-outline-secondary {
410
+ width: 40px;
411
+ }
412
+ .item-details {
413
+ display: none;
414
+ padding: 15px;
415
+ background-color: #f8f9fa;
416
+ border-radius: 8px;
417
+ margin: 10px 15px;
418
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
419
+ }
420
+ .item-details.show {
421
+ display: block;
422
+ }
423
+ .item-details h6 {
424
+ color: #0FAA39;
425
+ margin-bottom: 10px;
426
+ font-size: 1.1rem;
427
+ font-weight: bold;
428
+ }
429
+ .item-details p {
430
+ margin-bottom: 15px;
431
+ color: #555;
432
+ line-height: 1.5;
433
+ font-size: 0.9rem;
434
+ }
435
+ .toggle-details {
436
+ cursor: pointer;
437
+ color: #0FAA39;
438
+ font-size: 0.9rem;
439
+ padding: 5px 0;
440
+ transition: color 0.3s ease;
441
+ }
442
+ .toggle-details:hover {
443
+ color: #0D9232;
444
+ text-decoration: underline;
445
+ }
446
+ .category-buttons {
447
+ display: flex;
448
+ flex-wrap: wrap;
449
+ gap: 10px;
450
+ justify-content: center;
451
+ margin-top: 10px;
452
+ }
453
+ .category-button {
454
+ background-color: #fff;
455
+ border: 2px solid #0FAA39;
456
+ color: #0FAA39;
457
+ padding: 5px 15px;
458
+ border-radius: 20px;
459
+ font-size: 0.9rem;
460
+ cursor: pointer;
461
+ transition: background-color 0.3s, color 0.3s;
462
+ }
463
+ .category-button.selected {
464
+ background-color: #0FAA39;
465
+ color: #fff;
466
+ }
467
+ .category-button:hover {
468
+ background-color: #e6f4ea;
469
+ }
470
+ .modal-body::-webkit-scrollbar {
471
+ width: 8px;
472
+ }
473
+ .modal-body::-webkit-scrollbar-track {
474
+ background: #f1f1f1;
475
+ border-radius: 10px;
476
+ }
477
+ .modal-body::-webkit-scrollbar-thumb {
478
+ background: #0FAA39;
479
+ border-radius: 10px;
480
+ }
481
+ .modal-body::-webkit-scrollbar-thumb:hover {
482
+ background: #0D9232;
483
+ }
484
  #custom-dish-form {
485
  position: relative;
486
  padding: 20px;
 
516
  #custom-dish-form::-webkit-scrollbar-thumb:hover {
517
  background: #0D9232;
518
  }
519
+ #descriptionSuggestions {
520
  position: absolute;
521
  width: calc(100% - 30px);
522
  max-height: 200px;
 
528
  display: none;
529
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
530
  }
 
 
 
 
 
 
531
  .bottom-action-bar {
532
  position: fixed;
533
  bottom: 0;
 
542
  z-index: 1000;
543
  max-width: 900px;
544
  margin: 0 auto;
 
 
545
  }
546
+ .bottom-action-bar .btn {
547
+ flex: 1;
548
+ margin: 0 5px;
549
+ padding: 10px 15px;
550
+ border-radius: 8px;
551
+ font-weight: bold;
552
+ font-size: 16px;
553
+ color: white;
554
+ display: flex;
555
+ align-items: center;
556
+ justify-content: center;
557
  }
558
  .bottom-action-bar .btn-order-history {
559
  background-color: #FFA07A;
560
  border-color: #FFA07A;
561
+ }
562
+ .bottom-action-bar .btn-order-history:hover {
563
+ background-color: #FF8C61;
564
+ border-color: #FF8C61;
565
+ }
566
+ .bottom-action-bar .btn-view-cart {
567
+ background-color: #0FAA39;
568
+ border-color: #0FAA39;
569
+ }
570
+ .bottom-action-bar .btn-view-cart:hover {
571
+ background-color: #0D9232;
572
+ border-color: #0D9232;
573
  }
574
  .cart-icon-badge {
575
  background-color: white;
 
607
  color: #ff4444;
608
  animation: pulse 1.5s infinite;
609
  }
610
+ .mic-popup-text {
611
+ font-size: 18px;
612
+ margin-bottom: 15px;
613
+ }
614
+ .mic-popup-cancel {
615
+ background-color: #ff4444;
616
+ color: white;
617
+ border: none;
618
+ padding: 8px 20px;
619
+ border-radius: 20px;
620
+ cursor: pointer;
621
+ font-weight: bold;
622
+ }
623
  @keyframes pulse {
624
  0% { transform: scale(1); }
625
  50% { transform: scale(1.1); }
 
633
  .menu-card {
634
  max-width: 100%;
635
  }
636
+ .bottom-action-bar .btn {
637
+ font-size: 14px;
638
+ padding: 8px 10px;
639
+ }
640
+ .cart-icon-badge {
641
+ width: 18px;
642
+ height: 18px;
643
+ font-size: 10px;
644
+ }
645
  }
646
  </style>
647
  </head>
 
695
  {% if selected_category == "Customized Dish" %}
696
  <div id="custom-dish-form" class="mt-4">
697
  <h3>Create Your Custom Dish</h3>
698
+ <form method="POST" action="/customdish/generate_custom_dish" id="customDishForm" role="form">
699
+ <div class="mb-3">
 
 
 
 
700
  <label for="custom-dish-name" class="form-label">Dish Name</label>
701
  <input type="text" class="form-control" id="custom-dish-name" name="name" required autocomplete="off" placeholder="e.g., Spicy Chicken Curry" aria-label="Custom Dish Name">
 
702
  </div>
703
+ <div class="mb-3 position-relative">
 
 
 
 
704
  <label for="custom-dish-description" class="form-label">Dish Description</label>
705
+ <textarea class="form-control" id="custom-dish-description" name="description" required rows="3" placeholder="e.g., Chicken, Tomatoes, Spices" aria-label="Dish Description"></textarea>
706
+ <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
707
  </div>
708
+ <button type="submit" class="btn btn-primary" aria-label="Submit Custom Dish">Submit Custom Dish</button>
 
 
 
 
 
709
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
710
  </div>
711
  {% else %}
712
  {% if ordered_menu.items()|length == 0 %}
 
895
  <button class="mic-popup-cancel" id="micPopupCancel" aria-label="Cancel Voice Search">Cancel</button>
896
  </div>
897
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
898
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
899
  <script>
900
  let isProcessingRequest = false;
 
914
  {% endfor %}
915
  ];
916
 
917
+ const ingredientsList = [
918
+ "Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
919
+ "Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
920
+ "Chili Sauce", "Coconut Milk", "Coriander Powder", "Cornflour", "Cream", "Cumin Powder",
921
+ "Cumin Seeds", "Curd (Yogurt)", "Curry Leaves", "Fish (e.g., King Fish or Salmon)",
922
+ "Fresh Coriander Leaves", "Garam Masala", "Garlic", "Ghee (Clarified Butter)", "Ginger",
923
+ "Ginger-Garlic Paste", "Goat Meat (Mutton)", "Green Chilies", "Honey",
924
+ "Kasuri Methi (dried fenugreek leaves)", "Lemon Juice", "Mango Puree", "Mint Leaves",
925
+ "Mixed Vegetables (Carrot, Peas, Potato, Cauliflower)", "Mixed Vegetables (Carrot, Peas, Potato)",
926
+ "Mustard Seeds", "Mutton (Goat Meat)", "Oil", "Oil (for frying)", "Onion",
927
+ "Paneer (Indian Cottage Cheese)", "Peas", "Potatoes", "Prawns", "Red Chili Powder",
928
+ "Rice Flour", "Saffron", "Salt", "Soy Sauce", "Spring Onion", "Tamarind (for sourness)",
929
+ "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
930
+ "Whole Wheat Flour", "Yogurt (Curd)"
931
+ ];
 
932
 
933
  function sanitizeInput(input) {
934
  const div = document.createElement('div');
 
964
  cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
965
  }
966
 
 
 
 
 
 
 
 
 
 
 
 
967
  function showSoftDrinkModal(button) {
968
  currentSoftDrinkButton = button;
969
  const buttonContainer = button.closest('.button-container');
 
979
  }
980
 
981
  function addSoftDrinkToCart() {
982
+ if (!currentSoftDrinkButton || isProcessingRequest) return;
983
+ isProcessingRequest = true;
984
  const buttonContainer = currentSoftDrinkButton.closest('.button-container');
985
  const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
986
  if (quantity < 1 || quantity > 99) {
987
  alert('Quantity must be between 1 and 99.');
988
+ isProcessingRequest = false;
989
  return;
990
  }
991
  const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
 
1024
  const cart = addToCartLocalStorage(cartPayload);
1025
  updateCartUI(cart);
1026
  bootstrap.Modal.getInstance(document.getElementById('softDrinkModal')).hide();
1027
+ })
1028
+ .finally(() => {
1029
+ isProcessingRequest = false;
1030
  });
1031
  }
1032
 
 
1094
  avatarUpload.addEventListener('change', function(event) {
1095
  const file = event.target.files[0];
1096
  if (file) {
1097
+ const maxSize = 15 * 1024 * 1024; // 15MB
1098
+ if (file.size > maxSize) {
1099
+ alert('Image size must not exceed 15MB.');
1100
+ this.value = '';
1101
+ return;
1102
+ }
1103
  const reader = new FileReader();
1104
  reader.onload = function(e) {
1105
  const base64Image = e.target.result;
 
1120
  saveAvatar(data.image);
1121
  ensureAvatarOptions();
1122
  dropdownMenu.style.display = 'none';
1123
+ } else {
1124
+ throw new Error(data.error || 'Upload failed');
1125
  }
1126
  })
1127
  .catch(error => {
1128
+ alert('Error uploading image. Using default as fallback.');
1129
+ const img = document.createElement('img');
1130
+ img.src = '/static/default-avatar.jpg';
1131
+ img.alt = 'User Avatar';
1132
+ img.className = 'avatar-image';
1133
+ avatarIcon.innerHTML = '';
1134
+ avatarIcon.appendChild(img);
1135
+ saveAvatar('/static/default-avatar.jpg');
1136
+ ensureAvatarOptions();
1137
+ dropdownMenu.style.display = 'none';
1138
  });
1139
  };
1140
  reader.readAsDataURL(file);
 
1143
 
1144
  function addDeleteListener(deleteElement) {
1145
  deleteElement.addEventListener('click', function() {
1146
+ fetch('/delete_avatar', {
1147
+ method: 'POST',
1148
+ headers: { 'Content-Type': 'application/json' }
1149
+ })
1150
+ .then(response => response.json())
1151
+ .then(data => {
1152
+ if (data.success) {
 
 
 
 
 
 
 
1153
  clearAvatar();
1154
  dropdownMenu.style.display = 'none';
1155
+ } else {
1156
+ throw new Error(data.error || 'Delete failed');
1157
+ }
1158
+ })
1159
+ .catch(error => {
1160
+ alert('Error deleting image. Cleared locally.');
1161
+ clearAvatar();
1162
+ dropdownMenu.style.display = 'none';
1163
  });
1164
  });
1165
  }
 
1271
  suggestionDiv.addEventListener('click', function() {
1272
  searchBar.value = item;
1273
  suggestionsContainer.style.display = 'none';
1274
+ filterMenu();
1275
  });
1276
  suggestionsContainer.appendChild(suggestionDiv);
1277
  });
1278
  suggestionsContainer.style.display = 'block';
1279
  }
1280
  }
1281
+ filterMenu();
1282
  });
1283
  document.addEventListener('click', function(event) {
1284
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
 
1288
 
1289
  const customDishForm = document.getElementById('customDishForm');
1290
  if (customDishForm) {
1291
+ const descriptionTextarea = document.getElementById('custom-dish-description');
1292
+ const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1293
+ let usedIngredients = new Set();
 
 
 
 
 
 
 
 
 
 
1294
 
1295
+ function updateUsedIngredients() {
1296
+ const inputText = descriptionTextarea.value.trim();
1297
+ usedIngredients.clear();
1298
+ if (inputText) {
1299
+ const words = inputText.split(/,\s*/).map(word => word.trim());
1300
+ words.forEach(word => {
1301
+ if (ingredientsList.includes(word)) {
1302
+ usedIngredients.add(word);
1303
+ }
1304
+ });
1305
  }
1306
  }
1307
 
1308
+ descriptionTextarea.addEventListener('input', function() {
1309
+ const inputText = this.value.trim();
1310
+ const words = inputText.split(/,\s*/);
1311
+ const lastWord = words[words.length - 1].trim().toLowerCase();
1312
+ descriptionSuggestions.innerHTML = '';
1313
+ descriptionSuggestions.style.display = 'none';
1314
+ updateUsedIngredients();
1315
+ if (lastWord) {
1316
+ const filteredIngredients = ingredientsList.filter(ingredient =>
1317
+ ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
1318
+ );
1319
+ if (filteredIngredients.length > 0) {
1320
+ filteredIngredients.forEach(ingredient => {
1321
+ const suggestionDiv = document.createElement('div');
1322
+ suggestionDiv.classList.add('suggestion-item');
1323
+ suggestionDiv.textContent = ingredient;
1324
+ suggestionDiv.addEventListener('click', function() {
1325
+ const currentValue = descriptionTextarea.value;
1326
+ const lastCommaIndex = currentValue.lastIndexOf(',');
1327
+ const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
1328
+ descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
1329
+ descriptionSuggestions.style.display = 'none';
1330
+ descriptionTextarea.focus();
1331
+ updateUsedIngredients();
 
 
 
 
1332
  });
1333
+ descriptionSuggestions.appendChild(suggestionDiv);
1334
  });
1335
+ descriptionSuggestions.style.display = 'block';
1336
  }
1337
  }
 
 
 
 
 
 
 
 
 
 
1338
  });
1339
 
1340
+ document.addEventListener('click', function(event) {
1341
+ if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1342
+ descriptionSuggestions.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1343
  }
 
1344
  });
1345
 
1346
  customDishForm.addEventListener('submit', function(event) {
1347
  event.preventDefault();
1348
+ const dishName = sanitizeInput(document.getElementById('custom-dish-name').value.trim());
1349
+ const description = sanitizeInput(descriptionTextarea.value.trim());
1350
+ if (!dishName || !description) {
1351
+ alert('Please fill in both the dish name and description.');
 
 
 
 
 
 
1352
  return;
1353
  }
1354
+ fetch('/customdish/generate_custom_dish', {
 
 
 
 
 
 
 
 
 
 
1355
  method: 'POST',
1356
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
1357
+ body: new URLSearchParams({ 'name': dishName, 'description': description })
1358
  })
1359
  .then(response => response.json())
1360
  .then(data => {
1361
  if (data.success) {
1362
+ alert('Custom dish submitted successfully!');
1363
+ window.location.reload();
 
 
 
 
1364
  } else {
1365
  throw new Error(data.error || 'Submission failed');
1366
  }
1367
  })
1368
  .catch(error => {
1369
+ alert(`Error submitting custom dish: ${error.message}`);
 
 
 
 
 
 
1370
  });
1371
  });
1372
  }
 
1381
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1382
  const recognition = new SpeechRecognition();
1383
  recognition.continuous = false;
1384
+ recognition.interimResults = true;
1385
  recognition.lang = 'en-US';
1386
 
1387
  micIcon.addEventListener('click', function() {
 
1395
  };
1396
 
1397
  recognition.onresult = function(event) {
1398
+ let interimTranscript = '';
1399
+ let finalTranscript = '';
1400
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1401
+ const transcript = event.results[i][0].transcript;
1402
+ if (event.results[i].isFinal) {
1403
+ finalTranscript += transcript;
1404
+ } else {
1405
+ interimTranscript += transcript;
1406
+ }
1407
+ }
1408
+ if (interimTranscript) {
1409
+ micPopupText.textContent = interimTranscript;
1410
+ }
1411
+ if (finalTranscript) {
1412
+ searchBar.value = sanitizeInput(finalTranscript.trim());
1413
+ filterMenu();
1414
  micPopup.classList.remove('active');
 
 
1415
  micIcon.classList.remove('active');
1416
+ }
1417
  };
1418
 
1419
  recognition.onerror = function(event) {
 
1444
  micUnsupported.style.display = 'block';
1445
  }
1446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1447
  document.getElementById('increaseQuantity').addEventListener('click', function() {
1448
  let quantity = parseInt(document.getElementById('quantityInput').value);
1449
  if (quantity < 99) document.getElementById('quantityInput').value = quantity + 1;
 
1466
 
1467
  const initialCart = getCartLocalStorage();
1468
  updateCartUI(initialCart);
1469
+
1470
+ fetch('/cart/get')
1471
+ .then(response => response.json())
1472
+ .then(data => {
1473
+ if (data.success) {
1474
+ updateCartUI(data.cart);
1475
+ }
1476
+ })
1477
+ .catch(() => {
1478
+ updateCartUI(initialCart);
1479
+ });
1480
  });
1481
 
1482
+ function filterMenu() {
1483
+ const input = sanitizeInput(document.getElementById('searchBar').value.trim().toLowerCase());
1484
+ const sections = document.querySelectorAll('h3');
1485
+ const items = document.querySelectorAll('.menu-card');
1486
+ let matchedSections = new Set();
1487
+ items.forEach(item => {
1488
+ const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1489
+ const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
1490
+ if (itemName.includes(input) || itemSection.includes(input)) {
1491
+ item.style.display = 'block';
1492
+ item.classList.add('visible');
1493
+ matchedSections.add(item.closest('.row'));
1494
+ } else {
1495
+ item.style.display = 'none';
1496
+ }
1497
+ });
1498
+ sections.forEach(section => {
1499
+ const sectionRow = section.nextElementSibling;
1500
+ if (matchedSections.has(sectionRow)) {
1501
+ section.style.display = 'block';
1502
+ sectionRow.style.display = 'flex';
1503
+ } else {
1504
+ section.style.display = 'none';
1505
+ sectionRow.style.display = 'none';
1506
+ }
1507
+ });
1508
+ if (!input) {
1509
+ sections.forEach(section => {
1510
+ section.style.display = 'block';
1511
+ section.nextElementSibling.style.display = 'flex';
1512
+ });
1513
+ items.forEach(item => {
1514
+ item.style.display = 'block';
1515
+ item.classList.add('visible');
1516
+ });
1517
+ }
1518
+ }
1519
+
1520
+ function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, selectedCategory) {
1521
  document.getElementById('modal-name').textContent = name;
1522
  document.getElementById('modal-price').textContent = `$${price}`;
1523
+ document.getElementById('modal-img').src = image || '/static/placeholder.jpg';
1524
+ document.getElementById('modal-description').textContent = description || 'No description available.';
1525
+ document.getElementById('modal-ingredients').textContent = ingredients || 'Not specified';
1526
+ document.getElementById('modal-nutrition').textContent = nutrition || 'Not available';
1527
+ document.getElementById('modal-allergens').textContent = allergens || 'None listed';
1528
  document.getElementById('modal-section').dataset.section = section;
1529
+ document.getElementById('modal-section').dataset.category = selectedCategory;
1530
  document.getElementById('quantityInput').value = '1';
1531
  document.getElementById('modal-instructions').value = '';
1532
  fetchAddons(name, section);
 
1535
  function fetchAddons(itemName, section) {
1536
  const addonsList = document.getElementById('addons-list');
1537
  addonsList.innerHTML = 'Loading customization options...';
1538
+ fetch(`/api/addons?item_name=${encodeURIComponent(itemName)}&item_section=${encodeURIComponent(section)}`)
1539
  .then(response => response.json())
1540
  .then(data => {
1541
  addonsList.innerHTML = '';
1542
+ if (!data.success || !data.addons || data.addons.length === 0) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1543
  addonsList.innerHTML = '<p>No customization options available.</p>';
1544
+ return;
1545
  }
1546
+ data.addons.forEach(addon => {
1547
+ const sectionDiv = document.createElement('div');
1548
+ sectionDiv.className = 'addon-section';
1549
+ sectionDiv.innerHTML = `<h6>${addon.name}</h6>`;
1550
+ const optionsContainer = document.createElement('div');
1551
+ addon.options.forEach((option, index) => {
1552
+ const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1553
+ const listItem = document.createElement('div');
1554
+ listItem.className = 'form-check';
1555
+ listItem.innerHTML = `
1556
+ <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1557
+ data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1558
+ <label class="form-check-label" for="${optionId}">
1559
+ ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
1560
+ </label>
1561
+ `;
1562
+ optionsContainer.appendChild(listItem);
1563
+ });
1564
+ sectionDiv.appendChild(optionsContainer);
1565
+ addonsList.appendChild(sectionDiv);
1566
+ });
1567
  })
1568
+ .catch(() => {
1569
  addonsList.innerHTML = '<p>Error loading customization options.</p>';
1570
  });
1571
  }
1572
 
1573
+ document.addEventListener('click', function(event) {
1574
+ if (event.target.classList.contains('addon-option')) {
1575
+ handleAddonClick(event.target);
1576
+ }
1577
+ });
1578
+
1579
+ function handleAddonClick(checkbox) {
1580
+ const groupName = checkbox.getAttribute('data-group');
1581
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it"].includes(groupName);
1582
+ const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1583
+ if (!isMultiSelectGroup) {
1584
+ checkboxes.forEach(cb => {
1585
+ if (cb !== checkbox) cb.checked = false;
1586
+ });
1587
+ }
1588
+ }
1589
+
1590
  function addToCartFromModal() {
1591
  if (isProcessingRequest) return;
1592
  isProcessingRequest = true;
 
1602
  isProcessingRequest = false;
1603
  return;
1604
  }
1605
+ const addons = Array.from(document.querySelectorAll('.addon-option:checked')).map(cb => ({
1606
+ name: cb.getAttribute('data-name'),
1607
+ price: parseFloat(cb.getAttribute('data-price') || 0)
1608
+ }));
 
 
 
1609
  const cartPayload = {
1610
  itemName: itemName,
1611
  itemPrice: itemPrice,