lokesh341 commited on
Commit
a2262a9
·
verified ·
1 Parent(s): 4fb1436

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +112 -569
templates/menu.html CHANGED
@@ -1,4 +1,4 @@
1
- <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
@@ -49,14 +49,14 @@
49
  border-radius: 15px 15px 0 0;
50
  opacity: 0;
51
  transition: opacity 0.5s ease-in-out;
52
- background-color: #000;
53
  }
54
  .menu-video.loaded {
55
  opacity: 1;
56
  }
57
  .menu-card:hover .menu-video {
58
  opacity: 1;
59
- transform: scale(1.05);
60
  }
61
  .menu-card .card-body .card-title {
62
  font-size: 1.2rem;
@@ -131,7 +131,7 @@
131
  top: 50%;
132
  transform: translateY(-50%);
133
  display: flex;
134
- align-items: center;
135
  justify-content: center;
136
  }
137
  .avatar-icon {
@@ -146,64 +146,33 @@
146
  color: white;
147
  font-size: 20px;
148
  font-weight: bold;
149
- position: relative;
150
- transition: transform 0.2s ease;
151
- }
152
- .avatar-icon:hover {
153
- transform: scale(1.1);
154
- }
155
- .avatar-icon img {
156
- width: 100%;
157
- height: 100%;
158
- object-fit: cover;
159
- border-radius: 50%;
160
- transition: transform 0.2s ease;
161
- }
162
- .avatar-icon img:hover {
163
- transform: scale(1.1);
164
  }
165
  .dropdown-menu {
166
  position: absolute;
167
  right: 0;
168
  top: 100%;
169
  background-color: #fff8f0;
170
- border-radius: 8px;
171
- width: 200px;
172
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
173
  display: none;
174
  border: 1px solid #ffd8b1;
175
- padding: 5px 0;
176
- z-index: 1000;
177
  }
178
  .dropdown-menu .dropdown-item {
179
- padding: 10px 16px;
180
  text-decoration: none;
181
  color: #333;
182
- font-size: 14px;
183
- transition: background-color 0.2s ease, color 0.2s ease;
184
- cursor: pointer;
 
 
 
 
185
  }
186
  .dropdown-menu .dropdown-item:hover {
187
  background-color: #ffe4c4;
188
- color: #2c2c2c;
189
- }
190
- .upload-item,
191
- .delete-item,
192
- .view-item,
193
- .profile-item {
194
- padding: 10px 16px;
195
- text-decoration: none;
196
  color: #333;
197
- font-size: 14px;
198
- transition: background-color 0.2s ease;
199
- cursor: pointer;
200
- }
201
- .upload-item:hover,
202
- .delete-item:hover,
203
- .view-item:hover,
204
- .profile-item:hover {
205
- background-color: #ffe4c4;
206
- color: #2c2c2c;
207
  }
208
  .fixed-top-bar {
209
  position: relative;
@@ -260,17 +229,6 @@
260
  .mic-icon.active {
261
  color: #007bff;
262
  }
263
- .mic-unsupported {
264
- display: none;
265
- position: absolute;
266
- right: 15px;
267
- font-size: 14px;
268
- color: #888;
269
- background-color: #fff;
270
- padding: 2px 8px;
271
- border-radius: 10px;
272
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
273
- }
274
  .autocomplete-suggestions {
275
  position: absolute;
276
  top: 100%;
@@ -363,16 +321,6 @@
363
  max-height: 60vh;
364
  overflow-y: auto;
365
  padding: 15px;
366
- text-align: center;
367
- }
368
- .modal-body #avatar-modal-img {
369
- max-width: 100%;
370
- max-height: 400px;
371
- object-fit: contain;
372
- border-radius: 50%; /* Make the avatar image round */
373
- margin: 0 auto;
374
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
375
- border: 2px solid #0FAA39; /* Optional: Add a border for style */
376
  }
377
  .modal-body #modal-img {
378
  max-height: 200px;
@@ -634,6 +582,8 @@
634
  font-size: 12px;
635
  margin-left: 8px;
636
  }
 
 
637
  .mic-popup {
638
  position: fixed;
639
  top: 50%;
@@ -650,20 +600,24 @@
650
  max-width: 90%;
651
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
652
  }
 
653
  .mic-popup.active {
654
  display: block;
655
  }
 
656
  .mic-popup-icon {
657
  font-size: 48px;
658
  margin-bottom: 20px;
659
  color: #ff4444;
660
  animation: pulse 1.5s infinite;
661
  }
 
662
  .mic-popup-text {
663
  font-size: 18px;
664
  margin-bottom: 15px;
665
  min-height: 24px;
666
  }
 
667
  .mic-popup-cancel {
668
  background-color: #ff4444;
669
  color: white;
@@ -673,11 +627,13 @@
673
  cursor: pointer;
674
  font-weight: bold;
675
  }
 
676
  @keyframes pulse {
677
  0% { transform: scale(1); }
678
  50% { transform: scale(1.1); }
679
  100% { transform: scale(1); }
680
  }
 
681
  @media (max-width: 576px) {
682
  .fixed-top-bar {
683
  height: 60px;
@@ -712,18 +668,11 @@
712
  font-size: 20px;
713
  }
714
  .dropdown-menu {
715
- width: 180px;
716
  }
717
  .dropdown-menu .dropdown-item {
718
- padding: 8px 12px;
719
- font-size: 13px;
720
- }
721
- .upload-item,
722
- .delete-item,
723
- .view-item,
724
- .profile-item {
725
- padding: 8px 12px;
726
- font-size: 13px;
727
  }
728
  .category-buttons {
729
  gap: 8px;
@@ -746,9 +695,6 @@
746
  max-height: 50vh;
747
  padding: 8px;
748
  }
749
- .modal-body #avatar-modal-img {
750
- max-height: 300px;
751
- }
752
  .modal-body #modal-img {
753
  max-height: 150px;
754
  width: 100%;
@@ -861,6 +807,7 @@
861
  .toggle-details {
862
  font-size: 0.8rem;
863
  }
 
864
  .mic-popup {
865
  padding: 20px;
866
  width: 280px;
@@ -880,28 +827,15 @@
880
  </style>
881
  </head>
882
  <body>
 
883
  <div class="fixed-top-bar">
884
  <div class="avatar-dropdown-container">
885
- <div class="avatar-icon" id="avatarIcon">
886
- {% if user_image %}
887
- <img src="{{ user_image }}" alt="User Avatar" class="avatar-image">
888
- {% else %}
889
- <span>{{ first_letter }}</span>
890
- {% endif %}
891
  </div>
892
- <div class="dropdown-menu" id="avatarDropdown">
893
- {% if user_image %}
894
- <div class="dropdown-item delete-item" id="deleteAvatar">Delete Image</div>
895
- <div class="dropdown-item view-item" id="viewAvatar">View Avatar</div>
896
- <div class="dropdown-item profile-item" id="viewProfile">View Profile Customer Details</div>
897
- {% endif %}
898
  <a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item">Order History</a>
899
- <div class="dropdown-item upload-item">
900
- <label for="avatarUpload" style="cursor: pointer; margin: 0; width: 100%;">
901
- Upload Image
902
- </label>
903
- <input type="file" id="avatarUpload" accept="image/*" style="display: none;">
904
- </div>
905
  <a href="{{ url_for('logout') }}" class="dropdown-item">Logout</a>
906
  </div>
907
  </div>
@@ -909,7 +843,6 @@
909
  <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
910
  <i class="bi bi-search search-icon"></i>
911
  <i class="bi bi-mic mic-icon" id="micIcon"></i>
912
- <span class="mic-unsupported" id="micUnsupported">Mic not supported</span>
913
  <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
914
  </div>
915
  </div>
@@ -929,7 +862,7 @@
929
  {% if selected_category == "Customized Dish" %}
930
  <div id="custom-dish-form" class="mt-4">
931
  <h3>Create Your Custom Dish</h3>
932
- <form method="POST" action="/customdish/generate_custom_dish" id="customDishForm">
933
  <div class="mb-3">
934
  <label for="custom-dish-name" class="form-label">Dish Name</label>
935
  <input type="text" class="form-control" id="custom-dish-name" name="name" required>
@@ -989,11 +922,11 @@
989
  <button class="btn btn-primary"
990
  data-bs-toggle="modal"
991
  data-bs-target="#itemModal"
992
- onclick="showItemDetails('{{ item.Name | default('Unnamed Item') | e }}', '{{ item.Price__c | default('0.00') }}', '{{ item.Image2__c | default(item.Image1__c) | default('/static/placeholder.jpg') }}', '{{ item.Description__c | default('No description') | e }}', '{{ item.IngredientsInfo__c | default('Not specified') | e }}', '{{ item.NutritionalInfo__c | default('Not available') | e }}', '{{ item.Allergens__c | default('None listed') | e }}', '{{ item.Section__c | default(section) | e }}', '{{ selected_category | e }}')">
993
  ADD
994
  </button>
995
  {% endif %}
996
- {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c != 'Soft Drinks' %}
997
  <span class="customisable-text">Customisable</span>
998
  {% endif %}
999
  </div>
@@ -1004,7 +937,7 @@
1004
  <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
1005
  <h6>Description</h6>
1006
  <p>{{ item.Description__c | default('No description available') }}</p>
1007
- <h6>Ingredients Info</h6>
1008
  <p>{{ item.IngredientsInfo__c | default('Not specified') }}</p>
1009
  <h6>Nutritional Info</h6>
1010
  <p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
@@ -1071,46 +1004,6 @@
1071
  </div>
1072
  </div>
1073
 
1074
- <!-- Modal for Avatar View -->
1075
- <div class="modal fade" id="avatarModal" tabindex="-1" aria-labelledby="avatarModalLabel" aria-hidden="true">
1076
- <div class="modal-dialog modal-dialog-centered modal-lg">
1077
- <div class="modal-content">
1078
- <div class="modal-header">
1079
- <h5 class="modal-title" id="avatarModalLabel">View Avatar</h5>
1080
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1081
- </div>
1082
- <div class="modal-body">
1083
- <img id="avatar-modal-img" class="img-fluid rounded mx-auto d-block" alt="Avatar Image" style="max-height: 400px; object-fit: contain; border-radius: 50%; border: 2px solid #0FAA39;">
1084
- </div>
1085
- <div class="modal-footer">
1086
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
1087
- </div>
1088
- </div>
1089
- </div>
1090
- </div>
1091
-
1092
- <!-- Modal for Profile Customer Details -->
1093
- <div class="modal fade" id="profileModal" tabindex="-1" aria-labelledby="profileModalLabel" aria-hidden="true">
1094
- <div class="modal-dialog modal-dialog-centered modal-lg">
1095
- <div class="modal-content">
1096
- <div class="modal-header">
1097
- <h5 class="modal-title" id="profileModalLabel">Customer Profile Details</h5>
1098
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1099
- </div>
1100
- <div class="modal-body">
1101
- <p><strong>Name:</strong> {{ user_name | default('Not specified') }}</p>
1102
- <p><strong>Email:</strong> {{ user_email | default('Not specified') }}</p>
1103
- <p><strong>Joined Date:</strong> {{ user_joined_date | default('Not specified') }}</p>
1104
- <p><strong>Total Orders:</strong> {{ total_orders | default('0') }}</p>
1105
- <!-- Add more profile details as needed -->
1106
- </div>
1107
- <div class="modal-footer">
1108
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
1109
- </div>
1110
- </div>
1111
- </div>
1112
- </div>
1113
-
1114
  <!-- Modal for Soft Drinks Quantity Selection -->
1115
  <div class="modal fade" id="softDrinkModal" tabindex="-1" aria-labelledby="softDrinkModalLabel" aria-hidden="true">
1116
  <div class="modal-dialog modal-dialog-centered">
@@ -1151,13 +1044,27 @@
1151
 
1152
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1153
  <script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1154
  let isProcessingRequest = false;
1155
  let currentSoftDrinkButton = null;
1156
 
1157
  const menuItems = [
1158
  {% for section, items in ordered_menu.items() %}
1159
  {% for item in items %}
1160
- "{{ item.Name | default('Unnamed Item') | e }}",
1161
  {% endfor %}
1162
  {% endfor %}
1163
  ];
@@ -1178,13 +1085,6 @@
1178
  "Whole Wheat Flour", "Yogurt (Curd)"
1179
  ];
1180
 
1181
- // Utility function to sanitize input to prevent XSS
1182
- function sanitizeInput(input) {
1183
- const div = document.createElement('div');
1184
- div.textContent = input;
1185
- return div.innerHTML;
1186
- }
1187
-
1188
  function addToCartLocalStorage(payload) {
1189
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1190
  const existingItem = cart.find(item =>
@@ -1234,7 +1134,7 @@
1234
  function showSoftDrinkModal(button) {
1235
  currentSoftDrinkButton = button;
1236
  const buttonContainer = button.closest('.button-container');
1237
- const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
1238
  const itemPrice = buttonContainer.getAttribute('data-item-price');
1239
  const itemImage = buttonContainer.getAttribute('data-item-image');
1240
 
@@ -1254,11 +1154,11 @@
1254
  const buttonContainer = currentSoftDrinkButton.closest('.button-container');
1255
  const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
1256
 
1257
- const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
1258
  const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1259
  const itemImage = buttonContainer.getAttribute('data-item-image');
1260
- const section = sanitizeInput(buttonContainer.getAttribute('data-item-section'));
1261
- const selectedCategory = sanitizeInput(buttonContainer.getAttribute('data-item-category'));
1262
 
1263
  const cartPayload = {
1264
  itemName: itemName,
@@ -1281,13 +1181,11 @@
1281
  .then(response => response.json())
1282
  .then(data => {
1283
  if (data.success) {
1284
- alert('Item added to cart successfully!');
1285
  updateCartUI(data.cart);
1286
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1287
  modal.hide();
1288
  } else {
1289
  console.error('Failed to add item to cart:', data.error);
1290
- alert(data.error || 'Failed to add item to cart. Using local storage as fallback.');
1291
  const cart = addToCartLocalStorage(cartPayload);
1292
  updateCartUI(cart);
1293
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
@@ -1296,7 +1194,6 @@
1296
  })
1297
  .catch(err => {
1298
  console.error('Error adding item to cart:', err);
1299
- alert('Error adding item to cart. Using local storage as fallback.');
1300
  const cart = addToCartLocalStorage(cartPayload);
1301
  updateCartUI(cart);
1302
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
@@ -1325,345 +1222,20 @@
1325
  document.addEventListener('DOMContentLoaded', function () {
1326
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1327
  const dropdownMenu = document.querySelector('.dropdown-menu');
1328
- const avatarIcon = document.getElementById('avatarIcon');
1329
- const avatarUpload = document.getElementById('avatarUpload');
1330
- const deleteAvatar = document.getElementById('deleteAvatar');
1331
- const viewAvatar = document.getElementById('viewAvatar');
1332
- const viewProfile = document.getElementById('viewProfile');
1333
-
1334
- // Load avatar from localStorage if available
1335
- function loadAvatar() {
1336
- const savedAvatar = localStorage.getItem('userAvatar');
1337
- const avatarImage = avatarIcon.querySelector('.avatar-image');
1338
- if (savedAvatar && !avatarImage) {
1339
- const img = document.createElement('img');
1340
- img.src = savedAvatar;
1341
- img.alt = 'User Avatar';
1342
- img.className = 'avatar-image';
1343
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1344
- avatarIcon.innerHTML = '';
1345
- avatarIcon.appendChild(img);
1346
- ensureAvatarOptions();
1347
- } else if (!savedAvatar && avatarImage) {
1348
- avatarIcon.innerHTML = `<span>{{ first_letter }}</span>`;
1349
- removeAvatarOptions();
1350
- }
1351
- }
1352
-
1353
- // Ensure avatar options (Delete, View, Profile) are present only once
1354
- function ensureAvatarOptions() {
1355
- if (!document.querySelector('#deleteAvatar')) {
1356
- const deleteItem = document.createElement('div');
1357
- deleteItem.className = 'dropdown-item delete-item';
1358
- deleteItem.id = 'deleteAvatar';
1359
- deleteItem.innerText = 'Delete Image';
1360
- dropdownMenu.insertBefore(deleteItem, dropdownMenu.firstChild);
1361
- addDeleteListener(deleteItem);
1362
- }
1363
- if (!document.querySelector('#viewAvatar')) {
1364
- const viewItem = document.createElement('div');
1365
- viewItem.className = 'dropdown-item view-item';
1366
- viewItem.id = 'viewAvatar';
1367
- viewItem.innerText = 'View Avatar';
1368
- const firstChild = dropdownMenu.firstChild;
1369
- if (firstChild) dropdownMenu.insertBefore(viewItem, firstChild.nextSibling);
1370
- addViewListener(viewItem);
1371
- }
1372
- if (!document.querySelector('#viewProfile')) {
1373
- const profileItem = document.createElement('div');
1374
- profileItem.className = 'dropdown-item profile-item';
1375
- profileItem.id = 'viewProfile';
1376
- profileItem.innerText = 'View Profile Customer Details';
1377
- const viewItem = document.getElementById('viewAvatar');
1378
- if (viewItem) dropdownMenu.insertBefore(profileItem, viewItem.nextSibling);
1379
- addProfileListener(profileItem);
1380
- }
1381
- }
1382
-
1383
- // Remove avatar options
1384
- function removeAvatarOptions() {
1385
- const deleteItem = document.getElementById('deleteAvatar');
1386
- const viewItem = document.getElementById('viewAvatar');
1387
- const profileItem = document.getElementById('viewProfile');
1388
- if (deleteItem) deleteItem.remove();
1389
- if (viewItem) viewItem.remove();
1390
- if (profileItem) profileItem.remove();
1391
- }
1392
-
1393
- // Save avatar to localStorage with size check and compression
1394
- function saveAvatar(imageUrl) {
1395
- const img = new Image();
1396
- img.src = imageUrl;
1397
- img.onload = function() {
1398
- const maxSize = 50 * 1024 * 1024; // 50MB in bytes
1399
- const canvas = document.createElement('canvas');
1400
- const ctx = canvas.getContext('2d');
1401
- let width = img.width;
1402
- let height = img.height;
1403
-
1404
- // Resize if larger than 50MB (approximate based on pixel data)
1405
- if (img.size > maxSize || (width * height * 4) > maxSize) {
1406
- const aspectRatio = width / height;
1407
- if (width > height) {
1408
- width = 800; // Reduce to a reasonable size
1409
- height = width / aspectRatio;
1410
- } else {
1411
- height = 800;
1412
- width = height * aspectRatio;
1413
- }
1414
- canvas.width = width;
1415
- canvas.height = height;
1416
- ctx.drawImage(img, 0, 0, width, height);
1417
- const compressedDataUrl = canvas.toDataURL('image/jpeg', 0.7); // Compress to JPEG
1418
- localStorage.setItem('userAvatar', compressedDataUrl);
1419
- } else {
1420
- localStorage.setItem('userAvatar', imageUrl);
1421
- }
1422
- };
1423
- }
1424
-
1425
- // Clear avatar from localStorage
1426
- function clearAvatar() {
1427
- localStorage.removeItem('userAvatar');
1428
- avatarIcon.innerHTML = `<span>{{ first_letter }}</span>`;
1429
- removeAvatarOptions();
1430
- }
1431
-
1432
- // Avatar click handler
1433
- avatarIcon.addEventListener('click', function(event) {
1434
  event.stopPropagation();
1435
  dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1436
  });
1437
-
1438
- // Handle image upload with 50MB limit and compression
1439
- avatarUpload.addEventListener('change', function(event) {
1440
- const file = event.target.files[0];
1441
- if (file) {
1442
- const maxSize = 50 * 1024 * 1024; // 50MB in bytes
1443
- if (file.size > maxSize) {
1444
- alert('Image size must not exceed 50MB. Compressing and resizing...');
1445
- compressImage(file);
1446
- return;
1447
- }
1448
- const reader = new FileReader();
1449
- reader.onload = function(e) {
1450
- const base64Image = e.target.result;
1451
- fetch('/upload_avatar', {
1452
- method: 'POST',
1453
- headers: {
1454
- 'Content-Type': 'application/json',
1455
- },
1456
- body: JSON.stringify({ image: base64Image })
1457
- })
1458
- .then(response => {
1459
- if (!response.ok) throw new Error('Upload failed');
1460
- return response.json();
1461
- })
1462
- .then(data => {
1463
- if (data.success) {
1464
- const img = document.createElement('img');
1465
- img.src = data.image || '/static/default-avatar.jpg';
1466
- img.alt = 'User Avatar';
1467
- img.className = 'avatar-image';
1468
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1469
- avatarIcon.innerHTML = '';
1470
- avatarIcon.appendChild(img);
1471
- saveAvatar(data.image);
1472
- ensureAvatarOptions();
1473
- dropdownMenu.style.display = 'none';
1474
- } else {
1475
- alert('Failed to upload image: ' + (data.error || 'Unknown error. Using default image.'));
1476
- const img = document.createElement('img');
1477
- img.src = '/static/default-avatar.jpg';
1478
- img.alt = 'User Avatar';
1479
- img.className = 'avatar-image';
1480
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1481
- avatarIcon.innerHTML = '';
1482
- avatarIcon.appendChild(img);
1483
- saveAvatar('/static/default-avatar.jpg');
1484
- ensureAvatarOptions();
1485
- dropdownMenu.style.display = 'none';
1486
- }
1487
- })
1488
- .catch(error => {
1489
- console.error('Upload error:', error);
1490
- alert('Error uploading image. Using default image as fallback.');
1491
- const img = document.createElement('img');
1492
- img.src = '/static/default-avatar.jpg';
1493
- img.alt = 'User Avatar';
1494
- img.className = 'avatar-image';
1495
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1496
- avatarIcon.innerHTML = '';
1497
- avatarIcon.appendChild(img);
1498
- saveAvatar('/static/default-avatar.jpg');
1499
- ensureAvatarOptions();
1500
- dropdownMenu.style.display = 'none';
1501
- });
1502
- };
1503
- reader.onerror = function() {
1504
- alert('Error reading the image file. Using default image as fallback.');
1505
- const img = document.createElement('img');
1506
- img.src = '/static/default-avatar.jpg';
1507
- img.alt = 'User Avatar';
1508
- img.className = 'avatar-image';
1509
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1510
- avatarIcon.innerHTML = '';
1511
- avatarIcon.appendChild(img);
1512
- saveAvatar('/static/default-avatar.jpg');
1513
- ensureAvatarOptions();
1514
- dropdownMenu.style.display = 'none';
1515
- };
1516
- reader.readAsDataURL(file);
1517
  }
1518
  });
1519
-
1520
- // Compress image if it exceeds 50MB
1521
- function compressImage(file) {
1522
- const reader = new FileReader();
1523
- reader.onload = function(e) {
1524
- const img = new Image();
1525
- img.src = e.target.result;
1526
- img.onload = function() {
1527
- const canvas = document.createElement('canvas');
1528
- const ctx = canvas.getContext('2d');
1529
- let width = img.width;
1530
- let height = img.height;
1531
- const maxWidth = 800; // Max width for compression
1532
- const maxHeight = 800; // Max height for compression
1533
-
1534
- if (width > height) {
1535
- if (width > maxWidth) {
1536
- height *= maxWidth / width;
1537
- width = maxWidth;
1538
- }
1539
- } else {
1540
- if (height > maxHeight) {
1541
- width *= maxHeight / height;
1542
- height = maxHeight;
1543
- }
1544
- }
1545
-
1546
- canvas.width = width;
1547
- canvas.height = height;
1548
- ctx.drawImage(img, 0, 0, width, height);
1549
- const compressedDataUrl = canvas.toDataURL('image/jpeg', 0.7); // Compress to 70% quality
1550
- fetch('/upload_avatar', {
1551
- method: 'POST',
1552
- headers: { 'Content-Type': 'application/json' },
1553
- body: JSON.stringify({ image: compressedDataUrl })
1554
- })
1555
- .then(response => response.json())
1556
- .then(data => {
1557
- if (data.success) {
1558
- const imgElement = document.createElement('img');
1559
- imgElement.src = data.image || '/static/default-avatar.jpg';
1560
- imgElement.alt = 'User Avatar';
1561
- imgElement.className = 'avatar-image';
1562
- imgElement.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1563
- avatarIcon.innerHTML = '';
1564
- avatarIcon.appendChild(imgElement);
1565
- saveAvatar(data.image);
1566
- ensureAvatarOptions();
1567
- dropdownMenu.style.display = 'none';
1568
- } else {
1569
- alert('Failed to upload compressed image: ' + (data.error || 'Unknown error'));
1570
- const img = document.createElement('img');
1571
- img.src = '/static/default-avatar.jpg';
1572
- img.alt = 'User Avatar';
1573
- img.className = 'avatar-image';
1574
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1575
- avatarIcon.innerHTML = '';
1576
- avatarIcon.appendChild(img);
1577
- saveAvatar('/static/default-avatar.jpg');
1578
- ensureAvatarOptions();
1579
- dropdownMenu.style.display = 'none';
1580
- }
1581
- })
1582
- .catch(error => {
1583
- console.error('Upload error:', error);
1584
- alert('Error uploading compressed image. Using default image.');
1585
- const img = document.createElement('img');
1586
- img.src = '/static/default-avatar.jpg';
1587
- img.alt = 'User Avatar';
1588
- img.className = 'avatar-image';
1589
- img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1590
- avatarIcon.innerHTML = '';
1591
- avatarIcon.appendChild(img);
1592
- saveAvatar('/static/default-avatar.jpg');
1593
- ensureAvatarOptions();
1594
- dropdownMenu.style.display = 'none';
1595
- });
1596
- };
1597
- };
1598
- reader.readAsDataURL(file);
1599
- }
1600
-
1601
- // Handle image deletion
1602
- function addDeleteListener(deleteElement) {
1603
- deleteElement.addEventListener('click', function() {
1604
- fetch('/delete_avatar', {
1605
- method: 'POST',
1606
- headers: {
1607
- 'Content-Type': 'application/json'
1608
- }
1609
- })
1610
- .then(response => {
1611
- if (!response.ok) throw new Error('Delete failed');
1612
- return response.json();
1613
- })
1614
- .then(data => {
1615
- if (data.success) {
1616
- clearAvatar();
1617
- dropdownMenu.style.display = 'none';
1618
- } else {
1619
- alert('Failed to delete image: ' + (data.error || 'Unknown error'));
1620
- }
1621
- })
1622
- .catch(error => {
1623
- console.error('Delete error:', error);
1624
- alert('Error deleting image. Please try again.');
1625
- });
1626
- });
1627
- }
1628
-
1629
- // Handle avatar view
1630
- function addViewListener(viewElement) {
1631
- viewElement.addEventListener('click', function() {
1632
- const avatarImage = avatarIcon.querySelector('.avatar-image');
1633
- if (avatarImage) {
1634
- const modalImg = document.getElementById('avatar-modal-img');
1635
- modalImg.src = avatarImage.src;
1636
- const modal = new bootstrap.Modal(document.getElementById('avatarModal'));
1637
- modal.show();
1638
- } else {
1639
- alert('No avatar image to view.');
1640
- }
1641
- dropdownMenu.style.display = 'none';
1642
- });
1643
- }
1644
-
1645
- // Handle profile view
1646
- function addProfileListener(profileElement) {
1647
- profileElement.addEventListener('click', function() {
1648
- const modal = new bootstrap.Modal(document.getElementById('profileModal'));
1649
- modal.show();
1650
  dropdownMenu.style.display = 'none';
1651
  });
1652
- }
1653
-
1654
- // Add listeners if elements exist
1655
- if (deleteAvatar) addDeleteListener(deleteAvatar);
1656
- if (viewAvatar) addViewListener(viewAvatar);
1657
- if (viewProfile) addProfileListener(viewProfile);
1658
-
1659
- // Load avatar on page load
1660
- loadAvatar();
1661
-
1662
- // Close dropdown when clicking outside
1663
- document.addEventListener('click', function(event) {
1664
- if (!avatarContainer.contains(event.target)) {
1665
- dropdownMenu.style.display = 'none';
1666
- }
1667
  });
1668
 
1669
  const menuCards = document.querySelectorAll('.menu-card');
@@ -1744,12 +1316,10 @@
1744
  categoryForm.submit();
1745
  });
1746
  });
1747
-
1748
  const searchBar = document.getElementById('searchBar');
1749
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1750
- const debouncedFilterMenu = debounce(filterMenu, 300);
1751
  searchBar.addEventListener('input', function () {
1752
- const input = sanitizeInput(this.value.trim().toLowerCase());
1753
  suggestionsContainer.innerHTML = '';
1754
  suggestionsContainer.style.display = 'none';
1755
  if (input) {
@@ -1764,21 +1334,20 @@
1764
  suggestionDiv.addEventListener('click', function () {
1765
  searchBar.value = item;
1766
  suggestionsContainer.style.display = 'none';
1767
- debouncedFilterMenu();
1768
  });
1769
  suggestionsContainer.appendChild(suggestionDiv);
1770
  });
1771
  suggestionsContainer.style.display = 'block';
1772
  }
1773
  }
1774
- debouncedFilterMenu();
1775
  });
1776
  document.addEventListener('click', function (event) {
1777
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1778
  suggestionsContainer.style.display = 'none';
1779
  }
1780
  });
1781
-
1782
  const descriptionTextarea = document.getElementById('custom-dish-description');
1783
  const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1784
  if (descriptionTextarea && descriptionSuggestions) {
@@ -1832,45 +1401,6 @@
1832
  }
1833
  });
1834
  }
1835
-
1836
- // Custom Dish Form Validation
1837
- const customDishForm = document.getElementById('customDishForm');
1838
- if (customDishForm) {
1839
- customDishForm.addEventListener('submit', function(event) {
1840
- const dishName = document.getElementById('custom-dish-name').value.trim();
1841
- const description = document.getElementById('custom-dish-description').value.trim();
1842
- if (!dishName || !description) {
1843
- event.preventDefault();
1844
- alert('Please fill in both the dish name and description.');
1845
- return;
1846
- }
1847
- event.preventDefault();
1848
- fetch('/customdish/generate_custom_dish', {
1849
- method: 'POST',
1850
- headers: {
1851
- 'Content-Type': 'application/x-www-form-urlencoded',
1852
- },
1853
- body: new URLSearchParams({
1854
- 'name': dishName,
1855
- 'description': description
1856
- })
1857
- })
1858
- .then(response => response.json())
1859
- .then(data => {
1860
- if (data.success) {
1861
- alert('Custom dish submitted successfully!');
1862
- window.location.reload();
1863
- } else {
1864
- alert('Failed to submit custom dish: ' + (data.error || 'Unknown error'));
1865
- }
1866
- })
1867
- .catch(error => {
1868
- console.error('Error submitting custom dish:', error);
1869
- alert('Error submitting custom dish. Please try again.');
1870
- });
1871
- });
1872
- }
1873
-
1874
  fetch('/cart/get')
1875
  .then(response => {
1876
  if (!response.ok) {
@@ -1892,14 +1422,12 @@
1892
  const cart = getCartLocalStorage();
1893
  updateCartUI(cart);
1894
  });
1895
-
1896
  const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1897
  preloadedVideos.forEach(link => {
1898
  const video = document.createElement('video');
1899
  video.src = link.href;
1900
  video.preload = 'auto';
1901
  });
1902
-
1903
  const decreaseBtn = document.getElementById('decreaseQuantity');
1904
  const increaseBtn = document.getElementById('increaseQuantity');
1905
  const quantityInput = document.getElementById('quantityInput');
@@ -1938,12 +1466,11 @@
1938
 
1939
  // Mic Popup Functionality
1940
  const micIcon = document.getElementById('micIcon');
1941
- const micUnsupported = document.getElementById('micUnsupported');
1942
  const micPopup = document.getElementById('micPopup');
1943
  const micPopupText = document.getElementById('micPopupText');
1944
  const micPopupCancel = document.getElementById('micPopupCancel');
1945
 
1946
- if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
1947
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1948
  const recognition = new SpeechRecognition();
1949
  recognition.continuous = false;
@@ -1969,13 +1496,15 @@
1969
  }
1970
  }
1971
 
 
1972
  if (interimTranscript) {
1973
  micPopupText.textContent = interimTranscript;
1974
  }
1975
 
 
1976
  if (finalTranscript) {
1977
- searchBar.value = sanitizeInput(finalTranscript.trim());
1978
- debouncedFilterMenu();
1979
  micPopup.classList.remove('active');
1980
  }
1981
  };
@@ -1983,6 +1512,7 @@
1983
  recognition.onend = () => {
1984
  micIcon.classList.remove('active');
1985
  if (micPopup.classList.contains('active')) {
 
1986
  setTimeout(() => {
1987
  micPopup.classList.remove('active');
1988
  }, 1000);
@@ -2017,11 +1547,11 @@
2017
  });
2018
  } else {
2019
  micIcon.style.display = 'none';
2020
- micUnsupported.style.display = 'block';
2021
  }
2022
  });
 
2023
  function filterMenu() {
2024
- const input = sanitizeInput(document.getElementById('searchBar').value.trim().toLowerCase());
2025
  const sections = document.querySelectorAll('h3');
2026
  const items = document.querySelectorAll('.menu-card');
2027
  let matchedSections = new Set();
@@ -2057,14 +1587,15 @@
2057
  });
2058
  }
2059
  }
2060
- function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, selectedCategory) {
 
2061
  document.getElementById('modal-name').innerText = name;
2062
  document.getElementById('modal-price').innerText = `$${price}`;
2063
  const modalImg = document.getElementById('modal-img');
2064
  modalImg.src = image || '/static/placeholder.jpg';
2065
  document.getElementById('modal-description').innerText = description || 'No description available.';
2066
  document.getElementById('modal-ingredients').innerText = ingredients || 'Not specified';
2067
- document.getElementById('modal-nutrition').innerText = nutrition || 'Not available';
2068
  document.getElementById('modal-allergens').innerText = allergens || 'None listed';
2069
  document.getElementById('addons-list').innerHTML = 'Loading customization options...';
2070
  document.getElementById('modal-instructions').value = '';
@@ -2072,6 +1603,7 @@
2072
  modalSectionEl.setAttribute('data-section', section);
2073
  modalSectionEl.setAttribute('data-category', selectedCategory);
2074
  document.getElementById('quantityInput').value = 1;
 
2075
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
2076
  .then(response => response.json())
2077
  .then(data => {
@@ -2102,7 +1634,7 @@
2102
  optionsContainer.appendChild(listItem);
2103
  });
2104
  sectionDiv.appendChild(optionsContainer);
2105
- addonsList.appendChild(sectionDiv);
2106
  });
2107
  })
2108
  .catch(err => {
@@ -2110,43 +1642,60 @@
2110
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
2111
  });
2112
  }
 
2113
  document.addEventListener('click', function(event) {
2114
  if (event.target.classList.contains('addon-option')) {
2115
  handleAddonClick(event.target);
2116
  }
2117
  });
 
2118
  function handleAddonClick(checkbox) {
2119
  const groupName = checkbox.getAttribute('data-group');
2120
- const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it"].includes(groupName);
2121
- const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
2122
  if (!isMultiSelectGroup) {
2123
- checkboxes.forEach(cb => {
2124
- if (cb !== checkbox) cb.checked = false;
 
 
 
2125
  });
2126
  }
2127
  }
 
2128
  function addToCartFromModal() {
2129
  const itemName = document.getElementById('modal-name').innerText;
2130
- const itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
 
 
 
 
2131
  const itemImage = document.getElementById('modal-img').src;
2132
- const instructions = document.getElementById('modal-instructions').value.trim();
2133
- const quantity = parseInt(document.getElementById('quantityInput').value);
2134
- const section = document.getElementById('modal-section').getAttribute('data-section');
2135
- const selectedCategory = document.getElementById('modal-section').getAttribute('data-category');
2136
- const addons = Array.from(document.querySelectorAll('.addon-option:checked')).map(cb => ({
2137
- name: cb.getAttribute('data-name'),
2138
- price: parseFloat(cb.getAttribute('data-price') || 0)
 
 
 
 
 
2139
  }));
 
 
2140
  const cartPayload = {
2141
  itemName: itemName,
2142
  itemPrice: itemPrice,
2143
  itemImage: itemImage,
2144
  section: section,
2145
  category: selectedCategory,
2146
- addons: addons,
2147
  instructions: instructions,
2148
  quantity: quantity
2149
  };
 
2150
  fetch('/cart/add', {
2151
  method: 'POST',
2152
  headers: {
@@ -2159,21 +1708,16 @@
2159
  if (data.success) {
2160
  alert('Item added to cart successfully!');
2161
  updateCartUI(data.cart);
2162
- const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
2163
- modal.hide();
2164
- } else {
2165
- console.error('Failed to add item to cart:', data.error);
2166
- alert(data.error || 'Failed to add item to cart. Using local storage as fallback.');
2167
- const cart = addToCartLocalStorage(cartPayload);
2168
- updateCartUI(cart);
2169
  const modal = document.getElementById('itemModal');
2170
  const modalInstance = bootstrap.Modal.getInstance(modal);
2171
  modalInstance.hide();
 
 
 
2172
  }
2173
  })
2174
  .catch(err => {
2175
  console.error('Error adding item to cart:', err);
2176
- alert('Error adding item to cart. Using local storage as fallback.');
2177
  const cart = addToCartLocalStorage(cartPayload);
2178
  updateCartUI(cart);
2179
  const modal = document.getElementById('itemModal');
@@ -2183,5 +1727,4 @@
2183
  }
2184
  </script>
2185
  </body>
2186
- </html>
2187
-
 
1
+ <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
 
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;
 
131
  top: 50%;
132
  transform: translateY(-50%);
133
  display: flex;
134
+ align-items: right;
135
  justify-content: center;
136
  }
137
  .avatar-icon {
 
146
  color: white;
147
  font-size: 20px;
148
  font-weight: bold;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
150
  .dropdown-menu {
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;
 
229
  .mic-icon.active {
230
  color: #007bff;
231
  }
 
 
 
 
 
 
 
 
 
 
 
232
  .autocomplete-suggestions {
233
  position: absolute;
234
  top: 100%;
 
321
  max-height: 60vh;
322
  overflow-y: auto;
323
  padding: 15px;
 
 
 
 
 
 
 
 
 
 
324
  }
325
  .modal-body #modal-img {
326
  max-height: 200px;
 
582
  font-size: 12px;
583
  margin-left: 8px;
584
  }
585
+
586
+ /* Mic Popup Styles */
587
  .mic-popup {
588
  position: fixed;
589
  top: 50%;
 
600
  max-width: 90%;
601
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
602
  }
603
+
604
  .mic-popup.active {
605
  display: block;
606
  }
607
+
608
  .mic-popup-icon {
609
  font-size: 48px;
610
  margin-bottom: 20px;
611
  color: #ff4444;
612
  animation: pulse 1.5s infinite;
613
  }
614
+
615
  .mic-popup-text {
616
  font-size: 18px;
617
  margin-bottom: 15px;
618
  min-height: 24px;
619
  }
620
+
621
  .mic-popup-cancel {
622
  background-color: #ff4444;
623
  color: white;
 
627
  cursor: pointer;
628
  font-weight: bold;
629
  }
630
+
631
  @keyframes pulse {
632
  0% { transform: scale(1); }
633
  50% { transform: scale(1.1); }
634
  100% { transform: scale(1); }
635
  }
636
+
637
  @media (max-width: 576px) {
638
  .fixed-top-bar {
639
  height: 60px;
 
668
  font-size: 20px;
669
  }
670
  .dropdown-menu {
671
+ width: 220px;
672
  }
673
  .dropdown-menu .dropdown-item {
674
+ padding: 12px 16px;
675
+ font-size: 15px;
 
 
 
 
 
 
 
676
  }
677
  .category-buttons {
678
  gap: 8px;
 
695
  max-height: 50vh;
696
  padding: 8px;
697
  }
 
 
 
698
  .modal-body #modal-img {
699
  max-height: 150px;
700
  width: 100%;
 
807
  .toggle-details {
808
  font-size: 0.8rem;
809
  }
810
+ /* Mic Popup Mobile Styles */
811
  .mic-popup {
812
  padding: 20px;
813
  width: 280px;
 
827
  </style>
828
  </head>
829
  <body>
830
+
831
  <div class="fixed-top-bar">
832
  <div class="avatar-dropdown-container">
833
+ <div class="avatar-icon">
834
+ <span>{{ first_letter }}</span>
 
 
 
 
835
  </div>
836
+ <div class="dropdown-menu">
837
+ <a href="{{ url_for('user_details.customer_details') }}" class="dropdown-item">View Profile</a>
 
 
 
 
838
  <a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item">Order History</a>
 
 
 
 
 
 
839
  <a href="{{ url_for('logout') }}" class="dropdown-item">Logout</a>
840
  </div>
841
  </div>
 
843
  <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
844
  <i class="bi bi-search search-icon"></i>
845
  <i class="bi bi-mic mic-icon" id="micIcon"></i>
 
846
  <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
847
  </div>
848
  </div>
 
862
  {% if selected_category == "Customized Dish" %}
863
  <div id="custom-dish-form" class="mt-4">
864
  <h3>Create Your Custom Dish</h3>
865
+ <form method="POST" action="/customdish/generate_custom_dish">
866
  <div class="mb-3">
867
  <label for="custom-dish-name" class="form-label">Dish Name</label>
868
  <input type="text" class="form-control" id="custom-dish-name" name="name" required>
 
922
  <button class="btn btn-primary"
923
  data-bs-toggle="modal"
924
  data-bs-target="#itemModal"
925
+ onclick="showItemDetails('{{ item.Name | default('Unnamed Item') }}', '{{ item.Price__c | default('0.00') }}', '{{ item.Image2__c | default(item.Image1__c) | default('/static/placeholder.jpg') }}', '{{ item.Description__c | default('No description') }}', '{{ item.IngredientsInfo__c | default('Not specified') }}', '{{ item.NutritionalInfo__c | default('Not available') }}', '{{ item.Allergens__c | default('None listed') }}', '{{ item.Section__c | default(section) }}', '{{ selected_category }}')">
926
  ADD
927
  </button>
928
  {% endif %}
929
+ {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
930
  <span class="customisable-text">Customisable</span>
931
  {% endif %}
932
  </div>
 
937
  <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
938
  <h6>Description</h6>
939
  <p>{{ item.Description__c | default('No description available') }}</p>
940
+ <h6>Ingredients</h6>
941
  <p>{{ item.IngredientsInfo__c | default('Not specified') }}</p>
942
  <h6>Nutritional Info</h6>
943
  <p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
 
1004
  </div>
1005
  </div>
1006
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1007
  <!-- Modal for Soft Drinks Quantity Selection -->
1008
  <div class="modal fade" id="softDrinkModal" tabindex="-1" aria-labelledby="softDrinkModalLabel" aria-hidden="true">
1009
  <div class="modal-dialog modal-dialog-centered">
 
1044
 
1045
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1046
  <script>
1047
+ // Dynamically pass menu item details from Salesforce via Flask
1048
+ const menuItemDetails = {
1049
+ {% for section, items in ordered_menu.items() %}
1050
+ {% for item in items %}
1051
+ "{{ item.Name | default('Unnamed Item') }}": {
1052
+ "description": "{{ item.Description__c | default('No description') }}",
1053
+ "ingredients": "{{ item.IngredientsInfo__c | default('Not specified') }}",
1054
+ "nutritionalInfo": "{{ item.NutritionalInfo__c | default('Not available') }}",
1055
+ "allergens": "{{ item.Allergens__c | default('None listed') }}"
1056
+ },
1057
+ {% endfor %}
1058
+ {% endfor %}
1059
+ };
1060
+
1061
  let isProcessingRequest = false;
1062
  let currentSoftDrinkButton = null;
1063
 
1064
  const menuItems = [
1065
  {% for section, items in ordered_menu.items() %}
1066
  {% for item in items %}
1067
+ "{{ item.Name | default('Unnamed Item') }}",
1068
  {% endfor %}
1069
  {% endfor %}
1070
  ];
 
1085
  "Whole Wheat Flour", "Yogurt (Curd)"
1086
  ];
1087
 
 
 
 
 
 
 
 
1088
  function addToCartLocalStorage(payload) {
1089
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1090
  const existingItem = cart.find(item =>
 
1134
  function showSoftDrinkModal(button) {
1135
  currentSoftDrinkButton = button;
1136
  const buttonContainer = button.closest('.button-container');
1137
+ const itemName = buttonContainer.getAttribute('data-item-name');
1138
  const itemPrice = buttonContainer.getAttribute('data-item-price');
1139
  const itemImage = buttonContainer.getAttribute('data-item-image');
1140
 
 
1154
  const buttonContainer = currentSoftDrinkButton.closest('.button-container');
1155
  const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
1156
 
1157
+ const itemName = buttonContainer.getAttribute('data-item-name');
1158
  const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1159
  const itemImage = buttonContainer.getAttribute('data-item-image');
1160
+ const section = buttonContainer.getAttribute('data-item-section');
1161
+ const selectedCategory = buttonContainer.getAttribute('data-item-category');
1162
 
1163
  const cartPayload = {
1164
  itemName: itemName,
 
1181
  .then(response => response.json())
1182
  .then(data => {
1183
  if (data.success) {
 
1184
  updateCartUI(data.cart);
1185
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1186
  modal.hide();
1187
  } else {
1188
  console.error('Failed to add item to cart:', data.error);
 
1189
  const cart = addToCartLocalStorage(cartPayload);
1190
  updateCartUI(cart);
1191
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
 
1194
  })
1195
  .catch(err => {
1196
  console.error('Error adding item to cart:', err);
 
1197
  const cart = addToCartLocalStorage(cartPayload);
1198
  updateCartUI(cart);
1199
  const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
 
1222
  document.addEventListener('DOMContentLoaded', function () {
1223
  const avatarContainer = document.querySelector('.avatar-dropdown-container');
1224
  const dropdownMenu = document.querySelector('.dropdown-menu');
1225
+ avatarContainer.addEventListener('click', function (event) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1226
  event.stopPropagation();
1227
  dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1228
  });
1229
+ document.addEventListener('click', function (event) {
1230
+ if (!avatarContainer.contains(event.target)) {
1231
+ dropdownMenu.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1232
  }
1233
  });
1234
+ const dropdownItems = document.querySelectorAll('.dropdown-item');
1235
+ dropdownItems.forEach(item => {
1236
+ item.addEventListener('click', function () {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1237
  dropdownMenu.style.display = 'none';
1238
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1239
  });
1240
 
1241
  const menuCards = document.querySelectorAll('.menu-card');
 
1316
  categoryForm.submit();
1317
  });
1318
  });
 
1319
  const searchBar = document.getElementById('searchBar');
1320
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
 
1321
  searchBar.addEventListener('input', function () {
1322
+ const input = this.value.trim().toLowerCase();
1323
  suggestionsContainer.innerHTML = '';
1324
  suggestionsContainer.style.display = 'none';
1325
  if (input) {
 
1334
  suggestionDiv.addEventListener('click', function () {
1335
  searchBar.value = item;
1336
  suggestionsContainer.style.display = 'none';
1337
+ filterMenu();
1338
  });
1339
  suggestionsContainer.appendChild(suggestionDiv);
1340
  });
1341
  suggestionsContainer.style.display = 'block';
1342
  }
1343
  }
1344
+ filterMenu();
1345
  });
1346
  document.addEventListener('click', function (event) {
1347
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1348
  suggestionsContainer.style.display = 'none';
1349
  }
1350
  });
 
1351
  const descriptionTextarea = document.getElementById('custom-dish-description');
1352
  const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1353
  if (descriptionTextarea && descriptionSuggestions) {
 
1401
  }
1402
  });
1403
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1404
  fetch('/cart/get')
1405
  .then(response => {
1406
  if (!response.ok) {
 
1422
  const cart = getCartLocalStorage();
1423
  updateCartUI(cart);
1424
  });
 
1425
  const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1426
  preloadedVideos.forEach(link => {
1427
  const video = document.createElement('video');
1428
  video.src = link.href;
1429
  video.preload = 'auto';
1430
  });
 
1431
  const decreaseBtn = document.getElementById('decreaseQuantity');
1432
  const increaseBtn = document.getElementById('increaseQuantity');
1433
  const quantityInput = document.getElementById('quantityInput');
 
1466
 
1467
  // Mic Popup Functionality
1468
  const micIcon = document.getElementById('micIcon');
 
1469
  const micPopup = document.getElementById('micPopup');
1470
  const micPopupText = document.getElementById('micPopupText');
1471
  const micPopupCancel = document.getElementById('micPopupCancel');
1472
 
1473
+ if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
1474
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1475
  const recognition = new SpeechRecognition();
1476
  recognition.continuous = false;
 
1496
  }
1497
  }
1498
 
1499
+ // Display interim results
1500
  if (interimTranscript) {
1501
  micPopupText.textContent = interimTranscript;
1502
  }
1503
 
1504
+ // When we have a final result
1505
  if (finalTranscript) {
1506
+ searchBar.value = finalTranscript.trim();
1507
+ filterMenu();
1508
  micPopup.classList.remove('active');
1509
  }
1510
  };
 
1512
  recognition.onend = () => {
1513
  micIcon.classList.remove('active');
1514
  if (micPopup.classList.contains('active')) {
1515
+ // If still active, it means it ended unexpectedly
1516
  setTimeout(() => {
1517
  micPopup.classList.remove('active');
1518
  }, 1000);
 
1547
  });
1548
  } else {
1549
  micIcon.style.display = 'none';
 
1550
  }
1551
  });
1552
+
1553
  function filterMenu() {
1554
+ const input = document.getElementById('searchBar').value.trim().toLowerCase();
1555
  const sections = document.querySelectorAll('h3');
1556
  const items = document.querySelectorAll('.menu-card');
1557
  let matchedSections = new Set();
 
1587
  });
1588
  }
1589
  }
1590
+
1591
+ function showItemDetails(name, price, image, description, ingredients, nutritionalInfo, allergens, section, selectedCategory) {
1592
  document.getElementById('modal-name').innerText = name;
1593
  document.getElementById('modal-price').innerText = `$${price}`;
1594
  const modalImg = document.getElementById('modal-img');
1595
  modalImg.src = image || '/static/placeholder.jpg';
1596
  document.getElementById('modal-description').innerText = description || 'No description available.';
1597
  document.getElementById('modal-ingredients').innerText = ingredients || 'Not specified';
1598
+ document.getElementById('modal-nutrition').innerText = nutritionalInfo || 'Not available';
1599
  document.getElementById('modal-allergens').innerText = allergens || 'None listed';
1600
  document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1601
  document.getElementById('modal-instructions').value = '';
 
1603
  modalSectionEl.setAttribute('data-section', section);
1604
  modalSectionEl.setAttribute('data-category', selectedCategory);
1605
  document.getElementById('quantityInput').value = 1;
1606
+
1607
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1608
  .then(response => response.json())
1609
  .then(data => {
 
1634
  optionsContainer.appendChild(listItem);
1635
  });
1636
  sectionDiv.appendChild(optionsContainer);
1637
+ addonsList.appendChild(sectionDiv);
1638
  });
1639
  })
1640
  .catch(err => {
 
1642
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1643
  });
1644
  }
1645
+
1646
  document.addEventListener('click', function(event) {
1647
  if (event.target.classList.contains('addon-option')) {
1648
  handleAddonClick(event.target);
1649
  }
1650
  });
1651
+
1652
  function handleAddonClick(checkbox) {
1653
  const groupName = checkbox.getAttribute('data-group');
1654
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo","Beverages","Sauces"].includes(groupName);
 
1655
  if (!isMultiSelectGroup) {
1656
+ const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1657
+ checkboxes.forEach(otherCheckbox => {
1658
+ if (otherCheckbox !== checkbox) {
1659
+ otherCheckbox.checked = false;
1660
+ }
1661
  });
1662
  }
1663
  }
1664
+
1665
  function addToCartFromModal() {
1666
  const itemName = document.getElementById('modal-name').innerText;
1667
+ let itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
1668
+ if (isNaN(itemPrice)) {
1669
+ alert('Invalid price for the item. Please check the item details.');
1670
+ return;
1671
+ }
1672
  const itemImage = document.getElementById('modal-img').src;
1673
+ const modalSectionEl = document.getElementById('modal-section');
1674
+ const section = modalSectionEl.getAttribute('data-section');
1675
+ const selectedCategory = modalSectionEl.getAttribute('data-category');
1676
+ if (!itemName || !itemPrice || !section || !itemImage) {
1677
+ console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage });
1678
+ return;
1679
+ }
1680
+ const selectedAddOns = Array.from(
1681
+ document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
1682
+ ).map(addon => ({
1683
+ name: addon.getAttribute('data-name') || 'Default Name',
1684
+ price: parseFloat(addon.getAttribute('data-price') || 0)
1685
  }));
1686
+ const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1687
+ const instructions = document.getElementById('modal-instructions').value;
1688
  const cartPayload = {
1689
  itemName: itemName,
1690
  itemPrice: itemPrice,
1691
  itemImage: itemImage,
1692
  section: section,
1693
  category: selectedCategory,
1694
+ addons: selectedAddOns,
1695
  instructions: instructions,
1696
  quantity: quantity
1697
  };
1698
+
1699
  fetch('/cart/add', {
1700
  method: 'POST',
1701
  headers: {
 
1708
  if (data.success) {
1709
  alert('Item added to cart successfully!');
1710
  updateCartUI(data.cart);
 
 
 
 
 
 
 
1711
  const modal = document.getElementById('itemModal');
1712
  const modalInstance = bootstrap.Modal.getInstance(modal);
1713
  modalInstance.hide();
1714
+ } else {
1715
+ console.error('Failed to add item to cart:', data.error);
1716
+ alert(data.error || 'Failed to add item to cart.');
1717
  }
1718
  })
1719
  .catch(err => {
1720
  console.error('Error adding item to cart:', err);
 
1721
  const cart = addToCartLocalStorage(cartPayload);
1722
  updateCartUI(cart);
1723
  const modal = document.getElementById('itemModal');
 
1727
  }
1728
  </script>
1729
  </body>
1730
+ </html>