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

Update templates/menu.html

Browse files
Files changed (1) hide show
  1. templates/menu.html +0 -1893
templates/menu.html CHANGED
@@ -1,1893 +0,0 @@
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>