lokesh341 commited on
Commit
5e1e5fe
·
verified ·
1 Parent(s): 86881a3

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +1893 -0
templates/menu.html CHANGED
@@ -0,0 +1,1893 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>
18
+ body {
19
+ font-family: Arial, sans-serif;
20
+ background-color: #fdf4e3;
21
+ margin: 0;
22
+ padding: 0;
23
+ display: flex;
24
+ flex-direction: column;
25
+ padding-bottom: 70px;
26
+ }
27
+ .container {
28
+ max-width: 900px;
29
+ }
30
+ .menu-card {
31
+ max-width: 350px;
32
+ border-radius: 15px;
33
+ overflow: hidden;
34
+ background-color: #fff;
35
+ margin: auto;
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;
47
+ width: 100%;
48
+ object-fit: cover;
49
+ border-radius: 15px 15px 0 0;
50
+ opacity: 0;
51
+ transition: opacity 0.5s ease-in-out;
52
+ background-color: #000; /* Fallback color if video fails */
53
+ }
54
+ .menu-video.loaded {
55
+ opacity: 1;
56
+ }
57
+ .menu-card:hover .menu-video {
58
+ opacity: 1;
59
+ transform: scale(1.05); /* Slight zoom effect on hover */
60
+ }
61
+ .menu-card .card-body .card-title {
62
+ font-size: 1.2rem;
63
+ font-weight: 600;
64
+ margin: 10px 0;
65
+ color: #333333;
66
+ }
67
+ .menu-card .card-body .card-text.price {
68
+ font-size: 1rem;
69
+ font-weight: 500;
70
+ color: #000000;
71
+ margin-bottom: 5px;
72
+ }
73
+ .addbutton .btn {
74
+ background-color: #28a745;
75
+ color: white;
76
+ padding: 10px 20px;
77
+ font-size: 16px;
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;
89
+ flex-direction: column;
90
+ align-items: center;
91
+ justify-content: center;
92
+ gap: 6px;
93
+ }
94
+ .customisable-text {
95
+ color: #0FAA39;
96
+ font-size: 10px;
97
+ font-weight: 500;
98
+ margin: 0;
99
+ text-align: center;
100
+ line-height: 1;
101
+ }
102
+ .btn-primary {
103
+ font-size: 12px;
104
+ font-weight: bold;
105
+ border-radius: 8px;
106
+ width: 70px;
107
+ height: 35px;
108
+ background-color: #0FAA39;
109
+ border-color: #0FAA39;
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: center;
113
+ padding: 0;
114
+ transition: background-color 0.3s ease, transform 0.1s ease;
115
+ }
116
+ .btn-primary:hover {
117
+ background-color: #0D9232;
118
+ border-color: #0D9232;
119
+ transform: scale(1.05);
120
+ }
121
+ .btn-primary:active,
122
+ .btn-primary:focus {
123
+ background-color: #0B7A29;
124
+ border-color: #0B7A29;
125
+ box-shadow: none;
126
+ transform: scale(0.98);
127
+ }
128
+ .avatar-dropdown-container {
129
+ position: absolute;
130
+ right: 10px;
131
+ top: 50%;
132
+ transform: translateY(-50%);
133
+ display: flex;
134
+ align-items: right;
135
+ justify-content: center;
136
+ }
137
+ .avatar-icon {
138
+ width: 40px;
139
+ height: 40px;
140
+ border-radius: 50%;
141
+ background-color: #007bff;
142
+ cursor: pointer;
143
+ display: flex;
144
+ align-items: center;
145
+ justify-content: center;
146
+ color: white;
147
+ font-size: 20px;
148
+ font-weight: bold;
149
+ }
150
+ .dropdown-menu {
151
+ position: absolute;
152
+ right: 0;
153
+ top: 100%;
154
+ background-color: #fff8f0;
155
+ border-radius: 5px;
156
+ width: 220px;
157
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
158
+ display: none;
159
+ border: 1px solid #ffd8b1;
160
+ }
161
+ .dropdown-menu .dropdown-item {
162
+ padding: 12px 16px;
163
+ text-decoration: none;
164
+ color: #333;
165
+ border-bottom: 1px solid #ffd8b1;
166
+ display: block;
167
+ font-size: 15px;
168
+ transition: background-color 0.2s ease;
169
+ }
170
+ .dropdown-menu .dropdown-item:last-child {
171
+ border-bottom: none;
172
+ }
173
+ .dropdown-menu .dropdown-item:hover {
174
+ background-color: #ffe4c4;
175
+ color: #333;
176
+ }
177
+ .upload-item:hover,
178
+ .delete-item:hover {
179
+ background-color: #ffe4c4;
180
+ color: #333;
181
+ }
182
+ .upload-item,
183
+ .delete-item {
184
+ padding: 12px 16px;
185
+ text-decoration: none;
186
+ color: #333;
187
+ border-bottom: 1px solid #ffd8b1;
188
+ display: block;
189
+ font-size: 15px;
190
+ transition: background-color 0.2s ease;
191
+ cursor: pointer;
192
+ }
193
+ .fixed-top-bar {
194
+ position: relative;
195
+ top: 0;
196
+ left: 0;
197
+ width: 100%;
198
+ height: 54px;
199
+ background: linear-gradient(45deg, #FFA07A, #FFB347);
200
+ color: white;
201
+ padding: 15px;
202
+ display: flex;
203
+ justify-content: space-between;
204
+ align-items: center;
205
+ z-index: 1000;
206
+ }
207
+ .search-bar-container {
208
+ position: absolute;
209
+ left: 20px;
210
+ top: 50%;
211
+ transform: translateY(-50%);
212
+ display: flex;
213
+ align-items: center;
214
+ width: 300px;
215
+ max-width: 90%;
216
+ position: relative;
217
+ }
218
+ .search-bar-container input {
219
+ width: 100%;
220
+ padding: 8px 40px 8px 40px;
221
+ font-size: 16px;
222
+ border-radius: 25px;
223
+ border: none;
224
+ background-color: #fff;
225
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
226
+ outline: none;
227
+ }
228
+ .search-bar-container input::placeholder {
229
+ color: #888;
230
+ }
231
+ .search-icon {
232
+ position: absolute;
233
+ left: 15px;
234
+ font-size: 18px;
235
+ color: #888;
236
+ }
237
+ .mic-icon {
238
+ position: absolute;
239
+ right: 15px;
240
+ font-size: 18px;
241
+ color: #888;
242
+ cursor: pointer;
243
+ transition: color 0.3s ease;
244
+ }
245
+ .mic-icon.active {
246
+ color: #007bff;
247
+ }
248
+ .mic-unsupported {
249
+ display: none;
250
+ position: absolute;
251
+ right: 15px;
252
+ font-size: 14px;
253
+ color: #888;
254
+ background-color: #fff;
255
+ padding: 2px 8px;
256
+ border-radius: 10px;
257
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
258
+ }
259
+ .autocomplete-suggestions {
260
+ position: absolute;
261
+ top: 100%;
262
+ left: 0;
263
+ width: 100%;
264
+ max-height: 200px;
265
+ overflow-y: auto;
266
+ background-color: #fff;
267
+ border: 1px solid #ddd;
268
+ border-radius: 5px;
269
+ box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
270
+ z-index: 1000;
271
+ display: none;
272
+ }
273
+ .autocomplete-suggestions .suggestion-item {
274
+ padding: 8px 15px;
275
+ cursor: pointer;
276
+ font-size: 14px;
277
+ color: #333;
278
+ }
279
+ .autocomplete-suggestions .suggestion-item:hover {
280
+ background-color: #f1f1f1;
281
+ }
282
+ .addon-section {
283
+ background-color: #fff;
284
+ border: 2px solid #ffa500;
285
+ border-radius: 8px;
286
+ padding: 12px;
287
+ margin-bottom: 10px;
288
+ }
289
+ .addon-section h6 {
290
+ margin-bottom: 10px;
291
+ font-size: 1.1rem;
292
+ font-weight: bold;
293
+ color: #343a40;
294
+ }
295
+ .addon-section .form-check {
296
+ display: inline-flex;
297
+ align-items: center;
298
+ margin-left: 10px;
299
+ color: #343a40;
300
+ }
301
+ .addon-section .form-check-input {
302
+ -webkit-appearance: none;
303
+ -moz-appearance: none;
304
+ appearance: none;
305
+ width: 20px;
306
+ height: 20px;
307
+ border: 2px solid #343a40;
308
+ border-radius: 5px;
309
+ background-color: #f0f0f0;
310
+ position: relative;
311
+ margin-right: 10px;
312
+ }
313
+ .addon-section .form-check-input:checked {
314
+ background-color: #006400;
315
+ border-color: #006400;
316
+ }
317
+ .addon-section .form-check-input:checked::before {
318
+ content: '\2713';
319
+ font-size: 14px;
320
+ position: absolute;
321
+ top: 3px;
322
+ left: 4px;
323
+ color: white;
324
+ }
325
+ .addon-section .form-check-label {
326
+ font-size: 16px;
327
+ margin-left: 5px;
328
+ margin-right: 15px;
329
+ cursor: pointer;
330
+ display: inline-block;
331
+ vertical-align: middle;
332
+ }
333
+ form.text-center.mb-4 {
334
+ display: flex;
335
+ flex-direction: column;
336
+ align-items: center;
337
+ justify-content: center;
338
+ margin-bottom: 5px;
339
+ }
340
+ .modal-header {
341
+ padding: 10px 15px;
342
+ }
343
+ .modal-title {
344
+ font-size: 16px;
345
+ font-weight: bold;
346
+ }
347
+ .modal-body {
348
+ max-height: 60vh;
349
+ overflow-y: auto;
350
+ padding: 15px;
351
+ }
352
+ .modal-body #modal-img {
353
+ max-height: 200px;
354
+ width: 100%;
355
+ object-fit: cover;
356
+ border-radius: 8px;
357
+ margin-bottom: 10px;
358
+ }
359
+ .modal-body #modal-name {
360
+ font-size: 20px;
361
+ font-weight: bold;
362
+ text-align: center;
363
+ margin-bottom: 5px;
364
+ color: #333333;
365
+ }
366
+ .modal-body #modal-price {
367
+ font-size: 16px;
368
+ font-weight: 500;
369
+ color: #000000;
370
+ text-align: center;
371
+ margin-bottom: 10px;
372
+ }
373
+ .modal-body #modal-description {
374
+ font-size: 14px;
375
+ color: #6c757d;
376
+ margin-bottom: 10px;
377
+ }
378
+ .modal-body .nutritional-info {
379
+ font-size: 12px;
380
+ color: #6c757d;
381
+ margin-bottom: 10px;
382
+ }
383
+ .modal-body #modal-addons h6,
384
+ .modal-body #first-row h6 {
385
+ font-size: 14px;
386
+ font-weight: bold;
387
+ margin-bottom: 10px;
388
+ }
389
+ .modal-footer {
390
+ display: flex;
391
+ align-items: center;
392
+ justify-content: space-between;
393
+ padding: 10px;
394
+ }
395
+ .modal-footer .d-flex {
396
+ display: flex;
397
+ align-items: center;
398
+ gap: 10px;
399
+ }
400
+ .modal-footer .btn {
401
+ height: 40px;
402
+ padding: 0 15px;
403
+ }
404
+ .modal-footer .form-control {
405
+ width: 50px;
406
+ height: 40px;
407
+ text-align: center;
408
+ }
409
+ .modal-footer .btn-primary {
410
+ background-color: #0FAA39;
411
+ border-color: #0FAA39;
412
+ font-weight: bold;
413
+ padding: 10px 20px;
414
+ height: 40px;
415
+ display: flex;
416
+ justify-content: center;
417
+ align-items: center;
418
+ width: auto;
419
+ }
420
+ .modal-footer .btn-outline-secondary {
421
+ height: 40px;
422
+ width: 40px;
423
+ }
424
+ .item-details {
425
+ display: none;
426
+ padding: 15px;
427
+ background-color: #f8f9fa;
428
+ border-radius: 8px;
429
+ margin-top: 10px;
430
+ margin: 10px 15px;
431
+ box-shadow: 0 2px 5px rgba(0,0,0,0.1);
432
+ }
433
+ .item-details.show {
434
+ display: block;
435
+ }
436
+ .item-details h6 {
437
+ color: #0FAA39;
438
+ margin-bottom: 10px;
439
+ font-size: 1.1rem;
440
+ font-weight: bold;
441
+ }
442
+ .item-details p {
443
+ margin-bottom: 15px;
444
+ color: #555;
445
+ line-height: 1.5;
446
+ font-size: 0.9rem;
447
+ }
448
+ .toggle-details {
449
+ cursor: pointer;
450
+ color: #0FAA39;
451
+ font-size: 0.9rem;
452
+ text-align: left;
453
+ padding: 5px 0;
454
+ transition: color 0.3s ease;
455
+ display: block;
456
+ width: 100%;
457
+ margin-top: 5px;
458
+ }
459
+ .toggle-details:hover {
460
+ color: #0D9232;
461
+ text-decoration: underline;
462
+ }
463
+ .category-buttons {
464
+ display: flex;
465
+ flex-wrap: wrap;
466
+ gap: 10px;
467
+ justify-content: center;
468
+ margin-top: 10px;
469
+ }
470
+ .category-button {
471
+ background-color: #fff;
472
+ border: 2px solid #0FAA39;
473
+ color: #0FAA39;
474
+ padding: 5px 15px;
475
+ border-radius: 20px;
476
+ font-size: 0.9rem;
477
+ cursor: pointer;
478
+ transition: background-color 0.3s, color 0.3s;
479
+ }
480
+ .category-button.selected {
481
+ background-color: #0FAA39;
482
+ color: #fff;
483
+ border-color: #0FAA39;
484
+ }
485
+ .category-button:hover {
486
+ background-color: #e6f4ea;
487
+ }
488
+ .quantity-selector {
489
+ display: flex;
490
+ align-items: center;
491
+ gap: 5px;
492
+ }
493
+ .quantity-selector .btn {
494
+ width: 25px;
495
+ height: 25px;
496
+ padding: 0;
497
+ font-size: 12px;
498
+ line-height: 25px;
499
+ text-align: center;
500
+ }
501
+ .quantity-selector .quantity-display {
502
+ width: 25px;
503
+ text-align: center;
504
+ font-size: 12px;
505
+ font-weight: bold;
506
+ line-height: 25px;
507
+ }
508
+ .quantity-selector .quantity-to-add,
509
+ .quantity-selector .quantity-to-remove {
510
+ width: 45px;
511
+ height: 25px;
512
+ font-size: 12px;
513
+ padding: 0 5px;
514
+ }
515
+ .modal-dialog {
516
+ max-height: 90vh;
517
+ }
518
+ .modal-body::-webkit-scrollbar {
519
+ width: 8px;
520
+ }
521
+ .modal-body::-webkit-scrollbar-track {
522
+ background: #f1f1f1;
523
+ border-radius: 10px;
524
+ }
525
+ .modal-body::-webkit-scrollbar-thumb {
526
+ background: #0FAA39;
527
+ border-radius: 10px;
528
+ }
529
+ .modal-body::-webkit-scrollbar-thumb:hover {
530
+ background: #0D9232;
531
+ }
532
+ .btn-primary:disabled {
533
+ opacity: 0.65;
534
+ cursor: not-allowed;
535
+ }
536
+ .quantity-selector select {
537
+ width: 60px;
538
+ height: 35px;
539
+ padding: 5px;
540
+ border-radius: 5px;
541
+ border: 1px solid #ced4da;
542
+ }
543
+ #custom-dish-form {
544
+ position: relative;
545
+ padding-bottom: 80px;
546
+ }
547
+ #custom-dish-form .btn-primary {
548
+ position: absolute;
549
+ right: 15px;
550
+ bottom: 15px;
551
+ width: auto;
552
+ padding: 10px 20px;
553
+ }
554
+ .bottom-action-bar {
555
+ position: fixed;
556
+ bottom: 0;
557
+ left: 0;
558
+ right: 0;
559
+ background-color: white;
560
+ padding: 10px 20px;
561
+ display: flex;
562
+ justify-content: space-between;
563
+ align-items: center;
564
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
565
+ z-index: 1000;
566
+ max-width: 900px;
567
+ margin: 0 auto;
568
+ }
569
+ .bottom-action-bar .btn {
570
+ flex: 1;
571
+ margin: 0 5px;
572
+ padding: 10px 15px;
573
+ border-radius: 8px;
574
+ font-weight: bold;
575
+ font-size: 16px;
576
+ color: white;
577
+ display: flex;
578
+ align-items: center;
579
+ justify-content: center;
580
+ text-align: center;
581
+ min-width: 0;
582
+ white-space: nowrap;
583
+ }
584
+ .bottom-action-bar .btn-order-history {
585
+ background-color: #FFA07A;
586
+ border-color: #FFA07A;
587
+ }
588
+ .bottom-action-bar .btn-order-history:hover {
589
+ background-color: #FF8C61;
590
+ border-color: #FF8C61;
591
+ }
592
+ .bottom-action-bar .btn-view-cart {
593
+ background-color: #0FAA39;
594
+ border-color: #0FAA39;
595
+ }
596
+ .bottom-action-bar .btn-view-cart:hover {
597
+ background-color: #0D9232;
598
+ border-color: #0D9232;
599
+ }
600
+ .cart-icon-badge {
601
+ background-color: white;
602
+ color: #0FAA39;
603
+ border-radius: 50%;
604
+ width: 20px;
605
+ height: 20px;
606
+ display: inline-flex;
607
+ align-items: center;
608
+ justify-content: center;
609
+ font-size: 12px;
610
+ margin-left: 8px;
611
+ }
612
+ .mic-popup {
613
+ position: fixed;
614
+ top: 50%;
615
+ left: 50%;
616
+ transform: translate(-50%, -50%);
617
+ background-color: rgba(0, 0, 0, 0.8);
618
+ color: white;
619
+ padding: 30px;
620
+ border-radius: 15px;
621
+ text-align: center;
622
+ z-index: 2000;
623
+ display: none;
624
+ width: 300px;
625
+ max-width: 90%;
626
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
627
+ }
628
+ .mic-popup.active {
629
+ display: block;
630
+ }
631
+ .mic-popup-icon {
632
+ font-size: 48px;
633
+ margin-bottom: 20px;
634
+ color: #ff4444;
635
+ animation: pulse 1.5s infinite;
636
+ }
637
+ .mic-popup-text {
638
+ font-size: 18px;
639
+ margin-bottom: 15px;
640
+ min-height: 24px;
641
+ }
642
+ .mic-popup-cancel {
643
+ background-color: #ff4444;
644
+ color: white;
645
+ border: none;
646
+ padding: 8px 20px;
647
+ border-radius: 20px;
648
+ cursor: pointer;
649
+ font-weight: bold;
650
+ }
651
+ @keyframes pulse {
652
+ 0% { transform: scale(1); }
653
+ 50% { transform: scale(1.1); }
654
+ 100% { transform: scale(1); }
655
+ }
656
+ @media (max-width: 576px) {
657
+ .fixed-top-bar {
658
+ height: 60px;
659
+ padding: 10px;
660
+ }
661
+ .search-bar-container {
662
+ width: 80%;
663
+ max-width: 100%;
664
+ left: 10px;
665
+ top: 50%;
666
+ transform: translateY(-50%);
667
+ }
668
+ .search-bar-container input {
669
+ padding: 6px 35px 6px 35px;
670
+ font-size: 14px;
671
+ border-radius: 20px;
672
+ }
673
+ .search-icon {
674
+ left: 12px;
675
+ font-size: 16px;
676
+ }
677
+ .mic-icon {
678
+ right: 12px;
679
+ font-size: 16px;
680
+ }
681
+ .avatar-dropdown-container {
682
+ right: 10px;
683
+ }
684
+ .avatar-icon {
685
+ width: 40px;
686
+ height: 40px;
687
+ font-size: 20px;
688
+ }
689
+ .dropdown-menu {
690
+ width: 220px;
691
+ }
692
+ .dropdown-menu .dropdown-item {
693
+ padding: 12px 16px;
694
+ font-size: 15px;
695
+ }
696
+ .category-buttons {
697
+ gap: 8px;
698
+ }
699
+ .category-button {
700
+ padding: 4px 12px;
701
+ font-size: 0.85rem;
702
+ }
703
+ .modal-dialog {
704
+ max-width: 96%;
705
+ margin: 5px auto;
706
+ }
707
+ .modal-header {
708
+ padding: 5px 10px;
709
+ }
710
+ .modal-title {
711
+ font-size: 14px;
712
+ }
713
+ .modal-body {
714
+ max-height: 50vh;
715
+ padding: 8px;
716
+ }
717
+ .modal-body #modal-img {
718
+ max-height: 150px;
719
+ width: 100%;
720
+ max-width: 150px;
721
+ margin: 0 auto 5px;
722
+ display: block;
723
+ }
724
+ .modal-body #modal-name {
725
+ font-size: 18px;
726
+ margin-bottom: 3px;
727
+ }
728
+ .modal-body #modal-price {
729
+ font-size: 14px;
730
+ margin-bottom: 5px;
731
+ }
732
+ .modal-body #modal-description {
733
+ font-size: 12px;
734
+ margin-bottom: 5px;
735
+ }
736
+ .modal-body .nutritional-info {
737
+ font-size: 10px;
738
+ margin-bottom: 5px;
739
+ }
740
+ .modal-body #modal-addons h6,
741
+ .modal-body #first-row h6 {
742
+ font-size: 12px;
743
+ margin-bottom: 5px;
744
+ }
745
+ .modal-footer {
746
+ padding: 5px;
747
+ }
748
+ .modal-footer .btn {
749
+ height: 30px;
750
+ padding: 0 10px;
751
+ }
752
+ .modal-footer .form-control {
753
+ width: 30px;
754
+ height: 30px;
755
+ font-size: 12px;
756
+ font-weight: bold;
757
+ }
758
+ .modal-footer .btn-outline-secondary {
759
+ width: 25px;
760
+ height: 25px;
761
+ font-size: 12px;
762
+ line-height: 25px;
763
+ }
764
+ .modal-footer .btn-primary {
765
+ font-size: 12px;
766
+ height: 30px;
767
+ padding: 0 15px;
768
+ border-radius: 5px;
769
+ }
770
+ .btn-primary {
771
+ font-size: 10px;
772
+ width: 50px;
773
+ height: 25px;
774
+ }
775
+ .customisable-text {
776
+ font-size: 8px;
777
+ }
778
+ .button-container {
779
+ gap: 3px;
780
+ }
781
+ .quantity-selector .btn {
782
+ width: 18px;
783
+ height: 18px;
784
+ font-size: 9px;
785
+ line-height: 18px;
786
+ }
787
+ .quantity-selector .quantity-display {
788
+ width: 18px;
789
+ font-size: 9px;
790
+ line-height: 18px;
791
+ }
792
+ .quantity-selector .quantity-to-add,
793
+ .quantity-selector .quantity-to-remove {
794
+ width: 35px;
795
+ height: 18px;
796
+ font-size: 9px;
797
+ }
798
+ .quantity-selector select {
799
+ width: 50px;
800
+ height: 30px;
801
+ font-size: 12px;
802
+ }
803
+ .bottom-action-bar {
804
+ padding: 8px 10px;
805
+ }
806
+ .bottom-action-bar .btn {
807
+ padding: 8px 10px;
808
+ font-size: 14px;
809
+ }
810
+ .cart-icon-badge {
811
+ width: 18px;
812
+ height: 18px;
813
+ font-size: 10px;
814
+ margin-left: 5px;
815
+ }
816
+ .item-details {
817
+ padding: 10px;
818
+ margin: 5px 10px;
819
+ }
820
+ .item-details h6 {
821
+ font-size: 0.95rem;
822
+ }
823
+ .item-details p {
824
+ font-size: 0.8rem;
825
+ }
826
+ .toggle-details {
827
+ font-size: 0.8rem;
828
+ }
829
+ .mic-popup {
830
+ padding: 20px;
831
+ width: 280px;
832
+ }
833
+ .mic-popup-icon {
834
+ font-size: 36px;
835
+ margin-bottom: 15px;
836
+ }
837
+ .mic-popup-text {
838
+ font-size: 16px;
839
+ }
840
+ .mic-popup-cancel {
841
+ padding: 6px 16px;
842
+ font-size: 14px;
843
+ }
844
+ }
845
+ </style>
846
+ </head>
847
+ <body>
848
+
849
+ <div class="fixed-top-bar">
850
+ <div class="avatar-dropdown-container">
851
+ <div class="avatar-icon" id="avatarIcon">
852
+ {% if user_image %}
853
+ <img src="{{ user_image }}" alt="User Avatar" class="avatar-image"
854
+ style="width: 100%; height: 100%; object-fit: cover; border-radius: 50%;">
855
+ {% else %}
856
+ <span>{{ first_letter }}</span>
857
+ {% endif %}
858
+ </div>
859
+ <div class="dropdown-menu" id="avatarDropdown">
860
+ <a href="{{ url_for('user_details.customer_details') }}" class="dropdown-item">View Profile</a>
861
+ <a href="{{ url_for('orderhistory.order_history') }}" class="dropdown-item">Order History</a>
862
+ <div class="dropdown-item upload-item">
863
+ <label for="avatarUpload" style="cursor: pointer; margin: 0; width: 100%;">
864
+ Upload Image
865
+ </label>
866
+ <input type="file" id="avatarUpload" accept="image/*" style="display: none;">
867
+ </div>
868
+ {% if user_image %}
869
+ <div class="dropdown-item delete-item" id="deleteAvatar">Delete Image</div>
870
+ {% endif %}
871
+ <a href="{{ url_for('logout') }}" class="dropdown-item">Logout</a>
872
+ </div>
873
+ </div>
874
+ <div class="search-bar-container">
875
+ <input type="text" id="searchBar" class="form-control" placeholder="Search items or sections..." autocomplete="off">
876
+ <i class="bi bi-search search-icon"></i>
877
+ <i class="bi bi-mic mic-icon" id="micIcon"></i>
878
+ <span class="mic-unsupported" id="micUnsupported">Mic not supported</span>
879
+ <div id="autocompleteSuggestions" class="autocomplete-suggestions"></div>
880
+ </div>
881
+ </div>
882
+
883
+ <form method="get" action="/menu" class="text-center mb-4" id="categoryForm">
884
+ <label class="form-label fw-bold">Select a Category:</label>
885
+ <div class="category-buttons">
886
+ {% for category in categories %}
887
+ <button type="button" class="category-button {% if selected_category == category %}selected{% endif %}" data-category="{{ category }}">{{ category }}</button>
888
+ {% endfor %}
889
+ <button type="button" class="category-button {% if selected_category == 'Customized Dish' %}selected{% endif %}" data-category="Customized Dish">Customized Dish</button>
890
+ </div>
891
+ <input type="hidden" name="category" id="selectedCategoryInput" value="{{ selected_category }}">
892
+ </form>
893
+
894
+ <div class="container mt-4">
895
+ {% if selected_category == "Customized Dish" %}
896
+ <div id="custom-dish-form" class="mt-4">
897
+ <h3>Create Your Custom Dish</h3>
898
+ <form method="POST" action="/customdish/generate_custom_dish" id="customDishForm">
899
+ <div class="mb-3">
900
+ <label for="custom-dish-name" class="form-label">Dish Name</label>
901
+ <input type="text" class="form-control" id="custom-dish-name" name="name" required>
902
+ </div>
903
+ <div class="mb-3 position-relative">
904
+ <label for="custom-dish-description" class="form-label">Dish Description</label>
905
+ <textarea class="form-control" id="custom-dish-description" name="description" required></textarea>
906
+ <div id="descriptionSuggestions" class="autocomplete-suggestions"></div>
907
+ </div>
908
+ <button type="submit" class="btn btn-primary">Submit Custom Dish</button>
909
+ </form>
910
+ </div>
911
+ {% else %}
912
+ {% if ordered_menu.items()|length == 0 %}
913
+ <p>No menu items available for this category.</p>
914
+ {% else %}
915
+ {% for section, items in ordered_menu.items() %}
916
+ <h3>{{ section }}</h3>
917
+ <div class="row">
918
+ {% for item in items %}
919
+ <div class="col-md-6 mb-4">
920
+ <div class="card menu-card">
921
+ <video
922
+ class="card-img-top menu-video"
923
+ muted
924
+ loop
925
+ preload="auto"
926
+ data-src="{{ item.Video1__c | default('/static/placeholder.mp4') }}"
927
+ poster="{{ item.Image1__c | default('/static/placeholder.jpg') }}"
928
+ width="350"
929
+ height="200"
930
+ onmouseover="this.play()"
931
+ onmouseout="this.pause(); this.currentTime = 0;"
932
+ onerror="this.poster='/static/placeholder.jpg';">
933
+ <source src="{{ item.Video1__c | default('/static/placeholder.mp4') }}" type="video/mp4">
934
+ Your browser does not support the video tag.
935
+ </video>
936
+ <div class="addbutton">
937
+ <div class="card-body d-flex align-items-center justify-content-between">
938
+ <div>
939
+ <h5 class="card-title">{{ item.Name | default('Unnamed Item') }}</h5>
940
+ <p class="card-text price">${{ item.Price__c | default('0.00') }}</p>
941
+ {% if item.Section__c != 'Soft Drinks' %}
942
+ <div class="toggle-details" data-item-name="{{ item.Name | default('Unnamed Item') }}">Show Details</div>
943
+ {% endif %}
944
+ </div>
945
+ <div class="d-flex flex-column align-item-center justify-content-center">
946
+ <div class="button-container"
947
+ data-item-name="{{ item.Name | default('Unnamed Item') }}"
948
+ data-item-price="{{ item.Price__c | default('0.00') }}"
949
+ data-item-image="{{ item.Image2__c | default(item.Image1__c) | default('/static/placeholder.jpg') }}"
950
+ data-item-section="{{ item.Section__c | default(section) }}"
951
+ data-item-category="{{ selected_category }}">
952
+ {% if item.Section__c == 'Soft Drinks' %}
953
+ <button class="btn btn-primary add-to-cart-btn" onclick="showSoftDrinkModal(this)">ADD</button>
954
+ {% else %}
955
+ <button class="btn btn-primary"
956
+ data-bs-toggle="modal"
957
+ data-bs-target="#itemModal"
958
+ 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 }}')">
959
+ ADD
960
+ </button>
961
+ {% endif %}
962
+ {% if item.Section__c != 'Apetizer' and item.Section__c != 'Customized dish' and item.Section__c !='Soft Drinks' %}
963
+ <span class="customisable-text">Customisable</span>
964
+ {% endif %}
965
+ </div>
966
+ </div>
967
+ </div>
968
+ </div>
969
+ {% if item.Section__c != 'Soft Drinks' %}
970
+ <div class="item-details" id="details-{{ item.Name | default('unnamed-item') | replace(' ', '-') }}">
971
+ <h6>Description</h6>
972
+ <p>{{ item.Description__c | default('No description available') }}</p>
973
+ <h6>Ingredients Info</h6>
974
+ <p>{{ item.IngredientsInfo__c | default('Not specified') }}</p>
975
+ <h6>Nutritional Info</h6>
976
+ <p>{{ item.NutritionalInfo__c | default('Not available') }}</p>
977
+ <h6>Allergens</h6>
978
+ <p>{{ item.Allergens__c | default('None listed') }}</p>
979
+ </div>
980
+ {% endif %}
981
+ </div>
982
+ </div>
983
+ {% endfor %}
984
+ </div>
985
+ {% endfor %}
986
+ {% endif %}
987
+ {% endif %}
988
+ </div>
989
+
990
+ <div class="bottom-action-bar">
991
+ <a href="{{ url_for('orderhistory.order_history') }}" class="btn btn-order-history">
992
+ <i class="bi bi-clock-history"></i> Order History
993
+ </a>
994
+ <a href="{{ url_for('cart.cart') }}" class="btn btn-view-cart">
995
+ <i class="bi bi-cart"></i> View Cart
996
+ <span class="cart-icon-badge" id="cart-item-count">{{ cart_item_count }}</span>
997
+ </a>
998
+ </div>
999
+
1000
+ <!-- Modal for Item Details -->
1001
+ <div class="modal fade" id="itemModal" tabindex="-1" aria-labelledby="itemModalLabel" aria-hidden="true">
1002
+ <div class="modal-dialog modal-dialog-centered">
1003
+ <div class="modal-content">
1004
+ <div class="modal-header">
1005
+ <h5 class="modal-title" id="itemModalLabel">Item Details</h5>
1006
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
1007
+ </div>
1008
+ <div class="modal-body">
1009
+ <img id="modal-img" class="img-fluid rounded mb-3 d-block mx-auto" alt="Item Image" style="max-height: 200px; object-fit: cover;">
1010
+ <h5 id="modal-name" class="fw-bold text-center"></h5>
1011
+ <p id="modal-price" class="text-muted text-center"></p>
1012
+ <p id="modal-description" class="text-secondary"></p>
1013
+ <div class="nutritional-info">
1014
+ <strong>Ingredients:</strong> <span id="modal-ingredients"></span><br>
1015
+ <strong>Nutrition:</strong> <span id="modal-nutrition"></span><br>
1016
+ <strong>Allergens:</strong> <span id="modal-allergens"></span>
1017
+ </div>
1018
+ <div id="modal-addons" class="modal-addons mt-4">
1019
+ <h6>Customization Options</h6>
1020
+ <div id="addons-list" class="addons-container">Loading customization options...</div>
1021
+ </div>
1022
+ <div class="mt-4">
1023
+ <h6>Custom Request</h6>
1024
+ <textarea id="modal-instructions" class="form-control" placeholder="Enter any special instructions here..."></textarea>
1025
+ </div>
1026
+ <span id="modal-section" data-section="" data-category="" style="display: none;"></span>
1027
+ </div>
1028
+ <div class="modal-footer d-flex align-items-center justify-content-between">
1029
+ <div class="d-flex align-items-center gap-2">
1030
+ <button type="button" class="btn btn-outline-secondary" id="decreaseQuantity">-</button>
1031
+ <input type="text" class="form-control text-center" id="quantityInput" value="1" readonly style="width: 50px;"/>
1032
+ <button type="button" class="btn btn-outline-secondary" id="increaseQuantity">+</button>
1033
+ </div>
1034
+ <button type="button" class="btn btn-primary" onclick="addToCartFromModal()">Add to Cart</button>
1035
+ </div>
1036
+ </div>
1037
+ </div>
1038
+ </div>
1039
+
1040
+ <!-- Modal for Soft Drinks Quantity Selection -->
1041
+ <div class="modal fade" id="softDrinkModal" tabindex="-1" aria-labelledby="softDrinkModalLabel" aria-hidden="true">
1042
+ <div class="modal-dialog modal-dialog-centered">
1043
+ <div class="modal-content" style="border-radius: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.2);">
1044
+ <div class="modal-header" style="background: linear-gradient(45deg, #0FAA39, #0D9232); color: white; border-radius: 15px 15px 0 0;">
1045
+ <h5 class="modal-title" id="softDrinkModalLabel">Select Your Drink</h5>
1046
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
1047
+ </div>
1048
+ <div class="modal-body" style="padding: 20px;">
1049
+ <div class="text-center mb-4">
1050
+ <img id="soft-drink-image" class="img-fluid rounded mb-3" alt="Soft Drink Image" style="max-height: 150px; width: auto; object-fit: contain;">
1051
+ <h5 id="soft-drink-name" class="fw-bold" style="color: #333;"></h5>
1052
+ <p id="soft-drink-price" class="text-muted" style="font-size: 1.1rem;"></p>
1053
+ </div>
1054
+ <div class="d-flex justify-content-center align-items-center mb-4">
1055
+ <div class="quantity-selector" style="background-color: #f8f9fa; padding: 10px; border-radius: 10px;">
1056
+ <button type="button" class="btn btn-outline-secondary" id="soft-drink-decrease" style="width: 40px; height: 40px;">-</button>
1057
+ <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;">
1058
+ <button type="button" class="btn btn-outline-secondary" id="soft-drink-increase" style="width: 40px; height: 40px;">+</button>
1059
+ </div>
1060
+ </div>
1061
+ </div>
1062
+ <div class="modal-footer" style="border-top: none; padding: 0 20px 20px 20px; justify-content: center;">
1063
+ <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>
1064
+ </div>
1065
+ </div>
1066
+ </div>
1067
+ </div>
1068
+
1069
+ <!-- Mic Popup -->
1070
+ <div class="mic-popup" id="micPopup">
1071
+ <div class="mic-popup-icon">
1072
+ <i class="bi bi-mic-fill"></i>
1073
+ </div>
1074
+ <div class="mic-popup-text" id="micPopupText">Listening...</div>
1075
+ <button class="mic-popup-cancel" id="micPopupCancel">Cancel</button>
1076
+ </div>
1077
+
1078
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
1079
+ <script>
1080
+ let isProcessingRequest = false;
1081
+ let currentSoftDrinkButton = null;
1082
+
1083
+ const menuItems = [
1084
+ {% for section, items in ordered_menu.items() %}
1085
+ {% for item in items %}
1086
+ "{{ item.Name | default('Unnamed Item') | e }}",
1087
+ {% endfor %}
1088
+ {% endfor %}
1089
+ ];
1090
+
1091
+ const ingredientsList = [
1092
+ "Basmati Rice", "Bell Pepper", "Biryani Masala", "Butter", "Capsicum", "Cauliflower",
1093
+ "Chickpea Flour (Besan)", "Chickpea Flour (for batter)", "Chickpeas (Channa)", "Chili Powder",
1094
+ "Chili Sauce", "Coconut Milk", "Coriander Powder", "Cornflour", "Cream", "Cumin Powder",
1095
+ "Cumin Seeds", "Curd (Yogurt)", "Curry Leaves", "Fish (e.g., King Fish or Salmon)",
1096
+ "Fresh Coriander Leaves", "Garam Masala", "Garlic", "Ghee (Clarified Butter)", "Ginger",
1097
+ "Ginger-Garlic Paste", "Goat Meat (Mutton)", "Green Chilies", "Honey",
1098
+ "Kasuri Methi (dried fenugreek leaves)", "Lemon Juice", "Mango Puree", "Mint Leaves",
1099
+ "Mixed Vegetables (Carrot, Peas, Potato, Cauliflower)", "Mixed Vegetables (Carrot, Peas, Potato)",
1100
+ "Mustard Seeds", "Mutton (Goat Meat)", "Oil", "Oil (for frying)", "Onion",
1101
+ "Paneer (Indian Cottage Cheese)", "Peas", "Potatoes", "Prawns", "Red Chili Powder",
1102
+ "Rice Flour", "Saffron", "Salt", "Soy Sauce", "Spring Onion", "Tamarind (for sourness)",
1103
+ "Tomato Ketchup", "Tomatoes", "Turmeric Powder", "Vinegar", "Water", "Wheat Flour (for dough)",
1104
+ "Whole Wheat Flour", "Yogurt (Curd)"
1105
+ ];
1106
+
1107
+ // Utility function to sanitize input to prevent XSS
1108
+ function sanitizeInput(input) {
1109
+ const div = document.createElement('div');
1110
+ div.textContent = input;
1111
+ return div.innerHTML;
1112
+ }
1113
+
1114
+ function addToCartLocalStorage(payload) {
1115
+ let cart = JSON.parse(localStorage.getItem('cart')) || [];
1116
+ const existingItem = cart.find(item =>
1117
+ item.itemName === payload.itemName &&
1118
+ item.instructions === payload.instructions &&
1119
+ JSON.stringify(item.addons) === JSON.stringify(payload.addons)
1120
+ );
1121
+ if (existingItem) {
1122
+ existingItem.quantity = payload.quantity;
1123
+ } else {
1124
+ cart.push(payload);
1125
+ }
1126
+ localStorage.setItem('cart', JSON.stringify(cart));
1127
+ return cart;
1128
+ }
1129
+
1130
+ function removeFromCartLocalStorage(itemName, quantityToRemove, instructions, addons) {
1131
+ let cart = JSON.parse(localStorage.getItem('cart')) || [];
1132
+ const itemIndex = cart.findIndex(item =>
1133
+ item.itemName === itemName &&
1134
+ item.instructions === instructions &&
1135
+ JSON.stringify(item.addons) === JSON.stringify(addons)
1136
+ );
1137
+ if (itemIndex !== -1) {
1138
+ if (quantityToRemove >= cart[itemIndex].quantity) {
1139
+ cart.splice(itemIndex, 1);
1140
+ } else {
1141
+ cart[itemIndex].quantity -= quantityToRemove;
1142
+ }
1143
+ }
1144
+ localStorage.setItem('cart', JSON.stringify(cart));
1145
+ return cart;
1146
+ }
1147
+
1148
+ function getCartLocalStorage() {
1149
+ return JSON.parse(localStorage.getItem('cart')) || [];
1150
+ }
1151
+
1152
+ function debounce(func, wait) {
1153
+ let timeout;
1154
+ return function (...args) {
1155
+ clearTimeout(timeout);
1156
+ timeout = setTimeout(() => func.apply(this, args), wait);
1157
+ };
1158
+ }
1159
+
1160
+ function showSoftDrinkModal(button) {
1161
+ currentSoftDrinkButton = button;
1162
+ const buttonContainer = button.closest('.button-container');
1163
+ const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
1164
+ const itemPrice = buttonContainer.getAttribute('data-item-price');
1165
+ const itemImage = buttonContainer.getAttribute('data-item-image');
1166
+
1167
+ document.getElementById('soft-drink-name').textContent = itemName;
1168
+ document.getElementById('soft-drink-price').textContent = `$${itemPrice}`;
1169
+ document.getElementById('soft-drink-quantity').value = '1';
1170
+ const softDrinkImage = document.getElementById('soft-drink-image');
1171
+ softDrinkImage.src = itemImage || '/static/placeholder.jpg';
1172
+
1173
+ const modal = new bootstrap.Modal(document.getElementById('softDrinkModal'));
1174
+ modal.show();
1175
+ }
1176
+
1177
+ function addSoftDrinkToCart() {
1178
+ if (!currentSoftDrinkButton) return;
1179
+
1180
+ const buttonContainer = currentSoftDrinkButton.closest('.button-container');
1181
+ const quantity = parseInt(document.getElementById('soft-drink-quantity').value) || 1;
1182
+
1183
+ const itemName = sanitizeInput(buttonContainer.getAttribute('data-item-name'));
1184
+ const itemPrice = parseFloat(buttonContainer.getAttribute('data-item-price'));
1185
+ const itemImage = buttonContainer.getAttribute('data-item-image');
1186
+ const section = sanitizeInput(buttonContainer.getAttribute('data-item-section'));
1187
+ const selectedCategory = sanitizeInput(buttonContainer.getAttribute('data-item-category'));
1188
+
1189
+ const cartPayload = {
1190
+ itemName: itemName,
1191
+ itemPrice: itemPrice,
1192
+ itemImage: itemImage,
1193
+ section: section,
1194
+ category: selectedCategory,
1195
+ addons: [],
1196
+ instructions: '',
1197
+ quantity: quantity
1198
+ };
1199
+
1200
+ fetch('/cart/add', {
1201
+ method: 'POST',
1202
+ headers: {
1203
+ 'Content-Type': 'application/json',
1204
+ },
1205
+ body: JSON.stringify(cartPayload)
1206
+ })
1207
+ .then(response => response.json())
1208
+ .then(data => {
1209
+ if (data.success) {
1210
+ alert('Item added to cart successfully!');
1211
+ updateCartUI(data.cart);
1212
+ const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1213
+ modal.hide();
1214
+ } else {
1215
+ console.error('Failed to add item to cart:', data.error);
1216
+ alert(data.error || 'Failed to add item to cart. Using local storage as fallback.');
1217
+ const cart = addToCartLocalStorage(cartPayload);
1218
+ updateCartUI(cart);
1219
+ const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1220
+ modal.hide();
1221
+ }
1222
+ })
1223
+ .catch(err => {
1224
+ console.error('Error adding item to cart:', err);
1225
+ alert('Error adding item to cart. Using local storage as fallback.');
1226
+ const cart = addToCartLocalStorage(cartPayload);
1227
+ updateCartUI(cart);
1228
+ const modal = bootstrap.Modal.getInstance(document.getElementById('softDrinkModal'));
1229
+ modal.hide();
1230
+ });
1231
+ }
1232
+
1233
+ function updateCartUI(cart) {
1234
+ if (!Array.isArray(cart)) {
1235
+ console.error('Invalid cart data:', cart);
1236
+ return;
1237
+ }
1238
+
1239
+ let totalQuantity = 0;
1240
+ cart.forEach(item => {
1241
+ totalQuantity += item.quantity;
1242
+ });
1243
+
1244
+ const cartItemCount = document.getElementById('cart-item-count');
1245
+ if (cartItemCount) {
1246
+ cartItemCount.innerText = totalQuantity;
1247
+ cartItemCount.style.display = totalQuantity > 0 ? 'inline-flex' : 'none';
1248
+ }
1249
+ }
1250
+
1251
+ document.addEventListener('DOMContentLoaded', function () {
1252
+ const avatarContainer = document.querySelector('.avatar-dropdown-container');
1253
+ const dropdownMenu = document.querySelector('.dropdown-menu');
1254
+ const avatarIcon = document.getElementById('avatarIcon');
1255
+ const avatarUpload = document.getElementById('avatarUpload');
1256
+ const deleteAvatar = document.getElementById('deleteAvatar');
1257
+
1258
+ // Avatar click handler
1259
+ avatarIcon.addEventListener('click', function(event) {
1260
+ event.stopPropagation();
1261
+ dropdownMenu.style.display = dropdownMenu.style.display === 'block' ? 'none' : 'block';
1262
+ });
1263
+
1264
+ // Close dropdown when clicking outside
1265
+ document.addEventListener('click', function(event) {
1266
+ if (!avatarContainer.contains(event.target)) {
1267
+ dropdownMenu.style.display = 'none';
1268
+ }
1269
+ });
1270
+
1271
+ // Handle image upload
1272
+ avatarUpload.addEventListener('change', function(event) {
1273
+ const file = event.target.files[0];
1274
+ if (file) {
1275
+ const reader = new FileReader();
1276
+ reader.onload = function(e) {
1277
+ const base64Image = e.target.result;
1278
+
1279
+ fetch('/upload_avatar', {
1280
+ method: 'POST',
1281
+ headers: {
1282
+ 'Content-Type': 'application/json',
1283
+ },
1284
+ body: JSON.stringify({ image: base64Image })
1285
+ })
1286
+ .then(response => response.json())
1287
+ .then(data => {
1288
+ if (data.success) {
1289
+ // Update avatar image
1290
+ const avatarImg = avatarIcon.querySelector('.avatar-image');
1291
+ if (avatarImg) {
1292
+ avatarImg.src = data.image;
1293
+ } else {
1294
+ const img = document.createElement('img');
1295
+ img.src = data.image;
1296
+ img.alt = 'User Avatar';
1297
+ img.className = 'avatar-image';
1298
+ img.style.cssText = 'width: 100%; height: 100%; object-fit: cover; border-radius: 50%;';
1299
+ avatarIcon.innerHTML = '';
1300
+ avatarIcon.appendChild(img);
1301
+ }
1302
+ // Add delete option if not present
1303
+ if (!document.getElementById('deleteAvatar')) {
1304
+ const deleteItem = document.createElement('div');
1305
+ deleteItem.className = 'dropdown-item delete-item';
1306
+ deleteItem.id = 'deleteAvatar';
1307
+ deleteItem.innerText = 'Delete Image';
1308
+ dropdownMenu.insertBefore(deleteItem, dropdownMenu.querySelector('.dropdown-item:last-child'));
1309
+ addDeleteListener(deleteItem);
1310
+ }
1311
+ dropdownMenu.style.display = 'none';
1312
+ } else {
1313
+ alert('Failed to upload image: ' + (data.error || 'Unknown error'));
1314
+ }
1315
+ })
1316
+ .catch(error => {
1317
+ console.error('Upload error:', error);
1318
+ alert('Error uploading image');
1319
+ });
1320
+ };
1321
+ reader.readAsDataURL(file);
1322
+ }
1323
+ });
1324
+
1325
+ // Handle image deletion
1326
+ function addDeleteListener(deleteElement) {
1327
+ deleteElement.addEventListener('click', function() {
1328
+ fetch('/delete_avatar', {
1329
+ method: 'POST',
1330
+ headers: {
1331
+ 'Content-Type': 'application/json'
1332
+ }
1333
+ })
1334
+ .then(response => response.json())
1335
+ .then(data => {
1336
+ if (data.success) {
1337
+ // Replace image with first letter
1338
+ const firstLetter = "{{ first_letter }}";
1339
+ avatarIcon.innerHTML = `<span>${firstLetter}</span>`;
1340
+ // Remove delete option
1341
+ deleteElement.remove();
1342
+ dropdownMenu.style.display = 'none';
1343
+ } else {
1344
+ alert('Failed to delete image: ' + (data.error || 'Unknown error'));
1345
+ }
1346
+ })
1347
+ .catch(error => {
1348
+ console.error('Delete error:', error);
1349
+ alert('Error deleting image');
1350
+ });
1351
+ });
1352
+ }
1353
+
1354
+ // Add delete listener if delete option exists
1355
+ if (deleteAvatar) {
1356
+ addDeleteListener(deleteAvatar);
1357
+ }
1358
+
1359
+ const menuCards = document.querySelectorAll('.menu-card');
1360
+ const menuVideos = document.querySelectorAll('.menu-video');
1361
+ const cardObserver = new IntersectionObserver((entries, observer) => {
1362
+ entries.forEach(entry => {
1363
+ if (entry.isIntersecting) {
1364
+ entry.target.classList.add('visible');
1365
+ observer.unobserve(entry.target);
1366
+ }
1367
+ });
1368
+ }, {
1369
+ root: null,
1370
+ rootMargin: '0px',
1371
+ threshold: 0.1
1372
+ });
1373
+ const videoObserver = new IntersectionObserver((entries, observer) => {
1374
+ entries.forEach(entry => {
1375
+ if (entry.isIntersecting) {
1376
+ const video = entry.target;
1377
+ const src = video.getAttribute('data-src');
1378
+ if (src && !video.querySelector('source[src="' + src + '"]')) {
1379
+ const source = video.querySelector('source');
1380
+ source.src = src;
1381
+ video.load();
1382
+ }
1383
+ video.classList.add('loaded');
1384
+ observer.unobserve(video);
1385
+ }
1386
+ });
1387
+ }, {
1388
+ root: null,
1389
+ rootMargin: '200px',
1390
+ threshold: 0.01
1391
+ });
1392
+ menuCards.forEach(card => cardObserver.observe(card));
1393
+ menuVideos.forEach(video => videoObserver.observe(video));
1394
+
1395
+ const toggleLinks = document.querySelectorAll('.toggle-details');
1396
+ toggleLinks.forEach(link => {
1397
+ link.addEventListener('click', function () {
1398
+ const itemName = this.getAttribute('data-item-name').replace(/ /g, '-');
1399
+ const detailsDiv = document.getElementById(`details-${itemName}`);
1400
+ const isCurrentlyShown = detailsDiv.classList.contains('show');
1401
+
1402
+ document.querySelectorAll('.item-details.show').forEach(otherDetails => {
1403
+ if (otherDetails !== detailsDiv) {
1404
+ otherDetails.classList.remove('show');
1405
+ const otherLink = otherDetails.previousElementSibling.querySelector('.toggle-details');
1406
+ if (otherLink) {
1407
+ otherLink.innerText = 'Show Details';
1408
+ }
1409
+ }
1410
+ });
1411
+
1412
+ if (!isCurrentlyShown) {
1413
+ detailsDiv.classList.add('show');
1414
+ this.innerText = 'Hide Details';
1415
+ } else {
1416
+ detailsDiv.classList.remove('show');
1417
+ this.innerText = 'Show Details';
1418
+ }
1419
+ });
1420
+ });
1421
+
1422
+ const categoryButtons = document.querySelectorAll('.category-button');
1423
+ const categoryForm = document.getElementById('categoryForm');
1424
+ const selectedCategoryInput = document.getElementById('selectedCategoryInput');
1425
+ if (!selectedCategoryInput.value) {
1426
+ selectedCategoryInput.value = "All";
1427
+ document.querySelector('.category-button[data-category="All"]').classList.add('selected');
1428
+ }
1429
+ categoryButtons.forEach(button => {
1430
+ button.addEventListener('click', function () {
1431
+ categoryButtons.forEach(btn => btn.classList.remove('selected'));
1432
+ this.classList.add('selected');
1433
+ selectedCategoryInput.value = this.getAttribute('data-category');
1434
+ categoryForm.submit();
1435
+ });
1436
+ });
1437
+
1438
+ const searchBar = document.getElementById('searchBar');
1439
+ const suggestionsContainer = document.getElementById('autocompleteSuggestions');
1440
+ const debouncedFilterMenu = debounce(filterMenu, 300);
1441
+ searchBar.addEventListener('input', function () {
1442
+ const input = sanitizeInput(this.value.trim().toLowerCase());
1443
+ suggestionsContainer.innerHTML = '';
1444
+ suggestionsContainer.style.display = 'none';
1445
+ if (input) {
1446
+ const filteredItems = menuItems.filter(item =>
1447
+ item.toLowerCase().includes(input)
1448
+ );
1449
+ if (filteredItems.length > 0) {
1450
+ filteredItems.forEach(item => {
1451
+ const suggestionDiv = document.createElement('div');
1452
+ suggestionDiv.classList.add('suggestion-item');
1453
+ suggestionDiv.innerText = item;
1454
+ suggestionDiv.addEventListener('click', function () {
1455
+ searchBar.value = item;
1456
+ suggestionsContainer.style.display = 'none';
1457
+ debouncedFilterMenu();
1458
+ });
1459
+ suggestionsContainer.appendChild(suggestionDiv);
1460
+ });
1461
+ suggestionsContainer.style.display = 'block';
1462
+ }
1463
+ }
1464
+ debouncedFilterMenu();
1465
+ });
1466
+ document.addEventListener('click', function (event) {
1467
+ if (!searchBar.contains(event.target) && !suggestionsContainer.contains(event.target)) {
1468
+ suggestionsContainer.style.display = 'none';
1469
+ }
1470
+ });
1471
+
1472
+ const descriptionTextarea = document.getElementById('custom-dish-description');
1473
+ const descriptionSuggestions = document.getElementById('descriptionSuggestions');
1474
+ if (descriptionTextarea && descriptionSuggestions) {
1475
+ let usedIngredients = new Set();
1476
+ function updateUsedIngredients() {
1477
+ const inputText = descriptionTextarea.value.trim();
1478
+ usedIngredients.clear();
1479
+ if (inputText) {
1480
+ const words = inputText.split(/,\s*/).map(word => word.trim());
1481
+ words.forEach(word => {
1482
+ if (word && ingredientsList.includes(word)) {
1483
+ usedIngredients.add(word);
1484
+ }
1485
+ });
1486
+ }
1487
+ }
1488
+ descriptionTextarea.addEventListener('input', function () {
1489
+ const inputText = this.value.trim();
1490
+ const words = inputText.split(/,\s*/);
1491
+ const lastWord = words[words.length - 1].trim().toLowerCase();
1492
+ descriptionSuggestions.innerHTML = '';
1493
+ descriptionSuggestions.style.display = 'none';
1494
+ updateUsedIngredients();
1495
+ if (lastWord) {
1496
+ const filteredIngredients = ingredientsList.filter(ingredient =>
1497
+ ingredient.toLowerCase().includes(lastWord) && !usedIngredients.has(ingredient)
1498
+ );
1499
+ if (filteredIngredients.length > 0) {
1500
+ filteredIngredients.forEach(ingredient => {
1501
+ const suggestionDiv = document.createElement('div');
1502
+ suggestionDiv.classList.add('suggestion-item');
1503
+ suggestionDiv.innerText = ingredient;
1504
+ suggestionDiv.addEventListener('click', function () {
1505
+ const currentValue = descriptionTextarea.value;
1506
+ const lastCommaIndex = currentValue.lastIndexOf(',');
1507
+ const baseText = lastCommaIndex !== -1 ? currentValue.substring(0, lastCommaIndex + 1) : '';
1508
+ descriptionTextarea.value = baseText + (baseText ? ' ' : '') + ingredient + ', ';
1509
+ descriptionSuggestions.style.display = 'none';
1510
+ descriptionTextarea.focus();
1511
+ updateUsedIngredients();
1512
+ });
1513
+ descriptionSuggestions.appendChild(suggestionDiv);
1514
+ });
1515
+ descriptionSuggestions.style.display = 'block';
1516
+ }
1517
+ }
1518
+ });
1519
+ document.addEventListener('click', function (event) {
1520
+ if (!descriptionTextarea.contains(event.target) && !descriptionSuggestions.contains(event.target)) {
1521
+ descriptionSuggestions.style.display = 'none';
1522
+ }
1523
+ });
1524
+ }
1525
+
1526
+ // Custom Dish Form Validation
1527
+ const customDishForm = document.getElementById('customDishForm');
1528
+ if (customDishForm) {
1529
+ customDishForm.addEventListener('submit', function(event) {
1530
+ const dishName = document.getElementById('custom-dish-name').value.trim();
1531
+ const description = document.getElementById('custom-dish-description').value.trim();
1532
+ if (!dishName || !description) {
1533
+ event.preventDefault();
1534
+ alert('Please fill in both the dish name and description.');
1535
+ return;
1536
+ }
1537
+ // Optional: Add AJAX submission to provide feedback
1538
+ event.preventDefault();
1539
+ fetch('/customdish/generate_custom_dish', {
1540
+ method: 'POST',
1541
+ headers: {
1542
+ 'Content-Type': 'application/x-www-form-urlencoded',
1543
+ },
1544
+ body: new URLSearchParams({
1545
+ 'name': dishName,
1546
+ 'description': description
1547
+ })
1548
+ })
1549
+ .then(response => response.json())
1550
+ .then(data => {
1551
+ if (data.success) {
1552
+ alert('Custom dish submitted successfully!');
1553
+ window.location.reload();
1554
+ } else {
1555
+ alert('Failed to submit custom dish: ' + (data.error || 'Unknown error'));
1556
+ }
1557
+ })
1558
+ .catch(error => {
1559
+ console.error('Error submitting custom dish:', error);
1560
+ alert('Error submitting custom dish. Please try again.');
1561
+ });
1562
+ });
1563
+ }
1564
+
1565
+ fetch('/cart/get')
1566
+ .then(response => {
1567
+ if (!response.ok) {
1568
+ throw new Error(`HTTP error! Status: ${response.status}`);
1569
+ }
1570
+ return response.json();
1571
+ })
1572
+ .then(data => {
1573
+ if (data.success) {
1574
+ updateCartUI(data.cart);
1575
+ } else {
1576
+ console.error('Failed to fetch cart:', data.error);
1577
+ const cart = getCartLocalStorage();
1578
+ updateCartUI(cart);
1579
+ }
1580
+ })
1581
+ .catch(err => {
1582
+ console.error('Error fetching cart:', err);
1583
+ const cart = getCartLocalStorage();
1584
+ updateCartUI(cart);
1585
+ });
1586
+
1587
+ const preloadedVideos = document.querySelectorAll('link[rel="preload"][as="video"]');
1588
+ preloadedVideos.forEach(link => {
1589
+ const video = document.createElement('video');
1590
+ video.src = link.href;
1591
+ video.preload = 'auto';
1592
+ });
1593
+
1594
+ const decreaseBtn = document.getElementById('decreaseQuantity');
1595
+ const increaseBtn = document.getElementById('increaseQuantity');
1596
+ const quantityInput = document.getElementById('quantityInput');
1597
+ decreaseBtn.addEventListener('click', function () {
1598
+ let currentQuantity = parseInt(quantityInput.value);
1599
+ if (currentQuantity > 1) {
1600
+ currentQuantity--;
1601
+ quantityInput.value = currentQuantity;
1602
+ }
1603
+ });
1604
+ increaseBtn.addEventListener('click', function () {
1605
+ let currentQuantity = parseInt(quantityInput.value);
1606
+ currentQuantity++;
1607
+ quantityInput.value = currentQuantity;
1608
+ });
1609
+
1610
+ const softDrinkDecreaseBtn = document.getElementById('soft-drink-decrease');
1611
+ const softDrinkIncreaseBtn = document.getElementById('soft-drink-increase');
1612
+ const softDrinkQuantityInput = document.getElementById('soft-drink-quantity');
1613
+
1614
+ softDrinkDecreaseBtn.addEventListener('click', function() {
1615
+ let currentQuantity = parseInt(softDrinkQuantityInput.value);
1616
+ if (currentQuantity > 1) {
1617
+ currentQuantity--;
1618
+ softDrinkQuantityInput.value = currentQuantity;
1619
+ }
1620
+ });
1621
+
1622
+ softDrinkIncreaseBtn.addEventListener('click', function() {
1623
+ let currentQuantity = parseInt(softDrinkQuantityInput.value);
1624
+ if (currentQuantity < 1000) {
1625
+ currentQuantity++;
1626
+ softDrinkQuantityInput.value = currentQuantity;
1627
+ }
1628
+ });
1629
+
1630
+ // Mic Popup Functionality
1631
+ const micIcon = document.getElementById('micIcon');
1632
+ const micUnsupported = document.getElementById('micUnsupported');
1633
+ const micPopup = document.getElementById('micPopup');
1634
+ const micPopupText = document.getElementById('micPopupText');
1635
+ const micPopupCancel = document.getElementById('micPopupCancel');
1636
+
1637
+ if ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window) {
1638
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
1639
+ const recognition = new SpeechRecognition();
1640
+ recognition.continuous = false;
1641
+ recognition.interimResults = true;
1642
+ recognition.lang = 'en-US';
1643
+
1644
+ recognition.onstart = () => {
1645
+ micIcon.classList.add('active');
1646
+ micPopup.classList.add('active');
1647
+ micPopupText.textContent = 'Listening...';
1648
+ };
1649
+
1650
+ recognition.onresult = (event) => {
1651
+ let interimTranscript = '';
1652
+ let finalTranscript = '';
1653
+
1654
+ for (let i = event.resultIndex; i < event.results.length; i++) {
1655
+ const transcript = event.results[i][0].transcript;
1656
+ if (event.results[i].isFinal) {
1657
+ finalTranscript += transcript;
1658
+ } else {
1659
+ interimTranscript += transcript;
1660
+ }
1661
+ }
1662
+
1663
+ if (interimTranscript) {
1664
+ micPopupText.textContent = interimTranscript;
1665
+ }
1666
+
1667
+ if (finalTranscript) {
1668
+ searchBar.value = sanitizeInput(finalTranscript.trim());
1669
+ debouncedFilterMenu();
1670
+ micPopup.classList.remove('active');
1671
+ }
1672
+ };
1673
+
1674
+ recognition.onend = () => {
1675
+ micIcon.classList.remove('active');
1676
+ if (micPopup.classList.contains('active')) {
1677
+ setTimeout(() => {
1678
+ micPopup.classList.remove('active');
1679
+ }, 1000);
1680
+ }
1681
+ };
1682
+
1683
+ recognition.onerror = (event) => {
1684
+ micIcon.classList.remove('active');
1685
+ micPopupText.textContent = 'Error: ' + event.error;
1686
+ setTimeout(() => {
1687
+ micPopup.classList.remove('active');
1688
+ }, 2000);
1689
+ console.error('Speech error:', event.error);
1690
+ };
1691
+
1692
+ micIcon.addEventListener('click', () => {
1693
+ try {
1694
+ recognition.start();
1695
+ } catch (e) {
1696
+ micPopupText.textContent = 'Error starting microphone';
1697
+ setTimeout(() => {
1698
+ micPopup.classList.remove('active');
1699
+ }, 2000);
1700
+ console.error('Recognition start error:', e);
1701
+ }
1702
+ });
1703
+
1704
+ micPopupCancel.addEventListener('click', () => {
1705
+ recognition.stop();
1706
+ micPopup.classList.remove('active');
1707
+ micIcon.classList.remove('active');
1708
+ });
1709
+ } else {
1710
+ micIcon.style.display = 'none';
1711
+ micUnsupported.style.display = 'block';
1712
+ }
1713
+ });
1714
+
1715
+ function filterMenu() {
1716
+ const input = sanitizeInput(document.getElementById('searchBar').value.trim().toLowerCase());
1717
+ const sections = document.querySelectorAll('h3');
1718
+ const items = document.querySelectorAll('.menu-card');
1719
+ let matchedSections = new Set();
1720
+ items.forEach(item => {
1721
+ const itemName = item.querySelector('.card-title').innerText.toLowerCase();
1722
+ const itemSection = item.closest('.row').previousElementSibling.innerText.toLowerCase();
1723
+ if (itemName.includes(input) || (itemSection && itemSection.includes(input))) {
1724
+ item.style.display = 'block';
1725
+ item.classList.add('visible');
1726
+ matchedSections.add(item.closest('.row'));
1727
+ } else {
1728
+ item.style.display = 'none';
1729
+ }
1730
+ });
1731
+ sections.forEach(section => {
1732
+ const sectionRow = section.nextElementSibling;
1733
+ if (matchedSections.has(sectionRow)) {
1734
+ section.style.display = 'block';
1735
+ sectionRow.style.display = 'flex';
1736
+ } else {
1737
+ section.style.display = 'none';
1738
+ sectionRow.style.display = 'none';
1739
+ }
1740
+ });
1741
+ if (!input) {
1742
+ sections.forEach(section => {
1743
+ section.style.display = 'block';
1744
+ section.nextElementSibling.style.display = 'flex';
1745
+ });
1746
+ items.forEach(item => {
1747
+ item.style.display = 'block';
1748
+ item.classList.add('visible');
1749
+ });
1750
+ }
1751
+ }
1752
+
1753
+ function showItemDetails(name, price, image, description, ingredients, nutrition, allergens, section, selectedCategory) {
1754
+ document.getElementById('modal-name').innerText = name;
1755
+ document.getElementById('modal-price').innerText = `$${price}`;
1756
+ const modalImg = document.getElementById('modal-img');
1757
+ modalImg.src = image || '/static/placeholder.jpg';
1758
+ document.getElementById('modal-description').innerText = description || 'No description available.';
1759
+ document.getElementById('modal-ingredients').innerText = ingredients || 'Not specified';
1760
+ document.getElementById('modal-nutrition').innerText = nutrition || 'Not available';
1761
+ document.getElementById('modal-allergens').innerText = allergens || 'None listed';
1762
+ document.getElementById('addons-list').innerHTML = 'Loading customization options...';
1763
+ document.getElementById('modal-instructions').value = '';
1764
+ const modalSectionEl = document.getElementById('modal-section');
1765
+ modalSectionEl.setAttribute('data-section', section);
1766
+ modalSectionEl.setAttribute('data-category', selectedCategory);
1767
+ document.getElementById('quantityInput').value = 1;
1768
+
1769
+ fetch(`/api/addons?item_name=${encodeURIComponent(name)}&item_section=${encodeURIComponent(section)}`)
1770
+ .then(response => response.json())
1771
+ .then(data => {
1772
+ const addonsList = document.getElementById('addons-list');
1773
+ addonsList.innerHTML = '';
1774
+ if (!data.success || !data.addons || data.addons.length === 0) {
1775
+ addonsList.innerHTML = '<p>No customization options available.</p>';
1776
+ return;
1777
+ }
1778
+ data.addons.forEach(addon => {
1779
+ const sectionDiv = document.createElement('div');
1780
+ sectionDiv.classList.add('addon-section');
1781
+ const title = document.createElement('h6');
1782
+ title.innerText = addon.name;
1783
+ sectionDiv.appendChild(title);
1784
+ const optionsContainer = document.createElement('div');
1785
+ addon.options.forEach((option, index) => {
1786
+ const optionId = `addon-${addon.name.replace(/\s+/g, '')}-${index}`;
1787
+ const listItem = document.createElement('div');
1788
+ listItem.classList.add('form-check');
1789
+ listItem.innerHTML = `
1790
+ <input type="checkbox" class="form-check-input addon-option" id="${optionId}" value="${option}"
1791
+ data-name="${option}" data-group="${addon.name}" data-price="${addon.extra_charge ? addon.extra_charge_amount : 0}">
1792
+ <label class="form-check-label" for="${optionId}">
1793
+ ${option} ${addon.extra_charge ? `($${addon.extra_charge_amount})` : ''}
1794
+ </label>
1795
+ `;
1796
+ optionsContainer.appendChild(listItem);
1797
+ });
1798
+ sectionDiv.appendChild(optionsContainer);
1799
+ addonsList.appendChild(sectionDiv);
1800
+ });
1801
+ })
1802
+ .catch(err => {
1803
+ console.error('Error fetching add-ons:', err);
1804
+ document.getElementById('addons-list').innerHTML = '<p>Error loading customization options.</p>';
1805
+ });
1806
+ }
1807
+
1808
+ document.addEventListener('click', function(event) {
1809
+ if (event.target.classList.contains('addon-option')) {
1810
+ handleAddonClick(event.target);
1811
+ }
1812
+ });
1813
+
1814
+ function handleAddonClick(checkbox) {
1815
+ const groupName = checkbox.getAttribute('data-group');
1816
+ const isMultiSelectGroup = ["Extra Toppings", "Choose Raita/Sides", "Select Dip/Sauce", "Extra Add-ons", "Make it a Combo", "Beverages", "Sauces"].includes(groupName);
1817
+ if (!isMultiSelectGroup) {
1818
+ const checkboxes = document.querySelectorAll(`.addon-option[data-group="${groupName}"]`);
1819
+ checkboxes.forEach(otherCheckbox => {
1820
+ if (otherCheckbox !== checkbox) {
1821
+ otherCheckbox.checked = false;
1822
+ }
1823
+ });
1824
+ }
1825
+ }
1826
+
1827
+ function addToCartFromModal() {
1828
+ const itemName = document.getElementById('modal-name').innerText;
1829
+ let itemPrice = parseFloat(document.getElementById('modal-price').innerText.replace('$', ''));
1830
+ if (isNaN(itemPrice)) {
1831
+ alert('Invalid price for the item. Please check the item details.');
1832
+ return;
1833
+ }
1834
+ const itemImage = document.getElementById('modal-img').src;
1835
+ const modalSectionEl = document.getElementById('modal-section');
1836
+ const section = modalSectionEl.getAttribute('data-section');
1837
+ const selectedCategory = modalSectionEl.getAttribute('data-category');
1838
+ if (!itemName || !itemPrice || !section || !itemImage) {
1839
+ console.error('Missing data for cart item:', { itemName, itemPrice, section, itemImage });
1840
+ return;
1841
+ }
1842
+ const selectedAddOns = Array.from(
1843
+ document.querySelectorAll('#addons-list input[type="checkbox"]:checked')
1844
+ ).map(addon => ({
1845
+ name: addon.getAttribute('data-name') || 'Default Name',
1846
+ price: parseFloat(addon.getAttribute('data-price') || 0)
1847
+ }));
1848
+ const quantity = parseInt(document.getElementById('quantityInput').value) || 1;
1849
+ const instructions = document.getElementById('modal-instructions').value;
1850
+ const cartPayload = {
1851
+ itemName: itemName,
1852
+ itemPrice: itemPrice,
1853
+ itemImage: itemImage,
1854
+ section: section,
1855
+ category: selectedCategory,
1856
+
1857
+ addons: selectedAddOns,
1858
+ instructions: instructions,
1859
+ quantity: quantity
1860
+ };
1861
+
1862
+ fetch('/cart/add', {
1863
+ method: 'POST',
1864
+ headers: {
1865
+ 'Content-Type': 'application/json',
1866
+ },
1867
+ body: JSON.stringify(cartPayload)
1868
+ })
1869
+ .then(response => response.json())
1870
+ .then(data => {
1871
+ if (data.success) {
1872
+ alert('Item added to cart successfully!');
1873
+ updateCartUI(data.cart);
1874
+ const modal = document.getElementById('itemModal');
1875
+ const modalInstance = bootstrap.Modal.getInstance(modal);
1876
+ modalInstance.hide();
1877
+ } else {
1878
+ console.error('Failed to add item to cart:', data.error);
1879
+ alert(data.error || 'Failed to add item to cart.');
1880
+ }
1881
+ })
1882
+ .catch(err => {
1883
+ console.error('Error adding item to cart:', err);
1884
+ const cart = addToCartLocalStorage(cartPayload);
1885
+ updateCartUI(cart);
1886
+ const modal = document.getElementById('itemModal');
1887
+ const modalInstance = bootstrap.Modal.getInstance(modal);
1888
+ modalInstance.hide();
1889
+ });
1890
+ }
1891
+ </script>
1892
+ </body>
1893
+ </html>