lokesh341 commited on
Commit
0d04669
·
verified ·
1 Parent(s): 211d39c

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +549 -833
templates/menu.html CHANGED
@@ -1,16 +1,17 @@
1
-
2
- <!DOCTYPE html>
3
- <html lang="en">
4
- <head>
5
  <meta charset="UTF-8">
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
  <title>Menu</title>
 
8
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
9
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
10
- <link rel="preload" href="/static/placeholder.jpg" as="image">
 
11
  {% for section, items in ordered_menu.items() %}
12
  {% for item in items[:1] %}
13
- <link rel="preload" href="{{ item.Image1__c }}" as="image" fetchpriority="high">
14
  {% endfor %}
15
  {% endfor %}
16
  <style>
@@ -21,6 +22,7 @@
21
  padding: 0;
22
  display: flex;
23
  flex-direction: column;
 
24
  }
25
  .container {
26
  max-width: 900px;
@@ -35,92 +37,52 @@
35
  flex-direction: column;
36
  opacity: 0;
37
  transition: opacity 0.3s ease-in-out;
 
38
  }
39
  .menu-card.visible {
40
  opacity: 1;
41
  }
42
-
43
  .video-container {
 
44
  position: relative;
45
- width: 100%;
46
- height: 200px;
47
- overflow: hidden;
48
- border-radius: 15px 15px 0 0;
49
- background-color: #000;
50
- touch-action: manipulation;
51
  }
52
-
53
- .video-container video {
54
- width: 100%;
55
- height: 100%;
56
- object-fit: cover;
57
- transition: opacity 0.5s ease;
58
- -webkit-user-select: none;
59
- user-select: none;
60
  }
61
-
62
- .video-container .video-placeholder {
63
- position: absolute;
64
- top: 0;
65
- left: 0;
66
- width: 100%;
67
- height: 100%;
68
- display: flex;
69
- align-items: center;
70
- justify-content: center;
71
- background-color: #f0f0f0;
72
- color: #666;
73
- font-size: 14px;
74
  }
75
-
76
- .video-container .play-button {
77
- position: absolute;
78
- top: 50%;
79
- left: 50%;
80
- transform: translate(-50%, -50%);
81
- width: 50px;
82
- height: 50px;
83
- background-color: rgba(0, 0, 0, 0.7);
84
- border-radius: 50%;
85
- display: flex;
86
- align-items: center;
87
- justify-content: center;
88
- cursor: pointer;
89
- z-index: 10;
90
  opacity: 0;
91
- transition: opacity 0.3s ease;
 
92
  }
93
-
94
- .video-container:hover .play-button {
95
  opacity: 1;
96
  }
97
-
98
- .video-container .play-button i {
99
- color: white;
100
- font-size: 20px;
101
- margin-left: 3px;
102
- }
103
-
104
- @media (max-width: 768px) {
105
- .video-container .play-button {
106
- opacity: 1;
107
- }
108
- .video-container:hover .play-button {
109
- opacity: 1;
110
- }
111
- .video-container {
112
- height: 150px;
113
- }
114
  }
115
-
116
- .card-title {
117
  font-size: 1.2rem;
118
- font-weight: bold;
119
  margin: 10px 0;
 
120
  }
121
- .card-text {
122
  font-size: 1rem;
123
- color: #6c757d;
 
 
124
  }
125
  .addbutton .btn {
126
  background-color: #28a745;
@@ -177,53 +139,6 @@
177
  box-shadow: none;
178
  transform: scale(0.98);
179
  }
180
- .view-cart-container {
181
- position: fixed;
182
- bottom: 20px;
183
- right: 20px;
184
- z-index: 999;
185
- display: flex;
186
- align-items: center;
187
- }
188
- .view-cart-button {
189
- background-color: #0FAA39;
190
- color: #fff;
191
- padding: 10px 20px;
192
- border-radius: 30px;
193
- font-size: 1rem;
194
- font-weight: bold;
195
- text-decoration: none;
196
- box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
197
- display: flex;
198
- align-items: center;
199
- justify-content: center;
200
- gap: 8px;
201
- transition: background-color 0.3s ease, transform 0.2s ease;
202
- }
203
- .view-cart-button:hover {
204
- background-color: #109835;
205
- text-decoration: none;
206
- color: #fff;
207
- transform: scale(1.05);
208
- }
209
- .cart-icon-badge {
210
- background-color: #fff;
211
- color: #0FAA39;
212
- border-radius: 50%;
213
- width: 24px;
214
- height: 24px;
215
- display: flex;
216
- align-items: center;
217
- justify-content: center;
218
- font-size: 12px;
219
- font-weight: bold;
220
- margin-left: 5px;
221
- transition: background-color 0.3s ease, color 0.3s ease;
222
- }
223
- .cart-icon-badge.active {
224
- background-color: #ffcc00;
225
- color: #0FAA39;
226
- }
227
  .avatar-dropdown-container {
228
  position: absolute;
229
  right: 10px;
@@ -237,7 +152,7 @@
237
  width: 40px;
238
  height: 40px;
239
  border-radius: 50%;
240
- background-color: #5bbfc1;
241
  cursor: pointer;
242
  display: flex;
243
  align-items: center;
@@ -250,25 +165,28 @@
250
  position: absolute;
251
  right: 0;
252
  top: 100%;
253
- background-color: #fff;
254
  border-radius: 5px;
255
  width: 220px;
256
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
257
  display: none;
 
258
  }
259
  .dropdown-menu .dropdown-item {
260
  padding: 12px 16px;
261
  text-decoration: none;
262
  color: #333;
263
- border-bottom: 1px solid #ddd;
264
  display: block;
265
  font-size: 15px;
 
266
  }
267
  .dropdown-menu .dropdown-item:last-child {
268
  border-bottom: none;
269
  }
270
  .dropdown-menu .dropdown-item:hover {
271
- background-color: #f1f1f1;
 
272
  }
273
  .fixed-top-bar {
274
  position: relative;
@@ -293,10 +211,11 @@
293
  align-items: center;
294
  width: 300px;
295
  max-width: 90%;
 
296
  }
297
  .search-bar-container input {
298
  width: 100%;
299
- padding: 8px 15px 8px 40px;
300
  font-size: 16px;
301
  border-radius: 25px;
302
  border: none;
@@ -313,6 +232,17 @@
313
  font-size: 18px;
314
  color: #888;
315
  }
 
 
 
 
 
 
 
 
 
 
 
316
  .autocomplete-suggestions {
317
  position: absolute;
318
  top: 100%;
@@ -338,7 +268,7 @@
338
  }
339
  .addon-section {
340
  background-color: #fff;
341
- border: 2px solid #6c757d;
342
  border-radius: 8px;
343
  padding: 12px;
344
  margin-bottom: 10px;
@@ -418,11 +348,12 @@
418
  font-weight: bold;
419
  text-align: center;
420
  margin-bottom: 5px;
 
421
  }
422
  .modal-body #modal-price {
423
  font-size: 16px;
424
- font-weight: bold;
425
- color: #6c757d;
426
  text-align: center;
427
  margin-bottom: 10px;
428
  }
@@ -491,6 +422,7 @@
491
  font-size: 0.9rem;
492
  font-weight: bold;
493
  margin-bottom: 5px;
 
494
  }
495
  .item-details p {
496
  font-size: 0.85rem;
@@ -587,6 +519,86 @@
587
  .modal-body::-webkit-scrollbar-thumb:hover {
588
  background: #0D9232;
589
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
  @media (max-width: 576px) {
591
  .fixed-top-bar {
592
  height: 60px;
@@ -600,7 +612,7 @@
600
  transform: translateY(-50%);
601
  }
602
  .search-bar-container input {
603
- padding: 6px 12px 6px 35px;
604
  font-size: 14px;
605
  border-radius: 20px;
606
  }
@@ -608,6 +620,10 @@
608
  left: 12px;
609
  font-size: 16px;
610
  }
 
 
 
 
611
  .avatar-dropdown-container {
612
  right: 10px;
613
  }
@@ -725,20 +741,28 @@
725
  height: 18px;
726
  font-size: 9px;
727
  }
728
- .view-cart-button {
729
- padding: 8px 16px;
 
 
 
 
 
 
 
 
730
  font-size: 14px;
731
  }
732
  .cart-icon-badge {
733
- width: 20px;
734
- height: 20px;
735
  font-size: 10px;
 
736
  }
737
  }
738
  </style>
739
  </head>
740
  <body>
741
-
742
  <div class="fixed-top-bar">
743
  <div class="avatar-dropdown-container">
744
  <div class="avatar-icon">
@@ -753,6 +777,7 @@
753
  <div class="search-bar-container">
754
  <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
755
  <i class="bi bi-search search-icon"></i>
 
756
  <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
757
  </div>
758
  </div>
@@ -782,96 +807,100 @@
782
  <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
783
  <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
784
  </div>
785
- <button type="submit" class="btn btn-primary">Submit</button>
786
  </form>
787
  </div>
788
  {% else %}
789
- {% for section, items in ordered_menu.items() %}
790
- <h3>{{ section }}</h3>
791
- <div class="row">
792
- {% for item in items %}
793
- <div class="col-md-6 mb-4">
794
- <div class="card menu-card">
795
- <div class="video-container">
796
- <video
797
- id="video-{{ loop.index }}"
798
- preload="metadata"
799
- poster="{{ item.Image1__c if item.Image1__c else '/static/placeholder.jpg' }}"
800
- data-src="{{ item.Video1__c }}"
801
- muted
802
- loop
803
- >
804
- <source src="{{ item.Video1__c }}" type="video/mp4">
805
- Your browser does not support the video tag.
806
- </video>
807
- {% if not item.Video1__c %}
808
- <div class="video-placeholder">
809
- No video available
810
- </div>
811
- {% endif %}
812
- <div class="play-button" onclick="togglePlay(this)">
813
- <i class="bi bi-play-fill"></i>
 
 
 
 
 
 
 
814
  </div>
815
- </div>
816
-
817
- <div class="addbutton">
818
- <div class="card-body d-flex align-items-center justify-content-between">
819
- <div>
820
- <h5 class="card-title">{{ item.Name }}</h5>
821
- <p class="card-text">${{ item.Price__c }}</p>
822
- </div>
823
- <div class="d-flex flex-column align-item-center justify-content-center">
824
- <div class="button-container" data-item-name="{{ item.Name }}" data-item-price="{{ item.Price__c }}" data-item-image="{{ item.Image1__c }}" data-item-section="{{ item.Section__c }}" data-item-category="{{ selected_category }}">
825
- {% if item.Section__c == 'Soft Drinks' %}
826
- <button class="btn btn-primary add-to-cart-btn" onclick="handleSoftDrinkAdd(this)">ADD</button>
827
- <div class="quantity-selector" style="display: none;">
828
- <button class="btn btn-outline-secondary decrease-btn" onclick="decreaseQuantity(this)">-</button>
829
- <select class="quantity-to-remove">
830
- {% for i in range(1, 21) %}
831
- <option value="{{ i }}">{{ i }}</option>
832
- {% endfor %}
833
- </select>
834
- <span class="quantity-display">0</span>
835
- <button class="btn btn-outline-secondary increase-btn" onclick="increaseQuantity(this)">+</button>
836
- <select class="quantity-to-add">
837
- {% for i in range(1, 21) %}
838
- <option value="{{ i }}">{{ i }}</option>
839
- {% endfor %}
840
- </select>
841
- </div>
842
- {% else %}
843
- <button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#itemModal"
844
- onclick="showItemDetails('{{ item.Name }}', '{{ item.Price__c }}', '{{ item.Image2__c }}', '{{ item.Description__c }}', '{{ item.Section__c }}','{{ selected_category }}')">
845
- ADD
846
- </button>
847
- {% endif %}
848
- {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
849
- <span class="customisable-text">Customisable</span>
850
- {% endif %}
851
  </div>
852
  </div>
853
  </div>
 
 
854
  </div>
855
- <div class="toggle-details" data-item-name="{{ item.Name }}">Show Details</div>
856
- <div class="item-details" id="details-{{ item.Name | replace(' ', '-') }}"></div>
857
  </div>
858
- </div>
859
- {% endfor %}
860
- </div>
861
- {% endfor %}
862
  {% endif %}
863
  </div>
864
 
865
- <div class="view-cart-container">
866
- <a href="{{ url_for('cart.cart') }}" class="view-cart-button">
867
- <i class="bi bi-cart"></i>
868
- view Cart
869
- <span id="cart-item-count" class="cart-icon-badge {% if cart_item_count > 0 %}active{% endif %}">
870
- {{ cart_item_count if cart_item_count > 0 else '' }}
871
- </span>
872
  </a>
873
  </div>
874
 
 
875
  <div class="modal fade" id="itemModal" tabindex="-1" aria-labelledby="itemModalLabel" aria-hidden="true">
876
  <div class="modal-dialog modal-dialog-centered">
877
  <div class="modal-content">
@@ -880,33 +909,14 @@
880
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
881
  </div>
882
  <div class="modal-body">
883
- <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image">
884
  <h5 id="modal-name" class="fw-bold text-center"></h5>
885
  <p id="modal-price" class="text-muted text-center"></p>
886
  <p id="modal-description" class="text-secondary"></p>
887
- <p class="nutritional-info" id="modal-nutritional-info"></p>
888
-
889
- <div id="first-row">
890
- <h6 id="first-row-title" style="display: none;">Customization Options</h6>
891
- <div class="addon-section">
892
- <h6>Choose Preparation Style</h6>
893
- <div id="prep-style-options"></div>
894
- </div>
895
- <div class="addon-section">
896
- <h6>Type</h6>
897
- <div id="type-options"></div>
898
- </div>
899
- <div class="addon-section">
900
- <h6>Spice Level</h6>
901
- <div id="spice-level-options"></div>
902
- </div>
903
- </div>
904
-
905
  <div id="modal-addons" class="modal-addons mt-4">
906
- <h6 id="addons-title">Customization Options</h6>
907
  <div id="addons-list" class="addons-container">Loading customization options...</div>
908
  </div>
909
-
910
  <div class="mt-4">
911
  <h6>Custom Request</h6>
912
  <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
@@ -927,29 +937,12 @@
927
 
928
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
929
  <script>
930
- function togglePlay(button) {
931
- const videoContainer = button.closest('.video-container');
932
- const video = videoContainer.querySelector('video');
933
-
934
- if (video.paused) {
935
- if (!video.src && video.dataset.src) {
936
- video.src = video.dataset.src;
937
- }
938
- video.play()
939
- .then(() => {
940
- button.innerHTML = '<i class="bi bi-pause-fill"></i>';
941
- })
942
- .catch(e => console.log('Video play error:', e));
943
- } else {
944
- video.pause();
945
- button.innerHTML = '<i class="bi bi-play-fill"></i>';
946
- }
947
- }
948
 
949
  const menuItems = [
950
  {% for section, items in ordered_menu.items() %}
951
  {% for item in items %}
952
- "{{ item.Name }}",
953
  {% endfor %}
954
  {% endfor %}
955
  ];
@@ -1063,6 +1056,49 @@
1063
  }
1064
  };
1065
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1066
  function addToCartLocalStorage(payload) {
1067
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1068
  const existingItem = cart.find(item =>
@@ -1071,7 +1107,7 @@
1071
  JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1072
  );
1073
  if (existingItem) {
1074
- existingItem.quantity += payload.quantity;
1075
  } else {
1076
  cart.push(payload);
1077
  }
@@ -1101,99 +1137,211 @@
1101
  return JSON.parse(localStorage.getItem('cart')) || [];
1102
  }
1103
 
1104
- document.addEventListener('DOMContentLoaded', function () {
1105
- const videoContainers = document.querySelectorAll('.video-container');
 
 
 
 
 
 
 
 
 
 
 
1106
 
1107
- videoContainers.forEach(container => {
1108
- const video = container.querySelector('video');
1109
- const playButton = container.querySelector('.play-button');
1110
-
1111
- const observer = new IntersectionObserver((entries) => {
1112
- entries.forEach(entry => {
1113
- if (entry.isIntersecting && video.dataset.src && !video.src) {
1114
- video.src = video.dataset.src;
1115
- observer.unobserve(entry.target);
1116
- }
1117
- });
1118
- }, {
1119
- rootMargin: '200px',
1120
- threshold: 0.1
1121
- });
1122
-
1123
- observer.observe(container);
1124
 
1125
- container.addEventListener('mouseenter', () => {
1126
- if (window.innerWidth > 768) {
1127
- if (video.src || video.dataset.src) {
1128
- if (!video.src) video.src = video.dataset.src;
1129
- video.play().catch(e => console.log('Autoplay prevented:', e));
1130
- }
1131
- }
1132
- });
1133
-
1134
- container.addEventListener('mouseleave', () => {
1135
- if (window.innerWidth > 768) {
1136
- if (!video.paused) {
1137
- video.pause();
1138
- if (playButton) {
1139
- playButton.innerHTML = '<i class="bi bi-play-fill"></i>';
1140
- }
1141
- }
1142
- }
1143
- });
1144
 
1145
- container.addEventListener('touchstart', (e) => {
1146
- e.preventDefault();
1147
- if (window.innerWidth <= 768) {
1148
- if (video.src || video.dataset.src) {
1149
- if (!video.src) video.src = video.dataset.src;
1150
- if (video.paused) {
1151
- video.play()
1152
- .then(() => {
1153
- if (playButton) {
1154
- playButton.innerHTML = '<i class="bi bi-pause-fill"></i>';
1155
- }
1156
- })
1157
- .catch(e => console.log('Video play error:', e));
1158
- } else {
1159
- video.pause();
1160
- if (playButton) {
1161
- playButton.innerHTML = '<i class="bi bi-play-fill"></i>';
1162
- }
1163
- }
1164
- }
1165
- }
1166
- }, { passive: false });
1167
- });
1168
 
1169
- const avatarContainer = document.querySelector('.avatar-dropdown-container');
1170
- const dropdownMenu = document.querySelector('.dropdown-menu');
 
 
 
 
 
 
 
1171
 
1172
- avatarContainer.addEventListener('click', function (event) {
1173
- event.stopPropagation();
1174
- dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1175
- });
1176
 
1177
- document.addEventListener('click', function (event) {
1178
- if (!avatarContainer.contains(event.target)) {
1179
- dropdownMenu.style.display = 'none';
1180
- }
1181
- });
1182
 
1183
- const dropdownItems = document.querySelectorAll('.dropdown-item');
1184
- dropdownItems.forEach(item => {
1185
- item.addEventListener('click', function () {
1186
- dropdownMenu.style.display = 'none';
1187
- });
1188
- });
 
 
 
 
 
1189
 
1190
- const menuCards = document.querySelectorAll('.menu-card');
1191
- menuCards.forEach(card => {
1192
- const itemName = card.querySelector('.card-title').innerText;
1193
- const detailsDiv = card.querySelector('.item-details');
1194
- const detailsData = menuItemDetails[itemName];
 
1195
 
1196
- if (detailsData) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1197
  detailsDiv.innerHTML = `
1198
  <h6>Ingredients</h6>
1199
  <p>${detailsData.ingredients}</p>
@@ -1211,7 +1359,6 @@
1211
  detailsDiv.innerHTML = '<p>No details available for this item.</p>';
1212
  }
1213
  });
1214
-
1215
  const cardObserver = new IntersectionObserver((entries, observer) => {
1216
  entries.forEach(entry => {
1217
  if (entry.isIntersecting) {
@@ -1224,9 +1371,32 @@
1224
  rootMargin: '0px',
1225
  threshold: 0.1
1226
  });
1227
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1228
  menuCards.forEach(card => cardObserver.observe(card));
1229
-
1230
  const toggleLinks = document.querySelectorAll('.toggle-details');
1231
  toggleLinks.forEach(link => {
1232
  link.addEventListener('click', function () {
@@ -1236,16 +1406,13 @@
1236
  this.innerText = detailsDiv.classList.contains('show') ? 'Hide Details' : 'Show Details';
1237
  });
1238
  });
1239
-
1240
  const categoryButtons = document.querySelectorAll('.category-button');
1241
  const categoryForm = document.getElementById('categoryForm');
1242
  const selectedCategoryInput = document.getElementById('selectedCategoryInput');
1243
-
1244
  if (!selectedCategoryInput.value) {
1245
  selectedCategoryInput.value = "All";
1246
  document.querySelector('.category-button[data-category="All"]').classList.add('selected');
1247
  }
1248
-
1249
  categoryButtons.forEach(button => {
1250
  button.addEventListener('click', function () {
1251
  categoryButtons.forEach(btn => btn.classList.remove('selected'));
@@ -1254,15 +1421,12 @@
1254
  categoryForm.submit();
1255
  });
1256
  });
1257
-
1258
  const searchBar = document.getElementById('searchBar');
1259
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1260
-
1261
  searchBar.addEventListener('input', function () {
1262
  const input = this.value.trim().toLowerCase();
1263
  suggestionsContainer.innerHTML = '';
1264
  suggestionsContainer.style.display = 'none';
1265
-
1266
  if (input) {
1267
  const filteredItems = menuItems.filter(item =>
1268
  item.toLowerCase().includes(input)
@@ -1284,19 +1448,15 @@
1284
  }
1285
  filterMenu();
1286
  });
1287
-
1288
  document.addEventListener('click', function (event) {
1289
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1290
  suggestionsContainer.style.display = 'none';
1291
  }
1292
  });
1293
-
1294
  const descriptionTextarea = document.getElementById('custom-dish-description');
1295
  const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1296
-
1297
  if (descriptionTextarea && descriptionSuggestions) {
1298
  let usedIngredients = new Set();
1299
-
1300
  function updateUsedIngredients() {
1301
  const inputText = descriptionTextarea.value.trim();
1302
  usedIngredients.clear();
@@ -1309,16 +1469,13 @@
1309
  });
1310
  }
1311
  }
1312
-
1313
  descriptionTextarea.addEventListener('input', function () {
1314
  const inputText = this.value.trim();
1315
  const words = inputText.split(/,\s*/);
1316
  const lastWord = words[words.length - 1].trim().toLowerCase();
1317
  descriptionSuggestions.innerHTML = '';
1318
  descriptionSuggestions.style.display = 'none';
1319
-
1320
  updateUsedIngredients();
1321
-
1322
  if (lastWord) {
1323
  const filteredIngredients = ingredientsList.filter(ingredient =>
1324
  ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
@@ -1343,14 +1500,12 @@
1343
  }
1344
  }
1345
  });
1346
-
1347
  document.addEventListener('click', function (event) {
1348
  if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1349
  descriptionSuggestions.style.display = 'none';
1350
  }
1351
  });
1352
  }
1353
-
1354
  fetch('/cart/get')
1355
  .then(response => {
1356
  if (!response.ok) {
@@ -1372,17 +1527,15 @@
1372
  const cart = getCartLocalStorage();
1373
  updateCartUI(cart);
1374
  });
1375
-
1376
- const preloadedImages = document.querySelectorAll('link[rel="preload"]');
1377
- preloadedImages.forEach(link => {
1378
- const img = new Image();
1379
- img.src = link.href;
1380
  });
1381
-
1382
  const decreaseBtn = document.getElementById('decreaseQuantity');
1383
  const increaseBtn = document.getElementById('increaseQuantity');
1384
  const quantityInput = document.getElementById('quantityInput');
1385
-
1386
  decreaseBtn.addEventListener('click', function () {
1387
  let currentQuantity = parseInt(quantityInput.value);
1388
  if (currentQuantity > 1) {
@@ -1390,32 +1543,43 @@
1390
  quantityInput.value = currentQuantity;
1391
  }
1392
  });
1393
-
1394
  increaseBtn.addEventListener('click', function () {
1395
  let currentQuantity = parseInt(quantityInput.value);
1396
  currentQuantity++;
1397
  quantityInput.value = currentQuantity;
1398
  });
1399
- });
1400
 
1401
- function debounce(func, wait) {
1402
- let timeout;
1403
- return function (...args) {
1404
- clearTimeout(timeout);
1405
- timeout = setTimeout(() => func.apply(this, args), wait);
1406
- };
1407
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1408
 
1409
  function filterMenu() {
1410
  const input = document.getElementById('searchBar').value.trim().toLowerCase();
1411
  const sections = document.querySelectorAll('h3');
1412
  const items = document.querySelectorAll('.menu-card');
1413
  let matchedSections = new Set();
1414
-
1415
  items.forEach(item => {
1416
  const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1417
  const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
1418
-
1419
  if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1420
  item.style.display = 'block';
1421
  item.classList.add('visible');
@@ -1424,7 +1588,6 @@
1424
  item.style.display = 'none';
1425
  }
1426
  });
1427
-
1428
  sections.forEach(section => {
1429
  const sectionRow = section.nextElementSibling;
1430
  if (matchedSections.has(sectionRow)) {
@@ -1435,7 +1598,6 @@
1435
  sectionRow.style.display = 'none';
1436
  }
1437
  });
1438
-
1439
  if (!input) {
1440
  sections.forEach(section => {
1441
  section.style.display = 'block';
@@ -1452,22 +1614,8 @@
1452
  document.getElementById('modal-name').innerText = name;
1453
  document.getElementById('modal-price').innerText = `$${price}`;
1454
  const modalImg = document.getElementById('modal-img');
1455
- if (section.toLowerCase() === 'soft drinks') {
1456
- modalImg.style.display = 'none';
1457
- } else {
1458
- modalImg.style.display = 'block';
1459
- modalImg.src = image || '/static/placeholder.jpg';
1460
- }
1461
  document.getElementById('modal-description').innerText = description || 'No description available.';
1462
- const nutritionalInfoEl = document.getElementById('modal-nutritional-info');
1463
- const itemDetails = menuItemDetails[name];
1464
- if (itemDetails && itemDetails.nutritionalInfo) {
1465
- const { calories, protein, carbs, fats, fiber, sugar } = itemDetails.nutritionalInfo;
1466
- nutritionalInfoEl.innerText = `[Energy: ${calories} kcal, Protein: ${protein}g, Carbohydrates: ${carbs}g, Fiber: ${fiber}g, Fat: ${fats}g, Sugar: ${sugar}g]`;
1467
- } else {
1468
- nutritionalInfoEl.innerText = '[No nutritional info available.]';
1469
- }
1470
-
1471
  document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1472
  document.getElementById('modal-instructions').value = '';
1473
  const modalSectionEl = document.getElementById('modal-section');
@@ -1475,148 +1623,28 @@
1475
  modalSectionEl.setAttribute('data-category', selectedCategory);
1476
  document.getElementById('quantityInput').value = 1;
1477
 
1478
- const prepStyleOptions = document.getElementById('prep-style-options');
1479
- const typeOptions = document.getElementById('type-options');
1480
- const spiceLevelOptions = document.getElementById('spice-level-options');
1481
- const firstRow = document.getElementById('first-row');
1482
- const firstRowTitle = document.getElementById('first-row-title');
1483
- const addonsTitle = document.getElementById('addons-title');
1484
-
1485
- prepStyleOptions.innerHTML = '';
1486
- typeOptions.innerHTML = '';
1487
- spiceLevelOptions.innerHTML = '';
1488
-
1489
- if (section.toLowerCase() === 'starters') {
1490
- firstRow.style.display = 'block';
1491
- firstRowTitle.style.display = 'block';
1492
- addonsTitle.style.display = 'none';
1493
- } else {
1494
- firstRow.style.display = 'none';
1495
- firstRowTitle.style.display = 'none';
1496
- addonsTitle.style.display = 'block';
1497
- }
1498
-
1499
- const addonsList = document.getElementById('addons-list');
1500
- addonsList.innerHTML = '';
1501
- const dummySections = [
1502
- { name: "Beverages", options: ["Sprite ($3)", "Thums Up ($3)", "Virgin Mojito ($3)", "Lemonade ($3)", "Blue Lagoon Mocktail ($3)"] },
1503
- { name: "Sauces", options: ["Mint Chutney", "Tomato Sauce"] },
1504
- { name: "Extra Toppings", options: ["Cheese ($2)", "Olives ($1)", "Jalapenos ($1)", "Mushrooms ($2)", "Peppers ($1)"] },
1505
- { name: "Sides", options: ["Fries ($3)", "Salad ($2)", "Garlic Bread ($3)", "Onion Rings ($2)", "Coleslaw ($2)"] },
1506
- { name: "Desserts", options: ["Ice Cream ($3)", "Brownie ($3)", "Cheesecake ($4)", "Gulab Jamun ($3)", "Rasmalai ($4)"] }
1507
- ];
1508
-
1509
- dummySections.forEach(addon => {
1510
- const sectionDiv = document.createElement('div');
1511
- sectionDiv.classList.add('addon-section');
1512
- sectionDiv.setAttribute('data-addon-name', addon.name);
1513
-
1514
- const title = document.createElement('h6');
1515
- title.innerText = addon.name;
1516
- sectionDiv.appendChild(title);
1517
-
1518
- const optionsContainer = document.createElement('div');
1519
- addon.options.forEach((option, index) => {
1520
- const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1521
- const listItem = document.createElement('div');
1522
- listItem.classList.add('form-check');
1523
-
1524
- listItem.innerHTML = `
1525
- <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1526
- data-name="${option}" data-group="${addon.name}" data-price="0">
1527
- <label class="form-check-label" for="${optionId}">
1528
- ${option}
1529
- </label>
1530
- `;
1531
- optionsContainer.appendChild(listItem);
1532
- });
1533
- sectionDiv.appendChild(optionsContainer);
1534
- addonsList.appendChild(sectionDiv);
1535
- });
1536
-
1537
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1538
  .then(response => response.json())
1539
  .then(data => {
1540
  const addonsList = document.getElementById('addons-list');
1541
  addonsList.innerHTML = '';
1542
-
1543
  if (!data.success || !data.addons || data.addons.length === 0) {
1544
  addonsList.innerHTML = '<p>No customization options available.</p>';
1545
- addonsTitle.style.display = 'none';
1546
  return;
1547
  }
1548
-
1549
- if (section.toLowerCase() === 'starters') {
1550
- data.addons.forEach(addon => {
1551
- if (addon.name.toLowerCase() === "choose preparation style") {
1552
- addon.options.forEach(option => {
1553
- const optionId = `addon-prep-style-${option}`;
1554
- const optionHTML = `
1555
- <div class="form-check">
1556
- <input type="checkbox" class="form-check-input" id="${optionId}" value="${option}" data-name="${option}" data-group="Choose Preparation Style">
1557
- <label class="form-check-label" for="${optionId}">
1558
- ${option}
1559
- </label>
1560
- </div>
1561
- `;
1562
- prepStyleOptions.innerHTML += optionHTML;
1563
- });
1564
- }
1565
- if (addon.name.toLowerCase() === "type") {
1566
- addon.options.forEach(option => {
1567
- const optionId = `addon-type-${option}`;
1568
- const optionHTML = `
1569
- <div class="form-check">
1570
- <input type="checkbox" class="form-check-input" id="${optionId}" value="${option}" data-name="${option}" data-group="Type">
1571
- <label class="form-check-label" for="${optionId}">
1572
- ${option}
1573
- </label>
1574
- </div>
1575
- `;
1576
- typeOptions.innerHTML += optionHTML;
1577
- });
1578
- }
1579
- if (addon.name.toLowerCase() === "spice level") {
1580
- addon.options.forEach(option => {
1581
- const optionId = `addon-spice-level-${option}`;
1582
- const optionHTML = `
1583
- <div class="form-check">
1584
- <input type="checkbox" class="form-check-input spice-level-option" id="${optionId}" value="${option}" data-name="${option}" data-group="Spice Level">
1585
- <label class="form-check-label" for="${optionId}">
1586
- ${option}
1587
- </label>
1588
- </div>
1589
- `;
1590
- spiceLevelOptions.innerHTML += optionHTML;
1591
- });
1592
- }
1593
- });
1594
- }
1595
-
1596
  data.addons.forEach(addon => {
1597
- if (section.toLowerCase() === 'starters' &&
1598
- (addon.name.toLowerCase() === "type" ||
1599
- addon.name.toLowerCase() === "spice level" ||
1600
- addon.name.toLowerCase() === "choose preparation style")) {
1601
- return;
1602
- }
1603
-
1604
  const sectionDiv = document.createElement('div');
1605
  sectionDiv.classList.add('addon-section');
1606
- sectionDiv.setAttribute('data-addon-name', addon.name);
1607
-
1608
  const title = document.createElement('h6');
1609
  title.innerText = addon.name;
1610
  sectionDiv.appendChild(title);
1611
-
1612
  const optionsContainer = document.createElement('div');
1613
  addon.options.forEach((option, index) => {
1614
  const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1615
  const listItem = document.createElement('div');
1616
  listItem.classList.add('form-check');
1617
-
1618
  listItem.innerHTML = `
1619
- <input type="checkbox" class="form-check-input ${addon.name.toLowerCase() === 'spice level' ? 'spice-level-option' : 'addon-option'}" id="${optionId}" value="${option}"
1620
  data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1621
  <label class="form-check-label" for="${optionId}">
1622
  ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
@@ -1627,66 +1655,23 @@
1627
  sectionDiv.appendChild(optionsContainer);
1628
  addonsList.appendChild(sectionDiv);
1629
  });
1630
-
1631
- const startersOrder = [
1632
- "Select Dip/Sauce",
1633
- "Extra Add-ons",
1634
- "Make it a Combo"
1635
- ];
1636
-
1637
- const desiredOrder = [
1638
- "Spice Level",
1639
- "Choose Preparation Style",
1640
- "Select Dip/Sauce",
1641
- "Extra Add-ons",
1642
- "Make it a Combo",
1643
- "Type"
1644
- ];
1645
-
1646
- const orderToUse = section.toLowerCase() === 'starters' ? startersOrder : desiredOrder;
1647
-
1648
- const sections = Array.from(addonsList.children);
1649
- addonsList.innerHTML = '';
1650
-
1651
- orderToUse.forEach(sectionName => {
1652
- const section = sections.find(s => s.getAttribute('data-addon-name') === sectionName);
1653
- if (section) {
1654
- addonsList.appendChild(section);
1655
- }
1656
- });
1657
-
1658
- sections.forEach(section => {
1659
- if (!orderToUse.includes(section.getAttribute('data-addon-name'))) {
1660
- addonsList.appendChild(section);
1661
- }
1662
- });
1663
  })
1664
  .catch(err => {
1665
  console.error('Error fetching add-ons:', err);
1666
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1667
- addonsTitle.style.display = 'none';
1668
  });
1669
  }
1670
 
1671
  document.addEventListener('click', function(event) {
1672
- if (event.target.classList.contains('spice-level-option') || event.target.classList.contains('addon-option')) {
1673
  handleAddonClick(event.target);
1674
  }
1675
  });
1676
 
1677
  function handleAddonClick(checkbox) {
1678
  const groupName = checkbox.getAttribute('data-group');
1679
- const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
1680
-
1681
- if (groupName.toLowerCase() === "spice level") {
1682
- const allSpiceLevelCheckboxes = document.querySelectorAll('.spice-level-option');
1683
- allSpiceLevelCheckboxes.forEach(otherCheckbox => {
1684
- if (otherCheckbox !== checkbox) {
1685
- otherCheckbox.checked = false;
1686
- }
1687
- });
1688
- }
1689
- else if (!isMultiSelectGroup) {
1690
  const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1691
  checkboxes.forEach(otherCheckbox => {
1692
  if (otherCheckbox !== checkbox) {
@@ -1708,41 +1693,15 @@
1708
  const section = modalSectionEl.getAttribute('data-section');
1709
  const selectedCategory = modalSectionEl.getAttribute('data-category');
1710
  if (!itemName || !itemPrice || !section || !itemImage) {
1711
- console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage});
1712
  return;
1713
  }
1714
-
1715
- let selectedAddOns = [];
1716
- const addonsListOptions = document.querySelectorAll('#addons-list .addon-option');
1717
- addonsListOptions.forEach(option => {
1718
- if (option.checked) {
1719
- selectedAddOns.push({
1720
- name: option.getAttribute('data-name') || 'Default Name',
1721
- price: parseFloat(option.getAttribute('data-price') || 0)
1722
- });
1723
- }
1724
- });
1725
-
1726
- if (section.toLowerCase() === 'starters') {
1727
- const prepStyleOptions = Array.from(
1728
- document.querySelectorAll('#prep-style-options input[type="checkbox"]:checked')
1729
- ).map(option => ({
1730
- name: option.getAttribute('data-name') || 'Default Prep Style',
1731
- price: 0
1732
- }));
1733
- const typeOptions = Array.from(
1734
- document.querySelectorAll('#type-options input[type="checkbox"]:checked')
1735
- ).map(option => ({
1736
- name: option.getAttribute('data-name') || 'Default Type',
1737
- price: 0
1738
- }));
1739
- const spiceLevelOption = document.querySelector('#spice-level-options input[type="checkbox"].spice-level-option:checked');
1740
- const spiceLevelOptions = spiceLevelOption ? [{
1741
- name: spiceLevelOption.getAttribute('data-name') || 'Default Spice Level',
1742
- price: 0
1743
- }] : [];
1744
- selectedAddOns = [...selectedAddOns, ...prepStyleOptions, ...typeOptions, ...spiceLevelOptions];
1745
- }
1746
  const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1747
  const instructions = document.getElementById('modal-instructions').value;
1748
  const cartPayload = {
@@ -1755,6 +1714,7 @@
1755
  instructions: instructions,
1756
  quantity: quantity
1757
  };
 
1758
  fetch('/cart/add', {
1759
  method: 'POST',
1760
  headers: {
@@ -1762,12 +1722,7 @@
1762
  },
1763
  body: JSON.stringify(cartPayload)
1764
  })
1765
- .then(response => {
1766
- if (!response.ok) {
1767
- throw new Error(`HTTP error! Status: ${response.status}`);
1768
- }
1769
- return response.json();
1770
- })
1771
  .then(data => {
1772
  if (data.success) {
1773
  alert('Item added to cart successfully!');
@@ -1782,7 +1737,6 @@
1782
  })
1783
  .catch(err => {
1784
  console.error('Error adding item to cart:', err);
1785
- alert('An error occurred while adding the item to the cart: ' + err.message);
1786
  const cart = addToCartLocalStorage(cartPayload);
1787
  updateCartUI(cart);
1788
  const modal = document.getElementById('itemModal');
@@ -1790,244 +1744,6 @@
1790
  modalInstance.hide();
1791
  });
1792
  }
1793
- function handleSoftDrinkAdd(button) {
1794
- const buttonContainer = button.closest('.button-container');
1795
- const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1796
- const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1797
- const quantityDisplay = quantitySelector.querySelector('.quantity-display');
1798
- const quantityToAddSelect = quantitySelector.querySelector('.quantity-to-add');
1799
- const quantityToAdd = parseInt(quantityToAddSelect.value);
1800
- const itemName = buttonContainer.getAttribute('data-item-name');
1801
- const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1802
- const itemImage = buttonContainer.getAttribute('data-item-image');
1803
- const section = buttonContainer.getAttribute('data-item-section');
1804
- const selectedCategory = buttonContainer.getAttribute('data-item-category');
1805
- addButton.style.display = 'none';
1806
- quantitySelector.style.display = 'flex';
1807
- let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1808
- currentQuantity += quantityToAdd;
1809
- quantityDisplay.innerText = currentQuantity;
1810
- const cartPayload = {
1811
- itemName: itemName,
1812
- itemPrice: itemPrice,
1813
- itemImage: itemImage,
1814
- section: section,
1815
- category: selectedCategory,
1816
- addons: [],
1817
- instructions: '',
1818
- quantity: quantityToAdd
1819
- };
1820
- button.disabled = true;
1821
- fetch('/cart/add', {
1822
- method: 'POST',
1823
- headers: {
1824
- 'Content-Type': 'application/json',
1825
- },
1826
- body: JSON.stringify(cartPayload)
1827
- })
1828
- .then(response => {
1829
- if (!response.ok) {
1830
- throw new Error(`HTTP error! Status: ${response.status}`);
1831
- }
1832
- return response.json();
1833
- })
1834
- .then(data => {
1835
- if (data.success) {
1836
- alert(`Added ${quantityToAdd} item(s) to cart successfully!`);
1837
- updateCartUI(data.cart);
1838
- } else {
1839
- console.error('Failed to add item to cart:', data.error);
1840
- alert(data.error || 'Failed to add item to cart.');
1841
- currentQuantity -= quantityToAdd;
1842
- quantityDisplay.innerText = currentQuantity;
1843
- }
1844
- })
1845
- .catch(err => {
1846
- console.error('Error adding item to cart:', err);
1847
- alert('An error occurred while adding the item to the cart: ' + err.message);
1848
- const cart = addToCartLocalStorage(cartPayload);
1849
- updateCartUI(cart);
1850
- })
1851
- .finally(() => {
1852
- button.disabled = false;
1853
- });
1854
- }
1855
- function increaseQuantity(button) {
1856
- const buttonContainer = button.closest('.button-container');
1857
- const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1858
- const quantityToAddSelect = buttonContainer.querySelector('.quantity-to-add');
1859
- const quantityToAdd = parseInt(quantityToAddSelect.value);
1860
- const itemName = buttonContainer.getAttribute('data-item-name');
1861
- const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1862
- const itemImage = buttonContainer.getAttribute('data-item-image');
1863
- const section = buttonContainer.getAttribute('data-item-section');
1864
- const selectedCategory = buttonContainer.getAttribute('data-item-category');
1865
- let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1866
- currentQuantity += quantityToAdd;
1867
- quantityDisplay.innerText = currentQuantity;
1868
- const cartPayload = {
1869
- itemName: itemName,
1870
- itemPrice: itemPrice,
1871
- itemImage: itemImage,
1872
- section: section,
1873
- category: selectedCategory,
1874
- addons: [],
1875
- instructions: '',
1876
- quantity: quantityToAdd
1877
- };
1878
- fetch('/cart/add', {
1879
- method: 'POST',
1880
- headers: {
1881
- 'Content-Type': 'application/json',
1882
- },
1883
- body: JSON.stringify(cartPayload)
1884
- })
1885
- .then(response => {
1886
- if (!response.ok) {
1887
- throw new Error(`HTTP error! Status: ${response.status}`);
1888
- }
1889
- return response.json();
1890
- })
1891
- .then(data => {
1892
- if (data.success) {
1893
- alert(`Added ${quantityToAdd} item(s) to cart successfully!`);
1894
- updateCartUI(data.cart);
1895
- } else {
1896
- console.error('Failed to add item to cart:', data.error);
1897
- alert(data.error || 'Failed to add item to cart.');
1898
- currentQuantity -= quantityToAdd;
1899
- quantityDisplay.innerText = currentQuantity;
1900
- }
1901
- })
1902
- .catch(err => {
1903
- console.error('Error adding item to cart:', err);
1904
- alert('An error occurred while adding the item to the cart: ' + err.message);
1905
- const cart = addToCartLocalStorage(cartPayload);
1906
- updateCartUI(cart);
1907
- });
1908
- }
1909
- function decreaseQuantity(button) {
1910
- const buttonContainer = button.closest('.button-container');
1911
- const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1912
- const quantityToRemoveSelect = buttonContainer.querySelector('.quantity-to-remove');
1913
- const quantityToRemove = parseInt(quantityToRemoveSelect.value);
1914
- const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1915
- const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1916
- let currentQuantity = parseInt(quantityDisplay.innerText);
1917
- if (currentQuantity <= quantityToRemove) {
1918
- const itemName = buttonContainer.getAttribute('data-item-name');
1919
- fetch(`/cart/remove?item_name=${encodeURIComponent(itemName)}&quantity=${currentQuantity}&instructions=&addons=[]`, {
1920
- method: 'POST',
1921
- headers: {
1922
- 'Content-Type': 'application/json',
1923
- }
1924
- })
1925
- .then(response => {
1926
- if (!response.ok) {
1927
- throw new Error(`HTTP error! Status: ${response.status}`);
1928
- }
1929
- return response.json();
1930
- })
1931
- .then(data => {
1932
- if (data.success) {
1933
- updateCartUI(data.cart);
1934
- addButton.style.display = 'block';
1935
- quantitySelector.style.display = 'none';
1936
- quantityDisplay.innerText = 0;
1937
- } else {
1938
- console.error('Failed to remove item from cart:', data.error);
1939
- alert(data.error || 'Failed to remove item from cart.');
1940
- }
1941
- })
1942
- .catch(err => {
1943
- console.error('Error removing item from cart:', err);
1944
- alert('An error occurred while removing the item from the cart: ' + err.message);
1945
- const cart = removeFromCartLocalStorage(itemName, currentQuantity, '', []);
1946
- updateCartUI(cart);
1947
- addButton.style.display = 'block';
1948
- quantitySelector.style.display = 'none';
1949
- quantityDisplay.innerText = 0;
1950
- });
1951
- } else {
1952
- currentQuantity -= quantityToRemove;
1953
- quantityDisplay.innerText = currentQuantity;
1954
- const itemName = buttonContainer.getAttribute('data-item-name');
1955
- fetch(`/cart/remove?item_name=${encodeURIComponent(itemName)}&quantity=${quantityToRemove}&instructions=&addons=[]`, {
1956
- method: 'POST',
1957
- headers: {
1958
- 'Content-Type': 'application/json',
1959
- }
1960
- })
1961
- .then(response => {
1962
- if (!response.ok) {
1963
- throw new Error(`HTTP error! Status: ${response.status}`);
1964
- }
1965
- return response.json();
1966
- })
1967
- .then(data => {
1968
- if (data.success) {
1969
- alert(`Removed ${quantityToRemove} item(s) from cart successfully!`);
1970
- updateCartUI(data.cart);
1971
- } else {
1972
- console.error('Failed to remove item from cart:', data.error);
1973
- alert(data.error || 'Failed to remove item from cart.');
1974
- currentQuantity += quantityToRemove;
1975
- quantityDisplay.innerText = currentQuantity;
1976
- }
1977
- })
1978
- .catch(err => {
1979
- console.error('Error removing item from cart:', err);
1980
- alert('An error occurred while removing the item from the cart: ' + err.message);
1981
- const cart = removeFromCartLocalStorage(itemName, quantityToRemove, '', []);
1982
- updateCartUI(cart);
1983
- });
1984
- }
1985
- }
1986
- function updateCartUI(cart) {
1987
- if (!Array.isArray(cart)) {
1988
- console.error('Invalid cart data:', cart);
1989
- return;
1990
- }
1991
-
1992
- // Calculate total quantity of items in cart
1993
- let totalQuantity = 0;
1994
- cart.forEach(item => {
1995
- totalQuantity += item.quantity;
1996
- });
1997
-
1998
- // Update cart item count badge
1999
- const cartItemCount = document.getElementById('cart-item-count');
2000
- if (cartItemCount) {
2001
- cartItemCount.innerText = totalQuantity;
2002
- if (totalQuantity > 0) {
2003
- cartItemCount.classList.add('active');
2004
- } else {
2005
- cartItemCount.classList.remove('active');
2006
- }
2007
- }
2008
- // Update quantity displays for soft drinks
2009
- const buttonContainers = document.querySelectorAll('.button-container');
2010
- buttonContainers.forEach(container => {
2011
- const itemName = container.getAttribute('data-item-name');
2012
- const quantityDisplay = container.querySelector('.quantity-display');
2013
- const addButton = container.querySelector('.add-to-cart-btn');
2014
- const quantitySelector = container.querySelector('.quantity-selector');
2015
- const cartItem = cart.find(item =>
2016
- item.itemName === itemName &&
2017
- item.instructions === '' &&
2018
- JSON.stringify(item.addons) === JSON.stringify([])
2019
- );
2020
- if (cartItem && cartItem.quantity > 0) {
2021
- quantityDisplay.innerText = cartItem.quantity;
2022
- addButton.style.display = 'none';
2023
- quantitySelector.style.display = 'flex';
2024
- } else {
2025
- quantityDisplay.innerText = 0;
2026
- addButton.style.display = 'block';
2027
- quantitySelector.style.display = 'none';
2028
- }
2029
- });
2030
- }
2031
  </script>
2032
  </body>
2033
  </html>
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
 
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
  <title>Menu</title>
7
+ <!-- Bootstrap CSS -->
8
  <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
9
  <link href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css" rel="stylesheet">
10
+ <!-- Preload Critical Resources -->
11
+ <link rel="preload" href="/static/placeholder.mp4" as="video">
12
  {% for section, items in ordered_menu.items() %}
13
  {% for item in items[:1] %}
14
+ <link rel="preload" href="{{ item.Video1__c | default('/static/placeholder.mp4') }}" as="video" fetchpriority="high">
15
  {% endfor %}
16
  {% endfor %}
17
  <style>
 
22
  padding: 0;
23
  display: flex;
24
  flex-direction: column;
25
+ padding-bottom: 70px;
26
  }
27
  .container {
28
  max-width: 900px;
 
37
  flex-direction: column;
38
  opacity: 0;
39
  transition: opacity 0.3s ease-in-out;
40
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
41
  }
42
  .menu-card.visible {
43
  opacity: 1;
44
  }
 
45
  .video-container {
46
+ transition: all 0.3s ease;
47
  position: relative;
 
 
 
 
 
 
48
  }
49
+ .video-container.video-error .video-fallback {
50
+ opacity: 1 !important;
 
 
 
 
 
 
51
  }
52
+ .video-container.video-error video {
53
+ display: none !important;
 
 
 
 
 
 
 
 
 
 
 
54
  }
55
+ .video-container:focus-within {
56
+ outline: 2px solid #3a86ff;
57
+ outline-offset: 2px;
58
+ }
59
+ .menu-video {
60
+ height: 200px;
61
+ width: 100%;
62
+ object-fit: cover;
63
+ border-radius: 15px 15px 0 0;
 
 
 
 
 
 
64
  opacity: 0;
65
+ transition: opacity 0.5s ease-in-out;
66
+ background-color: #000;
67
  }
68
+ .menu-video.loaded {
 
69
  opacity: 1;
70
  }
71
+ .menu-card:hover .menu-video {
72
+ opacity: 1;
73
+ transform: scale(1.05);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  }
75
+ .menu-card .card-body .card-title {
 
76
  font-size: 1.2rem;
77
+ font-weight: 600;
78
  margin: 10px 0;
79
+ color: #333333;
80
  }
81
+ .menu-card .card-body .card-text.price {
82
  font-size: 1rem;
83
+ font-weight: 500;
84
+ color: #000000;
85
+ margin-bottom: 5px;
86
  }
87
  .addbutton .btn {
88
  background-color: #28a745;
 
139
  box-shadow: none;
140
  transform: scale(0.98);
141
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  .avatar-dropdown-container {
143
  position: absolute;
144
  right: 10px;
 
152
  width: 40px;
153
  height: 40px;
154
  border-radius: 50%;
155
+ background-color: #007bff;
156
  cursor: pointer;
157
  display: flex;
158
  align-items: center;
 
165
  position: absolute;
166
  right: 0;
167
  top: 100%;
168
+ background-color: #fff8f0;
169
  border-radius: 5px;
170
  width: 220px;
171
  box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
172
  display: none;
173
+ border: 1px solid #ffd8b1;
174
  }
175
  .dropdown-menu .dropdown-item {
176
  padding: 12px 16px;
177
  text-decoration: none;
178
  color: #333;
179
+ border-bottom: 1px solid #ffd8b1;
180
  display: block;
181
  font-size: 15px;
182
+ transition: background-color 0.2s ease;
183
  }
184
  .dropdown-menu .dropdown-item:last-child {
185
  border-bottom: none;
186
  }
187
  .dropdown-menu .dropdown-item:hover {
188
+ background-color: #ffe4c4;
189
+ color: #333;
190
  }
191
  .fixed-top-bar {
192
  position: relative;
 
211
  align-items: center;
212
  width: 300px;
213
  max-width: 90%;
214
+ position: relative;
215
  }
216
  .search-bar-container input {
217
  width: 100%;
218
+ padding: 8px 40px 8px 40px;
219
  font-size: 16px;
220
  border-radius: 25px;
221
  border: none;
 
232
  font-size: 18px;
233
  color: #888;
234
  }
235
+ .mic-icon {
236
+ position: absolute;
237
+ right: 15px;
238
+ font-size: 18px;
239
+ color: #888;
240
+ cursor: pointer;
241
+ transition: color 0.3s ease;
242
+ }
243
+ .mic-icon.active {
244
+ color: #007bff;
245
+ }
246
  .autocomplete-suggestions {
247
  position: absolute;
248
  top: 100%;
 
268
  }
269
  .addon-section {
270
  background-color: #fff;
271
+ border: 2px solid #ffa500;
272
  border-radius: 8px;
273
  padding: 12px;
274
  margin-bottom: 10px;
 
348
  font-weight: bold;
349
  text-align: center;
350
  margin-bottom: 5px;
351
+ color: #333333;
352
  }
353
  .modal-body #modal-price {
354
  font-size: 16px;
355
+ font-weight: 500;
356
+ color: #000000;
357
  text-align: center;
358
  margin-bottom: 10px;
359
  }
 
422
  font-size: 0.9rem;
423
  font-weight: bold;
424
  margin-bottom: 5px;
425
+ color: #333333;
426
  }
427
  .item-details p {
428
  font-size: 0.85rem;
 
519
  .modal-body::-webkit-scrollbar-thumb:hover {
520
  background: #0D9232;
521
  }
522
+ .btn-primary:disabled {
523
+ opacity: 0.65;
524
+ cursor: not-allowed;
525
+ }
526
+ .quantity-selector select {
527
+ width: 60px;
528
+ height: 35px;
529
+ padding: 5px;
530
+ border-radius: 5px;
531
+ border: 1px solid #ced4da;
532
+ }
533
+ #custom-dish-form {
534
+ position: relative;
535
+ padding-bottom: 80px;
536
+ }
537
+ #custom-dish-form .btn-primary {
538
+ position: absolute;
539
+ right: 15px;
540
+ bottom: 15px;
541
+ width: auto;
542
+ padding: 10px 20px;
543
+ }
544
+ .bottom-action-bar {
545
+ position: fixed;
546
+ bottom: 0;
547
+ left: 0;
548
+ right: 0;
549
+ background-color: white;
550
+ padding: 10px 20px;
551
+ display: flex;
552
+ justify-content: space-between;
553
+ align-items: center;
554
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
555
+ z-index: 1000;
556
+ max-width: 900px;
557
+ margin: 0 auto;
558
+ }
559
+ .bottom-action-bar .btn {
560
+ flex: 1;
561
+ margin: 0 5px;
562
+ padding: 10px 15px;
563
+ border-radius: 8px;
564
+ font-weight: bold;
565
+ font-size: 16px;
566
+ color: white;
567
+ display: flex;
568
+ align-items: center;
569
+ justify-content: center;
570
+ text-align: center;
571
+ min-width: 0;
572
+ white-space: nowrap;
573
+ }
574
+ .bottom-action-bar .btn-order-history {
575
+ background-color: #FFA07A;
576
+ border-color: #FFA07A;
577
+ }
578
+ .bottom-action-bar .btn-order-history:hover {
579
+ background-color: #FF8C61;
580
+ border-color: #FF8C61;
581
+ }
582
+ .bottom-action-bar .btn-view-cart {
583
+ background-color: #0FAA39;
584
+ border-color: #0FAA39;
585
+ }
586
+ .bottom-action-bar .btn-view-cart:hover {
587
+ background-color: #0D9232;
588
+ border-color: #0D9232;
589
+ }
590
+ .cart-icon-badge {
591
+ background-color: white;
592
+ color: #0FAA39;
593
+ border-radius: 50%;
594
+ width: 20px;
595
+ height: 20px;
596
+ display: inline-flex;
597
+ align-items: center;
598
+ justify-content: center;
599
+ font-size: 12px;
600
+ margin-left: 8px;
601
+ }
602
  @media (max-width: 576px) {
603
  .fixed-top-bar {
604
  height: 60px;
 
612
  transform: translateY(-50%);
613
  }
614
  .search-bar-container input {
615
+ padding: 6px 35px 6px 35px;
616
  font-size: 14px;
617
  border-radius: 20px;
618
  }
 
620
  left: 12px;
621
  font-size: 16px;
622
  }
623
+ .mic-icon {
624
+ right: 12px;
625
+ font-size: 16px;
626
+ }
627
  .avatar-dropdown-container {
628
  right: 10px;
629
  }
 
741
  height: 18px;
742
  font-size: 9px;
743
  }
744
+ .quantity-selector select {
745
+ width: 50px;
746
+ height: 30px;
747
+ font-size: 12px;
748
+ }
749
+ .bottom-action-bar {
750
+ padding: 8px 10px;
751
+ }
752
+ .bottom-action-bar .btn {
753
+ padding: 8px 10px;
754
  font-size: 14px;
755
  }
756
  .cart-icon-badge {
757
+ width: 18px;
758
+ height: 18px;
759
  font-size: 10px;
760
+ margin-left: 5px;
761
  }
762
  }
763
  </style>
764
  </head>
765
  <body>
 
766
  <div class="fixed-top-bar">
767
  <div class="avatar-dropdown-container">
768
  <div class="avatar-icon">
 
777
  <div class="search-bar-container">
778
  <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
779
  <i class="bi bi-search search-icon"></i>
780
+ <i class="bi bi-mic mic-icon" id="micIcon"></i>
781
  <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
782
  </div>
783
  </div>
 
807
  <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
808
  <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
809
  </div>
810
+ <button type="submit" class="btn btn-primary">Submit Custom Dish</button>
811
  </form>
812
  </div>
813
  {% else %}
814
+ {% if ordered_menu.items()|length == 0 %}
815
+ <p>No menu items available for this category.</p>
816
+ {% else %}
817
+ {% for section, items in ordered_menu.items() %}
818
+ <h3>{{ section }}</h3>
819
+ <div class="row">
820
+ {% for item in items %}
821
+ <div class="col-md-6 mb-4">
822
+ <div class="card menu-card">
823
+ <div class="video-container">
824
+ <video
825
+ class="card-img-top menu-video"
826
+ id="video-{{ loop.index }}"
827
+ muted
828
+ loop
829
+ preload="metadata"
830
+ autoplay
831
+ data-src="{{ item.Video1__c | default('/static/placeholder.mp4') }}"
832
+ poster="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
833
+ width="350"
834
+ height="200"
835
+ aria-label="Video demonstration of {{ item.Name }}"
836
+ onmouseover="handleVideoHover(this.parentElement, true)"
837
+ onmouseout="handleVideoHover(this.parentElement, false)"
838
+ onerror="this.parentElement.classList.add('video-error');">
839
+ <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
840
+ Your browser does not support the video tag.
841
+ </video>
842
+ <img class="video-fallback"
843
+ src="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
844
+ alt="{{ item.Name }}"
845
+ style="opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; transition: opacity 0.3s ease;">
846
  </div>
847
+ <div class="addbutton">
848
+ <div class="card-body d-flex align-items-center justify-content-between">
849
+ <div>
850
+ <h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5>
851
+ <p class="card-text">${{ item.Price__c | default('0.00') }}</p>
852
+ </div>
853
+ <div class="d-flex flex-column align-item-center justify-content-center">
854
+ <div class="button-container"
855
+ data-item-name="{{ item.Name | default('Unnamed Item') }}"
856
+ data-item-price="{{ item.Price transcriptase__c | default('0.00') }}"
857
+ data-item-image="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
858
+ data-item-section="{{ item.Section__c | default(section) }}"
859
+ data-item-category="{{ selected_category }}">
860
+ {% if item.Section__c == 'Soft Drinks' %}
861
+ <button class="btn btn-primary add-to-cart-btn" onclick="handleSoftDrinkAdd(this)">ADD</button>
862
+ <div class="quantity-selector" style="display: none;">
863
+ <button class="btn btn-outline-secondary decrease-btn" onclick="decreaseQuantity(this)">-</button>
864
+ <span class="quantity-display">0</span>
865
+ <button class="btn btn-outline-secondary increase-btn" onclick="increaseQuantity(this)">+</button>
866
+ </div>
867
+ {% else %}
868
+ <button class="btn btn-primary"
869
+ data-bs-toggle="modal"
870
+ data-bs-target="#itemModal"
871
+ onclick="showItemDetails('{{ item.Name | default('Unnamed Item') }}', '{{ item.Price__c | default('0.00') }}', '{{ item.Image2__c | default(item.Image1__c) }}', '{{ item.Description__c | default('No description') }}', '{{ item.Section__c | default(section) }}', '{{ selected_category }}')">
872
+ ADD
873
+ </button>
874
+ {% endif %}
875
+ {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
876
+ <span class="customisable-text">Customisable</span>
877
+ {% endif %}
878
+ </div>
 
 
 
 
879
  </div>
880
  </div>
881
  </div>
882
+ <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
883
+ <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}"></div>
884
  </div>
 
 
885
  </div>
886
+ {% endfor %}
887
+ </div>
888
+ {% endfor %}
889
+ {% endif %}
890
  {% endif %}
891
  </div>
892
 
893
+ <div class="bottom-action-bar">
894
+ <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history">
895
+ <i class="bi bi-clock-history"></i> Order History
896
+ </a>
897
+ <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart">
898
+ <i class="bi bi-cart"></i> View Cart
899
+ <span class="cart-icon-badge" id="cart-item-count">0</span>
900
  </a>
901
  </div>
902
 
903
+ <!-- Modal for Item Details -->
904
  <div class="modal fade" id="itemModal" tabindex="-1" aria-labelledby="itemModalLabel" aria-hidden="true">
905
  <div class="modal-dialog modal-dialog-centered">
906
  <div class="modal-content">
 
909
  <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
910
  </div>
911
  <div class="modal-body">
912
+ <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" style="max-height: 200px; object-fit: cover;">
913
  <h5 id="modal-name" class="fw-bold text-center"></h5>
914
  <p id="modal-price" class="text-muted text-center"></p>
915
  <p id="modal-description" class="text-secondary"></p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
916
  <div id="modal-addons" class="modal-addons mt-4">
917
+ <h6>Customization Options</h6>
918
  <div id="addons-list" class="addons-container">Loading customization options...</div>
919
  </div>
 
920
  <div class="mt-4">
921
  <h6>Custom Request</h6>
922
  <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
 
937
 
938
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
939
  <script>
940
+ let isProcessingRequest = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
941
 
942
  const menuItems = [
943
  {% for section, items in ordered_menu.items() %}
944
  {% for item in items %}
945
+ "{{ item.Name | default('Unnamed Item') }}",
946
  {% endfor %}
947
  {% endfor %}
948
  ];
 
1056
  }
1057
  };
1058
 
1059
+ function getValidVideoUrl(videoUrl) {
1060
+ if (!videoUrl) return null;
1061
+ const sfDomain = window.SF_DOMAIN || 'https://yourdomain.my.salesforce.com';
1062
+ if (videoUrl.startsWith('http')) return videoUrl;
1063
+ if (videoUrl.startsWith('/')) return videoUrl;
1064
+ if (videoUrl.startsWith('069')) return `${sfDomain}/sfc/servlet.shepherd/version/download/${videoUrl}`;
1065
+ return null;
1066
+ }
1067
+
1068
+ async function handleVideoHover(container, isHovering) {
1069
+ const video = container.querySelector('video');
1070
+ if (!video) return;
1071
+
1072
+ try {
1073
+ if (isHovering) {
1074
+ if (!video.src && video.dataset.src) {
1075
+ video.src = getValidVideoUrl(video.dataset.src);
1076
+ await video.load();
1077
+ }
1078
+ try {
1079
+ await video.play();
1080
+ video.classList.add('visible');
1081
+ if (video.controls !== true) video.controls = true;
1082
+ } catch (e) {
1083
+ console.warn('Autoplay blocked:', e);
1084
+ video.controls = true;
1085
+ }
1086
+ } else {
1087
+ video.pause();
1088
+ video.currentTime = 0;
1089
+ video.controls = false;
1090
+ video.classList.remove('visible');
1091
+ }
1092
+ } catch (e) {
1093
+ console.error('Video handling error:', e);
1094
+ const fallbackImg = container.querySelector('.video-fallback');
1095
+ if (fallbackImg) {
1096
+ video.style.display = 'none';
1097
+ fallbackImg.style.opacity = '1';
1098
+ }
1099
+ }
1100
+ }
1101
+
1102
  function addToCartLocalStorage(payload) {
1103
  let cart = JSON.parse(localStorage.getItem('cart')) || [];
1104
  const existingItem = cart.find(item =>
 
1107
  JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1108
  );
1109
  if (existingItem) {
1110
+ existingItem.quantity = payload.quantity;
1111
  } else {
1112
  cart.push(payload);
1113
  }
 
1137
  return JSON.parse(localStorage.getItem('cart')) || [];
1138
  }
1139
 
1140
+ function debounce(func, wait) {
1141
+ let timeout;
1142
+ return function (...args) {
1143
+ clearTimeout(timeout);
1144
+ timeout = setTimeout(() => func.apply(this, args), wait);
1145
+ };
1146
+ }
1147
+
1148
+ function handleSoftDrinkAdd(button) {
1149
+ const buttonContainer = button.closest('.button-container');
1150
+ const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1151
+ const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1152
+ const quantityDisplay = quantitySelector.querySelector('.quantity-display');
1153
 
1154
+ addButton.style.display = 'none';
1155
+ quantitySelector.style.display = 'flex';
1156
+
1157
+ if (parseInt(quantityDisplay.innerText) === 0) {
1158
+ quantityDisplay.innerText = '1';
1159
+ updateCartQuantity(buttonContainer, 1);
1160
+ }
1161
+ }
 
 
 
 
 
 
 
 
 
1162
 
1163
+ function increaseQuantity(button) {
1164
+ if (isProcessingRequest) return;
1165
+ isProcessingRequest = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1166
 
1167
+ const buttonContainer = button.closest('.button-container');
1168
+ const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1169
+ let currentQuantity = parseInt(quantityDisplay.innerText) || 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1170
 
1171
+ if (currentQuantity < 10) {
1172
+ const newQuantity = currentQuantity + 1;
1173
+ quantityDisplay.innerText = newQuantity;
1174
+ debouncedUpdateCart(buttonContainer, newQuantity);
1175
+ } else {
1176
+ console.log('Maximum quantity of 10 reached for Soft Drinks');
1177
+ isProcessingRequest = false;
1178
+ }
1179
+ }
1180
 
1181
+ function decreaseQuantity(button) {
1182
+ if (isProcessingRequest) return;
1183
+ isProcessingRequest = true;
 
1184
 
1185
+ const buttonContainer = button.closest('.button-container');
1186
+ const quantityDisplay = buttonContainer.querySelector('.quantity-display');
1187
+ const addButton = buttonContainer.querySelector('.add-to-cart-btn');
1188
+ const quantitySelector = buttonContainer.querySelector('.quantity-selector');
1189
+ const currentQuantity = parseInt(quantityDisplay.innerText) || 0;
1190
 
1191
+ if (currentQuantity <= 1) {
1192
+ quantityDisplay.innerText = '0';
1193
+ addButton.style.display = 'block';
1194
+ quantitySelector.style.display = 'none';
1195
+ updateCartQuantity(buttonContainer, 0);
1196
+ } else {
1197
+ const newQuantity = currentQuantity - 1;
1198
+ quantityDisplay.innerText = newQuantity;
1199
+ debouncedUpdateCart(buttonContainer, newQuantity);
1200
+ }
1201
+ }
1202
 
1203
+ function updateCartQuantity(buttonContainer, newQuantity) {
1204
+ const itemName = buttonContainer.getAttribute('data-item-name');
1205
+ const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1206
+ const itemImage = buttonContainer.getAttribute('data-item-image');
1207
+ const section = buttonContainer.getAttribute('data-item-section');
1208
+ const selectedCategory = buttonContainer.getAttribute('data-item-category');
1209
 
1210
+ const cartPayload = {
1211
+ itemName: itemName,
1212
+ itemPrice: itemPrice,
1213
+ itemImage: itemImage,
1214
+ section: section,
1215
+ category: selectedCategory,
1216
+ addons: [],
1217
+ instructions: '',
1218
+ quantity: newQuantity
1219
+ };
1220
+
1221
+ if (newQuantity > 0) {
1222
+ fetch('/cart/add', {
1223
+ method: 'POST',
1224
+ headers: {
1225
+ 'Content-Type': 'application/json',
1226
+ },
1227
+ body: JSON.stringify(cartPayload)
1228
+ })
1229
+ .then(response => response.json())
1230
+ .then(data => {
1231
+ if (data.success) {
1232
+ updateCartUI(data.cart);
1233
+ } else {
1234
+ console.error('Failed to update cart:', data.error);
1235
+ const cart = addToCartLocalStorage(cartPayload);
1236
+ updateCartUI(cart);
1237
+ }
1238
+ })
1239
+ .catch(err => {
1240
+ console.error('Error updating cart:', err);
1241
+ const cart = addToCartLocalStorage(cartPayload);
1242
+ updateCartUI(cart);
1243
+ })
1244
+ .finally(() => {
1245
+ isProcessingRequest = false;
1246
+ });
1247
+ } else {
1248
+ fetch(`/cart/remove?item_name=${encodeURIComponent(itemName)}&instructions=&addons=[]`, {
1249
+ method: 'POST',
1250
+ headers: {
1251
+ 'Content-Type': 'application/json',
1252
+ }
1253
+ })
1254
+ .then(response => response.json())
1255
+ .then(data => {
1256
+ if (data.success) {
1257
+ updateCartUI(data.cart);
1258
+ } else {
1259
+ console.error('Failed to remove item:', data.error);
1260
+ const cart = removeFromCartLocalStorage(itemName, 1, '', []);
1261
+ updateCartUI(cart);
1262
+ }
1263
+ })
1264
+ .catch(err => {
1265
+ console.error('Error removing item:', err);
1266
+ const cart = removeFromCartLocalStorage(itemName, 1, '', []);
1267
+ updateCartUI(cart);
1268
+ })
1269
+ .finally(() => {
1270
+ isProcessingRequest = false;
1271
+ });
1272
+ }
1273
+ }
1274
+
1275
+ const debouncedUpdateCart = debounce(updateCartQuantity, 300);
1276
+
1277
+ function updateCartUI(cart) {
1278
+ if (!Array.isArray(cart)) {
1279
+ console.error('Invalid cart data:', cart);
1280
+ return;
1281
+ }
1282
+
1283
+ let totalQuantity = 0;
1284
+ cart.forEach(item => {
1285
+ totalQuantity += item.quantity;
1286
+ });
1287
+
1288
+ const cartItemCount = document.getElementById('cart-item-count');
1289
+ if (cartItemCount) {
1290
+ cartItemCount.innerText = totalQuantity;
1291
+ cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
1292
+ }
1293
+
1294
+ const buttonContainers = document.querySelectorAll('.button-container');
1295
+ buttonContainers.forEach(container => {
1296
+ const itemName = container.getAttribute('data-item-name');
1297
+ const section = container.getAttribute('data-item-section');
1298
+ const quantityDisplay = container.querySelector('.quantity-display');
1299
+ const addButton = container.querySelector('.add-to-cart-btn');
1300
+ const quantitySelector = container.querySelector('.quantity-selector');
1301
+
1302
+ const cartItem = cart.find(item =>
1303
+ item.itemName === itemName &&
1304
+ item.section === section &&
1305
+ item.instructions === '' &&
1306
+ JSON.stringify(item.addons) === JSON.stringify([])
1307
+ );
1308
+
1309
+ if (cartItem && cartItem.quantity > 0) {
1310
+ quantityDisplay.innerText = cartItem.quantity;
1311
+ addButton.style.display = 'none';
1312
+ quantitySelector.style.display = 'flex';
1313
+ } else {
1314
+ quantityDisplay.innerText = '0';
1315
+ addButton.style.display = 'block';
1316
+ quantitySelector.style.display = 'none';
1317
+ }
1318
+ });
1319
+ }
1320
+
1321
+ document.addEventListener('DOMContentLoaded', function () {
1322
+ const avatarContainer = document.querySelector('.avatar-dropdown-container');
1323
+ const dropdownMenu = document.querySelector('.dropdown-menu');
1324
+ avatarContainer.addEventListener('click', function (event) {
1325
+ event.stopPropagation();
1326
+ dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1327
+ });
1328
+ document.addEventListener('click', function (event) {
1329
+ if (!avatarContainer.contains(event.target)) {
1330
+ dropdownMenu.style.display = 'none';
1331
+ }
1332
+ });
1333
+ const dropdownItems = document.querySelectorAll('.dropdown-item');
1334
+ dropdownItems.forEach(item => {
1335
+ item.addEventListener('click', function () {
1336
+ dropdownMenu.style.display = 'none';
1337
+ });
1338
+ });
1339
+ const menuCards = document.querySelectorAll('.menu-card');
1340
+ menuCards.forEach(card => {
1341
+ const itemName = card.querySelector('.card-title').innerText;
1342
+ const detailsDiv = card.querySelector('.item-details');
1343
+ const detailsData = menuItemDetails[itemName];
1344
+ if (detailsData) {
1345
  detailsDiv.innerHTML = `
1346
  <h6>Ingredients</h6>
1347
  <p>${detailsData.ingredients}</p>
 
1359
  detailsDiv.innerHTML = '<p>No details available for this item.</p>';
1360
  }
1361
  });
 
1362
  const cardObserver = new IntersectionObserver((entries, observer) => {
1363
  entries.forEach(entry => {
1364
  if (entry.isIntersecting) {
 
1371
  rootMargin: '0px',
1372
  threshold: 0.1
1373
  });
1374
+ const videoObserver = new IntersectionObserver((entries, observer) => {
1375
+ entries.forEach(entry => {
1376
+ if (entry.isIntersecting) {
1377
+ const video = entry.target;
1378
+ const container = video.closest('.video-container');
1379
+ const src = getValidVideoUrl(video.getAttribute('data-src'));
1380
+ if (src && !video.querySelector('source[src="' + src + '"]')) {
1381
+ const source = video.querySelector('source');
1382
+ source.src = src;
1383
+ video.load().then(() => {
1384
+ video.classList.add('loaded');
1385
+ }).catch(err => {
1386
+ console.error('Video load error:', err);
1387
+ container.classList.add('video-error');
1388
+ });
1389
+ }
1390
+ observer.unobserve(video);
1391
+ }
1392
+ });
1393
+ }, {
1394
+ root: null,
1395
+ rootMargin: '200px',
1396
+ threshold: 0.01
1397
+ });
1398
  menuCards.forEach(card => cardObserver.observe(card));
1399
+ document.querySelectorAll('.menu-video').forEach(video => videoObserver.observe(video));
1400
  const toggleLinks = document.querySelectorAll('.toggle-details');
1401
  toggleLinks.forEach(link => {
1402
  link.addEventListener('click', function () {
 
1406
  this.innerText = detailsDiv.classList.contains('show') ? 'Hide Details' : 'Show Details';
1407
  });
1408
  });
 
1409
  const categoryButtons = document.querySelectorAll('.category-button');
1410
  const categoryForm = document.getElementById('categoryForm');
1411
  const selectedCategoryInput = document.getElementById('selectedCategoryInput');
 
1412
  if (!selectedCategoryInput.value) {
1413
  selectedCategoryInput.value = "All";
1414
  document.querySelector('.category-button[data-category="All"]').classList.add('selected');
1415
  }
 
1416
  categoryButtons.forEach(button => {
1417
  button.addEventListener('click', function () {
1418
  categoryButtons.forEach(btn => btn.classList.remove('selected'));
 
1421
  categoryForm.submit();
1422
  });
1423
  });
 
1424
  const searchBar = document.getElementById('searchBar');
1425
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
 
1426
  searchBar.addEventListener('input', function () {
1427
  const input = this.value.trim().toLowerCase();
1428
  suggestionsContainer.innerHTML = '';
1429
  suggestionsContainer.style.display = 'none';
 
1430
  if (input) {
1431
  const filteredItems = menuItems.filter(item =>
1432
  item.toLowerCase().includes(input)
 
1448
  }
1449
  filterMenu();
1450
  });
 
1451
  document.addEventListener('click', function (event) {
1452
  if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1453
  suggestionsContainer.style.display = 'none';
1454
  }
1455
  });
 
1456
  const descriptionTextarea = document.getElementById('custom-dish-description');
1457
  const descriptionSuggestions = document.getElementById('descriptionSuggestions');
 
1458
  if (descriptionTextarea && descriptionSuggestions) {
1459
  let usedIngredients = new Set();
 
1460
  function updateUsedIngredients() {
1461
  const inputText = descriptionTextarea.value.trim();
1462
  usedIngredients.clear();
 
1469
  });
1470
  }
1471
  }
 
1472
  descriptionTextarea.addEventListener('input', function () {
1473
  const inputText = this.value.trim();
1474
  const words = inputText.split(/,\s*/);
1475
  const lastWord = words[words.length - 1].trim().toLowerCase();
1476
  descriptionSuggestions.innerHTML = '';
1477
  descriptionSuggestions.style.display = 'none';
 
1478
  updateUsedIngredients();
 
1479
  if (lastWord) {
1480
  const filteredIngredients = ingredientsList.filter(ingredient =>
1481
  ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
 
1500
  }
1501
  }
1502
  });
 
1503
  document.addEventListener('click', function (event) {
1504
  if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1505
  descriptionSuggestions.style.display = 'none';
1506
  }
1507
  });
1508
  }
 
1509
  fetch('/cart/get')
1510
  .then(response => {
1511
  if (!response.ok) {
 
1527
  const cart = getCartLocalStorage();
1528
  updateCartUI(cart);
1529
  });
1530
+ const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1531
+ preloadedVideos.forEach(link => {
1532
+ const video = document.createElement('video');
1533
+ video.src = link.href;
1534
+ video.preload = 'auto';
1535
  });
 
1536
  const decreaseBtn = document.getElementById('decreaseQuantity');
1537
  const increaseBtn = document.getElementById('increaseQuantity');
1538
  const quantityInput = document.getElementById('quantityInput');
 
1539
  decreaseBtn.addEventListener('click', function () {
1540
  let currentQuantity = parseInt(quantityInput.value);
1541
  if (currentQuantity > 1) {
 
1543
  quantityInput.value = currentQuantity;
1544
  }
1545
  });
 
1546
  increaseBtn.addEventListener('click', function () {
1547
  let currentQuantity = parseInt(quantityInput.value);
1548
  currentQuantity++;
1549
  quantityInput.value = currentQuantity;
1550
  });
 
1551
 
1552
+ const micIcon = document.getElementById('micIcon');
1553
+ if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
1554
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1555
+ const recognition = new SpeechRecognition();
1556
+ recognition.lang = 'en-US';
1557
+ recognition.onstart = () => micIcon.classList.add('active');
1558
+ recognition.onresult = (event) => {
1559
+ searchBar.value = event.results[0][0].transcript.trim();
1560
+ filterMenu();
1561
+ };
1562
+ recognition.onend = () => micIcon.classList.remove('active');
1563
+ recognition.onerror = (event) => {
1564
+ micIcon.classList.remove('active');
1565
+ console.error('Speech error:', event.error);
1566
+ };
1567
+ micIcon.addEventListener('click', () => {
1568
+ recognition.start();
1569
+ });
1570
+ } else {
1571
+ micIcon.style.display = 'none';
1572
+ }
1573
+ });
1574
 
1575
  function filterMenu() {
1576
  const input = document.getElementById('searchBar').value.trim().toLowerCase();
1577
  const sections = document.querySelectorAll('h3');
1578
  const items = document.querySelectorAll('.menu-card');
1579
  let matchedSections = new Set();
 
1580
  items.forEach(item => {
1581
  const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1582
  const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
 
1583
  if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1584
  item.style.display = 'block';
1585
  item.classList.add('visible');
 
1588
  item.style.display = 'none';
1589
  }
1590
  });
 
1591
  sections.forEach(section => {
1592
  const sectionRow = section.nextElementSibling;
1593
  if (matchedSections.has(sectionRow)) {
 
1598
  sectionRow.style.display = 'none';
1599
  }
1600
  });
 
1601
  if (!input) {
1602
  sections.forEach(section => {
1603
  section.style.display = 'block';
 
1614
  document.getElementById('modal-name').innerText = name;
1615
  document.getElementById('modal-price').innerText = `$${price}`;
1616
  const modalImg = document.getElementById('modal-img');
1617
+ modalImg.src = image || '/static/placeholder.jpg';
 
 
 
 
 
1618
  document.getElementById('modal-description').innerText = description || 'No description available.';
 
 
 
 
 
 
 
 
 
1619
  document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1620
  document.getElementById('modal-instructions').value = '';
1621
  const modalSectionEl = document.getElementById('modal-section');
 
1623
  modalSectionEl.setAttribute('data-category', selectedCategory);
1624
  document.getElementById('quantityInput').value = 1;
1625
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1626
  fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1627
  .then(response => response.json())
1628
  .then(data => {
1629
  const addonsList = document.getElementById('addons-list');
1630
  addonsList.innerHTML = '';
 
1631
  if (!data.success || !data.addons || data.addons.length === 0) {
1632
  addonsList.innerHTML = '<p>No customization options available.</p>';
 
1633
  return;
1634
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1635
  data.addons.forEach(addon => {
 
 
 
 
 
 
 
1636
  const sectionDiv = document.createElement('div');
1637
  sectionDiv.classList.add('addon-section');
 
 
1638
  const title = document.createElement('h6');
1639
  title.innerText = addon.name;
1640
  sectionDiv.appendChild(title);
 
1641
  const optionsContainer = document.createElement('div');
1642
  addon.options.forEach((option, index) => {
1643
  const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1644
  const listItem = document.createElement('div');
1645
  listItem.classList.add('form-check');
 
1646
  listItem.innerHTML = `
1647
+ <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1648
  data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1649
  <label class="form-check-label" for="${optionId}">
1650
  ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
 
1655
  sectionDiv.appendChild(optionsContainer);
1656
  addonsList.appendChild(sectionDiv);
1657
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1658
  })
1659
  .catch(err => {
1660
  console.error('Error fetching add-ons:', err);
1661
  document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
 
1662
  });
1663
  }
1664
 
1665
  document.addEventListener('click', function(event) {
1666
+ if (event.target.classList.contains('addon-option')) {
1667
  handleAddonClick(event.target);
1668
  }
1669
  });
1670
 
1671
  function handleAddonClick(checkbox) {
1672
  const groupName = checkbox.getAttribute('data-group');
1673
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo"].includes(groupName);
1674
+ if (!isMultiSelectGroup) {
 
 
 
 
 
 
 
 
 
1675
  const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1676
  checkboxes.forEach(otherCheckbox => {
1677
  if (otherCheckbox !== checkbox) {
 
1693
  const section = modalSectionEl.getAttribute('data-section');
1694
  const selectedCategory = modalSectionEl.getAttribute('data-category');
1695
  if (!itemName || !itemPrice || !section || !itemImage) {
1696
+ console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage });
1697
  return;
1698
  }
1699
+ const selectedAddOns = Array.from(
1700
+ document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
1701
+ ).map(addon => ({
1702
+ name: addon.getAttribute('data-name') || 'Default Name',
1703
+ price: parseFloat(addon.getAttribute('data-price') || 0)
1704
+ }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1705
  const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1706
  const instructions = document.getElementById('modal-instructions').value;
1707
  const cartPayload = {
 
1714
  instructions: instructions,
1715
  quantity: quantity
1716
  };
1717
+
1718
  fetch('/cart/add', {
1719
  method: 'POST',
1720
  headers: {
 
1722
  },
1723
  body: JSON.stringify(cartPayload)
1724
  })
1725
+ .then(response => response.json())
 
 
 
 
 
1726
  .then(data => {
1727
  if (data.success) {
1728
  alert('Item added to cart successfully!');
 
1737
  })
1738
  .catch(err => {
1739
  console.error('Error adding item to cart:', err);
 
1740
  const cart = addToCartLocalStorage(cartPayload);
1741
  updateCartUI(cart);
1742
  const modal = document.getElementById('itemModal');
 
1744
  modalInstance.hide();
1745
  });
1746
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1747
  </script>
1748
  </body>
1749
  </html>