lokesh341 commited on
Commit
9715188
·
verified ·
1 Parent(s): 1e496b7

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +500 -637
templates/menu.html CHANGED
@@ -1,4 +1,3 @@
1
-
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
@@ -27,7 +26,6 @@
27
  }
28
  .container {
29
  max-width: 900px;
30
- padding: 0 15px;
31
  }
32
  .menu-card {
33
  max-width: 350px;
@@ -38,12 +36,11 @@
38
  display: flex;
39
  flex-direction: column;
40
  opacity: 0;
41
- transition: opacity 0.3s ease-in-out, transform 0.3s ease;
42
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
43
  }
44
  .menu-card.visible {
45
  opacity: 1;
46
- transform: translateY(0);
47
  }
48
  .menu-video {
49
  height: 200px;
@@ -81,12 +78,11 @@
81
  font-weight: bold;
82
  border-radius: 5px;
83
  border: none;
84
- transition: background-color 0.3s ease, transform 0.2s ease;
85
  margin-left: 13px;
86
  }
87
  .addbutton .btn:hover {
88
  background-color: #218838;
89
- transform: scale(1.05);
90
  }
91
  .button-container {
92
  display: flex;
@@ -178,11 +174,6 @@
178
  border: 1px solid #ffd8b1;
179
  padding: 5px 0;
180
  z-index: 1000;
181
- animation: slideDown 0.2s ease-out;
182
- }
183
- @keyframes slideDown {
184
- from { opacity: 0; transform: translateY(-10px); }
185
- to { opacity: 1; transform: translateY(0); }
186
  }
187
  .dropdown-menu .dropdown-item {
188
  padding: 10px 16px;
@@ -213,7 +204,7 @@
213
  color: #2c2c2c;
214
  }
215
  .fixed-top-bar {
216
- position: fixed;
217
  top: 0;
218
  left: 0;
219
  width: 100%;
@@ -225,7 +216,6 @@
225
  justify-content: space-between;
226
  align-items: center;
227
  z-index: 1000;
228
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
229
  }
230
  .search-bar-container {
231
  position: absolute;
@@ -247,10 +237,6 @@
247
  background-color: #fff;
248
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
249
  outline: none;
250
- transition: box-shadow 0.3s ease;
251
- }
252
- .search-bar-container input:focus {
253
- box-shadow: 0 4px 8px rgba(0,0,0,0.15);
254
  }
255
  .search-bar-container input::placeholder {
256
  color: #888;
@@ -302,7 +288,6 @@
302
  cursor: pointer;
303
  font-size: 14px;
304
  color: #333;
305
- transition: background-color 0.2s ease;
306
  }
307
  .autocomplete-suggestions .suggestion-item:hover {
308
  background-color: #f1f1f1;
@@ -580,56 +565,13 @@
580
  #custom-dish-form {
581
  position: relative;
582
  padding-bottom: 80px;
583
- background: white;
584
- padding: 20px;
585
- border-radius: 10px;
586
- box-shadow: 0 2px 10px rgba(0,0,0,0.1);
587
- margin-bottom: 20px;
588
- max-height: 70vh;
589
- overflow-y: auto;
590
- }
591
- #custom-dish-form h3 {
592
- font-size: 1.3rem;
593
- color: #0FAA39;
594
- margin-bottom: 20px;
595
- text-align: center;
596
  }
597
  #custom-dish-form .btn-primary {
598
- width: 100%;
599
- padding: 10px 20px;
600
- margin-top: 15px;
601
- }
602
- #custom-dish-form::-webkit-scrollbar {
603
- width: 8px;
604
- }
605
- #custom-dish-form::-webkit-scrollbar-track {
606
- background: #f1f1f1;
607
- border-radius: 10px;
608
- }
609
- #custom-dish-form::-webkit-scrollbar-thumb {
610
- background: #0FAA39;
611
- border-radius: 10px;
612
- }
613
- #custom-dish-form::-webkit-scrollbar-thumb:hover {
614
- background: #0D9232;
615
- }
616
- #dishNameSuggestions {
617
  position: absolute;
618
- width: calc(100% - 30px);
619
- max-height: 200px;
620
- overflow-y: auto;
621
- background: white;
622
- border: 1px solid #ddd;
623
- border-radius: 0 0 4px 4px;
624
- z-index: 1000;
625
- display: none;
626
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
627
- }
628
- .selection-buttons .btn {
629
- width: 100px;
630
- }
631
- .chat-bar {
632
- margin-top: 20px;
633
  }
634
  .bottom-action-bar {
635
  position: fixed;
@@ -660,10 +602,6 @@
660
  text-align: center;
661
  min-width: 0;
662
  white-space: nowrap;
663
- transition: transform 0.2s ease;
664
- }
665
- .bottom-action-bar .btn:hover {
666
- transform: scale(1.05);
667
  }
668
  .bottom-action-bar .btn-order-history {
669
  background-color: #FFA07A;
@@ -692,7 +630,6 @@
692
  justify-content: center;
693
  font-size: 12px;
694
  margin-left: 8px;
695
- transition: all 0.3s ease;
696
  }
697
  .mic-popup {
698
  position: fixed;
@@ -935,120 +872,81 @@
935
  padding: 6px 16px;
936
  font-size: 14px;
937
  }
938
- #custom-dish-form {
939
- padding: 15px;
940
- max-height: 60vh;
941
- }
942
  }
943
  </style>
944
  </head>
945
  <body>
946
- <div class="fixed-top-bar" role="banner">
947
  <div class="avatar-dropdown-container">
948
- <div class="avatar-icon" id="avatarIcon" role="button" aria-label="User Menu">
949
  {% if user_image %}
950
  <img src="{{ user_image }}" alt="User Avatar" class="avatar-image">
951
  {% else %}
952
  <span>{{ first_letter }}</span>
953
  {% endif %}
954
  </div>
955
- <div class="dropdown-menu" id="avatarDropdown" role="menu">
956
  {% if user_image %}
957
- <div class="dropdown-item delete-item" id="deleteAvatar" role="menuitem">Delete Image</div>
958
- <div class="dropdown-item view-item" id="viewAvatar" role="menuitem">View Avatar</div>
959
  {% endif %}
960
- <a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item" role="menuitem">Order History</a>
961
- <a href="{{ url_for('user_details.customer_details') }}" class="dropdown-item" role="menuitem">View Profile</a>
962
- <div class="dropdown-item upload-item" role="menuitem">
963
  <label for="avatarUpload" style="cursor: pointer; margin: 0; width: 100%;">
964
  Upload Image
965
  </label>
966
- <input type="file" id="avatarUpload" accept="image/*" style="display: none;" aria-label="Upload Avatar">
967
  </div>
968
- <a href="{{ url_for('logout') }}" class="dropdown-item" role="menuitem">Logout</a>
969
  </div>
970
  </div>
971
  <div class="search-bar-container">
972
- <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off" aria-label="Search menu items">
973
  <i class="bi bi-search search-icon"></i>
974
- <i class="bi bi-mic mic-icon" id="micIcon" role="button" aria-label="Voice Search"></i>
975
  <span class="mic-unsupported" id="micUnsupported">Mic not supported</span>
976
  <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
977
  </div>
978
  </div>
979
 
980
- <form method="get" action="/menu" class="text-center mb-4" id="categoryForm" role="form">
981
- <label class="form-label fw-bold" for="selectedCategoryInput">Select a Category:</label>
982
  <div class="category-buttons">
983
  {% for category in categories %}
984
- <button type="button" class="category-button {% if selected_category == category %}selected{% endif %}" data-category="{{ category }}" aria-pressed="{% if selected_category == category %}true{% else %}false{% endif %}">{{ category }}</button>
985
  {% endfor %}
986
- <button type="button" class="category-button {% if selected_category == 'Customized Dish' %}selected{% endif %}" data-category="Customized Dish" aria-pressed="{% if selected_category == 'Customized Dish' %}true{% else %}false{% endif %}">Customized Dish</button>
987
  </div>
988
  <input type="hidden" name="category" id="selectedCategoryInput" value="{{ selected_category }}">
989
  </form>
990
 
991
- <div class="container mt-4" role="main">
992
  {% if selected_category == "Customized Dish" %}
993
  <div id="custom-dish-form" class="mt-4">
994
  <h3>Create Your Custom Dish</h3>
995
-
996
- <!-- Veg/Non-Veg Selection -->
997
- <div class="selection-buttons mb-3">
998
- <button id="veg-btn" class="btn btn-outline-success me-2" aria-label="Select Vegetarian">Veg</button>
999
- <button id="non-veg-btn" class="btn btn-outline-danger" aria-label="Select Non-Vegetarian">Non-Veg</button>
1000
- </div>
1001
-
1002
- <!-- Custom Dish Form -->
1003
- <form id="customDishForm" role="form">
1004
- <div class="mb-3 position-relative">
1005
- <label for="custom-dish-name" class="form-label">Dish Name</label>
1006
- <input type="text" class="form-control" id="custom-dish-name" name="name" required autocomplete="off" placeholder="e.g., Spicy Chicken Curry" aria-label="Custom Dish Name">
1007
- <div id="dishNameSuggestions" class="autocomplete-suggestions"></div>
1008
- </div>
1009
  <div class="mb-3">
1010
- <label for="custom-dish-ingredients" class="form-label">Ingredients List</label>
1011
- <textarea class="form-control" id="custom-dish-ingredients" name="ingredients" required rows="3" placeholder="e.g., Chicken, Tomatoes, Spices" aria-label="Ingredients List"></textarea>
1012
  </div>
1013
- <div class="mb-3">
1014
  <label for="custom-dish-description" class="form-label">Dish Description</label>
1015
- <textarea class="form-control" id="custom-dish-description" name="description" required rows="3" placeholder="e.g., A spicy and flavorful chicken dish" aria-label="Dish Description"></textarea>
 
1016
  </div>
1017
- <div class="mb-3">
1018
- <label for="custom-dish-price" class="form-label">Price ($)</label>
1019
- <input type="number" class="form-control" id="custom-dish-price" name="price" step="0.01" min="0.01" required placeholder="e.g., 12.99" aria-label="Price">
1020
- </div>
1021
- <button type="submit" class="btn btn-primary" id="submitCustomDish" aria-label="Submit to Salesforce">Submit to Salesforce</button>
1022
- <div id="form-feedback" class="mt-3" style="display: none; text-align: center;"></div>
1023
  </form>
1024
-
1025
- <!-- Selected Item Details -->
1026
- <div id="item-details" class="mt-3" style="display: none; background: #f8f9fa; padding: 15px; border-radius: 8px;">
1027
- <h4>Selected Item Details</h4>
1028
- <p><strong>Name:</strong> <span id="detail-name"></span></p>
1029
- <p><strong>Ingredients:</strong> <span id="detail-ingredients"></span></p>
1030
- <p><strong>Description:</strong> <span id="detail-description"></span></p>
1031
- <p><strong>Price:</strong> $<span id="detail-price"></span></p>
1032
- </div>
1033
-
1034
- <!-- Text Bar and Send Button -->
1035
- <div class="chat-bar mt-3">
1036
- <div class="input-group">
1037
- <input type="text" class="form-control" id="chat-input" placeholder="Type 'hi' or 'veg'/'non-veg'..." aria-label="Chat Input">
1038
- <button class="btn btn-primary" id="send-btn" aria-label="Send Message">Send</button>
1039
- </div>
1040
- <div id="chat-feedback" class="mt-2" style="text-align: center;"></div>
1041
- </div>
1042
  </div>
1043
  {% else %}
1044
  {% if ordered_menu.items()|length == 0 %}
1045
- <p class="text-center">No menu items available for this category.</p>
1046
  {% else %}
1047
  {% for section, items in ordered_menu.items() %}
1048
  <h3>{{ section }}</h3>
1049
  <div class="row">
1050
  {% for item in items %}
1051
- <div class="col-md-6 mb-4" data-item-name="{{ item.Name | default('Unnamed Item') | e }}">
1052
  <div class="card menu-card">
1053
  <video
1054
  class="card-img-top menu-video"
@@ -1061,8 +959,7 @@
1061
  height="200"
1062
  onmouseover="this.play()"
1063
  onmouseout="this.pause(); this.currentTime = 0;"
1064
- onerror="this.poster='/static/placeholder.jpg';"
1065
- aria-label="Video preview of {{ item.Name | default('Unnamed Item') }}">
1066
  <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
1067
  Your browser does not support the video tag.
1068
  </video>
@@ -1072,7 +969,7 @@
1072
  <h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5>
1073
  <p class="card-text price">${{ item.Price__c | default('0.00') }}</p>
1074
  {% if item.Section__c != 'Soft Drinks' %}
1075
- <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}" role="button" aria-expanded="false" aria-controls="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">Show Details</div>
1076
  {% endif %}
1077
  </div>
1078
  <div class="d-flex flex-column align-item-center justify-content-center">
@@ -1083,13 +980,12 @@
1083
  data-item-section="{{ item.Section__c | default(section) }}"
1084
  data-item-category="{{ selected_category }}">
1085
  {% if item.Section__c == 'Soft Drinks' %}
1086
- <button class="btn btn-primary add-to-cart-btn" onclick="showSoftDrinkModal(this)" aria-label="Add {{ item.Name | default('Unnamed Item') }} to cart">ADD</button>
1087
  {% else %}
1088
  <button class="btn btn-primary"
1089
  data-bs-toggle="modal"
1090
  data-bs-target="#itemModal"
1091
- 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 }}')"
1092
- aria-label="Add {{ item.Name | default('Unnamed Item') }} to cart">
1093
  ADD
1094
  </button>
1095
  {% endif %}
@@ -1105,7 +1001,7 @@
1105
  <h6>Description</h6>
1106
  <p>{{ item.Description__c | default('No description available') }}</p>
1107
  <h6>Ingredients Info</h6>
1108
- <p>{{ item.IngredientsInfo__c | default('Not specified') }}</p>
1109
  <h6>Nutritional Info</h6>
1110
  <p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
1111
  <h6>Allergens</h6>
@@ -1121,11 +1017,11 @@
1121
  {% endif %}
1122
  </div>
1123
 
1124
- <div class="bottom-action-bar" role="navigation">
1125
- <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history" aria-label="View Order History">
1126
  <i class="bi bi-clock-history"></i> Order History
1127
  </a>
1128
- <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart" aria-label="View Cart">
1129
  <i class="bi bi-cart"></i> View Cart
1130
  <span class="cart-icon-badge" id="cart-item-count">{{ cart_item_count }}</span>
1131
  </a>
@@ -1155,17 +1051,17 @@
1155
  </div>
1156
  <div class="mt-4">
1157
  <h6>Custom Request</h6>
1158
- <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..." aria-label="Special Instructions"></textarea>
1159
  </div>
1160
  <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
1161
  </div>
1162
  <div class="modal-footer d-flex align-items-center justify-content-between">
1163
  <div class="d-flex align-items-center gap-2">
1164
- <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity" aria-label="Decrease Quantity">-</button>
1165
- <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;" aria-label="Quantity"/>
1166
- <button type="button" class="btn btn-outline-secondary" id="increaseQuantity" aria-label="Increase Quantity">+</button>
1167
  </div>
1168
- <button type="button" class="btn btn-primary" onclick="addToCartFromModal()" aria-label="Add to Cart">Add to Cart</button>
1169
  </div>
1170
  </div>
1171
  </div>
@@ -1183,7 +1079,7 @@
1183
  <img id="avatar-modal-img" class="img-fluid rounded mx-auto d-block" alt="Avatar Image" style="max-height: 400px; object-fit: contain;">
1184
  </div>
1185
  <div class="modal-footer">
1186
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-label="Close Avatar Modal">Close</button>
1187
  </div>
1188
  </div>
1189
  </div>
@@ -1205,45 +1101,26 @@
1205
  </div>
1206
  <div class="d-flex justify-content-center align-items-center mb-4">
1207
  <div class="quantity-selector" style="background-color: #f8f9fa; padding: 10px; border-radius: 10px;">
1208
- <button type="button" class="btn btn-outline-secondary" id="soft-drink-decrease" style="width: 40px; height: 40px;" aria-label="Decrease Drink Quantity">-</button>
1209
- <input type="text" class="form-control text-center mx-2" id="soft-drink-quantity" value="1" readonly style="width: 60px; font-weight: bold; font-size: 1.1rem;" aria-label="Drink Quantity">
1210
- <button type="button" class="btn btn-outline-secondary" id="soft-drink-increase" style="width: 40px; height: 40px;" aria-label="Increase Drink Quantity">+</button>
1211
  </div>
1212
  </div>
1213
  </div>
1214
  <div class="modal-footer" style="border-top: none; padding: 0 20px 20px 20px; justify-content: center;">
1215
- <button type="button" class="btn btn-primary" onclick="addSoftDrinkToCart()" style="width: 200px; padding: 12px; font-size: 1.1rem; background-color: #0FAA39; border-color: #0FAA39;" aria-label="Add Drink to Cart">Add to Cart</button>
1216
  </div>
1217
  </div>
1218
  </div>
1219
  </div>
1220
 
1221
  <!-- Mic Popup -->
1222
- <div class="mic-popup" id="micPopup" role="alert" aria-live="polite">
1223
  <div class="mic-popup-icon">
1224
  <i class="bi bi-mic-fill"></i>
1225
  </div>
1226
  <div class="mic-popup-text" id="micPopupText">Listening...</div>
1227
- <button class="mic-popup-cancel" id="micPopupCancel" aria-label="Cancel Voice Search">Cancel</button>
1228
- </div>
1229
-
1230
- <!-- Confirmation Modal -->
1231
- <div class="modal fade" id="confirmationModal" tabindex="-1" aria-labelledby="confirmationModalLabel" aria-hidden="true">
1232
- <div class="modal-dialog modal-dialog-centered">
1233
- <div class="modal-content">
1234
- <div class="modal-header">
1235
- <h5 class="modal-title" id="confirmationModalLabel">Confirm Action</h5>
1236
- <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1237
- </div>
1238
- <div class="modal-body">
1239
- <p id="confirmationMessage"></p>
1240
- </div>
1241
- <div class="modal-footer">
1242
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal" aria-label="Cancel">Cancel</button>
1243
- <button type="button" class="btn btn-primary" id="confirmAction" aria-label="Confirm">Confirm</button>
1244
- </div>
1245
- </div>
1246
- </div>
1247
  </div>
1248
 
1249
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
@@ -1259,29 +1136,23 @@
1259
  {% endfor %}
1260
  ];
1261
 
1262
- const sections = [
1263
- {% for section in ordered_menu.keys() %}
1264
- "{{ section | e }}",
1265
- {% endfor %}
 
 
 
 
 
 
 
 
 
 
1266
  ];
1267
 
1268
- const dishDatabase = {
1269
- veg: {
1270
- "Aloo Paratha": { ingredients: "Wheat flour, potatoes, spices (cumin, coriander), ghee", description: "Stuffed potato flatbread cooked with ghee, a North Indian breakfast favorite.", price: 8.99 },
1271
- "Dosa": { ingredients: "Rice, urad dal, salt (fermented batter)", description: "Crispy South Indian crepe, typically served with sambar and chutney.", price: 8.99 },
1272
- "Dal Tadka": { ingredients: "Toor dal, garlic, cumin, ghee, tomatoes", description: "Tempered lentil dish with aromatic spices, a staple across India.", price: 7.99 },
1273
- "Pav Bhaji": { ingredients: "Mixed veggies (potatoes, peas, capsicum), butter, pav bread", description: "Mumbai's famous buttery vegetable mash with soft bread rolls.", price: 10.49 },
1274
- "Masala Dosa": { ingredients: "Dosa batter, potato filling, mustard seeds, curry leaves", description: "Crispy crepe stuffed with spiced potatoes, served with chutney.", price: 9.99 }
1275
- },
1276
- nonVeg: {
1277
- "Butter Chicken": { ingredients: "Chicken, tomato puree, butter, cream, garam masala", description: "Creamy Punjabi curry with tender chicken pieces in rich tomato sauce.", price: 15.99 },
1278
- "Biryani": { ingredients: "Basmati rice, meat (chicken/mutton), spices (saffron, bay leaf), yogurt, fried onions", description: "Fragrant layered rice dish with spiced meat, a celebration favorite.", price: 14.99 },
1279
- "Chicken Chettinad": { ingredients: "Chicken, coconut, black pepper, fennel, curry leaves", description: "Fiery Tamil Nadu chicken curry with aromatic spices and coconut.", price: 16.99 },
1280
- "Fish Curry": { ingredients: "Fish (pomfret/rohu), coconut milk, tamarind, curry leaves", description: "Goan/Kerala style fish in tangy coconut gravy with spices.", price: 17.99 },
1281
- "Tandoori Chicken": { ingredients: "Chicken, yogurt, red chili, tandoori masala, lemon", description: "Clay oven-roasted chicken marinated in yogurt and spices.", price: 15.99 }
1282
- }
1283
- };
1284
-
1285
  function sanitizeInput(input) {
1286
  const div = document.createElement('div');
1287
  div.textContent = input;
@@ -1296,7 +1167,7 @@
1296
  JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1297
  );
1298
  if (existingItem) {
1299
- existingItem.quantity += payload.quantity;
1300
  } else {
1301
  cart.push(payload);
1302
  }
@@ -1334,17 +1205,6 @@
1334
  };
1335
  }
1336
 
1337
- function showConfirmation(message, callback) {
1338
- document.getElementById('confirmationMessage').innerText = message;
1339
- const confirmBtn = document.getElementById('confirmAction');
1340
- confirmBtn.onclick = function() {
1341
- callback();
1342
- bootstrap.Modal.getInstance(document.getElementById('confirmationModal')).hide();
1343
- };
1344
- const modal = new bootstrap.Modal(document.getElementById('confirmationModal'));
1345
- modal.show();
1346
- }
1347
-
1348
  function showSoftDrinkModal(button) {
1349
  currentSoftDrinkButton = button;
1350
  const buttonContainer = button.closest('.button-container');
@@ -1367,10 +1227,6 @@
1367
 
1368
  const buttonContainer = currentSoftDrinkButton.closest('.button-container');
1369
  const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
1370
- if (quantity < 1 || quantity > 99) {
1371
- alert('Quantity must be between 1 and 99.');
1372
- return;
1373
- }
1374
 
1375
  const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
1376
  const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
@@ -1386,8 +1242,7 @@
1386
  category: selectedCategory,
1387
  addons: [],
1388
  instructions: '',
1389
- quantity: quantity,
1390
- timestamp: new Date().toISOString()
1391
  };
1392
 
1393
  fetch('/cart/add', {
@@ -1402,9 +1257,15 @@
1402
  if (data.success) {
1403
  alert('Item added to cart successfully!');
1404
  updateCartUI(data.cart);
1405
- bootstrap.Modal.getInstance(document.getElementById('softDrinkModal')).hide();
 
1406
  } else {
1407
- throw new Error(data.error || 'Failed to add item to cart');
 
 
 
 
 
1408
  }
1409
  })
1410
  .catch(err => {
@@ -1412,7 +1273,8 @@
1412
  alert('Error adding item to cart. Using local storage as fallback.');
1413
  const cart = addToCartLocalStorage(cartPayload);
1414
  updateCartUI(cart);
1415
- bootstrap.Modal.getInstance(document.getElementById('softDrinkModal')).hide();
 
1416
  });
1417
  }
1418
 
@@ -1442,6 +1304,7 @@
1442
  const deleteAvatar = document.getElementById('deleteAvatar');
1443
  const viewAvatar = document.getElementById('viewAvatar');
1444
 
 
1445
  function loadAvatar() {
1446
  const savedAvatar = localStorage.getItem('userAvatar');
1447
  const avatarImage = avatarIcon.querySelector('.avatar-image');
@@ -1460,6 +1323,7 @@
1460
  }
1461
  }
1462
 
 
1463
  function ensureAvatarOptions() {
1464
  if (!document.querySelector('#deleteAvatar')) {
1465
  const deleteItem = document.createElement('div');
@@ -1480,6 +1344,7 @@
1480
  }
1481
  }
1482
 
 
1483
  function removeAvatarOptions() {
1484
  const deleteItem = document.getElementById('deleteAvatar');
1485
  const viewItem = document.getElementById('viewAvatar');
@@ -1487,21 +1352,25 @@
1487
  if (viewItem) viewItem.remove();
1488
  }
1489
 
 
1490
  function saveAvatar(imageUrl) {
1491
  localStorage.setItem('userAvatar', imageUrl);
1492
  }
1493
 
 
1494
  function clearAvatar() {
1495
  localStorage.removeItem('userAvatar');
1496
  avatarIcon.innerHTML = `<span>{{ first_letter }}</span>`;
1497
  removeAvatarOptions();
1498
  }
1499
 
 
1500
  avatarIcon.addEventListener('click', function(event) {
1501
  event.stopPropagation();
1502
  dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1503
  });
1504
 
 
1505
  avatarUpload.addEventListener('change', function(event) {
1506
  const file = event.target.files[0];
1507
  if (file) {
@@ -1538,7 +1407,17 @@
1538
  ensureAvatarOptions();
1539
  dropdownMenu.style.display = 'none';
1540
  } else {
1541
- throw new Error(data.error || 'Unknown error');
 
 
 
 
 
 
 
 
 
 
1542
  }
1543
  })
1544
  .catch(error => {
@@ -1557,43 +1436,51 @@
1557
  });
1558
  };
1559
  reader.onerror = function() {
1560
- alert('Error reading the image file.');
 
 
 
 
 
 
 
 
 
 
1561
  };
1562
  reader.readAsDataURL(file);
1563
  }
1564
  });
1565
 
 
1566
  function addDeleteListener(deleteElement) {
1567
  deleteElement.addEventListener('click', function() {
1568
- showConfirmation('Are you sure you want to delete your avatar?', function() {
1569
- fetch('/delete_avatar', {
1570
- method: 'POST',
1571
- headers: {
1572
- 'Content-Type': 'application/json'
1573
- }
1574
- })
1575
- .then(response => {
1576
- if (!response.ok) throw new Error('Delete failed');
1577
- return response.json();
1578
- })
1579
- .then(data => {
1580
- if (data.success) {
1581
- clearAvatar();
1582
- dropdownMenu.style.display = 'none';
1583
- } else {
1584
- throw new Error(data.error || 'Unknown error');
1585
- }
1586
- })
1587
- .catch(error => {
1588
- console.error('Delete error:', error);
1589
- alert('Error deleting image. Cleared locally.');
1590
  clearAvatar();
1591
  dropdownMenu.style.display = 'none';
1592
- });
 
 
 
 
 
 
1593
  });
1594
  });
1595
  }
1596
 
 
1597
  function addViewListener(viewElement) {
1598
  viewElement.addEventListener('click', function() {
1599
  const avatarImage = avatarIcon.querySelector('.avatar-image');
@@ -1609,11 +1496,14 @@
1609
  });
1610
  }
1611
 
 
1612
  if (deleteAvatar) addDeleteListener(deleteAvatar);
1613
  if (viewAvatar) addViewListener(viewAvatar);
1614
 
 
1615
  loadAvatar();
1616
 
 
1617
  document.addEventListener('click', function(event) {
1618
  if (!avatarContainer.contains(event.target)) {
1619
  dropdownMenu.style.display = 'none';
@@ -1669,7 +1559,6 @@
1669
  const otherLink = otherDetails.previousElementSibling.querySelector('.toggle-details');
1670
  if (otherLink) {
1671
  otherLink.innerText = 'Show Details';
1672
- otherLink.setAttribute('aria-expanded', 'false');
1673
  }
1674
  }
1675
  });
@@ -1677,11 +1566,9 @@
1677
  if (!isCurrentlyShown) {
1678
  detailsDiv.classList.add('show');
1679
  this.innerText = 'Hide Details';
1680
- this.setAttribute('aria-expanded', 'true');
1681
  } else {
1682
  detailsDiv.classList.remove('show');
1683
  this.innerText = 'Show Details';
1684
- this.setAttribute('aria-expanded', 'false');
1685
  }
1686
  });
1687
  });
@@ -1691,8 +1578,7 @@
1691
  const selectedCategoryInput = document.getElementById('selectedCategoryInput');
1692
  if (!selectedCategoryInput.value) {
1693
  selectedCategoryInput.value = "All";
1694
- const allBtn = document.querySelector('.category-button[data-category="All"]');
1695
- if (allBtn) allBtn.classList.add('selected');
1696
  }
1697
  categoryButtons.forEach(button => {
1698
  button.addEventListener('click', function () {
@@ -1705,290 +1591,270 @@
1705
 
1706
  const searchBar = document.getElementById('searchBar');
1707
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1708
-
1709
- // Redirect to search page on click
1710
- searchBar.addEventListener('click', function (event) {
1711
- event.stopPropagation();
1712
- window.location.href = '/search';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1713
  });
1714
 
1715
- // Customized Dish Logic
1716
- const customDishForm = document.getElementById('customDishForm');
1717
- const dishNameInput = document.getElementById('custom-dish-name');
1718
- const dishIngredients = document.getElementById('custom-dish-ingredients');
1719
- const dishDescription = document.getElementById('custom-dish-description');
1720
- const dishPrice = document.getElementById('custom-dish-price');
1721
- const dishSuggestions = document.getElementById('dishNameSuggestions');
1722
- const formFeedback = document.getElementById('form-feedback');
1723
- const chatInput = document.getElementById('chat-input');
1724
- const sendBtn = document.getElementById('send-btn');
1725
- const chatFeedback = document.getElementById('chat-feedback');
1726
- const vegBtn = document.getElementById('veg-btn');
1727
- const nonVegBtn = document.getElementById('non-veg-btn');
1728
- const itemDetails = document.getElementById('item-details');
1729
-
1730
- if (customDishForm) {
1731
- let filterType = 'all'; // Default to show all items
1732
-
1733
- // Show item details
1734
- function showItemDetails(dish) {
1735
- if (dish) {
1736
- itemDetails.style.display = 'block';
1737
- document.getElementById('detail-name').textContent = dish;
1738
- document.getElementById('detail-ingredients').textContent = (dishDatabase.veg[dish] || dishDatabase.nonVeg[dish]).ingredients;
1739
- document.getElementById('detail-description').textContent = (dishDatabase.veg[dish] || dishDatabase.nonVeg[dish]).description;
1740
- document.getElementById('detail-price').textContent = (dishDatabase.veg[dish] || dishDatabase.nonVeg[dish]).price.toFixed(2);
1741
- } else {
1742
- itemDetails.style.display = 'none';
1743
  }
1744
  }
1745
-
1746
- // Autocomplete function
1747
- function updateSuggestions(input) {
1748
- const sanitizedInput = sanitizeInput(input.trim().toLowerCase());
1749
- dishSuggestions.innerHTML = '';
1750
- dishSuggestions.style.display = 'none';
1751
-
1752
- if (sanitizedInput.length > 0) {
1753
- let matches = [];
1754
- if (filterType === 'veg') {
1755
- matches = Object.keys(dishDatabase.veg).filter(dish => dish.toLowerCase().includes(sanitizedInput));
1756
- } else if (filterType === 'non-veg') {
1757
- matches = Object.keys(dishDatabase.nonVeg).filter(dish => dish.toLowerCase().includes(sanitizedInput));
1758
- } else {
1759
- matches = [...Object.keys(dishDatabase.veg), ...Object.keys(dishDatabase.nonVeg)].filter(dish => dish.toLowerCase().includes(sanitizedInput));
1760
- }
1761
-
1762
- if (matches.length > 0) {
1763
- matches.slice(0, 5).forEach(dish => {
1764
- const suggestion = document.createElement('div');
1765
- suggestion.classList.add('suggestion-item');
1766
- suggestion.textContent = dish;
1767
- suggestion.addEventListener('click', function () {
1768
- dishNameInput.value = dish;
1769
- const dishData = dishDatabase.veg[dish] || dishDatabase.nonVeg[dish];
1770
- if (dishData) {
1771
- dishIngredients.value = dishData.ingredients;
1772
- dishDescription.value = dishData.description;
1773
- dishPrice.value = dishData.price.toFixed(2);
1774
- showItemDetails(dish);
1775
- }
1776
- dishSuggestions.style.display = 'none';
1777
  });
1778
- dishSuggestions.appendChild(suggestion);
1779
  });
1780
- dishSuggestions.style.display = 'block';
1781
  }
1782
  }
1783
- }
1784
-
1785
- // Autocomplete for dish name
1786
- dishNameInput.addEventListener('input', function () {
1787
- updateSuggestions(this.value);
1788
- });
1789
-
1790
- // Close suggestions when clicking outside
1791
- document.addEventListener('click', function (e) {
1792
- if (e.target !== dishNameInput && !dishSuggestions.contains(e.target)) {
1793
- dishSuggestions.style.display = 'none';
1794
- }
1795
- });
1796
-
1797
- // Veg/Non-Veg button handlers
1798
- vegBtn.addEventListener('click', function () {
1799
- filterType = 'veg';
1800
- vegBtn.classList.remove('btn-outline-success');
1801
- vegBtn.classList.add('btn-success');
1802
- nonVegBtn.classList.remove('btn-danger');
1803
- nonVegBtn.classList.add('btn-outline-danger');
1804
- updateSuggestions(dishNameInput.value);
1805
- });
1806
-
1807
- nonVegBtn.addEventListener('click', function () {
1808
- filterType = 'non-veg';
1809
- nonVegBtn.classList.remove('btn-outline-danger');
1810
- nonVegBtn.classList.add('btn-danger');
1811
- vegBtn.classList.remove('btn-success');
1812
- vegBtn.classList.add('btn-outline-success');
1813
- updateSuggestions(dishNameInput.value);
1814
  });
1815
-
1816
- // Chat bar handler
1817
- sendBtn.addEventListener('click', function () {
1818
- const input = sanitizeInput(chatInput.value.trim().toLowerCase());
1819
- chatFeedback.innerHTML = '';
1820
-
1821
- if (input === 'hi') {
1822
- fetch('/salesforce/get_customer_name', {
1823
- method: 'GET',
1824
- headers: { 'Content-Type': 'application/json' }
1825
- })
1826
- .then(response => response.json())
1827
- .then(data => {
1828
- const customerName = data.name || 'Customer';
1829
- chatFeedback.innerHTML = `Welcome ${customerName}!`;
1830
- chatFeedback.className = 'text-success';
1831
- })
1832
- .catch(error => {
1833
- console.error('Error fetching customer name:', error);
1834
- chatFeedback.innerHTML = 'Welcome Guest!';
1835
- chatFeedback.className = 'text-success';
1836
- });
1837
- } else if (input === 'veg') {
1838
- filterType = 'veg';
1839
- vegBtn.click();
1840
- chatFeedback.innerHTML = 'Showing vegetarian items.';
1841
- chatFeedback.className = 'text-success';
1842
- } else if (input === 'non-veg' || input === 'non veg') {
1843
- filterType = 'non-veg';
1844
- nonVegBtn.click();
1845
- chatFeedback.innerHTML = 'Showing non-vegetarian items.';
1846
- chatFeedback.className = 'text-success';
1847
- } else {
1848
- chatFeedback.innerHTML = 'Please type "hi", "veg", or "non-veg".';
1849
- chatFeedback.className = 'text-danger';
1850
  }
1851
- chatInput.value = '';
1852
  });
 
1853
 
1854
- // Form submission to Salesforce
1855
- customDishForm.addEventListener('submit', function (event) {
1856
- event.preventDefault();
1857
-
1858
- formFeedback.style.display = 'none';
1859
- formFeedback.innerHTML = '';
1860
-
1861
- const dishName = sanitizeInput(dishNameInput.value.trim());
1862
- const ingredients = sanitizeInput(dishIngredients.value.trim());
1863
- const description = sanitizeInput(dishDescription.value.trim());
1864
- const price = parseFloat(dishPrice.value);
1865
-
1866
- if (!dishName) {
1867
- showFeedback('Please enter a dish name.', 'text-danger');
1868
- return;
1869
- }
1870
- if (!ingredients) {
1871
- showFeedback('Please enter ingredients.', 'text-danger');
1872
- return;
1873
- }
1874
- if (!description) {
1875
- showFeedback('Please enter a description.', 'text-danger');
1876
- return;
1877
- }
1878
- if (isNaN(price) || price <= 0) {
1879
- showFeedback('Please enter a valid price greater than 0.', 'text-danger');
1880
  return;
1881
  }
1882
-
1883
- const submitBtn = document.getElementById('submitCustomDish');
1884
- submitBtn.disabled = true;
1885
- submitBtn.innerText = 'Submitting...';
1886
-
1887
- const formData = {
1888
- name: dishName,
1889
- ingredients: ingredients,
1890
- description: description,
1891
- price: price,
1892
- type: filterType === 'veg' ? 'Vegetarian' : 'Non-Vegetarian'
1893
- };
1894
-
1895
- fetch('/salesforce/submit_custom_dish', {
1896
  method: 'POST',
1897
  headers: {
1898
- 'Content-Type': 'application/json',
1899
  },
1900
- body: JSON.stringify(formData)
1901
- })
1902
- .then(response => {
1903
- if (!response.ok) {
1904
- throw new Error(`Server error: ${response.status}`);
1905
- }
1906
- return response.json();
1907
  })
 
1908
  .then(data => {
1909
  if (data.success) {
1910
- showFeedback('Custom dish submitted to Salesforce successfully!', 'text-success');
1911
- customDishForm.reset();
1912
- itemDetails.style.display = 'none';
1913
- setTimeout(() => {
1914
- window.location.reload();
1915
- }, 1500);
1916
  } else {
1917
- throw new Error(data.error || 'Submission failed');
1918
  }
1919
  })
1920
  .catch(error => {
1921
  console.error('Error submitting custom dish:', error);
1922
- showFeedback(`Error: ${error.message}. Please try again.`, 'text-danger');
1923
- })
1924
- .finally(() => {
1925
- submitBtn.disabled = false;
1926
- submitBtn.innerText = 'Submit to Salesforce';
1927
  });
1928
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1929
 
1930
- // Show feedback to user
1931
- function showFeedback(message, className) {
1932
- formFeedback.innerHTML = message;
1933
- formFeedback.className = `mt-3 ${className}`;
1934
- formFeedback.style.display = 'block';
 
 
 
1935
  }
1936
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1937
 
1938
- // Voice Search Functionality
1939
  const micIcon = document.getElementById('micIcon');
 
1940
  const micPopup = document.getElementById('micPopup');
1941
  const micPopupText = document.getElementById('micPopupText');
1942
  const micPopupCancel = document.getElementById('micPopupCancel');
1943
- const micUnsupported = document.getElementById('micUnsupported');
1944
-
1945
- if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
1946
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1947
  const recognition = new SpeechRecognition();
1948
  recognition.continuous = false;
1949
- recognition.interimResults = false;
1950
  recognition.lang = 'en-US';
1951
-
1952
- micIcon.addEventListener('click', function () {
 
1953
  micPopup.classList.add('active');
1954
- recognition.start();
1955
- });
1956
-
1957
- recognition.onstart = function () {
1958
  micPopupText.textContent = 'Listening...';
1959
- micIcon.classList.add('active');
1960
  };
1961
-
1962
- recognition.onresult = function (event) {
1963
- const transcript = event.results[0][0].transcript.trim().toLowerCase();
1964
- micPopupText.textContent = `You said: "${transcript}"`;
1965
- setTimeout(() => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1966
  micPopup.classList.remove('active');
1967
- searchBar.value = transcript;
1968
- performSearch(transcript);
1969
- micIcon.classList.remove('active');
1970
- }, 1000);
1971
  };
1972
-
1973
- recognition.onerror = function (event) {
1974
- micPopupText.textContent = 'Error occurred in recognition: ' + event.error;
 
 
 
 
 
 
 
 
 
 
1975
  setTimeout(() => {
1976
  micPopup.classList.remove('active');
1977
- micIcon.classList.remove('active');
1978
  }, 2000);
 
1979
  };
1980
-
1981
- recognition.onend = function () {
1982
- if (micPopup.classList.contains('active')) {
1983
- micPopupText.textContent = 'Recognition ended.';
 
 
1984
  setTimeout(() => {
1985
  micPopup.classList.remove('active');
1986
- micIcon.classList.remove('active');
1987
- }, 1000);
1988
  }
1989
- };
1990
-
1991
- micPopupCancel.addEventListener('click', function () {
1992
  recognition.stop();
1993
  micPopup.classList.remove('active');
1994
  micIcon.classList.remove('active');
@@ -1997,179 +1863,176 @@
1997
  micIcon.style.display = 'none';
1998
  micUnsupported.style.display = 'block';
1999
  }
 
2000
 
2001
- function performSearch(query) {
2002
- const lowerQuery = query.toLowerCase();
2003
- const matchingSection = sections.find(section => section.toLowerCase().includes(lowerQuery));
2004
- if (matchingSection) {
2005
- selectedCategoryInput.value = matchingSection;
2006
- categoryForm.submit();
 
 
 
 
 
 
2007
  } else {
2008
- const matchingItem = menuItems.find(item => item.toLowerCase().includes(lowerQuery));
2009
- if (matchingItem) {
2010
- const itemCard = document.querySelector(`[data-item-name="${matchingItem}"]`);
2011
- if (itemCard) {
2012
- itemCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
2013
- itemCard.classList.add('highlight');
2014
- setTimeout(() => itemCard.classList.remove('highlight'), 2000);
2015
- }
2016
- } else {
2017
- alert('No matching items or sections found.');
2018
- }
2019
  }
2020
- }
2021
-
2022
- function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, category) {
2023
- document.getElementById('modal-name').textContent = name;
2024
- document.getElementById('modal-price').textContent = `$${price}`;
2025
- document.getElementById('modal-img').src = image;
2026
- document.getElementById('modal-description').textContent = description;
2027
- document.getElementById('modal-ingredients').textContent = ingredients;
2028
- document.getElementById('modal-nutrition').textContent = nutrition;
2029
- document.getElementById('modal-allergens').textContent = allergens;
2030
- document.getElementById('modal-section').dataset.section = section;
2031
- document.getElementById('modal-section').dataset.category = category;
2032
- document.getElementById('quantityInput').value = '1';
2033
- document.getElementById('modal-instructions').value = '';
2034
-
2035
- fetchAddons(name, section);
2036
- }
2037
-
2038
- function fetchAddons(itemName, section) {
2039
- const addonsList = document.getElementById('addons-list');
2040
- addonsList.innerHTML = 'Loading customization options...';
2041
-
2042
- fetch(`/menu/addons?item_name=${encodeURIComponent(itemName)}&section=${encodeURIComponent(section)}`)
2043
- .then(response => response.json())
2044
- .then(data => {
2045
- addonsList.innerHTML = '';
2046
- if (data.addons && data.addons.length > 0) {
2047
- data.addons.forEach(addon => {
2048
- const addonDiv = document.createElement('div');
2049
- addonDiv.className = 'addon-section mb-3';
2050
- addonDiv.innerHTML = `
2051
- <h6>${addon.name}</h6>
2052
- ${addon.options.map(option => `
2053
- <div class="form-check">
2054
- <input class="form-check-input" type="${addon.type === 'single' ? 'radio' : 'checkbox'}"
2055
- name="${addon.name}" id="${option.id}" value="${option.name}"
2056
- data-price="${option.price}">
2057
- <label class="form-check-label" for="${option.id}">
2058
- ${option.name} ${option.price > 0 ? '(+$' + option.price.toFixed(2) + ')' : ''}
2059
- </label>
2060
- </div>
2061
- `).join('')}
2062
- `;
2063
- addonsList.appendChild(addonDiv);
2064
- });
2065
- } else {
2066
- addonsList.innerHTML = '<p>No customization options available.</p>';
2067
- }
2068
- })
2069
- .catch(error => {
2070
- console.error('Error fetching addons:', error);
2071
- addonsList.innerHTML = '<p>Error loading customization options.</p>';
2072
- });
2073
- }
2074
-
2075
- function addToCartFromModal() {
2076
- if (isProcessingRequest) return;
2077
- isProcessingRequest = true;
2078
-
2079
- const itemName = sanitizeInput(document.getElementById('modal-name').textContent);
2080
- const itemPrice = parseFloat(document.getElementById('modal-price').textContent.replace('$', ''));
2081
- const itemImage = document.getElementById('modal-img').src;
2082
- const section = sanitizeInput(document.getElementById('modal-section').dataset.section);
2083
- const category = sanitizeInput(document.getElementById('modal-section').dataset.category);
2084
- const quantity = parseInt(document.getElementById('quantityInput').value);
2085
- const instructions = sanitizeInput(document.getElementById('modal-instructions').value);
2086
-
2087
- if (quantity < 1 || quantity > 99) {
2088
- alert('Quantity must be between 1 and 99.');
2089
- isProcessingRequest = false;
2090
- return;
2091
  }
2092
-
2093
- const addons = [];
2094
- document.querySelectorAll('#addons-list .form-check-input:checked').forEach(input => {
2095
- const addonName = input.getAttribute('name');
2096
- const optionName = input.value;
2097
- const optionPrice = parseFloat(input.getAttribute('data-price')) || 0;
2098
- addons.push({ addonName, optionName, optionPrice });
2099
  });
 
 
 
 
 
 
2100
 
2101
- const cartPayload = {
2102
- itemName: itemName,
2103
- itemPrice: itemPrice,
2104
- itemImage: itemImage,
2105
- section: section,
2106
- category: category,
2107
- addons: addons,
2108
- instructions: instructions,
2109
- quantity: quantity,
2110
- timestamp: new Date().toISOString()
2111
- };
2112
-
2113
- fetch('/cart/add', {
2114
- method: 'POST',
2115
- headers: {
2116
- 'Content-Type': 'application/json',
2117
- },
2118
- body: JSON.stringify(cartPayload)
2119
- })
2120
  .then(response => response.json())
2121
  .then(data => {
2122
- if (data.success) {
2123
- alert('Item added to cart successfully!');
2124
- updateCartUI(data.cart);
2125
- bootstrap.Modal.getInstance(document.getElementById('itemModal')).hide();
2126
- } else {
2127
- throw new Error(data.error || 'Failed to add item to cart');
2128
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2129
  })
2130
  .catch(err => {
2131
- console.error('Error adding item to cart:', err);
2132
- alert('Error adding item to cart. Using local storage as fallback.');
2133
- const cart = addToCartLocalStorage(cartPayload);
2134
- updateCartUI(cart);
2135
- bootstrap.Modal.getInstance(document.getElementById('itemModal')).hide();
2136
- })
2137
- .finally(() => {
2138
- isProcessingRequest = false;
2139
  });
 
 
 
 
 
2140
  }
 
2141
 
2142
- document.getElementById('increaseQuantity').addEventListener('click', function () {
2143
- let quantity = parseInt(document.getElementById('quantityInput').value);
2144
- if (quantity < 99) {
2145
- document.getElementById('quantityInput').value = quantity + 1;
2146
- }
2147
- });
 
 
 
 
2148
 
2149
- document.getElementById('decreaseQuantity').addEventListener('click', function () {
2150
- let quantity = parseInt(document.getElementById('quantityInput').value);
2151
- if (quantity > 1) {
2152
- document.getElementById('quantityInput').value = quantity - 1;
2153
- }
2154
- });
 
 
 
 
 
 
2155
 
2156
- document.getElementById('soft-drink-increase').addEventListener('click', function () {
2157
- let quantity = parseInt(document.getElementById('soft-drink-quantity').value);
2158
- if (quantity < 99) {
2159
- document.getElementById('soft-drink-quantity').value = quantity + 1;
2160
- }
2161
- });
 
 
 
 
2162
 
2163
- document.getElementById('soft-drink-decrease').addEventListener('click', function () {
2164
- let quantity = parseInt(document.getElementById('soft-drink-quantity').value);
2165
- if (quantity > 1) {
2166
- document.getElementById('soft-drink-quantity').value = quantity - 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2167
  }
 
 
 
 
 
 
 
 
 
2168
  });
2169
-
2170
- const initialCart = getCartLocalStorage();
2171
- updateCartUI(initialCart);
2172
- });
2173
  </script>
2174
  </body>
2175
- </html>
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
 
26
  }
27
  .container {
28
  max-width: 900px;
 
29
  }
30
  .menu-card {
31
  max-width: 350px;
 
36
  display: flex;
37
  flex-direction: column;
38
  opacity: 0;
39
+ transition: opacity 0.3s ease-in-out;
40
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
41
  }
42
  .menu-card.visible {
43
  opacity: 1;
 
44
  }
45
  .menu-video {
46
  height: 200px;
 
78
  font-weight: bold;
79
  border-radius: 5px;
80
  border: none;
81
+ transition: background-color 0.3s ease;
82
  margin-left: 13px;
83
  }
84
  .addbutton .btn:hover {
85
  background-color: #218838;
 
86
  }
87
  .button-container {
88
  display: flex;
 
174
  border: 1px solid #ffd8b1;
175
  padding: 5px 0;
176
  z-index: 1000;
 
 
 
 
 
177
  }
178
  .dropdown-menu .dropdown-item {
179
  padding: 10px 16px;
 
204
  color: #2c2c2c;
205
  }
206
  .fixed-top-bar {
207
+ position: relative;
208
  top: 0;
209
  left: 0;
210
  width: 100%;
 
216
  justify-content: space-between;
217
  align-items: center;
218
  z-index: 1000;
 
219
  }
220
  .search-bar-container {
221
  position: absolute;
 
237
  background-color: #fff;
238
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
239
  outline: none;
 
 
 
 
240
  }
241
  .search-bar-container input::placeholder {
242
  color: #888;
 
288
  cursor: pointer;
289
  font-size: 14px;
290
  color: #333;
 
291
  }
292
  .autocomplete-suggestions .suggestion-item:hover {
293
  background-color: #f1f1f1;
 
565
  #custom-dish-form {
566
  position: relative;
567
  padding-bottom: 80px;
 
 
 
 
 
 
 
 
 
 
 
 
 
568
  }
569
  #custom-dish-form .btn-primary {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
  position: absolute;
571
+ right: 15px;
572
+ bottom: 15px;
573
+ width: auto;
574
+ padding: 10px 20px;
 
 
 
 
 
 
 
 
 
 
 
575
  }
576
  .bottom-action-bar {
577
  position: fixed;
 
602
  text-align: center;
603
  min-width: 0;
604
  white-space: nowrap;
 
 
 
 
605
  }
606
  .bottom-action-bar .btn-order-history {
607
  background-color: #FFA07A;
 
630
  justify-content: center;
631
  font-size: 12px;
632
  margin-left: 8px;
 
633
  }
634
  .mic-popup {
635
  position: fixed;
 
872
  padding: 6px 16px;
873
  font-size: 14px;
874
  }
 
 
 
 
875
  }
876
  </style>
877
  </head>
878
  <body>
879
+ <div class="fixed-top-bar">
880
  <div class="avatar-dropdown-container">
881
+ <div class="avatar-icon" id="avatarIcon">
882
  {% if user_image %}
883
  <img src="{{ user_image }}" alt="User Avatar" class="avatar-image">
884
  {% else %}
885
  <span>{{ first_letter }}</span>
886
  {% endif %}
887
  </div>
888
+ <div class="dropdown-menu" id="avatarDropdown">
889
  {% if user_image %}
890
+ <div class="dropdown-item delete-item" id="deleteAvatar">Delete Image</div>
891
+ <div class="dropdown-item view-item" id="viewAvatar">View Avatar</div>
892
  {% endif %}
893
+ <a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item">Order History</a>
894
+ <a href="{{ url_for('user_details.customer_details') }}" class="dropdown-item">View Profile</a>
895
+ <div class="dropdown-item upload-item">
896
  <label for="avatarUpload" style="cursor: pointer; margin: 0; width: 100%;">
897
  Upload Image
898
  </label>
899
+ <input type="file" id="avatarUpload" accept="image/*" style="display: none;">
900
  </div>
901
+ <a href="{{ url_for('logout') }}" class="dropdown-item">Logout</a>
902
  </div>
903
  </div>
904
  <div class="search-bar-container">
905
+ <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
906
  <i class="bi bi-search search-icon"></i>
907
+ <i class="bi bi-mic mic-icon" id="micIcon"></i>
908
  <span class="mic-unsupported" id="micUnsupported">Mic not supported</span>
909
  <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
910
  </div>
911
  </div>
912
 
913
+ <form method="get" action="/menu" class="text-center mb-4" id="categoryForm">
914
+ <label class="form-label fw-bold">Select a Category:</label>
915
  <div class="category-buttons">
916
  {% for category in categories %}
917
+ <button type="button" class="category-button {% if selected_category == category %}selected{% endif %}" data-category="{{ category }}">{{ category }}</button>
918
  {% endfor %}
919
+ <button type="button" class="category-button {% if selected_category == 'Customized Dish' %}selected{% endif %}" data-category="Customized Dish">Customized Dish</button>
920
  </div>
921
  <input type="hidden" name="category" id="selectedCategoryInput" value="{{ selected_category }}">
922
  </form>
923
 
924
+ <div class="container mt-4">
925
  {% if selected_category == "Customized Dish" %}
926
  <div id="custom-dish-form" class="mt-4">
927
  <h3>Create Your Custom Dish</h3>
928
+ <form method="POST" action="/customdish/generate_custom_dish" id="customDishForm">
 
 
 
 
 
 
 
 
 
 
 
 
 
929
  <div class="mb-3">
930
+ <label for="custom-dish-name" class="form-label">Dish Name</label>
931
+ <input type="text" class="form-control" id="custom-dish-name" name="name" required>
932
  </div>
933
+ <div class="mb-3 position-relative">
934
  <label for="custom-dish-description" class="form-label">Dish Description</label>
935
+ <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
936
+ <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
937
  </div>
938
+ <button type="submit" class="btn btn-primary">Submit Custom Dish</button>
 
 
 
 
 
939
  </form>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
940
  </div>
941
  {% else %}
942
  {% if ordered_menu.items()|length == 0 %}
943
+ <p>No menu items available for this category.</p>
944
  {% else %}
945
  {% for section, items in ordered_menu.items() %}
946
  <h3>{{ section }}</h3>
947
  <div class="row">
948
  {% for item in items %}
949
+ <div class="col-md-6 mb-4">
950
  <div class="card menu-card">
951
  <video
952
  class="card-img-top menu-video"
 
959
  height="200"
960
  onmouseover="this.play()"
961
  onmouseout="this.pause(); this.currentTime = 0;"
962
+ onerror="this.poster='/static/placeholder.jpg';">
 
963
  <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
964
  Your browser does not support the video tag.
965
  </video>
 
969
  <h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5>
970
  <p class="card-text price">${{ item.Price__c | default('0.00') }}</p>
971
  {% if item.Section__c != 'Soft Drinks' %}
972
+ <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
973
  {% endif %}
974
  </div>
975
  <div class="d-flex flex-column align-item-center justify-content-center">
 
980
  data-item-section="{{ item.Section__c | default(section) }}"
981
  data-item-category="{{ selected_category }}">
982
  {% if item.Section__c == 'Soft Drinks' %}
983
+ <button class="btn btn-primary add-to-cart-btn" onclick="showSoftDrinkModal(this)">ADD</button>
984
  {% else %}
985
  <button class="btn btn-primary"
986
  data-bs-toggle="modal"
987
  data-bs-target="#itemModal"
988
+ 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 }}')">
 
989
  ADD
990
  </button>
991
  {% endif %}
 
1001
  <h6>Description</h6>
1002
  <p>{{ item.Description__c | default('No description available') }}</p>
1003
  <h6>Ingredients Info</h6>
1004
+ <p>{{ item.Ingredientsinfo__c | default('Not specified') }}</p>
1005
  <h6>Nutritional Info</h6>
1006
  <p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
1007
  <h6>Allergens</h6>
 
1017
  {% endif %}
1018
  </div>
1019
 
1020
+ <div class="bottom-action-bar">
1021
+ <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history">
1022
  <i class="bi bi-clock-history"></i> Order History
1023
  </a>
1024
+ <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart">
1025
  <i class="bi bi-cart"></i> View Cart
1026
  <span class="cart-icon-badge" id="cart-item-count">{{ cart_item_count }}</span>
1027
  </a>
 
1051
  </div>
1052
  <div class="mt-4">
1053
  <h6>Custom Request</h6>
1054
+ <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
1055
  </div>
1056
  <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
1057
  </div>
1058
  <div class="modal-footer d-flex align-items-center justify-content-between">
1059
  <div class="d-flex align-items-center gap-2">
1060
+ <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
1061
+ <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
1062
+ <button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
1063
  </div>
1064
+ <button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
1065
  </div>
1066
  </div>
1067
  </div>
 
1079
  <img id="avatar-modal-img" class="img-fluid rounded mx-auto d-block" alt="Avatar Image" style="max-height: 400px; object-fit: contain;">
1080
  </div>
1081
  <div class="modal-footer">
1082
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
1083
  </div>
1084
  </div>
1085
  </div>
 
1101
  </div>
1102
  <div class="d-flex justify-content-center align-items-center mb-4">
1103
  <div class="quantity-selector" style="background-color: #f8f9fa; padding: 10px; border-radius: 10px;">
1104
+ <button type="button" class="btn btn-outline-secondary" id="soft-drink-decrease" style="width: 40px; height: 40px;">-</button>
1105
+ <input type="text" class="form-control text-center mx-2" id="soft-drink-quantity" value="1" readonly style="width: 60px; font-weight: bold; font-size: 1.1rem;">
1106
+ <button type="button" class="btn btn-outline-secondary" id="soft-drink-increase" style="width: 40px; height: 40px;">+</button>
1107
  </div>
1108
  </div>
1109
  </div>
1110
  <div class="modal-footer" style="border-top: none; padding: 0 20px 20px 20px; justify-content: center;">
1111
+ <button type="button" class="btn btn-primary" onclick="addSoftDrinkToCart()" style="width: 200px; padding: 12px; font-size: 1.1rem; background-color: #0FAA39; border-color: #0FAA39;">Add to Cart</button>
1112
  </div>
1113
  </div>
1114
  </div>
1115
  </div>
1116
 
1117
  <!-- Mic Popup -->
1118
+ <div class="mic-popup" id="micPopup">
1119
  <div class="mic-popup-icon">
1120
  <i class="bi bi-mic-fill"></i>
1121
  </div>
1122
  <div class="mic-popup-text" id="micPopupText">Listening...</div>
1123
+ <button class="mic-popup-cancel" id="micPopupCancel">Cancel</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1124
  </div>
1125
 
1126
  <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
 
1136
  {% endfor %}
1137
  ];
1138
 
1139
+ const ingredientsList = [
1140
+ "Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
1141
+ "Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
1142
+ "Chili Sauce", "Coconut Milk", "Coriander Powder", "Cornflour", "Cream", "Cumin Powder",
1143
+ "Cumin Seeds", "Curd (Yogurt)", "Curry Leaves", "Fish (e.g., King Fish or Salmon)",
1144
+ "Fresh Coriander Leaves", "Garam Masala", "Garlic", "Ghee (Clarified Butter)", "Ginger",
1145
+ "Ginger-Garlic Paste", "Goat Meat (Mutton)", "Green Chilies", "Honey",
1146
+ "Kasuri Methi (dried fenugreek leaves)", "Lemon Juice", "Mango Puree", "Mint Leaves",
1147
+ "Mixed Vegetables (Carrot, Peas, Potato, Cauliflower)", "Mixed Vegetables (Carrot, Peas, Potato)",
1148
+ "Mustard Seeds", "Mutton (Goat Meat)", "Oil", "Oil (for frying)", "Onion",
1149
+ "Paneer (Indian Cottage Cheese)", "Peas", "Potatoes", "Prawns", "Red Chili Powder",
1150
+ "Rice Flour", "Saffron", "Salt", "Soy Sauce", "Spring Onion", "Tamarind (for sourness)",
1151
+ "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
1152
+ "Whole Wheat Flour", "Yogurt (Curd)"
1153
  ];
1154
 
1155
+ // Utility function to sanitize input to prevent XSS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1156
  function sanitizeInput(input) {
1157
  const div = document.createElement('div');
1158
  div.textContent = input;
 
1167
  JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1168
  );
1169
  if (existingItem) {
1170
+ existingItem.quantity = payload.quantity;
1171
  } else {
1172
  cart.push(payload);
1173
  }
 
1205
  };
1206
  }
1207
 
 
 
 
 
 
 
 
 
 
 
 
1208
  function showSoftDrinkModal(button) {
1209
  currentSoftDrinkButton = button;
1210
  const buttonContainer = button.closest('.button-container');
 
1227
 
1228
  const buttonContainer = currentSoftDrinkButton.closest('.button-container');
1229
  const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
 
 
 
 
1230
 
1231
  const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
1232
  const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
 
1242
  category: selectedCategory,
1243
  addons: [],
1244
  instructions: '',
1245
+ quantity: quantity
 
1246
  };
1247
 
1248
  fetch('/cart/add', {
 
1257
  if (data.success) {
1258
  alert('Item added to cart successfully!');
1259
  updateCartUI(data.cart);
1260
+ const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1261
+ modal.hide();
1262
  } else {
1263
+ console.error('Failed to add item to cart:', data.error);
1264
+ alert(data.error || 'Failed to add item to cart. Using local storage as fallback.');
1265
+ const cart = addToCartLocalStorage(cartPayload);
1266
+ updateCartUI(cart);
1267
+ const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1268
+ modal.hide();
1269
  }
1270
  })
1271
  .catch(err => {
 
1273
  alert('Error adding item to cart. Using local storage as fallback.');
1274
  const cart = addToCartLocalStorage(cartPayload);
1275
  updateCartUI(cart);
1276
+ const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1277
+ modal.hide();
1278
  });
1279
  }
1280
 
 
1304
  const deleteAvatar = document.getElementById('deleteAvatar');
1305
  const viewAvatar = document.getElementById('viewAvatar');
1306
 
1307
+ // Load avatar from localStorage if available
1308
  function loadAvatar() {
1309
  const savedAvatar = localStorage.getItem('userAvatar');
1310
  const avatarImage = avatarIcon.querySelector('.avatar-image');
 
1323
  }
1324
  }
1325
 
1326
+ // Ensure avatar options (Delete and View) are present only once
1327
  function ensureAvatarOptions() {
1328
  if (!document.querySelector('#deleteAvatar')) {
1329
  const deleteItem = document.createElement('div');
 
1344
  }
1345
  }
1346
 
1347
+ // Remove avatar options
1348
  function removeAvatarOptions() {
1349
  const deleteItem = document.getElementById('deleteAvatar');
1350
  const viewItem = document.getElementById('viewAvatar');
 
1352
  if (viewItem) viewItem.remove();
1353
  }
1354
 
1355
+ // Save avatar to localStorage
1356
  function saveAvatar(imageUrl) {
1357
  localStorage.setItem('userAvatar', imageUrl);
1358
  }
1359
 
1360
+ // Clear avatar from localStorage
1361
  function clearAvatar() {
1362
  localStorage.removeItem('userAvatar');
1363
  avatarIcon.innerHTML = `<span>{{ first_letter }}</span>`;
1364
  removeAvatarOptions();
1365
  }
1366
 
1367
+ // Avatar click handler
1368
  avatarIcon.addEventListener('click', function(event) {
1369
  event.stopPropagation();
1370
  dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1371
  });
1372
 
1373
+ // Handle image upload with 15MB limit and error handling
1374
  avatarUpload.addEventListener('change', function(event) {
1375
  const file = event.target.files[0];
1376
  if (file) {
 
1407
  ensureAvatarOptions();
1408
  dropdownMenu.style.display = 'none';
1409
  } else {
1410
+ alert('Failed to upload image: ' + (data.error || 'Unknown error. Using default image.'));
1411
+ const img = document.createElement('img');
1412
+ img.src = '/static/default-avatar.jpg';
1413
+ img.alt = 'User Avatar';
1414
+ img.className = 'avatar-image';
1415
+ img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1416
+ avatarIcon.innerHTML = '';
1417
+ avatarIcon.appendChild(img);
1418
+ saveAvatar('/static/default-avatar.jpg');
1419
+ ensureAvatarOptions();
1420
+ dropdownMenu.style.display = 'none';
1421
  }
1422
  })
1423
  .catch(error => {
 
1436
  });
1437
  };
1438
  reader.onerror = function() {
1439
+ alert('Error reading the image file. Using default image as fallback.');
1440
+ const img = document.createElement('img');
1441
+ img.src = '/static/default-avatar.jpg';
1442
+ img.alt = 'User Avatar';
1443
+ img.className = 'avatar-image';
1444
+ img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1445
+ avatarIcon.innerHTML = '';
1446
+ avatarIcon.appendChild(img);
1447
+ saveAvatar('/static/default-avatar.jpg');
1448
+ ensureAvatarOptions();
1449
+ dropdownMenu.style.display = 'none';
1450
  };
1451
  reader.readAsDataURL(file);
1452
  }
1453
  });
1454
 
1455
+ // Handle image deletion
1456
  function addDeleteListener(deleteElement) {
1457
  deleteElement.addEventListener('click', function() {
1458
+ fetch('/delete_avatar', {
1459
+ method: 'POST',
1460
+ headers: {
1461
+ 'Content-Type': 'application/json'
1462
+ }
1463
+ })
1464
+ .then(response => {
1465
+ if (!response.ok) throw new Error('Delete failed');
1466
+ return response.json();
1467
+ })
1468
+ .then(data => {
1469
+ if (data.success) {
 
 
 
 
 
 
 
 
 
 
1470
  clearAvatar();
1471
  dropdownMenu.style.display = 'none';
1472
+ } else {
1473
+ alert('Failed to delete image: ' + (data.error || 'Unknown error'));
1474
+ }
1475
+ })
1476
+ .catch(error => {
1477
+ console.error('Delete error:', error);
1478
+ alert('Error deleting image. Please try again.');
1479
  });
1480
  });
1481
  }
1482
 
1483
+ // Handle avatar view
1484
  function addViewListener(viewElement) {
1485
  viewElement.addEventListener('click', function() {
1486
  const avatarImage = avatarIcon.querySelector('.avatar-image');
 
1496
  });
1497
  }
1498
 
1499
+ // Add listeners if elements exist
1500
  if (deleteAvatar) addDeleteListener(deleteAvatar);
1501
  if (viewAvatar) addViewListener(viewAvatar);
1502
 
1503
+ // Load avatar on page load
1504
  loadAvatar();
1505
 
1506
+ // Close dropdown when clicking outside
1507
  document.addEventListener('click', function(event) {
1508
  if (!avatarContainer.contains(event.target)) {
1509
  dropdownMenu.style.display = 'none';
 
1559
  const otherLink = otherDetails.previousElementSibling.querySelector('.toggle-details');
1560
  if (otherLink) {
1561
  otherLink.innerText = 'Show Details';
 
1562
  }
1563
  }
1564
  });
 
1566
  if (!isCurrentlyShown) {
1567
  detailsDiv.classList.add('show');
1568
  this.innerText = 'Hide Details';
 
1569
  } else {
1570
  detailsDiv.classList.remove('show');
1571
  this.innerText = 'Show Details';
 
1572
  }
1573
  });
1574
  });
 
1578
  const selectedCategoryInput = document.getElementById('selectedCategoryInput');
1579
  if (!selectedCategoryInput.value) {
1580
  selectedCategoryInput.value = "All";
1581
+ document.querySelector('.category-button[data-category="All"]').classList.add('selected');
 
1582
  }
1583
  categoryButtons.forEach(button => {
1584
  button.addEventListener('click', function () {
 
1591
 
1592
  const searchBar = document.getElementById('searchBar');
1593
  const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1594
+ const debouncedFilterMenu = debounce(filterMenu, 300);
1595
+ searchBar.addEventListener('input', function () {
1596
+ const input = sanitizeInput(this.value.trim().toLowerCase());
1597
+ suggestionsContainer.innerHTML = '';
1598
+ suggestionsContainer.style.display = 'none';
1599
+ if (input) {
1600
+ const filteredItems = menuItems.filter(item =>
1601
+ item.toLowerCase().includes(input)
1602
+ );
1603
+ if (filteredItems.length > 0) {
1604
+ filteredItems.forEach(item => {
1605
+ const suggestionDiv = document.createElement('div');
1606
+ suggestionDiv.classList.add('suggestion-item');
1607
+ suggestionDiv.innerText = item;
1608
+ suggestionDiv.addEventListener('click', function () {
1609
+ searchBar.value = item;
1610
+ suggestionsContainer.style.display = 'none';
1611
+ debouncedFilterMenu();
1612
+ });
1613
+ suggestionsContainer.appendChild(suggestionDiv);
1614
+ });
1615
+ suggestionsContainer.style.display = 'block';
1616
+ }
1617
+ }
1618
+ debouncedFilterMenu();
1619
+ });
1620
+ document.addEventListener('click', function (event) {
1621
+ if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1622
+ suggestionsContainer.style.display = 'none';
1623
+ }
1624
  });
1625
 
1626
+ const descriptionTextarea = document.getElementById('custom-dish-description');
1627
+ const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1628
+ if (descriptionTextarea && descriptionSuggestions) {
1629
+ let usedIngredients = new Set();
1630
+ function updateUsedIngredients() {
1631
+ const inputText = descriptionTextarea.value.trim();
1632
+ usedIngredients.clear();
1633
+ if (inputText) {
1634
+ const words = inputText.split(/,\s*/).map(word => word.trim());
1635
+ words.forEach(word => {
1636
+ if (word && ingredientsList.includes(word)) {
1637
+ usedIngredients.add(word);
1638
+ }
1639
+ });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1640
  }
1641
  }
1642
+ descriptionTextarea.addEventListener('input', function () {
1643
+ const inputText = this.value.trim();
1644
+ const words = inputText.split(/,\s*/);
1645
+ const lastWord = words[words.length - 1].trim().toLowerCase();
1646
+ descriptionSuggestions.innerHTML = '';
1647
+ descriptionSuggestions.style.display = 'none';
1648
+ updateUsedIngredients();
1649
+ if (lastWord) {
1650
+ const filteredIngredients = ingredientsList.filter(ingredient =>
1651
+ ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
1652
+ );
1653
+ if (filteredIngredients.length > 0) {
1654
+ filteredIngredients.forEach(ingredient => {
1655
+ const suggestionDiv = document.createElement('div');
1656
+ suggestionDiv.classList.add('suggestion-item');
1657
+ suggestionDiv.innerText = ingredient;
1658
+ suggestionDiv.addEventListener('click', function () {
1659
+ const currentValue = descriptionTextarea.value;
1660
+ const lastCommaIndex = currentValue.lastIndexOf(',');
1661
+ const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
1662
+ descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
1663
+ descriptionSuggestions.style.display = 'none';
1664
+ descriptionTextarea.focus();
1665
+ updateUsedIngredients();
 
 
 
 
 
 
 
 
1666
  });
1667
+ descriptionSuggestions.appendChild(suggestionDiv);
1668
  });
1669
+ descriptionSuggestions.style.display = 'block';
1670
  }
1671
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1672
  });
1673
+ document.addEventListener('click', function (event) {
1674
+ if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1675
+ descriptionSuggestions.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1676
  }
 
1677
  });
1678
+ }
1679
 
1680
+ // Custom Dish Form Validation
1681
+ const customDishForm = document.getElementById('customDishForm');
1682
+ if (customDishForm) {
1683
+ customDishForm.addEventListener('submit', function(event) {
1684
+ const dishName = document.getElementById('custom-dish-name').value.trim();
1685
+ const description = document.getElementById('custom-dish-description').value.trim();
1686
+ if (!dishName || !description) {
1687
+ event.preventDefault();
1688
+ alert('Please fill in both the dish name and description.');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1689
  return;
1690
  }
1691
+ event.preventDefault();
1692
+ fetch('/customdish/generate_custom_dish', {
 
 
 
 
 
 
 
 
 
 
 
 
1693
  method: 'POST',
1694
  headers: {
1695
+ 'Content-Type': 'application/x-www-form-urlencoded',
1696
  },
1697
+ body: new URLSearchParams({
1698
+ 'name': dishName,
1699
+ 'description': description
1700
+ })
 
 
 
1701
  })
1702
+ .then(response => response.json())
1703
  .then(data => {
1704
  if (data.success) {
1705
+ alert('Custom dish submitted successfully!');
1706
+ window.location.reload();
 
 
 
 
1707
  } else {
1708
+ alert('Failed to submit custom dish: ' + (data.error || 'Unknown error'));
1709
  }
1710
  })
1711
  .catch(error => {
1712
  console.error('Error submitting custom dish:', error);
1713
+ alert('Error submitting custom dish. Please try again.');
 
 
 
 
1714
  });
1715
  });
1716
+ }
1717
+
1718
+ fetch('/cart/get')
1719
+ .then(response => {
1720
+ if (!response.ok) {
1721
+ throw new Error(`HTTP error! Status: ${response.status}`);
1722
+ }
1723
+ return response.json();
1724
+ })
1725
+ .then(data => {
1726
+ if (data.success) {
1727
+ updateCartUI(data.cart);
1728
+ } else {
1729
+ console.error('Failed to fetch cart:', data.error);
1730
+ const cart = getCartLocalStorage();
1731
+ updateCartUI(cart);
1732
+ }
1733
+ })
1734
+ .catch(err => {
1735
+ console.error('Error fetching cart:', err);
1736
+ const cart = getCartLocalStorage();
1737
+ updateCartUI(cart);
1738
+ });
1739
+
1740
+ const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1741
+ preloadedVideos.forEach(link => {
1742
+ const video = document.createElement('video');
1743
+ video.src = link.href;
1744
+ video.preload = 'auto';
1745
+ });
1746
 
1747
+ const decreaseBtn = document.getElementById('decreaseQuantity');
1748
+ const increaseBtn = document.getElementById('increaseQuantity');
1749
+ const quantityInput = document.getElementById('quantityInput');
1750
+ decreaseBtn.addEventListener('click', function () {
1751
+ let currentQuantity = parseInt(quantityInput.value);
1752
+ if (currentQuantity > 1) {
1753
+ currentQuantity--;
1754
+ quantityInput.value = currentQuantity;
1755
  }
1756
+ });
1757
+ increaseBtn.addEventListener('click', function () {
1758
+ let currentQuantity = parseInt(quantityInput.value);
1759
+ currentQuantity++;
1760
+ quantityInput.value = currentQuantity;
1761
+ });
1762
+
1763
+ const softDrinkDecreaseBtn = document.getElementById('soft-drink-decrease');
1764
+ const softDrinkIncreaseBtn = document.getElementById('soft-drink-increase');
1765
+ const softDrinkQuantityInput = document.getElementById('soft-drink-quantity');
1766
+
1767
+ softDrinkDecreaseBtn.addEventListener('click', function() {
1768
+ let currentQuantity = parseInt(softDrinkQuantityInput.value);
1769
+ if (currentQuantity > 1) {
1770
+ currentQuantity--;
1771
+ softDrinkQuantityInput.value = currentQuantity;
1772
+ }
1773
+ });
1774
+
1775
+ softDrinkIncreaseBtn.addEventListener('click', function() {
1776
+ let currentQuantity = parseInt(softDrinkQuantityInput.value);
1777
+ if (currentQuantity < 1000) {
1778
+ currentQuantity++;
1779
+ softDrinkQuantityInput.value = currentQuantity;
1780
+ }
1781
+ });
1782
 
1783
+ // Mic Popup Functionality
1784
  const micIcon = document.getElementById('micIcon');
1785
+ const micUnsupported = document.getElementById('micUnsupported');
1786
  const micPopup = document.getElementById('micPopup');
1787
  const micPopupText = document.getElementById('micPopupText');
1788
  const micPopupCancel = document.getElementById('micPopupCancel');
1789
+
1790
+ if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
 
1791
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1792
  const recognition = new SpeechRecognition();
1793
  recognition.continuous = false;
1794
+ recognition.interimResults = true;
1795
  recognition.lang = 'en-US';
1796
+
1797
+ recognition.onstart = () => {
1798
+ micIcon.classList.add('active');
1799
  micPopup.classList.add('active');
 
 
 
 
1800
  micPopupText.textContent = 'Listening...';
 
1801
  };
1802
+
1803
+ recognition.onresult = (event) => {
1804
+ let interimTranscript = '';
1805
+ let finalTranscript = '';
1806
+
1807
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1808
+ const transcript = event.results[i][0].transcript;
1809
+ if (event.results[i].isFinal) {
1810
+ finalTranscript += transcript;
1811
+ } else {
1812
+ interimTranscript += transcript;
1813
+ }
1814
+ }
1815
+
1816
+ if (interimTranscript) {
1817
+ micPopupText.textContent = interimTranscript;
1818
+ }
1819
+
1820
+ if (finalTranscript) {
1821
+ searchBar.value = sanitizeInput(finalTranscript.trim());
1822
+ debouncedFilterMenu();
1823
  micPopup.classList.remove('active');
1824
+ }
 
 
 
1825
  };
1826
+
1827
+ recognition.onend = () => {
1828
+ micIcon.classList.remove('active');
1829
+ if (micPopup.classList.contains('active')) {
1830
+ setTimeout(() => {
1831
+ micPopup.classList.remove('active');
1832
+ }, 1000);
1833
+ }
1834
+ };
1835
+
1836
+ recognition.onerror = (event) => {
1837
+ micIcon.classList.remove('active');
1838
+ micPopupText.textContent = 'Error: ' + event.error;
1839
  setTimeout(() => {
1840
  micPopup.classList.remove('active');
 
1841
  }, 2000);
1842
+ console.error('Speech error:', event.error);
1843
  };
1844
+
1845
+ micIcon.addEventListener('click', () => {
1846
+ try {
1847
+ recognition.start();
1848
+ } catch (e) {
1849
+ micPopupText.textContent = 'Error starting microphone';
1850
  setTimeout(() => {
1851
  micPopup.classList.remove('active');
1852
+ }, 2000);
1853
+ console.error('Recognition start error:', e);
1854
  }
1855
+ });
1856
+
1857
+ micPopupCancel.addEventListener('click', () => {
1858
  recognition.stop();
1859
  micPopup.classList.remove('active');
1860
  micIcon.classList.remove('active');
 
1863
  micIcon.style.display = 'none';
1864
  micUnsupported.style.display = 'block';
1865
  }
1866
+ });
1867
 
1868
+ function filterMenu() {
1869
+ const input = sanitizeInput(document.getElementById('searchBar').value.trim().toLowerCase());
1870
+ const sections = document.querySelectorAll('h3');
1871
+ const items = document.querySelectorAll('.menu-card');
1872
+ let matchedSections = new Set();
1873
+ items.forEach(item => {
1874
+ const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1875
+ const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
1876
+ if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1877
+ item.style.display = 'block';
1878
+ item.classList.add('visible');
1879
+ matchedSections.add(item.closest('.row'));
1880
  } else {
1881
+ item.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
1882
  }
1883
+ });
1884
+ sections.forEach(section => {
1885
+ const sectionRow = section.nextElementSibling;
1886
+ if (matchedSections.has(sectionRow)) {
1887
+ section.style.display = 'block';
1888
+ sectionRow.style.display = 'flex';
1889
+ } else {
1890
+ section.style.display = 'none';
1891
+ sectionRow.style.display = 'none';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1892
  }
1893
+ });
1894
+ if (!input) {
1895
+ sections.forEach(section => {
1896
+ section.style.display = 'block';
1897
+ section.nextElementSibling.style.display = 'flex';
 
 
1898
  });
1899
+ items.forEach(item => {
1900
+ item.style.display = 'block';
1901
+ item.classList.add('visible');
1902
+ });
1903
+ }
1904
+ }
1905
 
1906
+ function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, selectedCategory) {
1907
+ document.getElementById('modal-name').innerText = name;
1908
+ document.getElementById('modal-price').innerText = `$${price}`;
1909
+ const modalImg = document.getElementById('modal-img');
1910
+ modalImg.src = image || '/static/placeholder.jpg';
1911
+ document.getElementById('modal-description').innerText = description || 'No description available.';
1912
+ document.getElementById('modal-ingredients').innerText = ingredients || 'Not specified';
1913
+ document.getElementById('modal-nutrition').innerText = nutrition || 'Not available';
1914
+ document.getElementById('modal-allergens').innerText = allergens || 'None listed';
1915
+ document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1916
+ document.getElementById('modal-instructions').value = '';
1917
+ const modalSectionEl = document.getElementById('modal-section');
1918
+ modalSectionEl.setAttribute('data-section', section);
1919
+ modalSectionEl.setAttribute('data-category', selectedCategory);
1920
+ document.getElementById('quantityInput').value = 1;
1921
+
1922
+ fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
 
 
1923
  .then(response => response.json())
1924
  .then(data => {
1925
+ const addonsList = document.getElementById('addons-list');
1926
+ addonsList.innerHTML = '';
1927
+ if (!data.success || !data.addons || data.addons.length === 0) {
1928
+ addonsList.innerHTML = '<p>No customization options available.</p>';
1929
+ return;
 
1930
  }
1931
+ data.addons.forEach(addon => {
1932
+ const sectionDiv = document.createElement('div');
1933
+ sectionDiv.classList.add('addon-section');
1934
+ const title = document.createElement('h6');
1935
+ title.innerText = addon.name;
1936
+ sectionDiv.appendChild(title);
1937
+ const optionsContainer = document.createElement('div');
1938
+ addon.options.forEach((option, index) => {
1939
+ const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1940
+ const listItem = document.createElement('div');
1941
+ listItem.classList.add('form-check');
1942
+ listItem.innerHTML = `
1943
+ <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1944
+ data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1945
+ <label class="form-check-label" for="${optionId}">
1946
+ ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
1947
+ </label>
1948
+ `;
1949
+ optionsContainer.appendChild(listItem);
1950
+ });
1951
+ sectionDiv.appendChild(optionsContainer);
1952
+ addonsList.appendChild(sectionDiv);
1953
+ });
1954
  })
1955
  .catch(err => {
1956
+ console.error('Error fetching add-ons:', err);
1957
+ document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
 
 
 
 
 
 
1958
  });
1959
+ }
1960
+
1961
+ document.addEventListener('click', function(event) {
1962
+ if (event.target.classList.contains('addon-option')) {
1963
+ handleAddonClick(event.target);
1964
  }
1965
+ });
1966
 
1967
+ function handleAddonClick(checkbox) {
1968
+ const groupName = checkbox.getAttribute('data-group');
1969
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it"].includes(groupName);
1970
+ const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1971
+ if (!isMultiSelectGroup) {
1972
+ checkboxes.forEach(cb => {
1973
+ if (cb !== checkbox) cb.checked = false;
1974
+ });
1975
+ }
1976
+ }
1977
 
1978
+ function addToCartFromModal() {
1979
+ const itemName = document.getElementById('modal-name').innerText;
1980
+ const itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
1981
+ const itemImage = document.getElementById('modal-img').src;
1982
+ const instructions = document.getElementById('modal-instructions').value.trim();
1983
+ const quantity = parseInt(document.getElementById('quantityInput').value);
1984
+ const section = document.getElementById('modal-section').getAttribute('data-section');
1985
+ const selectedCategory = document.getElementById('modal-section').getAttribute('data-category');
1986
+ const addons = Array.from(document.querySelectorAll('.addon-option:checked')).map(cb => ({
1987
+ name: cb.getAttribute('data-name'),
1988
+ price: parseFloat(cb.getAttribute('data-price') || 0)
1989
+ }));
1990
 
1991
+ const cartPayload = {
1992
+ itemName: itemName,
1993
+ itemPrice: itemPrice,
1994
+ itemImage: itemImage,
1995
+ section: section,
1996
+ category: selectedCategory,
1997
+ addons: addons,
1998
+ instructions: instructions,
1999
+ quantity: quantity
2000
+ };
2001
 
2002
+ fetch('/cart/add', {
2003
+ method: 'POST',
2004
+ headers: {
2005
+ 'Content-Type': 'application/json',
2006
+ },
2007
+ body: JSON.stringify(cartPayload)
2008
+ })
2009
+ .then(response => response.json())
2010
+ .then(data => {
2011
+ if (data.success) {
2012
+ alert('Item added to cart successfully!');
2013
+ updateCartUI(data.cart);
2014
+ const modal = bootstrap.Modal.getInstance(document.getElementById('itemModal'));
2015
+ modal.hide();
2016
+ } else {
2017
+ console.error('Failed to add item to cart:', data.error);
2018
+ alert(data.error || 'Failed to add item to cart. Using local storage as fallback.');
2019
+ const cart = addToCartLocalStorage(cartPayload);
2020
+ updateCartUI(cart);
2021
+ const modal = document.getElementById('itemModal');
2022
+ const modalInstance = bootstrap.Modal.getInstance(modal);
2023
+ modalInstance.hide();
2024
  }
2025
+ })
2026
+ .catch(err => {
2027
+ console.error('Error adding item to cart:', err);
2028
+ alert('Error adding item to cart. Using local storage as fallback.');
2029
+ const cart = addToCartLocalStorage(cartPayload);
2030
+ updateCartUI(cart);
2031
+ const modal = document.getElementById('itemModal');
2032
+ const modalInstance = bootstrap.Modal.getInstance(modal);
2033
+ modalInstance.hide();
2034
  });
2035
+ }
 
 
 
2036
  </script>
2037
  </body>
2038
+ </html>